0
0
mirror of https://github.com/nodejs/node.git synced 2024-11-29 23:16:30 +01:00
nodejs/lib/fs.js
Ruben Bridgewater 50a3fe20ea
lib: rename validateMode to parseMode
The function did not only validate the mode but it returns a new
value depending on the input. Thus `validate` did not seem to be an
appropriate name.

PR-URL: https://github.com/nodejs/node/pull/26809
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
2019-03-27 17:05:12 +01:00

1944 lines
51 KiB
JavaScript

// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// Maintainers, keep in mind that ES1-style octal literals (`0666`) are not
// allowed in strict mode. Use ES6-style octal literals instead (`0o666`).
'use strict';
const { fs: constants } = internalBinding('constants');
const {
S_IFIFO,
S_IFLNK,
S_IFMT,
S_IFREG,
S_IFSOCK,
F_OK,
R_OK,
W_OK,
X_OK,
O_WRONLY,
O_SYMLINK
} = constants;
const pathModule = require('path');
const { isArrayBufferView } = require('internal/util/types');
const binding = internalBinding('fs');
const { Buffer, kMaxLength } = require('buffer');
const {
codes: {
ERR_FS_FILE_TOO_LARGE,
ERR_INVALID_ARG_VALUE,
ERR_INVALID_ARG_TYPE,
ERR_INVALID_CALLBACK
},
uvException
} = require('internal/errors');
const { FSReqCallback, statValues } = binding;
const { toPathIfFileURL } = require('internal/url');
const internalUtil = require('internal/util');
const {
copyObject,
Dirent,
getDirents,
getOptions,
nullCheck,
preprocessSymlinkDestination,
Stats,
getStatsFromBinding,
realpathCacheKey,
stringToFlags,
stringToSymlinkType,
toUnixTimestamp,
validateBuffer,
validateOffsetLengthRead,
validateOffsetLengthWrite,
validatePath
} = require('internal/fs/utils');
const {
CHAR_FORWARD_SLASH,
CHAR_BACKWARD_SLASH,
} = require('internal/constants');
const {
isUint32,
parseMode,
validateInteger,
validateInt32,
validateUint32
} = require('internal/validators');
let truncateWarn = true;
let fs;
// Lazy loaded
let promises = null;
let watchers;
let ReadFileContext;
let ReadStream;
let WriteStream;
// These have to be separate because of how graceful-fs happens to do it's
// monkeypatching.
let FileReadStream;
let FileWriteStream;
const isWindows = process.platform === 'win32';
function showTruncateDeprecation() {
if (truncateWarn) {
process.emitWarning(
'Using fs.truncate with a file descriptor is deprecated. Please use ' +
'fs.ftruncate with a file descriptor instead.',
'DeprecationWarning', 'DEP0081');
truncateWarn = false;
}
}
function handleErrorFromBinding(ctx) {
if (ctx.errno !== undefined) { // libuv error numbers
const err = uvException(ctx);
// eslint-disable-next-line no-restricted-syntax
Error.captureStackTrace(err, handleErrorFromBinding);
throw err;
}
if (ctx.error !== undefined) { // errors created in C++ land.
// TODO(joyeecheung): currently, ctx.error are encoding errors
// usually caused by memory problems. We need to figure out proper error
// code(s) for this.
// eslint-disable-next-line no-restricted-syntax
Error.captureStackTrace(ctx.error, handleErrorFromBinding);
throw ctx.error;
}
}
function maybeCallback(cb) {
if (typeof cb === 'function')
return cb;
throw new ERR_INVALID_CALLBACK();
}
// Ensure that callbacks run in the global context. Only use this function
// for callbacks that are passed to the binding layer, callbacks that are
// invoked from JS already run in the proper scope.
function makeCallback(cb) {
if (typeof cb !== 'function') {
throw new ERR_INVALID_CALLBACK();
}
return (...args) => {
return Reflect.apply(cb, undefined, args);
};
}
// Special case of `makeCallback()` that is specific to async `*stat()` calls as
// an optimization, since the data passed back to the callback needs to be
// transformed anyway.
function makeStatsCallback(cb) {
if (typeof cb !== 'function') {
throw new ERR_INVALID_CALLBACK();
}
return (err, stats) => {
if (err) return cb(err);
cb(err, getStatsFromBinding(stats));
};
}
const isFd = isUint32;
function isFileType(stats, fileType) {
// Use stats array directly to avoid creating an fs.Stats instance just for
// our internal use.
return (stats[1/* mode */] & S_IFMT) === fileType;
}
function access(path, mode, callback) {
if (typeof mode === 'function') {
callback = mode;
mode = F_OK;
}
path = toPathIfFileURL(path);
validatePath(path);
mode = mode | 0;
const req = new FSReqCallback();
req.oncomplete = makeCallback(callback);
binding.access(pathModule.toNamespacedPath(path), mode, req);
}
function accessSync(path, mode) {
path = toPathIfFileURL(path);
validatePath(path);
if (mode === undefined)
mode = F_OK;
else
mode = mode | 0;
const ctx = { path };
binding.access(pathModule.toNamespacedPath(path), mode, undefined, ctx);
handleErrorFromBinding(ctx);
}
function exists(path, callback) {
maybeCallback(callback);
function suppressedCallback(err) {
callback(err ? false : true);
}
try {
fs.access(path, F_OK, suppressedCallback);
} catch {
return callback(false);
}
}
Object.defineProperty(exists, internalUtil.promisify.custom, {
value: (path) => {
return new Promise((resolve) => fs.exists(path, resolve));
}
});
// fs.existsSync never throws, it only returns true or false.
// Since fs.existsSync never throws, users have established
// the expectation that passing invalid arguments to it, even like
// fs.existsSync(), would only get a false in return, so we cannot signal
// validation errors to users properly out of compatibility concerns.
// TODO(joyeecheung): deprecate the never-throw-on-invalid-arguments behavior
function existsSync(path) {
try {
path = toPathIfFileURL(path);
validatePath(path);
} catch {
return false;
}
const ctx = { path };
binding.access(pathModule.toNamespacedPath(path), F_OK, undefined, ctx);
return ctx.errno === undefined;
}
function readFileAfterOpen(err, fd) {
const context = this.context;
if (err) {
context.callback(err);
return;
}
context.fd = fd;
const req = new FSReqCallback();
req.oncomplete = readFileAfterStat;
req.context = context;
binding.fstat(fd, false, req);
}
function readFileAfterStat(err, stats) {
const context = this.context;
if (err)
return context.close(err);
const size = context.size = isFileType(stats, S_IFREG) ? stats[8] : 0;
if (size === 0) {
context.buffers = [];
context.read();
return;
}
if (size > kMaxLength) {
err = new ERR_FS_FILE_TOO_LARGE(size);
return context.close(err);
}
try {
context.buffer = Buffer.allocUnsafeSlow(size);
} catch (err) {
return context.close(err);
}
context.read();
}
function readFile(path, options, callback) {
callback = maybeCallback(callback || options);
options = getOptions(options, { flag: 'r' });
if (!ReadFileContext)
ReadFileContext = require('internal/fs/read_file_context');
const context = new ReadFileContext(callback, options.encoding);
context.isUserFd = isFd(path); // file descriptor ownership
const req = new FSReqCallback();
req.context = context;
req.oncomplete = readFileAfterOpen;
if (context.isUserFd) {
process.nextTick(function tick() {
req.oncomplete(null, path);
});
return;
}
path = toPathIfFileURL(path);
validatePath(path);
binding.open(pathModule.toNamespacedPath(path),
stringToFlags(options.flag || 'r'),
0o666,
req);
}
function tryStatSync(fd, isUserFd) {
const ctx = {};
const stats = binding.fstat(fd, false, undefined, ctx);
if (ctx.errno !== undefined && !isUserFd) {
fs.closeSync(fd);
throw uvException(ctx);
}
return stats;
}
function tryCreateBuffer(size, fd, isUserFd) {
let threw = true;
let buffer;
try {
if (size > kMaxLength) {
throw new ERR_FS_FILE_TOO_LARGE(size);
}
buffer = Buffer.allocUnsafe(size);
threw = false;
} finally {
if (threw && !isUserFd) fs.closeSync(fd);
}
return buffer;
}
function tryReadSync(fd, isUserFd, buffer, pos, len) {
let threw = true;
let bytesRead;
try {
bytesRead = fs.readSync(fd, buffer, pos, len);
threw = false;
} finally {
if (threw && !isUserFd) fs.closeSync(fd);
}
return bytesRead;
}
function readFileSync(path, options) {
options = getOptions(options, { flag: 'r' });
const isUserFd = isFd(path); // file descriptor ownership
const fd = isUserFd ? path : fs.openSync(path, options.flag, 0o666);
const stats = tryStatSync(fd, isUserFd);
const size = isFileType(stats, S_IFREG) ? stats[8] : 0;
let pos = 0;
let buffer; // single buffer with file data
let buffers; // list for when size is unknown
if (size === 0) {
buffers = [];
} else {
buffer = tryCreateBuffer(size, fd, isUserFd);
}
let bytesRead;
if (size !== 0) {
do {
bytesRead = tryReadSync(fd, isUserFd, buffer, pos, size - pos);
pos += bytesRead;
} while (bytesRead !== 0 && pos < size);
} else {
do {
// The kernel lies about many files.
// Go ahead and try to read some bytes.
buffer = Buffer.allocUnsafe(8192);
bytesRead = tryReadSync(fd, isUserFd, buffer, 0, 8192);
if (bytesRead !== 0) {
buffers.push(buffer.slice(0, bytesRead));
}
pos += bytesRead;
} while (bytesRead !== 0);
}
if (!isUserFd)
fs.closeSync(fd);
if (size === 0) {
// Data was collected into the buffers list.
buffer = Buffer.concat(buffers, pos);
} else if (pos < size) {
buffer = buffer.slice(0, pos);
}
if (options.encoding) buffer = buffer.toString(options.encoding);
return buffer;
}
function close(fd, callback) {
validateUint32(fd, 'fd');
const req = new FSReqCallback();
req.oncomplete = makeCallback(callback);
binding.close(fd, req);
}
function closeSync(fd) {
validateUint32(fd, 'fd');
const ctx = {};
binding.close(fd, undefined, ctx);
handleErrorFromBinding(ctx);
}
function open(path, flags, mode, callback) {
path = toPathIfFileURL(path);
validatePath(path);
if (arguments.length < 3) {
callback = flags;
flags = 'r';
mode = 0o666;
} else if (typeof mode === 'function') {
callback = mode;
mode = 0o666;
}
const flagsNumber = stringToFlags(flags);
if (arguments.length >= 4) {
mode = parseMode(mode, 'mode', 0o666);
}
callback = makeCallback(callback);
const req = new FSReqCallback();
req.oncomplete = callback;
binding.open(pathModule.toNamespacedPath(path),
flagsNumber,
mode,
req);
}
function openSync(path, flags, mode) {
path = toPathIfFileURL(path);
validatePath(path);
const flagsNumber = stringToFlags(flags || 'r');
mode = parseMode(mode, 'mode', 0o666);
const ctx = { path };
const result = binding.open(pathModule.toNamespacedPath(path),
flagsNumber, mode,
undefined, ctx);
handleErrorFromBinding(ctx);
return result;
}
function read(fd, buffer, offset, length, position, callback) {
validateUint32(fd, 'fd');
validateBuffer(buffer);
callback = maybeCallback(callback);
offset |= 0;
length |= 0;
if (length === 0) {
return process.nextTick(function tick() {
callback(null, 0, buffer);
});
}
if (buffer.byteLength === 0) {
throw new ERR_INVALID_ARG_VALUE('buffer', buffer,
'is empty and cannot be written');
}
validateOffsetLengthRead(offset, length, buffer.byteLength);
if (!Number.isSafeInteger(position))
position = -1;
function wrapper(err, bytesRead) {
// Retain a reference to buffer so that it can't be GC'ed too soon.
callback(err, bytesRead || 0, buffer);
}
const req = new FSReqCallback();
req.oncomplete = wrapper;
binding.read(fd, buffer, offset, length, position, req);
}
Object.defineProperty(read, internalUtil.customPromisifyArgs,
{ value: ['bytesRead', 'buffer'], enumerable: false });
function readSync(fd, buffer, offset, length, position) {
validateUint32(fd, 'fd');
validateBuffer(buffer);
offset |= 0;
length |= 0;
if (length === 0) {
return 0;
}
if (buffer.byteLength === 0) {
throw new ERR_INVALID_ARG_VALUE('buffer', buffer,
'is empty and cannot be written');
}
validateOffsetLengthRead(offset, length, buffer.byteLength);
if (!Number.isSafeInteger(position))
position = -1;
const ctx = {};
const result = binding.read(fd, buffer, offset, length, position,
undefined, ctx);
handleErrorFromBinding(ctx);
return result;
}
// usage:
// fs.write(fd, buffer[, offset[, length[, position]]], callback);
// OR
// fs.write(fd, string[, position[, encoding]], callback);
function write(fd, buffer, offset, length, position, callback) {
function wrapper(err, written) {
// Retain a reference to buffer so that it can't be GC'ed too soon.
callback(err, written || 0, buffer);
}
validateUint32(fd, 'fd');
const req = new FSReqCallback();
req.oncomplete = wrapper;
if (isArrayBufferView(buffer)) {
callback = maybeCallback(callback || position || length || offset);
if (typeof offset !== 'number')
offset = 0;
if (typeof length !== 'number')
length = buffer.length - offset;
if (typeof position !== 'number')
position = null;
validateOffsetLengthWrite(offset, length, buffer.byteLength);
return binding.writeBuffer(fd, buffer, offset, length, position, req);
}
if (typeof buffer !== 'string')
buffer += '';
if (typeof position !== 'function') {
if (typeof offset === 'function') {
position = offset;
offset = null;
} else {
position = length;
}
length = 'utf8';
}
callback = maybeCallback(position);
return binding.writeString(fd, buffer, offset, length, req);
}
Object.defineProperty(write, internalUtil.customPromisifyArgs,
{ value: ['bytesWritten', 'buffer'], enumerable: false });
// usage:
// fs.writeSync(fd, buffer[, offset[, length[, position]]]);
// OR
// fs.writeSync(fd, string[, position[, encoding]]);
function writeSync(fd, buffer, offset, length, position) {
validateUint32(fd, 'fd');
const ctx = {};
let result;
if (isArrayBufferView(buffer)) {
if (position === undefined)
position = null;
if (typeof offset !== 'number')
offset = 0;
if (typeof length !== 'number')
length = buffer.byteLength - offset;
validateOffsetLengthWrite(offset, length, buffer.byteLength);
result = binding.writeBuffer(fd, buffer, offset, length, position,
undefined, ctx);
} else {
if (typeof buffer !== 'string')
buffer += '';
if (offset === undefined)
offset = null;
result = binding.writeString(fd, buffer, offset, length,
undefined, ctx);
}
handleErrorFromBinding(ctx);
return result;
}
function rename(oldPath, newPath, callback) {
callback = makeCallback(callback);
oldPath = toPathIfFileURL(oldPath);
validatePath(oldPath, 'oldPath');
newPath = toPathIfFileURL(newPath);
validatePath(newPath, 'newPath');
const req = new FSReqCallback();
req.oncomplete = callback;
binding.rename(pathModule.toNamespacedPath(oldPath),
pathModule.toNamespacedPath(newPath),
req);
}
function renameSync(oldPath, newPath) {
oldPath = toPathIfFileURL(oldPath);
validatePath(oldPath, 'oldPath');
newPath = toPathIfFileURL(newPath);
validatePath(newPath, 'newPath');
const ctx = { path: oldPath, dest: newPath };
binding.rename(pathModule.toNamespacedPath(oldPath),
pathModule.toNamespacedPath(newPath), undefined, ctx);
handleErrorFromBinding(ctx);
}
function truncate(path, len, callback) {
if (typeof path === 'number') {
showTruncateDeprecation();
return fs.ftruncate(path, len, callback);
}
if (typeof len === 'function') {
callback = len;
len = 0;
} else if (len === undefined) {
len = 0;
}
validateInteger(len, 'len');
callback = maybeCallback(callback);
fs.open(path, 'r+', (er, fd) => {
if (er) return callback(er);
const req = new FSReqCallback();
req.oncomplete = function oncomplete(er) {
fs.close(fd, (er2) => {
callback(er || er2);
});
};
binding.ftruncate(fd, len, req);
});
}
function truncateSync(path, len) {
if (typeof path === 'number') {
// legacy
showTruncateDeprecation();
return fs.ftruncateSync(path, len);
}
if (len === undefined) {
len = 0;
}
// Allow error to be thrown, but still close fd.
const fd = fs.openSync(path, 'r+');
let ret;
try {
ret = fs.ftruncateSync(fd, len);
} finally {
fs.closeSync(fd);
}
return ret;
}
function ftruncate(fd, len = 0, callback) {
if (typeof len === 'function') {
callback = len;
len = 0;
}
validateUint32(fd, 'fd');
validateInteger(len, 'len');
len = Math.max(0, len);
const req = new FSReqCallback();
req.oncomplete = makeCallback(callback);
binding.ftruncate(fd, len, req);
}
function ftruncateSync(fd, len = 0) {
validateUint32(fd, 'fd');
validateInteger(len, 'len');
len = Math.max(0, len);
const ctx = {};
binding.ftruncate(fd, len, undefined, ctx);
handleErrorFromBinding(ctx);
}
function rmdir(path, callback) {
callback = makeCallback(callback);
path = toPathIfFileURL(path);
validatePath(path);
const req = new FSReqCallback();
req.oncomplete = callback;
binding.rmdir(pathModule.toNamespacedPath(path), req);
}
function rmdirSync(path) {
path = toPathIfFileURL(path);
validatePath(path);
const ctx = { path };
binding.rmdir(pathModule.toNamespacedPath(path), undefined, ctx);
handleErrorFromBinding(ctx);
}
function fdatasync(fd, callback) {
validateUint32(fd, 'fd');
const req = new FSReqCallback();
req.oncomplete = makeCallback(callback);
binding.fdatasync(fd, req);
}
function fdatasyncSync(fd) {
validateUint32(fd, 'fd');
const ctx = {};
binding.fdatasync(fd, undefined, ctx);
handleErrorFromBinding(ctx);
}
function fsync(fd, callback) {
validateUint32(fd, 'fd');
const req = new FSReqCallback();
req.oncomplete = makeCallback(callback);
binding.fsync(fd, req);
}
function fsyncSync(fd) {
validateUint32(fd, 'fd');
const ctx = {};
binding.fsync(fd, undefined, ctx);
handleErrorFromBinding(ctx);
}
function mkdir(path, options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
} else if (typeof options === 'number' || typeof options === 'string') {
options = { mode: options };
}
const {
recursive = false,
mode = 0o777
} = options || {};
callback = makeCallback(callback);
path = toPathIfFileURL(path);
validatePath(path);
if (typeof recursive !== 'boolean')
throw new ERR_INVALID_ARG_TYPE('recursive', 'boolean', recursive);
const req = new FSReqCallback();
req.oncomplete = callback;
binding.mkdir(pathModule.toNamespacedPath(path),
parseMode(mode, 'mode', 0o777), recursive, req);
}
function mkdirSync(path, options) {
if (typeof options === 'number' || typeof options === 'string') {
options = { mode: options };
}
path = toPathIfFileURL(path);
const {
recursive = false,
mode = 0o777
} = options || {};
validatePath(path);
if (typeof recursive !== 'boolean')
throw new ERR_INVALID_ARG_TYPE('recursive', 'boolean', recursive);
const ctx = { path };
binding.mkdir(pathModule.toNamespacedPath(path),
parseMode(mode, 'mode', 0o777), recursive, undefined,
ctx);
handleErrorFromBinding(ctx);
}
function readdir(path, options, callback) {
callback = makeCallback(typeof options === 'function' ? options : callback);
options = getOptions(options, {});
path = toPathIfFileURL(path);
validatePath(path);
const req = new FSReqCallback();
if (!options.withFileTypes) {
req.oncomplete = callback;
} else {
req.oncomplete = (err, result) => {
if (err) {
callback(err);
return;
}
getDirents(path, result, callback);
};
}
binding.readdir(pathModule.toNamespacedPath(path), options.encoding,
!!options.withFileTypes, req);
}
function readdirSync(path, options) {
options = getOptions(options, {});
path = toPathIfFileURL(path);
validatePath(path);
const ctx = { path };
const result = binding.readdir(pathModule.toNamespacedPath(path),
options.encoding, !!options.withFileTypes,
undefined, ctx);
handleErrorFromBinding(ctx);
return options.withFileTypes ? getDirents(path, result) : result;
}
function fstat(fd, options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
validateUint32(fd, 'fd');
const req = new FSReqCallback(options.bigint);
req.oncomplete = makeStatsCallback(callback);
binding.fstat(fd, options.bigint, req);
}
function lstat(path, options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
callback = makeStatsCallback(callback);
path = toPathIfFileURL(path);
validatePath(path);
const req = new FSReqCallback(options.bigint);
req.oncomplete = callback;
binding.lstat(pathModule.toNamespacedPath(path), options.bigint, req);
}
function stat(path, options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
callback = makeStatsCallback(callback);
path = toPathIfFileURL(path);
validatePath(path);
const req = new FSReqCallback(options.bigint);
req.oncomplete = callback;
binding.stat(pathModule.toNamespacedPath(path), options.bigint, req);
}
function fstatSync(fd, options = {}) {
validateUint32(fd, 'fd');
const ctx = { fd };
const stats = binding.fstat(fd, options.bigint, undefined, ctx);
handleErrorFromBinding(ctx);
return getStatsFromBinding(stats);
}
function lstatSync(path, options = {}) {
path = toPathIfFileURL(path);
validatePath(path);
const ctx = { path };
const stats = binding.lstat(pathModule.toNamespacedPath(path),
options.bigint, undefined, ctx);
handleErrorFromBinding(ctx);
return getStatsFromBinding(stats);
}
function statSync(path, options = {}) {
path = toPathIfFileURL(path);
validatePath(path);
const ctx = { path };
const stats = binding.stat(pathModule.toNamespacedPath(path),
options.bigint, undefined, ctx);
handleErrorFromBinding(ctx);
return getStatsFromBinding(stats);
}
function readlink(path, options, callback) {
callback = makeCallback(typeof options === 'function' ? options : callback);
options = getOptions(options, {});
path = toPathIfFileURL(path);
validatePath(path, 'oldPath');
const req = new FSReqCallback();
req.oncomplete = callback;
binding.readlink(pathModule.toNamespacedPath(path), options.encoding, req);
}
function readlinkSync(path, options) {
options = getOptions(options, {});
path = toPathIfFileURL(path);
validatePath(path, 'oldPath');
const ctx = { path };
const result = binding.readlink(pathModule.toNamespacedPath(path),
options.encoding, undefined, ctx);
handleErrorFromBinding(ctx);
return result;
}
function symlink(target, path, type_, callback_) {
const type = (typeof type_ === 'string' ? type_ : null);
const callback = makeCallback(arguments[arguments.length - 1]);
target = toPathIfFileURL(target);
path = toPathIfFileURL(path);
validatePath(target, 'target');
validatePath(path);
const req = new FSReqCallback();
req.oncomplete = callback;
if (isWindows && type === null) {
let absoluteTarget;
try {
// Symlinks targets can be relative to the newly created path.
// Calculate absolute file name of the symlink target, and check
// if it is a directory. Ignore resolve error to keep symlink
// errors consistent between platforms if invalid path is
// provided.
absoluteTarget = pathModule.resolve(path, '..', target);
} catch { }
if (absoluteTarget !== undefined) {
stat(absoluteTarget, (err, stat) => {
const resolvedType = !err && stat.isDirectory() ? 'dir' : 'file';
const resolvedFlags = stringToSymlinkType(resolvedType);
binding.symlink(preprocessSymlinkDestination(target,
resolvedType,
path),
pathModule.toNamespacedPath(path), resolvedFlags, req);
});
return;
}
}
const flags = stringToSymlinkType(type);
binding.symlink(preprocessSymlinkDestination(target, type, path),
pathModule.toNamespacedPath(path), flags, req);
}
function symlinkSync(target, path, type) {
type = (typeof type === 'string' ? type : null);
if (isWindows && type === null) {
try {
const absoluteTarget = pathModule.resolve(path, '..', target);
if (statSync(absoluteTarget).isDirectory()) {
type = 'dir';
}
} catch { }
}
target = toPathIfFileURL(target);
path = toPathIfFileURL(path);
validatePath(target, 'target');
validatePath(path);
const flags = stringToSymlinkType(type);
const ctx = { path: target, dest: path };
binding.symlink(preprocessSymlinkDestination(target, type, path),
pathModule.toNamespacedPath(path), flags, undefined, ctx);
handleErrorFromBinding(ctx);
}
function link(existingPath, newPath, callback) {
callback = makeCallback(callback);
existingPath = toPathIfFileURL(existingPath);
newPath = toPathIfFileURL(newPath);
validatePath(existingPath, 'existingPath');
validatePath(newPath, 'newPath');
const req = new FSReqCallback();
req.oncomplete = callback;
binding.link(pathModule.toNamespacedPath(existingPath),
pathModule.toNamespacedPath(newPath),
req);
}
function linkSync(existingPath, newPath) {
existingPath = toPathIfFileURL(existingPath);
newPath = toPathIfFileURL(newPath);
validatePath(existingPath, 'existingPath');
validatePath(newPath, 'newPath');
const ctx = { path: existingPath, dest: newPath };
const result = binding.link(pathModule.toNamespacedPath(existingPath),
pathModule.toNamespacedPath(newPath),
undefined, ctx);
handleErrorFromBinding(ctx);
return result;
}
function unlink(path, callback) {
callback = makeCallback(callback);
path = toPathIfFileURL(path);
validatePath(path);
const req = new FSReqCallback();
req.oncomplete = callback;
binding.unlink(pathModule.toNamespacedPath(path), req);
}
function unlinkSync(path) {
path = toPathIfFileURL(path);
validatePath(path);
const ctx = { path };
binding.unlink(pathModule.toNamespacedPath(path), undefined, ctx);
handleErrorFromBinding(ctx);
}
function fchmod(fd, mode, callback) {
validateInt32(fd, 'fd', 0);
mode = parseMode(mode, 'mode');
callback = makeCallback(callback);
const req = new FSReqCallback();
req.oncomplete = callback;
binding.fchmod(fd, mode, req);
}
function fchmodSync(fd, mode) {
validateInt32(fd, 'fd', 0);
mode = parseMode(mode, 'mode');
const ctx = {};
binding.fchmod(fd, mode, undefined, ctx);
handleErrorFromBinding(ctx);
}
function lchmod(path, mode, callback) {
callback = maybeCallback(callback);
fs.open(path, O_WRONLY | O_SYMLINK, (err, fd) => {
if (err) {
callback(err);
return;
}
// Prefer to return the chmod error, if one occurs,
// but still try to close, and report closing errors if they occur.
fs.fchmod(fd, mode, (err) => {
fs.close(fd, (err2) => {
callback(err || err2);
});
});
});
}
function lchmodSync(path, mode) {
const fd = fs.openSync(path, O_WRONLY | O_SYMLINK);
// Prefer to return the chmod error, if one occurs,
// but still try to close, and report closing errors if they occur.
let ret;
try {
ret = fs.fchmodSync(fd, mode);
} finally {
fs.closeSync(fd);
}
return ret;
}
function chmod(path, mode, callback) {
path = toPathIfFileURL(path);
validatePath(path);
mode = parseMode(mode, 'mode');
callback = makeCallback(callback);
const req = new FSReqCallback();
req.oncomplete = callback;
binding.chmod(pathModule.toNamespacedPath(path), mode, req);
}
function chmodSync(path, mode) {
path = toPathIfFileURL(path);
validatePath(path);
mode = parseMode(mode, 'mode');
const ctx = { path };
binding.chmod(pathModule.toNamespacedPath(path), mode, undefined, ctx);
handleErrorFromBinding(ctx);
}
function lchown(path, uid, gid, callback) {
callback = makeCallback(callback);
path = toPathIfFileURL(path);
validatePath(path);
validateUint32(uid, 'uid');
validateUint32(gid, 'gid');
const req = new FSReqCallback();
req.oncomplete = callback;
binding.lchown(pathModule.toNamespacedPath(path), uid, gid, req);
}
function lchownSync(path, uid, gid) {
path = toPathIfFileURL(path);
validatePath(path);
validateUint32(uid, 'uid');
validateUint32(gid, 'gid');
const ctx = { path };
binding.lchown(pathModule.toNamespacedPath(path), uid, gid, undefined, ctx);
handleErrorFromBinding(ctx);
}
function fchown(fd, uid, gid, callback) {
validateUint32(fd, 'fd');
validateUint32(uid, 'uid');
validateUint32(gid, 'gid');
const req = new FSReqCallback();
req.oncomplete = makeCallback(callback);
binding.fchown(fd, uid, gid, req);
}
function fchownSync(fd, uid, gid) {
validateUint32(fd, 'fd');
validateUint32(uid, 'uid');
validateUint32(gid, 'gid');
const ctx = {};
binding.fchown(fd, uid, gid, undefined, ctx);
handleErrorFromBinding(ctx);
}
function chown(path, uid, gid, callback) {
callback = makeCallback(callback);
path = toPathIfFileURL(path);
validatePath(path);
validateUint32(uid, 'uid');
validateUint32(gid, 'gid');
const req = new FSReqCallback();
req.oncomplete = callback;
binding.chown(pathModule.toNamespacedPath(path), uid, gid, req);
}
function chownSync(path, uid, gid) {
path = toPathIfFileURL(path);
validatePath(path);
validateUint32(uid, 'uid');
validateUint32(gid, 'gid');
const ctx = { path };
binding.chown(pathModule.toNamespacedPath(path), uid, gid, undefined, ctx);
handleErrorFromBinding(ctx);
}
function utimes(path, atime, mtime, callback) {
callback = makeCallback(callback);
path = toPathIfFileURL(path);
validatePath(path);
const req = new FSReqCallback();
req.oncomplete = callback;
binding.utimes(pathModule.toNamespacedPath(path),
toUnixTimestamp(atime),
toUnixTimestamp(mtime),
req);
}
function utimesSync(path, atime, mtime) {
path = toPathIfFileURL(path);
validatePath(path);
const ctx = { path };
binding.utimes(pathModule.toNamespacedPath(path),
toUnixTimestamp(atime), toUnixTimestamp(mtime),
undefined, ctx);
handleErrorFromBinding(ctx);
}
function futimes(fd, atime, mtime, callback) {
validateUint32(fd, 'fd');
atime = toUnixTimestamp(atime, 'atime');
mtime = toUnixTimestamp(mtime, 'mtime');
const req = new FSReqCallback();
req.oncomplete = makeCallback(callback);
binding.futimes(fd, atime, mtime, req);
}
function futimesSync(fd, atime, mtime) {
validateUint32(fd, 'fd');
atime = toUnixTimestamp(atime, 'atime');
mtime = toUnixTimestamp(mtime, 'mtime');
const ctx = {};
binding.futimes(fd, atime, mtime, undefined, ctx);
handleErrorFromBinding(ctx);
}
function writeAll(fd, isUserFd, buffer, offset, length, position, callback) {
// write(fd, buffer, offset, length, position, callback)
fs.write(fd, buffer, offset, length, position, (writeErr, written) => {
if (writeErr) {
if (isUserFd) {
callback(writeErr);
} else {
fs.close(fd, function close() {
callback(writeErr);
});
}
} else if (written === length) {
if (isUserFd) {
callback(null);
} else {
fs.close(fd, callback);
}
} else {
offset += written;
length -= written;
if (position !== null) {
position += written;
}
writeAll(fd, isUserFd, buffer, offset, length, position, callback);
}
});
}
function writeFile(path, data, options, callback) {
callback = maybeCallback(callback || options);
options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'w' });
const flag = options.flag || 'w';
if (isFd(path)) {
writeFd(path, true);
return;
}
fs.open(path, flag, options.mode, (openErr, fd) => {
if (openErr) {
callback(openErr);
} else {
writeFd(fd, false);
}
});
function writeFd(fd, isUserFd) {
const buffer = isArrayBufferView(data) ?
data : Buffer.from('' + data, options.encoding || 'utf8');
const position = (/a/.test(flag) || isUserFd) ? null : 0;
writeAll(fd, isUserFd, buffer, 0, buffer.byteLength, position, callback);
}
}
function writeFileSync(path, data, options) {
options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'w' });
const flag = options.flag || 'w';
const isUserFd = isFd(path); // file descriptor ownership
const fd = isUserFd ? path : fs.openSync(path, flag, options.mode);
if (!isArrayBufferView(data)) {
data = Buffer.from('' + data, options.encoding || 'utf8');
}
let offset = 0;
let length = data.byteLength;
let position = (/a/.test(flag) || isUserFd) ? null : 0;
try {
while (length > 0) {
const written = fs.writeSync(fd, data, offset, length, position);
offset += written;
length -= written;
if (position !== null) {
position += written;
}
}
} finally {
if (!isUserFd) fs.closeSync(fd);
}
}
function appendFile(path, data, options, callback) {
callback = maybeCallback(callback || options);
options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'a' });
// Don't make changes directly on options object
options = copyObject(options);
// Force append behavior when using a supplied file descriptor
if (!options.flag || isFd(path))
options.flag = 'a';
fs.writeFile(path, data, options, callback);
}
function appendFileSync(path, data, options) {
options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'a' });
// Don't make changes directly on options object
options = copyObject(options);
// Force append behavior when using a supplied file descriptor
if (!options.flag || isFd(path))
options.flag = 'a';
fs.writeFileSync(path, data, options);
}
function watch(filename, options, listener) {
if (typeof options === 'function') {
listener = options;
}
options = getOptions(options, {});
// Don't make changes directly on options object
options = copyObject(options);
if (options.persistent === undefined) options.persistent = true;
if (options.recursive === undefined) options.recursive = false;
if (!watchers)
watchers = require('internal/fs/watchers');
const watcher = new watchers.FSWatcher();
watcher.start(filename,
options.persistent,
options.recursive,
options.encoding);
if (listener) {
watcher.addListener('change', listener);
}
return watcher;
}
const statWatchers = new Map();
function watchFile(filename, options, listener) {
filename = toPathIfFileURL(filename);
validatePath(filename);
filename = pathModule.resolve(filename);
let stat;
if (options === null || typeof options !== 'object') {
listener = options;
options = null;
}
options = {
// Poll interval in milliseconds. 5007 is what libev used to use. It's
// a little on the slow side but let's stick with it for now to keep
// behavioral changes to a minimum.
interval: 5007,
persistent: true,
...options
};
if (typeof listener !== 'function') {
throw new ERR_INVALID_ARG_TYPE('listener', 'Function', listener);
}
stat = statWatchers.get(filename);
if (stat === undefined) {
if (!watchers)
watchers = require('internal/fs/watchers');
stat = new watchers.StatWatcher(options.bigint);
stat.start(filename, options.persistent, options.interval);
statWatchers.set(filename, stat);
}
stat.addListener('change', listener);
return stat;
}
function unwatchFile(filename, listener) {
filename = toPathIfFileURL(filename);
validatePath(filename);
filename = pathModule.resolve(filename);
const stat = statWatchers.get(filename);
if (stat === undefined) return;
if (typeof listener === 'function') {
stat.removeListener('change', listener);
} else {
stat.removeAllListeners('change');
}
if (stat.listenerCount('change') === 0) {
stat.stop();
statWatchers.delete(filename);
}
}
let splitRoot;
if (isWindows) {
// Regex to find the device root on Windows (e.g. 'c:\\'), including trailing
// slash.
const splitRootRe = /^(?:[a-zA-Z]:|[\\/]{2}[^\\/]+[\\/][^\\/]+)?[\\/]*/;
splitRoot = function splitRoot(str) {
return splitRootRe.exec(str)[0];
};
} else {
splitRoot = function splitRoot(str) {
for (var i = 0; i < str.length; ++i) {
if (str.charCodeAt(i) !== CHAR_FORWARD_SLASH)
return str.slice(0, i);
}
return str;
};
}
function encodeRealpathResult(result, options) {
if (!options || !options.encoding || options.encoding === 'utf8')
return result;
const asBuffer = Buffer.from(result);
if (options.encoding === 'buffer') {
return asBuffer;
} else {
return asBuffer.toString(options.encoding);
}
}
// Finds the next portion of a (partial) path, up to the next path delimiter
let nextPart;
if (isWindows) {
nextPart = function nextPart(p, i) {
for (; i < p.length; ++i) {
const ch = p.charCodeAt(i);
// Check for a separator character
if (ch === CHAR_BACKWARD_SLASH || ch === CHAR_FORWARD_SLASH)
return i;
}
return -1;
};
} else {
nextPart = function nextPart(p, i) { return p.indexOf('/', i); };
}
const emptyObj = Object.create(null);
function realpathSync(p, options) {
if (!options)
options = emptyObj;
else
options = getOptions(options, emptyObj);
p = toPathIfFileURL(p);
if (typeof p !== 'string') {
p += '';
}
validatePath(p);
p = pathModule.resolve(p);
const cache = options[realpathCacheKey];
const maybeCachedResult = cache && cache.get(p);
if (maybeCachedResult) {
return maybeCachedResult;
}
const seenLinks = Object.create(null);
const knownHard = Object.create(null);
const original = p;
// Current character position in p
let pos;
// The partial path so far, including a trailing slash if any
let current;
// The partial path without a trailing slash (except when pointing at a root)
let base;
// The partial path scanned in the previous round, with slash
let previous;
// Skip over roots
current = base = splitRoot(p);
pos = current.length;
// On windows, check that the root exists. On unix there is no need.
if (isWindows && !knownHard[base]) {
const ctx = { path: base };
binding.lstat(pathModule.toNamespacedPath(base), false, undefined, ctx);
handleErrorFromBinding(ctx);
knownHard[base] = true;
}
// Walk down the path, swapping out linked path parts for their real
// values
// NB: p.length changes.
while (pos < p.length) {
// find the next part
const result = nextPart(p, pos);
previous = current;
if (result === -1) {
const last = p.slice(pos);
current += last;
base = previous + last;
pos = p.length;
} else {
current += p.slice(pos, result + 1);
base = previous + p.slice(pos, result);
pos = result + 1;
}
// Continue if not a symlink, break if a pipe/socket
if (knownHard[base] || (cache && cache.get(base) === base)) {
if (isFileType(statValues, S_IFIFO) ||
isFileType(statValues, S_IFSOCK)) {
break;
}
continue;
}
let resolvedLink;
const maybeCachedResolved = cache && cache.get(base);
if (maybeCachedResolved) {
resolvedLink = maybeCachedResolved;
} else {
// Use stats array directly to avoid creating an fs.Stats instance just
// for our internal use.
const baseLong = pathModule.toNamespacedPath(base);
const ctx = { path: base };
const stats = binding.lstat(baseLong, false, undefined, ctx);
handleErrorFromBinding(ctx);
if (!isFileType(stats, S_IFLNK)) {
knownHard[base] = true;
if (cache) cache.set(base, base);
continue;
}
// Read the link if it wasn't read before
// dev/ino always return 0 on windows, so skip the check.
let linkTarget = null;
let id;
if (!isWindows) {
const dev = stats[0].toString(32);
const ino = stats[7].toString(32);
id = `${dev}:${ino}`;
if (seenLinks[id]) {
linkTarget = seenLinks[id];
}
}
if (linkTarget === null) {
const ctx = { path: base };
binding.stat(baseLong, false, undefined, ctx);
handleErrorFromBinding(ctx);
linkTarget = binding.readlink(baseLong, undefined, undefined, ctx);
handleErrorFromBinding(ctx);
}
resolvedLink = pathModule.resolve(previous, linkTarget);
if (cache) cache.set(base, resolvedLink);
if (!isWindows) seenLinks[id] = linkTarget;
}
// Resolve the link, then start over
p = pathModule.resolve(resolvedLink, p.slice(pos));
// Skip over roots
current = base = splitRoot(p);
pos = current.length;
// On windows, check that the root exists. On unix there is no need.
if (isWindows && !knownHard[base]) {
const ctx = { path: base };
binding.lstat(pathModule.toNamespacedPath(base), false, undefined, ctx);
handleErrorFromBinding(ctx);
knownHard[base] = true;
}
}
if (cache) cache.set(original, p);
return encodeRealpathResult(p, options);
}
realpathSync.native = (path, options) => {
options = getOptions(options, {});
path = toPathIfFileURL(path);
validatePath(path);
const ctx = { path };
const result = binding.realpath(path, options.encoding, undefined, ctx);
handleErrorFromBinding(ctx);
return result;
};
function realpath(p, options, callback) {
callback = typeof options === 'function' ? options : maybeCallback(callback);
if (!options)
options = emptyObj;
else
options = getOptions(options, emptyObj);
p = toPathIfFileURL(p);
if (typeof p !== 'string') {
p += '';
}
validatePath(p);
p = pathModule.resolve(p);
const seenLinks = Object.create(null);
const knownHard = Object.create(null);
// Current character position in p
let pos;
// The partial path so far, including a trailing slash if any
let current;
// The partial path without a trailing slash (except when pointing at a root)
let base;
// The partial path scanned in the previous round, with slash
let previous;
current = base = splitRoot(p);
pos = current.length;
// On windows, check that the root exists. On unix there is no need.
if (isWindows && !knownHard[base]) {
fs.lstat(base, (err, stats) => {
if (err) return callback(err);
knownHard[base] = true;
LOOP();
});
} else {
process.nextTick(LOOP);
}
// Walk down the path, swapping out linked path parts for their real
// values
function LOOP() {
// Stop if scanned past end of path
if (pos >= p.length) {
return callback(null, encodeRealpathResult(p, options));
}
// find the next part
const result = nextPart(p, pos);
previous = current;
if (result === -1) {
const last = p.slice(pos);
current += last;
base = previous + last;
pos = p.length;
} else {
current += p.slice(pos, result + 1);
base = previous + p.slice(pos, result);
pos = result + 1;
}
// Continue if not a symlink, break if a pipe/socket
if (knownHard[base]) {
if (isFileType(statValues, S_IFIFO) ||
isFileType(statValues, S_IFSOCK)) {
return callback(null, encodeRealpathResult(p, options));
}
return process.nextTick(LOOP);
}
return fs.lstat(base, gotStat);
}
function gotStat(err, stats) {
if (err) return callback(err);
// If not a symlink, skip to the next path part
if (!stats.isSymbolicLink()) {
knownHard[base] = true;
return process.nextTick(LOOP);
}
// Stat & read the link if not read before.
// Call `gotTarget()` as soon as the link target is known.
// `dev`/`ino` always return 0 on windows, so skip the check.
let id;
if (!isWindows) {
const dev = stats.dev.toString(32);
const ino = stats.ino.toString(32);
id = `${dev}:${ino}`;
if (seenLinks[id]) {
return gotTarget(null, seenLinks[id], base);
}
}
fs.stat(base, (err) => {
if (err) return callback(err);
fs.readlink(base, (err, target) => {
if (!isWindows) seenLinks[id] = target;
gotTarget(err, target);
});
});
}
function gotTarget(err, target, base) {
if (err) return callback(err);
gotResolvedLink(pathModule.resolve(previous, target));
}
function gotResolvedLink(resolvedLink) {
// Resolve the link, then start over
p = pathModule.resolve(resolvedLink, p.slice(pos));
current = base = splitRoot(p);
pos = current.length;
// On windows, check that the root exists. On unix there is no need.
if (isWindows && !knownHard[base]) {
fs.lstat(base, (err) => {
if (err) return callback(err);
knownHard[base] = true;
LOOP();
});
} else {
process.nextTick(LOOP);
}
}
}
realpath.native = (path, options, callback) => {
callback = makeCallback(callback || options);
options = getOptions(options, {});
path = toPathIfFileURL(path);
validatePath(path);
const req = new FSReqCallback();
req.oncomplete = callback;
return binding.realpath(path, options.encoding, req);
};
function mkdtemp(prefix, options, callback) {
callback = makeCallback(typeof options === 'function' ? options : callback);
options = getOptions(options, {});
if (!prefix || typeof prefix !== 'string') {
throw new ERR_INVALID_ARG_TYPE('prefix', 'string', prefix);
}
nullCheck(prefix, 'prefix');
const req = new FSReqCallback();
req.oncomplete = callback;
binding.mkdtemp(`${prefix}XXXXXX`, options.encoding, req);
}
function mkdtempSync(prefix, options) {
options = getOptions(options, {});
if (!prefix || typeof prefix !== 'string') {
throw new ERR_INVALID_ARG_TYPE('prefix', 'string', prefix);
}
nullCheck(prefix, 'prefix');
const path = `${prefix}XXXXXX`;
const ctx = { path };
const result = binding.mkdtemp(path, options.encoding,
undefined, ctx);
handleErrorFromBinding(ctx);
return result;
}
function copyFile(src, dest, flags, callback) {
if (typeof flags === 'function') {
callback = flags;
flags = 0;
} else if (typeof callback !== 'function') {
throw new ERR_INVALID_CALLBACK();
}
src = toPathIfFileURL(src);
dest = toPathIfFileURL(dest);
validatePath(src, 'src');
validatePath(dest, 'dest');
src = pathModule._makeLong(src);
dest = pathModule._makeLong(dest);
flags = flags | 0;
const req = new FSReqCallback();
req.oncomplete = makeCallback(callback);
binding.copyFile(src, dest, flags, req);
}
function copyFileSync(src, dest, flags) {
src = toPathIfFileURL(src);
dest = toPathIfFileURL(dest);
validatePath(src, 'src');
validatePath(dest, 'dest');
const ctx = { path: src, dest }; // non-prefixed
src = pathModule._makeLong(src);
dest = pathModule._makeLong(dest);
flags = flags | 0;
binding.copyFile(src, dest, flags, undefined, ctx);
handleErrorFromBinding(ctx);
}
function lazyLoadStreams() {
if (!ReadStream) {
({ ReadStream, WriteStream } = require('internal/fs/streams'));
[ FileReadStream, FileWriteStream ] = [ ReadStream, WriteStream ];
}
}
function createReadStream(path, options) {
lazyLoadStreams();
return new ReadStream(path, options);
}
function createWriteStream(path, options) {
lazyLoadStreams();
return new WriteStream(path, options);
}
module.exports = fs = {
appendFile,
appendFileSync,
access,
accessSync,
chown,
chownSync,
chmod,
chmodSync,
close,
closeSync,
copyFile,
copyFileSync,
createReadStream,
createWriteStream,
exists,
existsSync,
fchown,
fchownSync,
fchmod,
fchmodSync,
fdatasync,
fdatasyncSync,
fstat,
fstatSync,
fsync,
fsyncSync,
ftruncate,
ftruncateSync,
futimes,
futimesSync,
lchown,
lchownSync,
lchmod: constants.O_SYMLINK !== undefined ? lchmod : undefined,
lchmodSync: constants.O_SYMLINK !== undefined ? lchmodSync : undefined,
link,
linkSync,
lstat,
lstatSync,
mkdir,
mkdirSync,
mkdtemp,
mkdtempSync,
open,
openSync,
readdir,
readdirSync,
read,
readSync,
readFile,
readFileSync,
readlink,
readlinkSync,
realpath,
realpathSync,
rename,
renameSync,
rmdir,
rmdirSync,
stat,
statSync,
symlink,
symlinkSync,
truncate,
truncateSync,
unwatchFile,
unlink,
unlinkSync,
utimes,
utimesSync,
watch,
watchFile,
writeFile,
writeFileSync,
write,
writeSync,
Dirent,
Stats,
get ReadStream() {
lazyLoadStreams();
return ReadStream;
},
set ReadStream(val) {
ReadStream = val;
},
get WriteStream() {
lazyLoadStreams();
return WriteStream;
},
set WriteStream(val) {
WriteStream = val;
},
// Legacy names... these have to be separate because of how graceful-fs
// (and possibly other) modules monkey patch the values.
get FileReadStream() {
lazyLoadStreams();
return FileReadStream;
},
set FileReadStream(val) {
FileReadStream = val;
},
get FileWriteStream() {
lazyLoadStreams();
return FileWriteStream;
},
set FileWriteStream(val) {
FileWriteStream = val;
},
// For tests
_toUnixTimestamp: toUnixTimestamp
};
Object.defineProperties(fs, {
F_OK: { enumerable: true, value: F_OK || 0 },
R_OK: { enumerable: true, value: R_OK || 0 },
W_OK: { enumerable: true, value: W_OK || 0 },
X_OK: { enumerable: true, value: X_OK || 0 },
constants: {
configurable: false,
enumerable: true,
value: constants
},
promises: {
configurable: true,
enumerable: false,
get() {
if (promises === null) {
promises = require('internal/fs/promises');
process.emitWarning('The fs.promises API is experimental',
'ExperimentalWarning');
}
return promises;
}
}
});