• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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