1// Copyright Joyent, Inc. and other Node contributors. 2// 3// Permission is hereby granted, free of charge, to any person obtaining a 4// copy of this software and associated documentation files (the 5// "Software"), to deal in the Software without restriction, including 6// without limitation the rights to use, copy, modify, merge, publish, 7// distribute, sublicense, and/or sell copies of the Software, and to permit 8// persons to whom the Software is furnished to do so, subject to the 9// following conditions: 10// 11// The above copyright notice and this permission notice shall be included 12// in all copies or substantial portions of the Software. 13// 14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20// USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22// Maintainers, keep in mind that ES1-style octal literals (`0666`) are not 23// allowed in strict mode. Use ES6-style octal literals instead (`0o666`). 24 25'use strict'; 26 27// When using FSReqCallback, make sure to create the object only *after* all 28// parameter validation has happened, so that the objects are not kept in memory 29// in case they are created but never used due to an exception. 30 31const { 32 Map, 33 MathMax, 34 Number, 35 ObjectCreate, 36 ObjectDefineProperties, 37 ObjectDefineProperty, 38 Promise, 39 String, 40} = primordials; 41 42const { fs: constants } = internalBinding('constants'); 43const { 44 S_IFIFO, 45 S_IFLNK, 46 S_IFMT, 47 S_IFREG, 48 S_IFSOCK, 49 F_OK, 50 R_OK, 51 W_OK, 52 X_OK, 53 O_WRONLY, 54 O_SYMLINK 55} = constants; 56 57const pathModule = require('path'); 58const { isArrayBufferView } = require('internal/util/types'); 59const binding = internalBinding('fs'); 60const { Buffer } = require('buffer'); 61const { 62 codes: { 63 ERR_FS_FILE_TOO_LARGE, 64 ERR_INVALID_ARG_VALUE, 65 ERR_INVALID_ARG_TYPE, 66 ERR_INVALID_CALLBACK, 67 ERR_FEATURE_UNAVAILABLE_ON_PLATFORM, 68 }, 69 hideStackFrames, 70 uvErrmapGet, 71 uvException 72} = require('internal/errors'); 73 74const { FSReqCallback, statValues } = binding; 75const { toPathIfFileURL } = require('internal/url'); 76const internalUtil = require('internal/util'); 77const { 78 constants: { 79 kIoMaxLength, 80 kMaxUserId, 81 }, 82 copyObject, 83 Dirent, 84 getDirents, 85 getOptions, 86 getValidatedPath, 87 getValidMode, 88 handleErrorFromBinding, 89 nullCheck, 90 preprocessSymlinkDestination, 91 Stats, 92 getStatsFromBinding, 93 realpathCacheKey, 94 stringToFlags, 95 stringToSymlinkType, 96 toUnixTimestamp, 97 validateBufferArray, 98 validateOffsetLengthRead, 99 validateOffsetLengthWrite, 100 validatePath, 101 validatePosition, 102 validateRmOptions, 103 validateRmOptionsSync, 104 validateRmdirOptions, 105 validateStringAfterArrayBufferView, 106 warnOnNonPortableTemplate 107} = require('internal/fs/utils'); 108const { 109 Dir, 110 opendir, 111 opendirSync 112} = require('internal/fs/dir'); 113const { 114 CHAR_FORWARD_SLASH, 115 CHAR_BACKWARD_SLASH, 116} = require('internal/constants'); 117const { 118 isUint32, 119 parseFileMode, 120 validateBuffer, 121 validateInteger, 122 validateInt32, 123 validateString, 124} = require('internal/validators'); 125 126let truncateWarn = true; 127let fs; 128 129// Lazy loaded 130let promises = null; 131let watchers; 132let ReadFileContext; 133let ReadStream; 134let WriteStream; 135let rimraf; 136let rimrafSync; 137let DOMException; 138 139const lazyDOMException = hideStackFrames((message, name) => { 140 if (DOMException === undefined) 141 DOMException = internalBinding('messaging').DOMException; 142 return new DOMException(message, name); 143}); 144 145// These have to be separate because of how graceful-fs happens to do it's 146// monkeypatching. 147let FileReadStream; 148let FileWriteStream; 149 150const isWindows = process.platform === 'win32'; 151const isOSX = process.platform === 'darwin'; 152 153 154function showTruncateDeprecation() { 155 if (truncateWarn) { 156 process.emitWarning( 157 'Using fs.truncate with a file descriptor is deprecated. Please use ' + 158 'fs.ftruncate with a file descriptor instead.', 159 'DeprecationWarning', 'DEP0081'); 160 truncateWarn = false; 161 } 162} 163 164function maybeCallback(cb) { 165 if (typeof cb === 'function') 166 return cb; 167 168 throw new ERR_INVALID_CALLBACK(cb); 169} 170 171// Ensure that callbacks run in the global context. Only use this function 172// for callbacks that are passed to the binding layer, callbacks that are 173// invoked from JS already run in the proper scope. 174function makeCallback(cb) { 175 if (typeof cb !== 'function') { 176 throw new ERR_INVALID_CALLBACK(cb); 177 } 178 179 return (...args) => cb(...args); 180} 181 182// Special case of `makeCallback()` that is specific to async `*stat()` calls as 183// an optimization, since the data passed back to the callback needs to be 184// transformed anyway. 185function makeStatsCallback(cb) { 186 if (typeof cb !== 'function') { 187 throw new ERR_INVALID_CALLBACK(cb); 188 } 189 190 return (err, stats) => { 191 if (err) return cb(err); 192 cb(err, getStatsFromBinding(stats)); 193 }; 194} 195 196const isFd = isUint32; 197 198function isFileType(stats, fileType) { 199 // Use stats array directly to avoid creating an fs.Stats instance just for 200 // our internal use. 201 let mode = stats[1]; 202 if (typeof mode === 'bigint') 203 mode = Number(mode); 204 return (mode & S_IFMT) === fileType; 205} 206 207function access(path, mode, callback) { 208 if (typeof mode === 'function') { 209 callback = mode; 210 mode = F_OK; 211 } 212 213 path = getValidatedPath(path); 214 mode = getValidMode(mode, 'access'); 215 callback = makeCallback(callback); 216 217 const req = new FSReqCallback(); 218 req.oncomplete = callback; 219 binding.access(pathModule.toNamespacedPath(path), mode, req); 220} 221 222function accessSync(path, mode) { 223 path = getValidatedPath(path); 224 mode = getValidMode(mode, 'access'); 225 226 const ctx = { path }; 227 binding.access(pathModule.toNamespacedPath(path), mode, undefined, ctx); 228 handleErrorFromBinding(ctx); 229} 230 231function exists(path, callback) { 232 maybeCallback(callback); 233 234 function suppressedCallback(err) { 235 callback(err ? false : true); 236 } 237 238 try { 239 fs.access(path, F_OK, suppressedCallback); 240 } catch { 241 return callback(false); 242 } 243} 244 245ObjectDefineProperty(exists, internalUtil.promisify.custom, { 246 value: (path) => { 247 return new Promise((resolve) => fs.exists(path, resolve)); 248 } 249}); 250 251// fs.existsSync never throws, it only returns true or false. 252// Since fs.existsSync never throws, users have established 253// the expectation that passing invalid arguments to it, even like 254// fs.existsSync(), would only get a false in return, so we cannot signal 255// validation errors to users properly out of compatibility concerns. 256// TODO(joyeecheung): deprecate the never-throw-on-invalid-arguments behavior 257function existsSync(path) { 258 try { 259 path = getValidatedPath(path); 260 } catch { 261 return false; 262 } 263 const ctx = { path }; 264 const nPath = pathModule.toNamespacedPath(path); 265 binding.access(nPath, F_OK, undefined, ctx); 266 267 // In case of an invalid symlink, `binding.access()` on win32 268 // will **not** return an error and is therefore not enough. 269 // Double check with `binding.stat()`. 270 if (isWindows && ctx.errno === undefined) { 271 binding.stat(nPath, false, undefined, ctx); 272 } 273 274 return ctx.errno === undefined; 275} 276 277function readFileAfterOpen(err, fd) { 278 const context = this.context; 279 280 if (err) { 281 context.callback(err); 282 return; 283 } 284 285 context.fd = fd; 286 287 const req = new FSReqCallback(); 288 req.oncomplete = readFileAfterStat; 289 req.context = context; 290 binding.fstat(fd, false, req); 291} 292 293function readFileAfterStat(err, stats) { 294 const context = this.context; 295 296 if (err) 297 return context.close(err); 298 299 const size = context.size = isFileType(stats, S_IFREG) ? stats[8] : 0; 300 301 if (size > kIoMaxLength) { 302 err = new ERR_FS_FILE_TOO_LARGE(size); 303 return context.close(err); 304 } 305 306 try { 307 if (size === 0) { 308 context.buffers = []; 309 } else { 310 context.buffer = Buffer.allocUnsafeSlow(size); 311 } 312 } catch (err) { 313 return context.close(err); 314 } 315 context.read(); 316} 317 318function readFile(path, options, callback) { 319 callback = maybeCallback(callback || options); 320 options = getOptions(options, { flag: 'r' }); 321 if (!ReadFileContext) 322 ReadFileContext = require('internal/fs/read_file_context'); 323 const context = new ReadFileContext(callback, options.encoding); 324 context.isUserFd = isFd(path); // File descriptor ownership 325 326 if (options.signal) { 327 context.signal = options.signal; 328 } 329 if (context.isUserFd) { 330 process.nextTick(function tick(context) { 331 readFileAfterOpen.call({ context }, null, path); 332 }, context); 333 return; 334 } 335 336 if (options.signal?.aborted) { 337 callback(lazyDOMException('The operation was aborted', 'AbortError')); 338 return; 339 } 340 341 const flagsNumber = stringToFlags(options.flag); 342 path = getValidatedPath(path); 343 344 const req = new FSReqCallback(); 345 req.context = context; 346 req.oncomplete = readFileAfterOpen; 347 binding.open(pathModule.toNamespacedPath(path), 348 flagsNumber, 349 0o666, 350 req); 351} 352 353function tryStatSync(fd, isUserFd) { 354 const ctx = {}; 355 const stats = binding.fstat(fd, false, undefined, ctx); 356 if (ctx.errno !== undefined && !isUserFd) { 357 fs.closeSync(fd); 358 throw uvException(ctx); 359 } 360 return stats; 361} 362 363function tryCreateBuffer(size, fd, isUserFd) { 364 let threw = true; 365 let buffer; 366 try { 367 if (size > kIoMaxLength) { 368 throw new ERR_FS_FILE_TOO_LARGE(size); 369 } 370 buffer = Buffer.allocUnsafe(size); 371 threw = false; 372 } finally { 373 if (threw && !isUserFd) fs.closeSync(fd); 374 } 375 return buffer; 376} 377 378function tryReadSync(fd, isUserFd, buffer, pos, len) { 379 let threw = true; 380 let bytesRead; 381 try { 382 bytesRead = fs.readSync(fd, buffer, pos, len); 383 threw = false; 384 } finally { 385 if (threw && !isUserFd) fs.closeSync(fd); 386 } 387 return bytesRead; 388} 389 390function readFileSync(path, options) { 391 options = getOptions(options, { flag: 'r' }); 392 const isUserFd = isFd(path); // File descriptor ownership 393 const fd = isUserFd ? path : fs.openSync(path, options.flag, 0o666); 394 395 const stats = tryStatSync(fd, isUserFd); 396 const size = isFileType(stats, S_IFREG) ? stats[8] : 0; 397 let pos = 0; 398 let buffer; // Single buffer with file data 399 let buffers; // List for when size is unknown 400 401 if (size === 0) { 402 buffers = []; 403 } else { 404 buffer = tryCreateBuffer(size, fd, isUserFd); 405 } 406 407 let bytesRead; 408 409 if (size !== 0) { 410 do { 411 bytesRead = tryReadSync(fd, isUserFd, buffer, pos, size - pos); 412 pos += bytesRead; 413 } while (bytesRead !== 0 && pos < size); 414 } else { 415 do { 416 // The kernel lies about many files. 417 // Go ahead and try to read some bytes. 418 buffer = Buffer.allocUnsafe(8192); 419 bytesRead = tryReadSync(fd, isUserFd, buffer, 0, 8192); 420 if (bytesRead !== 0) { 421 buffers.push(buffer.slice(0, bytesRead)); 422 } 423 pos += bytesRead; 424 } while (bytesRead !== 0); 425 } 426 427 if (!isUserFd) 428 fs.closeSync(fd); 429 430 if (size === 0) { 431 // Data was collected into the buffers list. 432 buffer = Buffer.concat(buffers, pos); 433 } else if (pos < size) { 434 buffer = buffer.slice(0, pos); 435 } 436 437 if (options.encoding) buffer = buffer.toString(options.encoding); 438 return buffer; 439} 440 441function defaultCloseCallback(err) { 442 if (err != null) throw err; 443} 444 445function close(fd, callback = defaultCloseCallback) { 446 validateInt32(fd, 'fd', 0); 447 if (callback !== defaultCloseCallback) 448 callback = makeCallback(callback); 449 450 const req = new FSReqCallback(); 451 req.oncomplete = callback; 452 binding.close(fd, req); 453} 454 455function closeSync(fd) { 456 validateInt32(fd, 'fd', 0); 457 458 const ctx = {}; 459 binding.close(fd, undefined, ctx); 460 handleErrorFromBinding(ctx); 461} 462 463function open(path, flags, mode, callback) { 464 path = getValidatedPath(path); 465 if (arguments.length < 3) { 466 callback = flags; 467 flags = 'r'; 468 mode = 0o666; 469 } else if (typeof mode === 'function') { 470 callback = mode; 471 mode = 0o666; 472 } else { 473 mode = parseFileMode(mode, 'mode', 0o666); 474 } 475 const flagsNumber = stringToFlags(flags); 476 callback = makeCallback(callback); 477 478 const req = new FSReqCallback(); 479 req.oncomplete = callback; 480 481 binding.open(pathModule.toNamespacedPath(path), 482 flagsNumber, 483 mode, 484 req); 485} 486 487 488function openSync(path, flags, mode) { 489 path = getValidatedPath(path); 490 const flagsNumber = stringToFlags(flags); 491 mode = parseFileMode(mode, 'mode', 0o666); 492 493 const ctx = { path }; 494 const result = binding.open(pathModule.toNamespacedPath(path), 495 flagsNumber, mode, 496 undefined, ctx); 497 handleErrorFromBinding(ctx); 498 return result; 499} 500 501// usage: 502// fs.read(fd, buffer, offset, length, position, callback); 503// OR 504// fs.read(fd, {}, callback) 505function read(fd, buffer, offset, length, position, callback) { 506 validateInt32(fd, 'fd', 0); 507 508 if (arguments.length <= 3) { 509 // Assume fs.read(fd, options, callback) 510 let options = {}; 511 if (arguments.length < 3) { 512 // This is fs.read(fd, callback) 513 // buffer will be the callback 514 callback = buffer; 515 } else { 516 // This is fs.read(fd, {}, callback) 517 // buffer will be the options object 518 // offset is the callback 519 options = buffer; 520 callback = offset; 521 } 522 523 ({ 524 buffer = Buffer.alloc(16384), 525 offset = 0, 526 length = buffer.byteLength, 527 position 528 } = options); 529 } 530 531 validateBuffer(buffer); 532 callback = maybeCallback(callback); 533 534 if (offset == null) { 535 offset = 0; 536 } else { 537 validateInteger(offset, 'offset', 0); 538 } 539 540 length |= 0; 541 542 if (length === 0) { 543 return process.nextTick(function tick() { 544 callback(null, 0, buffer); 545 }); 546 } 547 548 if (buffer.byteLength === 0) { 549 throw new ERR_INVALID_ARG_VALUE('buffer', buffer, 550 'is empty and cannot be written'); 551 } 552 553 validateOffsetLengthRead(offset, length, buffer.byteLength); 554 555 if (position == null) 556 position = -1; 557 558 validatePosition(position, 'position'); 559 560 function wrapper(err, bytesRead) { 561 // Retain a reference to buffer so that it can't be GC'ed too soon. 562 callback(err, bytesRead || 0, buffer); 563 } 564 565 const req = new FSReqCallback(); 566 req.oncomplete = wrapper; 567 568 binding.read(fd, buffer, offset, length, position, req); 569} 570 571ObjectDefineProperty(read, internalUtil.customPromisifyArgs, 572 { value: ['bytesRead', 'buffer'], enumerable: false }); 573 574// usage: 575// fs.readSync(fd, buffer, offset, length, position); 576// OR 577// fs.readSync(fd, buffer, {}) or fs.readSync(fd, buffer) 578function readSync(fd, buffer, offset, length, position) { 579 validateInt32(fd, 'fd', 0); 580 581 validateBuffer(buffer); 582 583 if (arguments.length <= 3) { 584 // Assume fs.read(fd, buffer, options) 585 const options = offset || {}; 586 587 ({ offset = 0, length = buffer.byteLength, position } = options); 588 } 589 590 if (offset == null) { 591 offset = 0; 592 } else { 593 validateInteger(offset, 'offset', 0); 594 } 595 596 length |= 0; 597 598 if (length === 0) { 599 return 0; 600 } 601 602 if (buffer.byteLength === 0) { 603 throw new ERR_INVALID_ARG_VALUE('buffer', buffer, 604 'is empty and cannot be written'); 605 } 606 607 validateOffsetLengthRead(offset, length, buffer.byteLength); 608 609 if (position == null) 610 position = -1; 611 612 validatePosition(position, 'position'); 613 614 const ctx = {}; 615 const result = binding.read(fd, buffer, offset, length, position, 616 undefined, ctx); 617 handleErrorFromBinding(ctx); 618 return result; 619} 620 621function readv(fd, buffers, position, callback) { 622 function wrapper(err, read) { 623 callback(err, read || 0, buffers); 624 } 625 626 validateInt32(fd, 'fd', /* min */ 0); 627 validateBufferArray(buffers); 628 callback = maybeCallback(callback || position); 629 630 const req = new FSReqCallback(); 631 req.oncomplete = wrapper; 632 633 if (typeof position !== 'number') 634 position = null; 635 636 return binding.readBuffers(fd, buffers, position, req); 637} 638 639ObjectDefineProperty(readv, internalUtil.customPromisifyArgs, 640 { value: ['bytesRead', 'buffers'], enumerable: false }); 641 642function readvSync(fd, buffers, position) { 643 validateInt32(fd, 'fd', 0); 644 validateBufferArray(buffers); 645 646 const ctx = {}; 647 648 if (typeof position !== 'number') 649 position = null; 650 651 const result = binding.readBuffers(fd, buffers, position, undefined, ctx); 652 handleErrorFromBinding(ctx); 653 return result; 654} 655 656// usage: 657// fs.write(fd, buffer[, offset[, length[, position]]], callback); 658// OR 659// fs.write(fd, string[, position[, encoding]], callback); 660function write(fd, buffer, offset, length, position, callback) { 661 function wrapper(err, written) { 662 // Retain a reference to buffer so that it can't be GC'ed too soon. 663 callback(err, written || 0, buffer); 664 } 665 666 validateInt32(fd, 'fd', 0); 667 668 if (isArrayBufferView(buffer)) { 669 callback = maybeCallback(callback || position || length || offset); 670 if (offset == null || typeof offset === 'function') { 671 offset = 0; 672 } else { 673 validateInteger(offset, 'offset', 0); 674 } 675 if (typeof length !== 'number') 676 length = buffer.byteLength - offset; 677 if (typeof position !== 'number') 678 position = null; 679 validateOffsetLengthWrite(offset, length, buffer.byteLength); 680 681 const req = new FSReqCallback(); 682 req.oncomplete = wrapper; 683 return binding.writeBuffer(fd, buffer, offset, length, position, req); 684 } 685 686 validateStringAfterArrayBufferView(buffer, 'buffer'); 687 688 if (typeof position !== 'function') { 689 if (typeof offset === 'function') { 690 position = offset; 691 offset = null; 692 } else { 693 position = length; 694 } 695 length = 'utf8'; 696 } 697 callback = maybeCallback(position); 698 699 const req = new FSReqCallback(); 700 req.oncomplete = wrapper; 701 return binding.writeString(fd, String(buffer), offset, length, req); 702} 703 704ObjectDefineProperty(write, internalUtil.customPromisifyArgs, 705 { value: ['bytesWritten', 'buffer'], enumerable: false }); 706 707// Usage: 708// fs.writeSync(fd, buffer[, offset[, length[, position]]]); 709// OR 710// fs.writeSync(fd, string[, position[, encoding]]); 711function writeSync(fd, buffer, offset, length, position) { 712 validateInt32(fd, 'fd', 0); 713 const ctx = {}; 714 let result; 715 if (isArrayBufferView(buffer)) { 716 if (position === undefined) 717 position = null; 718 if (offset == null) { 719 offset = 0; 720 } else { 721 validateInteger(offset, 'offset', 0); 722 } 723 if (typeof length !== 'number') 724 length = buffer.byteLength - offset; 725 validateOffsetLengthWrite(offset, length, buffer.byteLength); 726 result = binding.writeBuffer(fd, buffer, offset, length, position, 727 undefined, ctx); 728 } else { 729 validateStringAfterArrayBufferView(buffer, 'buffer'); 730 731 if (offset === undefined) 732 offset = null; 733 result = binding.writeString(fd, buffer, offset, length, 734 undefined, ctx); 735 } 736 handleErrorFromBinding(ctx); 737 return result; 738} 739 740// usage: 741// fs.writev(fd, buffers[, position], callback); 742function writev(fd, buffers, position, callback) { 743 function wrapper(err, written) { 744 callback(err, written || 0, buffers); 745 } 746 747 validateInt32(fd, 'fd', 0); 748 validateBufferArray(buffers); 749 callback = maybeCallback(callback || position); 750 751 const req = new FSReqCallback(); 752 req.oncomplete = wrapper; 753 754 if (typeof position !== 'number') 755 position = null; 756 757 return binding.writeBuffers(fd, buffers, position, req); 758} 759 760ObjectDefineProperty(writev, internalUtil.customPromisifyArgs, { 761 value: ['bytesWritten', 'buffer'], 762 enumerable: false 763}); 764 765function writevSync(fd, buffers, position) { 766 validateInt32(fd, 'fd', 0); 767 validateBufferArray(buffers); 768 769 const ctx = {}; 770 771 if (typeof position !== 'number') 772 position = null; 773 774 const result = binding.writeBuffers(fd, buffers, position, undefined, ctx); 775 776 handleErrorFromBinding(ctx); 777 return result; 778} 779 780function rename(oldPath, newPath, callback) { 781 callback = makeCallback(callback); 782 oldPath = getValidatedPath(oldPath, 'oldPath'); 783 newPath = getValidatedPath(newPath, 'newPath'); 784 const req = new FSReqCallback(); 785 req.oncomplete = callback; 786 binding.rename(pathModule.toNamespacedPath(oldPath), 787 pathModule.toNamespacedPath(newPath), 788 req); 789} 790 791function renameSync(oldPath, newPath) { 792 oldPath = getValidatedPath(oldPath, 'oldPath'); 793 newPath = getValidatedPath(newPath, 'newPath'); 794 const ctx = { path: oldPath, dest: newPath }; 795 binding.rename(pathModule.toNamespacedPath(oldPath), 796 pathModule.toNamespacedPath(newPath), undefined, ctx); 797 handleErrorFromBinding(ctx); 798} 799 800function truncate(path, len, callback) { 801 if (typeof path === 'number') { 802 showTruncateDeprecation(); 803 return fs.ftruncate(path, len, callback); 804 } 805 if (typeof len === 'function') { 806 callback = len; 807 len = 0; 808 } else if (len === undefined) { 809 len = 0; 810 } 811 812 validateInteger(len, 'len'); 813 len = MathMax(0, len); 814 callback = maybeCallback(callback); 815 fs.open(path, 'r+', (er, fd) => { 816 if (er) return callback(er); 817 const req = new FSReqCallback(); 818 req.oncomplete = function oncomplete(er) { 819 fs.close(fd, (er2) => { 820 callback(er || er2); 821 }); 822 }; 823 binding.ftruncate(fd, len, req); 824 }); 825} 826 827function truncateSync(path, len) { 828 if (typeof path === 'number') { 829 // legacy 830 showTruncateDeprecation(); 831 return fs.ftruncateSync(path, len); 832 } 833 if (len === undefined) { 834 len = 0; 835 } 836 // Allow error to be thrown, but still close fd. 837 const fd = fs.openSync(path, 'r+'); 838 let ret; 839 840 try { 841 ret = fs.ftruncateSync(fd, len); 842 } finally { 843 fs.closeSync(fd); 844 } 845 return ret; 846} 847 848function ftruncate(fd, len = 0, callback) { 849 if (typeof len === 'function') { 850 callback = len; 851 len = 0; 852 } 853 validateInt32(fd, 'fd', 0); 854 validateInteger(len, 'len'); 855 len = MathMax(0, len); 856 callback = makeCallback(callback); 857 858 const req = new FSReqCallback(); 859 req.oncomplete = callback; 860 binding.ftruncate(fd, len, req); 861} 862 863function ftruncateSync(fd, len = 0) { 864 validateInt32(fd, 'fd', 0); 865 validateInteger(len, 'len'); 866 len = MathMax(0, len); 867 const ctx = {}; 868 binding.ftruncate(fd, len, undefined, ctx); 869 handleErrorFromBinding(ctx); 870} 871 872 873function lazyLoadRimraf() { 874 if (rimraf === undefined) 875 ({ rimraf, rimrafSync } = require('internal/fs/rimraf')); 876} 877 878function rmdir(path, options, callback) { 879 if (typeof options === 'function') { 880 callback = options; 881 options = undefined; 882 } 883 884 callback = makeCallback(callback); 885 path = pathModule.toNamespacedPath(getValidatedPath(path)); 886 887 if (options && options.recursive) { 888 validateRmOptions(path, { ...options, force: true }, (err, options) => { 889 if (err) { 890 return callback(err); 891 } 892 893 lazyLoadRimraf(); 894 return rimraf(path, options, callback); 895 }); 896 } else { 897 validateRmdirOptions(options); 898 const req = new FSReqCallback(); 899 req.oncomplete = callback; 900 return binding.rmdir(path, req); 901 } 902} 903 904function rmdirSync(path, options) { 905 path = getValidatedPath(path); 906 907 if (options && options.recursive) { 908 options = validateRmOptionsSync(path, { ...options, force: true }); 909 lazyLoadRimraf(); 910 return rimrafSync(pathModule.toNamespacedPath(path), options); 911 } 912 913 validateRmdirOptions(options); 914 const ctx = { path }; 915 binding.rmdir(pathModule.toNamespacedPath(path), undefined, ctx); 916 return handleErrorFromBinding(ctx); 917} 918 919function rm(path, options, callback) { 920 if (typeof options === 'function') { 921 callback = options; 922 options = undefined; 923 } 924 925 validateRmOptions(path, options, (err, options) => { 926 if (err) { 927 return callback(err); 928 } 929 lazyLoadRimraf(); 930 return rimraf(pathModule.toNamespacedPath(path), options, callback); 931 }); 932} 933 934function rmSync(path, options) { 935 options = validateRmOptionsSync(path, options); 936 937 lazyLoadRimraf(); 938 return rimrafSync(pathModule.toNamespacedPath(path), options); 939} 940 941function fdatasync(fd, callback) { 942 validateInt32(fd, 'fd', 0); 943 const req = new FSReqCallback(); 944 req.oncomplete = makeCallback(callback); 945 binding.fdatasync(fd, req); 946} 947 948function fdatasyncSync(fd) { 949 validateInt32(fd, 'fd', 0); 950 const ctx = {}; 951 binding.fdatasync(fd, undefined, ctx); 952 handleErrorFromBinding(ctx); 953} 954 955function fsync(fd, callback) { 956 validateInt32(fd, 'fd', 0); 957 const req = new FSReqCallback(); 958 req.oncomplete = makeCallback(callback); 959 binding.fsync(fd, req); 960} 961 962function fsyncSync(fd) { 963 validateInt32(fd, 'fd', 0); 964 const ctx = {}; 965 binding.fsync(fd, undefined, ctx); 966 handleErrorFromBinding(ctx); 967} 968 969function mkdir(path, options, callback) { 970 let mode = 0o777; 971 let recursive = false; 972 if (typeof options === 'function') { 973 callback = options; 974 } else if (typeof options === 'number' || typeof options === 'string') { 975 mode = options; 976 } else if (options) { 977 if (options.recursive !== undefined) 978 recursive = options.recursive; 979 if (options.mode !== undefined) 980 mode = options.mode; 981 } 982 callback = makeCallback(callback); 983 path = getValidatedPath(path); 984 985 if (typeof recursive !== 'boolean') 986 throw new ERR_INVALID_ARG_TYPE('options.recursive', 'boolean', recursive); 987 988 const req = new FSReqCallback(); 989 req.oncomplete = callback; 990 binding.mkdir(pathModule.toNamespacedPath(path), 991 parseFileMode(mode, 'mode'), recursive, req); 992} 993 994function mkdirSync(path, options) { 995 let mode = 0o777; 996 let recursive = false; 997 if (typeof options === 'number' || typeof options === 'string') { 998 mode = options; 999 } else if (options) { 1000 if (options.recursive !== undefined) 1001 recursive = options.recursive; 1002 if (options.mode !== undefined) 1003 mode = options.mode; 1004 } 1005 path = getValidatedPath(path); 1006 if (typeof recursive !== 'boolean') 1007 throw new ERR_INVALID_ARG_TYPE('options.recursive', 'boolean', recursive); 1008 1009 const ctx = { path }; 1010 const result = binding.mkdir(pathModule.toNamespacedPath(path), 1011 parseFileMode(mode, 'mode'), recursive, 1012 undefined, ctx); 1013 handleErrorFromBinding(ctx); 1014 if (recursive) { 1015 return result; 1016 } 1017} 1018 1019function readdir(path, options, callback) { 1020 callback = makeCallback(typeof options === 'function' ? options : callback); 1021 options = getOptions(options, {}); 1022 path = getValidatedPath(path); 1023 1024 const req = new FSReqCallback(); 1025 if (!options.withFileTypes) { 1026 req.oncomplete = callback; 1027 } else { 1028 req.oncomplete = (err, result) => { 1029 if (err) { 1030 callback(err); 1031 return; 1032 } 1033 getDirents(path, result, callback); 1034 }; 1035 } 1036 binding.readdir(pathModule.toNamespacedPath(path), options.encoding, 1037 !!options.withFileTypes, req); 1038} 1039 1040function readdirSync(path, options) { 1041 options = getOptions(options, {}); 1042 path = getValidatedPath(path); 1043 const ctx = { path }; 1044 const result = binding.readdir(pathModule.toNamespacedPath(path), 1045 options.encoding, !!options.withFileTypes, 1046 undefined, ctx); 1047 handleErrorFromBinding(ctx); 1048 return options.withFileTypes ? getDirents(path, result) : result; 1049} 1050 1051function fstat(fd, options = { bigint: false }, callback) { 1052 if (typeof options === 'function') { 1053 callback = options; 1054 options = {}; 1055 } 1056 validateInt32(fd, 'fd', 0); 1057 callback = makeStatsCallback(callback); 1058 1059 const req = new FSReqCallback(options.bigint); 1060 req.oncomplete = callback; 1061 binding.fstat(fd, options.bigint, req); 1062} 1063 1064function lstat(path, options = { bigint: false }, callback) { 1065 if (typeof options === 'function') { 1066 callback = options; 1067 options = {}; 1068 } 1069 callback = makeStatsCallback(callback); 1070 path = getValidatedPath(path); 1071 1072 const req = new FSReqCallback(options.bigint); 1073 req.oncomplete = callback; 1074 binding.lstat(pathModule.toNamespacedPath(path), options.bigint, req); 1075} 1076 1077function stat(path, options = { bigint: false }, callback) { 1078 if (typeof options === 'function') { 1079 callback = options; 1080 options = {}; 1081 } 1082 callback = makeStatsCallback(callback); 1083 path = getValidatedPath(path); 1084 1085 const req = new FSReqCallback(options.bigint); 1086 req.oncomplete = callback; 1087 binding.stat(pathModule.toNamespacedPath(path), options.bigint, req); 1088} 1089 1090function hasNoEntryError(ctx) { 1091 if (ctx.errno) { 1092 const uvErr = uvErrmapGet(ctx.errno); 1093 return uvErr && uvErr[0] === 'ENOENT'; 1094 } 1095 1096 if (ctx.error) { 1097 return ctx.error.code === 'ENOENT'; 1098 } 1099 1100 return false; 1101} 1102 1103function fstatSync(fd, options = { bigint: false, throwIfNoEntry: true }) { 1104 validateInt32(fd, 'fd', 0); 1105 const ctx = { fd }; 1106 const stats = binding.fstat(fd, options.bigint, undefined, ctx); 1107 handleErrorFromBinding(ctx); 1108 return getStatsFromBinding(stats); 1109} 1110 1111function lstatSync(path, options = { bigint: false, throwIfNoEntry: true }) { 1112 path = getValidatedPath(path); 1113 const ctx = { path }; 1114 const stats = binding.lstat(pathModule.toNamespacedPath(path), 1115 options.bigint, undefined, ctx); 1116 if (options.throwIfNoEntry === false && hasNoEntryError(ctx)) { 1117 return undefined; 1118 } 1119 handleErrorFromBinding(ctx); 1120 return getStatsFromBinding(stats); 1121} 1122 1123function statSync(path, options = { bigint: false, throwIfNoEntry: true }) { 1124 path = getValidatedPath(path); 1125 const ctx = { path }; 1126 const stats = binding.stat(pathModule.toNamespacedPath(path), 1127 options.bigint, undefined, ctx); 1128 if (options.throwIfNoEntry === false && hasNoEntryError(ctx)) { 1129 return undefined; 1130 } 1131 handleErrorFromBinding(ctx); 1132 return getStatsFromBinding(stats); 1133} 1134 1135function readlink(path, options, callback) { 1136 callback = makeCallback(typeof options === 'function' ? options : callback); 1137 options = getOptions(options, {}); 1138 path = getValidatedPath(path, 'oldPath'); 1139 const req = new FSReqCallback(); 1140 req.oncomplete = callback; 1141 binding.readlink(pathModule.toNamespacedPath(path), options.encoding, req); 1142} 1143 1144function readlinkSync(path, options) { 1145 options = getOptions(options, {}); 1146 path = getValidatedPath(path, 'oldPath'); 1147 const ctx = { path }; 1148 const result = binding.readlink(pathModule.toNamespacedPath(path), 1149 options.encoding, undefined, ctx); 1150 handleErrorFromBinding(ctx); 1151 return result; 1152} 1153 1154function symlink(target, path, type_, callback_) { 1155 const type = (typeof type_ === 'string' ? type_ : null); 1156 const callback = makeCallback(arguments[arguments.length - 1]); 1157 1158 target = getValidatedPath(target, 'target'); 1159 path = getValidatedPath(path); 1160 1161 if (isWindows && type === null) { 1162 let absoluteTarget; 1163 try { 1164 // Symlinks targets can be relative to the newly created path. 1165 // Calculate absolute file name of the symlink target, and check 1166 // if it is a directory. Ignore resolve error to keep symlink 1167 // errors consistent between platforms if invalid path is 1168 // provided. 1169 absoluteTarget = pathModule.resolve(path, '..', target); 1170 } catch { } 1171 if (absoluteTarget !== undefined) { 1172 stat(absoluteTarget, (err, stat) => { 1173 const resolvedType = !err && stat.isDirectory() ? 'dir' : 'file'; 1174 const resolvedFlags = stringToSymlinkType(resolvedType); 1175 const destination = preprocessSymlinkDestination(target, 1176 resolvedType, 1177 path); 1178 1179 const req = new FSReqCallback(); 1180 req.oncomplete = callback; 1181 binding.symlink(destination, 1182 pathModule.toNamespacedPath(path), resolvedFlags, req); 1183 }); 1184 return; 1185 } 1186 } 1187 1188 const destination = preprocessSymlinkDestination(target, type, path); 1189 1190 const flags = stringToSymlinkType(type); 1191 const req = new FSReqCallback(); 1192 req.oncomplete = callback; 1193 binding.symlink(destination, pathModule.toNamespacedPath(path), flags, req); 1194} 1195 1196function symlinkSync(target, path, type) { 1197 type = (typeof type === 'string' ? type : null); 1198 if (isWindows && type === null) { 1199 try { 1200 const absoluteTarget = pathModule.resolve(path, '..', target); 1201 if (statSync(absoluteTarget).isDirectory()) { 1202 type = 'dir'; 1203 } 1204 } catch { } 1205 } 1206 target = getValidatedPath(target, 'target'); 1207 path = getValidatedPath(path); 1208 const flags = stringToSymlinkType(type); 1209 1210 const ctx = { path: target, dest: path }; 1211 binding.symlink(preprocessSymlinkDestination(target, type, path), 1212 pathModule.toNamespacedPath(path), flags, undefined, ctx); 1213 1214 handleErrorFromBinding(ctx); 1215} 1216 1217function link(existingPath, newPath, callback) { 1218 callback = makeCallback(callback); 1219 1220 existingPath = getValidatedPath(existingPath, 'existingPath'); 1221 newPath = getValidatedPath(newPath, 'newPath'); 1222 1223 const req = new FSReqCallback(); 1224 req.oncomplete = callback; 1225 1226 binding.link(pathModule.toNamespacedPath(existingPath), 1227 pathModule.toNamespacedPath(newPath), 1228 req); 1229} 1230 1231function linkSync(existingPath, newPath) { 1232 existingPath = getValidatedPath(existingPath, 'existingPath'); 1233 newPath = getValidatedPath(newPath, 'newPath'); 1234 1235 const ctx = { path: existingPath, dest: newPath }; 1236 const result = binding.link(pathModule.toNamespacedPath(existingPath), 1237 pathModule.toNamespacedPath(newPath), 1238 undefined, ctx); 1239 handleErrorFromBinding(ctx); 1240 return result; 1241} 1242 1243function unlink(path, callback) { 1244 callback = makeCallback(callback); 1245 path = getValidatedPath(path); 1246 const req = new FSReqCallback(); 1247 req.oncomplete = callback; 1248 binding.unlink(pathModule.toNamespacedPath(path), req); 1249} 1250 1251function unlinkSync(path) { 1252 path = getValidatedPath(path); 1253 const ctx = { path }; 1254 binding.unlink(pathModule.toNamespacedPath(path), undefined, ctx); 1255 handleErrorFromBinding(ctx); 1256} 1257 1258function fchmod(fd, mode, callback) { 1259 validateInt32(fd, 'fd', 0); 1260 mode = parseFileMode(mode, 'mode'); 1261 callback = makeCallback(callback); 1262 1263 const req = new FSReqCallback(); 1264 req.oncomplete = callback; 1265 binding.fchmod(fd, mode, req); 1266} 1267 1268function fchmodSync(fd, mode) { 1269 validateInt32(fd, 'fd', 0); 1270 mode = parseFileMode(mode, 'mode'); 1271 const ctx = {}; 1272 binding.fchmod(fd, mode, undefined, ctx); 1273 handleErrorFromBinding(ctx); 1274} 1275 1276function lchmod(path, mode, callback) { 1277 callback = maybeCallback(callback); 1278 fs.open(path, O_WRONLY | O_SYMLINK, (err, fd) => { 1279 if (err) { 1280 callback(err); 1281 return; 1282 } 1283 // Prefer to return the chmod error, if one occurs, 1284 // but still try to close, and report closing errors if they occur. 1285 fs.fchmod(fd, mode, (err) => { 1286 fs.close(fd, (err2) => { 1287 callback(err || err2); 1288 }); 1289 }); 1290 }); 1291} 1292 1293function lchmodSync(path, mode) { 1294 const fd = fs.openSync(path, O_WRONLY | O_SYMLINK); 1295 1296 // Prefer to return the chmod error, if one occurs, 1297 // but still try to close, and report closing errors if they occur. 1298 let ret; 1299 try { 1300 ret = fs.fchmodSync(fd, mode); 1301 } finally { 1302 fs.closeSync(fd); 1303 } 1304 return ret; 1305} 1306 1307 1308function chmod(path, mode, callback) { 1309 path = getValidatedPath(path); 1310 mode = parseFileMode(mode, 'mode'); 1311 callback = makeCallback(callback); 1312 1313 const req = new FSReqCallback(); 1314 req.oncomplete = callback; 1315 binding.chmod(pathModule.toNamespacedPath(path), mode, req); 1316} 1317 1318function chmodSync(path, mode) { 1319 path = getValidatedPath(path); 1320 mode = parseFileMode(mode, 'mode'); 1321 1322 const ctx = { path }; 1323 binding.chmod(pathModule.toNamespacedPath(path), mode, undefined, ctx); 1324 handleErrorFromBinding(ctx); 1325} 1326 1327function lchown(path, uid, gid, callback) { 1328 callback = makeCallback(callback); 1329 path = getValidatedPath(path); 1330 validateInteger(uid, 'uid', -1, kMaxUserId); 1331 validateInteger(gid, 'gid', -1, kMaxUserId); 1332 const req = new FSReqCallback(); 1333 req.oncomplete = callback; 1334 binding.lchown(pathModule.toNamespacedPath(path), uid, gid, req); 1335} 1336 1337function lchownSync(path, uid, gid) { 1338 path = getValidatedPath(path); 1339 validateInteger(uid, 'uid', -1, kMaxUserId); 1340 validateInteger(gid, 'gid', -1, kMaxUserId); 1341 const ctx = { path }; 1342 binding.lchown(pathModule.toNamespacedPath(path), uid, gid, undefined, ctx); 1343 handleErrorFromBinding(ctx); 1344} 1345 1346function fchown(fd, uid, gid, callback) { 1347 validateInt32(fd, 'fd', 0); 1348 validateInteger(uid, 'uid', -1, kMaxUserId); 1349 validateInteger(gid, 'gid', -1, kMaxUserId); 1350 callback = makeCallback(callback); 1351 1352 const req = new FSReqCallback(); 1353 req.oncomplete = callback; 1354 binding.fchown(fd, uid, gid, req); 1355} 1356 1357function fchownSync(fd, uid, gid) { 1358 validateInt32(fd, 'fd', 0); 1359 validateInteger(uid, 'uid', -1, kMaxUserId); 1360 validateInteger(gid, 'gid', -1, kMaxUserId); 1361 1362 const ctx = {}; 1363 binding.fchown(fd, uid, gid, undefined, ctx); 1364 handleErrorFromBinding(ctx); 1365} 1366 1367function chown(path, uid, gid, callback) { 1368 callback = makeCallback(callback); 1369 path = getValidatedPath(path); 1370 validateInteger(uid, 'uid', -1, kMaxUserId); 1371 validateInteger(gid, 'gid', -1, kMaxUserId); 1372 1373 const req = new FSReqCallback(); 1374 req.oncomplete = callback; 1375 binding.chown(pathModule.toNamespacedPath(path), uid, gid, req); 1376} 1377 1378function chownSync(path, uid, gid) { 1379 path = getValidatedPath(path); 1380 validateInteger(uid, 'uid', -1, kMaxUserId); 1381 validateInteger(gid, 'gid', -1, kMaxUserId); 1382 const ctx = { path }; 1383 binding.chown(pathModule.toNamespacedPath(path), uid, gid, undefined, ctx); 1384 handleErrorFromBinding(ctx); 1385} 1386 1387function utimes(path, atime, mtime, callback) { 1388 callback = makeCallback(callback); 1389 path = getValidatedPath(path); 1390 1391 const req = new FSReqCallback(); 1392 req.oncomplete = callback; 1393 binding.utimes(pathModule.toNamespacedPath(path), 1394 toUnixTimestamp(atime), 1395 toUnixTimestamp(mtime), 1396 req); 1397} 1398 1399function utimesSync(path, atime, mtime) { 1400 path = getValidatedPath(path); 1401 const ctx = { path }; 1402 binding.utimes(pathModule.toNamespacedPath(path), 1403 toUnixTimestamp(atime), toUnixTimestamp(mtime), 1404 undefined, ctx); 1405 handleErrorFromBinding(ctx); 1406} 1407 1408function futimes(fd, atime, mtime, callback) { 1409 validateInt32(fd, 'fd', 0); 1410 atime = toUnixTimestamp(atime, 'atime'); 1411 mtime = toUnixTimestamp(mtime, 'mtime'); 1412 callback = makeCallback(callback); 1413 1414 const req = new FSReqCallback(); 1415 req.oncomplete = callback; 1416 binding.futimes(fd, atime, mtime, req); 1417} 1418 1419function futimesSync(fd, atime, mtime) { 1420 validateInt32(fd, 'fd', 0); 1421 atime = toUnixTimestamp(atime, 'atime'); 1422 mtime = toUnixTimestamp(mtime, 'mtime'); 1423 const ctx = {}; 1424 binding.futimes(fd, atime, mtime, undefined, ctx); 1425 handleErrorFromBinding(ctx); 1426} 1427 1428function lutimes(path, atime, mtime, callback) { 1429 callback = makeCallback(callback); 1430 path = getValidatedPath(path); 1431 1432 const req = new FSReqCallback(); 1433 req.oncomplete = callback; 1434 binding.lutimes(pathModule.toNamespacedPath(path), 1435 toUnixTimestamp(atime), 1436 toUnixTimestamp(mtime), 1437 req); 1438} 1439 1440function lutimesSync(path, atime, mtime) { 1441 path = getValidatedPath(path); 1442 const ctx = { path }; 1443 binding.lutimes(pathModule.toNamespacedPath(path), 1444 toUnixTimestamp(atime), 1445 toUnixTimestamp(mtime), 1446 undefined, ctx); 1447 handleErrorFromBinding(ctx); 1448} 1449 1450function writeAll(fd, isUserFd, buffer, offset, length, signal, callback) { 1451 if (signal?.aborted) { 1452 if (isUserFd) { 1453 callback(lazyDOMException('The operation was aborted', 'AbortError')); 1454 } else { 1455 fs.close(fd, function() { 1456 callback(lazyDOMException('The operation was aborted', 'AbortError')); 1457 }); 1458 } 1459 return; 1460 } 1461 // write(fd, buffer, offset, length, position, callback) 1462 fs.write(fd, buffer, offset, length, null, (writeErr, written) => { 1463 if (writeErr) { 1464 if (isUserFd) { 1465 callback(writeErr); 1466 } else { 1467 fs.close(fd, function close() { 1468 callback(writeErr); 1469 }); 1470 } 1471 } else if (written === length) { 1472 if (isUserFd) { 1473 callback(null); 1474 } else { 1475 fs.close(fd, callback); 1476 } 1477 } else { 1478 offset += written; 1479 length -= written; 1480 writeAll(fd, isUserFd, buffer, offset, length, signal, callback); 1481 } 1482 }); 1483} 1484 1485function writeFile(path, data, options, callback) { 1486 callback = maybeCallback(callback || options); 1487 options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'w' }); 1488 const flag = options.flag || 'w'; 1489 1490 if (!isArrayBufferView(data)) { 1491 validateStringAfterArrayBufferView(data, 'data'); 1492 data = Buffer.from(String(data), options.encoding || 'utf8'); 1493 } 1494 1495 if (isFd(path)) { 1496 const isUserFd = true; 1497 const signal = options.signal; 1498 writeAll(path, isUserFd, data, 0, data.byteLength, signal, callback); 1499 return; 1500 } 1501 1502 if (options.signal?.aborted) { 1503 callback(lazyDOMException('The operation was aborted', 'AbortError')); 1504 return; 1505 } 1506 fs.open(path, flag, options.mode, (openErr, fd) => { 1507 if (openErr) { 1508 callback(openErr); 1509 } else { 1510 const isUserFd = false; 1511 const signal = options.signal; 1512 writeAll(fd, isUserFd, data, 0, data.byteLength, signal, callback); 1513 } 1514 }); 1515} 1516 1517function writeFileSync(path, data, options) { 1518 options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'w' }); 1519 1520 if (!isArrayBufferView(data)) { 1521 validateStringAfterArrayBufferView(data, 'data'); 1522 data = Buffer.from(String(data), options.encoding || 'utf8'); 1523 } 1524 1525 const flag = options.flag || 'w'; 1526 1527 const isUserFd = isFd(path); // File descriptor ownership 1528 const fd = isUserFd ? path : fs.openSync(path, flag, options.mode); 1529 1530 let offset = 0; 1531 let length = data.byteLength; 1532 try { 1533 while (length > 0) { 1534 const written = fs.writeSync(fd, data, offset, length); 1535 offset += written; 1536 length -= written; 1537 } 1538 } finally { 1539 if (!isUserFd) fs.closeSync(fd); 1540 } 1541} 1542 1543function appendFile(path, data, options, callback) { 1544 callback = maybeCallback(callback || options); 1545 options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'a' }); 1546 1547 // Don't make changes directly on options object 1548 options = copyObject(options); 1549 1550 // Force append behavior when using a supplied file descriptor 1551 if (!options.flag || isFd(path)) 1552 options.flag = 'a'; 1553 1554 fs.writeFile(path, data, options, callback); 1555} 1556 1557function appendFileSync(path, data, options) { 1558 options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'a' }); 1559 1560 // Don't make changes directly on options object 1561 options = copyObject(options); 1562 1563 // Force append behavior when using a supplied file descriptor 1564 if (!options.flag || isFd(path)) 1565 options.flag = 'a'; 1566 1567 fs.writeFileSync(path, data, options); 1568} 1569 1570function watch(filename, options, listener) { 1571 if (typeof options === 'function') { 1572 listener = options; 1573 } 1574 options = getOptions(options, {}); 1575 1576 // Don't make changes directly on options object 1577 options = copyObject(options); 1578 1579 if (options.persistent === undefined) options.persistent = true; 1580 if (options.recursive === undefined) options.recursive = false; 1581 if (options.recursive && !(isOSX || isWindows)) 1582 throw new ERR_FEATURE_UNAVAILABLE_ON_PLATFORM('watch recursively'); 1583 if (!watchers) 1584 watchers = require('internal/fs/watchers'); 1585 const watcher = new watchers.FSWatcher(); 1586 watcher[watchers.kFSWatchStart](filename, 1587 options.persistent, 1588 options.recursive, 1589 options.encoding); 1590 1591 if (listener) { 1592 watcher.addListener('change', listener); 1593 } 1594 if (options.signal) { 1595 if (options.signal.aborted) { 1596 process.nextTick(() => watcher.close()); 1597 } else { 1598 const listener = () => watcher.close(); 1599 options.signal.addEventListener('abort', listener); 1600 watcher.once('close', () => { 1601 options.signal.removeEventListener('abort', listener); 1602 }); 1603 } 1604 } 1605 1606 return watcher; 1607} 1608 1609 1610const statWatchers = new Map(); 1611 1612function watchFile(filename, options, listener) { 1613 filename = getValidatedPath(filename); 1614 filename = pathModule.resolve(filename); 1615 let stat; 1616 1617 if (options === null || typeof options !== 'object') { 1618 listener = options; 1619 options = null; 1620 } 1621 1622 options = { 1623 // Poll interval in milliseconds. 5007 is what libev used to use. It's 1624 // a little on the slow side but let's stick with it for now to keep 1625 // behavioral changes to a minimum. 1626 interval: 5007, 1627 persistent: true, 1628 ...options 1629 }; 1630 1631 if (typeof listener !== 'function') { 1632 throw new ERR_INVALID_ARG_TYPE('listener', 'Function', listener); 1633 } 1634 1635 stat = statWatchers.get(filename); 1636 1637 if (stat === undefined) { 1638 if (!watchers) 1639 watchers = require('internal/fs/watchers'); 1640 stat = new watchers.StatWatcher(options.bigint); 1641 stat[watchers.kFSStatWatcherStart](filename, 1642 options.persistent, options.interval); 1643 statWatchers.set(filename, stat); 1644 } else { 1645 stat[watchers.kFSStatWatcherAddOrCleanRef]('add'); 1646 } 1647 1648 stat.addListener('change', listener); 1649 return stat; 1650} 1651 1652function unwatchFile(filename, listener) { 1653 filename = getValidatedPath(filename); 1654 filename = pathModule.resolve(filename); 1655 const stat = statWatchers.get(filename); 1656 1657 if (stat === undefined) return; 1658 1659 if (typeof listener === 'function') { 1660 const beforeListenerCount = stat.listenerCount('change'); 1661 stat.removeListener('change', listener); 1662 if (stat.listenerCount('change') < beforeListenerCount) 1663 stat[watchers.kFSStatWatcherAddOrCleanRef]('clean'); 1664 } else { 1665 stat.removeAllListeners('change'); 1666 stat[watchers.kFSStatWatcherAddOrCleanRef]('cleanAll'); 1667 } 1668 1669 if (stat.listenerCount('change') === 0) { 1670 stat.stop(); 1671 statWatchers.delete(filename); 1672 } 1673} 1674 1675 1676let splitRoot; 1677if (isWindows) { 1678 // Regex to find the device root on Windows (e.g. 'c:\\'), including trailing 1679 // slash. 1680 const splitRootRe = /^(?:[a-zA-Z]:|[\\/]{2}[^\\/]+[\\/][^\\/]+)?[\\/]*/; 1681 splitRoot = function splitRoot(str) { 1682 return splitRootRe.exec(str)[0]; 1683 }; 1684} else { 1685 splitRoot = function splitRoot(str) { 1686 for (let i = 0; i < str.length; ++i) { 1687 if (str.charCodeAt(i) !== CHAR_FORWARD_SLASH) 1688 return str.slice(0, i); 1689 } 1690 return str; 1691 }; 1692} 1693 1694function encodeRealpathResult(result, options) { 1695 if (!options || !options.encoding || options.encoding === 'utf8') 1696 return result; 1697 const asBuffer = Buffer.from(result); 1698 if (options.encoding === 'buffer') { 1699 return asBuffer; 1700 } 1701 return asBuffer.toString(options.encoding); 1702} 1703 1704// Finds the next portion of a (partial) path, up to the next path delimiter 1705let nextPart; 1706if (isWindows) { 1707 nextPart = function nextPart(p, i) { 1708 for (; i < p.length; ++i) { 1709 const ch = p.charCodeAt(i); 1710 1711 // Check for a separator character 1712 if (ch === CHAR_BACKWARD_SLASH || ch === CHAR_FORWARD_SLASH) 1713 return i; 1714 } 1715 return -1; 1716 }; 1717} else { 1718 nextPart = function nextPart(p, i) { return p.indexOf('/', i); }; 1719} 1720 1721const emptyObj = ObjectCreate(null); 1722function realpathSync(p, options) { 1723 options = getOptions(options, emptyObj); 1724 p = toPathIfFileURL(p); 1725 if (typeof p !== 'string') { 1726 p += ''; 1727 } 1728 validatePath(p); 1729 p = pathModule.resolve(p); 1730 1731 const cache = options[realpathCacheKey]; 1732 const maybeCachedResult = cache && cache.get(p); 1733 if (maybeCachedResult) { 1734 return maybeCachedResult; 1735 } 1736 1737 const seenLinks = ObjectCreate(null); 1738 const knownHard = ObjectCreate(null); 1739 const original = p; 1740 1741 // Current character position in p 1742 let pos; 1743 // The partial path so far, including a trailing slash if any 1744 let current; 1745 // The partial path without a trailing slash (except when pointing at a root) 1746 let base; 1747 // The partial path scanned in the previous round, with slash 1748 let previous; 1749 1750 // Skip over roots 1751 current = base = splitRoot(p); 1752 pos = current.length; 1753 1754 // On windows, check that the root exists. On unix there is no need. 1755 if (isWindows) { 1756 const ctx = { path: base }; 1757 binding.lstat(pathModule.toNamespacedPath(base), false, undefined, ctx); 1758 handleErrorFromBinding(ctx); 1759 knownHard[base] = true; 1760 } 1761 1762 // Walk down the path, swapping out linked path parts for their real 1763 // values 1764 // NB: p.length changes. 1765 while (pos < p.length) { 1766 // find the next part 1767 const result = nextPart(p, pos); 1768 previous = current; 1769 if (result === -1) { 1770 const last = p.slice(pos); 1771 current += last; 1772 base = previous + last; 1773 pos = p.length; 1774 } else { 1775 current += p.slice(pos, result + 1); 1776 base = previous + p.slice(pos, result); 1777 pos = result + 1; 1778 } 1779 1780 // Continue if not a symlink, break if a pipe/socket 1781 if (knownHard[base] || (cache && cache.get(base) === base)) { 1782 if (isFileType(statValues, S_IFIFO) || 1783 isFileType(statValues, S_IFSOCK)) { 1784 break; 1785 } 1786 continue; 1787 } 1788 1789 let resolvedLink; 1790 const maybeCachedResolved = cache && cache.get(base); 1791 if (maybeCachedResolved) { 1792 resolvedLink = maybeCachedResolved; 1793 } else { 1794 // Use stats array directly to avoid creating an fs.Stats instance just 1795 // for our internal use. 1796 1797 const baseLong = pathModule.toNamespacedPath(base); 1798 const ctx = { path: base }; 1799 const stats = binding.lstat(baseLong, true, undefined, ctx); 1800 handleErrorFromBinding(ctx); 1801 1802 if (!isFileType(stats, S_IFLNK)) { 1803 knownHard[base] = true; 1804 if (cache) cache.set(base, base); 1805 continue; 1806 } 1807 1808 // Read the link if it wasn't read before 1809 // dev/ino always return 0 on windows, so skip the check. 1810 let linkTarget = null; 1811 let id; 1812 if (!isWindows) { 1813 const dev = stats[0].toString(32); 1814 const ino = stats[7].toString(32); 1815 id = `${dev}:${ino}`; 1816 if (seenLinks[id]) { 1817 linkTarget = seenLinks[id]; 1818 } 1819 } 1820 if (linkTarget === null) { 1821 const ctx = { path: base }; 1822 binding.stat(baseLong, false, undefined, ctx); 1823 handleErrorFromBinding(ctx); 1824 linkTarget = binding.readlink(baseLong, undefined, undefined, ctx); 1825 handleErrorFromBinding(ctx); 1826 } 1827 resolvedLink = pathModule.resolve(previous, linkTarget); 1828 1829 if (cache) cache.set(base, resolvedLink); 1830 if (!isWindows) seenLinks[id] = linkTarget; 1831 } 1832 1833 // Resolve the link, then start over 1834 p = pathModule.resolve(resolvedLink, p.slice(pos)); 1835 1836 // Skip over roots 1837 current = base = splitRoot(p); 1838 pos = current.length; 1839 1840 // On windows, check that the root exists. On unix there is no need. 1841 if (isWindows && !knownHard[base]) { 1842 const ctx = { path: base }; 1843 binding.lstat(pathModule.toNamespacedPath(base), false, undefined, ctx); 1844 handleErrorFromBinding(ctx); 1845 knownHard[base] = true; 1846 } 1847 } 1848 1849 if (cache) cache.set(original, p); 1850 return encodeRealpathResult(p, options); 1851} 1852 1853 1854realpathSync.native = (path, options) => { 1855 options = getOptions(options, {}); 1856 path = getValidatedPath(path); 1857 const ctx = { path }; 1858 const result = binding.realpath(path, options.encoding, undefined, ctx); 1859 handleErrorFromBinding(ctx); 1860 return result; 1861}; 1862 1863 1864function realpath(p, options, callback) { 1865 callback = typeof options === 'function' ? options : maybeCallback(callback); 1866 options = getOptions(options, {}); 1867 p = toPathIfFileURL(p); 1868 1869 if (typeof p !== 'string') { 1870 p += ''; 1871 } 1872 validatePath(p); 1873 p = pathModule.resolve(p); 1874 1875 const seenLinks = ObjectCreate(null); 1876 const knownHard = ObjectCreate(null); 1877 1878 // Current character position in p 1879 let pos; 1880 // The partial path so far, including a trailing slash if any 1881 let current; 1882 // The partial path without a trailing slash (except when pointing at a root) 1883 let base; 1884 // The partial path scanned in the previous round, with slash 1885 let previous; 1886 1887 current = base = splitRoot(p); 1888 pos = current.length; 1889 1890 // On windows, check that the root exists. On unix there is no need. 1891 if (isWindows && !knownHard[base]) { 1892 fs.lstat(base, (err, stats) => { 1893 if (err) return callback(err); 1894 knownHard[base] = true; 1895 LOOP(); 1896 }); 1897 } else { 1898 process.nextTick(LOOP); 1899 } 1900 1901 // Walk down the path, swapping out linked path parts for their real 1902 // values 1903 function LOOP() { 1904 // Stop if scanned past end of path 1905 if (pos >= p.length) { 1906 return callback(null, encodeRealpathResult(p, options)); 1907 } 1908 1909 // find the next part 1910 const result = nextPart(p, pos); 1911 previous = current; 1912 if (result === -1) { 1913 const last = p.slice(pos); 1914 current += last; 1915 base = previous + last; 1916 pos = p.length; 1917 } else { 1918 current += p.slice(pos, result + 1); 1919 base = previous + p.slice(pos, result); 1920 pos = result + 1; 1921 } 1922 1923 // Continue if not a symlink, break if a pipe/socket 1924 if (knownHard[base]) { 1925 if (isFileType(statValues, S_IFIFO) || 1926 isFileType(statValues, S_IFSOCK)) { 1927 return callback(null, encodeRealpathResult(p, options)); 1928 } 1929 return process.nextTick(LOOP); 1930 } 1931 1932 return fs.lstat(base, { bigint: true }, gotStat); 1933 } 1934 1935 function gotStat(err, stats) { 1936 if (err) return callback(err); 1937 1938 // If not a symlink, skip to the next path part 1939 if (!stats.isSymbolicLink()) { 1940 knownHard[base] = true; 1941 return process.nextTick(LOOP); 1942 } 1943 1944 // Stat & read the link if not read before. 1945 // Call `gotTarget()` as soon as the link target is known. 1946 // `dev`/`ino` always return 0 on windows, so skip the check. 1947 let id; 1948 if (!isWindows) { 1949 const dev = stats.dev.toString(32); 1950 const ino = stats.ino.toString(32); 1951 id = `${dev}:${ino}`; 1952 if (seenLinks[id]) { 1953 return gotTarget(null, seenLinks[id]); 1954 } 1955 } 1956 fs.stat(base, (err) => { 1957 if (err) return callback(err); 1958 1959 fs.readlink(base, (err, target) => { 1960 if (!isWindows) seenLinks[id] = target; 1961 gotTarget(err, target); 1962 }); 1963 }); 1964 } 1965 1966 function gotTarget(err, target) { 1967 if (err) return callback(err); 1968 1969 gotResolvedLink(pathModule.resolve(previous, target)); 1970 } 1971 1972 function gotResolvedLink(resolvedLink) { 1973 // Resolve the link, then start over 1974 p = pathModule.resolve(resolvedLink, p.slice(pos)); 1975 current = base = splitRoot(p); 1976 pos = current.length; 1977 1978 // On windows, check that the root exists. On unix there is no need. 1979 if (isWindows && !knownHard[base]) { 1980 fs.lstat(base, (err) => { 1981 if (err) return callback(err); 1982 knownHard[base] = true; 1983 LOOP(); 1984 }); 1985 } else { 1986 process.nextTick(LOOP); 1987 } 1988 } 1989} 1990 1991 1992realpath.native = (path, options, callback) => { 1993 callback = makeCallback(callback || options); 1994 options = getOptions(options, {}); 1995 path = getValidatedPath(path); 1996 const req = new FSReqCallback(); 1997 req.oncomplete = callback; 1998 return binding.realpath(path, options.encoding, req); 1999}; 2000 2001function mkdtemp(prefix, options, callback) { 2002 callback = makeCallback(typeof options === 'function' ? options : callback); 2003 options = getOptions(options, {}); 2004 2005 validateString(prefix, 'prefix'); 2006 nullCheck(prefix, 'prefix'); 2007 warnOnNonPortableTemplate(prefix); 2008 const req = new FSReqCallback(); 2009 req.oncomplete = callback; 2010 binding.mkdtemp(`${prefix}XXXXXX`, options.encoding, req); 2011} 2012 2013 2014function mkdtempSync(prefix, options) { 2015 options = getOptions(options, {}); 2016 2017 validateString(prefix, 'prefix'); 2018 nullCheck(prefix, 'prefix'); 2019 warnOnNonPortableTemplate(prefix); 2020 const path = `${prefix}XXXXXX`; 2021 const ctx = { path }; 2022 const result = binding.mkdtemp(path, options.encoding, 2023 undefined, ctx); 2024 handleErrorFromBinding(ctx); 2025 return result; 2026} 2027 2028 2029function copyFile(src, dest, mode, callback) { 2030 if (typeof mode === 'function') { 2031 callback = mode; 2032 mode = 0; 2033 } else if (typeof callback !== 'function') { 2034 throw new ERR_INVALID_CALLBACK(callback); 2035 } 2036 2037 src = getValidatedPath(src, 'src'); 2038 dest = getValidatedPath(dest, 'dest'); 2039 2040 src = pathModule._makeLong(src); 2041 dest = pathModule._makeLong(dest); 2042 mode = getValidMode(mode, 'copyFile'); 2043 callback = makeCallback(callback); 2044 2045 const req = new FSReqCallback(); 2046 req.oncomplete = callback; 2047 binding.copyFile(src, dest, mode, req); 2048} 2049 2050 2051function copyFileSync(src, dest, mode) { 2052 src = getValidatedPath(src, 'src'); 2053 dest = getValidatedPath(dest, 'dest'); 2054 2055 const ctx = { path: src, dest }; // non-prefixed 2056 2057 src = pathModule._makeLong(src); 2058 dest = pathModule._makeLong(dest); 2059 mode = getValidMode(mode, 'copyFile'); 2060 binding.copyFile(src, dest, mode, undefined, ctx); 2061 handleErrorFromBinding(ctx); 2062} 2063 2064function lazyLoadStreams() { 2065 if (!ReadStream) { 2066 ({ ReadStream, WriteStream } = require('internal/fs/streams')); 2067 FileReadStream = ReadStream; 2068 FileWriteStream = WriteStream; 2069 } 2070} 2071 2072function createReadStream(path, options) { 2073 lazyLoadStreams(); 2074 return new ReadStream(path, options); 2075} 2076 2077function createWriteStream(path, options) { 2078 lazyLoadStreams(); 2079 return new WriteStream(path, options); 2080} 2081 2082module.exports = fs = { 2083 appendFile, 2084 appendFileSync, 2085 access, 2086 accessSync, 2087 chown, 2088 chownSync, 2089 chmod, 2090 chmodSync, 2091 close, 2092 closeSync, 2093 copyFile, 2094 copyFileSync, 2095 createReadStream, 2096 createWriteStream, 2097 exists, 2098 existsSync, 2099 fchown, 2100 fchownSync, 2101 fchmod, 2102 fchmodSync, 2103 fdatasync, 2104 fdatasyncSync, 2105 fstat, 2106 fstatSync, 2107 fsync, 2108 fsyncSync, 2109 ftruncate, 2110 ftruncateSync, 2111 futimes, 2112 futimesSync, 2113 lchown, 2114 lchownSync, 2115 lchmod: constants.O_SYMLINK !== undefined ? lchmod : undefined, 2116 lchmodSync: constants.O_SYMLINK !== undefined ? lchmodSync : undefined, 2117 link, 2118 linkSync, 2119 lstat, 2120 lstatSync, 2121 lutimes, 2122 lutimesSync, 2123 mkdir, 2124 mkdirSync, 2125 mkdtemp, 2126 mkdtempSync, 2127 open, 2128 openSync, 2129 opendir, 2130 opendirSync, 2131 readdir, 2132 readdirSync, 2133 read, 2134 readSync, 2135 readv, 2136 readvSync, 2137 readFile, 2138 readFileSync, 2139 readlink, 2140 readlinkSync, 2141 realpath, 2142 realpathSync, 2143 rename, 2144 renameSync, 2145 rm, 2146 rmSync, 2147 rmdir, 2148 rmdirSync, 2149 stat, 2150 statSync, 2151 symlink, 2152 symlinkSync, 2153 truncate, 2154 truncateSync, 2155 unwatchFile, 2156 unlink, 2157 unlinkSync, 2158 utimes, 2159 utimesSync, 2160 watch, 2161 watchFile, 2162 writeFile, 2163 writeFileSync, 2164 write, 2165 writeSync, 2166 writev, 2167 writevSync, 2168 Dir, 2169 Dirent, 2170 Stats, 2171 2172 get ReadStream() { 2173 lazyLoadStreams(); 2174 return ReadStream; 2175 }, 2176 2177 set ReadStream(val) { 2178 ReadStream = val; 2179 }, 2180 2181 get WriteStream() { 2182 lazyLoadStreams(); 2183 return WriteStream; 2184 }, 2185 2186 set WriteStream(val) { 2187 WriteStream = val; 2188 }, 2189 2190 // Legacy names... these have to be separate because of how graceful-fs 2191 // (and possibly other) modules monkey patch the values. 2192 get FileReadStream() { 2193 lazyLoadStreams(); 2194 return FileReadStream; 2195 }, 2196 2197 set FileReadStream(val) { 2198 FileReadStream = val; 2199 }, 2200 2201 get FileWriteStream() { 2202 lazyLoadStreams(); 2203 return FileWriteStream; 2204 }, 2205 2206 set FileWriteStream(val) { 2207 FileWriteStream = val; 2208 }, 2209 2210 // For tests 2211 _toUnixTimestamp: toUnixTimestamp 2212}; 2213 2214ObjectDefineProperties(fs, { 2215 F_OK: { enumerable: true, value: F_OK || 0 }, 2216 R_OK: { enumerable: true, value: R_OK || 0 }, 2217 W_OK: { enumerable: true, value: W_OK || 0 }, 2218 X_OK: { enumerable: true, value: X_OK || 0 }, 2219 constants: { 2220 configurable: false, 2221 enumerable: true, 2222 value: constants 2223 }, 2224 promises: { 2225 configurable: true, 2226 enumerable: true, 2227 get() { 2228 if (promises === null) 2229 promises = require('internal/fs/promises').exports; 2230 return promises; 2231 } 2232 } 2233}); 2234