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