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