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