• 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
27// When using FSReqCallback, make sure to create the object only *after* all
28// parameter validation has happened, so that the objects are not kept in memory
29// in case they are created but never used due to an exception.
30
31const {
32  Map,
33  MathMax,
34  Number,
35  ObjectCreate,
36  ObjectDefineProperties,
37  ObjectDefineProperty,
38  Promise,
39  String,
40} = primordials;
41
42const { fs: constants } = internalBinding('constants');
43const {
44  S_IFIFO,
45  S_IFLNK,
46  S_IFMT,
47  S_IFREG,
48  S_IFSOCK,
49  F_OK,
50  R_OK,
51  W_OK,
52  X_OK,
53  O_WRONLY,
54  O_SYMLINK
55} = constants;
56
57const pathModule = require('path');
58const { isArrayBufferView } = require('internal/util/types');
59const binding = internalBinding('fs');
60const { Buffer } = require('buffer');
61const {
62  codes: {
63    ERR_FS_FILE_TOO_LARGE,
64    ERR_INVALID_ARG_VALUE,
65    ERR_INVALID_ARG_TYPE,
66    ERR_INVALID_CALLBACK,
67    ERR_FEATURE_UNAVAILABLE_ON_PLATFORM,
68  },
69  hideStackFrames,
70  uvErrmapGet,
71  uvException
72} = require('internal/errors');
73
74const { FSReqCallback, statValues } = binding;
75const { toPathIfFileURL } = require('internal/url');
76const internalUtil = require('internal/util');
77const {
78  constants: {
79    kIoMaxLength,
80    kMaxUserId,
81  },
82  copyObject,
83  Dirent,
84  getDirents,
85  getOptions,
86  getValidatedPath,
87  getValidMode,
88  handleErrorFromBinding,
89  nullCheck,
90  preprocessSymlinkDestination,
91  Stats,
92  getStatsFromBinding,
93  realpathCacheKey,
94  stringToFlags,
95  stringToSymlinkType,
96  toUnixTimestamp,
97  validateBufferArray,
98  validateOffsetLengthRead,
99  validateOffsetLengthWrite,
100  validatePath,
101  validatePosition,
102  validateRmOptions,
103  validateRmOptionsSync,
104  validateRmdirOptions,
105  validateStringAfterArrayBufferView,
106  validatePrimitiveStringAfterArrayBufferView,
107  warnOnNonPortableTemplate
108} = require('internal/fs/utils');
109const {
110  Dir,
111  opendir,
112  opendirSync
113} = require('internal/fs/dir');
114const {
115  CHAR_FORWARD_SLASH,
116  CHAR_BACKWARD_SLASH,
117} = require('internal/constants');
118const {
119  isUint32,
120  parseFileMode,
121  validateBuffer,
122  validateInteger,
123  validateInt32,
124  validateString,
125} = require('internal/validators');
126
127let truncateWarn = true;
128let fs;
129
130// Lazy loaded
131let promises = null;
132let watchers;
133let ReadFileContext;
134let ReadStream;
135let WriteStream;
136let rimraf;
137let rimrafSync;
138let DOMException;
139
140const lazyDOMException = hideStackFrames((message, name) => {
141  if (DOMException === undefined)
142    DOMException = internalBinding('messaging').DOMException;
143  return new DOMException(message, name);
144});
145
146// These have to be separate because of how graceful-fs happens to do it's
147// monkeypatching.
148let FileReadStream;
149let FileWriteStream;
150
151const isWindows = process.platform === 'win32';
152const isOSX = process.platform === 'darwin';
153
154
155function showTruncateDeprecation() {
156  if (truncateWarn) {
157    process.emitWarning(
158      'Using fs.truncate with a file descriptor is deprecated. Please use ' +
159      'fs.ftruncate with a file descriptor instead.',
160      'DeprecationWarning', 'DEP0081');
161    truncateWarn = false;
162  }
163}
164
165function maybeCallback(cb) {
166  if (typeof cb === 'function')
167    return cb;
168
169  throw new ERR_INVALID_CALLBACK(cb);
170}
171
172// Ensure that callbacks run in the global context. Only use this function
173// for callbacks that are passed to the binding layer, callbacks that are
174// invoked from JS already run in the proper scope.
175function makeCallback(cb) {
176  if (typeof cb !== 'function') {
177    throw new ERR_INVALID_CALLBACK(cb);
178  }
179
180  return (...args) => cb(...args);
181}
182
183// Special case of `makeCallback()` that is specific to async `*stat()` calls as
184// an optimization, since the data passed back to the callback needs to be
185// transformed anyway.
186function makeStatsCallback(cb) {
187  if (typeof cb !== 'function') {
188    throw new ERR_INVALID_CALLBACK(cb);
189  }
190
191  return (err, stats) => {
192    if (err) return cb(err);
193    cb(err, getStatsFromBinding(stats));
194  };
195}
196
197const isFd = isUint32;
198
199function isFileType(stats, fileType) {
200  // Use stats array directly to avoid creating an fs.Stats instance just for
201  // our internal use.
202  let mode = stats[1];
203  if (typeof mode === 'bigint')
204    mode = Number(mode);
205  return (mode & S_IFMT) === fileType;
206}
207
208function access(path, mode, callback) {
209  if (typeof mode === 'function') {
210    callback = mode;
211    mode = F_OK;
212  }
213
214  path = getValidatedPath(path);
215  mode = getValidMode(mode, 'access');
216  callback = makeCallback(callback);
217
218  const req = new FSReqCallback();
219  req.oncomplete = callback;
220  binding.access(pathModule.toNamespacedPath(path), mode, req);
221}
222
223function accessSync(path, mode) {
224  path = getValidatedPath(path);
225  mode = getValidMode(mode, 'access');
226
227  const ctx = { path };
228  binding.access(pathModule.toNamespacedPath(path), mode, undefined, ctx);
229  handleErrorFromBinding(ctx);
230}
231
232function exists(path, callback) {
233  maybeCallback(callback);
234
235  function suppressedCallback(err) {
236    callback(err ? false : true);
237  }
238
239  try {
240    fs.access(path, F_OK, suppressedCallback);
241  } catch {
242    return callback(false);
243  }
244}
245
246ObjectDefineProperty(exists, internalUtil.promisify.custom, {
247  value: (path) => {
248    return new Promise((resolve) => fs.exists(path, resolve));
249  }
250});
251
252// fs.existsSync never throws, it only returns true or false.
253// Since fs.existsSync never throws, users have established
254// the expectation that passing invalid arguments to it, even like
255// fs.existsSync(), would only get a false in return, so we cannot signal
256// validation errors to users properly out of compatibility concerns.
257// TODO(joyeecheung): deprecate the never-throw-on-invalid-arguments behavior
258function existsSync(path) {
259  try {
260    path = getValidatedPath(path);
261  } catch {
262    return false;
263  }
264  const ctx = { path };
265  const nPath = pathModule.toNamespacedPath(path);
266  binding.access(nPath, F_OK, undefined, ctx);
267
268  // In case of an invalid symlink, `binding.access()` on win32
269  // will **not** return an error and is therefore not enough.
270  // Double check with `binding.stat()`.
271  if (isWindows && ctx.errno === undefined) {
272    binding.stat(nPath, false, undefined, ctx);
273  }
274
275  return ctx.errno === undefined;
276}
277
278function readFileAfterOpen(err, fd) {
279  const context = this.context;
280
281  if (err) {
282    context.callback(err);
283    return;
284  }
285
286  context.fd = fd;
287
288  const req = new FSReqCallback();
289  req.oncomplete = readFileAfterStat;
290  req.context = context;
291  binding.fstat(fd, false, req);
292}
293
294function readFileAfterStat(err, stats) {
295  const context = this.context;
296
297  if (err)
298    return context.close(err);
299
300  const size = context.size = isFileType(stats, S_IFREG) ? stats[8] : 0;
301
302  if (size > kIoMaxLength) {
303    err = new ERR_FS_FILE_TOO_LARGE(size);
304    return context.close(err);
305  }
306
307  try {
308    if (size === 0) {
309      context.buffers = [];
310    } else {
311      context.buffer = Buffer.allocUnsafeSlow(size);
312    }
313  } catch (err) {
314    return context.close(err);
315  }
316  context.read();
317}
318
319function readFile(path, options, callback) {
320  callback = maybeCallback(callback || options);
321  options = getOptions(options, { flag: 'r' });
322  if (!ReadFileContext)
323    ReadFileContext = require('internal/fs/read_file_context');
324  const context = new ReadFileContext(callback, options.encoding);
325  context.isUserFd = isFd(path); // File descriptor ownership
326
327  if (options.signal) {
328    context.signal = options.signal;
329  }
330  if (context.isUserFd) {
331    process.nextTick(function tick(context) {
332      readFileAfterOpen.call({ context }, null, path);
333    }, context);
334    return;
335  }
336
337  if (options.signal?.aborted) {
338    callback(lazyDOMException('The operation was aborted', 'AbortError'));
339    return;
340  }
341
342  const flagsNumber = stringToFlags(options.flag);
343  path = getValidatedPath(path);
344
345  const req = new FSReqCallback();
346  req.context = context;
347  req.oncomplete = readFileAfterOpen;
348  binding.open(pathModule.toNamespacedPath(path),
349               flagsNumber,
350               0o666,
351               req);
352}
353
354function tryStatSync(fd, isUserFd) {
355  const ctx = {};
356  const stats = binding.fstat(fd, false, undefined, ctx);
357  if (ctx.errno !== undefined && !isUserFd) {
358    fs.closeSync(fd);
359    throw uvException(ctx);
360  }
361  return stats;
362}
363
364function tryCreateBuffer(size, fd, isUserFd) {
365  let threw = true;
366  let buffer;
367  try {
368    if (size > kIoMaxLength) {
369      throw new ERR_FS_FILE_TOO_LARGE(size);
370    }
371    buffer = Buffer.allocUnsafe(size);
372    threw = false;
373  } finally {
374    if (threw && !isUserFd) fs.closeSync(fd);
375  }
376  return buffer;
377}
378
379function tryReadSync(fd, isUserFd, buffer, pos, len) {
380  let threw = true;
381  let bytesRead;
382  try {
383    bytesRead = fs.readSync(fd, buffer, pos, len);
384    threw = false;
385  } finally {
386    if (threw && !isUserFd) fs.closeSync(fd);
387  }
388  return bytesRead;
389}
390
391function readFileSync(path, options) {
392  options = getOptions(options, { flag: 'r' });
393  const isUserFd = isFd(path); // File descriptor ownership
394  const fd = isUserFd ? path : fs.openSync(path, options.flag, 0o666);
395
396  const stats = tryStatSync(fd, isUserFd);
397  const size = isFileType(stats, S_IFREG) ? stats[8] : 0;
398  let pos = 0;
399  let buffer; // Single buffer with file data
400  let buffers; // List for when size is unknown
401
402  if (size === 0) {
403    buffers = [];
404  } else {
405    buffer = tryCreateBuffer(size, fd, isUserFd);
406  }
407
408  let bytesRead;
409
410  if (size !== 0) {
411    do {
412      bytesRead = tryReadSync(fd, isUserFd, buffer, pos, size - pos);
413      pos += bytesRead;
414    } while (bytesRead !== 0 && pos < size);
415  } else {
416    do {
417      // The kernel lies about many files.
418      // Go ahead and try to read some bytes.
419      buffer = Buffer.allocUnsafe(8192);
420      bytesRead = tryReadSync(fd, isUserFd, buffer, 0, 8192);
421      if (bytesRead !== 0) {
422        buffers.push(buffer.slice(0, bytesRead));
423      }
424      pos += bytesRead;
425    } while (bytesRead !== 0);
426  }
427
428  if (!isUserFd)
429    fs.closeSync(fd);
430
431  if (size === 0) {
432    // Data was collected into the buffers list.
433    buffer = Buffer.concat(buffers, pos);
434  } else if (pos < size) {
435    buffer = buffer.slice(0, pos);
436  }
437
438  if (options.encoding) buffer = buffer.toString(options.encoding);
439  return buffer;
440}
441
442function defaultCloseCallback(err) {
443  if (err != null) throw err;
444}
445
446function close(fd, callback = defaultCloseCallback) {
447  validateInt32(fd, 'fd', 0);
448  if (callback !== defaultCloseCallback)
449    callback = makeCallback(callback);
450
451  const req = new FSReqCallback();
452  req.oncomplete = callback;
453  binding.close(fd, req);
454}
455
456function closeSync(fd) {
457  validateInt32(fd, 'fd', 0);
458
459  const ctx = {};
460  binding.close(fd, undefined, ctx);
461  handleErrorFromBinding(ctx);
462}
463
464function open(path, flags, mode, callback) {
465  path = getValidatedPath(path);
466  if (arguments.length < 3) {
467    callback = flags;
468    flags = 'r';
469    mode = 0o666;
470  } else if (typeof mode === 'function') {
471    callback = mode;
472    mode = 0o666;
473  } else {
474    mode = parseFileMode(mode, 'mode', 0o666);
475  }
476  const flagsNumber = stringToFlags(flags);
477  callback = makeCallback(callback);
478
479  const req = new FSReqCallback();
480  req.oncomplete = callback;
481
482  binding.open(pathModule.toNamespacedPath(path),
483               flagsNumber,
484               mode,
485               req);
486}
487
488
489function openSync(path, flags, mode) {
490  path = getValidatedPath(path);
491  const flagsNumber = stringToFlags(flags);
492  mode = parseFileMode(mode, 'mode', 0o666);
493
494  const ctx = { path };
495  const result = binding.open(pathModule.toNamespacedPath(path),
496                              flagsNumber, mode,
497                              undefined, ctx);
498  handleErrorFromBinding(ctx);
499  return result;
500}
501
502// usage:
503// fs.read(fd, buffer, offset, length, position, callback);
504// OR
505// fs.read(fd, {}, callback)
506function read(fd, buffer, offset, length, position, callback) {
507  validateInt32(fd, 'fd', 0);
508
509  if (arguments.length <= 3) {
510    // Assume fs.read(fd, options, callback)
511    let options = {};
512    if (arguments.length < 3) {
513      // This is fs.read(fd, callback)
514      // buffer will be the callback
515      callback = buffer;
516    } else {
517      // This is fs.read(fd, {}, callback)
518      // buffer will be the options object
519      // offset is the callback
520      options = buffer;
521      callback = offset;
522    }
523
524    ({
525      buffer = Buffer.alloc(16384),
526      offset = 0,
527      length = buffer.byteLength,
528      position
529    } = options);
530  }
531
532  validateBuffer(buffer);
533  callback = maybeCallback(callback);
534
535  if (offset == null) {
536    offset = 0;
537  } else {
538    validateInteger(offset, 'offset', 0);
539  }
540
541  length |= 0;
542
543  if (length === 0) {
544    return process.nextTick(function tick() {
545      callback(null, 0, buffer);
546    });
547  }
548
549  if (buffer.byteLength === 0) {
550    throw new ERR_INVALID_ARG_VALUE('buffer', buffer,
551                                    'is empty and cannot be written');
552  }
553
554  validateOffsetLengthRead(offset, length, buffer.byteLength);
555
556  if (position == null)
557    position = -1;
558
559  validatePosition(position, 'position');
560
561  function wrapper(err, bytesRead) {
562    // Retain a reference to buffer so that it can't be GC'ed too soon.
563    callback(err, bytesRead || 0, buffer);
564  }
565
566  const req = new FSReqCallback();
567  req.oncomplete = wrapper;
568
569  binding.read(fd, buffer, offset, length, position, req);
570}
571
572ObjectDefineProperty(read, internalUtil.customPromisifyArgs,
573                     { value: ['bytesRead', 'buffer'], enumerable: false });
574
575// usage:
576// fs.readSync(fd, buffer, offset, length, position);
577// OR
578// fs.readSync(fd, buffer, {}) or fs.readSync(fd, buffer)
579function readSync(fd, buffer, offset, length, position) {
580  validateInt32(fd, 'fd', 0);
581
582  validateBuffer(buffer);
583
584  if (arguments.length <= 3) {
585    // Assume fs.read(fd, buffer, options)
586    const options = offset || {};
587
588    ({ offset = 0, length = buffer.byteLength, position } = options);
589  }
590
591  if (offset == null) {
592    offset = 0;
593  } else {
594    validateInteger(offset, 'offset', 0);
595  }
596
597  length |= 0;
598
599  if (length === 0) {
600    return 0;
601  }
602
603  if (buffer.byteLength === 0) {
604    throw new ERR_INVALID_ARG_VALUE('buffer', buffer,
605                                    'is empty and cannot be written');
606  }
607
608  validateOffsetLengthRead(offset, length, buffer.byteLength);
609
610  if (position == null)
611    position = -1;
612
613  validatePosition(position, 'position');
614
615  const ctx = {};
616  const result = binding.read(fd, buffer, offset, length, position,
617                              undefined, ctx);
618  handleErrorFromBinding(ctx);
619  return result;
620}
621
622function readv(fd, buffers, position, callback) {
623  function wrapper(err, read) {
624    callback(err, read || 0, buffers);
625  }
626
627  validateInt32(fd, 'fd', /* min */ 0);
628  validateBufferArray(buffers);
629  callback = maybeCallback(callback || position);
630
631  const req = new FSReqCallback();
632  req.oncomplete = wrapper;
633
634  if (typeof position !== 'number')
635    position = null;
636
637  return binding.readBuffers(fd, buffers, position, req);
638}
639
640ObjectDefineProperty(readv, internalUtil.customPromisifyArgs,
641                     { value: ['bytesRead', 'buffers'], enumerable: false });
642
643function readvSync(fd, buffers, position) {
644  validateInt32(fd, 'fd', 0);
645  validateBufferArray(buffers);
646
647  const ctx = {};
648
649  if (typeof position !== 'number')
650    position = null;
651
652  const result = binding.readBuffers(fd, buffers, position, undefined, ctx);
653  handleErrorFromBinding(ctx);
654  return result;
655}
656
657// usage:
658//  fs.write(fd, buffer[, offset[, length[, position]]], callback);
659// OR
660//  fs.write(fd, string[, position[, encoding]], callback);
661function write(fd, buffer, offset, length, position, callback) {
662  function wrapper(err, written) {
663    // Retain a reference to buffer so that it can't be GC'ed too soon.
664    callback(err, written || 0, buffer);
665  }
666
667  validateInt32(fd, 'fd', 0);
668
669  if (isArrayBufferView(buffer)) {
670    callback = maybeCallback(callback || position || length || offset);
671    if (offset == null || typeof offset === 'function') {
672      offset = 0;
673    } else {
674      validateInteger(offset, 'offset', 0);
675    }
676    if (typeof length !== 'number')
677      length = buffer.byteLength - offset;
678    if (typeof position !== 'number')
679      position = null;
680    validateOffsetLengthWrite(offset, length, buffer.byteLength);
681
682    const req = new FSReqCallback();
683    req.oncomplete = wrapper;
684    return binding.writeBuffer(fd, buffer, offset, length, position, req);
685  }
686
687  validateStringAfterArrayBufferView(buffer, 'buffer');
688
689  if (typeof position !== 'function') {
690    if (typeof offset === 'function') {
691      position = offset;
692      offset = null;
693    } else {
694      position = length;
695    }
696    length = 'utf8';
697  }
698  callback = maybeCallback(position);
699
700  const req = new FSReqCallback();
701  req.oncomplete = wrapper;
702  return binding.writeString(fd, String(buffer), offset, length, req);
703}
704
705ObjectDefineProperty(write, internalUtil.customPromisifyArgs,
706                     { value: ['bytesWritten', 'buffer'], enumerable: false });
707
708// Usage:
709//  fs.writeSync(fd, buffer[, offset[, length[, position]]]);
710// OR
711//  fs.writeSync(fd, string[, position[, encoding]]);
712function writeSync(fd, buffer, offset, length, position) {
713  validateInt32(fd, 'fd', 0);
714  const ctx = {};
715  let result;
716  if (isArrayBufferView(buffer)) {
717    if (position === undefined)
718      position = null;
719    if (offset == null) {
720      offset = 0;
721    } else {
722      validateInteger(offset, 'offset', 0);
723    }
724    if (typeof length !== 'number')
725      length = buffer.byteLength - offset;
726    validateOffsetLengthWrite(offset, length, buffer.byteLength);
727    result = binding.writeBuffer(fd, buffer, offset, length, position,
728                                 undefined, ctx);
729  } else {
730    validatePrimitiveStringAfterArrayBufferView(buffer, 'buffer');
731
732    if (offset === undefined)
733      offset = null;
734    result = binding.writeString(fd, buffer, offset, length,
735                                 undefined, ctx);
736  }
737  handleErrorFromBinding(ctx);
738  return result;
739}
740
741// usage:
742// fs.writev(fd, buffers[, position], callback);
743function writev(fd, buffers, position, callback) {
744  function wrapper(err, written) {
745    callback(err, written || 0, buffers);
746  }
747
748  validateInt32(fd, 'fd', 0);
749  validateBufferArray(buffers);
750  callback = maybeCallback(callback || position);
751
752  const req = new FSReqCallback();
753  req.oncomplete = wrapper;
754
755  if (typeof position !== 'number')
756    position = null;
757
758  return binding.writeBuffers(fd, buffers, position, req);
759}
760
761ObjectDefineProperty(writev, internalUtil.customPromisifyArgs, {
762  value: ['bytesWritten', 'buffer'],
763  enumerable: false
764});
765
766function writevSync(fd, buffers, position) {
767  validateInt32(fd, 'fd', 0);
768  validateBufferArray(buffers);
769
770  const ctx = {};
771
772  if (typeof position !== 'number')
773    position = null;
774
775  const result = binding.writeBuffers(fd, buffers, position, undefined, ctx);
776
777  handleErrorFromBinding(ctx);
778  return result;
779}
780
781function rename(oldPath, newPath, callback) {
782  callback = makeCallback(callback);
783  oldPath = getValidatedPath(oldPath, 'oldPath');
784  newPath = getValidatedPath(newPath, 'newPath');
785  const req = new FSReqCallback();
786  req.oncomplete = callback;
787  binding.rename(pathModule.toNamespacedPath(oldPath),
788                 pathModule.toNamespacedPath(newPath),
789                 req);
790}
791
792function renameSync(oldPath, newPath) {
793  oldPath = getValidatedPath(oldPath, 'oldPath');
794  newPath = getValidatedPath(newPath, 'newPath');
795  const ctx = { path: oldPath, dest: newPath };
796  binding.rename(pathModule.toNamespacedPath(oldPath),
797                 pathModule.toNamespacedPath(newPath), undefined, ctx);
798  handleErrorFromBinding(ctx);
799}
800
801function truncate(path, len, callback) {
802  if (typeof path === 'number') {
803    showTruncateDeprecation();
804    return fs.ftruncate(path, len, callback);
805  }
806  if (typeof len === 'function') {
807    callback = len;
808    len = 0;
809  } else if (len === undefined) {
810    len = 0;
811  }
812
813  validateInteger(len, 'len');
814  len = MathMax(0, len);
815  callback = maybeCallback(callback);
816  fs.open(path, 'r+', (er, fd) => {
817    if (er) return callback(er);
818    const req = new FSReqCallback();
819    req.oncomplete = function oncomplete(er) {
820      fs.close(fd, (er2) => {
821        callback(er || er2);
822      });
823    };
824    binding.ftruncate(fd, len, req);
825  });
826}
827
828function truncateSync(path, len) {
829  if (typeof path === 'number') {
830    // legacy
831    showTruncateDeprecation();
832    return fs.ftruncateSync(path, len);
833  }
834  if (len === undefined) {
835    len = 0;
836  }
837  // Allow error to be thrown, but still close fd.
838  const fd = fs.openSync(path, 'r+');
839  let ret;
840
841  try {
842    ret = fs.ftruncateSync(fd, len);
843  } finally {
844    fs.closeSync(fd);
845  }
846  return ret;
847}
848
849function ftruncate(fd, len = 0, callback) {
850  if (typeof len === 'function') {
851    callback = len;
852    len = 0;
853  }
854  validateInt32(fd, 'fd', 0);
855  validateInteger(len, 'len');
856  len = MathMax(0, len);
857  callback = makeCallback(callback);
858
859  const req = new FSReqCallback();
860  req.oncomplete = callback;
861  binding.ftruncate(fd, len, req);
862}
863
864function ftruncateSync(fd, len = 0) {
865  validateInt32(fd, 'fd', 0);
866  validateInteger(len, 'len');
867  len = MathMax(0, len);
868  const ctx = {};
869  binding.ftruncate(fd, len, undefined, ctx);
870  handleErrorFromBinding(ctx);
871}
872
873
874function lazyLoadRimraf() {
875  if (rimraf === undefined)
876    ({ rimraf, rimrafSync } = require('internal/fs/rimraf'));
877}
878
879function rmdir(path, options, callback) {
880  if (typeof options === 'function') {
881    callback = options;
882    options = undefined;
883  }
884
885  callback = makeCallback(callback);
886  path = pathModule.toNamespacedPath(getValidatedPath(path));
887
888  if (options && options.recursive) {
889    validateRmOptions(path, { ...options, force: true }, (err, options) => {
890      if (err) {
891        return callback(err);
892      }
893
894      lazyLoadRimraf();
895      return rimraf(path, options, callback);
896    });
897  } else {
898    validateRmdirOptions(options);
899    const req = new FSReqCallback();
900    req.oncomplete = callback;
901    return binding.rmdir(path, req);
902  }
903}
904
905function rmdirSync(path, options) {
906  path = getValidatedPath(path);
907
908  if (options && options.recursive) {
909    options = validateRmOptionsSync(path, { ...options, force: true });
910    lazyLoadRimraf();
911    return rimrafSync(pathModule.toNamespacedPath(path), options);
912  }
913
914  validateRmdirOptions(options);
915  const ctx = { path };
916  binding.rmdir(pathModule.toNamespacedPath(path), undefined, ctx);
917  return handleErrorFromBinding(ctx);
918}
919
920function rm(path, options, callback) {
921  if (typeof options === 'function') {
922    callback = options;
923    options = undefined;
924  }
925
926  validateRmOptions(path, options, (err, options) => {
927    if (err) {
928      return callback(err);
929    }
930    lazyLoadRimraf();
931    return rimraf(pathModule.toNamespacedPath(path), options, callback);
932  });
933}
934
935function rmSync(path, options) {
936  options = validateRmOptionsSync(path, options);
937
938  lazyLoadRimraf();
939  return rimrafSync(pathModule.toNamespacedPath(path), options);
940}
941
942function fdatasync(fd, callback) {
943  validateInt32(fd, 'fd', 0);
944  const req = new FSReqCallback();
945  req.oncomplete = makeCallback(callback);
946  binding.fdatasync(fd, req);
947}
948
949function fdatasyncSync(fd) {
950  validateInt32(fd, 'fd', 0);
951  const ctx = {};
952  binding.fdatasync(fd, undefined, ctx);
953  handleErrorFromBinding(ctx);
954}
955
956function fsync(fd, callback) {
957  validateInt32(fd, 'fd', 0);
958  const req = new FSReqCallback();
959  req.oncomplete = makeCallback(callback);
960  binding.fsync(fd, req);
961}
962
963function fsyncSync(fd) {
964  validateInt32(fd, 'fd', 0);
965  const ctx = {};
966  binding.fsync(fd, undefined, ctx);
967  handleErrorFromBinding(ctx);
968}
969
970function mkdir(path, options, callback) {
971  let mode = 0o777;
972  let recursive = false;
973  if (typeof options === 'function') {
974    callback = options;
975  } else if (typeof options === 'number' || typeof options === 'string') {
976    mode = options;
977  } else if (options) {
978    if (options.recursive !== undefined)
979      recursive = options.recursive;
980    if (options.mode !== undefined)
981      mode = options.mode;
982  }
983  callback = makeCallback(callback);
984  path = getValidatedPath(path);
985
986  if (typeof recursive !== 'boolean')
987    throw new ERR_INVALID_ARG_TYPE('options.recursive', 'boolean', recursive);
988
989  const req = new FSReqCallback();
990  req.oncomplete = callback;
991  binding.mkdir(pathModule.toNamespacedPath(path),
992                parseFileMode(mode, 'mode'), recursive, req);
993}
994
995function mkdirSync(path, options) {
996  let mode = 0o777;
997  let recursive = false;
998  if (typeof options === 'number' || typeof options === 'string') {
999    mode = options;
1000  } else if (options) {
1001    if (options.recursive !== undefined)
1002      recursive = options.recursive;
1003    if (options.mode !== undefined)
1004      mode = options.mode;
1005  }
1006  path = getValidatedPath(path);
1007  if (typeof recursive !== 'boolean')
1008    throw new ERR_INVALID_ARG_TYPE('options.recursive', 'boolean', recursive);
1009
1010  const ctx = { path };
1011  const result = binding.mkdir(pathModule.toNamespacedPath(path),
1012                               parseFileMode(mode, 'mode'), recursive,
1013                               undefined, ctx);
1014  handleErrorFromBinding(ctx);
1015  if (recursive) {
1016    return result;
1017  }
1018}
1019
1020function readdir(path, options, callback) {
1021  callback = makeCallback(typeof options === 'function' ? options : callback);
1022  options = getOptions(options, {});
1023  path = getValidatedPath(path);
1024
1025  const req = new FSReqCallback();
1026  if (!options.withFileTypes) {
1027    req.oncomplete = callback;
1028  } else {
1029    req.oncomplete = (err, result) => {
1030      if (err) {
1031        callback(err);
1032        return;
1033      }
1034      getDirents(path, result, callback);
1035    };
1036  }
1037  binding.readdir(pathModule.toNamespacedPath(path), options.encoding,
1038                  !!options.withFileTypes, req);
1039}
1040
1041function readdirSync(path, options) {
1042  options = getOptions(options, {});
1043  path = getValidatedPath(path);
1044  const ctx = { path };
1045  const result = binding.readdir(pathModule.toNamespacedPath(path),
1046                                 options.encoding, !!options.withFileTypes,
1047                                 undefined, ctx);
1048  handleErrorFromBinding(ctx);
1049  return options.withFileTypes ? getDirents(path, result) : result;
1050}
1051
1052function fstat(fd, options = { bigint: false }, callback) {
1053  if (typeof options === 'function') {
1054    callback = options;
1055    options = {};
1056  }
1057  validateInt32(fd, 'fd', 0);
1058  callback = makeStatsCallback(callback);
1059
1060  const req = new FSReqCallback(options.bigint);
1061  req.oncomplete = callback;
1062  binding.fstat(fd, options.bigint, req);
1063}
1064
1065function lstat(path, options = { bigint: false }, callback) {
1066  if (typeof options === 'function') {
1067    callback = options;
1068    options = {};
1069  }
1070  callback = makeStatsCallback(callback);
1071  path = getValidatedPath(path);
1072
1073  const req = new FSReqCallback(options.bigint);
1074  req.oncomplete = callback;
1075  binding.lstat(pathModule.toNamespacedPath(path), options.bigint, req);
1076}
1077
1078function stat(path, options = { bigint: false }, callback) {
1079  if (typeof options === 'function') {
1080    callback = options;
1081    options = {};
1082  }
1083  callback = makeStatsCallback(callback);
1084  path = getValidatedPath(path);
1085
1086  const req = new FSReqCallback(options.bigint);
1087  req.oncomplete = callback;
1088  binding.stat(pathModule.toNamespacedPath(path), options.bigint, req);
1089}
1090
1091function hasNoEntryError(ctx) {
1092  if (ctx.errno) {
1093    const uvErr = uvErrmapGet(ctx.errno);
1094    return uvErr && uvErr[0] === 'ENOENT';
1095  }
1096
1097  if (ctx.error) {
1098    return ctx.error.code === 'ENOENT';
1099  }
1100
1101  return false;
1102}
1103
1104function fstatSync(fd, options = { bigint: false, throwIfNoEntry: true }) {
1105  validateInt32(fd, 'fd', 0);
1106  const ctx = { fd };
1107  const stats = binding.fstat(fd, options.bigint, undefined, ctx);
1108  handleErrorFromBinding(ctx);
1109  return getStatsFromBinding(stats);
1110}
1111
1112function lstatSync(path, options = { bigint: false, throwIfNoEntry: true }) {
1113  path = getValidatedPath(path);
1114  const ctx = { path };
1115  const stats = binding.lstat(pathModule.toNamespacedPath(path),
1116                              options.bigint, undefined, ctx);
1117  if (options.throwIfNoEntry === false && hasNoEntryError(ctx)) {
1118    return undefined;
1119  }
1120  handleErrorFromBinding(ctx);
1121  return getStatsFromBinding(stats);
1122}
1123
1124function statSync(path, options = { bigint: false, throwIfNoEntry: true }) {
1125  path = getValidatedPath(path);
1126  const ctx = { path };
1127  const stats = binding.stat(pathModule.toNamespacedPath(path),
1128                             options.bigint, undefined, ctx);
1129  if (options.throwIfNoEntry === false && hasNoEntryError(ctx)) {
1130    return undefined;
1131  }
1132  handleErrorFromBinding(ctx);
1133  return getStatsFromBinding(stats);
1134}
1135
1136function readlink(path, options, callback) {
1137  callback = makeCallback(typeof options === 'function' ? options : callback);
1138  options = getOptions(options, {});
1139  path = getValidatedPath(path, 'oldPath');
1140  const req = new FSReqCallback();
1141  req.oncomplete = callback;
1142  binding.readlink(pathModule.toNamespacedPath(path), options.encoding, req);
1143}
1144
1145function readlinkSync(path, options) {
1146  options = getOptions(options, {});
1147  path = getValidatedPath(path, 'oldPath');
1148  const ctx = { path };
1149  const result = binding.readlink(pathModule.toNamespacedPath(path),
1150                                  options.encoding, undefined, ctx);
1151  handleErrorFromBinding(ctx);
1152  return result;
1153}
1154
1155function symlink(target, path, type_, callback_) {
1156  const type = (typeof type_ === 'string' ? type_ : null);
1157  const callback = makeCallback(arguments[arguments.length - 1]);
1158
1159  target = getValidatedPath(target, 'target');
1160  path = getValidatedPath(path);
1161
1162  if (isWindows && type === null) {
1163    let absoluteTarget;
1164    try {
1165      // Symlinks targets can be relative to the newly created path.
1166      // Calculate absolute file name of the symlink target, and check
1167      // if it is a directory. Ignore resolve error to keep symlink
1168      // errors consistent between platforms if invalid path is
1169      // provided.
1170      absoluteTarget = pathModule.resolve(path, '..', target);
1171    } catch { }
1172    if (absoluteTarget !== undefined) {
1173      stat(absoluteTarget, (err, stat) => {
1174        const resolvedType = !err && stat.isDirectory() ? 'dir' : 'file';
1175        const resolvedFlags = stringToSymlinkType(resolvedType);
1176        const destination = preprocessSymlinkDestination(target,
1177                                                         resolvedType,
1178                                                         path);
1179
1180        const req = new FSReqCallback();
1181        req.oncomplete = callback;
1182        binding.symlink(destination,
1183                        pathModule.toNamespacedPath(path), resolvedFlags, req);
1184      });
1185      return;
1186    }
1187  }
1188
1189  const destination = preprocessSymlinkDestination(target, type, path);
1190
1191  const flags = stringToSymlinkType(type);
1192  const req = new FSReqCallback();
1193  req.oncomplete = callback;
1194  binding.symlink(destination, pathModule.toNamespacedPath(path), flags, req);
1195}
1196
1197function symlinkSync(target, path, type) {
1198  type = (typeof type === 'string' ? type : null);
1199  if (isWindows && type === null) {
1200    try {
1201      const absoluteTarget = pathModule.resolve(path, '..', target);
1202      if (statSync(absoluteTarget).isDirectory()) {
1203        type = 'dir';
1204      }
1205    } catch { }
1206  }
1207  target = getValidatedPath(target, 'target');
1208  path = getValidatedPath(path);
1209  const flags = stringToSymlinkType(type);
1210
1211  const ctx = { path: target, dest: path };
1212  binding.symlink(preprocessSymlinkDestination(target, type, path),
1213                  pathModule.toNamespacedPath(path), flags, undefined, ctx);
1214
1215  handleErrorFromBinding(ctx);
1216}
1217
1218function link(existingPath, newPath, callback) {
1219  callback = makeCallback(callback);
1220
1221  existingPath = getValidatedPath(existingPath, 'existingPath');
1222  newPath = getValidatedPath(newPath, 'newPath');
1223
1224  const req = new FSReqCallback();
1225  req.oncomplete = callback;
1226
1227  binding.link(pathModule.toNamespacedPath(existingPath),
1228               pathModule.toNamespacedPath(newPath),
1229               req);
1230}
1231
1232function linkSync(existingPath, newPath) {
1233  existingPath = getValidatedPath(existingPath, 'existingPath');
1234  newPath = getValidatedPath(newPath, 'newPath');
1235
1236  const ctx = { path: existingPath, dest: newPath };
1237  const result = binding.link(pathModule.toNamespacedPath(existingPath),
1238                              pathModule.toNamespacedPath(newPath),
1239                              undefined, ctx);
1240  handleErrorFromBinding(ctx);
1241  return result;
1242}
1243
1244function unlink(path, callback) {
1245  callback = makeCallback(callback);
1246  path = getValidatedPath(path);
1247  const req = new FSReqCallback();
1248  req.oncomplete = callback;
1249  binding.unlink(pathModule.toNamespacedPath(path), req);
1250}
1251
1252function unlinkSync(path) {
1253  path = getValidatedPath(path);
1254  const ctx = { path };
1255  binding.unlink(pathModule.toNamespacedPath(path), undefined, ctx);
1256  handleErrorFromBinding(ctx);
1257}
1258
1259function fchmod(fd, mode, callback) {
1260  validateInt32(fd, 'fd', 0);
1261  mode = parseFileMode(mode, 'mode');
1262  callback = makeCallback(callback);
1263
1264  const req = new FSReqCallback();
1265  req.oncomplete = callback;
1266  binding.fchmod(fd, mode, req);
1267}
1268
1269function fchmodSync(fd, mode) {
1270  validateInt32(fd, 'fd', 0);
1271  mode = parseFileMode(mode, 'mode');
1272  const ctx = {};
1273  binding.fchmod(fd, mode, undefined, ctx);
1274  handleErrorFromBinding(ctx);
1275}
1276
1277function lchmod(path, mode, callback) {
1278  callback = maybeCallback(callback);
1279  fs.open(path, O_WRONLY | O_SYMLINK, (err, fd) => {
1280    if (err) {
1281      callback(err);
1282      return;
1283    }
1284    // Prefer to return the chmod error, if one occurs,
1285    // but still try to close, and report closing errors if they occur.
1286    fs.fchmod(fd, mode, (err) => {
1287      fs.close(fd, (err2) => {
1288        callback(err || err2);
1289      });
1290    });
1291  });
1292}
1293
1294function lchmodSync(path, mode) {
1295  const fd = fs.openSync(path, O_WRONLY | O_SYMLINK);
1296
1297  // Prefer to return the chmod error, if one occurs,
1298  // but still try to close, and report closing errors if they occur.
1299  let ret;
1300  try {
1301    ret = fs.fchmodSync(fd, mode);
1302  } finally {
1303    fs.closeSync(fd);
1304  }
1305  return ret;
1306}
1307
1308
1309function chmod(path, mode, callback) {
1310  path = getValidatedPath(path);
1311  mode = parseFileMode(mode, 'mode');
1312  callback = makeCallback(callback);
1313
1314  const req = new FSReqCallback();
1315  req.oncomplete = callback;
1316  binding.chmod(pathModule.toNamespacedPath(path), mode, req);
1317}
1318
1319function chmodSync(path, mode) {
1320  path = getValidatedPath(path);
1321  mode = parseFileMode(mode, 'mode');
1322
1323  const ctx = { path };
1324  binding.chmod(pathModule.toNamespacedPath(path), mode, undefined, ctx);
1325  handleErrorFromBinding(ctx);
1326}
1327
1328function lchown(path, uid, gid, callback) {
1329  callback = makeCallback(callback);
1330  path = getValidatedPath(path);
1331  validateInteger(uid, 'uid', -1, kMaxUserId);
1332  validateInteger(gid, 'gid', -1, kMaxUserId);
1333  const req = new FSReqCallback();
1334  req.oncomplete = callback;
1335  binding.lchown(pathModule.toNamespacedPath(path), uid, gid, req);
1336}
1337
1338function lchownSync(path, uid, gid) {
1339  path = getValidatedPath(path);
1340  validateInteger(uid, 'uid', -1, kMaxUserId);
1341  validateInteger(gid, 'gid', -1, kMaxUserId);
1342  const ctx = { path };
1343  binding.lchown(pathModule.toNamespacedPath(path), uid, gid, undefined, ctx);
1344  handleErrorFromBinding(ctx);
1345}
1346
1347function fchown(fd, uid, gid, callback) {
1348  validateInt32(fd, 'fd', 0);
1349  validateInteger(uid, 'uid', -1, kMaxUserId);
1350  validateInteger(gid, 'gid', -1, kMaxUserId);
1351  callback = makeCallback(callback);
1352
1353  const req = new FSReqCallback();
1354  req.oncomplete = callback;
1355  binding.fchown(fd, uid, gid, req);
1356}
1357
1358function fchownSync(fd, uid, gid) {
1359  validateInt32(fd, 'fd', 0);
1360  validateInteger(uid, 'uid', -1, kMaxUserId);
1361  validateInteger(gid, 'gid', -1, kMaxUserId);
1362
1363  const ctx = {};
1364  binding.fchown(fd, uid, gid, undefined, ctx);
1365  handleErrorFromBinding(ctx);
1366}
1367
1368function chown(path, uid, gid, callback) {
1369  callback = makeCallback(callback);
1370  path = getValidatedPath(path);
1371  validateInteger(uid, 'uid', -1, kMaxUserId);
1372  validateInteger(gid, 'gid', -1, kMaxUserId);
1373
1374  const req = new FSReqCallback();
1375  req.oncomplete = callback;
1376  binding.chown(pathModule.toNamespacedPath(path), uid, gid, req);
1377}
1378
1379function chownSync(path, uid, gid) {
1380  path = getValidatedPath(path);
1381  validateInteger(uid, 'uid', -1, kMaxUserId);
1382  validateInteger(gid, 'gid', -1, kMaxUserId);
1383  const ctx = { path };
1384  binding.chown(pathModule.toNamespacedPath(path), uid, gid, undefined, ctx);
1385  handleErrorFromBinding(ctx);
1386}
1387
1388function utimes(path, atime, mtime, callback) {
1389  callback = makeCallback(callback);
1390  path = getValidatedPath(path);
1391
1392  const req = new FSReqCallback();
1393  req.oncomplete = callback;
1394  binding.utimes(pathModule.toNamespacedPath(path),
1395                 toUnixTimestamp(atime),
1396                 toUnixTimestamp(mtime),
1397                 req);
1398}
1399
1400function utimesSync(path, atime, mtime) {
1401  path = getValidatedPath(path);
1402  const ctx = { path };
1403  binding.utimes(pathModule.toNamespacedPath(path),
1404                 toUnixTimestamp(atime), toUnixTimestamp(mtime),
1405                 undefined, ctx);
1406  handleErrorFromBinding(ctx);
1407}
1408
1409function futimes(fd, atime, mtime, callback) {
1410  validateInt32(fd, 'fd', 0);
1411  atime = toUnixTimestamp(atime, 'atime');
1412  mtime = toUnixTimestamp(mtime, 'mtime');
1413  callback = makeCallback(callback);
1414
1415  const req = new FSReqCallback();
1416  req.oncomplete = callback;
1417  binding.futimes(fd, atime, mtime, req);
1418}
1419
1420function futimesSync(fd, atime, mtime) {
1421  validateInt32(fd, 'fd', 0);
1422  atime = toUnixTimestamp(atime, 'atime');
1423  mtime = toUnixTimestamp(mtime, 'mtime');
1424  const ctx = {};
1425  binding.futimes(fd, atime, mtime, undefined, ctx);
1426  handleErrorFromBinding(ctx);
1427}
1428
1429function lutimes(path, atime, mtime, callback) {
1430  callback = makeCallback(callback);
1431  path = getValidatedPath(path);
1432
1433  const req = new FSReqCallback();
1434  req.oncomplete = callback;
1435  binding.lutimes(pathModule.toNamespacedPath(path),
1436                  toUnixTimestamp(atime),
1437                  toUnixTimestamp(mtime),
1438                  req);
1439}
1440
1441function lutimesSync(path, atime, mtime) {
1442  path = getValidatedPath(path);
1443  const ctx = { path };
1444  binding.lutimes(pathModule.toNamespacedPath(path),
1445                  toUnixTimestamp(atime),
1446                  toUnixTimestamp(mtime),
1447                  undefined, ctx);
1448  handleErrorFromBinding(ctx);
1449}
1450
1451function writeAll(fd, isUserFd, buffer, offset, length, signal, callback) {
1452  if (signal?.aborted) {
1453    if (isUserFd) {
1454      callback(lazyDOMException('The operation was aborted', 'AbortError'));
1455    } else {
1456      fs.close(fd, function() {
1457        callback(lazyDOMException('The operation was aborted', 'AbortError'));
1458      });
1459    }
1460    return;
1461  }
1462  // write(fd, buffer, offset, length, position, callback)
1463  fs.write(fd, buffer, offset, length, null, (writeErr, written) => {
1464    if (writeErr) {
1465      if (isUserFd) {
1466        callback(writeErr);
1467      } else {
1468        fs.close(fd, function close() {
1469          callback(writeErr);
1470        });
1471      }
1472    } else if (written === length) {
1473      if (isUserFd) {
1474        callback(null);
1475      } else {
1476        fs.close(fd, callback);
1477      }
1478    } else {
1479      offset += written;
1480      length -= written;
1481      writeAll(fd, isUserFd, buffer, offset, length, signal, callback);
1482    }
1483  });
1484}
1485
1486function writeFile(path, data, options, callback) {
1487  callback = maybeCallback(callback || options);
1488  options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'w' });
1489  const flag = options.flag || 'w';
1490
1491  if (!isArrayBufferView(data)) {
1492    validateStringAfterArrayBufferView(data, 'data');
1493    data = Buffer.from(String(data), options.encoding || 'utf8');
1494  }
1495
1496  if (isFd(path)) {
1497    const isUserFd = true;
1498    const signal = options.signal;
1499    writeAll(path, isUserFd, data, 0, data.byteLength, signal, callback);
1500    return;
1501  }
1502
1503  if (options.signal?.aborted) {
1504    callback(lazyDOMException('The operation was aborted', 'AbortError'));
1505    return;
1506  }
1507  fs.open(path, flag, options.mode, (openErr, fd) => {
1508    if (openErr) {
1509      callback(openErr);
1510    } else {
1511      const isUserFd = false;
1512      const signal = options.signal;
1513      writeAll(fd, isUserFd, data, 0, data.byteLength, signal, callback);
1514    }
1515  });
1516}
1517
1518function writeFileSync(path, data, options) {
1519  options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'w' });
1520
1521  if (!isArrayBufferView(data)) {
1522    validateStringAfterArrayBufferView(data, 'data');
1523    data = Buffer.from(String(data), options.encoding || 'utf8');
1524  }
1525
1526  const flag = options.flag || 'w';
1527
1528  const isUserFd = isFd(path); // File descriptor ownership
1529  const fd = isUserFd ? path : fs.openSync(path, flag, options.mode);
1530
1531  let offset = 0;
1532  let length = data.byteLength;
1533  try {
1534    while (length > 0) {
1535      const written = fs.writeSync(fd, data, offset, length);
1536      offset += written;
1537      length -= written;
1538    }
1539  } finally {
1540    if (!isUserFd) fs.closeSync(fd);
1541  }
1542}
1543
1544function appendFile(path, data, options, callback) {
1545  callback = maybeCallback(callback || options);
1546  options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'a' });
1547
1548  // Don't make changes directly on options object
1549  options = copyObject(options);
1550
1551  // Force append behavior when using a supplied file descriptor
1552  if (!options.flag || isFd(path))
1553    options.flag = 'a';
1554
1555  fs.writeFile(path, data, options, callback);
1556}
1557
1558function appendFileSync(path, data, options) {
1559  options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'a' });
1560
1561  // Don't make changes directly on options object
1562  options = copyObject(options);
1563
1564  // Force append behavior when using a supplied file descriptor
1565  if (!options.flag || isFd(path))
1566    options.flag = 'a';
1567
1568  fs.writeFileSync(path, data, options);
1569}
1570
1571function watch(filename, options, listener) {
1572  if (typeof options === 'function') {
1573    listener = options;
1574  }
1575  options = getOptions(options, {});
1576
1577  // Don't make changes directly on options object
1578  options = copyObject(options);
1579
1580  if (options.persistent === undefined) options.persistent = true;
1581  if (options.recursive === undefined) options.recursive = false;
1582  if (options.recursive && !(isOSX || isWindows))
1583    throw new ERR_FEATURE_UNAVAILABLE_ON_PLATFORM('watch recursively');
1584  if (!watchers)
1585    watchers = require('internal/fs/watchers');
1586  const watcher = new watchers.FSWatcher();
1587  watcher[watchers.kFSWatchStart](filename,
1588                                  options.persistent,
1589                                  options.recursive,
1590                                  options.encoding);
1591
1592  if (listener) {
1593    watcher.addListener('change', listener);
1594  }
1595  if (options.signal) {
1596    if (options.signal.aborted) {
1597      process.nextTick(() => watcher.close());
1598    } else {
1599      const listener = () => watcher.close();
1600      options.signal.addEventListener('abort', listener);
1601      watcher.once('close', () => {
1602        options.signal.removeEventListener('abort', listener);
1603      });
1604    }
1605  }
1606
1607  return watcher;
1608}
1609
1610
1611const statWatchers = new Map();
1612
1613function watchFile(filename, options, listener) {
1614  filename = getValidatedPath(filename);
1615  filename = pathModule.resolve(filename);
1616  let stat;
1617
1618  if (options === null || typeof options !== 'object') {
1619    listener = options;
1620    options = null;
1621  }
1622
1623  options = {
1624    // Poll interval in milliseconds. 5007 is what libev used to use. It's
1625    // a little on the slow side but let's stick with it for now to keep
1626    // behavioral changes to a minimum.
1627    interval: 5007,
1628    persistent: true,
1629    ...options
1630  };
1631
1632  if (typeof listener !== 'function') {
1633    throw new ERR_INVALID_ARG_TYPE('listener', 'Function', listener);
1634  }
1635
1636  stat = statWatchers.get(filename);
1637
1638  if (stat === undefined) {
1639    if (!watchers)
1640      watchers = require('internal/fs/watchers');
1641    stat = new watchers.StatWatcher(options.bigint);
1642    stat[watchers.kFSStatWatcherStart](filename,
1643                                       options.persistent, options.interval);
1644    statWatchers.set(filename, stat);
1645  } else {
1646    stat[watchers.kFSStatWatcherAddOrCleanRef]('add');
1647  }
1648
1649  stat.addListener('change', listener);
1650  return stat;
1651}
1652
1653function unwatchFile(filename, listener) {
1654  filename = getValidatedPath(filename);
1655  filename = pathModule.resolve(filename);
1656  const stat = statWatchers.get(filename);
1657
1658  if (stat === undefined) return;
1659
1660  if (typeof listener === 'function') {
1661    const beforeListenerCount = stat.listenerCount('change');
1662    stat.removeListener('change', listener);
1663    if (stat.listenerCount('change') < beforeListenerCount)
1664      stat[watchers.kFSStatWatcherAddOrCleanRef]('clean');
1665  } else {
1666    stat.removeAllListeners('change');
1667    stat[watchers.kFSStatWatcherAddOrCleanRef]('cleanAll');
1668  }
1669
1670  if (stat.listenerCount('change') === 0) {
1671    stat.stop();
1672    statWatchers.delete(filename);
1673  }
1674}
1675
1676
1677let splitRoot;
1678if (isWindows) {
1679  // Regex to find the device root on Windows (e.g. 'c:\\'), including trailing
1680  // slash.
1681  const splitRootRe = /^(?:[a-zA-Z]:|[\\/]{2}[^\\/]+[\\/][^\\/]+)?[\\/]*/;
1682  splitRoot = function splitRoot(str) {
1683    return splitRootRe.exec(str)[0];
1684  };
1685} else {
1686  splitRoot = function splitRoot(str) {
1687    for (let i = 0; i < str.length; ++i) {
1688      if (str.charCodeAt(i) !== CHAR_FORWARD_SLASH)
1689        return str.slice(0, i);
1690    }
1691    return str;
1692  };
1693}
1694
1695function encodeRealpathResult(result, options) {
1696  if (!options || !options.encoding || options.encoding === 'utf8')
1697    return result;
1698  const asBuffer = Buffer.from(result);
1699  if (options.encoding === 'buffer') {
1700    return asBuffer;
1701  }
1702  return asBuffer.toString(options.encoding);
1703}
1704
1705// Finds the next portion of a (partial) path, up to the next path delimiter
1706let nextPart;
1707if (isWindows) {
1708  nextPart = function nextPart(p, i) {
1709    for (; i < p.length; ++i) {
1710      const ch = p.charCodeAt(i);
1711
1712      // Check for a separator character
1713      if (ch === CHAR_BACKWARD_SLASH || ch === CHAR_FORWARD_SLASH)
1714        return i;
1715    }
1716    return -1;
1717  };
1718} else {
1719  nextPart = function nextPart(p, i) { return p.indexOf('/', i); };
1720}
1721
1722const emptyObj = ObjectCreate(null);
1723function realpathSync(p, options) {
1724  options = getOptions(options, emptyObj);
1725  p = toPathIfFileURL(p);
1726  if (typeof p !== 'string') {
1727    p += '';
1728  }
1729  validatePath(p);
1730  p = pathModule.resolve(p);
1731
1732  const cache = options[realpathCacheKey];
1733  const maybeCachedResult = cache && cache.get(p);
1734  if (maybeCachedResult) {
1735    return maybeCachedResult;
1736  }
1737
1738  const seenLinks = ObjectCreate(null);
1739  const knownHard = ObjectCreate(null);
1740  const original = p;
1741
1742  // Current character position in p
1743  let pos;
1744  // The partial path so far, including a trailing slash if any
1745  let current;
1746  // The partial path without a trailing slash (except when pointing at a root)
1747  let base;
1748  // The partial path scanned in the previous round, with slash
1749  let previous;
1750
1751  // Skip over roots
1752  current = base = splitRoot(p);
1753  pos = current.length;
1754
1755  // On windows, check that the root exists. On unix there is no need.
1756  if (isWindows) {
1757    const ctx = { path: base };
1758    binding.lstat(pathModule.toNamespacedPath(base), false, undefined, ctx);
1759    handleErrorFromBinding(ctx);
1760    knownHard[base] = true;
1761  }
1762
1763  // Walk down the path, swapping out linked path parts for their real
1764  // values
1765  // NB: p.length changes.
1766  while (pos < p.length) {
1767    // find the next part
1768    const result = nextPart(p, pos);
1769    previous = current;
1770    if (result === -1) {
1771      const last = p.slice(pos);
1772      current += last;
1773      base = previous + last;
1774      pos = p.length;
1775    } else {
1776      current += p.slice(pos, result + 1);
1777      base = previous + p.slice(pos, result);
1778      pos = result + 1;
1779    }
1780
1781    // Continue if not a symlink, break if a pipe/socket
1782    if (knownHard[base] || (cache && cache.get(base) === base)) {
1783      if (isFileType(statValues, S_IFIFO) ||
1784          isFileType(statValues, S_IFSOCK)) {
1785        break;
1786      }
1787      continue;
1788    }
1789
1790    let resolvedLink;
1791    const maybeCachedResolved = cache && cache.get(base);
1792    if (maybeCachedResolved) {
1793      resolvedLink = maybeCachedResolved;
1794    } else {
1795      // Use stats array directly to avoid creating an fs.Stats instance just
1796      // for our internal use.
1797
1798      const baseLong = pathModule.toNamespacedPath(base);
1799      const ctx = { path: base };
1800      const stats = binding.lstat(baseLong, true, undefined, ctx);
1801      handleErrorFromBinding(ctx);
1802
1803      if (!isFileType(stats, S_IFLNK)) {
1804        knownHard[base] = true;
1805        if (cache) cache.set(base, base);
1806        continue;
1807      }
1808
1809      // Read the link if it wasn't read before
1810      // dev/ino always return 0 on windows, so skip the check.
1811      let linkTarget = null;
1812      let id;
1813      if (!isWindows) {
1814        const dev = stats[0].toString(32);
1815        const ino = stats[7].toString(32);
1816        id = `${dev}:${ino}`;
1817        if (seenLinks[id]) {
1818          linkTarget = seenLinks[id];
1819        }
1820      }
1821      if (linkTarget === null) {
1822        const ctx = { path: base };
1823        binding.stat(baseLong, false, undefined, ctx);
1824        handleErrorFromBinding(ctx);
1825        linkTarget = binding.readlink(baseLong, undefined, undefined, ctx);
1826        handleErrorFromBinding(ctx);
1827      }
1828      resolvedLink = pathModule.resolve(previous, linkTarget);
1829
1830      if (cache) cache.set(base, resolvedLink);
1831      if (!isWindows) seenLinks[id] = linkTarget;
1832    }
1833
1834    // Resolve the link, then start over
1835    p = pathModule.resolve(resolvedLink, p.slice(pos));
1836
1837    // Skip over roots
1838    current = base = splitRoot(p);
1839    pos = current.length;
1840
1841    // On windows, check that the root exists. On unix there is no need.
1842    if (isWindows && !knownHard[base]) {
1843      const ctx = { path: base };
1844      binding.lstat(pathModule.toNamespacedPath(base), false, undefined, ctx);
1845      handleErrorFromBinding(ctx);
1846      knownHard[base] = true;
1847    }
1848  }
1849
1850  if (cache) cache.set(original, p);
1851  return encodeRealpathResult(p, options);
1852}
1853
1854
1855realpathSync.native = (path, options) => {
1856  options = getOptions(options, {});
1857  path = getValidatedPath(path);
1858  const ctx = { path };
1859  const result = binding.realpath(path, options.encoding, undefined, ctx);
1860  handleErrorFromBinding(ctx);
1861  return result;
1862};
1863
1864
1865function realpath(p, options, callback) {
1866  callback = typeof options === 'function' ? options : maybeCallback(callback);
1867  options = getOptions(options, {});
1868  p = toPathIfFileURL(p);
1869
1870  if (typeof p !== 'string') {
1871    p += '';
1872  }
1873  validatePath(p);
1874  p = pathModule.resolve(p);
1875
1876  const seenLinks = ObjectCreate(null);
1877  const knownHard = ObjectCreate(null);
1878
1879  // Current character position in p
1880  let pos;
1881  // The partial path so far, including a trailing slash if any
1882  let current;
1883  // The partial path without a trailing slash (except when pointing at a root)
1884  let base;
1885  // The partial path scanned in the previous round, with slash
1886  let previous;
1887
1888  current = base = splitRoot(p);
1889  pos = current.length;
1890
1891  // On windows, check that the root exists. On unix there is no need.
1892  if (isWindows && !knownHard[base]) {
1893    fs.lstat(base, (err, stats) => {
1894      if (err) return callback(err);
1895      knownHard[base] = true;
1896      LOOP();
1897    });
1898  } else {
1899    process.nextTick(LOOP);
1900  }
1901
1902  // Walk down the path, swapping out linked path parts for their real
1903  // values
1904  function LOOP() {
1905    // Stop if scanned past end of path
1906    if (pos >= p.length) {
1907      return callback(null, encodeRealpathResult(p, options));
1908    }
1909
1910    // find the next part
1911    const result = nextPart(p, pos);
1912    previous = current;
1913    if (result === -1) {
1914      const last = p.slice(pos);
1915      current += last;
1916      base = previous + last;
1917      pos = p.length;
1918    } else {
1919      current += p.slice(pos, result + 1);
1920      base = previous + p.slice(pos, result);
1921      pos = result + 1;
1922    }
1923
1924    // Continue if not a symlink, break if a pipe/socket
1925    if (knownHard[base]) {
1926      if (isFileType(statValues, S_IFIFO) ||
1927          isFileType(statValues, S_IFSOCK)) {
1928        return callback(null, encodeRealpathResult(p, options));
1929      }
1930      return process.nextTick(LOOP);
1931    }
1932
1933    return fs.lstat(base, { bigint: true }, gotStat);
1934  }
1935
1936  function gotStat(err, stats) {
1937    if (err) return callback(err);
1938
1939    // If not a symlink, skip to the next path part
1940    if (!stats.isSymbolicLink()) {
1941      knownHard[base] = true;
1942      return process.nextTick(LOOP);
1943    }
1944
1945    // Stat & read the link if not read before.
1946    // Call `gotTarget()` as soon as the link target is known.
1947    // `dev`/`ino` always return 0 on windows, so skip the check.
1948    let id;
1949    if (!isWindows) {
1950      const dev = stats.dev.toString(32);
1951      const ino = stats.ino.toString(32);
1952      id = `${dev}:${ino}`;
1953      if (seenLinks[id]) {
1954        return gotTarget(null, seenLinks[id]);
1955      }
1956    }
1957    fs.stat(base, (err) => {
1958      if (err) return callback(err);
1959
1960      fs.readlink(base, (err, target) => {
1961        if (!isWindows) seenLinks[id] = target;
1962        gotTarget(err, target);
1963      });
1964    });
1965  }
1966
1967  function gotTarget(err, target) {
1968    if (err) return callback(err);
1969
1970    gotResolvedLink(pathModule.resolve(previous, target));
1971  }
1972
1973  function gotResolvedLink(resolvedLink) {
1974    // Resolve the link, then start over
1975    p = pathModule.resolve(resolvedLink, p.slice(pos));
1976    current = base = splitRoot(p);
1977    pos = current.length;
1978
1979    // On windows, check that the root exists. On unix there is no need.
1980    if (isWindows && !knownHard[base]) {
1981      fs.lstat(base, (err) => {
1982        if (err) return callback(err);
1983        knownHard[base] = true;
1984        LOOP();
1985      });
1986    } else {
1987      process.nextTick(LOOP);
1988    }
1989  }
1990}
1991
1992
1993realpath.native = (path, options, callback) => {
1994  callback = makeCallback(callback || options);
1995  options = getOptions(options, {});
1996  path = getValidatedPath(path);
1997  const req = new FSReqCallback();
1998  req.oncomplete = callback;
1999  return binding.realpath(path, options.encoding, req);
2000};
2001
2002function mkdtemp(prefix, options, callback) {
2003  callback = makeCallback(typeof options === 'function' ? options : callback);
2004  options = getOptions(options, {});
2005
2006  validateString(prefix, 'prefix');
2007  nullCheck(prefix, 'prefix');
2008  warnOnNonPortableTemplate(prefix);
2009  const req = new FSReqCallback();
2010  req.oncomplete = callback;
2011  binding.mkdtemp(`${prefix}XXXXXX`, options.encoding, req);
2012}
2013
2014
2015function mkdtempSync(prefix, options) {
2016  options = getOptions(options, {});
2017
2018  validateString(prefix, 'prefix');
2019  nullCheck(prefix, 'prefix');
2020  warnOnNonPortableTemplate(prefix);
2021  const path = `${prefix}XXXXXX`;
2022  const ctx = { path };
2023  const result = binding.mkdtemp(path, options.encoding,
2024                                 undefined, ctx);
2025  handleErrorFromBinding(ctx);
2026  return result;
2027}
2028
2029
2030function copyFile(src, dest, mode, callback) {
2031  if (typeof mode === 'function') {
2032    callback = mode;
2033    mode = 0;
2034  } else if (typeof callback !== 'function') {
2035    throw new ERR_INVALID_CALLBACK(callback);
2036  }
2037
2038  src = getValidatedPath(src, 'src');
2039  dest = getValidatedPath(dest, 'dest');
2040
2041  src = pathModule._makeLong(src);
2042  dest = pathModule._makeLong(dest);
2043  mode = getValidMode(mode, 'copyFile');
2044  callback = makeCallback(callback);
2045
2046  const req = new FSReqCallback();
2047  req.oncomplete = callback;
2048  binding.copyFile(src, dest, mode, req);
2049}
2050
2051
2052function copyFileSync(src, dest, mode) {
2053  src = getValidatedPath(src, 'src');
2054  dest = getValidatedPath(dest, 'dest');
2055
2056  const ctx = { path: src, dest };  // non-prefixed
2057
2058  src = pathModule._makeLong(src);
2059  dest = pathModule._makeLong(dest);
2060  mode = getValidMode(mode, 'copyFile');
2061  binding.copyFile(src, dest, mode, undefined, ctx);
2062  handleErrorFromBinding(ctx);
2063}
2064
2065function lazyLoadStreams() {
2066  if (!ReadStream) {
2067    ({ ReadStream, WriteStream } = require('internal/fs/streams'));
2068    FileReadStream = ReadStream;
2069    FileWriteStream = WriteStream;
2070  }
2071}
2072
2073function createReadStream(path, options) {
2074  lazyLoadStreams();
2075  return new ReadStream(path, options);
2076}
2077
2078function createWriteStream(path, options) {
2079  lazyLoadStreams();
2080  return new WriteStream(path, options);
2081}
2082
2083module.exports = fs = {
2084  appendFile,
2085  appendFileSync,
2086  access,
2087  accessSync,
2088  chown,
2089  chownSync,
2090  chmod,
2091  chmodSync,
2092  close,
2093  closeSync,
2094  copyFile,
2095  copyFileSync,
2096  createReadStream,
2097  createWriteStream,
2098  exists,
2099  existsSync,
2100  fchown,
2101  fchownSync,
2102  fchmod,
2103  fchmodSync,
2104  fdatasync,
2105  fdatasyncSync,
2106  fstat,
2107  fstatSync,
2108  fsync,
2109  fsyncSync,
2110  ftruncate,
2111  ftruncateSync,
2112  futimes,
2113  futimesSync,
2114  lchown,
2115  lchownSync,
2116  lchmod: constants.O_SYMLINK !== undefined ? lchmod : undefined,
2117  lchmodSync: constants.O_SYMLINK !== undefined ? lchmodSync : undefined,
2118  link,
2119  linkSync,
2120  lstat,
2121  lstatSync,
2122  lutimes,
2123  lutimesSync,
2124  mkdir,
2125  mkdirSync,
2126  mkdtemp,
2127  mkdtempSync,
2128  open,
2129  openSync,
2130  opendir,
2131  opendirSync,
2132  readdir,
2133  readdirSync,
2134  read,
2135  readSync,
2136  readv,
2137  readvSync,
2138  readFile,
2139  readFileSync,
2140  readlink,
2141  readlinkSync,
2142  realpath,
2143  realpathSync,
2144  rename,
2145  renameSync,
2146  rm,
2147  rmSync,
2148  rmdir,
2149  rmdirSync,
2150  stat,
2151  statSync,
2152  symlink,
2153  symlinkSync,
2154  truncate,
2155  truncateSync,
2156  unwatchFile,
2157  unlink,
2158  unlinkSync,
2159  utimes,
2160  utimesSync,
2161  watch,
2162  watchFile,
2163  writeFile,
2164  writeFileSync,
2165  write,
2166  writeSync,
2167  writev,
2168  writevSync,
2169  Dir,
2170  Dirent,
2171  Stats,
2172
2173  get ReadStream() {
2174    lazyLoadStreams();
2175    return ReadStream;
2176  },
2177
2178  set ReadStream(val) {
2179    ReadStream = val;
2180  },
2181
2182  get WriteStream() {
2183    lazyLoadStreams();
2184    return WriteStream;
2185  },
2186
2187  set WriteStream(val) {
2188    WriteStream = val;
2189  },
2190
2191  // Legacy names... these have to be separate because of how graceful-fs
2192  // (and possibly other) modules monkey patch the values.
2193  get FileReadStream() {
2194    lazyLoadStreams();
2195    return FileReadStream;
2196  },
2197
2198  set FileReadStream(val) {
2199    FileReadStream = val;
2200  },
2201
2202  get FileWriteStream() {
2203    lazyLoadStreams();
2204    return FileWriteStream;
2205  },
2206
2207  set FileWriteStream(val) {
2208    FileWriteStream = val;
2209  },
2210
2211  // For tests
2212  _toUnixTimestamp: toUnixTimestamp
2213};
2214
2215ObjectDefineProperties(fs, {
2216  F_OK: { enumerable: true, value: F_OK || 0 },
2217  R_OK: { enumerable: true, value: R_OK || 0 },
2218  W_OK: { enumerable: true, value: W_OK || 0 },
2219  X_OK: { enumerable: true, value: X_OK || 0 },
2220  constants: {
2221    configurable: false,
2222    enumerable: true,
2223    value: constants
2224  },
2225  promises: {
2226    configurable: true,
2227    enumerable: true,
2228    get() {
2229      if (promises === null)
2230        promises = require('internal/fs/promises').exports;
2231      return promises;
2232    }
2233  }
2234});
2235