• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright Joyent, Inc. and other Node contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the
5// "Software"), to deal in the Software without restriction, including
6// without limitation the rights to use, copy, modify, merge, publish,
7// distribute, sublicense, and/or sell copies of the Software, and to permit
8// persons to whom the Software is furnished to do so, subject to the
9// following conditions:
10//
11// The above copyright notice and this permission notice shall be included
12// in all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20// USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22// Maintainers, keep in mind that ES1-style octal literals (`0666`) are not
23// allowed in strict mode. Use ES6-style octal literals instead (`0o666`).
24
25'use strict';
26
27const {
28  ArrayPrototypePush,
29  BigIntPrototypeToString,
30  Boolean,
31  MathMax,
32  Number,
33  ObjectDefineProperties,
34  ObjectDefineProperty,
35  Promise,
36  ReflectApply,
37  SafeMap,
38  SafeSet,
39  String,
40  StringPrototypeCharCodeAt,
41  StringPrototypeIndexOf,
42  StringPrototypeSlice,
43} = primordials;
44
45const { fs: constants } = internalBinding('constants');
46const {
47  S_IFIFO,
48  S_IFLNK,
49  S_IFMT,
50  S_IFREG,
51  S_IFSOCK,
52  F_OK,
53  R_OK,
54  W_OK,
55  X_OK,
56  O_WRONLY,
57  O_SYMLINK,
58} = constants;
59
60const pathModule = require('path');
61const { isArrayBufferView } = require('internal/util/types');
62
63// We need to get the statValues from the binding at the callsite since
64// it's re-initialized after deserialization.
65
66const binding = internalBinding('fs');
67const { Buffer } = require('buffer');
68const {
69  aggregateTwoErrors,
70  codes: {
71    ERR_FS_FILE_TOO_LARGE,
72    ERR_INVALID_ARG_VALUE,
73    ERR_FEATURE_UNAVAILABLE_ON_PLATFORM,
74  },
75  AbortError,
76  uvErrmapGet,
77  uvException,
78} = require('internal/errors');
79
80const { FSReqCallback } = binding;
81const { toPathIfFileURL } = require('internal/url');
82const {
83  customPromisifyArgs: kCustomPromisifyArgsSymbol,
84  deprecate,
85  kEmptyObject,
86  promisify: {
87    custom: kCustomPromisifiedSymbol,
88  },
89  SideEffectFreeRegExpPrototypeExec,
90  defineLazyProperties,
91} = require('internal/util');
92const {
93  constants: {
94    kIoMaxLength,
95    kMaxUserId,
96  },
97  copyObject,
98  Dirent,
99  emitRecursiveRmdirWarning,
100  getDirent,
101  getDirents,
102  getOptions,
103  getValidatedFd,
104  getValidatedPath,
105  getValidMode,
106  handleErrorFromBinding,
107  preprocessSymlinkDestination,
108  Stats,
109  getStatFsFromBinding,
110  getStatsFromBinding,
111  realpathCacheKey,
112  stringToFlags,
113  stringToSymlinkType,
114  toUnixTimestamp,
115  validateBufferArray,
116  validateCpOptions,
117  validateOffsetLengthRead,
118  validateOffsetLengthWrite,
119  validatePath,
120  validatePosition,
121  validateRmOptions,
122  validateRmOptionsSync,
123  validateRmdirOptions,
124  validateStringAfterArrayBufferView,
125  validatePrimitiveStringAfterArrayBufferView,
126  warnOnNonPortableTemplate,
127} = require('internal/fs/utils');
128const {
129  CHAR_FORWARD_SLASH,
130  CHAR_BACKWARD_SLASH,
131} = require('internal/constants');
132const {
133  isUint32,
134  parseFileMode,
135  validateBoolean,
136  validateBuffer,
137  validateEncoding,
138  validateFunction,
139  validateInteger,
140  validateObject,
141} = require('internal/validators');
142
143let truncateWarn = true;
144let fs;
145
146// Lazy loaded
147let cpFn;
148let cpSyncFn;
149let promises = null;
150let ReadStream;
151let WriteStream;
152let rimraf;
153let rimrafSync;
154let kResistStopPropagation;
155
156// These have to be separate because of how graceful-fs happens to do it's
157// monkeypatching.
158let FileReadStream;
159let FileWriteStream;
160
161const isWindows = process.platform === 'win32';
162const isOSX = process.platform === 'darwin';
163
164
165const showStringCoercionDeprecation = deprecate(
166  () => {},
167  'Implicit coercion of objects with own toString property is deprecated.',
168  'DEP0162',
169);
170function showTruncateDeprecation() {
171  if (truncateWarn) {
172    process.emitWarning(
173      'Using fs.truncate with a file descriptor is deprecated. Please use ' +
174      'fs.ftruncate with a file descriptor instead.',
175      'DeprecationWarning', 'DEP0081');
176    truncateWarn = false;
177  }
178}
179
180function maybeCallback(cb) {
181  validateFunction(cb, 'cb');
182
183  return cb;
184}
185
186// Ensure that callbacks run in the global context. Only use this function
187// for callbacks that are passed to the binding layer, callbacks that are
188// invoked from JS already run in the proper scope.
189function makeCallback(cb) {
190  validateFunction(cb, 'cb');
191
192  return (...args) => ReflectApply(cb, this, args);
193}
194
195// Special case of `makeCallback()` that is specific to async `*stat()` calls as
196// an optimization, since the data passed back to the callback needs to be
197// transformed anyway.
198function makeStatsCallback(cb) {
199  validateFunction(cb, 'cb');
200
201  return (err, stats) => {
202    if (err) return cb(err);
203    cb(err, getStatsFromBinding(stats));
204  };
205}
206
207const isFd = isUint32;
208
209function isFileType(stats, fileType) {
210  // Use stats array directly to avoid creating an fs.Stats instance just for
211  // our internal use.
212  let mode = stats[1];
213  if (typeof mode === 'bigint')
214    mode = Number(mode);
215  return (mode & S_IFMT) === fileType;
216}
217
218/**
219 * Tests a user's permissions for the file or directory
220 * specified by `path`.
221 * @param {string | Buffer | URL} path
222 * @param {number} [mode]
223 * @param {(err?: Error) => any} callback
224 * @returns {void}
225 */
226function access(path, mode, callback) {
227  if (typeof mode === 'function') {
228    callback = mode;
229    mode = F_OK;
230  }
231
232  path = getValidatedPath(path);
233  mode = getValidMode(mode, 'access');
234  callback = makeCallback(callback);
235
236  const req = new FSReqCallback();
237  req.oncomplete = callback;
238  binding.access(pathModule.toNamespacedPath(path), mode, req);
239}
240
241/**
242 * Synchronously tests a user's permissions for the file or
243 * directory specified by `path`.
244 * @param {string | Buffer | URL} path
245 * @param {number} [mode]
246 * @returns {void}
247 */
248function accessSync(path, mode) {
249  path = getValidatedPath(path);
250  mode = getValidMode(mode, 'access');
251
252  const ctx = { path };
253  binding.access(pathModule.toNamespacedPath(path), mode, undefined, ctx);
254  handleErrorFromBinding(ctx);
255}
256
257/**
258 * Tests whether or not the given path exists.
259 * @param {string | Buffer | URL} path
260 * @param {(exists?: boolean) => any} callback
261 * @returns {void}
262 */
263function exists(path, callback) {
264  maybeCallback(callback);
265
266  function suppressedCallback(err) {
267    callback(err ? false : true);
268  }
269
270  try {
271    fs.access(path, F_OK, suppressedCallback);
272  } catch {
273    return callback(false);
274  }
275}
276
277ObjectDefineProperty(exists, kCustomPromisifiedSymbol, {
278  __proto__: null,
279  value: function exists(path) { // eslint-disable-line func-name-matching
280    return new Promise((resolve) => fs.exists(path, resolve));
281  },
282});
283
284// fs.existsSync never throws, it only returns true or false.
285// Since fs.existsSync never throws, users have established
286// the expectation that passing invalid arguments to it, even like
287// fs.existsSync(), would only get a false in return, so we cannot signal
288// validation errors to users properly out of compatibility concerns.
289// TODO(joyeecheung): deprecate the never-throw-on-invalid-arguments behavior
290/**
291 * Synchronously tests whether or not the given path exists.
292 * @param {string | Buffer | URL} path
293 * @returns {boolean}
294 */
295function existsSync(path) {
296  try {
297    path = getValidatedPath(path);
298  } catch {
299    return false;
300  }
301  const ctx = { path };
302  const nPath = pathModule.toNamespacedPath(path);
303  binding.access(nPath, F_OK, undefined, ctx);
304
305  // In case of an invalid symlink, `binding.access()` on win32
306  // will **not** return an error and is therefore not enough.
307  // Double check with `binding.stat()`.
308  if (isWindows && ctx.errno === undefined) {
309    binding.stat(nPath, false, undefined, ctx);
310  }
311
312  return ctx.errno === undefined;
313}
314
315function readFileAfterOpen(err, fd) {
316  const context = this.context;
317
318  if (err) {
319    context.callback(err);
320    return;
321  }
322
323  context.fd = fd;
324
325  const req = new FSReqCallback();
326  req.oncomplete = readFileAfterStat;
327  req.context = context;
328  binding.fstat(fd, false, req);
329}
330
331function readFileAfterStat(err, stats) {
332  const context = this.context;
333
334  if (err)
335    return context.close(err);
336
337  // TODO(BridgeAR): Check if allocating a smaller chunk is better performance
338  // wise, similar to the promise based version (less peak memory and chunked
339  // stringify operations vs multiple C++/JS boundary crossings).
340  const size = context.size = isFileType(stats, S_IFREG) ? stats[8] : 0;
341
342  if (size > kIoMaxLength) {
343    err = new ERR_FS_FILE_TOO_LARGE(size);
344    return context.close(err);
345  }
346
347  try {
348    if (size === 0) {
349      // TODO(BridgeAR): If an encoding is set, use the StringDecoder to concat
350      // the result and reuse the buffer instead of allocating a new one.
351      context.buffers = [];
352    } else {
353      context.buffer = Buffer.allocUnsafeSlow(size);
354    }
355  } catch (err) {
356    return context.close(err);
357  }
358  context.read();
359}
360
361function checkAborted(signal, callback) {
362  if (signal?.aborted) {
363    callback(new AbortError(undefined, { cause: signal?.reason }));
364    return true;
365  }
366  return false;
367}
368
369/**
370 * Asynchronously reads the entire contents of a file.
371 * @param {string | Buffer | URL | number} path
372 * @param {{
373 *   encoding?: string | null;
374 *   flag?: string;
375 *   signal?: AbortSignal;
376 *   } | string} [options]
377 * @param {(
378 *   err?: Error,
379 *   data?: string | Buffer
380 *   ) => any} callback
381 * @returns {void}
382 */
383function readFile(path, options, callback) {
384  callback = maybeCallback(callback || options);
385  options = getOptions(options, { flag: 'r' });
386  const ReadFileContext = require('internal/fs/read_file_context');
387  const context = new ReadFileContext(callback, options.encoding);
388  context.isUserFd = isFd(path); // File descriptor ownership
389
390  if (options.signal) {
391    context.signal = options.signal;
392  }
393  if (context.isUserFd) {
394    process.nextTick(function tick(context) {
395      ReflectApply(readFileAfterOpen, { context }, [null, path]);
396    }, context);
397    return;
398  }
399
400  if (checkAborted(options.signal, callback))
401    return;
402
403  const flagsNumber = stringToFlags(options.flag, 'options.flag');
404  path = getValidatedPath(path);
405
406  const req = new FSReqCallback();
407  req.context = context;
408  req.oncomplete = readFileAfterOpen;
409  binding.open(pathModule.toNamespacedPath(path),
410               flagsNumber,
411               0o666,
412               req);
413}
414
415function tryStatSync(fd, isUserFd) {
416  const ctx = {};
417  const stats = binding.fstat(fd, false, undefined, ctx);
418  if (ctx.errno !== undefined && !isUserFd) {
419    fs.closeSync(fd);
420    throw uvException(ctx);
421  }
422  return stats;
423}
424
425function tryCreateBuffer(size, fd, isUserFd) {
426  let threw = true;
427  let buffer;
428  try {
429    if (size > kIoMaxLength) {
430      throw new ERR_FS_FILE_TOO_LARGE(size);
431    }
432    buffer = Buffer.allocUnsafe(size);
433    threw = false;
434  } finally {
435    if (threw && !isUserFd) fs.closeSync(fd);
436  }
437  return buffer;
438}
439
440function tryReadSync(fd, isUserFd, buffer, pos, len) {
441  let threw = true;
442  let bytesRead;
443  try {
444    bytesRead = fs.readSync(fd, buffer, pos, len);
445    threw = false;
446  } finally {
447    if (threw && !isUserFd) fs.closeSync(fd);
448  }
449  return bytesRead;
450}
451
452/**
453 * Synchronously reads the entire contents of a file.
454 * @param {string | Buffer | URL | number} path
455 * @param {{
456 *   encoding?: string | null;
457 *   flag?: string;
458 *   }} [options]
459 * @returns {string | Buffer}
460 */
461function readFileSync(path, options) {
462  options = getOptions(options, { flag: 'r' });
463  const isUserFd = isFd(path); // File descriptor ownership
464  const fd = isUserFd ? path : fs.openSync(path, options.flag, 0o666);
465
466  const stats = tryStatSync(fd, isUserFd);
467  const size = isFileType(stats, S_IFREG) ? stats[8] : 0;
468  let pos = 0;
469  let buffer; // Single buffer with file data
470  let buffers; // List for when size is unknown
471
472  if (size === 0) {
473    buffers = [];
474  } else {
475    buffer = tryCreateBuffer(size, fd, isUserFd);
476  }
477
478  let bytesRead;
479
480  if (size !== 0) {
481    do {
482      bytesRead = tryReadSync(fd, isUserFd, buffer, pos, size - pos);
483      pos += bytesRead;
484    } while (bytesRead !== 0 && pos < size);
485  } else {
486    do {
487      // The kernel lies about many files.
488      // Go ahead and try to read some bytes.
489      buffer = Buffer.allocUnsafe(8192);
490      bytesRead = tryReadSync(fd, isUserFd, buffer, 0, 8192);
491      if (bytesRead !== 0) {
492        ArrayPrototypePush(buffers, buffer.slice(0, bytesRead));
493      }
494      pos += bytesRead;
495    } while (bytesRead !== 0);
496  }
497
498  if (!isUserFd)
499    fs.closeSync(fd);
500
501  if (size === 0) {
502    // Data was collected into the buffers list.
503    buffer = Buffer.concat(buffers, pos);
504  } else if (pos < size) {
505    buffer = buffer.slice(0, pos);
506  }
507
508  if (options.encoding) buffer = buffer.toString(options.encoding);
509  return buffer;
510}
511
512function defaultCloseCallback(err) {
513  if (err != null) throw err;
514}
515
516/**
517 * Closes the file descriptor.
518 * @param {number} fd
519 * @param {(err?: Error) => any} [callback]
520 * @returns {void}
521 */
522function close(fd, callback = defaultCloseCallback) {
523  fd = getValidatedFd(fd);
524  if (callback !== defaultCloseCallback)
525    callback = makeCallback(callback);
526
527  const req = new FSReqCallback();
528  req.oncomplete = callback;
529  binding.close(fd, req);
530}
531
532/**
533 * Synchronously closes the file descriptor.
534 * @param {number} fd
535 * @returns {void}
536 */
537function closeSync(fd) {
538  fd = getValidatedFd(fd);
539
540  const ctx = {};
541  binding.close(fd, undefined, ctx);
542  handleErrorFromBinding(ctx);
543}
544
545/**
546 * Asynchronously opens a file.
547 * @param {string | Buffer | URL} path
548 * @param {string | number} [flags]
549 * @param {string | number} [mode]
550 * @param {(
551 *   err?: Error,
552 *   fd?: number
553 *   ) => any} callback
554 * @returns {void}
555 */
556function open(path, flags, mode, callback) {
557  path = getValidatedPath(path);
558  if (arguments.length < 3) {
559    callback = flags;
560    flags = 'r';
561    mode = 0o666;
562  } else if (typeof mode === 'function') {
563    callback = mode;
564    mode = 0o666;
565  } else {
566    mode = parseFileMode(mode, 'mode', 0o666);
567  }
568  const flagsNumber = stringToFlags(flags);
569  callback = makeCallback(callback);
570
571  const req = new FSReqCallback();
572  req.oncomplete = callback;
573
574  binding.open(pathModule.toNamespacedPath(path),
575               flagsNumber,
576               mode,
577               req);
578}
579
580/**
581 * Synchronously opens a file.
582 * @param {string | Buffer | URL} path
583 * @param {string | number} [flags]
584 * @param {string | number} [mode]
585 * @returns {number}
586 */
587function openSync(path, flags, mode) {
588  path = getValidatedPath(path);
589  const flagsNumber = stringToFlags(flags);
590  mode = parseFileMode(mode, 'mode', 0o666);
591
592  const ctx = { path };
593  const result = binding.open(pathModule.toNamespacedPath(path),
594                              flagsNumber, mode,
595                              undefined, ctx);
596  handleErrorFromBinding(ctx);
597  return result;
598}
599
600/**
601 * Reads file from the specified `fd` (file descriptor).
602 * @param {number} fd
603 * @param {Buffer | TypedArray | DataView} buffer
604 * @param {number} offsetOrOptions
605 * @param {number} length
606 * @param {number | bigint | null} position
607 * @param {(
608 *   err?: Error,
609 *   bytesRead?: number,
610 *   buffer?: Buffer
611 *   ) => any} callback
612 * @returns {void}
613 */
614function read(fd, buffer, offsetOrOptions, length, position, callback) {
615  fd = getValidatedFd(fd);
616
617  let offset = offsetOrOptions;
618  let params = null;
619  if (arguments.length <= 4) {
620    if (arguments.length === 4) {
621      // This is fs.read(fd, buffer, options, callback)
622      validateObject(offsetOrOptions, 'options', { nullable: true });
623      callback = length;
624      params = offsetOrOptions;
625    } else if (arguments.length === 3) {
626      // This is fs.read(fd, bufferOrParams, callback)
627      if (!isArrayBufferView(buffer)) {
628        // This is fs.read(fd, params, callback)
629        params = buffer;
630        ({ buffer = Buffer.alloc(16384) } = params ?? kEmptyObject);
631      }
632      callback = offsetOrOptions;
633    } else {
634      // This is fs.read(fd, callback)
635      callback = buffer;
636      buffer = Buffer.alloc(16384);
637    }
638
639    ({
640      offset = 0,
641      length = buffer.byteLength - offset,
642      position = null,
643    } = params ?? kEmptyObject);
644  }
645
646  validateBuffer(buffer);
647  callback = maybeCallback(callback);
648
649  if (offset == null) {
650    offset = 0;
651  } else {
652    validateInteger(offset, 'offset', 0);
653  }
654
655  length |= 0;
656
657  if (length === 0) {
658    return process.nextTick(function tick() {
659      callback(null, 0, buffer);
660    });
661  }
662
663  if (buffer.byteLength === 0) {
664    throw new ERR_INVALID_ARG_VALUE('buffer', buffer,
665                                    'is empty and cannot be written');
666  }
667
668  validateOffsetLengthRead(offset, length, buffer.byteLength);
669
670  if (position == null)
671    position = -1;
672
673  validatePosition(position, 'position');
674
675  function wrapper(err, bytesRead) {
676    // Retain a reference to buffer so that it can't be GC'ed too soon.
677    callback(err, bytesRead || 0, buffer);
678  }
679
680  const req = new FSReqCallback();
681  req.oncomplete = wrapper;
682
683  binding.read(fd, buffer, offset, length, position, req);
684}
685
686ObjectDefineProperty(read, kCustomPromisifyArgsSymbol,
687                     { __proto__: null, value: ['bytesRead', 'buffer'], enumerable: false });
688
689/**
690 * Synchronously reads the file from the
691 * specified `fd` (file descriptor).
692 * @param {number} fd
693 * @param {Buffer | TypedArray | DataView} buffer
694 * @param {{
695 *   offset?: number;
696 *   length?: number;
697 *   position?: number | bigint | null;
698 *   }} [offset]
699 * @returns {number}
700 */
701function readSync(fd, buffer, offset, length, position) {
702  fd = getValidatedFd(fd);
703
704  validateBuffer(buffer);
705
706  if (arguments.length <= 3) {
707    // Assume fs.readSync(fd, buffer, options)
708    const options = offset || kEmptyObject;
709
710    ({
711      offset = 0,
712      length = buffer.byteLength - offset,
713      position = null,
714    } = options);
715  }
716
717  if (offset == null) {
718    offset = 0;
719  } else {
720    validateInteger(offset, 'offset', 0);
721  }
722
723  length |= 0;
724
725  if (length === 0) {
726    return 0;
727  }
728
729  if (buffer.byteLength === 0) {
730    throw new ERR_INVALID_ARG_VALUE('buffer', buffer,
731                                    'is empty and cannot be written');
732  }
733
734  validateOffsetLengthRead(offset, length, buffer.byteLength);
735
736  if (position == null)
737    position = -1;
738
739  validatePosition(position, 'position');
740
741  const ctx = {};
742  const result = binding.read(fd, buffer, offset, length, position,
743                              undefined, ctx);
744  handleErrorFromBinding(ctx);
745  return result;
746}
747
748/**
749 * Reads file from the specified `fd` (file descriptor)
750 * and writes to an array of `ArrayBufferView`s.
751 * @param {number} fd
752 * @param {ArrayBufferView[]} buffers
753 * @param {number | null} [position]
754 * @param {(
755 *   err?: Error,
756 *   bytesRead?: number,
757 *   buffers?: ArrayBufferView[];
758 *   ) => any} callback
759 * @returns {void}
760 */
761function readv(fd, buffers, position, callback) {
762  function wrapper(err, read) {
763    callback(err, read || 0, buffers);
764  }
765
766  fd = getValidatedFd(fd);
767  validateBufferArray(buffers);
768  callback = maybeCallback(callback || position);
769
770  const req = new FSReqCallback();
771  req.oncomplete = wrapper;
772
773  if (typeof position !== 'number')
774    position = null;
775
776  return binding.readBuffers(fd, buffers, position, req);
777}
778
779ObjectDefineProperty(readv, kCustomPromisifyArgsSymbol,
780                     { __proto__: null, value: ['bytesRead', 'buffers'], enumerable: false });
781
782/**
783 * Synchronously reads file from the
784 * specified `fd` (file descriptor) and writes to an array
785 * of `ArrayBufferView`s.
786 * @param {number} fd
787 * @param {ArrayBufferView[]} buffers
788 * @param {number | null} [position]
789 * @returns {number}
790 */
791function readvSync(fd, buffers, position) {
792  fd = getValidatedFd(fd);
793  validateBufferArray(buffers);
794
795  const ctx = {};
796
797  if (typeof position !== 'number')
798    position = null;
799
800  const result = binding.readBuffers(fd, buffers, position, undefined, ctx);
801  handleErrorFromBinding(ctx);
802  return result;
803}
804
805/**
806 * Writes `buffer` to the specified `fd` (file descriptor).
807 * @param {number} fd
808 * @param {Buffer | TypedArray | DataView | string | object} buffer
809 * @param {number | object} [offsetOrOptions]
810 * @param {number} [length]
811 * @param {number | null} [position]
812 * @param {(
813 *   err?: Error,
814 *   bytesWritten?: number;
815 *   buffer?: Buffer | TypedArray | DataView
816 *   ) => any} callback
817 * @returns {void}
818 */
819function write(fd, buffer, offsetOrOptions, length, position, callback) {
820  function wrapper(err, written) {
821    // Retain a reference to buffer so that it can't be GC'ed too soon.
822    callback(err, written || 0, buffer);
823  }
824
825  fd = getValidatedFd(fd);
826
827  let offset = offsetOrOptions;
828  if (isArrayBufferView(buffer)) {
829    callback = maybeCallback(callback || position || length || offset);
830
831    if (typeof offset === 'object') {
832      ({
833        offset = 0,
834        length = buffer.byteLength - offset,
835        position = null,
836      } = offsetOrOptions ?? kEmptyObject);
837    }
838
839    if (offset == null || typeof offset === 'function') {
840      offset = 0;
841    } else {
842      validateInteger(offset, 'offset', 0);
843    }
844    if (typeof length !== 'number')
845      length = buffer.byteLength - offset;
846    if (typeof position !== 'number')
847      position = null;
848    validateOffsetLengthWrite(offset, length, buffer.byteLength);
849
850    const req = new FSReqCallback();
851    req.oncomplete = wrapper;
852    return binding.writeBuffer(fd, buffer, offset, length, position, req);
853  }
854
855  validateStringAfterArrayBufferView(buffer, 'buffer');
856  if (typeof buffer !== 'string') {
857    showStringCoercionDeprecation();
858  }
859
860  if (typeof position !== 'function') {
861    if (typeof offset === 'function') {
862      position = offset;
863      offset = null;
864    } else {
865      position = length;
866    }
867    length = 'utf8';
868  }
869
870  const str = String(buffer);
871  validateEncoding(str, length);
872  callback = maybeCallback(position);
873
874  const req = new FSReqCallback();
875  req.oncomplete = wrapper;
876  return binding.writeString(fd, str, offset, length, req);
877}
878
879ObjectDefineProperty(write, kCustomPromisifyArgsSymbol,
880                     { __proto__: null, value: ['bytesWritten', 'buffer'], enumerable: false });
881
882/**
883 * Synchronously writes `buffer` to the
884 * specified `fd` (file descriptor).
885 * @param {number} fd
886 * @param {Buffer | TypedArray | DataView | string} buffer
887 * @param {{
888 *   offset?: number;
889 *   length?: number;
890 *   position?: number | null;
891 *   }} [offsetOrOptions]
892 * @returns {number}
893 */
894function writeSync(fd, buffer, offsetOrOptions, length, position) {
895  fd = getValidatedFd(fd);
896  const ctx = {};
897  let result;
898
899  let offset = offsetOrOptions;
900  if (isArrayBufferView(buffer)) {
901    if (typeof offset === 'object') {
902      ({
903        offset = 0,
904        length = buffer.byteLength - offset,
905        position = null,
906      } = offsetOrOptions ?? kEmptyObject);
907    }
908    if (position === undefined)
909      position = null;
910    if (offset == null) {
911      offset = 0;
912    } else {
913      validateInteger(offset, 'offset', 0);
914    }
915    if (typeof length !== 'number')
916      length = buffer.byteLength - offset;
917    validateOffsetLengthWrite(offset, length, buffer.byteLength);
918    result = binding.writeBuffer(fd, buffer, offset, length, position,
919                                 undefined, ctx);
920  } else {
921    validatePrimitiveStringAfterArrayBufferView(buffer, 'buffer');
922    validateEncoding(buffer, length);
923
924    if (offset === undefined)
925      offset = null;
926    result = binding.writeString(fd, buffer, offset, length,
927                                 undefined, ctx);
928  }
929  handleErrorFromBinding(ctx);
930  return result;
931}
932
933/**
934 * Writes an array of `ArrayBufferView`s to the
935 * specified `fd` (file descriptor).
936 * @param {number} fd
937 * @param {ArrayBufferView[]} buffers
938 * @param {number | null} [position]
939 * @param {(
940 *   err?: Error,
941 *   bytesWritten?: number,
942 *   buffers?: ArrayBufferView[]
943 *   ) => any} callback
944 * @returns {void}
945 */
946function writev(fd, buffers, position, callback) {
947  function wrapper(err, written) {
948    callback(err, written || 0, buffers);
949  }
950
951  fd = getValidatedFd(fd);
952  validateBufferArray(buffers);
953  callback = maybeCallback(callback || position);
954
955  if (buffers.length === 0) {
956    process.nextTick(callback, null, 0, buffers);
957    return;
958  }
959
960  const req = new FSReqCallback();
961  req.oncomplete = wrapper;
962
963  if (typeof position !== 'number')
964    position = null;
965
966  return binding.writeBuffers(fd, buffers, position, req);
967}
968
969ObjectDefineProperty(writev, kCustomPromisifyArgsSymbol, {
970  __proto__: null,
971  value: ['bytesWritten', 'buffer'],
972  enumerable: false,
973});
974
975/**
976 * Synchronously writes an array of `ArrayBufferView`s
977 * to the specified `fd` (file descriptor).
978 * @param {number} fd
979 * @param {ArrayBufferView[]} buffers
980 * @param {number | null} [position]
981 * @returns {number}
982 */
983function writevSync(fd, buffers, position) {
984  fd = getValidatedFd(fd);
985  validateBufferArray(buffers);
986
987  if (buffers.length === 0) {
988    return 0;
989  }
990
991  const ctx = {};
992
993  if (typeof position !== 'number')
994    position = null;
995
996  const result = binding.writeBuffers(fd, buffers, position, undefined, ctx);
997
998  handleErrorFromBinding(ctx);
999  return result;
1000}
1001
1002/**
1003 * Asynchronously renames file at `oldPath` to
1004 * the pathname provided as `newPath`.
1005 * @param {string | Buffer | URL} oldPath
1006 * @param {string | Buffer | URL} newPath
1007 * @param {(err?: Error) => any} callback
1008 * @returns {void}
1009 */
1010function rename(oldPath, newPath, callback) {
1011  callback = makeCallback(callback);
1012  oldPath = getValidatedPath(oldPath, 'oldPath');
1013  newPath = getValidatedPath(newPath, 'newPath');
1014  const req = new FSReqCallback();
1015  req.oncomplete = callback;
1016  binding.rename(pathModule.toNamespacedPath(oldPath),
1017                 pathModule.toNamespacedPath(newPath),
1018                 req);
1019}
1020
1021
1022/**
1023 * Synchronously renames file at `oldPath` to
1024 * the pathname provided as `newPath`.
1025 * @param {string | Buffer | URL} oldPath
1026 * @param {string | Buffer | URL} newPath
1027 * @returns {void}
1028 */
1029function renameSync(oldPath, newPath) {
1030  oldPath = getValidatedPath(oldPath, 'oldPath');
1031  newPath = getValidatedPath(newPath, 'newPath');
1032  const ctx = { path: oldPath, dest: newPath };
1033  binding.rename(pathModule.toNamespacedPath(oldPath),
1034                 pathModule.toNamespacedPath(newPath), undefined, ctx);
1035  handleErrorFromBinding(ctx);
1036}
1037
1038/**
1039 * Truncates the file.
1040 * @param {string | Buffer | URL} path
1041 * @param {number} [len]
1042 * @param {(err?: Error) => any} callback
1043 * @returns {void}
1044 */
1045function truncate(path, len, callback) {
1046  if (typeof path === 'number') {
1047    showTruncateDeprecation();
1048    return fs.ftruncate(path, len, callback);
1049  }
1050  if (typeof len === 'function') {
1051    callback = len;
1052    len = 0;
1053  } else if (len === undefined) {
1054    len = 0;
1055  }
1056
1057  validateInteger(len, 'len');
1058  len = MathMax(0, len);
1059  callback = maybeCallback(callback);
1060  fs.open(path, 'r+', (er, fd) => {
1061    if (er) return callback(er);
1062    const req = new FSReqCallback();
1063    req.oncomplete = function oncomplete(er) {
1064      fs.close(fd, (er2) => {
1065        callback(aggregateTwoErrors(er2, er));
1066      });
1067    };
1068    binding.ftruncate(fd, len, req);
1069  });
1070}
1071
1072/**
1073 * Synchronously truncates the file.
1074 * @param {string | Buffer | URL} path
1075 * @param {number} [len]
1076 * @returns {void}
1077 */
1078function truncateSync(path, len) {
1079  if (typeof path === 'number') {
1080    // legacy
1081    showTruncateDeprecation();
1082    return fs.ftruncateSync(path, len);
1083  }
1084  if (len === undefined) {
1085    len = 0;
1086  }
1087  // Allow error to be thrown, but still close fd.
1088  const fd = fs.openSync(path, 'r+');
1089  let ret;
1090
1091  try {
1092    ret = fs.ftruncateSync(fd, len);
1093  } finally {
1094    fs.closeSync(fd);
1095  }
1096  return ret;
1097}
1098
1099/**
1100 * Truncates the file descriptor.
1101 * @param {number} fd
1102 * @param {number} [len]
1103 * @param {(err?: Error) => any} callback
1104 * @returns {void}
1105 */
1106function ftruncate(fd, len = 0, callback) {
1107  if (typeof len === 'function') {
1108    callback = len;
1109    len = 0;
1110  }
1111  fd = getValidatedFd(fd);
1112  validateInteger(len, 'len');
1113  len = MathMax(0, len);
1114  callback = makeCallback(callback);
1115
1116  const req = new FSReqCallback();
1117  req.oncomplete = callback;
1118  binding.ftruncate(fd, len, req);
1119}
1120
1121/**
1122 * Synchronously truncates the file descriptor.
1123 * @param {number} fd
1124 * @param {number} [len]
1125 * @returns {void}
1126 */
1127function ftruncateSync(fd, len = 0) {
1128  fd = getValidatedFd(fd);
1129  validateInteger(len, 'len');
1130  len = MathMax(0, len);
1131  const ctx = {};
1132  binding.ftruncate(fd, len, undefined, ctx);
1133  handleErrorFromBinding(ctx);
1134}
1135
1136function lazyLoadCp() {
1137  if (cpFn === undefined) {
1138    ({ cpFn } = require('internal/fs/cp/cp'));
1139    cpFn = require('util').callbackify(cpFn);
1140    ({ cpSyncFn } = require('internal/fs/cp/cp-sync'));
1141  }
1142}
1143
1144function lazyLoadRimraf() {
1145  if (rimraf === undefined)
1146    ({ rimraf, rimrafSync } = require('internal/fs/rimraf'));
1147}
1148
1149/**
1150 * Asynchronously removes a directory.
1151 * @param {string | Buffer | URL} path
1152 * @param {{
1153 *   maxRetries?: number;
1154 *   recursive?: boolean;
1155 *   retryDelay?: number;
1156 *   }} [options]
1157 * @param {(err?: Error) => any} callback
1158 * @returns {void}
1159 */
1160function rmdir(path, options, callback) {
1161  if (typeof options === 'function') {
1162    callback = options;
1163    options = undefined;
1164  }
1165
1166  callback = makeCallback(callback);
1167  path = pathModule.toNamespacedPath(getValidatedPath(path));
1168
1169  if (options?.recursive) {
1170    emitRecursiveRmdirWarning();
1171    validateRmOptions(
1172      path,
1173      { ...options, force: false },
1174      true,
1175      (err, options) => {
1176        if (err === false) {
1177          const req = new FSReqCallback();
1178          req.oncomplete = callback;
1179          return binding.rmdir(path, req);
1180        }
1181        if (err) {
1182          return callback(err);
1183        }
1184
1185        lazyLoadRimraf();
1186        rimraf(path, options, callback);
1187      });
1188  } else {
1189    validateRmdirOptions(options);
1190    const req = new FSReqCallback();
1191    req.oncomplete = callback;
1192    return binding.rmdir(path, req);
1193  }
1194}
1195
1196/**
1197 * Synchronously removes a directory.
1198 * @param {string | Buffer | URL} path
1199 * @param {{
1200 *   maxRetries?: number;
1201 *   recursive?: boolean;
1202 *   retryDelay?: number;
1203 *   }} [options]
1204 * @returns {void}
1205 */
1206function rmdirSync(path, options) {
1207  path = getValidatedPath(path);
1208
1209  if (options?.recursive) {
1210    emitRecursiveRmdirWarning();
1211    options = validateRmOptionsSync(path, { ...options, force: false }, true);
1212    if (options !== false) {
1213      lazyLoadRimraf();
1214      return rimrafSync(pathModule.toNamespacedPath(path), options);
1215    }
1216  } else {
1217    validateRmdirOptions(options);
1218  }
1219
1220  const ctx = { path };
1221  binding.rmdir(pathModule.toNamespacedPath(path), undefined, ctx);
1222  return handleErrorFromBinding(ctx);
1223}
1224
1225/**
1226 * Asynchronously removes files and
1227 * directories (modeled on the standard POSIX `rm` utility).
1228 * @param {string | Buffer | URL} path
1229 * @param {{
1230 *   force?: boolean;
1231 *   maxRetries?: number;
1232 *   recursive?: boolean;
1233 *   retryDelay?: number;
1234 *   }} [options]
1235 * @param {(err?: Error) => any} callback
1236 * @returns {void}
1237 */
1238function rm(path, options, callback) {
1239  if (typeof options === 'function') {
1240    callback = options;
1241    options = undefined;
1242  }
1243  path = getValidatedPath(path);
1244
1245  validateRmOptions(path, options, false, (err, options) => {
1246    if (err) {
1247      return callback(err);
1248    }
1249    lazyLoadRimraf();
1250    return rimraf(pathModule.toNamespacedPath(path), options, callback);
1251  });
1252}
1253
1254/**
1255 * Synchronously removes files and
1256 * directories (modeled on the standard POSIX `rm` utility).
1257 * @param {string | Buffer | URL} path
1258 * @param {{
1259 *   force?: boolean;
1260 *   maxRetries?: number;
1261 *   recursive?: boolean;
1262 *   retryDelay?: number;
1263 *   }} [options]
1264 * @returns {void}
1265 */
1266function rmSync(path, options) {
1267  path = getValidatedPath(path);
1268  options = validateRmOptionsSync(path, options, false);
1269
1270  lazyLoadRimraf();
1271  return rimrafSync(pathModule.toNamespacedPath(path), options);
1272}
1273
1274/**
1275 * Forces all currently queued I/O operations associated
1276 * with the file to the operating system's synchronized
1277 * I/O completion state.
1278 * @param {number} fd
1279 * @param {(err?: Error) => any} callback
1280 * @returns {void}
1281 */
1282function fdatasync(fd, callback) {
1283  fd = getValidatedFd(fd);
1284  const req = new FSReqCallback();
1285  req.oncomplete = makeCallback(callback);
1286  binding.fdatasync(fd, req);
1287}
1288
1289/**
1290 * Synchronously forces all currently queued I/O operations
1291 * associated with the file to the operating
1292 * system's synchronized I/O completion state.
1293 * @param {number} fd
1294 * @returns {void}
1295 */
1296function fdatasyncSync(fd) {
1297  fd = getValidatedFd(fd);
1298  const ctx = {};
1299  binding.fdatasync(fd, undefined, ctx);
1300  handleErrorFromBinding(ctx);
1301}
1302
1303/**
1304 * Requests for all data for the open file descriptor
1305 * to be flushed to the storage device.
1306 * @param {number} fd
1307 * @param {(err?: Error) => any} callback
1308 * @returns {void}
1309 */
1310function fsync(fd, callback) {
1311  fd = getValidatedFd(fd);
1312  const req = new FSReqCallback();
1313  req.oncomplete = makeCallback(callback);
1314  binding.fsync(fd, req);
1315}
1316
1317/**
1318 * Synchronously requests for all data for the open
1319 * file descriptor to be flushed to the storage device.
1320 * @param {number} fd
1321 * @returns {void}
1322 */
1323function fsyncSync(fd) {
1324  fd = getValidatedFd(fd);
1325  const ctx = {};
1326  binding.fsync(fd, undefined, ctx);
1327  handleErrorFromBinding(ctx);
1328}
1329
1330/**
1331 * Asynchronously creates a directory.
1332 * @param {string | Buffer | URL} path
1333 * @param {{
1334 *   recursive?: boolean;
1335 *   mode?: string | number;
1336 *   } | number} [options]
1337 * @param {(err?: Error) => any} callback
1338 * @returns {void}
1339 */
1340function mkdir(path, options, callback) {
1341  let mode = 0o777;
1342  let recursive = false;
1343  if (typeof options === 'function') {
1344    callback = options;
1345  } else if (typeof options === 'number' || typeof options === 'string') {
1346    mode = options;
1347  } else if (options) {
1348    if (options.recursive !== undefined)
1349      recursive = options.recursive;
1350    if (options.mode !== undefined)
1351      mode = options.mode;
1352  }
1353  callback = makeCallback(callback);
1354  path = getValidatedPath(path);
1355
1356  validateBoolean(recursive, 'options.recursive');
1357
1358  const req = new FSReqCallback();
1359  req.oncomplete = callback;
1360  binding.mkdir(pathModule.toNamespacedPath(path),
1361                parseFileMode(mode, 'mode'), recursive, req);
1362}
1363
1364/**
1365 * Synchronously creates a directory.
1366 * @param {string | Buffer | URL} path
1367 * @param {{
1368 *   recursive?: boolean;
1369 *   mode?: string | number;
1370 *   } | number} [options]
1371 * @returns {string | void}
1372 */
1373function mkdirSync(path, options) {
1374  let mode = 0o777;
1375  let recursive = false;
1376  if (typeof options === 'number' || typeof options === 'string') {
1377    mode = options;
1378  } else if (options) {
1379    if (options.recursive !== undefined)
1380      recursive = options.recursive;
1381    if (options.mode !== undefined)
1382      mode = options.mode;
1383  }
1384  path = getValidatedPath(path);
1385  validateBoolean(recursive, 'options.recursive');
1386
1387  const ctx = { path };
1388  const result = binding.mkdir(pathModule.toNamespacedPath(path),
1389                               parseFileMode(mode, 'mode'), recursive,
1390                               undefined, ctx);
1391  handleErrorFromBinding(ctx);
1392  if (recursive) {
1393    return result;
1394  }
1395}
1396
1397/**
1398 * An iterative algorithm for reading the entire contents of the `basePath` directory.
1399 * This function does not validate `basePath` as a directory. It is passed directly to
1400 * `binding.readdir`.
1401 * @param {string} basePath
1402 * @param {{ encoding: string, withFileTypes: boolean }} options
1403 * @returns {string[] | Dirent[]}
1404 */
1405function readdirSyncRecursive(basePath, options) {
1406  const withFileTypes = Boolean(options.withFileTypes);
1407  const encoding = options.encoding;
1408
1409  const readdirResults = [];
1410  const pathsQueue = [basePath];
1411
1412  const ctx = { path: basePath };
1413  function read(path) {
1414    ctx.path = path;
1415    const readdirResult = binding.readdir(
1416      pathModule.toNamespacedPath(path),
1417      encoding,
1418      withFileTypes,
1419      undefined,
1420      ctx,
1421    );
1422    handleErrorFromBinding(ctx);
1423
1424    if (withFileTypes) {
1425      // Calling `readdir` with `withFileTypes=true`, the result is an array of arrays.
1426      // The first array is the names, and the second array is the types.
1427      // They are guaranteed to be the same length; hence, setting `length` to the length
1428      // of the first array within the result.
1429      const length = readdirResult[0].length;
1430      for (let i = 0; i < length; i++) {
1431        const dirent = getDirent(path, readdirResult[0][i], readdirResult[1][i]);
1432        ArrayPrototypePush(readdirResults, dirent);
1433        if (dirent.isDirectory()) {
1434          ArrayPrototypePush(pathsQueue, pathModule.join(dirent.path, dirent.name));
1435        }
1436      }
1437    } else {
1438      for (let i = 0; i < readdirResult.length; i++) {
1439        const resultPath = pathModule.join(path, readdirResult[i]);
1440        const relativeResultPath = pathModule.relative(basePath, resultPath);
1441        const stat = binding.internalModuleStat(resultPath);
1442        ArrayPrototypePush(readdirResults, relativeResultPath);
1443        // 1 indicates directory
1444        if (stat === 1) {
1445          ArrayPrototypePush(pathsQueue, resultPath);
1446        }
1447      }
1448    }
1449  }
1450
1451  for (let i = 0; i < pathsQueue.length; i++) {
1452    read(pathsQueue[i]);
1453  }
1454
1455  return readdirResults;
1456}
1457
1458/**
1459 * Reads the contents of a directory.
1460 * @param {string | Buffer | URL} path
1461 * @param {string | {
1462 *   encoding?: string;
1463 *   withFileTypes?: boolean;
1464 *   }} [options]
1465 * @param {(
1466 *   err?: Error,
1467 *   files?: string[] | Buffer[] | Direct[];
1468 *   ) => any} callback
1469 * @returns {void}
1470 */
1471function readdir(path, options, callback) {
1472  callback = makeCallback(typeof options === 'function' ? options : callback);
1473  options = getOptions(options);
1474  path = getValidatedPath(path);
1475  if (options.recursive != null) {
1476    validateBoolean(options.recursive, 'options.recursive');
1477  }
1478
1479  if (options.recursive) {
1480    callback(null, readdirSyncRecursive(path, options));
1481    return;
1482  }
1483
1484  const req = new FSReqCallback();
1485  if (!options.withFileTypes) {
1486    req.oncomplete = callback;
1487  } else {
1488    req.oncomplete = (err, result) => {
1489      if (err) {
1490        callback(err);
1491        return;
1492      }
1493      getDirents(path, result, callback);
1494    };
1495  }
1496  binding.readdir(pathModule.toNamespacedPath(path), options.encoding,
1497                  !!options.withFileTypes, req);
1498}
1499
1500/**
1501 * Synchronously reads the contents of a directory.
1502 * @param {string | Buffer | URL} path
1503 * @param {string | {
1504 *   encoding?: string;
1505 *   withFileTypes?: boolean;
1506 *   recursive?: boolean;
1507 *   }} [options]
1508 * @returns {string | Buffer[] | Dirent[]}
1509 */
1510function readdirSync(path, options) {
1511  options = getOptions(options);
1512  path = getValidatedPath(path);
1513  if (options.recursive != null) {
1514    validateBoolean(options.recursive, 'options.recursive');
1515  }
1516
1517  if (options.recursive) {
1518    return readdirSyncRecursive(path, options);
1519  }
1520
1521  const ctx = { path };
1522  const result = binding.readdir(pathModule.toNamespacedPath(path),
1523                                 options.encoding, !!options.withFileTypes,
1524                                 undefined, ctx);
1525  handleErrorFromBinding(ctx);
1526  return options.withFileTypes ? getDirents(path, result) : result;
1527}
1528
1529/**
1530 * Invokes the callback with the `fs.Stats`
1531 * for the file descriptor.
1532 * @param {number} fd
1533 * @param {{ bigint?: boolean; }} [options]
1534 * @param {(
1535 *   err?: Error,
1536 *   stats?: Stats
1537 *   ) => any} callback
1538 * @returns {void}
1539 */
1540function fstat(fd, options = { bigint: false }, callback) {
1541  if (typeof options === 'function') {
1542    callback = options;
1543    options = kEmptyObject;
1544  }
1545  fd = getValidatedFd(fd);
1546  callback = makeStatsCallback(callback);
1547
1548  const req = new FSReqCallback(options.bigint);
1549  req.oncomplete = callback;
1550  binding.fstat(fd, options.bigint, req);
1551}
1552
1553/**
1554 * Retrieves the `fs.Stats` for the symbolic link
1555 * referred to by the `path`.
1556 * @param {string | Buffer | URL} path
1557 * @param {{ bigint?: boolean; }} [options]
1558 * @param {(
1559 *   err?: Error,
1560 *   stats?: Stats
1561 *   ) => any} callback
1562 * @returns {void}
1563 */
1564function lstat(path, options = { bigint: false }, callback) {
1565  if (typeof options === 'function') {
1566    callback = options;
1567    options = kEmptyObject;
1568  }
1569  callback = makeStatsCallback(callback);
1570  path = getValidatedPath(path);
1571
1572  const req = new FSReqCallback(options.bigint);
1573  req.oncomplete = callback;
1574  binding.lstat(pathModule.toNamespacedPath(path), options.bigint, req);
1575}
1576
1577/**
1578 * Asynchronously gets the stats of a file.
1579 * @param {string | Buffer | URL} path
1580 * @param {{ bigint?: boolean; }} [options]
1581 * @param {(
1582 *   err?: Error,
1583 *   stats?: Stats
1584 *   ) => any} callback
1585 * @returns {void}
1586 */
1587function stat(path, options = { bigint: false }, callback) {
1588  if (typeof options === 'function') {
1589    callback = options;
1590    options = kEmptyObject;
1591  }
1592  callback = makeStatsCallback(callback);
1593  path = getValidatedPath(path);
1594
1595  const req = new FSReqCallback(options.bigint);
1596  req.oncomplete = callback;
1597  binding.stat(pathModule.toNamespacedPath(path), options.bigint, req);
1598}
1599
1600function statfs(path, options = { bigint: false }, callback) {
1601  if (typeof options === 'function') {
1602    callback = options;
1603    options = kEmptyObject;
1604  }
1605  callback = maybeCallback(callback);
1606  path = getValidatedPath(path);
1607  const req = new FSReqCallback(options.bigint);
1608  req.oncomplete = (err, stats) => {
1609    if (err) {
1610      return callback(err);
1611    }
1612
1613    callback(err, getStatFsFromBinding(stats));
1614  };
1615  binding.statfs(pathModule.toNamespacedPath(path), options.bigint, req);
1616}
1617
1618function hasNoEntryError(ctx) {
1619  if (ctx.errno) {
1620    const uvErr = uvErrmapGet(ctx.errno);
1621    return uvErr?.[0] === 'ENOENT';
1622  }
1623
1624  if (ctx.error) {
1625    return ctx.error.code === 'ENOENT';
1626  }
1627
1628  return false;
1629}
1630
1631/**
1632 * Synchronously retrieves the `fs.Stats` for
1633 * the file descriptor.
1634 * @param {number} fd
1635 * @param {{
1636 *   bigint?: boolean;
1637 *   }} [options]
1638 * @returns {Stats}
1639 */
1640function fstatSync(fd, options = { bigint: false }) {
1641  fd = getValidatedFd(fd);
1642  const ctx = { fd };
1643  const stats = binding.fstat(fd, options.bigint, undefined, ctx);
1644  handleErrorFromBinding(ctx);
1645  return getStatsFromBinding(stats);
1646}
1647
1648/**
1649 * Synchronously retrieves the `fs.Stats` for
1650 * the symbolic link referred to by the `path`.
1651 * @param {string | Buffer | URL} path
1652 * @param {{
1653 *   bigint?: boolean;
1654 *   throwIfNoEntry?: boolean;
1655 *   }} [options]
1656 * @returns {Stats}
1657 */
1658function lstatSync(path, options = { bigint: false, throwIfNoEntry: true }) {
1659  path = getValidatedPath(path);
1660  const ctx = { path };
1661  const stats = binding.lstat(pathModule.toNamespacedPath(path),
1662                              options.bigint, undefined, ctx);
1663  if (options.throwIfNoEntry === false && hasNoEntryError(ctx)) {
1664    return undefined;
1665  }
1666  handleErrorFromBinding(ctx);
1667  return getStatsFromBinding(stats);
1668}
1669
1670/**
1671 * Synchronously retrieves the `fs.Stats`
1672 * for the `path`.
1673 * @param {string | Buffer | URL} path
1674 * @param {{
1675 *   bigint?: boolean;
1676 *   throwIfNoEntry?: boolean;
1677 *   }} [options]
1678 * @returns {Stats}
1679 */
1680function statSync(path, options = { bigint: false, throwIfNoEntry: true }) {
1681  path = getValidatedPath(path);
1682  const ctx = { path };
1683  const stats = binding.stat(pathModule.toNamespacedPath(path),
1684                             options.bigint, undefined, ctx);
1685  if (options.throwIfNoEntry === false && hasNoEntryError(ctx)) {
1686    return undefined;
1687  }
1688  handleErrorFromBinding(ctx);
1689  return getStatsFromBinding(stats);
1690}
1691
1692function statfsSync(path, options = { bigint: false }) {
1693  path = getValidatedPath(path);
1694  const ctx = { path };
1695  const stats = binding.statfs(pathModule.toNamespacedPath(path),
1696                               options.bigint, undefined, ctx);
1697  handleErrorFromBinding(ctx);
1698  return getStatFsFromBinding(stats);
1699}
1700
1701/**
1702 * Reads the contents of a symbolic link
1703 * referred to by `path`.
1704 * @param {string | Buffer | URL} path
1705 * @param {{ encoding?: string; } | string} [options]
1706 * @param {(
1707 *   err?: Error,
1708 *   linkString?: string | Buffer
1709 *   ) => any} callback
1710 * @returns {void}
1711 */
1712function readlink(path, options, callback) {
1713  callback = makeCallback(typeof options === 'function' ? options : callback);
1714  options = getOptions(options);
1715  path = getValidatedPath(path, 'oldPath');
1716  const req = new FSReqCallback();
1717  req.oncomplete = callback;
1718  binding.readlink(pathModule.toNamespacedPath(path), options.encoding, req);
1719}
1720
1721/**
1722 * Synchronously reads the contents of a symbolic link
1723 * referred to by `path`.
1724 * @param {string | Buffer | URL} path
1725 * @param {{ encoding?: string; } | string} [options]
1726 * @returns {string | Buffer}
1727 */
1728function readlinkSync(path, options) {
1729  options = getOptions(options);
1730  path = getValidatedPath(path, 'oldPath');
1731  const ctx = { path };
1732  const result = binding.readlink(pathModule.toNamespacedPath(path),
1733                                  options.encoding, undefined, ctx);
1734  handleErrorFromBinding(ctx);
1735  return result;
1736}
1737
1738/**
1739 * Creates the link called `path` pointing to `target`.
1740 * @param {string | Buffer | URL} target
1741 * @param {string | Buffer | URL} path
1742 * @param {string | null} [type_]
1743 * @param {(err?: Error) => any} callback_
1744 * @returns {void}
1745 */
1746function symlink(target, path, type_, callback_) {
1747  const type = (typeof type_ === 'string' ? type_ : null);
1748  const callback = makeCallback(arguments[arguments.length - 1]);
1749
1750  target = getValidatedPath(target, 'target');
1751  path = getValidatedPath(path);
1752
1753  if (isWindows && type === null) {
1754    let absoluteTarget;
1755    try {
1756      // Symlinks targets can be relative to the newly created path.
1757      // Calculate absolute file name of the symlink target, and check
1758      // if it is a directory. Ignore resolve error to keep symlink
1759      // errors consistent between platforms if invalid path is
1760      // provided.
1761      absoluteTarget = pathModule.resolve(path, '..', target);
1762    } catch {
1763      // Continue regardless of error.
1764    }
1765    if (absoluteTarget !== undefined) {
1766      stat(absoluteTarget, (err, stat) => {
1767        const resolvedType = !err && stat.isDirectory() ? 'dir' : 'file';
1768        const resolvedFlags = stringToSymlinkType(resolvedType);
1769        const destination = preprocessSymlinkDestination(target,
1770                                                         resolvedType,
1771                                                         path);
1772
1773        const req = new FSReqCallback();
1774        req.oncomplete = callback;
1775        binding.symlink(destination,
1776                        pathModule.toNamespacedPath(path), resolvedFlags, req);
1777      });
1778      return;
1779    }
1780  }
1781
1782  const destination = preprocessSymlinkDestination(target, type, path);
1783
1784  const flags = stringToSymlinkType(type);
1785  const req = new FSReqCallback();
1786  req.oncomplete = callback;
1787  binding.symlink(destination, pathModule.toNamespacedPath(path), flags, req);
1788}
1789
1790/**
1791 * Synchronously creates the link called `path`
1792 * pointing to `target`.
1793 * @param {string | Buffer | URL} target
1794 * @param {string | Buffer | URL} path
1795 * @param {string | null} [type]
1796 * @returns {void}
1797 */
1798function symlinkSync(target, path, type) {
1799  type = (typeof type === 'string' ? type : null);
1800  if (isWindows && type === null) {
1801    const absoluteTarget = pathModule.resolve(`${path}`, '..', `${target}`);
1802    if (statSync(absoluteTarget, { throwIfNoEntry: false })?.isDirectory()) {
1803      type = 'dir';
1804    }
1805  }
1806  target = getValidatedPath(target, 'target');
1807  path = getValidatedPath(path);
1808  const flags = stringToSymlinkType(type);
1809
1810  const ctx = { path: target, dest: path };
1811  binding.symlink(preprocessSymlinkDestination(target, type, path),
1812                  pathModule.toNamespacedPath(path), flags, undefined, ctx);
1813
1814  handleErrorFromBinding(ctx);
1815}
1816
1817/**
1818 * Creates a new link from the `existingPath`
1819 * to the `newPath`.
1820 * @param {string | Buffer | URL} existingPath
1821 * @param {string | Buffer | URL} newPath
1822 * @param {(err?: Error) => any} callback
1823 * @returns {void}
1824 */
1825function link(existingPath, newPath, callback) {
1826  callback = makeCallback(callback);
1827
1828  existingPath = getValidatedPath(existingPath, 'existingPath');
1829  newPath = getValidatedPath(newPath, 'newPath');
1830
1831  const req = new FSReqCallback();
1832  req.oncomplete = callback;
1833
1834  binding.link(pathModule.toNamespacedPath(existingPath),
1835               pathModule.toNamespacedPath(newPath),
1836               req);
1837}
1838
1839/**
1840 * Synchronously creates a new link from the `existingPath`
1841 * to the `newPath`.
1842 * @param {string | Buffer | URL} existingPath
1843 * @param {string | Buffer | URL} newPath
1844 * @returns {void}
1845 */
1846function linkSync(existingPath, newPath) {
1847  existingPath = getValidatedPath(existingPath, 'existingPath');
1848  newPath = getValidatedPath(newPath, 'newPath');
1849
1850  const ctx = { path: existingPath, dest: newPath };
1851  const result = binding.link(pathModule.toNamespacedPath(existingPath),
1852                              pathModule.toNamespacedPath(newPath),
1853                              undefined, ctx);
1854  handleErrorFromBinding(ctx);
1855  return result;
1856}
1857
1858/**
1859 * Asynchronously removes a file or symbolic link.
1860 * @param {string | Buffer | URL} path
1861 * @param {(err?: Error) => any} callback
1862 * @returns {void}
1863 */
1864function unlink(path, callback) {
1865  callback = makeCallback(callback);
1866  path = getValidatedPath(path);
1867  const req = new FSReqCallback();
1868  req.oncomplete = callback;
1869  binding.unlink(pathModule.toNamespacedPath(path), req);
1870}
1871
1872/**
1873 * Synchronously removes a file or symbolic link.
1874 * @param {string | Buffer | URL} path
1875 * @returns {void}
1876 */
1877function unlinkSync(path) {
1878  path = getValidatedPath(path);
1879  const ctx = { path };
1880  binding.unlink(pathModule.toNamespacedPath(path), undefined, ctx);
1881  handleErrorFromBinding(ctx);
1882}
1883
1884/**
1885 * Sets the permissions on the file.
1886 * @param {number} fd
1887 * @param {string | number} mode
1888 * @param {(err?: Error) => any} callback
1889 * @returns {void}
1890 */
1891function fchmod(fd, mode, callback) {
1892  fd = getValidatedFd(fd);
1893  mode = parseFileMode(mode, 'mode');
1894  callback = makeCallback(callback);
1895
1896  const req = new FSReqCallback();
1897  req.oncomplete = callback;
1898  binding.fchmod(fd, mode, req);
1899}
1900
1901/**
1902 * Synchronously sets the permissions on the file.
1903 * @param {number} fd
1904 * @param {string | number} mode
1905 * @returns {void}
1906 */
1907function fchmodSync(fd, mode) {
1908  fd = getValidatedFd(fd);
1909  mode = parseFileMode(mode, 'mode');
1910  const ctx = {};
1911  binding.fchmod(fd, mode, undefined, ctx);
1912  handleErrorFromBinding(ctx);
1913}
1914
1915/**
1916 * Changes the permissions on a symbolic link.
1917 * @param {string | Buffer | URL} path
1918 * @param {number} mode
1919 * @param {(err?: Error) => any} callback
1920 * @returns {void}
1921 */
1922function lchmod(path, mode, callback) {
1923  callback = maybeCallback(callback);
1924  mode = parseFileMode(mode, 'mode');
1925  fs.open(path, O_WRONLY | O_SYMLINK, (err, fd) => {
1926    if (err) {
1927      callback(err);
1928      return;
1929    }
1930    // Prefer to return the chmod error, if one occurs,
1931    // but still try to close, and report closing errors if they occur.
1932    fs.fchmod(fd, mode, (err) => {
1933      fs.close(fd, (err2) => {
1934        callback(aggregateTwoErrors(err2, err));
1935      });
1936    });
1937  });
1938}
1939
1940/**
1941 * Synchronously changes the permissions on a symbolic link.
1942 * @param {string | Buffer | URL} path
1943 * @param {number} mode
1944 * @returns {void}
1945 */
1946function lchmodSync(path, mode) {
1947  const fd = fs.openSync(path, O_WRONLY | O_SYMLINK);
1948
1949  // Prefer to return the chmod error, if one occurs,
1950  // but still try to close, and report closing errors if they occur.
1951  let ret;
1952  try {
1953    ret = fs.fchmodSync(fd, mode);
1954  } finally {
1955    fs.closeSync(fd);
1956  }
1957  return ret;
1958}
1959
1960/**
1961 * Asynchronously changes the permissions of a file.
1962 * @param {string | Buffer | URL} path
1963 * @param {string | number} mode
1964 * @param {(err?: Error) => any} callback
1965 * @returns {void}
1966 */
1967function chmod(path, mode, callback) {
1968  path = getValidatedPath(path);
1969  mode = parseFileMode(mode, 'mode');
1970  callback = makeCallback(callback);
1971
1972  const req = new FSReqCallback();
1973  req.oncomplete = callback;
1974  binding.chmod(pathModule.toNamespacedPath(path), mode, req);
1975}
1976
1977/**
1978 * Synchronously changes the permissions of a file.
1979 * @param {string | Buffer | URL} path
1980 * @param {string | number} mode
1981 * @returns {void}
1982 */
1983function chmodSync(path, mode) {
1984  path = getValidatedPath(path);
1985  mode = parseFileMode(mode, 'mode');
1986
1987  const ctx = { path };
1988  binding.chmod(pathModule.toNamespacedPath(path), mode, undefined, ctx);
1989  handleErrorFromBinding(ctx);
1990}
1991
1992/**
1993 * Sets the owner of the symbolic link.
1994 * @param {string | Buffer | URL} path
1995 * @param {number} uid
1996 * @param {number} gid
1997 * @param {(err?: Error) => any} callback
1998 * @returns {void}
1999 */
2000function lchown(path, uid, gid, callback) {
2001  callback = makeCallback(callback);
2002  path = getValidatedPath(path);
2003  validateInteger(uid, 'uid', -1, kMaxUserId);
2004  validateInteger(gid, 'gid', -1, kMaxUserId);
2005  const req = new FSReqCallback();
2006  req.oncomplete = callback;
2007  binding.lchown(pathModule.toNamespacedPath(path), uid, gid, req);
2008}
2009
2010/**
2011 * Synchronously sets the owner of the symbolic link.
2012 * @param {string | Buffer | URL} path
2013 * @param {number} uid
2014 * @param {number} gid
2015 * @returns {void}
2016 */
2017function lchownSync(path, uid, gid) {
2018  path = getValidatedPath(path);
2019  validateInteger(uid, 'uid', -1, kMaxUserId);
2020  validateInteger(gid, 'gid', -1, kMaxUserId);
2021  const ctx = { path };
2022  binding.lchown(pathModule.toNamespacedPath(path), uid, gid, undefined, ctx);
2023  handleErrorFromBinding(ctx);
2024}
2025
2026/**
2027 * Sets the owner of the file.
2028 * @param {number} fd
2029 * @param {number} uid
2030 * @param {number} gid
2031 * @param {(err?: Error) => any} callback
2032 * @returns {void}
2033 */
2034function fchown(fd, uid, gid, callback) {
2035  fd = getValidatedFd(fd);
2036  validateInteger(uid, 'uid', -1, kMaxUserId);
2037  validateInteger(gid, 'gid', -1, kMaxUserId);
2038  callback = makeCallback(callback);
2039
2040  const req = new FSReqCallback();
2041  req.oncomplete = callback;
2042  binding.fchown(fd, uid, gid, req);
2043}
2044
2045/**
2046 * Synchronously sets the owner of the file.
2047 * @param {number} fd
2048 * @param {number} uid
2049 * @param {number} gid
2050 * @returns {void}
2051 */
2052function fchownSync(fd, uid, gid) {
2053  fd = getValidatedFd(fd);
2054  validateInteger(uid, 'uid', -1, kMaxUserId);
2055  validateInteger(gid, 'gid', -1, kMaxUserId);
2056
2057  const ctx = {};
2058  binding.fchown(fd, uid, gid, undefined, ctx);
2059  handleErrorFromBinding(ctx);
2060}
2061
2062/**
2063 * Asynchronously changes the owner and group
2064 * of a file.
2065 * @param {string | Buffer | URL} path
2066 * @param {number} uid
2067 * @param {number} gid
2068 * @param {(err?: Error) => any} callback
2069 * @returns {void}
2070 */
2071function chown(path, uid, gid, callback) {
2072  callback = makeCallback(callback);
2073  path = getValidatedPath(path);
2074  validateInteger(uid, 'uid', -1, kMaxUserId);
2075  validateInteger(gid, 'gid', -1, kMaxUserId);
2076
2077  const req = new FSReqCallback();
2078  req.oncomplete = callback;
2079  binding.chown(pathModule.toNamespacedPath(path), uid, gid, req);
2080}
2081
2082/**
2083 * Synchronously changes the owner and group
2084 * of a file.
2085 * @param {string | Buffer | URL} path
2086 * @param {number} uid
2087 * @param {number} gid
2088 * @returns {void}
2089 */
2090function chownSync(path, uid, gid) {
2091  path = getValidatedPath(path);
2092  validateInteger(uid, 'uid', -1, kMaxUserId);
2093  validateInteger(gid, 'gid', -1, kMaxUserId);
2094  const ctx = { path };
2095  binding.chown(pathModule.toNamespacedPath(path), uid, gid, undefined, ctx);
2096  handleErrorFromBinding(ctx);
2097}
2098
2099/**
2100 * Changes the file system timestamps of the object
2101 * referenced by `path`.
2102 * @param {string | Buffer | URL} path
2103 * @param {number | string | Date} atime
2104 * @param {number | string | Date} mtime
2105 * @param {(err?: Error) => any} callback
2106 * @returns {void}
2107 */
2108function utimes(path, atime, mtime, callback) {
2109  callback = makeCallback(callback);
2110  path = getValidatedPath(path);
2111
2112  const req = new FSReqCallback();
2113  req.oncomplete = callback;
2114  binding.utimes(pathModule.toNamespacedPath(path),
2115                 toUnixTimestamp(atime),
2116                 toUnixTimestamp(mtime),
2117                 req);
2118}
2119
2120/**
2121 * Synchronously changes the file system timestamps
2122 * of the object referenced by `path`.
2123 * @param {string | Buffer | URL} path
2124 * @param {number | string | Date} atime
2125 * @param {number | string | Date} mtime
2126 * @returns {void}
2127 */
2128function utimesSync(path, atime, mtime) {
2129  path = getValidatedPath(path);
2130  const ctx = { path };
2131  binding.utimes(pathModule.toNamespacedPath(path),
2132                 toUnixTimestamp(atime), toUnixTimestamp(mtime),
2133                 undefined, ctx);
2134  handleErrorFromBinding(ctx);
2135}
2136
2137/**
2138 * Changes the file system timestamps of the object
2139 * referenced by the supplied `fd` (file descriptor).
2140 * @param {number} fd
2141 * @param {number | string | Date} atime
2142 * @param {number | string | Date} mtime
2143 * @param {(err?: Error) => any} callback
2144 * @returns {void}
2145 */
2146function futimes(fd, atime, mtime, callback) {
2147  fd = getValidatedFd(fd);
2148  atime = toUnixTimestamp(atime, 'atime');
2149  mtime = toUnixTimestamp(mtime, 'mtime');
2150  callback = makeCallback(callback);
2151
2152  const req = new FSReqCallback();
2153  req.oncomplete = callback;
2154  binding.futimes(fd, atime, mtime, req);
2155}
2156
2157/**
2158 * Synchronously changes the file system timestamps
2159 * of the object referenced by the
2160 * supplied `fd` (file descriptor).
2161 * @param {number} fd
2162 * @param {number | string | Date} atime
2163 * @param {number | string | Date} mtime
2164 * @returns {void}
2165 */
2166function futimesSync(fd, atime, mtime) {
2167  fd = getValidatedFd(fd);
2168  atime = toUnixTimestamp(atime, 'atime');
2169  mtime = toUnixTimestamp(mtime, 'mtime');
2170  const ctx = {};
2171  binding.futimes(fd, atime, mtime, undefined, ctx);
2172  handleErrorFromBinding(ctx);
2173}
2174
2175/**
2176 * Changes the access and modification times of
2177 * a file in the same way as `fs.utimes()`.
2178 * @param {string | Buffer | URL} path
2179 * @param {number | string | Date} atime
2180 * @param {number | string | Date} mtime
2181 * @param {(err?: Error) => any} callback
2182 * @returns {void}
2183 */
2184function lutimes(path, atime, mtime, callback) {
2185  callback = makeCallback(callback);
2186  path = getValidatedPath(path);
2187
2188  const req = new FSReqCallback();
2189  req.oncomplete = callback;
2190  binding.lutimes(pathModule.toNamespacedPath(path),
2191                  toUnixTimestamp(atime),
2192                  toUnixTimestamp(mtime),
2193                  req);
2194}
2195
2196/**
2197 * Synchronously changes the access and modification
2198 * times of a file in the same way as `fs.utimesSync()`.
2199 * @param {string | Buffer | URL} path
2200 * @param {number | string | Date} atime
2201 * @param {number | string | Date} mtime
2202 * @returns {void}
2203 */
2204function lutimesSync(path, atime, mtime) {
2205  path = getValidatedPath(path);
2206  const ctx = { path };
2207  binding.lutimes(pathModule.toNamespacedPath(path),
2208                  toUnixTimestamp(atime),
2209                  toUnixTimestamp(mtime),
2210                  undefined, ctx);
2211  handleErrorFromBinding(ctx);
2212}
2213
2214function writeAll(fd, isUserFd, buffer, offset, length, signal, callback) {
2215  if (signal?.aborted) {
2216    const abortError = new AbortError(undefined, { cause: signal?.reason });
2217    if (isUserFd) {
2218      callback(abortError);
2219    } else {
2220      fs.close(fd, (err) => {
2221        callback(aggregateTwoErrors(err, abortError));
2222      });
2223    }
2224    return;
2225  }
2226  // write(fd, buffer, offset, length, position, callback)
2227  fs.write(fd, buffer, offset, length, null, (writeErr, written) => {
2228    if (writeErr) {
2229      if (isUserFd) {
2230        callback(writeErr);
2231      } else {
2232        fs.close(fd, (err) => {
2233          callback(aggregateTwoErrors(err, writeErr));
2234        });
2235      }
2236    } else if (written === length) {
2237      if (isUserFd) {
2238        callback(null);
2239      } else {
2240        fs.close(fd, callback);
2241      }
2242    } else {
2243      offset += written;
2244      length -= written;
2245      writeAll(fd, isUserFd, buffer, offset, length, signal, callback);
2246    }
2247  });
2248}
2249
2250/**
2251 * Asynchronously writes data to the file.
2252 * @param {string | Buffer | URL | number} path
2253 * @param {string | Buffer | TypedArray | DataView | object} data
2254 * @param {{
2255 *   encoding?: string | null;
2256 *   mode?: number;
2257 *   flag?: string;
2258 *   signal?: AbortSignal;
2259 *   } | string} [options]
2260 * @param {(err?: Error) => any} callback
2261 * @returns {void}
2262 */
2263function writeFile(path, data, options, callback) {
2264  callback = maybeCallback(callback || options);
2265  options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'w' });
2266  const flag = options.flag || 'w';
2267
2268  if (!isArrayBufferView(data)) {
2269    validateStringAfterArrayBufferView(data, 'data');
2270    if (typeof data !== 'string') {
2271      showStringCoercionDeprecation();
2272    }
2273    data = Buffer.from(String(data), options.encoding || 'utf8');
2274  }
2275
2276  if (isFd(path)) {
2277    const isUserFd = true;
2278    const signal = options.signal;
2279    writeAll(path, isUserFd, data, 0, data.byteLength, signal, callback);
2280    return;
2281  }
2282
2283  if (checkAborted(options.signal, callback))
2284    return;
2285
2286  fs.open(path, flag, options.mode, (openErr, fd) => {
2287    if (openErr) {
2288      callback(openErr);
2289    } else {
2290      const isUserFd = false;
2291      const signal = options.signal;
2292      writeAll(fd, isUserFd, data, 0, data.byteLength, signal, callback);
2293    }
2294  });
2295}
2296
2297/**
2298 * Synchronously writes data to the file.
2299 * @param {string | Buffer | URL | number} path
2300 * @param {string | Buffer | TypedArray | DataView | object} data
2301 * @param {{
2302 *   encoding?: string | null;
2303 *   mode?: number;
2304 *   flag?: string;
2305 *   } | string} [options]
2306 * @returns {void}
2307 */
2308function writeFileSync(path, data, options) {
2309  options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'w' });
2310
2311  if (!isArrayBufferView(data)) {
2312    validateStringAfterArrayBufferView(data, 'data');
2313    if (typeof data !== 'string') {
2314      showStringCoercionDeprecation();
2315    }
2316    data = Buffer.from(String(data), options.encoding || 'utf8');
2317  }
2318
2319  const flag = options.flag || 'w';
2320
2321  const isUserFd = isFd(path); // File descriptor ownership
2322  const fd = isUserFd ? path : fs.openSync(path, flag, options.mode);
2323
2324  let offset = 0;
2325  let length = data.byteLength;
2326  try {
2327    while (length > 0) {
2328      const written = fs.writeSync(fd, data, offset, length);
2329      offset += written;
2330      length -= written;
2331    }
2332  } finally {
2333    if (!isUserFd) fs.closeSync(fd);
2334  }
2335}
2336
2337/**
2338 * Asynchronously appends data to a file.
2339 * @param {string | Buffer | URL | number} path
2340 * @param {string | Buffer} data
2341 * @param {{
2342 *   encoding?: string | null;
2343 *   mode?: number;
2344 *   flag?: string;
2345 *   } | string} [options]
2346 * @param {(err?: Error) => any} callback
2347 * @returns {void}
2348 */
2349function appendFile(path, data, options, callback) {
2350  callback = maybeCallback(callback || options);
2351  options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'a' });
2352
2353  // Don't make changes directly on options object
2354  options = copyObject(options);
2355
2356  // Force append behavior when using a supplied file descriptor
2357  if (!options.flag || isFd(path))
2358    options.flag = 'a';
2359
2360  fs.writeFile(path, data, options, callback);
2361}
2362
2363/**
2364 * Synchronously appends data to a file.
2365 * @param {string | Buffer | URL | number} path
2366 * @param {string | Buffer} data
2367 * @param {{
2368 *   encoding?: string | null;
2369 *   mode?: number;
2370 *   flag?: string;
2371 *   } | string} [options]
2372 * @returns {void}
2373 */
2374function appendFileSync(path, data, options) {
2375  options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'a' });
2376
2377  // Don't make changes directly on options object
2378  options = copyObject(options);
2379
2380  // Force append behavior when using a supplied file descriptor
2381  if (!options.flag || isFd(path))
2382    options.flag = 'a';
2383
2384  fs.writeFileSync(path, data, options);
2385}
2386
2387/**
2388 * Watches for the changes on `filename`.
2389 * @param {string | Buffer | URL} filename
2390 * @param {string | {
2391 *   persistent?: boolean;
2392 *   recursive?: boolean;
2393 *   encoding?: string;
2394 *   signal?: AbortSignal;
2395 *   }} [options]
2396 * @param {(
2397 *   eventType?: string,
2398 *   filename?: string | Buffer
2399 *   ) => any} [listener]
2400 * @returns {watchers.FSWatcher}
2401 */
2402function watch(filename, options, listener) {
2403  if (typeof options === 'function') {
2404    listener = options;
2405  }
2406  options = getOptions(options);
2407
2408  // Don't make changes directly on options object
2409  options = copyObject(options);
2410
2411  if (options.persistent === undefined) options.persistent = true;
2412  if (options.recursive === undefined) options.recursive = false;
2413  if (options.recursive && !(isOSX || isWindows))
2414    throw new ERR_FEATURE_UNAVAILABLE_ON_PLATFORM('watch recursively');
2415
2416  const watchers = require('internal/fs/watchers');
2417  const watcher = new watchers.FSWatcher();
2418  watcher[watchers.kFSWatchStart](filename,
2419                                  options.persistent,
2420                                  options.recursive,
2421                                  options.encoding);
2422  if (listener) {
2423    watcher.addListener('change', listener);
2424  }
2425  if (options.signal) {
2426    if (options.signal.aborted) {
2427      process.nextTick(() => watcher.close());
2428    } else {
2429      const listener = () => watcher.close();
2430      kResistStopPropagation ??= require('internal/event_target').kResistStopPropagation;
2431      options.signal.addEventListener('abort', listener, { __proto__: null, [kResistStopPropagation]: true });
2432      watcher.once('close', () => {
2433        options.signal.removeEventListener('abort', listener);
2434      });
2435    }
2436  }
2437
2438  return watcher;
2439}
2440
2441
2442const statWatchers = new SafeMap();
2443
2444/**
2445 * Watches for changes on `filename`.
2446 * @param {string | Buffer | URL} filename
2447 * @param {{
2448 *   bigint?: boolean;
2449 *   persistent?: boolean;
2450 *   interval?: number;
2451 *   }} [options]
2452 * @param {(
2453 *   current?: Stats,
2454 *   previous?: Stats
2455 *   ) => any} listener
2456 * @returns {watchers.StatWatcher}
2457 */
2458function watchFile(filename, options, listener) {
2459  filename = getValidatedPath(filename);
2460  filename = pathModule.resolve(filename);
2461  let stat;
2462
2463  if (options === null || typeof options !== 'object') {
2464    listener = options;
2465    options = null;
2466  }
2467
2468  options = {
2469    // Poll interval in milliseconds. 5007 is what libev used to use. It's
2470    // a little on the slow side but let's stick with it for now to keep
2471    // behavioral changes to a minimum.
2472    interval: 5007,
2473    persistent: true,
2474    ...options,
2475  };
2476
2477  validateFunction(listener, 'listener');
2478
2479  stat = statWatchers.get(filename);
2480  const watchers = require('internal/fs/watchers');
2481  if (stat === undefined) {
2482    stat = new watchers.StatWatcher(options.bigint);
2483    stat[watchers.kFSStatWatcherStart](filename,
2484                                       options.persistent, options.interval);
2485    statWatchers.set(filename, stat);
2486  } else {
2487    stat[watchers.kFSStatWatcherAddOrCleanRef]('add');
2488  }
2489
2490  stat.addListener('change', listener);
2491  return stat;
2492}
2493
2494/**
2495 * Stops watching for changes on `filename`.
2496 * @param {string | Buffer | URL} filename
2497 * @param {() => any} [listener]
2498 * @returns {void}
2499 */
2500function unwatchFile(filename, listener) {
2501  filename = getValidatedPath(filename);
2502  filename = pathModule.resolve(filename);
2503  const stat = statWatchers.get(filename);
2504
2505  if (stat === undefined) return;
2506  const watchers = require('internal/fs/watchers');
2507  if (typeof listener === 'function') {
2508    const beforeListenerCount = stat.listenerCount('change');
2509    stat.removeListener('change', listener);
2510    if (stat.listenerCount('change') < beforeListenerCount)
2511      stat[watchers.kFSStatWatcherAddOrCleanRef]('clean');
2512  } else {
2513    stat.removeAllListeners('change');
2514    stat[watchers.kFSStatWatcherAddOrCleanRef]('cleanAll');
2515  }
2516
2517  if (stat.listenerCount('change') === 0) {
2518    stat.stop();
2519    statWatchers.delete(filename);
2520  }
2521}
2522
2523
2524let splitRoot;
2525if (isWindows) {
2526  // Regex to find the device root on Windows (e.g. 'c:\\'), including trailing
2527  // slash.
2528  const splitRootRe = /^(?:[a-zA-Z]:|[\\/]{2}[^\\/]+[\\/][^\\/]+)?[\\/]*/;
2529  splitRoot = function splitRoot(str) {
2530    return SideEffectFreeRegExpPrototypeExec(splitRootRe, str)[0];
2531  };
2532} else {
2533  splitRoot = function splitRoot(str) {
2534    for (let i = 0; i < str.length; ++i) {
2535      if (StringPrototypeCharCodeAt(str, i) !== CHAR_FORWARD_SLASH)
2536        return StringPrototypeSlice(str, 0, i);
2537    }
2538    return str;
2539  };
2540}
2541
2542function encodeRealpathResult(result, options) {
2543  if (!options || !options.encoding || options.encoding === 'utf8')
2544    return result;
2545  const asBuffer = Buffer.from(result);
2546  if (options.encoding === 'buffer') {
2547    return asBuffer;
2548  }
2549  return asBuffer.toString(options.encoding);
2550}
2551
2552// Finds the next portion of a (partial) path, up to the next path delimiter
2553let nextPart;
2554if (isWindows) {
2555  nextPart = function nextPart(p, i) {
2556    for (; i < p.length; ++i) {
2557      const ch = StringPrototypeCharCodeAt(p, i);
2558
2559      // Check for a separator character
2560      if (ch === CHAR_BACKWARD_SLASH || ch === CHAR_FORWARD_SLASH)
2561        return i;
2562    }
2563    return -1;
2564  };
2565} else {
2566  nextPart = function nextPart(p, i) {
2567    return StringPrototypeIndexOf(p, '/', i);
2568  };
2569}
2570
2571/**
2572 * Returns the resolved pathname.
2573 * @param {string | Buffer | URL} p
2574 * @param {string | { encoding?: string | null; }} [options]
2575 * @returns {string | Buffer}
2576 */
2577function realpathSync(p, options) {
2578  options = getOptions(options);
2579  p = toPathIfFileURL(p);
2580  if (typeof p !== 'string') {
2581    p += '';
2582  }
2583  validatePath(p);
2584  p = pathModule.resolve(p);
2585
2586  const cache = options[realpathCacheKey];
2587  const maybeCachedResult = cache?.get(p);
2588  if (maybeCachedResult) {
2589    return maybeCachedResult;
2590  }
2591
2592  const seenLinks = new SafeMap();
2593  const knownHard = new SafeSet();
2594  const original = p;
2595
2596  // Current character position in p
2597  let pos;
2598  // The partial path so far, including a trailing slash if any
2599  let current;
2600  // The partial path without a trailing slash (except when pointing at a root)
2601  let base;
2602  // The partial path scanned in the previous round, with slash
2603  let previous;
2604
2605  // Skip over roots
2606  current = base = splitRoot(p);
2607  pos = current.length;
2608
2609  // On windows, check that the root exists. On unix there is no need.
2610  if (isWindows) {
2611    const ctx = { path: base };
2612    binding.lstat(pathModule.toNamespacedPath(base), false, undefined, ctx);
2613    handleErrorFromBinding(ctx);
2614    knownHard.add(base);
2615  }
2616
2617  // Walk down the path, swapping out linked path parts for their real
2618  // values
2619  // NB: p.length changes.
2620  while (pos < p.length) {
2621    // find the next part
2622    const result = nextPart(p, pos);
2623    previous = current;
2624    if (result === -1) {
2625      const last = StringPrototypeSlice(p, pos);
2626      current += last;
2627      base = previous + last;
2628      pos = p.length;
2629    } else {
2630      current += StringPrototypeSlice(p, pos, result + 1);
2631      base = previous + StringPrototypeSlice(p, pos, result);
2632      pos = result + 1;
2633    }
2634
2635    // Continue if not a symlink, break if a pipe/socket
2636    if (knownHard.has(base) || cache?.get(base) === base) {
2637      if (isFileType(binding.statValues, S_IFIFO) ||
2638          isFileType(binding.statValues, S_IFSOCK)) {
2639        break;
2640      }
2641      continue;
2642    }
2643
2644    let resolvedLink;
2645    const maybeCachedResolved = cache?.get(base);
2646    if (maybeCachedResolved) {
2647      resolvedLink = maybeCachedResolved;
2648    } else {
2649      // Use stats array directly to avoid creating an fs.Stats instance just
2650      // for our internal use.
2651
2652      const baseLong = pathModule.toNamespacedPath(base);
2653      const ctx = { path: base };
2654      const stats = binding.lstat(baseLong, true, undefined, ctx);
2655      handleErrorFromBinding(ctx);
2656
2657      if (!isFileType(stats, S_IFLNK)) {
2658        knownHard.add(base);
2659        cache?.set(base, base);
2660        continue;
2661      }
2662
2663      // Read the link if it wasn't read before
2664      // dev/ino always return 0 on windows, so skip the check.
2665      let linkTarget = null;
2666      let id;
2667      if (!isWindows) {
2668        const dev = BigIntPrototypeToString(stats[0], 32);
2669        const ino = BigIntPrototypeToString(stats[7], 32);
2670        id = `${dev}:${ino}`;
2671        if (seenLinks.has(id)) {
2672          linkTarget = seenLinks.get(id);
2673        }
2674      }
2675      if (linkTarget === null) {
2676        const ctx = { path: base };
2677        binding.stat(baseLong, false, undefined, ctx);
2678        handleErrorFromBinding(ctx);
2679        linkTarget = binding.readlink(baseLong, undefined, undefined, ctx);
2680        handleErrorFromBinding(ctx);
2681      }
2682      resolvedLink = pathModule.resolve(previous, linkTarget);
2683
2684      cache?.set(base, resolvedLink);
2685      if (!isWindows) seenLinks.set(id, linkTarget);
2686    }
2687
2688    // Resolve the link, then start over
2689    p = pathModule.resolve(resolvedLink, StringPrototypeSlice(p, pos));
2690
2691    // Skip over roots
2692    current = base = splitRoot(p);
2693    pos = current.length;
2694
2695    // On windows, check that the root exists. On unix there is no need.
2696    if (isWindows && !knownHard.has(base)) {
2697      const ctx = { path: base };
2698      binding.lstat(pathModule.toNamespacedPath(base), false, undefined, ctx);
2699      handleErrorFromBinding(ctx);
2700      knownHard.add(base);
2701    }
2702  }
2703
2704  cache?.set(original, p);
2705  return encodeRealpathResult(p, options);
2706}
2707
2708/**
2709 * Returns the resolved pathname.
2710 * @param {string | Buffer | URL} path
2711 * @param {string | { encoding?: string; }} [options]
2712 * @returns {string | Buffer}
2713 */
2714realpathSync.native = (path, options) => {
2715  options = getOptions(options);
2716  path = getValidatedPath(path);
2717  const ctx = { path };
2718  const result = binding.realpath(pathModule.toNamespacedPath(path), options.encoding, undefined, ctx);
2719  handleErrorFromBinding(ctx);
2720  return result;
2721};
2722
2723/**
2724 * Asynchronously computes the canonical pathname by
2725 * resolving `.`, `..` and symbolic links.
2726 * @param {string | Buffer | URL} p
2727 * @param {string | { encoding?: string; }} [options]
2728 * @param {(
2729 *   err?: Error,
2730 *   resolvedPath?: string | Buffer
2731 *   ) => any} callback
2732 * @returns {void}
2733 */
2734function realpath(p, options, callback) {
2735  callback = typeof options === 'function' ? options : maybeCallback(callback);
2736  options = getOptions(options);
2737  p = toPathIfFileURL(p);
2738
2739  if (typeof p !== 'string') {
2740    p += '';
2741  }
2742  validatePath(p);
2743  p = pathModule.resolve(p);
2744
2745  const seenLinks = new SafeMap();
2746  const knownHard = new SafeSet();
2747
2748  // Current character position in p
2749  let pos;
2750  // The partial path so far, including a trailing slash if any
2751  let current;
2752  // The partial path without a trailing slash (except when pointing at a root)
2753  let base;
2754  // The partial path scanned in the previous round, with slash
2755  let previous;
2756
2757  current = base = splitRoot(p);
2758  pos = current.length;
2759
2760  // On windows, check that the root exists. On unix there is no need.
2761  if (isWindows && !knownHard.has(base)) {
2762    fs.lstat(base, (err, stats) => {
2763      if (err) return callback(err);
2764      knownHard.add(base);
2765      LOOP();
2766    });
2767  } else {
2768    process.nextTick(LOOP);
2769  }
2770
2771  // Walk down the path, swapping out linked path parts for their real
2772  // values
2773  function LOOP() {
2774    // Stop if scanned past end of path
2775    if (pos >= p.length) {
2776      return callback(null, encodeRealpathResult(p, options));
2777    }
2778
2779    // find the next part
2780    const result = nextPart(p, pos);
2781    previous = current;
2782    if (result === -1) {
2783      const last = StringPrototypeSlice(p, pos);
2784      current += last;
2785      base = previous + last;
2786      pos = p.length;
2787    } else {
2788      current += StringPrototypeSlice(p, pos, result + 1);
2789      base = previous + StringPrototypeSlice(p, pos, result);
2790      pos = result + 1;
2791    }
2792
2793    // Continue if not a symlink, break if a pipe/socket
2794    if (knownHard.has(base)) {
2795      if (isFileType(binding.statValues, S_IFIFO) ||
2796          isFileType(binding.statValues, S_IFSOCK)) {
2797        return callback(null, encodeRealpathResult(p, options));
2798      }
2799      return process.nextTick(LOOP);
2800    }
2801
2802    return fs.lstat(base, { bigint: true }, gotStat);
2803  }
2804
2805  function gotStat(err, stats) {
2806    if (err) return callback(err);
2807
2808    // If not a symlink, skip to the next path part
2809    if (!stats.isSymbolicLink()) {
2810      knownHard.add(base);
2811      return process.nextTick(LOOP);
2812    }
2813
2814    // Stat & read the link if not read before.
2815    // Call `gotTarget()` as soon as the link target is known.
2816    // `dev`/`ino` always return 0 on windows, so skip the check.
2817    let id;
2818    if (!isWindows) {
2819      const dev = BigIntPrototypeToString(stats.dev, 32);
2820      const ino = BigIntPrototypeToString(stats.ino, 32);
2821      id = `${dev}:${ino}`;
2822      if (seenLinks.has(id)) {
2823        return gotTarget(null, seenLinks.get(id));
2824      }
2825    }
2826    fs.stat(base, (err) => {
2827      if (err) return callback(err);
2828
2829      fs.readlink(base, (err, target) => {
2830        if (!isWindows) seenLinks.set(id, target);
2831        gotTarget(err, target);
2832      });
2833    });
2834  }
2835
2836  function gotTarget(err, target) {
2837    if (err) return callback(err);
2838
2839    gotResolvedLink(pathModule.resolve(previous, target));
2840  }
2841
2842  function gotResolvedLink(resolvedLink) {
2843    // Resolve the link, then start over
2844    p = pathModule.resolve(resolvedLink, StringPrototypeSlice(p, pos));
2845    current = base = splitRoot(p);
2846    pos = current.length;
2847
2848    // On windows, check that the root exists. On unix there is no need.
2849    if (isWindows && !knownHard.has(base)) {
2850      fs.lstat(base, (err) => {
2851        if (err) return callback(err);
2852        knownHard.add(base);
2853        LOOP();
2854      });
2855    } else {
2856      process.nextTick(LOOP);
2857    }
2858  }
2859}
2860
2861/**
2862 * Asynchronously computes the canonical pathname by
2863 * resolving `.`, `..` and symbolic links.
2864 * @param {string | Buffer | URL} path
2865 * @param {string | { encoding?: string; }} [options]
2866 * @param {(
2867 *   err?: Error,
2868 *   resolvedPath?: string | Buffer
2869 *   ) => any} callback
2870 * @returns {void}
2871 */
2872realpath.native = (path, options, callback) => {
2873  callback = makeCallback(callback || options);
2874  options = getOptions(options);
2875  path = getValidatedPath(path);
2876  const req = new FSReqCallback();
2877  req.oncomplete = callback;
2878  return binding.realpath(pathModule.toNamespacedPath(path), options.encoding, req);
2879};
2880
2881/**
2882 * Creates a unique temporary directory.
2883 * @param {string | Buffer | URL} prefix
2884 * @param {string | { encoding?: string; }} [options]
2885 * @param {(
2886 *   err?: Error,
2887 *   directory?: string
2888 *   ) => any} callback
2889 * @returns {void}
2890 */
2891function mkdtemp(prefix, options, callback) {
2892  callback = makeCallback(typeof options === 'function' ? options : callback);
2893  options = getOptions(options);
2894
2895  prefix = getValidatedPath(prefix, 'prefix');
2896  warnOnNonPortableTemplate(prefix);
2897
2898  let path;
2899  if (typeof prefix === 'string') {
2900    path = `${prefix}XXXXXX`;
2901  } else {
2902    path = Buffer.concat([prefix, Buffer.from('XXXXXX')]);
2903  }
2904
2905  const req = new FSReqCallback();
2906  req.oncomplete = callback;
2907  binding.mkdtemp(path, options.encoding, req);
2908}
2909
2910/**
2911 * Synchronously creates a unique temporary directory.
2912 * @param {string | Buffer | URL} prefix
2913 * @param {string | { encoding?: string; }} [options]
2914 * @returns {string}
2915 */
2916function mkdtempSync(prefix, options) {
2917  options = getOptions(options);
2918
2919  prefix = getValidatedPath(prefix, 'prefix');
2920  warnOnNonPortableTemplate(prefix);
2921
2922  let path;
2923  if (typeof prefix === 'string') {
2924    path = `${prefix}XXXXXX`;
2925  } else {
2926    path = Buffer.concat([prefix, Buffer.from('XXXXXX')]);
2927  }
2928
2929  const ctx = { path };
2930  const result = binding.mkdtemp(path, options.encoding,
2931                                 undefined, ctx);
2932  handleErrorFromBinding(ctx);
2933  return result;
2934}
2935
2936/**
2937 * Asynchronously copies `src` to `dest`. By
2938 * default, `dest` is overwritten if it already exists.
2939 * @param {string | Buffer | URL} src
2940 * @param {string | Buffer | URL} dest
2941 * @param {number} [mode]
2942 * @param {() => any} callback
2943 * @returns {void}
2944 */
2945function copyFile(src, dest, mode, callback) {
2946  if (typeof mode === 'function') {
2947    callback = mode;
2948    mode = 0;
2949  }
2950
2951  src = getValidatedPath(src, 'src');
2952  dest = getValidatedPath(dest, 'dest');
2953
2954  src = pathModule._makeLong(src);
2955  dest = pathModule._makeLong(dest);
2956  mode = getValidMode(mode, 'copyFile');
2957  callback = makeCallback(callback);
2958
2959  const req = new FSReqCallback();
2960  req.oncomplete = callback;
2961  binding.copyFile(src, dest, mode, req);
2962}
2963
2964/**
2965 * Synchronously copies `src` to `dest`. By
2966 * default, `dest` is overwritten if it already exists.
2967 * @param {string | Buffer | URL} src
2968 * @param {string | Buffer | URL} dest
2969 * @param {number} [mode]
2970 * @returns {void}
2971 */
2972function copyFileSync(src, dest, mode) {
2973  src = getValidatedPath(src, 'src');
2974  dest = getValidatedPath(dest, 'dest');
2975
2976  const ctx = { path: src, dest };  // non-prefixed
2977
2978  src = pathModule._makeLong(src);
2979  dest = pathModule._makeLong(dest);
2980  mode = getValidMode(mode, 'copyFile');
2981  binding.copyFile(src, dest, mode, undefined, ctx);
2982  handleErrorFromBinding(ctx);
2983}
2984
2985/**
2986 * Asynchronously copies `src` to `dest`. `src` can be a file, directory, or
2987 * symlink. The contents of directories will be copied recursively.
2988 * @param {string | URL} src
2989 * @param {string | URL} dest
2990 * @param {object} [options]
2991 * @param {() => any} callback
2992 * @returns {void}
2993 */
2994function cp(src, dest, options, callback) {
2995  if (typeof options === 'function') {
2996    callback = options;
2997    options = undefined;
2998  }
2999  callback = makeCallback(callback);
3000  options = validateCpOptions(options);
3001  src = pathModule.toNamespacedPath(getValidatedPath(src, 'src'));
3002  dest = pathModule.toNamespacedPath(getValidatedPath(dest, 'dest'));
3003  lazyLoadCp();
3004  cpFn(src, dest, options, callback);
3005}
3006
3007/**
3008 * Synchronously copies `src` to `dest`. `src` can be a file, directory, or
3009 * symlink. The contents of directories will be copied recursively.
3010 * @param {string | URL} src
3011 * @param {string | URL} dest
3012 * @param {object} [options]
3013 * @returns {void}
3014 */
3015function cpSync(src, dest, options) {
3016  options = validateCpOptions(options);
3017  src = pathModule.toNamespacedPath(getValidatedPath(src, 'src'));
3018  dest = pathModule.toNamespacedPath(getValidatedPath(dest, 'dest'));
3019  lazyLoadCp();
3020  cpSyncFn(src, dest, options);
3021}
3022
3023function lazyLoadStreams() {
3024  if (!ReadStream) {
3025    ({ ReadStream, WriteStream } = require('internal/fs/streams'));
3026    FileReadStream = ReadStream;
3027    FileWriteStream = WriteStream;
3028  }
3029}
3030
3031/**
3032 * Creates a readable stream with a default `highWaterMark`
3033 * of 64 KiB.
3034 * @param {string | Buffer | URL} path
3035 * @param {string | {
3036 *   flags?: string;
3037 *   encoding?: string;
3038 *   fd?: number | FileHandle;
3039 *   mode?: number;
3040 *   autoClose?: boolean;
3041 *   emitClose?: boolean;
3042 *   start: number;
3043 *   end?: number;
3044 *   highWaterMark?: number;
3045 *   fs?: object | null;
3046 *   }} [options]
3047 * @returns {ReadStream}
3048 */
3049function createReadStream(path, options) {
3050  lazyLoadStreams();
3051  return new ReadStream(path, options);
3052}
3053
3054/**
3055 * Creates a write stream.
3056 * @param {string | Buffer | URL} path
3057 * @param {string | {
3058 *   flags?: string;
3059 *   encoding?: string;
3060 *   fd?: number | FileHandle;
3061 *   mode?: number;
3062 *   autoClose?: boolean;
3063 *   emitClose?: boolean;
3064 *   start: number;
3065 *   fs?: object | null;
3066 *   }} [options]
3067 * @returns {WriteStream}
3068 */
3069function createWriteStream(path, options) {
3070  lazyLoadStreams();
3071  return new WriteStream(path, options);
3072}
3073
3074module.exports = fs = {
3075  appendFile,
3076  appendFileSync,
3077  access,
3078  accessSync,
3079  chown,
3080  chownSync,
3081  chmod,
3082  chmodSync,
3083  close,
3084  closeSync,
3085  copyFile,
3086  copyFileSync,
3087  cp,
3088  cpSync,
3089  createReadStream,
3090  createWriteStream,
3091  exists,
3092  existsSync,
3093  fchown,
3094  fchownSync,
3095  fchmod,
3096  fchmodSync,
3097  fdatasync,
3098  fdatasyncSync,
3099  fstat,
3100  fstatSync,
3101  fsync,
3102  fsyncSync,
3103  ftruncate,
3104  ftruncateSync,
3105  futimes,
3106  futimesSync,
3107  lchown,
3108  lchownSync,
3109  lchmod: constants.O_SYMLINK !== undefined ? lchmod : undefined,
3110  lchmodSync: constants.O_SYMLINK !== undefined ? lchmodSync : undefined,
3111  link,
3112  linkSync,
3113  lstat,
3114  lstatSync,
3115  lutimes,
3116  lutimesSync,
3117  mkdir,
3118  mkdirSync,
3119  mkdtemp,
3120  mkdtempSync,
3121  open,
3122  openSync,
3123  readdir,
3124  readdirSync,
3125  read,
3126  readSync,
3127  readv,
3128  readvSync,
3129  readFile,
3130  readFileSync,
3131  readlink,
3132  readlinkSync,
3133  realpath,
3134  realpathSync,
3135  rename,
3136  renameSync,
3137  rm,
3138  rmSync,
3139  rmdir,
3140  rmdirSync,
3141  stat,
3142  statfs,
3143  statSync,
3144  statfsSync,
3145  symlink,
3146  symlinkSync,
3147  truncate,
3148  truncateSync,
3149  unwatchFile,
3150  unlink,
3151  unlinkSync,
3152  utimes,
3153  utimesSync,
3154  watch,
3155  watchFile,
3156  writeFile,
3157  writeFileSync,
3158  write,
3159  writeSync,
3160  writev,
3161  writevSync,
3162  Dirent,
3163  Stats,
3164
3165  get ReadStream() {
3166    lazyLoadStreams();
3167    return ReadStream;
3168  },
3169
3170  set ReadStream(val) {
3171    ReadStream = val;
3172  },
3173
3174  get WriteStream() {
3175    lazyLoadStreams();
3176    return WriteStream;
3177  },
3178
3179  set WriteStream(val) {
3180    WriteStream = val;
3181  },
3182
3183  // Legacy names... these have to be separate because of how graceful-fs
3184  // (and possibly other) modules monkey patch the values.
3185  get FileReadStream() {
3186    lazyLoadStreams();
3187    return FileReadStream;
3188  },
3189
3190  set FileReadStream(val) {
3191    FileReadStream = val;
3192  },
3193
3194  get FileWriteStream() {
3195    lazyLoadStreams();
3196    return FileWriteStream;
3197  },
3198
3199  set FileWriteStream(val) {
3200    FileWriteStream = val;
3201  },
3202
3203  // For tests
3204  _toUnixTimestamp: toUnixTimestamp,
3205};
3206
3207defineLazyProperties(
3208  fs,
3209  'internal/fs/dir',
3210  ['Dir', 'opendir', 'opendirSync'],
3211);
3212
3213ObjectDefineProperties(fs, {
3214  F_OK: { __proto__: null, enumerable: true, value: F_OK || 0 },
3215  R_OK: { __proto__: null, enumerable: true, value: R_OK || 0 },
3216  W_OK: { __proto__: null, enumerable: true, value: W_OK || 0 },
3217  X_OK: { __proto__: null, enumerable: true, value: X_OK || 0 },
3218  constants: {
3219    __proto__: null,
3220    configurable: false,
3221    enumerable: true,
3222    value: constants,
3223  },
3224  promises: {
3225    __proto__: null,
3226    configurable: true,
3227    enumerable: true,
3228    get() {
3229      promises ??= require('internal/fs/promises').exports;
3230      return promises;
3231    },
3232  },
3233});
3234