• 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} = require('internal/util');
91const {
92  constants: {
93    kIoMaxLength,
94    kMaxUserId,
95  },
96  copyObject,
97  Dirent,
98  emitRecursiveRmdirWarning,
99  getDirent,
100  getDirents,
101  getOptions,
102  getValidatedFd,
103  getValidatedPath,
104  getValidMode,
105  handleErrorFromBinding,
106  nullCheck,
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  Dir,
130  opendir,
131  opendirSync,
132} = require('internal/fs/dir');
133const {
134  CHAR_FORWARD_SLASH,
135  CHAR_BACKWARD_SLASH,
136} = require('internal/constants');
137const {
138  isUint32,
139  parseFileMode,
140  validateBoolean,
141  validateBuffer,
142  validateEncoding,
143  validateFunction,
144  validateInteger,
145  validateObject,
146  validateString,
147} = require('internal/validators');
148
149const watchers = require('internal/fs/watchers');
150const ReadFileContext = require('internal/fs/read_file_context');
151
152let truncateWarn = true;
153let fs;
154
155// Lazy loaded
156let cpFn;
157let cpSyncFn;
158let promises = null;
159let ReadStream;
160let WriteStream;
161let rimraf;
162let rimrafSync;
163
164// These have to be separate because of how graceful-fs happens to do it's
165// monkeypatching.
166let FileReadStream;
167let FileWriteStream;
168
169const isWindows = process.platform === 'win32';
170const isOSX = process.platform === 'darwin';
171
172
173const showStringCoercionDeprecation = deprecate(
174  () => {},
175  'Implicit coercion of objects with own toString property is deprecated.',
176  'DEP0162',
177);
178function showTruncateDeprecation() {
179  if (truncateWarn) {
180    process.emitWarning(
181      'Using fs.truncate with a file descriptor is deprecated. Please use ' +
182      'fs.ftruncate with a file descriptor instead.',
183      'DeprecationWarning', 'DEP0081');
184    truncateWarn = false;
185  }
186}
187
188function maybeCallback(cb) {
189  validateFunction(cb, 'cb');
190
191  return cb;
192}
193
194// Ensure that callbacks run in the global context. Only use this function
195// for callbacks that are passed to the binding layer, callbacks that are
196// invoked from JS already run in the proper scope.
197function makeCallback(cb) {
198  validateFunction(cb, 'cb');
199
200  return (...args) => ReflectApply(cb, this, args);
201}
202
203// Special case of `makeCallback()` that is specific to async `*stat()` calls as
204// an optimization, since the data passed back to the callback needs to be
205// transformed anyway.
206function makeStatsCallback(cb) {
207  validateFunction(cb, 'cb');
208
209  return (err, stats) => {
210    if (err) return cb(err);
211    cb(err, getStatsFromBinding(stats));
212  };
213}
214
215const isFd = isUint32;
216
217function isFileType(stats, fileType) {
218  // Use stats array directly to avoid creating an fs.Stats instance just for
219  // our internal use.
220  let mode = stats[1];
221  if (typeof mode === 'bigint')
222    mode = Number(mode);
223  return (mode & S_IFMT) === fileType;
224}
225
226/**
227 * Tests a user's permissions for the file or directory
228 * specified by `path`.
229 * @param {string | Buffer | URL} path
230 * @param {number} [mode]
231 * @param {(err?: Error) => any} callback
232 * @returns {void}
233 */
234function access(path, mode, callback) {
235  if (typeof mode === 'function') {
236    callback = mode;
237    mode = F_OK;
238  }
239
240  path = getValidatedPath(path);
241  mode = getValidMode(mode, 'access');
242  callback = makeCallback(callback);
243
244  const req = new FSReqCallback();
245  req.oncomplete = callback;
246  binding.access(pathModule.toNamespacedPath(path), mode, req);
247}
248
249/**
250 * Synchronously tests a user's permissions for the file or
251 * directory specified by `path`.
252 * @param {string | Buffer | URL} path
253 * @param {number} [mode]
254 * @returns {void}
255 */
256function accessSync(path, mode) {
257  path = getValidatedPath(path);
258  mode = getValidMode(mode, 'access');
259
260  const ctx = { path };
261  binding.access(pathModule.toNamespacedPath(path), mode, undefined, ctx);
262  handleErrorFromBinding(ctx);
263}
264
265/**
266 * Tests whether or not the given path exists.
267 * @param {string | Buffer | URL} path
268 * @param {(exists?: boolean) => any} callback
269 * @returns {void}
270 */
271function exists(path, callback) {
272  maybeCallback(callback);
273
274  function suppressedCallback(err) {
275    callback(err ? false : true);
276  }
277
278  try {
279    fs.access(path, F_OK, suppressedCallback);
280  } catch {
281    return callback(false);
282  }
283}
284
285ObjectDefineProperty(exists, kCustomPromisifiedSymbol, {
286  __proto__: null,
287  value: function exists(path) { // eslint-disable-line func-name-matching
288    return new Promise((resolve) => fs.exists(path, resolve));
289  },
290});
291
292// fs.existsSync never throws, it only returns true or false.
293// Since fs.existsSync never throws, users have established
294// the expectation that passing invalid arguments to it, even like
295// fs.existsSync(), would only get a false in return, so we cannot signal
296// validation errors to users properly out of compatibility concerns.
297// TODO(joyeecheung): deprecate the never-throw-on-invalid-arguments behavior
298/**
299 * Synchronously tests whether or not the given path exists.
300 * @param {string | Buffer | URL} path
301 * @returns {boolean}
302 */
303function existsSync(path) {
304  try {
305    path = getValidatedPath(path);
306  } catch {
307    return false;
308  }
309  const ctx = { path };
310  const nPath = pathModule.toNamespacedPath(path);
311  binding.access(nPath, F_OK, undefined, ctx);
312
313  // In case of an invalid symlink, `binding.access()` on win32
314  // will **not** return an error and is therefore not enough.
315  // Double check with `binding.stat()`.
316  if (isWindows && ctx.errno === undefined) {
317    binding.stat(nPath, false, undefined, ctx);
318  }
319
320  return ctx.errno === undefined;
321}
322
323function readFileAfterOpen(err, fd) {
324  const context = this.context;
325
326  if (err) {
327    context.callback(err);
328    return;
329  }
330
331  context.fd = fd;
332
333  const req = new FSReqCallback();
334  req.oncomplete = readFileAfterStat;
335  req.context = context;
336  binding.fstat(fd, false, req);
337}
338
339function readFileAfterStat(err, stats) {
340  const context = this.context;
341
342  if (err)
343    return context.close(err);
344
345  // TODO(BridgeAR): Check if allocating a smaller chunk is better performance
346  // wise, similar to the promise based version (less peak memory and chunked
347  // stringify operations vs multiple C++/JS boundary crossings).
348  const size = context.size = isFileType(stats, S_IFREG) ? stats[8] : 0;
349
350  if (size > kIoMaxLength) {
351    err = new ERR_FS_FILE_TOO_LARGE(size);
352    return context.close(err);
353  }
354
355  try {
356    if (size === 0) {
357      // TODO(BridgeAR): If an encoding is set, use the StringDecoder to concat
358      // the result and reuse the buffer instead of allocating a new one.
359      context.buffers = [];
360    } else {
361      context.buffer = Buffer.allocUnsafeSlow(size);
362    }
363  } catch (err) {
364    return context.close(err);
365  }
366  context.read();
367}
368
369function checkAborted(signal, callback) {
370  if (signal?.aborted) {
371    callback(new AbortError(undefined, { cause: signal?.reason }));
372    return true;
373  }
374  return false;
375}
376
377/**
378 * Asynchronously reads the entire contents of a file.
379 * @param {string | Buffer | URL | number} path
380 * @param {{
381 *   encoding?: string | null;
382 *   flag?: string;
383 *   signal?: AbortSignal;
384 *   } | string} [options]
385 * @param {(
386 *   err?: Error,
387 *   data?: string | Buffer
388 *   ) => any} callback
389 * @returns {void}
390 */
391function readFile(path, options, callback) {
392  callback = maybeCallback(callback || options);
393  options = getOptions(options, { flag: 'r' });
394  const context = new ReadFileContext(callback, options.encoding);
395  context.isUserFd = isFd(path); // File descriptor ownership
396
397  if (options.signal) {
398    context.signal = options.signal;
399  }
400  if (context.isUserFd) {
401    process.nextTick(function tick(context) {
402      ReflectApply(readFileAfterOpen, { context }, [null, path]);
403    }, context);
404    return;
405  }
406
407  if (checkAborted(options.signal, callback))
408    return;
409
410  const flagsNumber = stringToFlags(options.flag, 'options.flag');
411  path = getValidatedPath(path);
412
413  const req = new FSReqCallback();
414  req.context = context;
415  req.oncomplete = readFileAfterOpen;
416  binding.open(pathModule.toNamespacedPath(path),
417               flagsNumber,
418               0o666,
419               req);
420}
421
422function tryStatSync(fd, isUserFd) {
423  const ctx = {};
424  const stats = binding.fstat(fd, false, undefined, ctx);
425  if (ctx.errno !== undefined && !isUserFd) {
426    fs.closeSync(fd);
427    throw uvException(ctx);
428  }
429  return stats;
430}
431
432function tryCreateBuffer(size, fd, isUserFd) {
433  let threw = true;
434  let buffer;
435  try {
436    if (size > kIoMaxLength) {
437      throw new ERR_FS_FILE_TOO_LARGE(size);
438    }
439    buffer = Buffer.allocUnsafe(size);
440    threw = false;
441  } finally {
442    if (threw && !isUserFd) fs.closeSync(fd);
443  }
444  return buffer;
445}
446
447function tryReadSync(fd, isUserFd, buffer, pos, len) {
448  let threw = true;
449  let bytesRead;
450  try {
451    bytesRead = fs.readSync(fd, buffer, pos, len);
452    threw = false;
453  } finally {
454    if (threw && !isUserFd) fs.closeSync(fd);
455  }
456  return bytesRead;
457}
458
459/**
460 * Synchronously reads the entire contents of a file.
461 * @param {string | Buffer | URL | number} path
462 * @param {{
463 *   encoding?: string | null;
464 *   flag?: string;
465 *   }} [options]
466 * @returns {string | Buffer}
467 */
468function readFileSync(path, options) {
469  options = getOptions(options, { flag: 'r' });
470  const isUserFd = isFd(path); // File descriptor ownership
471  const fd = isUserFd ? path : fs.openSync(path, options.flag, 0o666);
472
473  const stats = tryStatSync(fd, isUserFd);
474  const size = isFileType(stats, S_IFREG) ? stats[8] : 0;
475  let pos = 0;
476  let buffer; // Single buffer with file data
477  let buffers; // List for when size is unknown
478
479  if (size === 0) {
480    buffers = [];
481  } else {
482    buffer = tryCreateBuffer(size, fd, isUserFd);
483  }
484
485  let bytesRead;
486
487  if (size !== 0) {
488    do {
489      bytesRead = tryReadSync(fd, isUserFd, buffer, pos, size - pos);
490      pos += bytesRead;
491    } while (bytesRead !== 0 && pos < size);
492  } else {
493    do {
494      // The kernel lies about many files.
495      // Go ahead and try to read some bytes.
496      buffer = Buffer.allocUnsafe(8192);
497      bytesRead = tryReadSync(fd, isUserFd, buffer, 0, 8192);
498      if (bytesRead !== 0) {
499        ArrayPrototypePush(buffers, buffer.slice(0, bytesRead));
500      }
501      pos += bytesRead;
502    } while (bytesRead !== 0);
503  }
504
505  if (!isUserFd)
506    fs.closeSync(fd);
507
508  if (size === 0) {
509    // Data was collected into the buffers list.
510    buffer = Buffer.concat(buffers, pos);
511  } else if (pos < size) {
512    buffer = buffer.slice(0, pos);
513  }
514
515  if (options.encoding) buffer = buffer.toString(options.encoding);
516  return buffer;
517}
518
519function defaultCloseCallback(err) {
520  if (err != null) throw err;
521}
522
523/**
524 * Closes the file descriptor.
525 * @param {number} fd
526 * @param {(err?: Error) => any} [callback]
527 * @returns {void}
528 */
529function close(fd, callback = defaultCloseCallback) {
530  fd = getValidatedFd(fd);
531  if (callback !== defaultCloseCallback)
532    callback = makeCallback(callback);
533
534  const req = new FSReqCallback();
535  req.oncomplete = callback;
536  binding.close(fd, req);
537}
538
539/**
540 * Synchronously closes the file descriptor.
541 * @param {number} fd
542 * @returns {void}
543 */
544function closeSync(fd) {
545  fd = getValidatedFd(fd);
546
547  const ctx = {};
548  binding.close(fd, undefined, ctx);
549  handleErrorFromBinding(ctx);
550}
551
552/**
553 * Asynchronously opens a file.
554 * @param {string | Buffer | URL} path
555 * @param {string | number} [flags]
556 * @param {string | number} [mode]
557 * @param {(
558 *   err?: Error,
559 *   fd?: number
560 *   ) => any} callback
561 * @returns {void}
562 */
563function open(path, flags, mode, callback) {
564  path = getValidatedPath(path);
565  if (arguments.length < 3) {
566    callback = flags;
567    flags = 'r';
568    mode = 0o666;
569  } else if (typeof mode === 'function') {
570    callback = mode;
571    mode = 0o666;
572  } else {
573    mode = parseFileMode(mode, 'mode', 0o666);
574  }
575  const flagsNumber = stringToFlags(flags);
576  callback = makeCallback(callback);
577
578  const req = new FSReqCallback();
579  req.oncomplete = callback;
580
581  binding.open(pathModule.toNamespacedPath(path),
582               flagsNumber,
583               mode,
584               req);
585}
586
587/**
588 * Synchronously opens a file.
589 * @param {string | Buffer | URL} path
590 * @param {string | number} [flags]
591 * @param {string | number} [mode]
592 * @returns {number}
593 */
594function openSync(path, flags, mode) {
595  path = getValidatedPath(path);
596  const flagsNumber = stringToFlags(flags);
597  mode = parseFileMode(mode, 'mode', 0o666);
598
599  const ctx = { path };
600  const result = binding.open(pathModule.toNamespacedPath(path),
601                              flagsNumber, mode,
602                              undefined, ctx);
603  handleErrorFromBinding(ctx);
604  return result;
605}
606
607/**
608 * Reads file from the specified `fd` (file descriptor).
609 * @param {number} fd
610 * @param {Buffer | TypedArray | DataView} buffer
611 * @param {number} offsetOrOptions
612 * @param {number} length
613 * @param {number | bigint | null} position
614 * @param {(
615 *   err?: Error,
616 *   bytesRead?: number,
617 *   buffer?: Buffer
618 *   ) => any} callback
619 * @returns {void}
620 */
621function read(fd, buffer, offsetOrOptions, length, position, callback) {
622  fd = getValidatedFd(fd);
623
624  let offset = offsetOrOptions;
625  let params = null;
626  if (arguments.length <= 4) {
627    if (arguments.length === 4) {
628      // This is fs.read(fd, buffer, options, callback)
629      validateObject(offsetOrOptions, 'options', { nullable: true });
630      callback = length;
631      params = offsetOrOptions;
632    } else if (arguments.length === 3) {
633      // This is fs.read(fd, bufferOrParams, callback)
634      if (!isArrayBufferView(buffer)) {
635        // This is fs.read(fd, params, callback)
636        params = buffer;
637        ({ buffer = Buffer.alloc(16384) } = params ?? kEmptyObject);
638      }
639      callback = offsetOrOptions;
640    } else {
641      // This is fs.read(fd, callback)
642      callback = buffer;
643      buffer = Buffer.alloc(16384);
644    }
645
646    ({
647      offset = 0,
648      length = buffer.byteLength - offset,
649      position = null,
650    } = params ?? kEmptyObject);
651  }
652
653  validateBuffer(buffer);
654  callback = maybeCallback(callback);
655
656  if (offset == null) {
657    offset = 0;
658  } else {
659    validateInteger(offset, 'offset', 0);
660  }
661
662  length |= 0;
663
664  if (length === 0) {
665    return process.nextTick(function tick() {
666      callback(null, 0, buffer);
667    });
668  }
669
670  if (buffer.byteLength === 0) {
671    throw new ERR_INVALID_ARG_VALUE('buffer', buffer,
672                                    'is empty and cannot be written');
673  }
674
675  validateOffsetLengthRead(offset, length, buffer.byteLength);
676
677  if (position == null)
678    position = -1;
679
680  validatePosition(position, 'position');
681
682  function wrapper(err, bytesRead) {
683    // Retain a reference to buffer so that it can't be GC'ed too soon.
684    callback(err, bytesRead || 0, buffer);
685  }
686
687  const req = new FSReqCallback();
688  req.oncomplete = wrapper;
689
690  binding.read(fd, buffer, offset, length, position, req);
691}
692
693ObjectDefineProperty(read, kCustomPromisifyArgsSymbol,
694                     { __proto__: null, value: ['bytesRead', 'buffer'], enumerable: false });
695
696/**
697 * Synchronously reads the file from the
698 * specified `fd` (file descriptor).
699 * @param {number} fd
700 * @param {Buffer | TypedArray | DataView} buffer
701 * @param {{
702 *   offset?: number;
703 *   length?: number;
704 *   position?: number | bigint | null;
705 *   }} [offset]
706 * @returns {number}
707 */
708function readSync(fd, buffer, offset, length, position) {
709  fd = getValidatedFd(fd);
710
711  validateBuffer(buffer);
712
713  if (arguments.length <= 3) {
714    // Assume fs.readSync(fd, buffer, options)
715    const options = offset || kEmptyObject;
716
717    ({
718      offset = 0,
719      length = buffer.byteLength - offset,
720      position = null,
721    } = options);
722  }
723
724  if (offset == null) {
725    offset = 0;
726  } else {
727    validateInteger(offset, 'offset', 0);
728  }
729
730  length |= 0;
731
732  if (length === 0) {
733    return 0;
734  }
735
736  if (buffer.byteLength === 0) {
737    throw new ERR_INVALID_ARG_VALUE('buffer', buffer,
738                                    'is empty and cannot be written');
739  }
740
741  validateOffsetLengthRead(offset, length, buffer.byteLength);
742
743  if (position == null)
744    position = -1;
745
746  validatePosition(position, 'position');
747
748  const ctx = {};
749  const result = binding.read(fd, buffer, offset, length, position,
750                              undefined, ctx);
751  handleErrorFromBinding(ctx);
752  return result;
753}
754
755/**
756 * Reads file from the specified `fd` (file descriptor)
757 * and writes to an array of `ArrayBufferView`s.
758 * @param {number} fd
759 * @param {ArrayBufferView[]} buffers
760 * @param {number | null} [position]
761 * @param {(
762 *   err?: Error,
763 *   bytesRead?: number,
764 *   buffers?: ArrayBufferView[];
765 *   ) => any} callback
766 * @returns {void}
767 */
768function readv(fd, buffers, position, callback) {
769  function wrapper(err, read) {
770    callback(err, read || 0, buffers);
771  }
772
773  fd = getValidatedFd(fd);
774  validateBufferArray(buffers);
775  callback = maybeCallback(callback || position);
776
777  const req = new FSReqCallback();
778  req.oncomplete = wrapper;
779
780  if (typeof position !== 'number')
781    position = null;
782
783  return binding.readBuffers(fd, buffers, position, req);
784}
785
786ObjectDefineProperty(readv, kCustomPromisifyArgsSymbol,
787                     { __proto__: null, value: ['bytesRead', 'buffers'], enumerable: false });
788
789/**
790 * Synchronously reads file from the
791 * specified `fd` (file descriptor) and writes to an array
792 * of `ArrayBufferView`s.
793 * @param {number} fd
794 * @param {ArrayBufferView[]} buffers
795 * @param {number | null} [position]
796 * @returns {number}
797 */
798function readvSync(fd, buffers, position) {
799  fd = getValidatedFd(fd);
800  validateBufferArray(buffers);
801
802  const ctx = {};
803
804  if (typeof position !== 'number')
805    position = null;
806
807  const result = binding.readBuffers(fd, buffers, position, undefined, ctx);
808  handleErrorFromBinding(ctx);
809  return result;
810}
811
812/**
813 * Writes `buffer` to the specified `fd` (file descriptor).
814 * @param {number} fd
815 * @param {Buffer | TypedArray | DataView | string | object} buffer
816 * @param {number | object} [offsetOrOptions]
817 * @param {number} [length]
818 * @param {number | null} [position]
819 * @param {(
820 *   err?: Error,
821 *   bytesWritten?: number;
822 *   buffer?: Buffer | TypedArray | DataView
823 *   ) => any} callback
824 * @returns {void}
825 */
826function write(fd, buffer, offsetOrOptions, length, position, callback) {
827  function wrapper(err, written) {
828    // Retain a reference to buffer so that it can't be GC'ed too soon.
829    callback(err, written || 0, buffer);
830  }
831
832  fd = getValidatedFd(fd);
833
834  let offset = offsetOrOptions;
835  if (isArrayBufferView(buffer)) {
836    callback = maybeCallback(callback || position || length || offset);
837
838    if (typeof offset === 'object') {
839      ({
840        offset = 0,
841        length = buffer.byteLength - offset,
842        position = null,
843      } = offsetOrOptions ?? kEmptyObject);
844    }
845
846    if (offset == null || typeof offset === 'function') {
847      offset = 0;
848    } else {
849      validateInteger(offset, 'offset', 0);
850    }
851    if (typeof length !== 'number')
852      length = buffer.byteLength - offset;
853    if (typeof position !== 'number')
854      position = null;
855    validateOffsetLengthWrite(offset, length, buffer.byteLength);
856
857    const req = new FSReqCallback();
858    req.oncomplete = wrapper;
859    return binding.writeBuffer(fd, buffer, offset, length, position, req);
860  }
861
862  validateStringAfterArrayBufferView(buffer, 'buffer');
863  if (typeof buffer !== 'string') {
864    showStringCoercionDeprecation();
865  }
866
867  if (typeof position !== 'function') {
868    if (typeof offset === 'function') {
869      position = offset;
870      offset = null;
871    } else {
872      position = length;
873    }
874    length = 'utf8';
875  }
876
877  const str = String(buffer);
878  validateEncoding(str, length);
879  callback = maybeCallback(position);
880
881  const req = new FSReqCallback();
882  req.oncomplete = wrapper;
883  return binding.writeString(fd, str, offset, length, req);
884}
885
886ObjectDefineProperty(write, kCustomPromisifyArgsSymbol,
887                     { __proto__: null, value: ['bytesWritten', 'buffer'], enumerable: false });
888
889/**
890 * Synchronously writes `buffer` to the
891 * specified `fd` (file descriptor).
892 * @param {number} fd
893 * @param {Buffer | TypedArray | DataView | string} buffer
894 * @param {{
895 *   offset?: number;
896 *   length?: number;
897 *   position?: number | null;
898 *   }} [offsetOrOptions]
899 * @returns {number}
900 */
901function writeSync(fd, buffer, offsetOrOptions, length, position) {
902  fd = getValidatedFd(fd);
903  const ctx = {};
904  let result;
905
906  let offset = offsetOrOptions;
907  if (isArrayBufferView(buffer)) {
908    if (typeof offset === 'object') {
909      ({
910        offset = 0,
911        length = buffer.byteLength - offset,
912        position = null,
913      } = offsetOrOptions ?? kEmptyObject);
914    }
915    if (position === undefined)
916      position = null;
917    if (offset == null) {
918      offset = 0;
919    } else {
920      validateInteger(offset, 'offset', 0);
921    }
922    if (typeof length !== 'number')
923      length = buffer.byteLength - offset;
924    validateOffsetLengthWrite(offset, length, buffer.byteLength);
925    result = binding.writeBuffer(fd, buffer, offset, length, position,
926                                 undefined, ctx);
927  } else {
928    validatePrimitiveStringAfterArrayBufferView(buffer, 'buffer');
929    validateEncoding(buffer, length);
930
931    if (offset === undefined)
932      offset = null;
933    result = binding.writeString(fd, buffer, offset, length,
934                                 undefined, ctx);
935  }
936  handleErrorFromBinding(ctx);
937  return result;
938}
939
940/**
941 * Writes an array of `ArrayBufferView`s to the
942 * specified `fd` (file descriptor).
943 * @param {number} fd
944 * @param {ArrayBufferView[]} buffers
945 * @param {number | null} [position]
946 * @param {(
947 *   err?: Error,
948 *   bytesWritten?: number,
949 *   buffers?: ArrayBufferView[]
950 *   ) => any} callback
951 * @returns {void}
952 */
953function writev(fd, buffers, position, callback) {
954  function wrapper(err, written) {
955    callback(err, written || 0, buffers);
956  }
957
958  fd = getValidatedFd(fd);
959  validateBufferArray(buffers);
960  callback = maybeCallback(callback || position);
961
962  if (buffers.length === 0) {
963    process.nextTick(callback, null, 0, buffers);
964    return;
965  }
966
967  const req = new FSReqCallback();
968  req.oncomplete = wrapper;
969
970  if (typeof position !== 'number')
971    position = null;
972
973  return binding.writeBuffers(fd, buffers, position, req);
974}
975
976ObjectDefineProperty(writev, kCustomPromisifyArgsSymbol, {
977  __proto__: null,
978  value: ['bytesWritten', 'buffer'],
979  enumerable: false,
980});
981
982/**
983 * Synchronously writes an array of `ArrayBufferView`s
984 * to the specified `fd` (file descriptor).
985 * @param {number} fd
986 * @param {ArrayBufferView[]} buffers
987 * @param {number | null} [position]
988 * @returns {number}
989 */
990function writevSync(fd, buffers, position) {
991  fd = getValidatedFd(fd);
992  validateBufferArray(buffers);
993
994  if (buffers.length === 0) {
995    return 0;
996  }
997
998  const ctx = {};
999
1000  if (typeof position !== 'number')
1001    position = null;
1002
1003  const result = binding.writeBuffers(fd, buffers, position, undefined, ctx);
1004
1005  handleErrorFromBinding(ctx);
1006  return result;
1007}
1008
1009/**
1010 * Asynchronously renames file at `oldPath` to
1011 * the pathname provided as `newPath`.
1012 * @param {string | Buffer | URL} oldPath
1013 * @param {string | Buffer | URL} newPath
1014 * @param {(err?: Error) => any} callback
1015 * @returns {void}
1016 */
1017function rename(oldPath, newPath, callback) {
1018  callback = makeCallback(callback);
1019  oldPath = getValidatedPath(oldPath, 'oldPath');
1020  newPath = getValidatedPath(newPath, 'newPath');
1021  const req = new FSReqCallback();
1022  req.oncomplete = callback;
1023  binding.rename(pathModule.toNamespacedPath(oldPath),
1024                 pathModule.toNamespacedPath(newPath),
1025                 req);
1026}
1027
1028
1029/**
1030 * Synchronously renames file at `oldPath` to
1031 * the pathname provided as `newPath`.
1032 * @param {string | Buffer | URL} oldPath
1033 * @param {string | Buffer | URL} newPath
1034 * @returns {void}
1035 */
1036function renameSync(oldPath, newPath) {
1037  oldPath = getValidatedPath(oldPath, 'oldPath');
1038  newPath = getValidatedPath(newPath, 'newPath');
1039  const ctx = { path: oldPath, dest: newPath };
1040  binding.rename(pathModule.toNamespacedPath(oldPath),
1041                 pathModule.toNamespacedPath(newPath), undefined, ctx);
1042  handleErrorFromBinding(ctx);
1043}
1044
1045/**
1046 * Truncates the file.
1047 * @param {string | Buffer | URL} path
1048 * @param {number} [len]
1049 * @param {(err?: Error) => any} callback
1050 * @returns {void}
1051 */
1052function truncate(path, len, callback) {
1053  if (typeof path === 'number') {
1054    showTruncateDeprecation();
1055    return fs.ftruncate(path, len, callback);
1056  }
1057  if (typeof len === 'function') {
1058    callback = len;
1059    len = 0;
1060  } else if (len === undefined) {
1061    len = 0;
1062  }
1063
1064  validateInteger(len, 'len');
1065  len = MathMax(0, len);
1066  callback = maybeCallback(callback);
1067  fs.open(path, 'r+', (er, fd) => {
1068    if (er) return callback(er);
1069    const req = new FSReqCallback();
1070    req.oncomplete = function oncomplete(er) {
1071      fs.close(fd, (er2) => {
1072        callback(aggregateTwoErrors(er2, er));
1073      });
1074    };
1075    binding.ftruncate(fd, len, req);
1076  });
1077}
1078
1079/**
1080 * Synchronously truncates the file.
1081 * @param {string | Buffer | URL} path
1082 * @param {number} [len]
1083 * @returns {void}
1084 */
1085function truncateSync(path, len) {
1086  if (typeof path === 'number') {
1087    // legacy
1088    showTruncateDeprecation();
1089    return fs.ftruncateSync(path, len);
1090  }
1091  if (len === undefined) {
1092    len = 0;
1093  }
1094  // Allow error to be thrown, but still close fd.
1095  const fd = fs.openSync(path, 'r+');
1096  let ret;
1097
1098  try {
1099    ret = fs.ftruncateSync(fd, len);
1100  } finally {
1101    fs.closeSync(fd);
1102  }
1103  return ret;
1104}
1105
1106/**
1107 * Truncates the file descriptor.
1108 * @param {number} fd
1109 * @param {number} [len]
1110 * @param {(err?: Error) => any} callback
1111 * @returns {void}
1112 */
1113function ftruncate(fd, len = 0, callback) {
1114  if (typeof len === 'function') {
1115    callback = len;
1116    len = 0;
1117  }
1118  fd = getValidatedFd(fd);
1119  validateInteger(len, 'len');
1120  len = MathMax(0, len);
1121  callback = makeCallback(callback);
1122
1123  const req = new FSReqCallback();
1124  req.oncomplete = callback;
1125  binding.ftruncate(fd, len, req);
1126}
1127
1128/**
1129 * Synchronously truncates the file descriptor.
1130 * @param {number} fd
1131 * @param {number} [len]
1132 * @returns {void}
1133 */
1134function ftruncateSync(fd, len = 0) {
1135  fd = getValidatedFd(fd);
1136  validateInteger(len, 'len');
1137  len = MathMax(0, len);
1138  const ctx = {};
1139  binding.ftruncate(fd, len, undefined, ctx);
1140  handleErrorFromBinding(ctx);
1141}
1142
1143function lazyLoadCp() {
1144  if (cpFn === undefined) {
1145    ({ cpFn } = require('internal/fs/cp/cp'));
1146    cpFn = require('util').callbackify(cpFn);
1147    ({ cpSyncFn } = require('internal/fs/cp/cp-sync'));
1148  }
1149}
1150
1151function lazyLoadRimraf() {
1152  if (rimraf === undefined)
1153    ({ rimraf, rimrafSync } = require('internal/fs/rimraf'));
1154}
1155
1156/**
1157 * Asynchronously removes a directory.
1158 * @param {string | Buffer | URL} path
1159 * @param {{
1160 *   maxRetries?: number;
1161 *   recursive?: boolean;
1162 *   retryDelay?: number;
1163 *   }} [options]
1164 * @param {(err?: Error) => any} callback
1165 * @returns {void}
1166 */
1167function rmdir(path, options, callback) {
1168  if (typeof options === 'function') {
1169    callback = options;
1170    options = undefined;
1171  }
1172
1173  callback = makeCallback(callback);
1174  path = pathModule.toNamespacedPath(getValidatedPath(path));
1175
1176  if (options?.recursive) {
1177    emitRecursiveRmdirWarning();
1178    validateRmOptions(
1179      path,
1180      { ...options, force: false },
1181      true,
1182      (err, options) => {
1183        if (err === false) {
1184          const req = new FSReqCallback();
1185          req.oncomplete = callback;
1186          return binding.rmdir(path, req);
1187        }
1188        if (err) {
1189          return callback(err);
1190        }
1191
1192        lazyLoadRimraf();
1193        rimraf(path, options, callback);
1194      });
1195  } else {
1196    validateRmdirOptions(options);
1197    const req = new FSReqCallback();
1198    req.oncomplete = callback;
1199    return binding.rmdir(path, req);
1200  }
1201}
1202
1203/**
1204 * Synchronously removes a directory.
1205 * @param {string | Buffer | URL} path
1206 * @param {{
1207 *   maxRetries?: number;
1208 *   recursive?: boolean;
1209 *   retryDelay?: number;
1210 *   }} [options]
1211 * @returns {void}
1212 */
1213function rmdirSync(path, options) {
1214  path = getValidatedPath(path);
1215
1216  if (options?.recursive) {
1217    emitRecursiveRmdirWarning();
1218    options = validateRmOptionsSync(path, { ...options, force: false }, true);
1219    if (options !== false) {
1220      lazyLoadRimraf();
1221      return rimrafSync(pathModule.toNamespacedPath(path), options);
1222    }
1223  } else {
1224    validateRmdirOptions(options);
1225  }
1226
1227  const ctx = { path };
1228  binding.rmdir(pathModule.toNamespacedPath(path), undefined, ctx);
1229  return handleErrorFromBinding(ctx);
1230}
1231
1232/**
1233 * Asynchronously removes files and
1234 * directories (modeled on the standard POSIX `rm` utility).
1235 * @param {string | Buffer | URL} path
1236 * @param {{
1237 *   force?: boolean;
1238 *   maxRetries?: number;
1239 *   recursive?: boolean;
1240 *   retryDelay?: number;
1241 *   }} [options]
1242 * @param {(err?: Error) => any} callback
1243 * @returns {void}
1244 */
1245function rm(path, options, callback) {
1246  if (typeof options === 'function') {
1247    callback = options;
1248    options = undefined;
1249  }
1250  path = getValidatedPath(path);
1251
1252  validateRmOptions(path, options, false, (err, options) => {
1253    if (err) {
1254      return callback(err);
1255    }
1256    lazyLoadRimraf();
1257    return rimraf(pathModule.toNamespacedPath(path), options, callback);
1258  });
1259}
1260
1261/**
1262 * Synchronously removes files and
1263 * directories (modeled on the standard POSIX `rm` utility).
1264 * @param {string | Buffer | URL} path
1265 * @param {{
1266 *   force?: boolean;
1267 *   maxRetries?: number;
1268 *   recursive?: boolean;
1269 *   retryDelay?: number;
1270 *   }} [options]
1271 * @returns {void}
1272 */
1273function rmSync(path, options) {
1274  path = getValidatedPath(path);
1275  options = validateRmOptionsSync(path, options, false);
1276
1277  lazyLoadRimraf();
1278  return rimrafSync(pathModule.toNamespacedPath(path), options);
1279}
1280
1281/**
1282 * Forces all currently queued I/O operations associated
1283 * with the file to the operating system's synchronized
1284 * I/O completion state.
1285 * @param {number} fd
1286 * @param {(err?: Error) => any} callback
1287 * @returns {void}
1288 */
1289function fdatasync(fd, callback) {
1290  fd = getValidatedFd(fd);
1291  const req = new FSReqCallback();
1292  req.oncomplete = makeCallback(callback);
1293  binding.fdatasync(fd, req);
1294}
1295
1296/**
1297 * Synchronously forces all currently queued I/O operations
1298 * associated with the file to the operating
1299 * system's synchronized I/O completion state.
1300 * @param {number} fd
1301 * @returns {void}
1302 */
1303function fdatasyncSync(fd) {
1304  fd = getValidatedFd(fd);
1305  const ctx = {};
1306  binding.fdatasync(fd, undefined, ctx);
1307  handleErrorFromBinding(ctx);
1308}
1309
1310/**
1311 * Requests for all data for the open file descriptor
1312 * to be flushed to the storage device.
1313 * @param {number} fd
1314 * @param {(err?: Error) => any} callback
1315 * @returns {void}
1316 */
1317function fsync(fd, callback) {
1318  fd = getValidatedFd(fd);
1319  const req = new FSReqCallback();
1320  req.oncomplete = makeCallback(callback);
1321  binding.fsync(fd, req);
1322}
1323
1324/**
1325 * Synchronously requests for all data for the open
1326 * file descriptor to be flushed to the storage device.
1327 * @param {number} fd
1328 * @returns {void}
1329 */
1330function fsyncSync(fd) {
1331  fd = getValidatedFd(fd);
1332  const ctx = {};
1333  binding.fsync(fd, undefined, ctx);
1334  handleErrorFromBinding(ctx);
1335}
1336
1337/**
1338 * Asynchronously creates a directory.
1339 * @param {string | Buffer | URL} path
1340 * @param {{
1341 *   recursive?: boolean;
1342 *   mode?: string | number;
1343 *   } | number} [options]
1344 * @param {(err?: Error) => any} callback
1345 * @returns {void}
1346 */
1347function mkdir(path, options, callback) {
1348  let mode = 0o777;
1349  let recursive = false;
1350  if (typeof options === 'function') {
1351    callback = options;
1352  } else if (typeof options === 'number' || typeof options === 'string') {
1353    mode = options;
1354  } else if (options) {
1355    if (options.recursive !== undefined)
1356      recursive = options.recursive;
1357    if (options.mode !== undefined)
1358      mode = options.mode;
1359  }
1360  callback = makeCallback(callback);
1361  path = getValidatedPath(path);
1362
1363  validateBoolean(recursive, 'options.recursive');
1364
1365  const req = new FSReqCallback();
1366  req.oncomplete = callback;
1367  binding.mkdir(pathModule.toNamespacedPath(path),
1368                parseFileMode(mode, 'mode'), recursive, req);
1369}
1370
1371/**
1372 * Synchronously creates a directory.
1373 * @param {string | Buffer | URL} path
1374 * @param {{
1375 *   recursive?: boolean;
1376 *   mode?: string | number;
1377 *   } | number} [options]
1378 * @returns {string | void}
1379 */
1380function mkdirSync(path, options) {
1381  let mode = 0o777;
1382  let recursive = false;
1383  if (typeof options === 'number' || typeof options === 'string') {
1384    mode = options;
1385  } else if (options) {
1386    if (options.recursive !== undefined)
1387      recursive = options.recursive;
1388    if (options.mode !== undefined)
1389      mode = options.mode;
1390  }
1391  path = getValidatedPath(path);
1392  validateBoolean(recursive, 'options.recursive');
1393
1394  const ctx = { path };
1395  const result = binding.mkdir(pathModule.toNamespacedPath(path),
1396                               parseFileMode(mode, 'mode'), recursive,
1397                               undefined, ctx);
1398  handleErrorFromBinding(ctx);
1399  if (recursive) {
1400    return result;
1401  }
1402}
1403
1404/**
1405 * An iterative algorithm for reading the entire contents of the `basePath` directory.
1406 * This function does not validate `basePath` as a directory. It is passed directly to
1407 * `binding.readdir` after a `nullCheck`.
1408 * @param {string} basePath
1409 * @param {{ encoding: string, withFileTypes: boolean }} options
1410 * @returns {string[] | Dirent[]}
1411 */
1412function readdirSyncRecursive(basePath, options) {
1413  nullCheck(basePath, 'path', true);
1414
1415  const withFileTypes = Boolean(options.withFileTypes);
1416  const encoding = options.encoding;
1417
1418  const readdirResults = [];
1419  const pathsQueue = [basePath];
1420
1421  const ctx = { path: basePath };
1422  function read(path) {
1423    ctx.path = path;
1424    const readdirResult = binding.readdir(
1425      pathModule.toNamespacedPath(path),
1426      encoding,
1427      withFileTypes,
1428      undefined,
1429      ctx,
1430    );
1431    handleErrorFromBinding(ctx);
1432
1433    for (let i = 0; i < readdirResult.length; i++) {
1434      if (withFileTypes) {
1435        const dirent = getDirent(path, readdirResult[0][i], readdirResult[1][i]);
1436        ArrayPrototypePush(readdirResults, dirent);
1437        if (dirent.isDirectory()) {
1438          ArrayPrototypePush(pathsQueue, pathModule.join(dirent.path, dirent.name));
1439        }
1440      } else {
1441        const resultPath = pathModule.join(path, readdirResult[i]);
1442        const relativeResultPath = pathModule.relative(basePath, resultPath);
1443        const stat = binding.internalModuleStat(resultPath);
1444        ArrayPrototypePush(readdirResults, relativeResultPath);
1445        // 1 indicates directory
1446        if (stat === 1) {
1447          ArrayPrototypePush(pathsQueue, resultPath);
1448        }
1449      }
1450    }
1451  }
1452
1453  for (let i = 0; i < pathsQueue.length; i++) {
1454    read(pathsQueue[i]);
1455  }
1456
1457  return readdirResults;
1458}
1459
1460/**
1461 * Reads the contents of a directory.
1462 * @param {string | Buffer | URL} path
1463 * @param {string | {
1464 *   encoding?: string;
1465 *   withFileTypes?: boolean;
1466 *   }} [options]
1467 * @param {(
1468 *   err?: Error,
1469 *   files?: string[] | Buffer[] | Direct[];
1470 *   ) => any} callback
1471 * @returns {void}
1472 */
1473function readdir(path, options, callback) {
1474  callback = makeCallback(typeof options === 'function' ? options : callback);
1475  options = getOptions(options);
1476  path = getValidatedPath(path);
1477  if (options.recursive != null) {
1478    validateBoolean(options.recursive, 'options.recursive');
1479  }
1480
1481  if (options.recursive) {
1482    callback(null, readdirSyncRecursive(path, options));
1483    return;
1484  }
1485
1486  const req = new FSReqCallback();
1487  if (!options.withFileTypes) {
1488    req.oncomplete = callback;
1489  } else {
1490    req.oncomplete = (err, result) => {
1491      if (err) {
1492        callback(err);
1493        return;
1494      }
1495      getDirents(path, result, callback);
1496    };
1497  }
1498  binding.readdir(pathModule.toNamespacedPath(path), options.encoding,
1499                  !!options.withFileTypes, req);
1500}
1501
1502/**
1503 * Synchronously reads the contents of a directory.
1504 * @param {string | Buffer | URL} path
1505 * @param {string | {
1506 *   encoding?: string;
1507 *   withFileTypes?: boolean;
1508 *   recursive?: boolean;
1509 *   }} [options]
1510 * @returns {string | Buffer[] | Dirent[]}
1511 */
1512function readdirSync(path, options) {
1513  options = getOptions(options);
1514  path = getValidatedPath(path);
1515  if (options.recursive != null) {
1516    validateBoolean(options.recursive, 'options.recursive');
1517  }
1518
1519  if (options.recursive) {
1520    return readdirSyncRecursive(path, options);
1521  }
1522
1523  const ctx = { path };
1524  const result = binding.readdir(pathModule.toNamespacedPath(path),
1525                                 options.encoding, !!options.withFileTypes,
1526                                 undefined, ctx);
1527  handleErrorFromBinding(ctx);
1528  return options.withFileTypes ? getDirents(path, result) : result;
1529}
1530
1531/**
1532 * Invokes the callback with the `fs.Stats`
1533 * for the file descriptor.
1534 * @param {number} fd
1535 * @param {{ bigint?: boolean; }} [options]
1536 * @param {(
1537 *   err?: Error,
1538 *   stats?: Stats
1539 *   ) => any} callback
1540 * @returns {void}
1541 */
1542function fstat(fd, options = { bigint: false }, callback) {
1543  if (typeof options === 'function') {
1544    callback = options;
1545    options = kEmptyObject;
1546  }
1547  fd = getValidatedFd(fd);
1548  callback = makeStatsCallback(callback);
1549
1550  const req = new FSReqCallback(options.bigint);
1551  req.oncomplete = callback;
1552  binding.fstat(fd, options.bigint, req);
1553}
1554
1555/**
1556 * Retrieves the `fs.Stats` for the symbolic link
1557 * referred to by the `path`.
1558 * @param {string | Buffer | URL} path
1559 * @param {{ bigint?: boolean; }} [options]
1560 * @param {(
1561 *   err?: Error,
1562 *   stats?: Stats
1563 *   ) => any} callback
1564 * @returns {void}
1565 */
1566function lstat(path, options = { bigint: false }, callback) {
1567  if (typeof options === 'function') {
1568    callback = options;
1569    options = kEmptyObject;
1570  }
1571  callback = makeStatsCallback(callback);
1572  path = getValidatedPath(path);
1573
1574  const req = new FSReqCallback(options.bigint);
1575  req.oncomplete = callback;
1576  binding.lstat(pathModule.toNamespacedPath(path), options.bigint, req);
1577}
1578
1579/**
1580 * Asynchronously gets the stats of a file.
1581 * @param {string | Buffer | URL} path
1582 * @param {{ bigint?: boolean; }} [options]
1583 * @param {(
1584 *   err?: Error,
1585 *   stats?: Stats
1586 *   ) => any} callback
1587 * @returns {void}
1588 */
1589function stat(path, options = { bigint: false }, callback) {
1590  if (typeof options === 'function') {
1591    callback = options;
1592    options = kEmptyObject;
1593  }
1594  callback = makeStatsCallback(callback);
1595  path = getValidatedPath(path);
1596
1597  const req = new FSReqCallback(options.bigint);
1598  req.oncomplete = callback;
1599  binding.stat(pathModule.toNamespacedPath(path), options.bigint, req);
1600}
1601
1602function statfs(path, options = { bigint: false }, callback) {
1603  if (typeof options === 'function') {
1604    callback = options;
1605    options = kEmptyObject;
1606  }
1607  callback = maybeCallback(callback);
1608  path = getValidatedPath(path);
1609  const req = new FSReqCallback(options.bigint);
1610  req.oncomplete = (err, stats) => {
1611    if (err) {
1612      return callback(err);
1613    }
1614
1615    callback(err, getStatFsFromBinding(stats));
1616  };
1617  binding.statfs(pathModule.toNamespacedPath(path), options.bigint, req);
1618}
1619
1620function hasNoEntryError(ctx) {
1621  if (ctx.errno) {
1622    const uvErr = uvErrmapGet(ctx.errno);
1623    return uvErr?.[0] === 'ENOENT';
1624  }
1625
1626  if (ctx.error) {
1627    return ctx.error.code === 'ENOENT';
1628  }
1629
1630  return false;
1631}
1632
1633/**
1634 * Synchronously retrieves the `fs.Stats` for
1635 * the file descriptor.
1636 * @param {number} fd
1637 * @param {{
1638 *   bigint?: boolean;
1639 *   }} [options]
1640 * @returns {Stats}
1641 */
1642function fstatSync(fd, options = { bigint: false }) {
1643  fd = getValidatedFd(fd);
1644  const ctx = { fd };
1645  const stats = binding.fstat(fd, options.bigint, undefined, ctx);
1646  handleErrorFromBinding(ctx);
1647  return getStatsFromBinding(stats);
1648}
1649
1650/**
1651 * Synchronously retrieves the `fs.Stats` for
1652 * the symbolic link referred to by the `path`.
1653 * @param {string | Buffer | URL} path
1654 * @param {{
1655 *   bigint?: boolean;
1656 *   throwIfNoEntry?: boolean;
1657 *   }} [options]
1658 * @returns {Stats}
1659 */
1660function lstatSync(path, options = { bigint: false, throwIfNoEntry: true }) {
1661  path = getValidatedPath(path);
1662  const ctx = { path };
1663  const stats = binding.lstat(pathModule.toNamespacedPath(path),
1664                              options.bigint, undefined, ctx);
1665  if (options.throwIfNoEntry === false && hasNoEntryError(ctx)) {
1666    return undefined;
1667  }
1668  handleErrorFromBinding(ctx);
1669  return getStatsFromBinding(stats);
1670}
1671
1672/**
1673 * Synchronously retrieves the `fs.Stats`
1674 * for the `path`.
1675 * @param {string | Buffer | URL} path
1676 * @param {{
1677 *   bigint?: boolean;
1678 *   throwIfNoEntry?: boolean;
1679 *   }} [options]
1680 * @returns {Stats}
1681 */
1682function statSync(path, options = { bigint: false, throwIfNoEntry: true }) {
1683  path = getValidatedPath(path);
1684  const ctx = { path };
1685  const stats = binding.stat(pathModule.toNamespacedPath(path),
1686                             options.bigint, undefined, ctx);
1687  if (options.throwIfNoEntry === false && hasNoEntryError(ctx)) {
1688    return undefined;
1689  }
1690  handleErrorFromBinding(ctx);
1691  return getStatsFromBinding(stats);
1692}
1693
1694function statfsSync(path, options = { bigint: false }) {
1695  path = getValidatedPath(path);
1696  const ctx = { path };
1697  const stats = binding.statfs(pathModule.toNamespacedPath(path),
1698                               options.bigint, undefined, ctx);
1699  handleErrorFromBinding(ctx);
1700  return getStatFsFromBinding(stats);
1701}
1702
1703/**
1704 * Reads the contents of a symbolic link
1705 * referred to by `path`.
1706 * @param {string | Buffer | URL} path
1707 * @param {{ encoding?: string; } | string} [options]
1708 * @param {(
1709 *   err?: Error,
1710 *   linkString?: string | Buffer
1711 *   ) => any} callback
1712 * @returns {void}
1713 */
1714function readlink(path, options, callback) {
1715  callback = makeCallback(typeof options === 'function' ? options : callback);
1716  options = getOptions(options);
1717  path = getValidatedPath(path, 'oldPath');
1718  const req = new FSReqCallback();
1719  req.oncomplete = callback;
1720  binding.readlink(pathModule.toNamespacedPath(path), options.encoding, req);
1721}
1722
1723/**
1724 * Synchronously reads the contents of a symbolic link
1725 * referred to by `path`.
1726 * @param {string | Buffer | URL} path
1727 * @param {{ encoding?: string; } | string} [options]
1728 * @returns {string | Buffer}
1729 */
1730function readlinkSync(path, options) {
1731  options = getOptions(options);
1732  path = getValidatedPath(path, 'oldPath');
1733  const ctx = { path };
1734  const result = binding.readlink(pathModule.toNamespacedPath(path),
1735                                  options.encoding, undefined, ctx);
1736  handleErrorFromBinding(ctx);
1737  return result;
1738}
1739
1740/**
1741 * Creates the link called `path` pointing to `target`.
1742 * @param {string | Buffer | URL} target
1743 * @param {string | Buffer | URL} path
1744 * @param {string | null} [type_]
1745 * @param {(err?: Error) => any} callback_
1746 * @returns {void}
1747 */
1748function symlink(target, path, type_, callback_) {
1749  const type = (typeof type_ === 'string' ? type_ : null);
1750  const callback = makeCallback(arguments[arguments.length - 1]);
1751
1752  target = getValidatedPath(target, 'target');
1753  path = getValidatedPath(path);
1754
1755  if (isWindows && type === null) {
1756    let absoluteTarget;
1757    try {
1758      // Symlinks targets can be relative to the newly created path.
1759      // Calculate absolute file name of the symlink target, and check
1760      // if it is a directory. Ignore resolve error to keep symlink
1761      // errors consistent between platforms if invalid path is
1762      // provided.
1763      absoluteTarget = pathModule.resolve(path, '..', target);
1764    } catch {
1765      // Continue regardless of error.
1766    }
1767    if (absoluteTarget !== undefined) {
1768      stat(absoluteTarget, (err, stat) => {
1769        const resolvedType = !err && stat.isDirectory() ? 'dir' : 'file';
1770        const resolvedFlags = stringToSymlinkType(resolvedType);
1771        const destination = preprocessSymlinkDestination(target,
1772                                                         resolvedType,
1773                                                         path);
1774
1775        const req = new FSReqCallback();
1776        req.oncomplete = callback;
1777        binding.symlink(destination,
1778                        pathModule.toNamespacedPath(path), resolvedFlags, req);
1779      });
1780      return;
1781    }
1782  }
1783
1784  const destination = preprocessSymlinkDestination(target, type, path);
1785
1786  const flags = stringToSymlinkType(type);
1787  const req = new FSReqCallback();
1788  req.oncomplete = callback;
1789  binding.symlink(destination, pathModule.toNamespacedPath(path), flags, req);
1790}
1791
1792/**
1793 * Synchronously creates the link called `path`
1794 * pointing to `target`.
1795 * @param {string | Buffer | URL} target
1796 * @param {string | Buffer | URL} path
1797 * @param {string | null} [type]
1798 * @returns {void}
1799 */
1800function symlinkSync(target, path, type) {
1801  type = (typeof type === 'string' ? type : null);
1802  if (isWindows && type === null) {
1803    const absoluteTarget = pathModule.resolve(`${path}`, '..', `${target}`);
1804    if (statSync(absoluteTarget, { throwIfNoEntry: false })?.isDirectory()) {
1805      type = 'dir';
1806    }
1807  }
1808  target = getValidatedPath(target, 'target');
1809  path = getValidatedPath(path);
1810  const flags = stringToSymlinkType(type);
1811
1812  const ctx = { path: target, dest: path };
1813  binding.symlink(preprocessSymlinkDestination(target, type, path),
1814                  pathModule.toNamespacedPath(path), flags, undefined, ctx);
1815
1816  handleErrorFromBinding(ctx);
1817}
1818
1819/**
1820 * Creates a new link from the `existingPath`
1821 * to the `newPath`.
1822 * @param {string | Buffer | URL} existingPath
1823 * @param {string | Buffer | URL} newPath
1824 * @param {(err?: Error) => any} callback
1825 * @returns {void}
1826 */
1827function link(existingPath, newPath, callback) {
1828  callback = makeCallback(callback);
1829
1830  existingPath = getValidatedPath(existingPath, 'existingPath');
1831  newPath = getValidatedPath(newPath, 'newPath');
1832
1833  const req = new FSReqCallback();
1834  req.oncomplete = callback;
1835
1836  binding.link(pathModule.toNamespacedPath(existingPath),
1837               pathModule.toNamespacedPath(newPath),
1838               req);
1839}
1840
1841/**
1842 * Synchronously creates a new link from the `existingPath`
1843 * to the `newPath`.
1844 * @param {string | Buffer | URL} existingPath
1845 * @param {string | Buffer | URL} newPath
1846 * @returns {void}
1847 */
1848function linkSync(existingPath, newPath) {
1849  existingPath = getValidatedPath(existingPath, 'existingPath');
1850  newPath = getValidatedPath(newPath, 'newPath');
1851
1852  const ctx = { path: existingPath, dest: newPath };
1853  const result = binding.link(pathModule.toNamespacedPath(existingPath),
1854                              pathModule.toNamespacedPath(newPath),
1855                              undefined, ctx);
1856  handleErrorFromBinding(ctx);
1857  return result;
1858}
1859
1860/**
1861 * Asynchronously removes a file or symbolic link.
1862 * @param {string | Buffer | URL} path
1863 * @param {(err?: Error) => any} callback
1864 * @returns {void}
1865 */
1866function unlink(path, callback) {
1867  callback = makeCallback(callback);
1868  path = getValidatedPath(path);
1869  const req = new FSReqCallback();
1870  req.oncomplete = callback;
1871  binding.unlink(pathModule.toNamespacedPath(path), req);
1872}
1873
1874/**
1875 * Synchronously removes a file or symbolic link.
1876 * @param {string | Buffer | URL} path
1877 * @returns {void}
1878 */
1879function unlinkSync(path) {
1880  path = getValidatedPath(path);
1881  const ctx = { path };
1882  binding.unlink(pathModule.toNamespacedPath(path), undefined, ctx);
1883  handleErrorFromBinding(ctx);
1884}
1885
1886/**
1887 * Sets the permissions on the file.
1888 * @param {number} fd
1889 * @param {string | number} mode
1890 * @param {(err?: Error) => any} callback
1891 * @returns {void}
1892 */
1893function fchmod(fd, mode, callback) {
1894  fd = getValidatedFd(fd);
1895  mode = parseFileMode(mode, 'mode');
1896  callback = makeCallback(callback);
1897
1898  const req = new FSReqCallback();
1899  req.oncomplete = callback;
1900  binding.fchmod(fd, mode, req);
1901}
1902
1903/**
1904 * Synchronously sets the permissions on the file.
1905 * @param {number} fd
1906 * @param {string | number} mode
1907 * @returns {void}
1908 */
1909function fchmodSync(fd, mode) {
1910  fd = getValidatedFd(fd);
1911  mode = parseFileMode(mode, 'mode');
1912  const ctx = {};
1913  binding.fchmod(fd, mode, undefined, ctx);
1914  handleErrorFromBinding(ctx);
1915}
1916
1917/**
1918 * Changes the permissions on a symbolic link.
1919 * @param {string | Buffer | URL} path
1920 * @param {number} mode
1921 * @param {(err?: Error) => any} callback
1922 * @returns {void}
1923 */
1924function lchmod(path, mode, callback) {
1925  callback = maybeCallback(callback);
1926  mode = parseFileMode(mode, 'mode');
1927  fs.open(path, O_WRONLY | O_SYMLINK, (err, fd) => {
1928    if (err) {
1929      callback(err);
1930      return;
1931    }
1932    // Prefer to return the chmod error, if one occurs,
1933    // but still try to close, and report closing errors if they occur.
1934    fs.fchmod(fd, mode, (err) => {
1935      fs.close(fd, (err2) => {
1936        callback(aggregateTwoErrors(err2, err));
1937      });
1938    });
1939  });
1940}
1941
1942/**
1943 * Synchronously changes the permissions on a symbolic link.
1944 * @param {string | Buffer | URL} path
1945 * @param {number} mode
1946 * @returns {void}
1947 */
1948function lchmodSync(path, mode) {
1949  const fd = fs.openSync(path, O_WRONLY | O_SYMLINK);
1950
1951  // Prefer to return the chmod error, if one occurs,
1952  // but still try to close, and report closing errors if they occur.
1953  let ret;
1954  try {
1955    ret = fs.fchmodSync(fd, mode);
1956  } finally {
1957    fs.closeSync(fd);
1958  }
1959  return ret;
1960}
1961
1962/**
1963 * Asynchronously changes the permissions of a file.
1964 * @param {string | Buffer | URL} path
1965 * @param {string | number} mode
1966 * @param {(err?: Error) => any} callback
1967 * @returns {void}
1968 */
1969function chmod(path, mode, callback) {
1970  path = getValidatedPath(path);
1971  mode = parseFileMode(mode, 'mode');
1972  callback = makeCallback(callback);
1973
1974  const req = new FSReqCallback();
1975  req.oncomplete = callback;
1976  binding.chmod(pathModule.toNamespacedPath(path), mode, req);
1977}
1978
1979/**
1980 * Synchronously changes the permissions of a file.
1981 * @param {string | Buffer | URL} path
1982 * @param {string | number} mode
1983 * @returns {void}
1984 */
1985function chmodSync(path, mode) {
1986  path = getValidatedPath(path);
1987  mode = parseFileMode(mode, 'mode');
1988
1989  const ctx = { path };
1990  binding.chmod(pathModule.toNamespacedPath(path), mode, undefined, ctx);
1991  handleErrorFromBinding(ctx);
1992}
1993
1994/**
1995 * Sets the owner of the symbolic link.
1996 * @param {string | Buffer | URL} path
1997 * @param {number} uid
1998 * @param {number} gid
1999 * @param {(err?: Error) => any} callback
2000 * @returns {void}
2001 */
2002function lchown(path, uid, gid, callback) {
2003  callback = makeCallback(callback);
2004  path = getValidatedPath(path);
2005  validateInteger(uid, 'uid', -1, kMaxUserId);
2006  validateInteger(gid, 'gid', -1, kMaxUserId);
2007  const req = new FSReqCallback();
2008  req.oncomplete = callback;
2009  binding.lchown(pathModule.toNamespacedPath(path), uid, gid, req);
2010}
2011
2012/**
2013 * Synchronously sets the owner of the symbolic link.
2014 * @param {string | Buffer | URL} path
2015 * @param {number} uid
2016 * @param {number} gid
2017 * @returns {void}
2018 */
2019function lchownSync(path, uid, gid) {
2020  path = getValidatedPath(path);
2021  validateInteger(uid, 'uid', -1, kMaxUserId);
2022  validateInteger(gid, 'gid', -1, kMaxUserId);
2023  const ctx = { path };
2024  binding.lchown(pathModule.toNamespacedPath(path), uid, gid, undefined, ctx);
2025  handleErrorFromBinding(ctx);
2026}
2027
2028/**
2029 * Sets the owner of the file.
2030 * @param {number} fd
2031 * @param {number} uid
2032 * @param {number} gid
2033 * @param {(err?: Error) => any} callback
2034 * @returns {void}
2035 */
2036function fchown(fd, uid, gid, callback) {
2037  fd = getValidatedFd(fd);
2038  validateInteger(uid, 'uid', -1, kMaxUserId);
2039  validateInteger(gid, 'gid', -1, kMaxUserId);
2040  callback = makeCallback(callback);
2041
2042  const req = new FSReqCallback();
2043  req.oncomplete = callback;
2044  binding.fchown(fd, uid, gid, req);
2045}
2046
2047/**
2048 * Synchronously sets the owner of the file.
2049 * @param {number} fd
2050 * @param {number} uid
2051 * @param {number} gid
2052 * @returns {void}
2053 */
2054function fchownSync(fd, uid, gid) {
2055  fd = getValidatedFd(fd);
2056  validateInteger(uid, 'uid', -1, kMaxUserId);
2057  validateInteger(gid, 'gid', -1, kMaxUserId);
2058
2059  const ctx = {};
2060  binding.fchown(fd, uid, gid, undefined, ctx);
2061  handleErrorFromBinding(ctx);
2062}
2063
2064/**
2065 * Asynchronously changes the owner and group
2066 * of a file.
2067 * @param {string | Buffer | URL} path
2068 * @param {number} uid
2069 * @param {number} gid
2070 * @param {(err?: Error) => any} callback
2071 * @returns {void}
2072 */
2073function chown(path, uid, gid, callback) {
2074  callback = makeCallback(callback);
2075  path = getValidatedPath(path);
2076  validateInteger(uid, 'uid', -1, kMaxUserId);
2077  validateInteger(gid, 'gid', -1, kMaxUserId);
2078
2079  const req = new FSReqCallback();
2080  req.oncomplete = callback;
2081  binding.chown(pathModule.toNamespacedPath(path), uid, gid, req);
2082}
2083
2084/**
2085 * Synchronously changes the owner and group
2086 * of a file.
2087 * @param {string | Buffer | URL} path
2088 * @param {number} uid
2089 * @param {number} gid
2090 * @returns {void}
2091 */
2092function chownSync(path, uid, gid) {
2093  path = getValidatedPath(path);
2094  validateInteger(uid, 'uid', -1, kMaxUserId);
2095  validateInteger(gid, 'gid', -1, kMaxUserId);
2096  const ctx = { path };
2097  binding.chown(pathModule.toNamespacedPath(path), uid, gid, undefined, ctx);
2098  handleErrorFromBinding(ctx);
2099}
2100
2101/**
2102 * Changes the file system timestamps of the object
2103 * referenced by `path`.
2104 * @param {string | Buffer | URL} path
2105 * @param {number | string | Date} atime
2106 * @param {number | string | Date} mtime
2107 * @param {(err?: Error) => any} callback
2108 * @returns {void}
2109 */
2110function utimes(path, atime, mtime, callback) {
2111  callback = makeCallback(callback);
2112  path = getValidatedPath(path);
2113
2114  const req = new FSReqCallback();
2115  req.oncomplete = callback;
2116  binding.utimes(pathModule.toNamespacedPath(path),
2117                 toUnixTimestamp(atime),
2118                 toUnixTimestamp(mtime),
2119                 req);
2120}
2121
2122/**
2123 * Synchronously changes the file system timestamps
2124 * of the object referenced by `path`.
2125 * @param {string | Buffer | URL} path
2126 * @param {number | string | Date} atime
2127 * @param {number | string | Date} mtime
2128 * @returns {void}
2129 */
2130function utimesSync(path, atime, mtime) {
2131  path = getValidatedPath(path);
2132  const ctx = { path };
2133  binding.utimes(pathModule.toNamespacedPath(path),
2134                 toUnixTimestamp(atime), toUnixTimestamp(mtime),
2135                 undefined, ctx);
2136  handleErrorFromBinding(ctx);
2137}
2138
2139/**
2140 * Changes the file system timestamps of the object
2141 * referenced by the supplied `fd` (file descriptor).
2142 * @param {number} fd
2143 * @param {number | string | Date} atime
2144 * @param {number | string | Date} mtime
2145 * @param {(err?: Error) => any} callback
2146 * @returns {void}
2147 */
2148function futimes(fd, atime, mtime, callback) {
2149  fd = getValidatedFd(fd);
2150  atime = toUnixTimestamp(atime, 'atime');
2151  mtime = toUnixTimestamp(mtime, 'mtime');
2152  callback = makeCallback(callback);
2153
2154  const req = new FSReqCallback();
2155  req.oncomplete = callback;
2156  binding.futimes(fd, atime, mtime, req);
2157}
2158
2159/**
2160 * Synchronously changes the file system timestamps
2161 * of the object referenced by the
2162 * supplied `fd` (file descriptor).
2163 * @param {number} fd
2164 * @param {number | string | Date} atime
2165 * @param {number | string | Date} mtime
2166 * @returns {void}
2167 */
2168function futimesSync(fd, atime, mtime) {
2169  fd = getValidatedFd(fd);
2170  atime = toUnixTimestamp(atime, 'atime');
2171  mtime = toUnixTimestamp(mtime, 'mtime');
2172  const ctx = {};
2173  binding.futimes(fd, atime, mtime, undefined, ctx);
2174  handleErrorFromBinding(ctx);
2175}
2176
2177/**
2178 * Changes the access and modification times of
2179 * a file in the same way as `fs.utimes()`.
2180 * @param {string | Buffer | URL} path
2181 * @param {number | string | Date} atime
2182 * @param {number | string | Date} mtime
2183 * @param {(err?: Error) => any} callback
2184 * @returns {void}
2185 */
2186function lutimes(path, atime, mtime, callback) {
2187  callback = makeCallback(callback);
2188  path = getValidatedPath(path);
2189
2190  const req = new FSReqCallback();
2191  req.oncomplete = callback;
2192  binding.lutimes(pathModule.toNamespacedPath(path),
2193                  toUnixTimestamp(atime),
2194                  toUnixTimestamp(mtime),
2195                  req);
2196}
2197
2198/**
2199 * Synchronously changes the access and modification
2200 * times of a file in the same way as `fs.utimesSync()`.
2201 * @param {string | Buffer | URL} path
2202 * @param {number | string | Date} atime
2203 * @param {number | string | Date} mtime
2204 * @returns {void}
2205 */
2206function lutimesSync(path, atime, mtime) {
2207  path = getValidatedPath(path);
2208  const ctx = { path };
2209  binding.lutimes(pathModule.toNamespacedPath(path),
2210                  toUnixTimestamp(atime),
2211                  toUnixTimestamp(mtime),
2212                  undefined, ctx);
2213  handleErrorFromBinding(ctx);
2214}
2215
2216function writeAll(fd, isUserFd, buffer, offset, length, signal, callback) {
2217  if (signal?.aborted) {
2218    const abortError = new AbortError(undefined, { cause: signal?.reason });
2219    if (isUserFd) {
2220      callback(abortError);
2221    } else {
2222      fs.close(fd, (err) => {
2223        callback(aggregateTwoErrors(err, abortError));
2224      });
2225    }
2226    return;
2227  }
2228  // write(fd, buffer, offset, length, position, callback)
2229  fs.write(fd, buffer, offset, length, null, (writeErr, written) => {
2230    if (writeErr) {
2231      if (isUserFd) {
2232        callback(writeErr);
2233      } else {
2234        fs.close(fd, (err) => {
2235          callback(aggregateTwoErrors(err, writeErr));
2236        });
2237      }
2238    } else if (written === length) {
2239      if (isUserFd) {
2240        callback(null);
2241      } else {
2242        fs.close(fd, callback);
2243      }
2244    } else {
2245      offset += written;
2246      length -= written;
2247      writeAll(fd, isUserFd, buffer, offset, length, signal, callback);
2248    }
2249  });
2250}
2251
2252/**
2253 * Asynchronously writes data to the file.
2254 * @param {string | Buffer | URL | number} path
2255 * @param {string | Buffer | TypedArray | DataView | object} data
2256 * @param {{
2257 *   encoding?: string | null;
2258 *   mode?: number;
2259 *   flag?: string;
2260 *   signal?: AbortSignal;
2261 *   } | string} [options]
2262 * @param {(err?: Error) => any} callback
2263 * @returns {void}
2264 */
2265function writeFile(path, data, options, callback) {
2266  callback = maybeCallback(callback || options);
2267  options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'w' });
2268  const flag = options.flag || 'w';
2269
2270  if (!isArrayBufferView(data)) {
2271    validateStringAfterArrayBufferView(data, 'data');
2272    if (typeof data !== 'string') {
2273      showStringCoercionDeprecation();
2274    }
2275    data = Buffer.from(String(data), options.encoding || 'utf8');
2276  }
2277
2278  if (isFd(path)) {
2279    const isUserFd = true;
2280    const signal = options.signal;
2281    writeAll(path, isUserFd, data, 0, data.byteLength, signal, callback);
2282    return;
2283  }
2284
2285  if (checkAborted(options.signal, callback))
2286    return;
2287
2288  fs.open(path, flag, options.mode, (openErr, fd) => {
2289    if (openErr) {
2290      callback(openErr);
2291    } else {
2292      const isUserFd = false;
2293      const signal = options.signal;
2294      writeAll(fd, isUserFd, data, 0, data.byteLength, signal, callback);
2295    }
2296  });
2297}
2298
2299/**
2300 * Synchronously writes data to the file.
2301 * @param {string | Buffer | URL | number} path
2302 * @param {string | Buffer | TypedArray | DataView | object} data
2303 * @param {{
2304 *   encoding?: string | null;
2305 *   mode?: number;
2306 *   flag?: string;
2307 *   } | string} [options]
2308 * @returns {void}
2309 */
2310function writeFileSync(path, data, options) {
2311  options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'w' });
2312
2313  if (!isArrayBufferView(data)) {
2314    validateStringAfterArrayBufferView(data, 'data');
2315    if (typeof data !== 'string') {
2316      showStringCoercionDeprecation();
2317    }
2318    data = Buffer.from(String(data), options.encoding || 'utf8');
2319  }
2320
2321  const flag = options.flag || 'w';
2322
2323  const isUserFd = isFd(path); // File descriptor ownership
2324  const fd = isUserFd ? path : fs.openSync(path, flag, options.mode);
2325
2326  let offset = 0;
2327  let length = data.byteLength;
2328  try {
2329    while (length > 0) {
2330      const written = fs.writeSync(fd, data, offset, length);
2331      offset += written;
2332      length -= written;
2333    }
2334  } finally {
2335    if (!isUserFd) fs.closeSync(fd);
2336  }
2337}
2338
2339/**
2340 * Asynchronously appends data to a file.
2341 * @param {string | Buffer | URL | number} path
2342 * @param {string | Buffer} data
2343 * @param {{
2344 *   encoding?: string | null;
2345 *   mode?: number;
2346 *   flag?: string;
2347 *   } | string} [options]
2348 * @param {(err?: Error) => any} callback
2349 * @returns {void}
2350 */
2351function appendFile(path, data, options, callback) {
2352  callback = maybeCallback(callback || options);
2353  options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'a' });
2354
2355  // Don't make changes directly on options object
2356  options = copyObject(options);
2357
2358  // Force append behavior when using a supplied file descriptor
2359  if (!options.flag || isFd(path))
2360    options.flag = 'a';
2361
2362  fs.writeFile(path, data, options, callback);
2363}
2364
2365/**
2366 * Synchronously appends data to a file.
2367 * @param {string | Buffer | URL | number} path
2368 * @param {string | Buffer} data
2369 * @param {{
2370 *   encoding?: string | null;
2371 *   mode?: number;
2372 *   flag?: string;
2373 *   } | string} [options]
2374 * @returns {void}
2375 */
2376function appendFileSync(path, data, options) {
2377  options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'a' });
2378
2379  // Don't make changes directly on options object
2380  options = copyObject(options);
2381
2382  // Force append behavior when using a supplied file descriptor
2383  if (!options.flag || isFd(path))
2384    options.flag = 'a';
2385
2386  fs.writeFileSync(path, data, options);
2387}
2388
2389/**
2390 * Watches for the changes on `filename`.
2391 * @param {string | Buffer | URL} filename
2392 * @param {string | {
2393 *   persistent?: boolean;
2394 *   recursive?: boolean;
2395 *   encoding?: string;
2396 *   signal?: AbortSignal;
2397 *   }} [options]
2398 * @param {(
2399 *   eventType?: string,
2400 *   filename?: string | Buffer
2401 *   ) => any} [listener]
2402 * @returns {watchers.FSWatcher}
2403 */
2404function watch(filename, options, listener) {
2405  if (typeof options === 'function') {
2406    listener = options;
2407  }
2408  options = getOptions(options);
2409
2410  // Don't make changes directly on options object
2411  options = copyObject(options);
2412
2413  if (options.persistent === undefined) options.persistent = true;
2414  if (options.recursive === undefined) options.recursive = false;
2415  if (options.recursive && !(isOSX || isWindows))
2416    throw new ERR_FEATURE_UNAVAILABLE_ON_PLATFORM('watch recursively');
2417  const watcher = new watchers.FSWatcher();
2418  watcher[watchers.kFSWatchStart](filename,
2419                                  options.persistent,
2420                                  options.recursive,
2421                                  options.encoding);
2422
2423  if (listener) {
2424    watcher.addListener('change', listener);
2425  }
2426  if (options.signal) {
2427    if (options.signal.aborted) {
2428      process.nextTick(() => watcher.close());
2429    } else {
2430      const listener = () => watcher.close();
2431      options.signal.addEventListener('abort', listener);
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
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
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} 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  validateString(prefix, 'prefix');
2896  nullCheck(prefix, 'prefix');
2897  warnOnNonPortableTemplate(prefix);
2898  const req = new FSReqCallback();
2899  req.oncomplete = callback;
2900  binding.mkdtemp(`${prefix}XXXXXX`, options.encoding, req);
2901}
2902
2903/**
2904 * Synchronously creates a unique temporary directory.
2905 * @param {string} prefix
2906 * @param {string | { encoding?: string; }} [options]
2907 * @returns {string}
2908 */
2909function mkdtempSync(prefix, options) {
2910  options = getOptions(options);
2911
2912  validateString(prefix, 'prefix');
2913  nullCheck(prefix, 'prefix');
2914  warnOnNonPortableTemplate(prefix);
2915  const path = `${prefix}XXXXXX`;
2916  const ctx = { path };
2917  const result = binding.mkdtemp(path, options.encoding,
2918                                 undefined, ctx);
2919  handleErrorFromBinding(ctx);
2920  return result;
2921}
2922
2923/**
2924 * Asynchronously copies `src` to `dest`. By
2925 * default, `dest` is overwritten if it already exists.
2926 * @param {string | Buffer | URL} src
2927 * @param {string | Buffer | URL} dest
2928 * @param {number} [mode]
2929 * @param {() => any} callback
2930 * @returns {void}
2931 */
2932function copyFile(src, dest, mode, callback) {
2933  if (typeof mode === 'function') {
2934    callback = mode;
2935    mode = 0;
2936  }
2937
2938  src = getValidatedPath(src, 'src');
2939  dest = getValidatedPath(dest, 'dest');
2940
2941  src = pathModule._makeLong(src);
2942  dest = pathModule._makeLong(dest);
2943  mode = getValidMode(mode, 'copyFile');
2944  callback = makeCallback(callback);
2945
2946  const req = new FSReqCallback();
2947  req.oncomplete = callback;
2948  binding.copyFile(src, dest, mode, req);
2949}
2950
2951/**
2952 * Synchronously copies `src` to `dest`. By
2953 * default, `dest` is overwritten if it already exists.
2954 * @param {string | Buffer | URL} src
2955 * @param {string | Buffer | URL} dest
2956 * @param {number} [mode]
2957 * @returns {void}
2958 */
2959function copyFileSync(src, dest, mode) {
2960  src = getValidatedPath(src, 'src');
2961  dest = getValidatedPath(dest, 'dest');
2962
2963  const ctx = { path: src, dest };  // non-prefixed
2964
2965  src = pathModule._makeLong(src);
2966  dest = pathModule._makeLong(dest);
2967  mode = getValidMode(mode, 'copyFile');
2968  binding.copyFile(src, dest, mode, undefined, ctx);
2969  handleErrorFromBinding(ctx);
2970}
2971
2972/**
2973 * Asynchronously copies `src` to `dest`. `src` can be a file, directory, or
2974 * symlink. The contents of directories will be copied recursively.
2975 * @param {string | URL} src
2976 * @param {string | URL} dest
2977 * @param {object} [options]
2978 * @param {() => any} callback
2979 * @returns {void}
2980 */
2981function cp(src, dest, options, callback) {
2982  if (typeof options === 'function') {
2983    callback = options;
2984    options = undefined;
2985  }
2986  callback = makeCallback(callback);
2987  options = validateCpOptions(options);
2988  src = pathModule.toNamespacedPath(getValidatedPath(src, 'src'));
2989  dest = pathModule.toNamespacedPath(getValidatedPath(dest, 'dest'));
2990  lazyLoadCp();
2991  cpFn(src, dest, options, callback);
2992}
2993
2994/**
2995 * Synchronously copies `src` to `dest`. `src` can be a file, directory, or
2996 * symlink. The contents of directories will be copied recursively.
2997 * @param {string | URL} src
2998 * @param {string | URL} dest
2999 * @param {object} [options]
3000 * @returns {void}
3001 */
3002function cpSync(src, dest, options) {
3003  options = validateCpOptions(options);
3004  src = pathModule.toNamespacedPath(getValidatedPath(src, 'src'));
3005  dest = pathModule.toNamespacedPath(getValidatedPath(dest, 'dest'));
3006  lazyLoadCp();
3007  cpSyncFn(src, dest, options);
3008}
3009
3010function lazyLoadStreams() {
3011  if (!ReadStream) {
3012    ({ ReadStream, WriteStream } = require('internal/fs/streams'));
3013    FileReadStream = ReadStream;
3014    FileWriteStream = WriteStream;
3015  }
3016}
3017
3018/**
3019 * Creates a readable stream with a default `highWaterMark`
3020 * of 64 KiB.
3021 * @param {string | Buffer | URL} path
3022 * @param {string | {
3023 *   flags?: string;
3024 *   encoding?: string;
3025 *   fd?: number | FileHandle;
3026 *   mode?: number;
3027 *   autoClose?: boolean;
3028 *   emitClose?: boolean;
3029 *   start: number;
3030 *   end?: number;
3031 *   highWaterMark?: number;
3032 *   fs?: object | null;
3033 *   }} [options]
3034 * @returns {ReadStream}
3035 */
3036function createReadStream(path, options) {
3037  lazyLoadStreams();
3038  return new ReadStream(path, options);
3039}
3040
3041/**
3042 * Creates a write stream.
3043 * @param {string | Buffer | URL} path
3044 * @param {string | {
3045 *   flags?: string;
3046 *   encoding?: string;
3047 *   fd?: number | FileHandle;
3048 *   mode?: number;
3049 *   autoClose?: boolean;
3050 *   emitClose?: boolean;
3051 *   start: number;
3052 *   fs?: object | null;
3053 *   }} [options]
3054 * @returns {WriteStream}
3055 */
3056function createWriteStream(path, options) {
3057  lazyLoadStreams();
3058  return new WriteStream(path, options);
3059}
3060
3061module.exports = fs = {
3062  appendFile,
3063  appendFileSync,
3064  access,
3065  accessSync,
3066  chown,
3067  chownSync,
3068  chmod,
3069  chmodSync,
3070  close,
3071  closeSync,
3072  copyFile,
3073  copyFileSync,
3074  cp,
3075  cpSync,
3076  createReadStream,
3077  createWriteStream,
3078  exists,
3079  existsSync,
3080  fchown,
3081  fchownSync,
3082  fchmod,
3083  fchmodSync,
3084  fdatasync,
3085  fdatasyncSync,
3086  fstat,
3087  fstatSync,
3088  fsync,
3089  fsyncSync,
3090  ftruncate,
3091  ftruncateSync,
3092  futimes,
3093  futimesSync,
3094  lchown,
3095  lchownSync,
3096  lchmod: constants.O_SYMLINK !== undefined ? lchmod : undefined,
3097  lchmodSync: constants.O_SYMLINK !== undefined ? lchmodSync : undefined,
3098  link,
3099  linkSync,
3100  lstat,
3101  lstatSync,
3102  lutimes,
3103  lutimesSync,
3104  mkdir,
3105  mkdirSync,
3106  mkdtemp,
3107  mkdtempSync,
3108  open,
3109  openSync,
3110  opendir,
3111  opendirSync,
3112  readdir,
3113  readdirSync,
3114  read,
3115  readSync,
3116  readv,
3117  readvSync,
3118  readFile,
3119  readFileSync,
3120  readlink,
3121  readlinkSync,
3122  realpath,
3123  realpathSync,
3124  rename,
3125  renameSync,
3126  rm,
3127  rmSync,
3128  rmdir,
3129  rmdirSync,
3130  stat,
3131  statfs,
3132  statSync,
3133  statfsSync,
3134  symlink,
3135  symlinkSync,
3136  truncate,
3137  truncateSync,
3138  unwatchFile,
3139  unlink,
3140  unlinkSync,
3141  utimes,
3142  utimesSync,
3143  watch,
3144  watchFile,
3145  writeFile,
3146  writeFileSync,
3147  write,
3148  writeSync,
3149  writev,
3150  writevSync,
3151  Dir,
3152  Dirent,
3153  Stats,
3154
3155  get ReadStream() {
3156    lazyLoadStreams();
3157    return ReadStream;
3158  },
3159
3160  set ReadStream(val) {
3161    ReadStream = val;
3162  },
3163
3164  get WriteStream() {
3165    lazyLoadStreams();
3166    return WriteStream;
3167  },
3168
3169  set WriteStream(val) {
3170    WriteStream = val;
3171  },
3172
3173  // Legacy names... these have to be separate because of how graceful-fs
3174  // (and possibly other) modules monkey patch the values.
3175  get FileReadStream() {
3176    lazyLoadStreams();
3177    return FileReadStream;
3178  },
3179
3180  set FileReadStream(val) {
3181    FileReadStream = val;
3182  },
3183
3184  get FileWriteStream() {
3185    lazyLoadStreams();
3186    return FileWriteStream;
3187  },
3188
3189  set FileWriteStream(val) {
3190    FileWriteStream = val;
3191  },
3192
3193  // For tests
3194  _toUnixTimestamp: toUnixTimestamp,
3195};
3196
3197ObjectDefineProperties(fs, {
3198  F_OK: { __proto__: null, enumerable: true, value: F_OK || 0 },
3199  R_OK: { __proto__: null, enumerable: true, value: R_OK || 0 },
3200  W_OK: { __proto__: null, enumerable: true, value: W_OK || 0 },
3201  X_OK: { __proto__: null, enumerable: true, value: X_OK || 0 },
3202  constants: {
3203    __proto__: null,
3204    configurable: false,
3205    enumerable: true,
3206    value: constants,
3207  },
3208  promises: {
3209    __proto__: null,
3210    configurable: true,
3211    enumerable: true,
3212    get() {
3213      promises ??= require('internal/fs/promises').exports;
3214      return promises;
3215    },
3216  },
3217});
3218