Skip to content
File

Blob: src/node/internal/internal_fs_callback.ts

typescript1512 lines
1// Copyright (c) 2017-2022 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4//
5// Copyright Joyent, Inc. and other Node contributors.
6//
7// Permission is hereby granted, free of charge, to any person obtaining a
8// copy of this software and associated documentation files (the
9// "Software"), to deal in the Software without restriction, including
10// without limitation the rights to use, copy, modify, merge, publish,
11// distribute, sublicense, and/or sell copies of the Software, and to permit
12// persons to whom the Software is furnished to do so, subject to the
13// following conditions:
14//
15// The above copyright notice and this permission notice shall be included
16// in all copies or substantial portions of the Software.
17//
18// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
21// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
22// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
24// USE OR OTHER DEALINGS IN THE SOFTWARE.
25import * as fssync from 'node-internal:internal_fs_sync';
26import { default as cffs } from 'cloudflare-internal:filesystem';
27import type {
28 FStatOptions,
29 MkdirTempSyncOptions,
30 ReadDirResult,
31 ReadFileSyncOptions,
32 ReadLinkSyncOptions,
33 StatOptions,
34} from 'node-internal:internal_fs_sync';
35import {
36 validatePosition,
37 getDate,
38 validateAccessArgs,
39 validateChownArgs,
40 validateChmodArgs,
41 validateStatArgs,
42 validateMkDirArgs,
43 validateOpendirArgs,
44 validateRmArgs,
45 validateRmDirArgs,
46 validateReaddirArgs,
47 validateWriteArgs,
48 validateWriteFileArgs,
49 normalizePath,
50 Stats,
51 type FilePath,
52 type Position,
53 type RawTime,
54 type SymlinkType,
55 type ReadDirOptions,
56 type WriteSyncOptions,
57 type ValidEncoding,
58 getValidatedFd,
59 validateBufferArray,
60 stringToFlags,
61} from 'node-internal:internal_fs_utils';
62import {
63 F_OK,
64 COPYFILE_EXCL,
65 COPYFILE_FICLONE,
66 COPYFILE_FICLONE_FORCE,
67} from 'node-internal:internal_fs_constants';
68import {
69 ERR_EBADF,
70 ERR_ENOENT,
71 ERR_EEXIST,
72 ERR_INVALID_ARG_TYPE,
73 ERR_INVALID_ARG_VALUE,
74 ERR_UNSUPPORTED_OPERATION,
75} from 'node-internal:internal_errors';
76import { type Dir, Dirent } from 'node-internal:internal_fs';
77import { Buffer } from 'node-internal:internal_buffer';
78import { isArrayBufferView } from 'node-internal:internal_types';
79import {
80 parseFileMode,
81 validateBoolean,
82 validateObject,
83 validateOneOf,
84 validateUint32,
85} from 'node-internal:validators';
86import type {
87 BigIntStatsFs,
88 CopySyncOptions,
89 GlobOptions,
90 GlobOptionsWithFileTypes,
91 GlobOptionsWithoutFileTypes,
92 MakeDirectoryOptions,
93 OpenDirOptions,
94 ReadOptionsWithBuffer,
95 RmOptions,
96 StatsFs,
97 WriteFileOptions,
98} from 'node:fs';
99import type { RmDirOptions } from 'node-internal:internal_fs_utils';
100 
101export type ErrorOnlyCallback = (err: unknown) => void;
102export type SingleArgCallback<T> = (err: unknown, result?: T) => void;
103export type DoubleArgCallback<T, U> = (
104 err: unknown,
105 result1?: T,
106 result2?: U
107) => void;
108 
109function callWithErrorOnlyCallback(
110 fn: () => void,
111 callback: undefined | ErrorOnlyCallback
112): void {
113 if (typeof callback !== 'function') {
114 throw new ERR_INVALID_ARG_TYPE('callback', ['function'], callback);
115 }
116 try {
117 fn();
118 // Note that any errors thrown by the callback will be "handled" by passing
119 // them along to the reportError function, which logs them and triggers the
120 // global "error" event.
121 queueMicrotask(() => {
122 callback(null);
123 });
124 } catch (err) {
125 queueMicrotask(() => {
126 callback(err);
127 });
128 }
129}
130 
131function callWithSingleArgCallback<T>(
132 fn: () => T,
133 callback: undefined | SingleArgCallback<T>
134): void {
135 if (typeof callback !== 'function') {
136 throw new ERR_INVALID_ARG_TYPE('callback', ['function'], callback);
137 }
138 try {
139 const result = fn();
140 queueMicrotask(() => {
141 callback(null, result);
142 });
143 } catch (err) {
144 queueMicrotask(() => {
145 callback(err);
146 });
147 }
148}
149 
150export function access(
151 path: FilePath,
152 modeOrCallback: number | ErrorOnlyCallback = F_OK,
153 callback?: ErrorOnlyCallback
154): void {
155 let mode: number;
156 if (typeof modeOrCallback === 'function') {
157 callback = modeOrCallback;
158 mode = F_OK;
159 } else {
160 mode = modeOrCallback;
161 }
162 
163 const { path: actualPath, mode: actualMode } = validateAccessArgs(path, mode);
164 
165 callWithErrorOnlyCallback(() => {
166 fssync.accessSyncImpl(actualPath, actualMode, true);
167 }, callback);
168}
169 
170export type ExistsCallback = (result: boolean) => void;
171 
172export function exists(path: FilePath, callback: ExistsCallback): void {
173 // With the other methods we perform the method and *then* pass the results
174 // back to the callback using queueMicrotask. Here, however, we wait to
175 // perform the method until we are in the mcirotask. This is so we can
176 // best avoid the race condition that has long existed with the exists
177 // method in Node.js where the file may be deleted between the time we
178 // check for its existence and the time we call the callback.
179 queueMicrotask(() => {
180 callback(fssync.existsSync(path));
181 });
182}
183 
184export function appendFile(
185 path: number | FilePath,
186 data: string | ArrayBufferView,
187 optionsOrCallback: WriteFileOptions | ErrorOnlyCallback,
188 callback?: ErrorOnlyCallback
189): void {
190 let options: WriteFileOptions;
191 if (typeof optionsOrCallback === 'function') {
192 callback = optionsOrCallback;
193 options = {
194 encoding: 'utf8',
195 mode: 0o666,
196 flag: 'a',
197 flush: false,
198 };
199 } else {
200 options = optionsOrCallback;
201 }
202 writeFile(path, data, options, callback);
203}
204 
205export function chmod(
206 path: FilePath,
207 mode: number | string,
208 callback: ErrorOnlyCallback
209): void {
210 const { pathOrFd } = validateChmodArgs(path, mode);
211 callWithErrorOnlyCallback(() => {
212 if (cffs.stat(pathOrFd as URL, { followSymlinks: true }) == null) {
213 throw new ERR_ENOENT((pathOrFd as URL).pathname, { syscall: 'chmod' });
214 }
215 }, callback);
216}
217 
218export function chown(
219 path: FilePath,
220 uid: number,
221 gid: number,
222 callback: ErrorOnlyCallback
223): void {
224 const { pathOrFd } = validateChownArgs(path, uid, gid);
225 callWithErrorOnlyCallback(() => {
226 if (cffs.stat(pathOrFd as URL, { followSymlinks: true }) == null) {
227 throw new ERR_ENOENT((path as URL).pathname, { syscall: 'chown' });
228 }
229 }, callback);
230}
231 
232export function close(
233 fd: number,
234 callback: ErrorOnlyCallback = () => {}
235): void {
236 fd = getValidatedFd(fd);
237 callWithErrorOnlyCallback(() => {
238 fssync.closeSync(fd);
239 }, callback);
240}
241 
242export function copyFile(
243 src: FilePath,
244 dest: FilePath,
245 modeOrCallback: number | ErrorOnlyCallback = 0,
246 callback?: ErrorOnlyCallback
247): void {
248 let mode: number;
249 if (typeof modeOrCallback === 'function') {
250 callback = modeOrCallback;
251 mode = 0;
252 } else {
253 mode = modeOrCallback;
254 }
255 const normalizedSrc = normalizePath(src);
256 const normalizedDest = normalizePath(dest);
257 
258 validateOneOf(mode, 'mode', [
259 0,
260 COPYFILE_EXCL,
261 COPYFILE_FICLONE_FORCE,
262 COPYFILE_FICLONE,
263 ]);
264 if (mode & COPYFILE_FICLONE_FORCE) {
265 throw new ERR_UNSUPPORTED_OPERATION();
266 }
267 if (mode & COPYFILE_EXCL && fssync.existsSync(dest)) {
268 throw new ERR_EEXIST({
269 syscall: 'copyFile',
270 path: normalizedDest.pathname,
271 });
272 }
273 
274 callWithErrorOnlyCallback(() => {
275 fssync.copyFileSync(normalizedSrc, normalizedDest, mode);
276 }, callback);
277}
278 
279export function cp(
280 src: FilePath,
281 dest: FilePath,
282 optionsOrCallback: CopySyncOptions | ErrorOnlyCallback,
283 callback?: ErrorOnlyCallback
284): void {
285 let options: CopySyncOptions;
286 if (typeof optionsOrCallback === 'function') {
287 callback = optionsOrCallback;
288 options = {};
289 } else {
290 options = optionsOrCallback;
291 }
292 
293 validateObject(options, 'options');
294 const {
295 dereference = false,
296 errorOnExist = false,
297 force = true,
298 mode = 0,
299 preserveTimestamps = false,
300 recursive = false,
301 verbatimSymlinks = false,
302 } = options;
303 
304 validateBoolean(dereference, 'options.dereference');
305 validateBoolean(errorOnExist, 'options.errorOnExist');
306 validateBoolean(force, 'options.force');
307 validateBoolean(preserveTimestamps, 'options.preserveTimestamps');
308 validateBoolean(recursive, 'options.recursive');
309 validateBoolean(verbatimSymlinks, 'options.verbatimSymlinks');
310 validateUint32(mode, 'options.mode');
311 
312 if (mode & COPYFILE_FICLONE_FORCE) {
313 throw new ERR_INVALID_ARG_VALUE(
314 'options.mode',
315 'COPYFILE_FICLONE_FORCE is not supported'
316 );
317 }
318 
319 if (options.filter !== undefined) {
320 if (typeof options.filter !== 'function') {
321 throw new ERR_INVALID_ARG_TYPE(
322 'options.filter',
323 'function',
324 options.filter
325 );
326 }
327 // We do not implement the filter option currently. There's a bug in the Node.js
328 // implementation of fs.cp and the option.filter in which non-UTF-8 encoded file
329 // names are not handled correctly and the option.filter fails when the src or
330 // dest is passed in as a Buffer. Fixing this bug in Node.js will require a breaking
331 // change to the API or a new API that appropriately handles Buffer inputs and non
332 // UTF-8 encoded names. We want to avoid implementing the filter option for now
333 // until Node.js settles on a better implementation and API.
334 throw new ERR_UNSUPPORTED_OPERATION();
335 }
336 
337 const exclusive = Boolean(mode & COPYFILE_EXCL);
338 // We're not current implementing the exclusive flag. We're validating
339 // it here just to use it so the compiler doesn't complain.
340 validateBoolean(exclusive, '');
341 
342 // We're not current implementing verbatimSymlinks in any meaningful way.
343 // Our symlinks are always fully qualfied. That is, they always point to
344 // an absolute path and never to a relative path, so there is no distinction
345 // between verbatimSymlinks and non-verbatimSymlinks. We validate the option
346 // value above but otherwise we ignore it.
347 
348 src = normalizePath(src);
349 dest = normalizePath(dest);
350 
351 callWithErrorOnlyCallback(() => {
352 cffs.cp(src, dest, {
353 deferenceSymlinks: dereference,
354 recursive,
355 force,
356 errorOnExist,
357 });
358 }, callback);
359}
360 
361export function fchmod(
362 fd: number,
363 mode: string | number,
364 callback: ErrorOnlyCallback
365): void {
366 fd = getValidatedFd(fd);
367 parseFileMode(mode, 'mode');
368 callWithErrorOnlyCallback(() => {
369 fssync.fchmodSync(fd, mode);
370 }, callback);
371}
372 
373export function fchown(
374 fd: number,
375 uid: number,
376 gid: number,
377 callback: ErrorOnlyCallback
378): void {
379 const { pathOrFd } = validateChownArgs(fd, uid, gid);
380 callWithErrorOnlyCallback(() => {
381 if (cffs.stat(pathOrFd as URL, { followSymlinks: false }) == null) {
382 throw new ERR_EBADF({ syscall: 'fchown' });
383 }
384 }, callback);
385}
386 
387export function fdatasync(fd: number, callback: ErrorOnlyCallback): void {
388 getValidatedFd(fd);
389 callWithErrorOnlyCallback(() => {
390 fssync.fdatasyncSync(fd);
391 }, callback);
392}
393 
394export function fstat(
395 fd: number,
396 optionsOrCallback: SingleArgCallback<Stats> | FStatOptions,
397 callback?: SingleArgCallback<Stats>
398): void {
399 let options: FStatOptions;
400 if (typeof optionsOrCallback === 'function') {
401 callback = optionsOrCallback;
402 options = { bigint: false };
403 } else {
404 options = optionsOrCallback;
405 }
406 validateStatArgs(fd, options, true /* is fstat */);
407 callWithSingleArgCallback(() => fssync.fstatSync(fd, options), callback);
408}
409 
410export function fsync(
411 fd: number,
412 callback: ErrorOnlyCallback = () => {}
413): void {
414 getValidatedFd(fd);
415 callWithErrorOnlyCallback(() => {
416 fssync.fsyncSync(fd);
417 }, callback);
418}
419 
420export function ftruncate(
421 fd: number,
422 lenOrCallback: number | ErrorOnlyCallback,
423 callback?: ErrorOnlyCallback
424): void {
425 let len: number;
426 if (typeof lenOrCallback === 'function') {
427 callback = lenOrCallback;
428 len = 0;
429 } else {
430 len = lenOrCallback;
431 }
432 fd = getValidatedFd(fd);
433 validateUint32(len, 'len');
434 callWithErrorOnlyCallback(() => {
435 fssync.ftruncateSync(fd, len);
436 }, callback);
437}
438 
439export function futimes(
440 fd: number,
441 atime: RawTime | Date,
442 mtime: RawTime | Date,
443 callback: ErrorOnlyCallback
444): void {
445 fd = getValidatedFd(fd);
446 atime = getDate(atime);
447 mtime = getDate(mtime);
448 callWithErrorOnlyCallback(() => {
449 fssync.futimesSync(fd, atime, mtime);
450 }, callback);
451}
452 
453export function lchmod(
454 path: FilePath,
455 mode: string | number,
456 callback: ErrorOnlyCallback
457): void {
458 const { pathOrFd } = validateChmodArgs(path, mode);
459 callWithErrorOnlyCallback(() => {
460 if (cffs.stat(pathOrFd as URL, { followSymlinks: false }) == null) {
461 throw new ERR_ENOENT((pathOrFd as URL).pathname, { syscall: 'lchmod' });
462 }
463 }, callback);
464}
465 
466export function lchown(
467 path: FilePath,
468 uid: number,
469 gid: number,
470 callback: ErrorOnlyCallback
471): void {
472 const { pathOrFd } = validateChownArgs(path, uid, gid);
473 callWithErrorOnlyCallback(() => {
474 if (cffs.stat(pathOrFd as URL, { followSymlinks: false }) == null) {
475 throw new ERR_ENOENT((path as URL).pathname, { syscall: 'lchown' });
476 }
477 }, callback);
478}
479 
480export function lutimes(
481 path: FilePath,
482 atime: RawTime | Date,
483 mtime: RawTime | Date,
484 callback: ErrorOnlyCallback
485): void {
486 atime = getDate(atime);
487 mtime = getDate(mtime);
488 path = normalizePath(path);
489 callWithErrorOnlyCallback(() => {
490 fssync.lutimesSync(path, atime, mtime);
491 }, callback);
492}
493 
494export function link(
495 src: FilePath,
496 dest: FilePath,
497 callback: ErrorOnlyCallback
498): void {
499 const normalizedSrc = normalizePath(src);
500 const normalizedDest = normalizePath(dest);
501 callWithErrorOnlyCallback(() => {
502 fssync.linkSync(normalizedSrc, normalizedDest);
503 }, callback);
504}
505 
506export function lstat(
507 path: FilePath,
508 optionsOrCallback: SingleArgCallback<Stats | undefined> | StatOptions,
509 callback?: SingleArgCallback<Stats | undefined>
510): void {
511 let options: StatOptions;
512 if (typeof optionsOrCallback === 'function') {
513 callback = optionsOrCallback;
514 options = { bigint: false };
515 } else {
516 options = optionsOrCallback;
517 }
518 const {
519 pathOrFd: normalizedPath,
520 bigint,
521 throwIfNoEntry,
522 } = validateStatArgs(path, options);
523 callWithSingleArgCallback(
524 () =>
525 fssync.lstatSync(normalizedPath as FilePath, {
526 bigint,
527 throwIfNoEntry,
528 }),
529 callback
530 );
531}
532 
533export function mkdir(
534 path: FilePath,
535 optionsOrCallback:
536 | number
537 | SingleArgCallback<string | undefined>
538 | MakeDirectoryOptions,
539 callback?: SingleArgCallback<string | undefined>
540): void {
541 let options: number | MakeDirectoryOptions;
542 if (typeof optionsOrCallback === 'function') {
543 callback = optionsOrCallback;
544 options = {};
545 } else {
546 options = optionsOrCallback;
547 }
548 const { path: normalizedPath, recursive } = validateMkDirArgs(path, options);
549 callWithSingleArgCallback(
550 () => fssync.mkdirSync(normalizedPath, { recursive }),
551 callback
552 );
553}
554 
555export function mkdtemp(
556 prefix: FilePath,
557 optionsOrCallback:
558 | SingleArgCallback<string>
559 | MkdirTempSyncOptions
560 | ValidEncoding,
561 callback?: SingleArgCallback<string>
562): void {
563 let options: MkdirTempSyncOptions | ValidEncoding;
564 if (typeof optionsOrCallback === 'function') {
565 callback = optionsOrCallback;
566 options = {};
567 } else {
568 options = optionsOrCallback;
569 }
570 if (typeof options === 'string' || options == null) {
571 options = { encoding: options };
572 }
573 validateObject(options, 'options');
574 const { encoding = null } = options;
575 if (
576 encoding !== null &&
577 encoding !== 'buffer' &&
578 !Buffer.isEncoding(encoding)
579 ) {
580 throw new ERR_INVALID_ARG_VALUE('options.encoding', encoding);
581 }
582 
583 callWithSingleArgCallback(
584 () => fssync.mkdtempSync(prefix, options),
585 callback
586 );
587}
588 
589export function open(
590 path: FilePath,
591 flagsOrCallback: string | number | SingleArgCallback<number> = 'r',
592 modeOrCallback: string | number | SingleArgCallback<number> = 0o666,
593 callback?: SingleArgCallback<number>
594): void {
595 let flags: string | number;
596 let mode: string | number;
597 if (typeof flagsOrCallback === 'function') {
598 callback = flagsOrCallback;
599 flags = 'r';
600 mode = 0o666;
601 } else if (typeof modeOrCallback === 'function') {
602 callback = modeOrCallback;
603 flags = flagsOrCallback;
604 mode = 0o666;
605 } else {
606 flags = flagsOrCallback;
607 mode = modeOrCallback;
608 }
609 path = normalizePath(path);
610 mode = parseFileMode(mode, 'mode');
611 flags = stringToFlags(flags);
612 callWithSingleArgCallback(() => fssync.openSync(path, flags, mode), callback);
613}
614 
615export function opendir(
616 path: FilePath,
617 optionsOrCallback: SingleArgCallback<Dir> | OpenDirOptions,
618 callback?: SingleArgCallback<Dir>
619): void {
620 let options: OpenDirOptions;
621 if (typeof optionsOrCallback === 'function') {
622 callback = optionsOrCallback;
623 options = {
624 encoding: 'utf8',
625 bufferSize: 32,
626 recursive: false,
627 };
628 } else {
629 options = optionsOrCallback;
630 }
631 
632 const {
633 path: validatedPath,
634 encoding,
635 recursive,
636 } = validateOpendirArgs(path, options);
637 
638 callWithSingleArgCallback(() => {
639 return fssync.opendirSync(validatedPath, {
640 encoding: encoding as BufferEncoding,
641 recursive,
642 });
643 }, callback);
644}
645 
646// read has a complex polymorphic signature so this is a bit gnarly.
647// The various signatures include:
648// fs.read(fd, buffer, offset, length, position, callback)
649// fs.read(fd, callback)
650// fs.read(fd, buffer, callback)
651// fs.read(fd, buffer, { offset, length, position }, callback)
652// fs.read(fd, { buffer, offset, length, position }, callback)
653//
654// Where fd is always a number, buffer is an ArrayBufferView, offset and
655// length are numbers, but position can be a number or bigint, and offset
656// length, and position are optional. The callback is always a function
657// that receives three arguments: err, bytesRead, and buffer.
658export function read<T extends NodeJS.ArrayBufferView>(
659 fd: number,
660 bufferOptionsOrCallback:
661 | T
662 | ReadOptionsWithBuffer<T>
663 | DoubleArgCallback<number, T>,
664 offsetOptionsOrCallback?:
665 | ReadOptionsWithBuffer<T>
666 | number
667 | DoubleArgCallback<number, T>,
668 lengthOrCallback?: null | number | DoubleArgCallback<number, T>,
669 position?: Position,
670 callback?: DoubleArgCallback<number, T>
671): void {
672 // Node.js... you're killing me here with these polymorphic signatures.
673 //
674 // We're going to normalize the arguments so that we can defer to the
675 // readSync variant using the signature readSync(fd, buffer, options)
676 //
677 // The callback is always the last argument but may appear in the second,
678 // third, fourth, or sixth position depending on the signature used. When we
679 // find it, we can ignore the remaining arguments that come after it,
680 // defaulting any missing arguments to whatever default is defined for
681 // them.
682 //
683 // The second argument is always either a buffer, an options object that
684 // contains a buffer property, or the callback. If it's the callback,
685 // then we will allocate a new buffer for the read with size 16384 bytes,
686 // and default the offset to 0, length to the buffer size, and position to
687 // null (indicating that the internal read position for the fd is to be
688 // used). If it's an options object, we will use the buffer property from it
689 // if it exists. If the buffer property is not present, we will allocate a
690 // new buffer for the read with size 16384 bytes. The offset, length, and
691 // position properties will be used if they are present in the options
692 // object or defaulted to 0, the buffer size, and null respectively.
693 // If the second argument is a buffer, we will use it and look for the
694 // offset, length, position, and callback arguments in the remaining arguments.
695 //
696 // The third argument is either ignored (if the second argument is the
697 // callback), or it is one of either the offset, the options object, or
698 // the callback. If it is the callback, we will default the offset to 0,
699 // length to the buffer size (that had to have been provided by the second
700 // argument), and position to null. If it is the options object, we will
701 // get the offset, length, and position properties from it if they exist,
702 // or default them to 0, the buffer size, and null respectively. If it is
703 // the offset, we will look for the length, position, and callback in the
704 // remaining arguments.
705 //
706 // The fourth argument is either ignored (if the second or third argument is
707 // the callback), or it is the length as either a number, null, or explicitly
708 // passed as undefined, or it is the callback. If it is the callback, we will
709 // default the length to the buffer size (that had to have been provided by
710 // the second argument), and default position to null, then look for the
711 // callback in the sixth argument. If it is the length, we will look for the
712 // position and callback in the remaining arguments.
713 //
714 // The fifth argument is either ignored (if the callback has already been
715 // seen) or it is the position as either a number, bigint, null, or explicitly
716 // undefined. Any other type in this position is an error.
717 //
718 // The sixth argument is either ignored (if the callback has already been
719 // seen) or it is the callback. If it is not a function then an error is
720 // thrown.
721 //
722 // Once we have collected all of the arguments, we will call the readSync
723 // method with signature readSync(fd, buffer, { offset, length, position })
724 // and pass the return value, and the buffer, to the Node.js-style callback
725 // with the signature callback(null, returnValue, buffer). If the call throws,
726 // then we will pass the error to the callback as the first argument.
727 
728 let actualCallback: undefined | DoubleArgCallback<number, T>;
729 let actualBuffer: T; // Buffer, TypedArray, or DataView
730 let actualOffset = 0; // Offset from the beginning of the buffer
731 let actualLength: number; // Length of the data to read into the buffer
732 // Should never be negative and never extends
733 // beyond the end of the buffer (that is,
734 // actualOffset + actualLength <= actualBuffer.byteLength)
735 let actualPosition: Position = null; // The position within the
736 // file to read from. If null,
737 // the current position for the fd
738 // is used.
739 
740 // Handle the case where the second argument is the callback
741 if (typeof bufferOptionsOrCallback === 'function') {
742 actualCallback = bufferOptionsOrCallback;
743 // Default buffer size when not provided
744 // The use of as unknown as T here is a bit of a hack to satisfy the types...
745 actualBuffer = Buffer.alloc(16384) as unknown as T;
746 actualLength = actualBuffer.byteLength;
747 }
748 // Handle the case where the second argument is an options object
749 else if (
750 // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
751 bufferOptionsOrCallback != null &&
752 typeof bufferOptionsOrCallback === 'object' &&
753 !isArrayBufferView(bufferOptionsOrCallback)
754 ) {
755 // It's an options object
756 const {
757 buffer = Buffer.alloc(16384),
758 offset = buffer.byteOffset,
759 length = buffer.byteLength,
760 position = null,
761 } = bufferOptionsOrCallback;
762 if (!isArrayBufferView(buffer)) {
763 throw new ERR_INVALID_ARG_TYPE(
764 'options.buffer',
765 ['Buffer', 'TypedArray', 'DataView'],
766 buffer
767 );
768 }
769 validateUint32(offset, 'options.offset');
770 validateUint32(length, 'options.length');
771 validatePosition(position, 'options.position');
772 
773 actualBuffer = buffer as unknown as T;
774 actualOffset = offset;
775 actualLength = length;
776 actualPosition = position;
777 
778 // The callback must be in the third argument
779 if (typeof offsetOptionsOrCallback !== 'function') {
780 throw new ERR_INVALID_ARG_TYPE(
781 'callback',
782 ['function'],
783 offsetOptionsOrCallback
784 );
785 }
786 actualCallback = offsetOptionsOrCallback;
787 }
788 // Handle the case where the second argument is a buffer
789 else {
790 actualBuffer = bufferOptionsOrCallback;
791 
792 if (!isArrayBufferView(actualBuffer)) {
793 throw new ERR_INVALID_ARG_TYPE(
794 'buffer',
795 ['Buffer', 'TypedArray', 'DataView'],
796 actualBuffer
797 );
798 }
799 
800 actualLength = actualBuffer.byteLength;
801 actualOffset = actualBuffer.byteOffset;
802 
803 // Now we need to find the callback and other parameters
804 if (typeof offsetOptionsOrCallback === 'function') {
805 // fs.read(fd, buffer, callback)
806 actualCallback = offsetOptionsOrCallback;
807 } else if (
808 typeof offsetOptionsOrCallback === 'object' &&
809 !(offsetOptionsOrCallback instanceof Number)
810 ) {
811 // fs.read(fd, buffer, options, callback)
812 const {
813 offset = actualOffset,
814 length = actualLength,
815 position = null,
816 } = offsetOptionsOrCallback;
817 validateUint32(offset, 'options.offset');
818 validateUint32(length, 'options.length');
819 validatePosition(position, 'options.position');
820 actualOffset = offset;
821 actualLength = length;
822 actualPosition = position;
823 
824 // The callback must be in the fourth argument.
825 if (typeof lengthOrCallback !== 'function') {
826 throw new ERR_INVALID_ARG_TYPE(
827 'callback',
828 ['function'],
829 lengthOrCallback
830 );
831 }
832 actualCallback = lengthOrCallback;
833 } else {
834 // fs.read(fd, buffer, offset, length, position, callback)
835 actualOffset =
836 typeof offsetOptionsOrCallback === 'number'
837 ? offsetOptionsOrCallback
838 : 0;
839 
840 if (typeof lengthOrCallback === 'function') {
841 actualCallback = lengthOrCallback;
842 actualPosition = null;
843 actualLength = actualBuffer.byteLength;
844 } else {
845 actualLength = lengthOrCallback ?? actualBuffer.byteLength;
846 
847 validateUint32(position, 'position');
848 actualPosition = position;
849 
850 actualCallback = callback;
851 }
852 }
853 }
854 
855 // We know that the function must be called with at least 3 arguments and
856 // that the first argument is always a number (the fd) and the last must
857 // always be the callback.
858 // If the actualCallback is not set at this point, then we have a problem.
859 if (typeof actualCallback !== 'function') {
860 throw new ERR_INVALID_ARG_TYPE('callback', ['function'], actualCallback);
861 }
862 
863 // We also have a problem if the actualBuffer is not set here correctly.
864 if (!isArrayBufferView(actualBuffer)) {
865 throw new ERR_INVALID_ARG_TYPE(
866 'buffer',
867 ['Buffer', 'TypedArray', 'DataView'],
868 actualBuffer
869 );
870 }
871 
872 // At this point we have the following:
873 // - actualBuffer: The buffer to read into
874 // - actualOffset: The offset into the buffer to start writing at
875 // - actualLength: The length of the data to read into the buffer
876 // - actualPosition: The position within the file to read from (or null)
877 // - actualCallback: The callback to call when done
878 // - fd: The file descriptor to read from
879 // Let actualOffset + actualLength should never be greater than the
880 // buffer size. Let's check that.
881 if (
882 actualOffset < 0 ||
883 actualLength < 0 ||
884 actualOffset + actualLength > actualBuffer.byteLength
885 ) {
886 throw new ERR_INVALID_ARG_VALUE(
887 'offset',
888 'must be >= 0 and <= buffer.length'
889 );
890 }
891 // The actualOffset, actualLength, and actualPosition values should always
892 // be greater or equal to 0 (unless actualPosition is null)... keeping in
893 // mind that actualPosition can be a number or a bigint.
894 
895 // As a special case, if the actualBuffer length is 0, or if actualLength
896 // is 0, then can just call the callback with 0 bytes read and return.
897 if (actualBuffer.byteLength === 0 || actualLength === 0) {
898 queueMicrotask(() => {
899 actualCallback(null, 0, actualBuffer);
900 });
901 return;
902 }
903 
904 // Now that we've normalized all the parameters, call readSync
905 try {
906 const bytesRead = fssync.readSync(fd, actualBuffer, {
907 offset: actualOffset,
908 length: actualLength,
909 position: actualPosition,
910 });
911 queueMicrotask(() => {
912 actualCallback(null, bytesRead, actualBuffer);
913 });
914 } catch (err) {
915 queueMicrotask(() => {
916 actualCallback(err);
917 });
918 }
919}
920 
921export function readdir(
922 path: FilePath,
923 optionsOrCallback:
924 | SingleArgCallback<ReadDirResult>
925 | ReadDirOptions
926 | ValidEncoding,
927 callback?: SingleArgCallback<ReadDirResult>
928): void {
929 let options: ReadDirOptions | ValidEncoding;
930 if (typeof optionsOrCallback === 'function') {
931 callback = optionsOrCallback;
932 options = {
933 encoding: 'utf8',
934 withFileTypes: false,
935 recursive: false,
936 };
937 } else {
938 options = optionsOrCallback;
939 }
940 const {
941 path: normalizedPath,
942 recursive,
943 withFileTypes,
944 encoding,
945 } = validateReaddirArgs(path, options);
946 callWithSingleArgCallback(() => {
947 return fssync.readdirSync(normalizedPath, {
948 recursive,
949 withFileTypes,
950 encoding,
951 });
952 }, callback);
953}
954 
955export function readFile(
956 path: FilePath,
957 optionsOrCallback:
958 | SingleArgCallback<string | Buffer>
959 | ValidEncoding
960 | ReadFileSyncOptions,
961 callback?: SingleArgCallback<string | Buffer>
962): void {
963 let options: ValidEncoding | ReadFileSyncOptions;
964 if (typeof optionsOrCallback === 'function') {
965 callback = optionsOrCallback;
966 options = {};
967 } else {
968 options = optionsOrCallback;
969 }
970 
971 if (typeof options === 'string' || options == null) {
972 options = { encoding: options };
973 }
974 validateObject(options, 'options');
975 const { encoding = null } = options;
976 if (
977 encoding !== null &&
978 encoding !== 'buffer' &&
979 !Buffer.isEncoding(encoding)
980 ) {
981 throw new ERR_INVALID_ARG_VALUE('options.encoding', encoding);
982 }
983 
984 callWithSingleArgCallback(() => fssync.readFileSync(path, options), callback);
985}
986 
987export function readlink(
988 path: FilePath,
989 optionsOrCallback:
990 | SingleArgCallback<string | Buffer>
991 | ValidEncoding
992 | ReadLinkSyncOptions,
993 callback?: SingleArgCallback<string | Buffer>
994): void {
995 let options: ValidEncoding | ReadLinkSyncOptions;
996 if (typeof optionsOrCallback === 'function') {
997 callback = optionsOrCallback;
998 options = {};
999 } else {
1000 options = optionsOrCallback;
1001 }
1002 if (typeof options === 'string' || options == null) {
1003 options = { encoding: options };
1004 }
1005 const normalizedPath = normalizePath(path);
1006 validateObject(options, 'options');
1007 const { encoding = 'utf8' } = options;
1008 if (
1009 encoding !== null &&
1010 encoding !== 'buffer' &&
1011 !Buffer.isEncoding(encoding)
1012 ) {
1013 throw new ERR_INVALID_ARG_VALUE('options.encoding', encoding);
1014 }
1015 callWithSingleArgCallback(
1016 () => fssync.readlinkSync(normalizedPath, { encoding }),
1017 callback
1018 );
1019}
1020 
1021export function readv<T extends NodeJS.ArrayBufferView>(
1022 fd: number,
1023 buffers: T[],
1024 positionOrCallback: undefined | Position | DoubleArgCallback<number, T[]>,
1025 callback?: DoubleArgCallback<number, T[]>
1026): void {
1027 if (typeof positionOrCallback === 'function') {
1028 callback = positionOrCallback;
1029 positionOrCallback = null;
1030 }
1031 if (typeof callback !== 'function') {
1032 throw new ERR_INVALID_ARG_TYPE('callback', ['function'], callback);
1033 }
1034 
1035 validatePosition(positionOrCallback, 'position');
1036 
1037 try {
1038 const read = fssync.readvSync(fd, buffers, positionOrCallback);
1039 queueMicrotask(() => {
1040 callback(null, read, buffers);
1041 });
1042 } catch (err) {
1043 queueMicrotask(() => {
1044 callback(err);
1045 });
1046 }
1047}
1048 
1049export function realpath(
1050 path: FilePath,
1051 optionsOrCallback:
1052 | SingleArgCallback<string | Buffer>
1053 | ValidEncoding
1054 | ReadLinkSyncOptions,
1055 callback?: SingleArgCallback<string | Buffer>
1056): void {
1057 let options: ValidEncoding | ReadLinkSyncOptions;
1058 if (typeof optionsOrCallback === 'function') {
1059 callback = optionsOrCallback;
1060 options = {};
1061 } else {
1062 options = optionsOrCallback;
1063 }
1064 
1065 if (typeof options === 'string' || options == null) {
1066 options = { encoding: options };
1067 }
1068 
1069 validateObject(options, 'options');
1070 const { encoding = 'utf8' } = options;
1071 if (
1072 encoding !== null &&
1073 encoding !== 'buffer' &&
1074 !Buffer.isEncoding(encoding)
1075 ) {
1076 throw new ERR_INVALID_ARG_VALUE('options.encoding', encoding);
1077 }
1078 
1079 const normalizedPath = normalizePath(path);
1080 
1081 callWithSingleArgCallback(
1082 () => fssync.realpathSync(normalizedPath, { encoding }),
1083 callback
1084 );
1085}
1086 
1087realpath.native = realpath;
1088 
1089export function rename(
1090 oldPath: FilePath,
1091 newPath: FilePath,
1092 callback: ErrorOnlyCallback
1093): void {
1094 const normalizedOldPath = normalizePath(oldPath);
1095 const normalizedNewPath = normalizePath(newPath);
1096 callWithErrorOnlyCallback(() => {
1097 fssync.renameSync(normalizedOldPath, normalizedNewPath);
1098 }, callback);
1099}
1100 
1101export function rmdir(
1102 path: FilePath,
1103 optionsOrCallback: ErrorOnlyCallback | RmDirOptions,
1104 callback?: ErrorOnlyCallback
1105): void {
1106 let options: RmDirOptions;
1107 let cb: ErrorOnlyCallback | undefined = callback;
1108 if (typeof optionsOrCallback === 'function') {
1109 cb = optionsOrCallback;
1110 options = {};
1111 } else {
1112 options = optionsOrCallback;
1113 }
1114 const { path: normalizedPath, recursive } = validateRmDirArgs(path, options);
1115 callWithErrorOnlyCallback(() => {
1116 fssync.rmdirSync(normalizedPath, { recursive });
1117 }, cb);
1118}
1119 
1120export function rm(
1121 path: FilePath,
1122 optionsOrCallback: ErrorOnlyCallback | RmOptions,
1123 callback?: ErrorOnlyCallback
1124): void {
1125 let options: RmOptions;
1126 if (typeof optionsOrCallback === 'function') {
1127 callback = optionsOrCallback;
1128 options = {};
1129 } else {
1130 options = optionsOrCallback;
1131 }
1132 const {
1133 path: normalizedPath,
1134 recursive,
1135 force,
1136 } = validateRmArgs(path, options);
1137 callWithErrorOnlyCallback(() => {
1138 fssync.rmSync(normalizedPath, { recursive, force });
1139 }, callback);
1140}
1141 
1142export function stat(
1143 path: FilePath,
1144 optionsOrCallback: SingleArgCallback<Stats | undefined> | StatOptions,
1145 callback?: SingleArgCallback<Stats | undefined>
1146): void {
1147 let options: StatOptions;
1148 if (typeof optionsOrCallback === 'function') {
1149 callback = optionsOrCallback;
1150 options = { bigint: false };
1151 } else {
1152 options = optionsOrCallback;
1153 }
1154 const {
1155 pathOrFd: normalizedPath,
1156 bigint,
1157 throwIfNoEntry,
1158 } = validateStatArgs(path, options);
1159 callWithSingleArgCallback(
1160 () =>
1161 fssync.statSync(normalizedPath as FilePath, {
1162 bigint,
1163 throwIfNoEntry,
1164 }),
1165 callback
1166 );
1167}
1168 
1169export function statfs(
1170 path: FilePath,
1171 optionsOrCallback:
1172 | SingleArgCallback<StatsFs | BigIntStatsFs>
1173 | { bigint?: boolean | undefined },
1174 callback?: SingleArgCallback<StatsFs | BigIntStatsFs>
1175): void {
1176 let options: { bigint?: boolean | undefined };
1177 if (typeof optionsOrCallback === 'function') {
1178 callback = optionsOrCallback;
1179 options = { bigint: false };
1180 } else {
1181 options = optionsOrCallback;
1182 }
1183 const normalizedPath = normalizePath(path);
1184 
1185 // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
1186 if (options !== undefined) {
1187 validateObject(options, 'options');
1188 const { bigint } = options;
1189 if (bigint !== undefined) {
1190 validateBoolean(bigint, 'options.bigint');
1191 }
1192 }
1193 
1194 callWithSingleArgCallback(
1195 () => fssync.statfsSync(normalizedPath, options),
1196 callback
1197 );
1198}
1199 
1200export function symlink(
1201 target: FilePath,
1202 path: FilePath,
1203 typeOrCallback: SymlinkType | ErrorOnlyCallback,
1204 callback?: ErrorOnlyCallback
1205): void {
1206 let type: SymlinkType;
1207 if (typeof typeOrCallback === 'function') {
1208 callback = typeOrCallback;
1209 type = null;
1210 } else {
1211 type = typeOrCallback;
1212 }
1213 const normalizedTarget = normalizePath(target);
1214 const normalizedPath = normalizePath(path);
1215 if (type != null) {
1216 validateOneOf(type, 'type', ['dir', 'file', 'junction', null]);
1217 }
1218 callWithErrorOnlyCallback(() => {
1219 fssync.symlinkSync(normalizedTarget, normalizedPath, type);
1220 }, callback);
1221}
1222 
1223export function truncate(
1224 path: FilePath,
1225 lenOrCallback: number | ErrorOnlyCallback,
1226 callback?: ErrorOnlyCallback
1227): void {
1228 let len: number;
1229 if (typeof lenOrCallback === 'function') {
1230 callback = lenOrCallback;
1231 len = 0;
1232 } else {
1233 len = lenOrCallback;
1234 }
1235 const normalizedPath = normalizePath(path);
1236 validateUint32(len, 'len');
1237 callWithErrorOnlyCallback(() => {
1238 fssync.truncateSync(normalizedPath, len);
1239 }, callback);
1240}
1241 
1242export function unlink(path: FilePath, callback: ErrorOnlyCallback): void {
1243 const normalizedPath = normalizePath(path);
1244 callWithErrorOnlyCallback(() => {
1245 fssync.unlinkSync(normalizedPath);
1246 }, callback);
1247}
1248 
1249export function utimes(
1250 path: FilePath,
1251 atime: RawTime | Date,
1252 mtime: RawTime | Date,
1253 callback: ErrorOnlyCallback
1254): void {
1255 atime = getDate(atime);
1256 mtime = getDate(mtime);
1257 const normalizedPath = normalizePath(path);
1258 callWithErrorOnlyCallback(() => {
1259 fssync.utimesSync(normalizedPath, atime, mtime);
1260 }, callback);
1261}
1262 
1263export function write<T extends NodeJS.ArrayBufferView>(
1264 fd: number,
1265 buffer: T | string,
1266 offsetOptionsPositionOrCallback?:
1267 | WriteSyncOptions
1268 | Position
1269 | DoubleArgCallback<number, T>,
1270 encodingLengthOrCallback?:
1271 | number
1272 | ValidEncoding
1273 | DoubleArgCallback<number, T>,
1274 positionOrCallback?: Position | DoubleArgCallback<number, T>,
1275 callback?: DoubleArgCallback<number, T>
1276): void {
1277 let offsetOrOptions: WriteSyncOptions | Position | undefined;
1278 let lengthOrEncoding: number | ValidEncoding | undefined;
1279 let position: Position | undefined;
1280 if (typeof offsetOptionsPositionOrCallback === 'function') {
1281 callback = offsetOptionsPositionOrCallback;
1282 offsetOrOptions = undefined;
1283 } else {
1284 offsetOrOptions = offsetOptionsPositionOrCallback;
1285 }
1286 if (typeof encodingLengthOrCallback === 'function') {
1287 callback = encodingLengthOrCallback;
1288 lengthOrEncoding = undefined;
1289 } else {
1290 lengthOrEncoding = encodingLengthOrCallback;
1291 }
1292 if (typeof positionOrCallback === 'function') {
1293 callback = positionOrCallback;
1294 position = undefined;
1295 } else {
1296 position = positionOrCallback;
1297 }
1298 if (typeof callback !== 'function') {
1299 throw new ERR_INVALID_ARG_TYPE('callback', ['function'], callback);
1300 }
1301 // Because the callback expects the buffer to be returned in the callback,
1302 // we need to make sure that the buffer is not a string here rather than
1303 // relying on the transformation in the writeSync call.
1304 if (typeof buffer === 'string') {
1305 let encoding = 'utf8';
1306 if (typeof lengthOrEncoding === 'string') {
1307 encoding = lengthOrEncoding;
1308 lengthOrEncoding = undefined;
1309 }
1310 buffer = Buffer.from(buffer, encoding) as unknown as T;
1311 }
1312 
1313 const {
1314 fd: validatedFd,
1315 buffer: actualBuffer,
1316 position: actualPosition,
1317 } = validateWriteArgs(
1318 fd,
1319 buffer,
1320 offsetOrOptions,
1321 lengthOrEncoding,
1322 position
1323 );
1324 
1325 try {
1326 const written = fssync.writevSync(
1327 validatedFd,
1328 actualBuffer,
1329 actualPosition
1330 );
1331 queueMicrotask(() => {
1332 callback(null, written, buffer as unknown as T);
1333 });
1334 } catch (err) {
1335 queueMicrotask(() => {
1336 callback(err);
1337 });
1338 }
1339}
1340 
1341export function writeFile(
1342 path: number | FilePath,
1343 data: string | ArrayBufferView,
1344 optionsOrCallback: ErrorOnlyCallback | ValidEncoding | WriteFileOptions,
1345 callback?: ErrorOnlyCallback
1346): void {
1347 let options: ValidEncoding | WriteFileOptions;
1348 if (typeof optionsOrCallback === 'function') {
1349 callback = optionsOrCallback;
1350 options = {
1351 encoding: 'utf8',
1352 mode: 0o666,
1353 flag: 'w',
1354 flush: false,
1355 };
1356 } else {
1357 options = optionsOrCallback;
1358 }
1359 
1360 const {
1361 path: validatedPath,
1362 data: validatedData,
1363 append,
1364 exclusive,
1365 } = validateWriteFileArgs(path, data, options);
1366 
1367 callWithSingleArgCallback<number>(
1368 () => cffs.writeAll(validatedPath, validatedData, { append, exclusive }),
1369 callback
1370 );
1371}
1372 
1373export function writev<T extends NodeJS.ArrayBufferView>(
1374 fd: number,
1375 buffers: T[],
1376 positionOrCallback?: Position | DoubleArgCallback<number, T[]>,
1377 callback?: DoubleArgCallback<number, T[]>
1378): void {
1379 if (typeof positionOrCallback === 'function') {
1380 callback = positionOrCallback;
1381 positionOrCallback = null;
1382 }
1383 if (typeof callback !== 'function') {
1384 throw new ERR_INVALID_ARG_TYPE('callback', ['function'], callback);
1385 }
1386 
1387 fd = getValidatedFd(fd);
1388 validateBufferArray(buffers);
1389 validatePosition(positionOrCallback, 'position');
1390 
1391 try {
1392 const written = fssync.writevSync(fd, buffers, positionOrCallback);
1393 queueMicrotask(() => {
1394 callback(null, written, buffers);
1395 });
1396 } catch (err) {
1397 queueMicrotask(() => {
1398 callback(err);
1399 });
1400 }
1401}
1402 
1403export function unwatchFile(): void {
1404 // We currently do not implement file watching.
1405 throw new ERR_UNSUPPORTED_OPERATION();
1406}
1407 
1408export function watch(): void {
1409 // We currently do not implement file watching.
1410 throw new ERR_UNSUPPORTED_OPERATION();
1411}
1412 
1413export function watchFile(): void {
1414 // We currently do not implement file watching.
1415 throw new ERR_UNSUPPORTED_OPERATION();
1416}
1417 
1418export function glob(
1419 pattern: string | readonly string[],
1420 optionsOrCallback:
1421 | GlobOptions
1422 | GlobOptionsWithFileTypes
1423 | GlobOptionsWithoutFileTypes
1424 | SingleArgCallback<string[] | Dirent[]>,
1425 callback?: SingleArgCallback<string[] | Dirent[]>
1426): void {
1427 let options:
1428 | GlobOptions
1429 | GlobOptionsWithFileTypes
1430 | GlobOptionsWithoutFileTypes;
1431 if (typeof optionsOrCallback === 'function') {
1432 callback = optionsOrCallback;
1433 options = {};
1434 } else {
1435 options = optionsOrCallback;
1436 }
1437 if (callback === undefined) {
1438 throw new ERR_INVALID_ARG_TYPE('callback', ['function'], callback);
1439 }
1440 callWithSingleArgCallback(() => fssync.globSync(pattern, options), callback);
1441}
1442 
1443// An API is considered stubbed if it is not implemented by the function
1444// exists with the correct signature and throws an error if called. If
1445// a function exists that does not have the correct signature, it is
1446// not considered fully stubbed.
1447// An API is considered optimized if the API has been implemented and
1448// tested and then optimized for performance.
1449// Implemented APIs here are a bit different than in the sync version
1450// since most of these are implemented in terms of calling the sync
1451// version. We consider it implemented here if the code is present and
1452// calls the sync api even if the sync api itself it not fully implemented.
1453//
1454// (S == Stubbed, I == Implemented, T == Tested, O == Optimized)
1455// S I T O
1456// [x][x][x][x] fs.access(path[, mode], callback)
1457// [x][x][x][x] fs.chmod(path, mode, callback)
1458// [x][x][x][x] fs.chown(path, uid, gid, callback)
1459// [x][x][x][x] fs.exists(path, callback)
1460// [x][x][x][x] fs.fchmod(fd, mode, callback)
1461// [x][x][x][x] fs.fchown(fd, uid, gid, callback)
1462// [x][x][x][x] fs.futimes(fd, atime, mtime, callback)
1463// [x][x][x][x] fs.lchmod(path, mode, callback)
1464// [x][x][x][x] fs.lchown(path, uid, gid, callback)
1465// [x][x][x][x] fs.lutimes(path, atime, mtime, callback)
1466// [x][x][x][x] fs.utimes(path, atime, mtime, callback)
1467// [x][x][x][x] fs.fstat(fd[, options], callback)
1468// [x][x][x][x] fs.lstat(path[, options], callback)
1469// [x][x][x][x] fs.stat(path[, options], callback)
1470// [x][x][x][x] fs.statfs(path[, options], callback)
1471// [x][x][x][x] fs.fdatasync(fd, callback)
1472// [x][x][x][x] fs.fsync(fd, callback)
1473// [x][x][x][x] fs.link(existingPath, newPath, callback)
1474// [x][x][x][x] fs.readlink(path[, options], callback)
1475// [x][x][x][x] fs.realpath(path[, options], callback)
1476// [x][x][x][x] fs.realpath.native(path[, options], callback)
1477// [x][x][x][x] fs.symlink(target, path[, type], callback)
1478// [x][x][x][x] fs.unlink(path, callback)
1479// [x][x][x][x] fs.mkdir(path[, options], callback)
1480// [x][x][x][x] fs.mkdtemp(prefix[, options], callback)
1481// [x][x][x][x] fs.readdir(path[, options], callback)
1482// [x][x][x][x] fs.rmdir(path[, options], callback)
1483// [x][x][x][x] fs.rm(path[, options], callback)
1484// [x][x][x][x] fs.appendFile(path, data[, options], callback)
1485// [x][x][x][x] fs.close(fd[, callback])
1486// [x][x][x][x] fs.copyFile(src, dest[, mode], callback)
1487// [x][x][x][x] fs.ftruncate(fd[, len], callback)
1488// [x][x][x][x] fs.open(path[, flags[, mode]], callback)
1489// [x][x][x][x] fs.read(fd, buffer, offset, length, position, callback)
1490// [x][x][x][x] fs.read(fd[, options], callback)
1491// [x][x][x][x] fs.read(fd, buffer[, options], callback)
1492// [x][x][x][x] fs.readFile(path[, options], callback)
1493// [x][x][x][x] fs.readv(fd, buffers[, position], callback)
1494// [x][x][x][x] fs.rename(oldPath, newPath, callback)
1495// [x][x][x][x] fs.truncate(path[, len], callback)
1496// [x][x][x][x] fs.write(fd, buffer, offset[, length[, position]], callback)
1497// [x][x][x][x] fs.write(fd, buffer[, options], callback)
1498// [x][x][x][x] fs.write(fd, string[, position[, encoding]], callback)
1499// [x][x][x][x] fs.writeFile(file, data[, options], callback)
1500// [x][x][x][x] fs.writev(fd, buffers[, position], callback)//
1501// [x][x][x][x] fs.opendir(path[, options], callback)
1502// [-][-][-][-] fs.unwatchFile(filename[, listener])
1503// [-][-][-][-] fs.watch(filename[, options][, listener])
1504// [-][-][-][-] fs.watchFile(filename[, options], listener)
1505// [x][x][x][x] fs.cp(src, dest[, options], callback)
1506//
1507// [ ][ ][ ][ ] fs.createReadStream(path[, options])
1508// [ ][ ][ ][ ] fs.createWriteStream(path[, options])
1509//
1510// [x][x][x][x] fs.glob(pattern[, options], callback)
1511// [ ][ ][ ][ ] fs.openAsBlob(path[, options])