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