1'use strict'; 2 3const { 4 ArrayIsArray, 5 BigInt, 6 Date, 7 DateNow, 8 DatePrototypeGetTime, 9 ErrorCaptureStackTrace, 10 FunctionPrototypeCall, 11 Number, 12 NumberIsFinite, 13 MathMin, 14 MathRound, 15 ObjectIs, 16 ObjectPrototypeHasOwnProperty, 17 ObjectSetPrototypeOf, 18 ReflectApply, 19 ReflectOwnKeys, 20 RegExpPrototypeSymbolReplace, 21 StringPrototypeEndsWith, 22 StringPrototypeIncludes, 23 Symbol, 24 TypedArrayPrototypeIncludes, 25} = primordials; 26 27const { Buffer } = require('buffer'); 28const { 29 codes: { 30 ERR_FS_EISDIR, 31 ERR_FS_INVALID_SYMLINK_TYPE, 32 ERR_INCOMPATIBLE_OPTION_PAIR, 33 ERR_INVALID_ARG_TYPE, 34 ERR_INVALID_ARG_VALUE, 35 ERR_OUT_OF_RANGE, 36 }, 37 hideStackFrames, 38 uvException, 39} = require('internal/errors'); 40const { 41 isArrayBufferView, 42 isBigInt64Array, 43 isDate, 44 isUint8Array, 45} = require('internal/util/types'); 46const { 47 kEmptyObject, 48 once, 49} = require('internal/util'); 50const { toPathIfFileURL } = require('internal/url'); 51const { 52 validateAbortSignal, 53 validateBoolean, 54 validateFunction, 55 validateInt32, 56 validateInteger, 57 validateObject, 58 validateUint32, 59} = require('internal/validators'); 60const pathModule = require('path'); 61const kType = Symbol('type'); 62const kStats = Symbol('stats'); 63const assert = require('internal/assert'); 64 65const { 66 fs: { 67 F_OK = 0, 68 W_OK = 0, 69 R_OK = 0, 70 X_OK = 0, 71 COPYFILE_EXCL, 72 COPYFILE_FICLONE, 73 COPYFILE_FICLONE_FORCE, 74 O_APPEND, 75 O_CREAT, 76 O_EXCL, 77 O_RDONLY, 78 O_RDWR, 79 O_SYNC, 80 O_TRUNC, 81 O_WRONLY, 82 S_IFBLK, 83 S_IFCHR, 84 S_IFDIR, 85 S_IFIFO, 86 S_IFLNK, 87 S_IFMT, 88 S_IFREG, 89 S_IFSOCK, 90 UV_FS_SYMLINK_DIR, 91 UV_FS_SYMLINK_JUNCTION, 92 UV_DIRENT_UNKNOWN, 93 UV_DIRENT_FILE, 94 UV_DIRENT_DIR, 95 UV_DIRENT_LINK, 96 UV_DIRENT_FIFO, 97 UV_DIRENT_SOCKET, 98 UV_DIRENT_CHAR, 99 UV_DIRENT_BLOCK, 100 }, 101 os: { 102 errno: { 103 EISDIR, 104 }, 105 }, 106} = internalBinding('constants'); 107 108// The access modes can be any of F_OK, R_OK, W_OK or X_OK. Some might not be 109// available on specific systems. They can be used in combination as well 110// (F_OK | R_OK | W_OK | X_OK). 111const kMinimumAccessMode = MathMin(F_OK, W_OK, R_OK, X_OK); 112const kMaximumAccessMode = F_OK | W_OK | R_OK | X_OK; 113 114const kDefaultCopyMode = 0; 115// The copy modes can be any of COPYFILE_EXCL, COPYFILE_FICLONE or 116// COPYFILE_FICLONE_FORCE. They can be used in combination as well 117// (COPYFILE_EXCL | COPYFILE_FICLONE | COPYFILE_FICLONE_FORCE). 118const kMinimumCopyMode = MathMin( 119 kDefaultCopyMode, 120 COPYFILE_EXCL, 121 COPYFILE_FICLONE, 122 COPYFILE_FICLONE_FORCE, 123); 124const kMaximumCopyMode = COPYFILE_EXCL | 125 COPYFILE_FICLONE | 126 COPYFILE_FICLONE_FORCE; 127 128// Most platforms don't allow reads or writes >= 2 GiB. 129// See https://github.com/libuv/libuv/pull/1501. 130const kIoMaxLength = 2 ** 31 - 1; 131 132// Use 64kb in case the file type is not a regular file and thus do not know the 133// actual file size. Increasing the value further results in more frequent over 134// allocation for small files and consumes CPU time and memory that should be 135// used else wise. 136// Use up to 512kb per read otherwise to partition reading big files to prevent 137// blocking other threads in case the available threads are all in use. 138const kReadFileUnknownBufferLength = 64 * 1024; 139const kReadFileBufferLength = 512 * 1024; 140 141const kWriteFileMaxChunkSize = 512 * 1024; 142 143const kMaxUserId = 2 ** 32 - 1; 144 145const isWindows = process.platform === 'win32'; 146 147let fs; 148function lazyLoadFs() { 149 if (!fs) { 150 fs = require('fs'); 151 } 152 return fs; 153} 154 155function assertEncoding(encoding) { 156 if (encoding && !Buffer.isEncoding(encoding)) { 157 const reason = 'is invalid encoding'; 158 throw new ERR_INVALID_ARG_VALUE(encoding, 'encoding', reason); 159 } 160} 161 162class Dirent { 163 constructor(name, type, path) { 164 this.name = name; 165 this.path = path; 166 this[kType] = type; 167 } 168 169 isDirectory() { 170 return this[kType] === UV_DIRENT_DIR; 171 } 172 173 isFile() { 174 return this[kType] === UV_DIRENT_FILE; 175 } 176 177 isBlockDevice() { 178 return this[kType] === UV_DIRENT_BLOCK; 179 } 180 181 isCharacterDevice() { 182 return this[kType] === UV_DIRENT_CHAR; 183 } 184 185 isSymbolicLink() { 186 return this[kType] === UV_DIRENT_LINK; 187 } 188 189 isFIFO() { 190 return this[kType] === UV_DIRENT_FIFO; 191 } 192 193 isSocket() { 194 return this[kType] === UV_DIRENT_SOCKET; 195 } 196} 197 198class DirentFromStats extends Dirent { 199 constructor(name, stats, path) { 200 super(name, null, path); 201 this[kStats] = stats; 202 } 203} 204 205for (const name of ReflectOwnKeys(Dirent.prototype)) { 206 if (name === 'constructor') { 207 continue; 208 } 209 DirentFromStats.prototype[name] = function() { 210 return this[kStats][name](); 211 }; 212} 213 214function copyObject(source) { 215 const target = {}; 216 for (const key in source) 217 target[key] = source[key]; 218 return target; 219} 220 221const bufferSep = Buffer.from(pathModule.sep); 222 223function join(path, name) { 224 if ((typeof path === 'string' || isUint8Array(path)) && 225 name === undefined) { 226 return path; 227 } 228 229 if (typeof path === 'string' && isUint8Array(name)) { 230 const pathBuffer = Buffer.from(pathModule.join(path, pathModule.sep)); 231 return Buffer.concat([pathBuffer, name]); 232 } 233 234 if (typeof path === 'string' && typeof name === 'string') { 235 return pathModule.basename(path) === name ? path : pathModule.join(path, name); 236 } 237 238 if (isUint8Array(path) && isUint8Array(name)) { 239 return Buffer.concat([path, bufferSep, name]); 240 } 241 242 throw new ERR_INVALID_ARG_TYPE( 243 'path', ['string', 'Buffer'], path); 244} 245 246function getDirents(path, { 0: names, 1: types }, callback) { 247 let i; 248 if (typeof callback === 'function') { 249 const len = names.length; 250 let toFinish = 0; 251 callback = once(callback); 252 for (i = 0; i < len; i++) { 253 const type = types[i]; 254 if (type === UV_DIRENT_UNKNOWN) { 255 const name = names[i]; 256 const idx = i; 257 toFinish++; 258 let filepath; 259 try { 260 filepath = join(path, name); 261 } catch (err) { 262 callback(err); 263 return; 264 } 265 lazyLoadFs().lstat(filepath, (err, stats) => { 266 if (err) { 267 callback(err); 268 return; 269 } 270 names[idx] = new DirentFromStats(name, stats, path); 271 if (--toFinish === 0) { 272 callback(null, names); 273 } 274 }); 275 } else { 276 names[i] = new Dirent(names[i], types[i], path); 277 } 278 } 279 if (toFinish === 0) { 280 callback(null, names); 281 } 282 } else { 283 const len = names.length; 284 for (i = 0; i < len; i++) { 285 names[i] = getDirent(path, names[i], types[i]); 286 } 287 return names; 288 } 289} 290 291function getDirent(path, name, type, callback) { 292 if (typeof callback === 'function') { 293 if (type === UV_DIRENT_UNKNOWN) { 294 let filepath; 295 try { 296 filepath = join(path, name); 297 } catch (err) { 298 callback(err); 299 return; 300 } 301 lazyLoadFs().lstat(filepath, (err, stats) => { 302 if (err) { 303 callback(err); 304 return; 305 } 306 callback(null, new DirentFromStats(name, stats, filepath)); 307 }); 308 } else { 309 callback(null, new Dirent(name, type, path)); 310 } 311 } else if (type === UV_DIRENT_UNKNOWN) { 312 const filepath = join(path, name); 313 const stats = lazyLoadFs().lstatSync(filepath); 314 return new DirentFromStats(name, stats, path); 315 } else { 316 return new Dirent(name, type, path); 317 } 318} 319 320function getOptions(options, defaultOptions = kEmptyObject) { 321 if (options == null || typeof options === 'function') { 322 return defaultOptions; 323 } 324 325 if (typeof options === 'string') { 326 defaultOptions = { ...defaultOptions }; 327 defaultOptions.encoding = options; 328 options = defaultOptions; 329 } else if (typeof options !== 'object') { 330 throw new ERR_INVALID_ARG_TYPE('options', ['string', 'Object'], options); 331 } 332 333 if (options.encoding !== 'buffer') 334 assertEncoding(options.encoding); 335 336 if (options.signal !== undefined) { 337 validateAbortSignal(options.signal, 'options.signal'); 338 } 339 340 return options; 341} 342 343/** 344 * @param {InternalFSBinding.FSSyncContext} ctx 345 */ 346function handleErrorFromBinding(ctx) { 347 if (ctx.errno !== undefined) { // libuv error numbers 348 const err = uvException(ctx); 349 ErrorCaptureStackTrace(err, handleErrorFromBinding); 350 throw err; 351 } 352 if (ctx.error !== undefined) { // Errors created in C++ land. 353 // TODO(joyeecheung): currently, ctx.error are encoding errors 354 // usually caused by memory problems. We need to figure out proper error 355 // code(s) for this. 356 ErrorCaptureStackTrace(ctx.error, handleErrorFromBinding); 357 throw ctx.error; 358 } 359} 360 361// Check if the path contains null types if it is a string nor Uint8Array, 362// otherwise return silently. 363const nullCheck = hideStackFrames((path, propName, throwError = true) => { 364 const pathIsString = typeof path === 'string'; 365 const pathIsUint8Array = isUint8Array(path); 366 367 // We can only perform meaningful checks on strings and Uint8Arrays. 368 if ((!pathIsString && !pathIsUint8Array) || 369 (pathIsString && !StringPrototypeIncludes(path, '\u0000')) || 370 (pathIsUint8Array && !TypedArrayPrototypeIncludes(path, 0))) { 371 return; 372 } 373 374 const err = new ERR_INVALID_ARG_VALUE( 375 propName, 376 path, 377 'must be a string or Uint8Array without null bytes', 378 ); 379 if (throwError) { 380 throw err; 381 } 382 return err; 383}); 384 385function preprocessSymlinkDestination(path, type, linkPath) { 386 if (!isWindows) { 387 // No preprocessing is needed on Unix. 388 return path; 389 } 390 path = '' + path; 391 if (type === 'junction') { 392 // Junctions paths need to be absolute and \\?\-prefixed. 393 // A relative target is relative to the link's parent directory. 394 path = pathModule.resolve(linkPath, '..', path); 395 return pathModule.toNamespacedPath(path); 396 } 397 if (pathModule.isAbsolute(path)) { 398 // If the path is absolute, use the \\?\-prefix to enable long filenames 399 return pathModule.toNamespacedPath(path); 400 } 401 // Windows symlinks don't tolerate forward slashes. 402 return RegExpPrototypeSymbolReplace(/\//g, path, '\\'); 403} 404 405// Constructor for file stats. 406function StatsBase(dev, mode, nlink, uid, gid, rdev, blksize, 407 ino, size, blocks) { 408 this.dev = dev; 409 this.mode = mode; 410 this.nlink = nlink; 411 this.uid = uid; 412 this.gid = gid; 413 this.rdev = rdev; 414 this.blksize = blksize; 415 this.ino = ino; 416 this.size = size; 417 this.blocks = blocks; 418} 419 420StatsBase.prototype.isDirectory = function() { 421 return this._checkModeProperty(S_IFDIR); 422}; 423 424StatsBase.prototype.isFile = function() { 425 return this._checkModeProperty(S_IFREG); 426}; 427 428StatsBase.prototype.isBlockDevice = function() { 429 return this._checkModeProperty(S_IFBLK); 430}; 431 432StatsBase.prototype.isCharacterDevice = function() { 433 return this._checkModeProperty(S_IFCHR); 434}; 435 436StatsBase.prototype.isSymbolicLink = function() { 437 return this._checkModeProperty(S_IFLNK); 438}; 439 440StatsBase.prototype.isFIFO = function() { 441 return this._checkModeProperty(S_IFIFO); 442}; 443 444StatsBase.prototype.isSocket = function() { 445 return this._checkModeProperty(S_IFSOCK); 446}; 447 448const kNsPerMsBigInt = 10n ** 6n; 449const kNsPerSecBigInt = 10n ** 9n; 450const kMsPerSec = 10 ** 3; 451const kNsPerMs = 10 ** 6; 452function msFromTimeSpec(sec, nsec) { 453 return sec * kMsPerSec + nsec / kNsPerMs; 454} 455 456function nsFromTimeSpecBigInt(sec, nsec) { 457 return sec * kNsPerSecBigInt + nsec; 458} 459 460// The Date constructor performs Math.floor() on the absolute value 461// of the timestamp: https://tc39.es/ecma262/#sec-timeclip 462// Since there may be a precision loss when the timestamp is 463// converted to a floating point number, we manually round 464// the timestamp here before passing it to Date(). 465// Refs: https://github.com/nodejs/node/pull/12607 466// Refs: https://github.com/nodejs/node/pull/43714 467function dateFromMs(ms) { 468 // Coercing to number, ms can be bigint 469 return new Date(MathRound(Number(ms))); 470} 471 472function BigIntStats(dev, mode, nlink, uid, gid, rdev, blksize, 473 ino, size, blocks, 474 atimeNs, mtimeNs, ctimeNs, birthtimeNs) { 475 ReflectApply(StatsBase, this, [dev, mode, nlink, uid, gid, rdev, blksize, 476 ino, size, blocks]); 477 478 this.atimeMs = atimeNs / kNsPerMsBigInt; 479 this.mtimeMs = mtimeNs / kNsPerMsBigInt; 480 this.ctimeMs = ctimeNs / kNsPerMsBigInt; 481 this.birthtimeMs = birthtimeNs / kNsPerMsBigInt; 482 this.atimeNs = atimeNs; 483 this.mtimeNs = mtimeNs; 484 this.ctimeNs = ctimeNs; 485 this.birthtimeNs = birthtimeNs; 486 this.atime = dateFromMs(this.atimeMs); 487 this.mtime = dateFromMs(this.mtimeMs); 488 this.ctime = dateFromMs(this.ctimeMs); 489 this.birthtime = dateFromMs(this.birthtimeMs); 490} 491 492ObjectSetPrototypeOf(BigIntStats.prototype, StatsBase.prototype); 493ObjectSetPrototypeOf(BigIntStats, StatsBase); 494 495BigIntStats.prototype._checkModeProperty = function(property) { 496 if (isWindows && (property === S_IFIFO || property === S_IFBLK || 497 property === S_IFSOCK)) { 498 return false; // Some types are not available on Windows 499 } 500 return (this.mode & BigInt(S_IFMT)) === BigInt(property); 501}; 502 503function Stats(dev, mode, nlink, uid, gid, rdev, blksize, 504 ino, size, blocks, 505 atimeMs, mtimeMs, ctimeMs, birthtimeMs) { 506 FunctionPrototypeCall(StatsBase, this, dev, mode, nlink, uid, gid, rdev, 507 blksize, ino, size, blocks); 508 this.atimeMs = atimeMs; 509 this.mtimeMs = mtimeMs; 510 this.ctimeMs = ctimeMs; 511 this.birthtimeMs = birthtimeMs; 512 this.atime = dateFromMs(atimeMs); 513 this.mtime = dateFromMs(mtimeMs); 514 this.ctime = dateFromMs(ctimeMs); 515 this.birthtime = dateFromMs(birthtimeMs); 516} 517 518ObjectSetPrototypeOf(Stats.prototype, StatsBase.prototype); 519ObjectSetPrototypeOf(Stats, StatsBase); 520 521// HACK: Workaround for https://github.com/standard-things/esm/issues/821. 522// TODO(ronag): Remove this as soon as `esm` publishes a fixed version. 523Stats.prototype.isFile = StatsBase.prototype.isFile; 524 525Stats.prototype._checkModeProperty = function(property) { 526 if (isWindows && (property === S_IFIFO || property === S_IFBLK || 527 property === S_IFSOCK)) { 528 return false; // Some types are not available on Windows 529 } 530 return (this.mode & S_IFMT) === property; 531}; 532 533/** 534 * @param {Float64Array | BigInt64Array} stats 535 * @param {number} offset 536 * @returns {BigIntStats | Stats} 537 */ 538function getStatsFromBinding(stats, offset = 0) { 539 if (isBigInt64Array(stats)) { 540 return new BigIntStats( 541 stats[0 + offset], stats[1 + offset], stats[2 + offset], 542 stats[3 + offset], stats[4 + offset], stats[5 + offset], 543 stats[6 + offset], stats[7 + offset], stats[8 + offset], 544 stats[9 + offset], 545 nsFromTimeSpecBigInt(stats[10 + offset], stats[11 + offset]), 546 nsFromTimeSpecBigInt(stats[12 + offset], stats[13 + offset]), 547 nsFromTimeSpecBigInt(stats[14 + offset], stats[15 + offset]), 548 nsFromTimeSpecBigInt(stats[16 + offset], stats[17 + offset]), 549 ); 550 } 551 return new Stats( 552 stats[0 + offset], stats[1 + offset], stats[2 + offset], 553 stats[3 + offset], stats[4 + offset], stats[5 + offset], 554 stats[6 + offset], stats[7 + offset], stats[8 + offset], 555 stats[9 + offset], 556 msFromTimeSpec(stats[10 + offset], stats[11 + offset]), 557 msFromTimeSpec(stats[12 + offset], stats[13 + offset]), 558 msFromTimeSpec(stats[14 + offset], stats[15 + offset]), 559 msFromTimeSpec(stats[16 + offset], stats[17 + offset]), 560 ); 561} 562 563class StatFs { 564 constructor(type, bsize, blocks, bfree, bavail, files, ffree) { 565 this.type = type; 566 this.bsize = bsize; 567 this.blocks = blocks; 568 this.bfree = bfree; 569 this.bavail = bavail; 570 this.files = files; 571 this.ffree = ffree; 572 } 573} 574 575function getStatFsFromBinding(stats) { 576 return new StatFs( 577 stats[0], stats[1], stats[2], stats[3], stats[4], stats[5], stats[6], 578 ); 579} 580 581function stringToFlags(flags, name = 'flags') { 582 if (typeof flags === 'number') { 583 validateInt32(flags, name); 584 return flags; 585 } 586 587 if (flags == null) { 588 return O_RDONLY; 589 } 590 591 switch (flags) { 592 case 'r' : return O_RDONLY; 593 case 'rs' : // Fall through. 594 case 'sr' : return O_RDONLY | O_SYNC; 595 case 'r+' : return O_RDWR; 596 case 'rs+' : // Fall through. 597 case 'sr+' : return O_RDWR | O_SYNC; 598 599 case 'w' : return O_TRUNC | O_CREAT | O_WRONLY; 600 case 'wx' : // Fall through. 601 case 'xw' : return O_TRUNC | O_CREAT | O_WRONLY | O_EXCL; 602 603 case 'w+' : return O_TRUNC | O_CREAT | O_RDWR; 604 case 'wx+': // Fall through. 605 case 'xw+': return O_TRUNC | O_CREAT | O_RDWR | O_EXCL; 606 607 case 'a' : return O_APPEND | O_CREAT | O_WRONLY; 608 case 'ax' : // Fall through. 609 case 'xa' : return O_APPEND | O_CREAT | O_WRONLY | O_EXCL; 610 case 'as' : // Fall through. 611 case 'sa' : return O_APPEND | O_CREAT | O_WRONLY | O_SYNC; 612 613 case 'a+' : return O_APPEND | O_CREAT | O_RDWR; 614 case 'ax+': // Fall through. 615 case 'xa+': return O_APPEND | O_CREAT | O_RDWR | O_EXCL; 616 case 'as+': // Fall through. 617 case 'sa+': return O_APPEND | O_CREAT | O_RDWR | O_SYNC; 618 } 619 620 throw new ERR_INVALID_ARG_VALUE('flags', flags); 621} 622 623const stringToSymlinkType = hideStackFrames((type) => { 624 let flags = 0; 625 if (typeof type === 'string') { 626 switch (type) { 627 case 'dir': 628 flags |= UV_FS_SYMLINK_DIR; 629 break; 630 case 'junction': 631 flags |= UV_FS_SYMLINK_JUNCTION; 632 break; 633 case 'file': 634 break; 635 default: 636 throw new ERR_FS_INVALID_SYMLINK_TYPE(type); 637 } 638 } 639 return flags; 640}); 641 642// converts Date or number to a fractional UNIX timestamp 643function toUnixTimestamp(time, name = 'time') { 644 // eslint-disable-next-line eqeqeq 645 if (typeof time === 'string' && +time == time) { 646 return +time; 647 } 648 if (NumberIsFinite(time)) { 649 if (time < 0) { 650 return DateNow() / 1000; 651 } 652 return time; 653 } 654 if (isDate(time)) { 655 // Convert to 123.456 UNIX timestamp 656 return DatePrototypeGetTime(time) / 1000; 657 } 658 throw new ERR_INVALID_ARG_TYPE(name, ['Date', 'Time in seconds'], time); 659} 660 661const validateOffsetLengthRead = hideStackFrames( 662 (offset, length, bufferLength) => { 663 if (offset < 0) { 664 throw new ERR_OUT_OF_RANGE('offset', '>= 0', offset); 665 } 666 if (length < 0) { 667 throw new ERR_OUT_OF_RANGE('length', '>= 0', length); 668 } 669 if (offset + length > bufferLength) { 670 throw new ERR_OUT_OF_RANGE('length', 671 `<= ${bufferLength - offset}`, length); 672 } 673 }, 674); 675 676const validateOffsetLengthWrite = hideStackFrames( 677 (offset, length, byteLength) => { 678 if (offset > byteLength) { 679 throw new ERR_OUT_OF_RANGE('offset', `<= ${byteLength}`, offset); 680 } 681 682 if (length > byteLength - offset) { 683 throw new ERR_OUT_OF_RANGE('length', `<= ${byteLength - offset}`, length); 684 } 685 686 if (length < 0) { 687 throw new ERR_OUT_OF_RANGE('length', '>= 0', length); 688 } 689 690 validateInt32(length, 'length', 0); 691 }, 692); 693 694const validatePath = hideStackFrames((path, propName = 'path') => { 695 if (typeof path !== 'string' && !isUint8Array(path)) { 696 throw new ERR_INVALID_ARG_TYPE(propName, ['string', 'Buffer', 'URL'], path); 697 } 698 699 const err = nullCheck(path, propName, false); 700 701 if (err !== undefined) { 702 throw err; 703 } 704}); 705 706const getValidatedPath = hideStackFrames((fileURLOrPath, propName = 'path') => { 707 const path = toPathIfFileURL(fileURLOrPath); 708 validatePath(path, propName); 709 return path; 710}); 711 712const getValidatedFd = hideStackFrames((fd, propName = 'fd') => { 713 if (ObjectIs(fd, -0)) { 714 return 0; 715 } 716 717 validateInt32(fd, propName, 0); 718 719 return fd; 720}); 721 722const validateBufferArray = hideStackFrames((buffers, propName = 'buffers') => { 723 if (!ArrayIsArray(buffers)) 724 throw new ERR_INVALID_ARG_TYPE(propName, 'ArrayBufferView[]', buffers); 725 726 for (let i = 0; i < buffers.length; i++) { 727 if (!isArrayBufferView(buffers[i])) 728 throw new ERR_INVALID_ARG_TYPE(propName, 'ArrayBufferView[]', buffers); 729 } 730 731 return buffers; 732}); 733 734let nonPortableTemplateWarn = true; 735 736function warnOnNonPortableTemplate(template) { 737 // Template strings passed to the mkdtemp() family of functions should not 738 // end with 'X' because they are handled inconsistently across platforms. 739 if (nonPortableTemplateWarn && StringPrototypeEndsWith(template, 'X')) { 740 process.emitWarning('mkdtemp() templates ending with X are not portable. ' + 741 'For details see: https://nodejs.org/api/fs.html'); 742 nonPortableTemplateWarn = false; 743 } 744} 745 746const defaultCpOptions = { 747 dereference: false, 748 errorOnExist: false, 749 filter: undefined, 750 force: true, 751 preserveTimestamps: false, 752 recursive: false, 753 verbatimSymlinks: false, 754}; 755 756const defaultRmOptions = { 757 recursive: false, 758 force: false, 759 retryDelay: 100, 760 maxRetries: 0, 761}; 762 763const defaultRmdirOptions = { 764 retryDelay: 100, 765 maxRetries: 0, 766 recursive: false, 767}; 768 769const validateCpOptions = hideStackFrames((options) => { 770 if (options === undefined) 771 return { ...defaultCpOptions }; 772 validateObject(options, 'options'); 773 options = { ...defaultCpOptions, ...options }; 774 validateBoolean(options.dereference, 'options.dereference'); 775 validateBoolean(options.errorOnExist, 'options.errorOnExist'); 776 validateBoolean(options.force, 'options.force'); 777 validateBoolean(options.preserveTimestamps, 'options.preserveTimestamps'); 778 validateBoolean(options.recursive, 'options.recursive'); 779 validateBoolean(options.verbatimSymlinks, 'options.verbatimSymlinks'); 780 options.mode = getValidMode(options.mode, 'copyFile'); 781 if (options.dereference === true && options.verbatimSymlinks === true) { 782 throw new ERR_INCOMPATIBLE_OPTION_PAIR('dereference', 'verbatimSymlinks'); 783 } 784 if (options.filter !== undefined) { 785 validateFunction(options.filter, 'options.filter'); 786 } 787 return options; 788}); 789 790const validateRmOptions = hideStackFrames((path, options, expectDir, cb) => { 791 options = validateRmdirOptions(options, defaultRmOptions); 792 validateBoolean(options.force, 'options.force'); 793 794 lazyLoadFs().lstat(path, (err, stats) => { 795 if (err) { 796 if (options.force && err.code === 'ENOENT') { 797 return cb(null, options); 798 } 799 return cb(err, options); 800 } 801 802 if (expectDir && !stats.isDirectory()) { 803 return cb(false); 804 } 805 806 if (stats.isDirectory() && !options.recursive) { 807 return cb(new ERR_FS_EISDIR({ 808 code: 'EISDIR', 809 message: 'is a directory', 810 path, 811 syscall: 'rm', 812 errno: EISDIR, 813 })); 814 } 815 return cb(null, options); 816 }); 817}); 818 819const validateRmOptionsSync = hideStackFrames((path, options, expectDir) => { 820 options = validateRmdirOptions(options, defaultRmOptions); 821 validateBoolean(options.force, 'options.force'); 822 823 if (!options.force || expectDir || !options.recursive) { 824 const isDirectory = lazyLoadFs() 825 .lstatSync(path, { throwIfNoEntry: !options.force })?.isDirectory(); 826 827 if (expectDir && !isDirectory) { 828 return false; 829 } 830 831 if (isDirectory && !options.recursive) { 832 throw new ERR_FS_EISDIR({ 833 code: 'EISDIR', 834 message: 'is a directory', 835 path, 836 syscall: 'rm', 837 errno: EISDIR, 838 }); 839 } 840 } 841 842 return options; 843}); 844 845let recursiveRmdirWarned = process.noDeprecation; 846function emitRecursiveRmdirWarning() { 847 if (!recursiveRmdirWarned) { 848 process.emitWarning( 849 'In future versions of Node.js, fs.rmdir(path, { recursive: true }) ' + 850 'will be removed. Use fs.rm(path, { recursive: true }) instead', 851 'DeprecationWarning', 852 'DEP0147', 853 ); 854 recursiveRmdirWarned = true; 855 } 856} 857 858const validateRmdirOptions = hideStackFrames( 859 (options, defaults = defaultRmdirOptions) => { 860 if (options === undefined) 861 return defaults; 862 validateObject(options, 'options'); 863 864 options = { ...defaults, ...options }; 865 866 validateBoolean(options.recursive, 'options.recursive'); 867 validateInt32(options.retryDelay, 'options.retryDelay', 0); 868 validateUint32(options.maxRetries, 'options.maxRetries'); 869 870 return options; 871 }); 872 873const getValidMode = hideStackFrames((mode, type) => { 874 let min = kMinimumAccessMode; 875 let max = kMaximumAccessMode; 876 let def = F_OK; 877 if (type === 'copyFile') { 878 min = kMinimumCopyMode; 879 max = kMaximumCopyMode; 880 def = mode || kDefaultCopyMode; 881 } else { 882 assert(type === 'access'); 883 } 884 if (mode == null) { 885 return def; 886 } 887 validateInteger(mode, 'mode', min, max); 888 return mode; 889}); 890 891const validateStringAfterArrayBufferView = hideStackFrames((buffer, name) => { 892 if (typeof buffer === 'string') { 893 return; 894 } 895 896 if ( 897 typeof buffer === 'object' && 898 buffer !== null && 899 typeof buffer.toString === 'function' && 900 ObjectPrototypeHasOwnProperty(buffer, 'toString') 901 ) { 902 return; 903 } 904 905 throw new ERR_INVALID_ARG_TYPE( 906 name, 907 ['string', 'Buffer', 'TypedArray', 'DataView'], 908 buffer, 909 ); 910}); 911 912const validatePrimitiveStringAfterArrayBufferView = hideStackFrames((buffer, name) => { 913 if (typeof buffer !== 'string') { 914 throw new ERR_INVALID_ARG_TYPE( 915 name, 916 ['string', 'Buffer', 'TypedArray', 'DataView'], 917 buffer, 918 ); 919 } 920}); 921 922const validatePosition = hideStackFrames((position, name) => { 923 if (typeof position === 'number') { 924 validateInteger(position, name); 925 } else if (typeof position === 'bigint') { 926 if (!(position >= -(2n ** 63n) && position <= 2n ** 63n - 1n)) { 927 throw new ERR_OUT_OF_RANGE(name, 928 `>= ${-(2n ** 63n)} && <= ${2n ** 63n - 1n}`, 929 position); 930 } 931 } else { 932 throw new ERR_INVALID_ARG_TYPE(name, ['integer', 'bigint'], position); 933 } 934}); 935 936module.exports = { 937 constants: { 938 kIoMaxLength, 939 kMaxUserId, 940 kReadFileBufferLength, 941 kReadFileUnknownBufferLength, 942 kWriteFileMaxChunkSize, 943 }, 944 assertEncoding, 945 BigIntStats, // for testing 946 copyObject, 947 Dirent, 948 emitRecursiveRmdirWarning, 949 getDirent, 950 getDirents, 951 getOptions, 952 getValidatedFd, 953 getValidatedPath, 954 getValidMode, 955 handleErrorFromBinding, 956 nullCheck, 957 preprocessSymlinkDestination, 958 realpathCacheKey: Symbol('realpathCacheKey'), 959 getStatFsFromBinding, 960 getStatsFromBinding, 961 stringToFlags, 962 stringToSymlinkType, 963 Stats, 964 toUnixTimestamp, 965 validateBufferArray, 966 validateCpOptions, 967 validateOffsetLengthRead, 968 validateOffsetLengthWrite, 969 validatePath, 970 validatePosition, 971 validateRmOptions, 972 validateRmOptionsSync, 973 validateRmdirOptions, 974 validateStringAfterArrayBufferView, 975 validatePrimitiveStringAfterArrayBufferView, 976 warnOnNonPortableTemplate, 977}; 978