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