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