2017-01-03 13:16:48 -08:00
|
|
|
// 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.
|
|
|
|
|
2015-01-10 23:12:37 +03:00
|
|
|
// Maintainers, keep in mind that ES1-style octal literals (`0666`) are not
|
|
|
|
// allowed in strict mode. Use ES6-style octal literals instead (`0o666`).
|
2011-11-04 13:10:07 +01:00
|
|
|
|
2014-11-22 16:59:48 +01:00
|
|
|
'use strict';
|
|
|
|
|
2016-05-02 10:27:12 -07:00
|
|
|
const constants = process.binding('constants').fs;
|
2017-05-15 03:27:54 +04:30
|
|
|
const { S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK } = constants;
|
2015-01-21 11:36:59 -05:00
|
|
|
const util = require('util');
|
|
|
|
const pathModule = require('path');
|
2017-09-28 00:16:41 -07:00
|
|
|
const { isUint8Array } = require('internal/util/types');
|
|
|
|
const { createPromise, promiseResolve } = process.binding('util');
|
2010-03-05 19:24:20 +01:00
|
|
|
|
2015-01-21 11:36:59 -05:00
|
|
|
const binding = process.binding('fs');
|
|
|
|
const fs = exports;
|
2017-10-07 22:50:42 +08:00
|
|
|
const { Buffer } = require('buffer');
|
2017-08-26 07:46:47 -04:00
|
|
|
const errors = require('internal/errors');
|
2017-10-07 22:50:42 +08:00
|
|
|
const { Readable, Writable } = require('stream');
|
2015-09-17 04:15:29 +05:30
|
|
|
const EventEmitter = require('events');
|
2017-10-07 22:50:42 +08:00
|
|
|
const { FSReqWrap } = binding;
|
|
|
|
const { FSEvent } = process.binding('fs_event_wrap');
|
2016-08-30 09:53:50 -07:00
|
|
|
const internalFS = require('internal/fs');
|
2017-10-07 22:50:42 +08:00
|
|
|
const { getPathFromURL } = require('internal/url');
|
2016-12-26 18:38:05 -08:00
|
|
|
const internalUtil = require('internal/util');
|
2017-10-07 22:50:42 +08:00
|
|
|
const {
|
|
|
|
assertEncoding,
|
|
|
|
stringToFlags
|
|
|
|
} = internalFS;
|
2010-03-11 14:32:10 -08:00
|
|
|
|
2016-05-02 10:27:12 -07:00
|
|
|
Object.defineProperty(exports, 'constants', {
|
|
|
|
configurable: false,
|
|
|
|
enumerable: true,
|
|
|
|
value: constants
|
|
|
|
});
|
|
|
|
|
2018-01-21 10:21:25 -08:00
|
|
|
const kHandle = Symbol('handle');
|
|
|
|
const { kUsePromises } = binding;
|
|
|
|
|
2015-01-19 23:01:42 -05:00
|
|
|
const kMinPoolSpace = 128;
|
2017-10-07 22:50:42 +08:00
|
|
|
const { kMaxLength } = require('buffer');
|
2010-05-24 15:47:40 -07:00
|
|
|
|
2015-01-21 11:36:59 -05:00
|
|
|
const isWindows = process.platform === 'win32';
|
2012-05-22 16:02:10 -07:00
|
|
|
|
2017-05-11 18:52:20 +03:00
|
|
|
const DEBUG = process.env.NODE_DEBUG && /fs/.test(process.env.NODE_DEBUG);
|
2018-02-03 17:09:15 +08:00
|
|
|
const errnoException = errors.errnoException;
|
2013-07-16 23:28:38 +02:00
|
|
|
|
2017-10-06 11:06:35 -07:00
|
|
|
let truncateWarn = true;
|
|
|
|
|
2017-12-13 14:24:34 -08:00
|
|
|
function isInt32(n) { return n === (n | 0); }
|
|
|
|
function isUint32(n) { return n === (n >>> 0); }
|
|
|
|
|
2017-10-06 11:06:35 -07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-26 00:03:05 +05:30
|
|
|
function getOptions(options, defaultOptions) {
|
|
|
|
if (options === null || options === undefined ||
|
|
|
|
typeof options === 'function') {
|
|
|
|
return defaultOptions;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof options === 'string') {
|
|
|
|
defaultOptions = util._extend({}, defaultOptions);
|
|
|
|
defaultOptions.encoding = options;
|
|
|
|
options = defaultOptions;
|
|
|
|
} else if (typeof options !== 'object') {
|
2017-08-26 07:46:47 -04:00
|
|
|
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
|
|
|
|
'options',
|
2017-10-28 17:39:55 +08:00
|
|
|
['string', 'Object'],
|
2017-08-26 07:46:47 -04:00
|
|
|
options);
|
2016-06-26 00:03:05 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
if (options.encoding !== 'buffer')
|
|
|
|
assertEncoding(options.encoding);
|
|
|
|
return options;
|
2015-06-02 20:43:46 +05:30
|
|
|
}
|
2012-12-04 03:35:54 +01:00
|
|
|
|
2016-12-01 11:48:07 -06:00
|
|
|
function copyObject(source) {
|
2017-04-24 02:20:45 -04:00
|
|
|
var target = {};
|
|
|
|
for (var key in source)
|
2016-10-09 19:37:59 +05:30
|
|
|
target[key] = source[key];
|
|
|
|
return target;
|
|
|
|
}
|
|
|
|
|
2018-01-23 10:23:46 +08:00
|
|
|
// TODO(joyeecheung): explore how the deprecation could be solved via linting
|
|
|
|
// rules. See https://github.com/nodejs/node/pull/12976
|
2017-05-11 18:52:20 +03:00
|
|
|
function rethrow() {
|
|
|
|
process.emitWarning(
|
|
|
|
'Calling an asynchronous function without callback is deprecated.',
|
|
|
|
'DeprecationWarning', 'DEP0013', rethrow
|
|
|
|
);
|
|
|
|
|
|
|
|
// Only enable in debug mode. A backtrace uses ~1000 bytes of heap space and
|
|
|
|
// is fairly slow to generate.
|
|
|
|
if (DEBUG) {
|
|
|
|
var backtrace = new Error();
|
|
|
|
return function(err) {
|
|
|
|
if (err) {
|
2017-11-10 18:17:17 +05:30
|
|
|
backtrace.stack = `${err.name}: ${err.message}` +
|
2017-05-11 18:52:20 +03:00
|
|
|
backtrace.stack.substr(backtrace.name.length);
|
|
|
|
throw backtrace;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
return function(err) {
|
|
|
|
if (err) {
|
|
|
|
throw err; // Forgot a callback but don't know where? Use NODE_DEBUG=fs
|
|
|
|
}
|
|
|
|
};
|
2016-07-22 16:38:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
function maybeCallback(cb) {
|
2017-05-11 18:52:20 +03:00
|
|
|
return typeof cb === 'function' ? cb : rethrow();
|
2016-07-22 16:38:36 -07:00
|
|
|
}
|
|
|
|
|
2012-12-04 03:17:52 +01:00
|
|
|
// 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) {
|
2017-05-11 18:52:20 +03:00
|
|
|
if (cb === undefined) {
|
|
|
|
return rethrow();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof cb !== 'function') {
|
2017-08-26 07:46:47 -04:00
|
|
|
throw new errors.TypeError('ERR_INVALID_CALLBACK');
|
2017-05-11 18:52:20 +03:00
|
|
|
}
|
2015-02-18 12:55:13 -05:00
|
|
|
|
2017-12-06 23:15:34 +05:30
|
|
|
return function(...args) {
|
|
|
|
return Reflect.apply(cb, undefined, args);
|
2012-12-04 03:17:52 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2017-12-13 19:54:09 -05:00
|
|
|
function validateBuffer(buffer) {
|
|
|
|
if (!isUint8Array(buffer)) {
|
|
|
|
const err = new errors.TypeError('ERR_INVALID_ARG_TYPE', 'buffer',
|
|
|
|
['Buffer', 'Uint8Array']);
|
|
|
|
Error.captureStackTrace(err, validateBuffer);
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-22 19:19:33 -05:00
|
|
|
function validateLen(len) {
|
2017-12-13 17:24:56 -05:00
|
|
|
let err;
|
|
|
|
|
2017-12-22 19:19:33 -05:00
|
|
|
if (!isInt32(len))
|
|
|
|
err = new errors.TypeError('ERR_INVALID_ARG_TYPE', 'len', 'integer');
|
2017-12-13 17:24:56 -05:00
|
|
|
|
|
|
|
if (err !== undefined) {
|
2017-12-22 19:19:33 -05:00
|
|
|
Error.captureStackTrace(err, validateLen);
|
2017-12-13 17:24:56 -05:00
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-13 17:29:03 -05:00
|
|
|
function validateOffsetLengthRead(offset, length, bufferLength) {
|
|
|
|
let err;
|
|
|
|
|
|
|
|
if (offset < 0 || offset >= bufferLength) {
|
|
|
|
err = new errors.RangeError('ERR_OUT_OF_RANGE', 'offset');
|
|
|
|
} else if (length < 0 || offset + length > bufferLength) {
|
|
|
|
err = new errors.RangeError('ERR_OUT_OF_RANGE', 'length');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err !== undefined) {
|
|
|
|
Error.captureStackTrace(err, validateOffsetLengthRead);
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-22 18:33:31 -05:00
|
|
|
function validateOffsetLengthWrite(offset, length, byteLength) {
|
|
|
|
let err;
|
|
|
|
|
|
|
|
if (offset > byteLength) {
|
|
|
|
err = new errors.RangeError('ERR_OUT_OF_RANGE', 'offset');
|
|
|
|
} else if (offset + length > byteLength || offset + length > kMaxLength) {
|
|
|
|
err = new errors.RangeError('ERR_OUT_OF_RANGE', 'length');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err !== undefined) {
|
|
|
|
Error.captureStackTrace(err, validateOffsetLengthWrite);
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-23 10:23:46 +08:00
|
|
|
// Check if the path contains null types if it is a string nor Uint8Array,
|
|
|
|
// otherwise return silently.
|
|
|
|
function nullCheck(path, propName, throwError = true) {
|
|
|
|
const pathIsString = typeof path === 'string';
|
|
|
|
const pathIsUint8Array = isUint8Array(path);
|
|
|
|
|
|
|
|
// We can only perform meaningful checks on strings and Uint8Arrays.
|
|
|
|
if (!pathIsString && !pathIsUint8Array) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pathIsString && path.indexOf('\u0000') === -1) {
|
|
|
|
return;
|
|
|
|
} else if (pathIsUint8Array && path.indexOf(0) === -1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const err = new errors.Error(
|
|
|
|
'ERR_INVALID_ARG_VALUE', propName, path,
|
|
|
|
'must be a string or Uint8Array without null bytes');
|
|
|
|
|
|
|
|
if (throwError) {
|
|
|
|
Error.captureStackTrace(err, nullCheck);
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2017-12-22 18:42:21 -05:00
|
|
|
function validatePath(path, propName) {
|
|
|
|
let err;
|
|
|
|
|
|
|
|
if (propName === undefined) {
|
|
|
|
propName = 'path';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof path !== 'string' && !isUint8Array(path)) {
|
|
|
|
err = new errors.TypeError('ERR_INVALID_ARG_TYPE', propName,
|
|
|
|
['string', 'Buffer', 'URL']);
|
2018-01-23 10:23:46 +08:00
|
|
|
} else {
|
|
|
|
err = nullCheck(path, propName, false);
|
2017-12-22 18:42:21 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (err !== undefined) {
|
|
|
|
Error.captureStackTrace(err, validatePath);
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-22 19:19:33 -05:00
|
|
|
function validateUint32(value, propName) {
|
|
|
|
let err;
|
|
|
|
|
|
|
|
if (!isUint32(value))
|
|
|
|
err = new errors.TypeError('ERR_INVALID_ARG_TYPE', propName, 'integer');
|
|
|
|
|
|
|
|
if (err !== undefined) {
|
|
|
|
Error.captureStackTrace(err, validateUint32);
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-11 19:41:20 -05:00
|
|
|
// 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) {
|
2017-05-11 18:52:20 +03:00
|
|
|
if (cb === undefined) {
|
|
|
|
return rethrow();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof cb !== 'function') {
|
2017-08-26 07:46:47 -04:00
|
|
|
throw new errors.TypeError('ERR_INVALID_CALLBACK');
|
2017-05-11 18:52:20 +03:00
|
|
|
}
|
2017-03-11 19:41:20 -05:00
|
|
|
|
|
|
|
return function(err) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
cb(err, statsFromValues());
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2016-07-22 16:37:54 -07:00
|
|
|
function isFd(path) {
|
|
|
|
return (path >>> 0) === path;
|
|
|
|
}
|
|
|
|
|
2017-03-11 19:41:20 -05:00
|
|
|
// Constructor for file stats.
|
2017-02-23 12:45:10 -05:00
|
|
|
function Stats(
|
2017-07-05 09:21:40 -07:00
|
|
|
dev,
|
|
|
|
mode,
|
|
|
|
nlink,
|
|
|
|
uid,
|
|
|
|
gid,
|
|
|
|
rdev,
|
|
|
|
blksize,
|
|
|
|
ino,
|
|
|
|
size,
|
|
|
|
blocks,
|
|
|
|
atim_msec,
|
|
|
|
mtim_msec,
|
|
|
|
ctim_msec,
|
|
|
|
birthtim_msec
|
|
|
|
) {
|
2014-02-27 15:45:18 -05:00
|
|
|
this.dev = dev;
|
|
|
|
this.mode = mode;
|
|
|
|
this.nlink = nlink;
|
|
|
|
this.uid = uid;
|
|
|
|
this.gid = gid;
|
|
|
|
this.rdev = rdev;
|
2014-04-14 16:35:33 -07:00
|
|
|
this.blksize = blksize;
|
2014-02-27 15:45:18 -05:00
|
|
|
this.ino = ino;
|
|
|
|
this.size = size;
|
|
|
|
this.blocks = blocks;
|
2017-05-23 12:15:56 -04:00
|
|
|
this.atimeMs = atim_msec;
|
|
|
|
this.mtimeMs = mtim_msec;
|
|
|
|
this.ctimeMs = ctim_msec;
|
|
|
|
this.birthtimeMs = birthtim_msec;
|
2017-05-27 20:41:48 +02:00
|
|
|
this.atime = new Date(atim_msec + 0.5);
|
|
|
|
this.mtime = new Date(mtim_msec + 0.5);
|
|
|
|
this.ctime = new Date(ctim_msec + 0.5);
|
|
|
|
this.birthtime = new Date(birthtim_msec + 0.5);
|
2017-02-23 12:45:10 -05:00
|
|
|
}
|
|
|
|
fs.Stats = Stats;
|
2014-02-27 15:45:18 -05:00
|
|
|
|
2017-03-11 19:41:20 -05:00
|
|
|
Stats.prototype._checkModeProperty = function(property) {
|
|
|
|
return ((this.mode & S_IFMT) === property);
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2017-03-11 19:41:20 -05:00
|
|
|
Stats.prototype.isDirectory = function() {
|
2010-09-16 23:13:03 -07:00
|
|
|
return this._checkModeProperty(constants.S_IFDIR);
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2017-03-11 19:41:20 -05:00
|
|
|
Stats.prototype.isFile = function() {
|
|
|
|
return this._checkModeProperty(S_IFREG);
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2017-03-11 19:41:20 -05:00
|
|
|
Stats.prototype.isBlockDevice = function() {
|
2010-09-16 23:13:03 -07:00
|
|
|
return this._checkModeProperty(constants.S_IFBLK);
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2017-03-11 19:41:20 -05:00
|
|
|
Stats.prototype.isCharacterDevice = function() {
|
2010-09-16 23:13:03 -07:00
|
|
|
return this._checkModeProperty(constants.S_IFCHR);
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2017-03-11 19:41:20 -05:00
|
|
|
Stats.prototype.isSymbolicLink = function() {
|
|
|
|
return this._checkModeProperty(S_IFLNK);
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2017-03-11 19:41:20 -05:00
|
|
|
Stats.prototype.isFIFO = function() {
|
2017-05-15 03:27:54 +04:30
|
|
|
return this._checkModeProperty(S_IFIFO);
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2017-03-11 19:41:20 -05:00
|
|
|
Stats.prototype.isSocket = function() {
|
2017-05-15 03:27:54 +04:30
|
|
|
return this._checkModeProperty(S_IFSOCK);
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2018-01-21 14:16:47 +08:00
|
|
|
const statValues = binding.statValues;
|
2017-03-11 19:41:20 -05:00
|
|
|
|
2018-01-21 10:21:25 -08:00
|
|
|
function statsFromValues(stats = statValues) {
|
|
|
|
return new Stats(stats[0], stats[1], stats[2], stats[3], stats[4], stats[5],
|
|
|
|
stats[6] < 0 ? undefined : stats[6], stats[7], stats[8],
|
|
|
|
stats[9] < 0 ? undefined : stats[9], stats[10], stats[11],
|
|
|
|
stats[12], stats[13]);
|
2017-03-11 19:41:20 -05:00
|
|
|
}
|
|
|
|
|
2015-01-19 18:12:16 +08:00
|
|
|
// Don't allow mode to accidentally be overwritten.
|
2017-02-25 22:52:16 -08:00
|
|
|
Object.defineProperties(fs, {
|
2017-07-10 20:55:21 -04:00
|
|
|
F_OK: { enumerable: true, value: constants.F_OK || 0 },
|
|
|
|
R_OK: { enumerable: true, value: constants.R_OK || 0 },
|
|
|
|
W_OK: { enumerable: true, value: constants.W_OK || 0 },
|
|
|
|
X_OK: { enumerable: true, value: constants.X_OK || 0 },
|
2015-01-19 18:12:16 +08:00
|
|
|
});
|
2014-12-15 10:44:46 -05:00
|
|
|
|
|
|
|
fs.access = function(path, mode, callback) {
|
|
|
|
if (typeof mode === 'function') {
|
2016-07-22 16:38:36 -07:00
|
|
|
callback = mode;
|
2015-01-19 18:12:16 +08:00
|
|
|
mode = fs.F_OK;
|
2017-05-11 18:52:20 +03:00
|
|
|
} else if (typeof callback !== 'function') {
|
2017-08-26 07:46:47 -04:00
|
|
|
throw new errors.TypeError('ERR_INVALID_CALLBACK');
|
2014-12-15 10:44:46 -05:00
|
|
|
}
|
|
|
|
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(path);
|
2017-11-21 04:47:57 +08:00
|
|
|
|
2014-12-15 10:44:46 -05:00
|
|
|
mode = mode | 0;
|
|
|
|
var req = new FSReqWrap();
|
2017-05-11 18:52:20 +03:00
|
|
|
req.oncomplete = makeCallback(callback);
|
2017-08-20 22:44:47 -07:00
|
|
|
binding.access(pathModule.toNamespacedPath(path), mode, req);
|
2014-12-15 10:44:46 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
fs.accessSync = function(path, mode) {
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(path);
|
2014-12-15 10:44:46 -05:00
|
|
|
|
|
|
|
if (mode === undefined)
|
2015-01-19 18:12:16 +08:00
|
|
|
mode = fs.F_OK;
|
2014-12-15 10:44:46 -05:00
|
|
|
else
|
|
|
|
mode = mode | 0;
|
|
|
|
|
2017-11-27 13:08:05 +09:00
|
|
|
const ctx = { path };
|
2017-11-21 04:47:57 +08:00
|
|
|
binding.access(pathModule.toNamespacedPath(path), mode, undefined, ctx);
|
|
|
|
|
2017-11-27 13:08:05 +09:00
|
|
|
if (ctx.errno !== undefined) {
|
2018-02-03 17:35:04 +08:00
|
|
|
throw errors.uvException(ctx);
|
2017-11-21 04:47:57 +08:00
|
|
|
}
|
2014-12-15 10:44:46 -05:00
|
|
|
};
|
|
|
|
|
2018-01-23 10:23:46 +08:00
|
|
|
// fs.exists never throws even when the arguments are invalid - if there is
|
|
|
|
// a callback it would invoke it with false, otherwise it emits a warning
|
|
|
|
// (see the comments of rethrow()).
|
|
|
|
// This is to bring it inline with fs.existsSync, which never throws.
|
|
|
|
// TODO(joyeecheung): deprecate the never-throw-on-invalid-arguments behavior
|
2015-01-12 09:24:30 -05:00
|
|
|
fs.exists = function(path, callback) {
|
2018-01-23 10:23:46 +08:00
|
|
|
if (typeof callback !== 'function') {
|
|
|
|
rethrow();
|
2017-01-11 07:02:40 -08:00
|
|
|
return;
|
2018-01-23 10:23:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
function suppressedCallback(err) {
|
|
|
|
callback(err ? false : true);
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
path = getPathFromURL(path);
|
|
|
|
validatePath(path);
|
|
|
|
} catch (err) {
|
|
|
|
return callback(false);
|
|
|
|
}
|
2014-12-09 05:29:47 +01:00
|
|
|
var req = new FSReqWrap();
|
2018-01-23 10:23:46 +08:00
|
|
|
req.oncomplete = suppressedCallback;
|
2017-08-20 22:44:47 -07:00
|
|
|
binding.stat(pathModule.toNamespacedPath(path), req);
|
2015-01-12 09:24:30 -05:00
|
|
|
};
|
2012-01-21 02:37:57 +01:00
|
|
|
|
2017-05-30 14:34:27 -07:00
|
|
|
Object.defineProperty(fs.exists, internalUtil.promisify.custom, {
|
|
|
|
value: (path) => {
|
|
|
|
const promise = createPromise();
|
|
|
|
fs.exists(path, (exists) => promiseResolve(promise, exists));
|
|
|
|
return promise;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2018-01-23 10:23:46 +08:00
|
|
|
// 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
|
2015-01-12 09:24:30 -05:00
|
|
|
fs.existsSync = function(path) {
|
2012-01-21 02:37:57 +01:00
|
|
|
try {
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
|
|
|
validatePath(path);
|
2017-12-28 04:01:45 +08:00
|
|
|
const ctx = { path };
|
|
|
|
binding.stat(pathModule.toNamespacedPath(path), undefined, ctx);
|
|
|
|
if (ctx.errno !== undefined) {
|
|
|
|
return false;
|
|
|
|
}
|
2012-01-21 02:37:57 +01:00
|
|
|
return true;
|
|
|
|
} catch (e) {
|
|
|
|
return false;
|
|
|
|
}
|
2015-01-12 09:24:30 -05:00
|
|
|
};
|
2012-01-21 02:37:57 +01:00
|
|
|
|
2016-06-26 00:03:05 +05:30
|
|
|
fs.readFile = function(path, options, callback) {
|
2017-04-24 02:20:45 -04:00
|
|
|
callback = maybeCallback(callback || options);
|
2016-06-26 00:03:05 +05:30
|
|
|
options = getOptions(options, { flag: 'r' });
|
|
|
|
var context = new ReadFileContext(callback, options.encoding);
|
2016-07-22 16:37:54 -07:00
|
|
|
context.isUserFd = isFd(path); // file descriptor ownership
|
2015-02-03 22:07:02 +03:00
|
|
|
var req = new FSReqWrap();
|
|
|
|
req.context = context;
|
|
|
|
req.oncomplete = readFileAfterOpen;
|
2010-12-01 17:43:30 -08:00
|
|
|
|
2015-10-03 02:06:42 +02:00
|
|
|
if (context.isUserFd) {
|
|
|
|
process.nextTick(function() {
|
|
|
|
req.oncomplete(null, path);
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(path);
|
2017-08-20 22:44:47 -07:00
|
|
|
binding.open(pathModule.toNamespacedPath(path),
|
2016-06-26 00:03:05 +05:30
|
|
|
stringToFlags(options.flag || 'r'),
|
2015-02-03 22:07:02 +03:00
|
|
|
0o666,
|
|
|
|
req);
|
|
|
|
};
|
|
|
|
|
|
|
|
const kReadFileBufferLength = 8 * 1024;
|
|
|
|
|
|
|
|
function ReadFileContext(callback, encoding) {
|
|
|
|
this.fd = undefined;
|
2015-10-03 02:06:42 +02:00
|
|
|
this.isUserFd = undefined;
|
2015-02-03 22:07:02 +03:00
|
|
|
this.size = undefined;
|
|
|
|
this.callback = callback;
|
|
|
|
this.buffers = null;
|
|
|
|
this.buffer = null;
|
|
|
|
this.pos = 0;
|
|
|
|
this.encoding = encoding;
|
|
|
|
this.err = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
ReadFileContext.prototype.read = function() {
|
|
|
|
var buffer;
|
|
|
|
var offset;
|
|
|
|
var length;
|
|
|
|
|
2015-06-03 03:41:04 +05:30
|
|
|
if (this.size === 0) {
|
2016-03-21 12:38:08 -07:00
|
|
|
buffer = this.buffer = Buffer.allocUnsafeSlow(kReadFileBufferLength);
|
2015-02-03 22:07:02 +03:00
|
|
|
offset = 0;
|
|
|
|
length = kReadFileBufferLength;
|
|
|
|
} else {
|
|
|
|
buffer = this.buffer;
|
|
|
|
offset = this.pos;
|
fs: partition readFile against pool exhaustion
Problem:
Node implements fs.readFile as:
- a call to stat, then
- a C++ -> libuv request to read the entire file using the stat size
Why is this bad?
The effect is to place on the libuv threadpool a potentially-large
read request, occupying the libuv thread until it completes.
While readFile certainly requires buffering the entire file contents,
it can partition the read into smaller buffers
(as is done on other read paths)
along the way to avoid threadpool exhaustion.
If the file is relatively large or stored on a slow medium, reading
the entire file in one shot seems particularly harmful,
and presents a possible DoS vector.
Solution:
Partition the read into multiple smaller requests.
Considerations:
1. Correctness
I don't think partitioning the read like this raises
any additional risk of read-write races on the FS.
If the application is concurrently readFile'ing and modifying the file,
it will already see funny behavior. Though libuv uses preadv where
available, this doesn't guarantee read atomicity in the presence of
concurrent writes.
2. Performance
Downside: Partitioning means that a single large readFile will
require into many "out and back" requests to libuv,
introducing overhead.
Upside: In between each "out and back", other work pending on the
threadpool can take a turn.
In short, although partitioning will slow down a large request,
it will lead to better throughput if the threadpool is handling
more than one type of request.
Fixes: https://github.com/nodejs/node/issues/17047
PR-URL: https://github.com/nodejs/node/pull/17054
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Sakthipriyan Vairamani <thechargingvolcano@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
2017-11-15 12:28:04 -05:00
|
|
|
length = Math.min(kReadFileBufferLength, this.size - this.pos);
|
2012-05-15 17:35:42 -07:00
|
|
|
}
|
2012-04-06 02:54:33 +02:00
|
|
|
|
2015-02-03 22:07:02 +03:00
|
|
|
var req = new FSReqWrap();
|
|
|
|
req.oncomplete = readFileAfterRead;
|
|
|
|
req.context = this;
|
2012-05-15 17:35:42 -07:00
|
|
|
|
2017-01-15 00:44:19 +05:30
|
|
|
binding.read(this.fd, buffer, offset, length, -1, req);
|
2015-02-03 22:07:02 +03:00
|
|
|
};
|
2012-06-12 15:32:40 +02:00
|
|
|
|
2015-02-03 22:07:02 +03:00
|
|
|
ReadFileContext.prototype.close = function(err) {
|
|
|
|
var req = new FSReqWrap();
|
|
|
|
req.oncomplete = readFileAfterClose;
|
|
|
|
req.context = this;
|
|
|
|
this.err = err;
|
2015-10-03 02:06:42 +02:00
|
|
|
|
|
|
|
if (this.isUserFd) {
|
|
|
|
process.nextTick(function() {
|
|
|
|
req.oncomplete(null);
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-02-03 22:07:02 +03:00
|
|
|
binding.close(this.fd, req);
|
|
|
|
};
|
|
|
|
|
|
|
|
function readFileAfterOpen(err, fd) {
|
|
|
|
var context = this.context;
|
|
|
|
|
|
|
|
if (err) {
|
2015-06-03 03:41:04 +05:30
|
|
|
context.callback(err);
|
2015-02-03 22:07:02 +03:00
|
|
|
return;
|
2012-05-15 17:35:42 -07:00
|
|
|
}
|
|
|
|
|
2015-02-03 22:07:02 +03:00
|
|
|
context.fd = fd;
|
2012-06-11 14:49:31 -07:00
|
|
|
|
2015-02-03 22:07:02 +03:00
|
|
|
var req = new FSReqWrap();
|
|
|
|
req.oncomplete = readFileAfterStat;
|
|
|
|
req.context = context;
|
|
|
|
binding.fstat(fd, req);
|
|
|
|
}
|
|
|
|
|
2017-03-11 19:41:20 -05:00
|
|
|
function readFileAfterStat(err) {
|
2015-02-03 22:07:02 +03:00
|
|
|
var context = this.context;
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
return context.close(err);
|
|
|
|
|
2017-03-11 19:41:20 -05:00
|
|
|
// Use stats array directly to avoid creating an fs.Stats instance just for
|
|
|
|
// our internal use.
|
|
|
|
var size;
|
|
|
|
if ((statValues[1/*mode*/] & S_IFMT) === S_IFREG)
|
|
|
|
size = context.size = statValues[8/*size*/];
|
|
|
|
else
|
|
|
|
size = context.size = 0;
|
2015-02-03 22:07:02 +03:00
|
|
|
|
|
|
|
if (size === 0) {
|
|
|
|
context.buffers = [];
|
|
|
|
context.read();
|
|
|
|
return;
|
2012-05-15 17:35:42 -07:00
|
|
|
}
|
2015-02-03 22:07:02 +03:00
|
|
|
|
|
|
|
if (size > kMaxLength) {
|
|
|
|
err = new RangeError('File size is greater than possible Buffer: ' +
|
|
|
|
`0x${kMaxLength.toString(16)} bytes`);
|
|
|
|
return context.close(err);
|
|
|
|
}
|
|
|
|
|
2017-10-15 13:18:09 +02:00
|
|
|
try {
|
|
|
|
context.buffer = Buffer.allocUnsafeSlow(size);
|
|
|
|
} catch (err) {
|
|
|
|
return context.close(err);
|
|
|
|
}
|
2015-02-03 22:07:02 +03:00
|
|
|
context.read();
|
|
|
|
}
|
|
|
|
|
|
|
|
function readFileAfterRead(err, bytesRead) {
|
|
|
|
var context = this.context;
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
return context.close(err);
|
|
|
|
|
|
|
|
if (bytesRead === 0)
|
|
|
|
return context.close();
|
|
|
|
|
|
|
|
context.pos += bytesRead;
|
|
|
|
|
|
|
|
if (context.size !== 0) {
|
|
|
|
if (context.pos === context.size)
|
|
|
|
context.close();
|
|
|
|
else
|
|
|
|
context.read();
|
|
|
|
} else {
|
|
|
|
// unknown size, just read until we don't get bytes.
|
|
|
|
context.buffers.push(context.buffer.slice(0, bytesRead));
|
|
|
|
context.read();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function readFileAfterClose(err) {
|
|
|
|
var context = this.context;
|
|
|
|
var buffer = null;
|
|
|
|
var callback = context.callback;
|
|
|
|
|
2016-11-17 22:03:39 -05:00
|
|
|
if (context.err || err)
|
|
|
|
return callback(context.err || err);
|
2015-02-03 22:07:02 +03:00
|
|
|
|
2015-10-22 06:58:26 -05:00
|
|
|
try {
|
2017-10-15 13:18:09 +02:00
|
|
|
if (context.size === 0)
|
|
|
|
buffer = Buffer.concat(context.buffers, context.pos);
|
|
|
|
else if (context.pos < context.size)
|
|
|
|
buffer = context.buffer.slice(0, context.pos);
|
|
|
|
else
|
|
|
|
buffer = context.buffer;
|
|
|
|
|
|
|
|
if (context.encoding)
|
|
|
|
buffer = buffer.toString(context.encoding);
|
2015-10-22 06:58:26 -05:00
|
|
|
} catch (err) {
|
2016-11-17 22:03:39 -05:00
|
|
|
return callback(err);
|
2015-10-22 06:58:26 -05:00
|
|
|
}
|
2017-10-15 13:18:09 +02:00
|
|
|
|
|
|
|
callback(null, buffer);
|
2015-10-22 06:58:26 -05:00
|
|
|
}
|
2010-03-15 10:41:58 -07:00
|
|
|
|
2016-04-07 10:04:47 -04:00
|
|
|
function tryStatSync(fd, isUserFd) {
|
2017-12-28 23:16:33 +08:00
|
|
|
const ctx = {};
|
|
|
|
binding.fstat(fd, undefined, ctx);
|
|
|
|
if (ctx.errno !== undefined && !isUserFd) {
|
|
|
|
fs.closeSync(fd);
|
2018-02-03 17:35:04 +08:00
|
|
|
throw errors.uvException(ctx);
|
2016-04-07 10:04:47 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function tryCreateBuffer(size, fd, isUserFd) {
|
|
|
|
var threw = true;
|
|
|
|
var buffer;
|
|
|
|
try {
|
|
|
|
buffer = Buffer.allocUnsafe(size);
|
|
|
|
threw = false;
|
|
|
|
} finally {
|
|
|
|
if (threw && !isUserFd) fs.closeSync(fd);
|
|
|
|
}
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
2017-01-15 00:44:19 +05:30
|
|
|
function tryReadSync(fd, isUserFd, buffer, pos, len) {
|
2016-04-07 10:04:47 -04:00
|
|
|
var threw = true;
|
|
|
|
var bytesRead;
|
|
|
|
try {
|
2017-01-15 00:44:19 +05:30
|
|
|
bytesRead = fs.readSync(fd, buffer, pos, len);
|
2016-04-07 10:04:47 -04:00
|
|
|
threw = false;
|
|
|
|
} finally {
|
|
|
|
if (threw && !isUserFd) fs.closeSync(fd);
|
|
|
|
}
|
|
|
|
return bytesRead;
|
|
|
|
}
|
|
|
|
|
2013-03-01 09:10:26 -08:00
|
|
|
fs.readFileSync = function(path, options) {
|
2016-06-26 00:03:05 +05:30
|
|
|
options = getOptions(options, { flag: 'r' });
|
2016-07-22 16:37:54 -07:00
|
|
|
var isUserFd = isFd(path); // file descriptor ownership
|
2016-06-26 00:03:05 +05:30
|
|
|
var fd = isUserFd ? path : fs.openSync(path, options.flag || 'r', 0o666);
|
2010-09-22 17:58:08 -07:00
|
|
|
|
2017-07-03 09:20:32 -04:00
|
|
|
tryStatSync(fd, isUserFd);
|
2017-03-11 19:41:20 -05:00
|
|
|
// Use stats array directly to avoid creating an fs.Stats instance just for
|
|
|
|
// our internal use.
|
|
|
|
var size;
|
2017-07-03 09:20:32 -04:00
|
|
|
if ((statValues[1/*mode*/] & S_IFMT) === S_IFREG)
|
2017-03-11 19:41:20 -05:00
|
|
|
size = statValues[8/*size*/];
|
|
|
|
else
|
|
|
|
size = 0;
|
2012-06-11 14:49:31 -07:00
|
|
|
var pos = 0;
|
|
|
|
var buffer; // single buffer with file data
|
|
|
|
var buffers; // list for when size is unknown
|
|
|
|
|
2012-05-15 17:35:42 -07:00
|
|
|
if (size === 0) {
|
2012-06-11 14:49:31 -07:00
|
|
|
buffers = [];
|
|
|
|
} else {
|
2016-04-07 10:04:47 -04:00
|
|
|
buffer = tryCreateBuffer(size, fd, isUserFd);
|
2012-05-03 01:03:08 +02:00
|
|
|
}
|
2010-03-15 10:41:58 -07:00
|
|
|
|
2015-06-03 03:41:04 +05:30
|
|
|
var bytesRead;
|
|
|
|
|
2016-04-07 10:04:47 -04:00
|
|
|
if (size !== 0) {
|
|
|
|
do {
|
2017-01-15 00:44:19 +05:30
|
|
|
bytesRead = tryReadSync(fd, isUserFd, buffer, pos, size - pos);
|
2016-04-07 10:04:47 -04:00
|
|
|
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);
|
2017-01-15 00:44:19 +05:30
|
|
|
bytesRead = tryReadSync(fd, isUserFd, buffer, 0, 8192);
|
2016-04-07 10:04:47 -04:00
|
|
|
if (bytesRead !== 0) {
|
|
|
|
buffers.push(buffer.slice(0, bytesRead));
|
2012-06-11 14:49:31 -07:00
|
|
|
}
|
2016-04-07 10:04:47 -04:00
|
|
|
pos += bytesRead;
|
|
|
|
} while (bytesRead !== 0);
|
2010-05-29 13:38:00 -07:00
|
|
|
}
|
2010-09-22 17:58:08 -07:00
|
|
|
|
2015-10-03 02:06:42 +02:00
|
|
|
if (!isUserFd)
|
|
|
|
fs.closeSync(fd);
|
2012-05-15 17:35:42 -07:00
|
|
|
|
2012-06-11 14:49:31 -07:00
|
|
|
if (size === 0) {
|
|
|
|
// data was collected into the buffers list.
|
|
|
|
buffer = Buffer.concat(buffers, pos);
|
2012-06-12 16:04:56 +02:00
|
|
|
} else if (pos < size) {
|
|
|
|
buffer = buffer.slice(0, pos);
|
2012-06-11 14:49:31 -07:00
|
|
|
}
|
|
|
|
|
2016-06-26 00:03:05 +05:30
|
|
|
if (options.encoding) buffer = buffer.toString(options.encoding);
|
2010-09-22 17:58:08 -07:00
|
|
|
return buffer;
|
2010-03-15 10:41:58 -07:00
|
|
|
};
|
2010-03-01 10:14:49 -08:00
|
|
|
|
2010-12-01 17:43:30 -08:00
|
|
|
fs.close = function(fd, callback) {
|
2017-12-22 19:19:33 -05:00
|
|
|
validateUint32(fd, 'fd');
|
2017-11-26 12:44:20 -08:00
|
|
|
const req = new FSReqWrap();
|
2016-07-22 16:38:36 -07:00
|
|
|
req.oncomplete = makeCallback(callback);
|
2016-07-22 16:37:54 -07:00
|
|
|
binding.close(fd, req);
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2010-12-01 17:43:30 -08:00
|
|
|
fs.closeSync = function(fd) {
|
2017-12-22 19:19:33 -05:00
|
|
|
validateUint32(fd, 'fd');
|
2017-11-26 12:44:20 -08:00
|
|
|
|
2017-11-29 12:52:16 +08:00
|
|
|
const ctx = {};
|
|
|
|
binding.close(fd, undefined, ctx);
|
|
|
|
if (ctx.errno !== undefined) {
|
2018-02-03 17:35:04 +08:00
|
|
|
throw errors.uvException(ctx);
|
2017-11-29 12:52:16 +08:00
|
|
|
}
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2011-01-25 10:40:12 -08:00
|
|
|
function modeNum(m, def) {
|
2015-01-28 20:05:53 -05:00
|
|
|
if (typeof m === 'number')
|
2013-07-24 18:03:53 +02:00
|
|
|
return m;
|
2015-01-28 20:05:53 -05:00
|
|
|
if (typeof m === 'string')
|
2013-07-24 18:03:53 +02:00
|
|
|
return parseInt(m, 8);
|
|
|
|
if (def)
|
|
|
|
return modeNum(def);
|
|
|
|
return undefined;
|
2011-01-25 10:40:12 -08:00
|
|
|
}
|
|
|
|
|
2016-07-22 16:38:36 -07:00
|
|
|
fs.open = function(path, flags, mode, callback_) {
|
|
|
|
var callback = makeCallback(arguments[arguments.length - 1]);
|
2015-01-10 23:12:37 +03:00
|
|
|
mode = modeNum(mode, 0o666);
|
2011-02-15 08:30:58 -05:00
|
|
|
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(path);
|
2017-12-22 19:19:33 -05:00
|
|
|
validateUint32(mode, 'mode');
|
2017-12-13 14:24:34 -08:00
|
|
|
|
|
|
|
const req = new FSReqWrap();
|
2014-12-09 05:29:47 +01:00
|
|
|
req.oncomplete = callback;
|
|
|
|
|
2017-08-20 22:44:47 -07:00
|
|
|
binding.open(pathModule.toNamespacedPath(path),
|
2012-02-18 15:01:35 -08:00
|
|
|
stringToFlags(flags),
|
|
|
|
mode,
|
2014-12-09 05:29:47 +01:00
|
|
|
req);
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2010-12-01 17:43:30 -08:00
|
|
|
fs.openSync = function(path, flags, mode) {
|
2015-01-10 23:12:37 +03:00
|
|
|
mode = modeNum(mode, 0o666);
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(path);
|
2017-12-22 19:19:33 -05:00
|
|
|
validateUint32(mode, 'mode');
|
2017-12-13 14:24:34 -08:00
|
|
|
|
2017-08-20 22:44:47 -07:00
|
|
|
return binding.open(pathModule.toNamespacedPath(path),
|
|
|
|
stringToFlags(flags), mode);
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2010-12-01 17:43:30 -08:00
|
|
|
fs.read = function(fd, buffer, offset, length, position, callback) {
|
2017-12-22 19:19:33 -05:00
|
|
|
validateUint32(fd, 'fd');
|
2017-12-13 19:54:09 -05:00
|
|
|
validateBuffer(buffer);
|
2017-11-26 20:51:01 -08:00
|
|
|
|
|
|
|
offset |= 0;
|
|
|
|
length |= 0;
|
|
|
|
|
2016-01-06 17:39:03 +01:00
|
|
|
if (length === 0) {
|
|
|
|
return process.nextTick(function() {
|
2016-07-22 16:38:36 -07:00
|
|
|
callback && callback(null, 0, buffer);
|
2016-01-06 17:39:03 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-12-13 17:29:03 -05:00
|
|
|
validateOffsetLengthRead(offset, length, buffer.length);
|
2017-11-26 20:51:01 -08:00
|
|
|
|
2017-12-13 14:24:34 -08:00
|
|
|
if (!isUint32(position))
|
2017-11-26 20:51:01 -08:00
|
|
|
position = -1;
|
|
|
|
|
2011-03-23 12:29:49 +01:00
|
|
|
function wrapper(err, bytesRead) {
|
|
|
|
// Retain a reference to buffer so that it can't be GC'ed too soon.
|
2016-07-22 16:38:36 -07:00
|
|
|
callback && callback(err, bytesRead || 0, buffer);
|
2011-03-23 12:29:49 +01:00
|
|
|
}
|
|
|
|
|
2017-11-26 20:51:01 -08:00
|
|
|
const req = new FSReqWrap();
|
2014-12-09 05:29:47 +01:00
|
|
|
req.oncomplete = wrapper;
|
|
|
|
|
|
|
|
binding.read(fd, buffer, offset, length, position, req);
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2017-04-16 21:19:36 +02:00
|
|
|
Object.defineProperty(fs.read, internalUtil.customPromisifyArgs,
|
|
|
|
{ value: ['bytesRead', 'buffer'], enumerable: false });
|
|
|
|
|
2010-12-01 17:43:30 -08:00
|
|
|
fs.readSync = function(fd, buffer, offset, length, position) {
|
2017-12-22 19:19:33 -05:00
|
|
|
validateUint32(fd, 'fd');
|
2017-12-13 19:54:09 -05:00
|
|
|
validateBuffer(buffer);
|
2017-11-26 20:51:01 -08:00
|
|
|
|
|
|
|
offset |= 0;
|
|
|
|
length |= 0;
|
|
|
|
|
2016-01-06 17:39:03 +01:00
|
|
|
if (length === 0) {
|
2017-01-10 14:26:23 +02:00
|
|
|
return 0;
|
2010-05-20 18:13:22 -04:00
|
|
|
}
|
|
|
|
|
2017-12-13 17:29:03 -05:00
|
|
|
validateOffsetLengthRead(offset, length, buffer.length);
|
2017-11-26 20:51:01 -08:00
|
|
|
|
2017-12-13 14:24:34 -08:00
|
|
|
if (!isUint32(position))
|
2017-11-26 20:51:01 -08:00
|
|
|
position = -1;
|
|
|
|
|
2017-01-10 14:26:23 +02:00
|
|
|
return binding.read(fd, buffer, offset, length, position);
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2013-07-02 00:27:26 -07:00
|
|
|
// usage:
|
2016-07-23 22:43:41 +02:00
|
|
|
// fs.write(fd, buffer[, offset[, length[, position]]], callback);
|
2013-07-02 00:27:26 -07:00
|
|
|
// OR
|
|
|
|
// fs.write(fd, string[, position[, encoding]], callback);
|
2010-12-01 17:43:30 -08:00
|
|
|
fs.write = function(fd, buffer, offset, length, position, callback) {
|
2015-09-18 17:36:57 +09:00
|
|
|
function wrapper(err, written) {
|
2014-12-09 05:29:47 +01:00
|
|
|
// Retain a reference to buffer so that it can't be GC'ed too soon.
|
|
|
|
callback(err, written || 0, buffer);
|
|
|
|
}
|
|
|
|
|
2017-12-22 19:19:33 -05:00
|
|
|
validateUint32(fd, 'fd');
|
2017-12-13 14:24:34 -08:00
|
|
|
|
|
|
|
const req = new FSReqWrap();
|
2015-09-18 17:36:57 +09:00
|
|
|
req.oncomplete = wrapper;
|
|
|
|
|
2016-12-21 08:27:34 +01:00
|
|
|
if (isUint8Array(buffer)) {
|
2016-07-23 22:43:41 +02:00
|
|
|
callback = maybeCallback(callback || position || length || offset);
|
2017-12-13 14:24:34 -08:00
|
|
|
if (typeof offset !== 'number')
|
2016-07-23 22:43:41 +02:00
|
|
|
offset = 0;
|
2017-12-13 14:24:34 -08:00
|
|
|
if (typeof length !== 'number')
|
2016-07-23 22:43:41 +02:00
|
|
|
length = buffer.length - offset;
|
2017-12-13 14:24:34 -08:00
|
|
|
if (typeof position !== 'number')
|
2013-07-02 00:27:26 -07:00
|
|
|
position = null;
|
2017-12-22 18:33:31 -05:00
|
|
|
validateOffsetLengthWrite(offset, length, buffer.byteLength);
|
2014-12-09 05:29:47 +01:00
|
|
|
return binding.writeBuffer(fd, buffer, offset, length, position, req);
|
2010-05-19 14:17:50 -04:00
|
|
|
}
|
2010-08-24 23:46:37 -04:00
|
|
|
|
2015-03-08 16:19:56 -04:00
|
|
|
if (typeof buffer !== 'string')
|
2013-07-02 00:27:26 -07:00
|
|
|
buffer += '';
|
2015-01-28 20:05:53 -05:00
|
|
|
if (typeof position !== 'function') {
|
|
|
|
if (typeof offset === 'function') {
|
2013-07-02 00:27:26 -07:00
|
|
|
position = offset;
|
|
|
|
offset = null;
|
|
|
|
} else {
|
|
|
|
position = length;
|
2010-08-24 23:46:37 -04:00
|
|
|
}
|
2013-07-02 00:27:26 -07:00
|
|
|
length = 'utf8';
|
2010-08-24 23:46:37 -04:00
|
|
|
}
|
2016-07-22 16:38:36 -07:00
|
|
|
callback = maybeCallback(position);
|
2014-12-09 05:29:47 +01:00
|
|
|
return binding.writeString(fd, buffer, offset, length, req);
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2017-04-16 21:19:36 +02:00
|
|
|
Object.defineProperty(fs.write, internalUtil.customPromisifyArgs,
|
|
|
|
{ value: ['bytesWritten', 'buffer'], enumerable: false });
|
|
|
|
|
2013-07-02 00:27:26 -07:00
|
|
|
// usage:
|
2016-07-23 22:43:41 +02:00
|
|
|
// fs.writeSync(fd, buffer[, offset[, length[, position]]]);
|
2013-07-02 00:27:26 -07:00
|
|
|
// OR
|
|
|
|
// fs.writeSync(fd, string[, position[, encoding]]);
|
2010-12-01 17:43:30 -08:00
|
|
|
fs.writeSync = function(fd, buffer, offset, length, position) {
|
2017-12-22 19:19:33 -05:00
|
|
|
validateUint32(fd, 'fd');
|
2016-12-21 08:27:34 +01:00
|
|
|
if (isUint8Array(buffer)) {
|
2015-01-28 20:05:53 -05:00
|
|
|
if (position === undefined)
|
2013-07-02 00:27:26 -07:00
|
|
|
position = null;
|
2016-07-23 22:43:41 +02:00
|
|
|
if (typeof offset !== 'number')
|
|
|
|
offset = 0;
|
|
|
|
if (typeof length !== 'number')
|
|
|
|
length = buffer.length - offset;
|
2017-12-22 18:33:31 -05:00
|
|
|
validateOffsetLengthWrite(offset, length, buffer.byteLength);
|
2013-07-02 00:27:26 -07:00
|
|
|
return binding.writeBuffer(fd, buffer, offset, length, position);
|
2010-05-19 14:17:50 -04:00
|
|
|
}
|
2015-01-28 20:05:53 -05:00
|
|
|
if (typeof buffer !== 'string')
|
2013-07-02 00:27:26 -07:00
|
|
|
buffer += '';
|
2015-01-28 20:05:53 -05:00
|
|
|
if (offset === undefined)
|
2013-07-02 00:27:26 -07:00
|
|
|
offset = null;
|
|
|
|
return binding.writeString(fd, buffer, offset, length, position);
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2010-12-01 17:43:30 -08:00
|
|
|
fs.rename = function(oldPath, newPath, callback) {
|
2016-07-22 16:38:36 -07:00
|
|
|
callback = makeCallback(callback);
|
2018-01-23 10:23:46 +08:00
|
|
|
oldPath = getPathFromURL(oldPath);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(oldPath, 'oldPath');
|
2018-01-23 10:23:46 +08:00
|
|
|
newPath = getPathFromURL(newPath);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(newPath, 'newPath');
|
2017-12-13 14:24:34 -08:00
|
|
|
const req = new FSReqWrap();
|
2014-12-09 05:29:47 +01:00
|
|
|
req.oncomplete = callback;
|
2017-08-20 22:44:47 -07:00
|
|
|
binding.rename(pathModule.toNamespacedPath(oldPath),
|
|
|
|
pathModule.toNamespacedPath(newPath),
|
2014-12-09 05:29:47 +01:00
|
|
|
req);
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2010-12-01 17:43:30 -08:00
|
|
|
fs.renameSync = function(oldPath, newPath) {
|
2018-01-23 10:23:46 +08:00
|
|
|
oldPath = getPathFromURL(oldPath);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(oldPath, 'oldPath');
|
2018-01-23 10:23:46 +08:00
|
|
|
newPath = getPathFromURL(newPath);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(newPath, 'newPath');
|
2018-01-24 09:03:45 +08:00
|
|
|
const ctx = { path: oldPath, dest: newPath };
|
|
|
|
binding.rename(pathModule.toNamespacedPath(oldPath),
|
|
|
|
pathModule.toNamespacedPath(newPath), undefined, ctx);
|
|
|
|
if (ctx.errno !== undefined) {
|
2018-02-03 17:35:04 +08:00
|
|
|
throw errors.uvException(ctx);
|
2018-01-24 09:03:45 +08:00
|
|
|
}
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2012-08-04 12:39:11 -07:00
|
|
|
fs.truncate = function(path, len, callback) {
|
2016-07-22 16:37:54 -07:00
|
|
|
if (typeof path === 'number') {
|
2017-10-06 11:06:35 -07:00
|
|
|
showTruncateDeprecation();
|
2015-02-07 19:27:20 +01:00
|
|
|
return fs.ftruncate(path, len, callback);
|
2016-07-22 16:37:54 -07:00
|
|
|
}
|
2016-07-22 16:38:36 -07:00
|
|
|
if (typeof len === 'function') {
|
|
|
|
callback = len;
|
|
|
|
len = 0;
|
|
|
|
} else if (len === undefined) {
|
2012-08-04 12:39:11 -07:00
|
|
|
len = 0;
|
|
|
|
}
|
2014-12-09 05:29:47 +01:00
|
|
|
|
2016-07-22 16:38:36 -07:00
|
|
|
callback = maybeCallback(callback);
|
2013-10-08 11:25:22 +02:00
|
|
|
fs.open(path, 'r+', function(er, fd) {
|
2012-08-04 12:39:11 -07:00
|
|
|
if (er) return callback(er);
|
2014-12-09 05:29:47 +01:00
|
|
|
var req = new FSReqWrap();
|
2016-10-15 15:02:43 -07:00
|
|
|
req.oncomplete = function oncomplete(er) {
|
2012-08-04 12:39:11 -07:00
|
|
|
fs.close(fd, function(er2) {
|
|
|
|
callback(er || er2);
|
|
|
|
});
|
2014-12-09 05:29:47 +01:00
|
|
|
};
|
|
|
|
binding.ftruncate(fd, len, req);
|
2012-08-04 12:39:11 -07:00
|
|
|
});
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2012-08-04 12:39:11 -07:00
|
|
|
fs.truncateSync = function(path, len) {
|
2016-07-22 16:37:54 -07:00
|
|
|
if (typeof path === 'number') {
|
|
|
|
// legacy
|
2017-10-06 11:06:35 -07:00
|
|
|
showTruncateDeprecation();
|
2012-08-08 23:07:18 +02:00
|
|
|
return fs.ftruncateSync(path, len);
|
2016-07-22 16:37:54 -07:00
|
|
|
}
|
|
|
|
if (len === undefined) {
|
2012-08-04 12:39:11 -07:00
|
|
|
len = 0;
|
|
|
|
}
|
|
|
|
// allow error to be thrown, but still close fd.
|
2013-10-08 11:25:22 +02:00
|
|
|
var fd = fs.openSync(path, 'r+');
|
2015-06-03 03:41:04 +05:30
|
|
|
var ret;
|
|
|
|
|
2012-08-04 12:39:11 -07:00
|
|
|
try {
|
2015-06-03 03:41:04 +05:30
|
|
|
ret = fs.ftruncateSync(fd, len);
|
2012-08-04 12:39:11 -07:00
|
|
|
} finally {
|
|
|
|
fs.closeSync(fd);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
|
2017-12-13 14:24:34 -08:00
|
|
|
fs.ftruncate = function(fd, len = 0, callback) {
|
2016-07-22 16:38:36 -07:00
|
|
|
if (typeof len === 'function') {
|
|
|
|
callback = len;
|
|
|
|
len = 0;
|
2012-08-04 12:39:11 -07:00
|
|
|
}
|
2017-12-22 19:19:33 -05:00
|
|
|
validateUint32(fd, 'fd');
|
|
|
|
validateLen(len);
|
2017-11-26 12:44:20 -08:00
|
|
|
len = Math.max(0, len);
|
|
|
|
const req = new FSReqWrap();
|
2016-07-22 16:38:36 -07:00
|
|
|
req.oncomplete = makeCallback(callback);
|
2014-12-09 05:29:47 +01:00
|
|
|
binding.ftruncate(fd, len, req);
|
2012-08-04 12:39:11 -07:00
|
|
|
};
|
|
|
|
|
2017-12-13 14:24:34 -08:00
|
|
|
fs.ftruncateSync = function(fd, len = 0) {
|
2017-12-22 19:19:33 -05:00
|
|
|
validateUint32(fd, 'fd');
|
|
|
|
validateLen(len);
|
2017-11-26 12:44:20 -08:00
|
|
|
len = Math.max(0, len);
|
2018-01-24 09:20:30 +08:00
|
|
|
const ctx = {};
|
|
|
|
binding.ftruncate(fd, len, undefined, ctx);
|
|
|
|
if (ctx.errno !== undefined) {
|
2018-02-03 17:35:04 +08:00
|
|
|
throw errors.uvException(ctx);
|
2018-01-24 09:20:30 +08:00
|
|
|
}
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2010-12-01 17:43:30 -08:00
|
|
|
fs.rmdir = function(path, callback) {
|
2016-07-22 16:38:36 -07:00
|
|
|
callback = maybeCallback(callback);
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(path);
|
2017-12-13 14:24:34 -08:00
|
|
|
const req = new FSReqWrap();
|
2014-12-09 05:29:47 +01:00
|
|
|
req.oncomplete = callback;
|
2017-08-20 22:44:47 -07:00
|
|
|
binding.rmdir(pathModule.toNamespacedPath(path), req);
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2010-12-01 17:43:30 -08:00
|
|
|
fs.rmdirSync = function(path) {
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(path);
|
2017-08-20 22:44:47 -07:00
|
|
|
return binding.rmdir(pathModule.toNamespacedPath(path));
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2010-12-01 17:43:30 -08:00
|
|
|
fs.fdatasync = function(fd, callback) {
|
2017-12-22 19:19:33 -05:00
|
|
|
validateUint32(fd, 'fd');
|
2017-11-26 12:44:20 -08:00
|
|
|
const req = new FSReqWrap();
|
2014-12-09 05:29:47 +01:00
|
|
|
req.oncomplete = makeCallback(callback);
|
2016-07-22 16:37:54 -07:00
|
|
|
binding.fdatasync(fd, req);
|
2010-03-22 07:25:24 +00:00
|
|
|
};
|
|
|
|
|
2010-12-01 17:43:30 -08:00
|
|
|
fs.fdatasyncSync = function(fd) {
|
2017-12-22 19:19:33 -05:00
|
|
|
validateUint32(fd, 'fd');
|
2018-01-24 09:37:52 +08:00
|
|
|
const ctx = {};
|
|
|
|
binding.fdatasync(fd, undefined, ctx);
|
|
|
|
if (ctx.errno !== undefined) {
|
2018-02-03 17:35:04 +08:00
|
|
|
throw errors.uvException(ctx);
|
2018-01-24 09:37:52 +08:00
|
|
|
}
|
2010-03-22 07:25:24 +00:00
|
|
|
};
|
|
|
|
|
2010-12-01 17:43:30 -08:00
|
|
|
fs.fsync = function(fd, callback) {
|
2017-12-22 19:19:33 -05:00
|
|
|
validateUint32(fd, 'fd');
|
2017-11-26 12:44:20 -08:00
|
|
|
const req = new FSReqWrap();
|
2016-07-22 16:38:36 -07:00
|
|
|
req.oncomplete = makeCallback(callback);
|
2016-07-22 16:37:54 -07:00
|
|
|
binding.fsync(fd, req);
|
2010-03-22 07:25:24 +00:00
|
|
|
};
|
|
|
|
|
2010-12-01 17:43:30 -08:00
|
|
|
fs.fsyncSync = function(fd) {
|
2017-12-22 19:19:33 -05:00
|
|
|
validateUint32(fd, 'fd');
|
2018-01-24 18:39:35 +08:00
|
|
|
const ctx = {};
|
|
|
|
binding.fsync(fd, undefined, ctx);
|
|
|
|
if (ctx.errno !== undefined) {
|
2018-02-03 17:35:04 +08:00
|
|
|
throw errors.uvException(ctx);
|
2018-01-24 18:39:35 +08:00
|
|
|
}
|
2010-03-22 07:25:24 +00:00
|
|
|
};
|
|
|
|
|
2010-12-01 17:43:30 -08:00
|
|
|
fs.mkdir = function(path, mode, callback) {
|
2016-07-22 16:38:36 -07:00
|
|
|
if (typeof mode === 'function') callback = mode;
|
|
|
|
callback = makeCallback(callback);
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(path);
|
2017-12-13 14:24:34 -08:00
|
|
|
mode = modeNum(mode, 0o777);
|
2017-12-22 19:19:33 -05:00
|
|
|
validateUint32(mode, 'mode');
|
2017-12-13 14:24:34 -08:00
|
|
|
|
|
|
|
const req = new FSReqWrap();
|
2014-12-09 05:29:47 +01:00
|
|
|
req.oncomplete = callback;
|
2017-12-13 14:24:34 -08:00
|
|
|
binding.mkdir(pathModule.toNamespacedPath(path), mode, req);
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2010-12-01 17:43:30 -08:00
|
|
|
fs.mkdirSync = function(path, mode) {
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(path);
|
2017-12-13 14:24:34 -08:00
|
|
|
mode = modeNum(mode, 0o777);
|
2017-12-22 19:19:33 -05:00
|
|
|
validateUint32(mode, 'mode');
|
2017-12-13 14:24:34 -08:00
|
|
|
return binding.mkdir(pathModule.toNamespacedPath(path), mode);
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
fs: Buffer and encoding enhancements to fs API
This makes several changes:
1. Allow path/filename to be passed in as a Buffer on fs methods
2. Add `options.encoding` to fs.readdir, fs.readdirSync, fs.readlink,
fs.readlinkSync and fs.watch.
3. Documentation updates
For 1... it's now possible to do:
```js
fs.open(Buffer('/fs/foo/bar'), 'w+', (err, fd) => { });
```
For 2...
```js
fs.readdir('/fs/foo/bar', {encoding:'hex'}, (err,list) => { });
fs.readdir('/fs/foo/bar', {encoding:'buffer'}, (err, list) => { });
```
encoding can also be passed as a string
```js
fs.readdir('/fs/foo/bar', 'hex', (err,list) => { });
```
The default encoding is set to UTF8 so this addresses the
discrepency that existed previously between fs.readdir and
fs.watch handling filenames differently.
Fixes: https://github.com/nodejs/node/issues/2088
Refs: https://github.com/nodejs/node/issues/3519
PR-URL: https://github.com/nodejs/node/pull/5616
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
2016-03-08 20:58:45 -08:00
|
|
|
fs.readdir = function(path, options, callback) {
|
2016-06-26 00:03:05 +05:30
|
|
|
callback = makeCallback(typeof options === 'function' ? options : callback);
|
|
|
|
options = getOptions(options, {});
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(path);
|
2017-12-13 14:24:34 -08:00
|
|
|
|
|
|
|
const req = new FSReqWrap();
|
2014-12-09 05:29:47 +01:00
|
|
|
req.oncomplete = callback;
|
2017-08-20 22:44:47 -07:00
|
|
|
binding.readdir(pathModule.toNamespacedPath(path), options.encoding, req);
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
fs: Buffer and encoding enhancements to fs API
This makes several changes:
1. Allow path/filename to be passed in as a Buffer on fs methods
2. Add `options.encoding` to fs.readdir, fs.readdirSync, fs.readlink,
fs.readlinkSync and fs.watch.
3. Documentation updates
For 1... it's now possible to do:
```js
fs.open(Buffer('/fs/foo/bar'), 'w+', (err, fd) => { });
```
For 2...
```js
fs.readdir('/fs/foo/bar', {encoding:'hex'}, (err,list) => { });
fs.readdir('/fs/foo/bar', {encoding:'buffer'}, (err, list) => { });
```
encoding can also be passed as a string
```js
fs.readdir('/fs/foo/bar', 'hex', (err,list) => { });
```
The default encoding is set to UTF8 so this addresses the
discrepency that existed previously between fs.readdir and
fs.watch handling filenames differently.
Fixes: https://github.com/nodejs/node/issues/2088
Refs: https://github.com/nodejs/node/issues/3519
PR-URL: https://github.com/nodejs/node/pull/5616
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
2016-03-08 20:58:45 -08:00
|
|
|
fs.readdirSync = function(path, options) {
|
2016-06-26 00:03:05 +05:30
|
|
|
options = getOptions(options, {});
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(path);
|
2017-08-20 22:44:47 -07:00
|
|
|
return binding.readdir(pathModule.toNamespacedPath(path), options.encoding);
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2010-12-01 17:43:30 -08:00
|
|
|
fs.fstat = function(fd, callback) {
|
2017-12-22 19:19:33 -05:00
|
|
|
validateUint32(fd, 'fd');
|
2017-11-26 12:44:20 -08:00
|
|
|
const req = new FSReqWrap();
|
2017-03-11 19:41:20 -05:00
|
|
|
req.oncomplete = makeStatsCallback(callback);
|
2016-07-22 16:37:54 -07:00
|
|
|
binding.fstat(fd, req);
|
2010-05-12 22:35:33 +02:00
|
|
|
};
|
|
|
|
|
2010-12-01 17:43:30 -08:00
|
|
|
fs.lstat = function(path, callback) {
|
2017-03-11 19:41:20 -05:00
|
|
|
callback = makeStatsCallback(callback);
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(path);
|
2017-12-13 14:24:34 -08:00
|
|
|
const req = new FSReqWrap();
|
2014-12-09 05:29:47 +01:00
|
|
|
req.oncomplete = callback;
|
2017-08-20 22:44:47 -07:00
|
|
|
binding.lstat(pathModule.toNamespacedPath(path), req);
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2010-12-01 17:43:30 -08:00
|
|
|
fs.stat = function(path, callback) {
|
2017-03-11 19:41:20 -05:00
|
|
|
callback = makeStatsCallback(callback);
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(path);
|
2017-12-13 14:24:34 -08:00
|
|
|
const req = new FSReqWrap();
|
2014-12-09 05:29:47 +01:00
|
|
|
req.oncomplete = callback;
|
2017-08-20 22:44:47 -07:00
|
|
|
binding.stat(pathModule.toNamespacedPath(path), req);
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2010-12-01 17:43:30 -08:00
|
|
|
fs.fstatSync = function(fd) {
|
2017-12-22 19:19:33 -05:00
|
|
|
validateUint32(fd, 'fd');
|
2017-12-28 23:16:33 +08:00
|
|
|
const ctx = { fd };
|
|
|
|
binding.fstat(fd, undefined, ctx);
|
|
|
|
if (ctx.errno !== undefined) {
|
2018-02-03 17:35:04 +08:00
|
|
|
throw errors.uvException(ctx);
|
2017-12-28 23:16:33 +08:00
|
|
|
}
|
2017-02-23 12:45:10 -05:00
|
|
|
return statsFromValues();
|
2010-05-12 22:35:33 +02:00
|
|
|
};
|
|
|
|
|
2010-12-01 17:43:30 -08:00
|
|
|
fs.lstatSync = function(path) {
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(path);
|
2017-12-28 14:51:05 +08:00
|
|
|
const ctx = { path };
|
|
|
|
binding.lstat(pathModule.toNamespacedPath(path), undefined, ctx);
|
|
|
|
if (ctx.errno !== undefined) {
|
2018-02-03 17:35:04 +08:00
|
|
|
throw errors.uvException(ctx);
|
2017-12-28 14:51:05 +08:00
|
|
|
}
|
2017-02-23 12:45:10 -05:00
|
|
|
return statsFromValues();
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2010-12-01 17:43:30 -08:00
|
|
|
fs.statSync = function(path) {
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(path);
|
2017-12-28 04:01:45 +08:00
|
|
|
const ctx = { path };
|
|
|
|
binding.stat(pathModule.toNamespacedPath(path), undefined, ctx);
|
|
|
|
if (ctx.errno !== undefined) {
|
2018-02-03 17:35:04 +08:00
|
|
|
throw errors.uvException(ctx);
|
2017-12-28 04:01:45 +08:00
|
|
|
}
|
2017-02-23 12:45:10 -05:00
|
|
|
return statsFromValues();
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
fs: Buffer and encoding enhancements to fs API
This makes several changes:
1. Allow path/filename to be passed in as a Buffer on fs methods
2. Add `options.encoding` to fs.readdir, fs.readdirSync, fs.readlink,
fs.readlinkSync and fs.watch.
3. Documentation updates
For 1... it's now possible to do:
```js
fs.open(Buffer('/fs/foo/bar'), 'w+', (err, fd) => { });
```
For 2...
```js
fs.readdir('/fs/foo/bar', {encoding:'hex'}, (err,list) => { });
fs.readdir('/fs/foo/bar', {encoding:'buffer'}, (err, list) => { });
```
encoding can also be passed as a string
```js
fs.readdir('/fs/foo/bar', 'hex', (err,list) => { });
```
The default encoding is set to UTF8 so this addresses the
discrepency that existed previously between fs.readdir and
fs.watch handling filenames differently.
Fixes: https://github.com/nodejs/node/issues/2088
Refs: https://github.com/nodejs/node/issues/3519
PR-URL: https://github.com/nodejs/node/pull/5616
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
2016-03-08 20:58:45 -08:00
|
|
|
fs.readlink = function(path, options, callback) {
|
2016-06-26 00:03:05 +05:30
|
|
|
callback = makeCallback(typeof options === 'function' ? options : callback);
|
|
|
|
options = getOptions(options, {});
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(path, 'oldPath');
|
2017-12-13 14:24:34 -08:00
|
|
|
const req = new FSReqWrap();
|
2014-12-09 05:29:47 +01:00
|
|
|
req.oncomplete = callback;
|
2017-08-20 22:44:47 -07:00
|
|
|
binding.readlink(pathModule.toNamespacedPath(path), options.encoding, req);
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
fs: Buffer and encoding enhancements to fs API
This makes several changes:
1. Allow path/filename to be passed in as a Buffer on fs methods
2. Add `options.encoding` to fs.readdir, fs.readdirSync, fs.readlink,
fs.readlinkSync and fs.watch.
3. Documentation updates
For 1... it's now possible to do:
```js
fs.open(Buffer('/fs/foo/bar'), 'w+', (err, fd) => { });
```
For 2...
```js
fs.readdir('/fs/foo/bar', {encoding:'hex'}, (err,list) => { });
fs.readdir('/fs/foo/bar', {encoding:'buffer'}, (err, list) => { });
```
encoding can also be passed as a string
```js
fs.readdir('/fs/foo/bar', 'hex', (err,list) => { });
```
The default encoding is set to UTF8 so this addresses the
discrepency that existed previously between fs.readdir and
fs.watch handling filenames differently.
Fixes: https://github.com/nodejs/node/issues/2088
Refs: https://github.com/nodejs/node/issues/3519
PR-URL: https://github.com/nodejs/node/pull/5616
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
2016-03-08 20:58:45 -08:00
|
|
|
fs.readlinkSync = function(path, options) {
|
2016-06-26 00:03:05 +05:30
|
|
|
options = getOptions(options, {});
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(path, 'oldPath');
|
2018-01-24 07:51:08 +08:00
|
|
|
const ctx = { path };
|
|
|
|
const result = binding.readlink(pathModule.toNamespacedPath(path),
|
|
|
|
options.encoding, undefined, ctx);
|
|
|
|
if (ctx.errno !== undefined) {
|
2018-02-03 17:35:04 +08:00
|
|
|
throw errors.uvException(ctx);
|
2018-02-03 21:58:22 +08:00
|
|
|
} else if (ctx.error) {
|
|
|
|
// TODO(joyeecheung): this is an encoding error usually caused by memory
|
|
|
|
// problems. We need to figure out proper error code(s) for this.
|
|
|
|
Error.captureStackTrace(ctx.error);
|
|
|
|
throw ctx.error;
|
2018-01-24 07:51:08 +08:00
|
|
|
}
|
|
|
|
return result;
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2014-12-03 15:38:53 +01:00
|
|
|
function preprocessSymlinkDestination(path, type, linkPath) {
|
2012-06-15 01:37:24 +02:00
|
|
|
if (!isWindows) {
|
|
|
|
// No preprocessing is needed on Unix.
|
|
|
|
return path;
|
|
|
|
} else if (type === 'junction') {
|
|
|
|
// Junctions paths need to be absolute and \\?\-prefixed.
|
2014-12-03 15:38:53 +01:00
|
|
|
// A relative target is relative to the link's parent directory.
|
|
|
|
path = pathModule.resolve(linkPath, '..', path);
|
2017-08-20 22:44:47 -07:00
|
|
|
return pathModule.toNamespacedPath(path);
|
2012-06-15 01:37:24 +02:00
|
|
|
} else {
|
|
|
|
// Windows symlinks don't tolerate forward slashes.
|
|
|
|
return ('' + path).replace(/\//g, '\\');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-13 14:24:34 -08:00
|
|
|
function stringToSymlinkType(type) {
|
|
|
|
let flags = 0;
|
|
|
|
if (typeof type === 'string') {
|
|
|
|
switch (type) {
|
|
|
|
case 'dir':
|
|
|
|
flags |= constants.UV_FS_SYMLINK_DIR;
|
|
|
|
break;
|
|
|
|
case 'junction':
|
|
|
|
flags |= constants.UV_FS_SYMLINK_JUNCTION;
|
|
|
|
break;
|
|
|
|
case 'file':
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
const err = new errors.Error('ERR_FS_INVALID_SYMLINK_TYPE', type);
|
|
|
|
Error.captureStackTrace(err, stringToSymlinkType);
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return flags;
|
|
|
|
}
|
|
|
|
|
2015-11-25 12:40:42 +08:00
|
|
|
fs.symlink = function(target, path, type_, callback_) {
|
2015-01-28 20:05:53 -05:00
|
|
|
var type = (typeof type_ === 'string' ? type_ : null);
|
2012-06-06 21:33:29 +02:00
|
|
|
var callback = makeCallback(arguments[arguments.length - 1]);
|
2012-05-22 16:02:10 -07:00
|
|
|
|
2018-01-23 10:23:46 +08:00
|
|
|
target = getPathFromURL(target);
|
|
|
|
path = getPathFromURL(path);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(target, 'target');
|
|
|
|
validatePath(path);
|
2014-12-09 05:29:47 +01:00
|
|
|
|
2017-12-13 14:24:34 -08:00
|
|
|
const flags = stringToSymlinkType(type);
|
|
|
|
const req = new FSReqWrap();
|
|
|
|
req.oncomplete = callback;
|
2018-01-24 06:53:47 +08:00
|
|
|
|
2015-11-25 12:40:42 +08:00
|
|
|
binding.symlink(preprocessSymlinkDestination(target, type, path),
|
2017-12-13 14:24:34 -08:00
|
|
|
pathModule.toNamespacedPath(path), flags, req);
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2015-11-25 12:40:42 +08:00
|
|
|
fs.symlinkSync = function(target, path, type) {
|
2015-01-28 20:05:53 -05:00
|
|
|
type = (typeof type === 'string' ? type : null);
|
2018-01-23 10:23:46 +08:00
|
|
|
target = getPathFromURL(target);
|
|
|
|
path = getPathFromURL(path);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(target, 'target');
|
|
|
|
validatePath(path);
|
2017-12-13 14:24:34 -08:00
|
|
|
const flags = stringToSymlinkType(type);
|
2018-01-24 06:53:47 +08:00
|
|
|
|
|
|
|
const ctx = { path: target, dest: path };
|
|
|
|
binding.symlink(preprocessSymlinkDestination(target, type, path),
|
|
|
|
pathModule.toNamespacedPath(path), flags, undefined, ctx);
|
|
|
|
|
|
|
|
if (ctx.errno !== undefined) {
|
2018-02-03 17:35:04 +08:00
|
|
|
throw errors.uvException(ctx);
|
2018-01-24 06:53:47 +08:00
|
|
|
}
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2016-10-19 13:42:34 -07:00
|
|
|
fs.link = function(existingPath, newPath, callback) {
|
2016-07-22 16:38:36 -07:00
|
|
|
callback = makeCallback(callback);
|
2017-01-11 07:02:40 -08:00
|
|
|
|
2018-01-23 10:23:46 +08:00
|
|
|
existingPath = getPathFromURL(existingPath);
|
|
|
|
newPath = getPathFromURL(newPath);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(existingPath, 'existingPath');
|
|
|
|
validatePath(newPath, 'newPath');
|
2017-12-13 14:24:34 -08:00
|
|
|
|
|
|
|
const req = new FSReqWrap();
|
2014-12-09 05:29:47 +01:00
|
|
|
req.oncomplete = callback;
|
|
|
|
|
2017-08-20 22:44:47 -07:00
|
|
|
binding.link(pathModule.toNamespacedPath(existingPath),
|
|
|
|
pathModule.toNamespacedPath(newPath),
|
2014-12-09 05:29:47 +01:00
|
|
|
req);
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2016-10-19 13:42:34 -07:00
|
|
|
fs.linkSync = function(existingPath, newPath) {
|
2018-01-23 10:23:46 +08:00
|
|
|
existingPath = getPathFromURL(existingPath);
|
|
|
|
newPath = getPathFromURL(newPath);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(existingPath, 'existingPath');
|
|
|
|
validatePath(newPath, 'newPath');
|
2018-01-24 07:03:11 +08:00
|
|
|
|
|
|
|
const ctx = { path: existingPath, dest: newPath };
|
|
|
|
const result = binding.link(pathModule.toNamespacedPath(existingPath),
|
|
|
|
pathModule.toNamespacedPath(newPath),
|
|
|
|
undefined, ctx);
|
|
|
|
if (ctx.errno !== undefined) {
|
2018-02-03 17:35:04 +08:00
|
|
|
throw errors.uvException(ctx);
|
2018-01-24 07:03:11 +08:00
|
|
|
}
|
|
|
|
return result;
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2010-12-01 17:43:30 -08:00
|
|
|
fs.unlink = function(path, callback) {
|
2016-07-22 16:38:36 -07:00
|
|
|
callback = makeCallback(callback);
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(path);
|
2017-12-13 14:24:34 -08:00
|
|
|
const req = new FSReqWrap();
|
2014-12-09 05:29:47 +01:00
|
|
|
req.oncomplete = callback;
|
2017-08-20 22:44:47 -07:00
|
|
|
binding.unlink(pathModule.toNamespacedPath(path), req);
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2010-12-01 17:43:30 -08:00
|
|
|
fs.unlinkSync = function(path) {
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(path);
|
2018-01-24 22:18:37 +08:00
|
|
|
const ctx = { path };
|
|
|
|
binding.unlink(pathModule.toNamespacedPath(path), undefined, ctx);
|
|
|
|
if (ctx.errno !== undefined) {
|
2018-02-03 17:35:04 +08:00
|
|
|
throw errors.uvException(ctx);
|
2018-01-24 22:18:37 +08:00
|
|
|
}
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2011-03-29 15:31:41 -07:00
|
|
|
fs.fchmod = function(fd, mode, callback) {
|
2017-11-26 12:44:20 -08:00
|
|
|
mode = modeNum(mode);
|
2017-12-22 19:19:33 -05:00
|
|
|
validateUint32(fd, 'fd');
|
|
|
|
validateUint32(mode, 'mode');
|
2018-01-17 15:30:03 -08:00
|
|
|
// values for mode < 0 are already checked via the validateUint32 function
|
|
|
|
if (mode > 0o777)
|
2017-11-26 12:44:20 -08:00
|
|
|
throw new errors.RangeError('ERR_OUT_OF_RANGE', 'mode');
|
|
|
|
|
|
|
|
const req = new FSReqWrap();
|
2016-07-22 16:38:36 -07:00
|
|
|
req.oncomplete = makeCallback(callback);
|
2017-11-26 12:44:20 -08:00
|
|
|
binding.fchmod(fd, mode, req);
|
2011-03-29 15:31:41 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
fs.fchmodSync = function(fd, mode) {
|
2017-11-26 12:44:20 -08:00
|
|
|
mode = modeNum(mode);
|
2017-12-22 19:19:33 -05:00
|
|
|
validateUint32(fd, 'fd');
|
|
|
|
validateUint32(mode, 'mode');
|
2017-11-26 12:44:20 -08:00
|
|
|
if (mode < 0 || mode > 0o777)
|
|
|
|
throw new errors.RangeError('ERR_OUT_OF_RANGE', 'mode');
|
|
|
|
return binding.fchmod(fd, mode);
|
2011-03-29 15:31:41 -07:00
|
|
|
};
|
|
|
|
|
2016-12-26 16:42:09 +05:30
|
|
|
if (constants.O_SYMLINK !== undefined) {
|
2011-03-29 16:34:05 -07:00
|
|
|
fs.lchmod = function(path, mode, callback) {
|
2016-07-22 16:38:36 -07:00
|
|
|
callback = maybeCallback(callback);
|
2011-10-04 18:08:18 -04:00
|
|
|
fs.open(path, constants.O_WRONLY | constants.O_SYMLINK, function(err, fd) {
|
2011-03-29 16:34:05 -07:00
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
return;
|
|
|
|
}
|
2017-07-03 09:20:32 -04:00
|
|
|
// Prefer to return the chmod error, if one occurs,
|
2011-11-22 13:10:57 -08:00
|
|
|
// but still try to close, and report closing errors if they occur.
|
|
|
|
fs.fchmod(fd, mode, function(err) {
|
|
|
|
fs.close(fd, function(err2) {
|
|
|
|
callback(err || err2);
|
|
|
|
});
|
|
|
|
});
|
2011-03-29 16:34:05 -07:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
fs.lchmodSync = function(path, mode) {
|
2018-01-23 15:07:42 -08:00
|
|
|
const fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK);
|
2011-11-22 13:10:57 -08:00
|
|
|
|
2017-07-03 09:20:32 -04:00
|
|
|
// Prefer to return the chmod error, if one occurs,
|
2011-11-22 13:10:57 -08:00
|
|
|
// but still try to close, and report closing errors if they occur.
|
2018-01-23 15:07:42 -08:00
|
|
|
let ret;
|
2011-11-22 13:10:57 -08:00
|
|
|
try {
|
2015-06-03 03:41:04 +05:30
|
|
|
ret = fs.fchmodSync(fd, mode);
|
2018-01-23 15:07:42 -08:00
|
|
|
} finally {
|
|
|
|
fs.closeSync(fd);
|
2011-11-22 13:10:57 -08:00
|
|
|
}
|
|
|
|
return ret;
|
2011-03-29 16:34:05 -07:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-12-01 17:43:30 -08:00
|
|
|
fs.chmod = function(path, mode, callback) {
|
2016-07-22 16:38:36 -07:00
|
|
|
callback = makeCallback(callback);
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(path);
|
2017-12-13 14:24:34 -08:00
|
|
|
mode = modeNum(mode);
|
2017-12-22 19:19:33 -05:00
|
|
|
validateUint32(mode, 'mode');
|
2017-12-13 14:24:34 -08:00
|
|
|
|
|
|
|
const req = new FSReqWrap();
|
2014-12-09 05:29:47 +01:00
|
|
|
req.oncomplete = callback;
|
2017-12-13 14:24:34 -08:00
|
|
|
binding.chmod(pathModule.toNamespacedPath(path), mode, req);
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2010-12-01 17:43:30 -08:00
|
|
|
fs.chmodSync = function(path, mode) {
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(path);
|
2017-12-13 14:24:34 -08:00
|
|
|
mode = modeNum(mode);
|
2017-12-22 19:19:33 -05:00
|
|
|
validateUint32(mode, 'mode');
|
2017-12-13 14:24:34 -08:00
|
|
|
return binding.chmod(pathModule.toNamespacedPath(path), mode);
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2016-12-26 16:42:09 +05:30
|
|
|
if (constants.O_SYMLINK !== undefined) {
|
2011-03-29 16:34:05 -07:00
|
|
|
fs.lchown = function(path, uid, gid, callback) {
|
2016-07-22 16:38:36 -07:00
|
|
|
callback = maybeCallback(callback);
|
2011-10-04 18:08:18 -04:00
|
|
|
fs.open(path, constants.O_WRONLY | constants.O_SYMLINK, function(err, fd) {
|
2011-03-29 16:34:05 -07:00
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
return;
|
|
|
|
}
|
2018-01-23 15:07:42 -08:00
|
|
|
// Prefer to return the chown error, if one occurs,
|
|
|
|
// but still try to close, and report closing errors if they occur.
|
|
|
|
fs.fchown(fd, uid, gid, function(err) {
|
|
|
|
fs.close(fd, function(err2) {
|
|
|
|
callback(err || err2);
|
|
|
|
});
|
|
|
|
});
|
2011-03-29 16:34:05 -07:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
fs.lchownSync = function(path, uid, gid) {
|
2018-01-23 15:07:42 -08:00
|
|
|
const fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK);
|
|
|
|
let ret;
|
|
|
|
try {
|
|
|
|
ret = fs.fchownSync(fd, uid, gid);
|
|
|
|
} finally {
|
|
|
|
fs.closeSync(fd);
|
|
|
|
}
|
|
|
|
return ret;
|
2011-03-29 16:34:05 -07:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2011-03-29 15:31:41 -07:00
|
|
|
fs.fchown = function(fd, uid, gid, callback) {
|
2017-12-22 19:19:33 -05:00
|
|
|
validateUint32(fd, 'fd');
|
|
|
|
validateUint32(uid, 'uid');
|
|
|
|
validateUint32(gid, 'gid');
|
2017-11-26 16:53:33 -08:00
|
|
|
|
|
|
|
const req = new FSReqWrap();
|
2016-07-22 16:38:36 -07:00
|
|
|
req.oncomplete = makeCallback(callback);
|
2016-07-22 16:37:54 -07:00
|
|
|
binding.fchown(fd, uid, gid, req);
|
2011-03-29 15:31:41 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
fs.fchownSync = function(fd, uid, gid) {
|
2017-12-22 19:19:33 -05:00
|
|
|
validateUint32(fd, 'fd');
|
|
|
|
validateUint32(uid, 'uid');
|
|
|
|
validateUint32(gid, 'gid');
|
2017-11-26 16:53:33 -08:00
|
|
|
|
2016-07-22 16:37:54 -07:00
|
|
|
return binding.fchown(fd, uid, gid);
|
2011-03-29 15:31:41 -07:00
|
|
|
};
|
|
|
|
|
2010-05-27 03:49:01 +10:00
|
|
|
fs.chown = function(path, uid, gid, callback) {
|
2016-07-22 16:38:36 -07:00
|
|
|
callback = makeCallback(callback);
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(path);
|
2017-12-22 19:19:33 -05:00
|
|
|
validateUint32(uid, 'uid');
|
|
|
|
validateUint32(gid, 'gid');
|
2017-12-13 14:24:34 -08:00
|
|
|
|
|
|
|
const req = new FSReqWrap();
|
2014-12-09 05:29:47 +01:00
|
|
|
req.oncomplete = callback;
|
2017-08-20 22:44:47 -07:00
|
|
|
binding.chown(pathModule.toNamespacedPath(path), uid, gid, req);
|
2010-05-27 03:49:01 +10:00
|
|
|
};
|
|
|
|
|
|
|
|
fs.chownSync = function(path, uid, gid) {
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(path);
|
2017-12-22 19:19:33 -05:00
|
|
|
validateUint32(uid, 'uid');
|
|
|
|
validateUint32(gid, 'gid');
|
2017-08-20 22:44:47 -07:00
|
|
|
return binding.chown(pathModule.toNamespacedPath(path), uid, gid);
|
2010-05-27 03:49:01 +10:00
|
|
|
};
|
|
|
|
|
2010-10-29 12:38:13 +02:00
|
|
|
// converts Date or number to a fractional UNIX timestamp
|
2017-11-26 17:05:26 -08:00
|
|
|
function toUnixTimestamp(time, name = 'time') {
|
2017-04-16 11:29:35 -07:00
|
|
|
// eslint-disable-next-line eqeqeq
|
2015-08-16 00:30:31 +08:00
|
|
|
if (typeof time === 'string' && +time == time) {
|
|
|
|
return +time;
|
|
|
|
}
|
2017-03-17 17:07:19 +01:00
|
|
|
if (Number.isFinite(time)) {
|
|
|
|
if (time < 0) {
|
2015-08-16 00:30:31 +08:00
|
|
|
return Date.now() / 1000;
|
|
|
|
}
|
2010-10-29 12:38:13 +02:00
|
|
|
return time;
|
|
|
|
}
|
2013-07-26 14:38:08 -07:00
|
|
|
if (util.isDate(time)) {
|
2010-10-29 12:38:13 +02:00
|
|
|
// convert to 123.456 UNIX timestamp
|
|
|
|
return time.getTime() / 1000;
|
|
|
|
}
|
2017-11-26 17:05:26 -08:00
|
|
|
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
|
|
|
|
name,
|
|
|
|
['Date', 'Time in seconds'],
|
|
|
|
time);
|
2010-10-29 12:38:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// exported for unit tests, not for public consumption
|
|
|
|
fs._toUnixTimestamp = toUnixTimestamp;
|
|
|
|
|
|
|
|
fs.utimes = function(path, atime, mtime, callback) {
|
2016-07-22 16:38:36 -07:00
|
|
|
callback = makeCallback(callback);
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(path);
|
2017-12-13 14:24:34 -08:00
|
|
|
|
|
|
|
const req = new FSReqWrap();
|
2014-12-09 05:29:47 +01:00
|
|
|
req.oncomplete = callback;
|
2017-08-20 22:44:47 -07:00
|
|
|
binding.utimes(pathModule.toNamespacedPath(path),
|
2012-06-06 21:33:29 +02:00
|
|
|
toUnixTimestamp(atime),
|
|
|
|
toUnixTimestamp(mtime),
|
2014-12-09 05:29:47 +01:00
|
|
|
req);
|
2010-10-29 12:38:13 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
fs.utimesSync = function(path, atime, mtime) {
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(path);
|
2017-12-13 14:24:34 -08:00
|
|
|
binding.utimes(pathModule.toNamespacedPath(path),
|
|
|
|
toUnixTimestamp(atime),
|
|
|
|
toUnixTimestamp(mtime));
|
2010-10-29 12:38:13 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
fs.futimes = function(fd, atime, mtime, callback) {
|
2017-12-22 19:19:33 -05:00
|
|
|
validateUint32(fd, 'fd');
|
2017-11-26 17:05:26 -08:00
|
|
|
atime = toUnixTimestamp(atime, 'atime');
|
|
|
|
mtime = toUnixTimestamp(mtime, 'mtime');
|
|
|
|
const req = new FSReqWrap();
|
2016-07-22 16:38:36 -07:00
|
|
|
req.oncomplete = makeCallback(callback);
|
2014-12-09 05:29:47 +01:00
|
|
|
binding.futimes(fd, atime, mtime, req);
|
2010-10-29 12:38:13 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
fs.futimesSync = function(fd, atime, mtime) {
|
2017-12-22 19:19:33 -05:00
|
|
|
validateUint32(fd, 'fd');
|
2017-11-26 17:05:26 -08:00
|
|
|
atime = toUnixTimestamp(atime, 'atime');
|
|
|
|
mtime = toUnixTimestamp(mtime, 'mtime');
|
2010-10-29 12:38:13 +02:00
|
|
|
binding.futimes(fd, atime, mtime);
|
|
|
|
};
|
|
|
|
|
2017-04-24 02:20:45 -04:00
|
|
|
function writeAll(fd, isUserFd, buffer, offset, length, position, callback) {
|
2010-11-13 20:12:47 -08:00
|
|
|
// write(fd, buffer, offset, length, position, callback)
|
2011-11-02 16:06:16 -02:00
|
|
|
fs.write(fd, buffer, offset, length, position, function(writeErr, written) {
|
2010-03-01 10:14:49 -08:00
|
|
|
if (writeErr) {
|
2015-10-03 02:06:42 +02:00
|
|
|
if (isUserFd) {
|
2016-01-21 17:35:10 +02:00
|
|
|
callback(writeErr);
|
2015-10-03 02:06:42 +02:00
|
|
|
} else {
|
|
|
|
fs.close(fd, function() {
|
2016-01-21 17:35:10 +02:00
|
|
|
callback(writeErr);
|
2015-10-03 02:06:42 +02:00
|
|
|
});
|
|
|
|
}
|
2017-10-16 18:37:14 -04:00
|
|
|
} else if (written === length) {
|
|
|
|
if (isUserFd) {
|
|
|
|
callback(null);
|
2010-03-01 10:14:49 -08:00
|
|
|
} else {
|
2017-10-16 18:37:14 -04:00
|
|
|
fs.close(fd, callback);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
offset += written;
|
|
|
|
length -= written;
|
|
|
|
if (position !== null) {
|
|
|
|
position += written;
|
2010-03-01 10:14:49 -08:00
|
|
|
}
|
2017-10-16 18:37:14 -04:00
|
|
|
writeAll(fd, isUserFd, buffer, offset, length, position, callback);
|
2010-03-01 10:14:49 -08:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-06-26 00:03:05 +05:30
|
|
|
fs.writeFile = function(path, data, options, callback) {
|
2017-04-24 02:20:45 -04:00
|
|
|
callback = maybeCallback(callback || options);
|
2016-06-26 00:03:05 +05:30
|
|
|
options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'w' });
|
|
|
|
const flag = options.flag || 'w';
|
2015-10-03 02:06:42 +02:00
|
|
|
|
2016-07-22 16:37:54 -07:00
|
|
|
if (isFd(path)) {
|
2015-10-03 02:06:42 +02:00
|
|
|
writeFd(path, true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-06-07 11:53:23 -04:00
|
|
|
fs.open(path, flag, options.mode, function(openErr, fd) {
|
2010-03-01 10:14:49 -08:00
|
|
|
if (openErr) {
|
2016-01-21 17:35:10 +02:00
|
|
|
callback(openErr);
|
2010-03-01 10:14:49 -08:00
|
|
|
} else {
|
2015-10-03 02:06:42 +02:00
|
|
|
writeFd(fd, false);
|
2010-03-01 10:14:49 -08:00
|
|
|
}
|
|
|
|
});
|
2015-10-03 02:06:42 +02:00
|
|
|
|
|
|
|
function writeFd(fd, isUserFd) {
|
2016-12-21 08:27:34 +01:00
|
|
|
var buffer = isUint8Array(data) ?
|
2017-07-05 09:21:40 -07:00
|
|
|
data : Buffer.from('' + data, options.encoding || 'utf8');
|
2015-10-03 02:06:42 +02:00
|
|
|
var position = /a/.test(flag) ? null : 0;
|
|
|
|
|
|
|
|
writeAll(fd, isUserFd, buffer, 0, buffer.length, position, callback);
|
|
|
|
}
|
2010-03-01 10:14:49 -08:00
|
|
|
};
|
|
|
|
|
2013-03-01 09:10:26 -08:00
|
|
|
fs.writeFileSync = function(path, data, options) {
|
2016-06-26 00:03:05 +05:30
|
|
|
options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'w' });
|
|
|
|
const flag = options.flag || 'w';
|
2013-03-01 09:10:26 -08:00
|
|
|
|
2016-07-22 16:37:54 -07:00
|
|
|
var isUserFd = isFd(path); // file descriptor ownership
|
2015-10-03 02:06:42 +02:00
|
|
|
var fd = isUserFd ? path : fs.openSync(path, flag, options.mode);
|
|
|
|
|
2016-12-21 08:27:34 +01:00
|
|
|
if (!isUint8Array(data)) {
|
2016-01-25 15:00:06 -08:00
|
|
|
data = Buffer.from('' + data, options.encoding || 'utf8');
|
2010-11-13 20:12:47 -08:00
|
|
|
}
|
2015-03-05 01:03:34 +01:00
|
|
|
var offset = 0;
|
2010-11-13 20:12:47 -08:00
|
|
|
var length = data.length;
|
2013-03-01 09:10:26 -08:00
|
|
|
var position = /a/.test(flag) ? null : 0;
|
2012-05-03 01:03:08 +02:00
|
|
|
try {
|
2015-03-05 01:03:34 +01:00
|
|
|
while (length > 0) {
|
|
|
|
var written = fs.writeSync(fd, data, offset, length, position);
|
|
|
|
offset += written;
|
|
|
|
length -= written;
|
|
|
|
if (position !== null) {
|
|
|
|
position += written;
|
|
|
|
}
|
2012-05-03 01:03:08 +02:00
|
|
|
}
|
|
|
|
} finally {
|
2015-10-03 02:06:42 +02:00
|
|
|
if (!isUserFd) fs.closeSync(fd);
|
2010-03-01 10:14:49 -08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-06-26 00:03:05 +05:30
|
|
|
fs.appendFile = function(path, data, options, callback) {
|
2017-02-28 16:13:49 +02:00
|
|
|
callback = maybeCallback(callback || options);
|
2016-06-26 00:03:05 +05:30
|
|
|
options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'a' });
|
2011-11-02 16:06:16 -02:00
|
|
|
|
2016-10-09 19:37:59 +05:30
|
|
|
// Don't make changes directly on options object
|
|
|
|
options = copyObject(options);
|
2015-10-03 02:06:42 +02:00
|
|
|
|
|
|
|
// force append behavior when using a supplied file descriptor
|
2016-10-09 19:37:59 +05:30
|
|
|
if (!options.flag || isFd(path))
|
2015-10-03 02:06:42 +02:00
|
|
|
options.flag = 'a';
|
|
|
|
|
2013-03-01 09:10:26 -08:00
|
|
|
fs.writeFile(path, data, options, callback);
|
2011-11-02 16:06:16 -02:00
|
|
|
};
|
|
|
|
|
2013-03-01 09:10:26 -08:00
|
|
|
fs.appendFileSync = function(path, data, options) {
|
2016-06-26 00:03:05 +05:30
|
|
|
options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'a' });
|
2015-10-03 02:06:42 +02:00
|
|
|
|
2016-10-09 19:37:59 +05:30
|
|
|
// Don't make changes directly on options object
|
|
|
|
options = copyObject(options);
|
2011-11-02 16:06:16 -02:00
|
|
|
|
2015-10-03 02:06:42 +02:00
|
|
|
// force append behavior when using a supplied file descriptor
|
2016-10-09 19:37:59 +05:30
|
|
|
if (!options.flag || isFd(path))
|
2015-10-03 02:06:42 +02:00
|
|
|
options.flag = 'a';
|
|
|
|
|
2013-03-01 09:10:26 -08:00
|
|
|
fs.writeFileSync(path, data, options);
|
2011-11-02 16:06:16 -02:00
|
|
|
};
|
2011-09-22 16:18:08 -07:00
|
|
|
|
2011-09-23 00:45:41 -07:00
|
|
|
function FSWatcher() {
|
2012-06-12 21:53:08 +02:00
|
|
|
EventEmitter.call(this);
|
|
|
|
|
2011-09-22 16:18:08 -07:00
|
|
|
var self = this;
|
|
|
|
this._handle = new FSEvent();
|
2012-04-27 04:39:05 +02:00
|
|
|
this._handle.owner = this;
|
2011-09-22 16:18:08 -07:00
|
|
|
|
2016-07-01 09:33:20 +01:00
|
|
|
this._handle.onchange = function(status, eventType, filename) {
|
2013-07-18 23:18:50 +02:00
|
|
|
if (status < 0) {
|
2012-05-12 19:37:33 +02:00
|
|
|
self._handle.close();
|
2016-03-17 18:14:37 -07:00
|
|
|
const error = !filename ?
|
2017-07-04 17:59:36 -07:00
|
|
|
errnoException(status, 'Error watching file for changes:') :
|
|
|
|
errnoException(status, `Error watching file ${filename} for changes:`);
|
2015-09-29 22:03:27 -07:00
|
|
|
error.filename = filename;
|
|
|
|
self.emit('error', error);
|
2011-09-22 16:18:08 -07:00
|
|
|
} else {
|
2016-07-01 09:33:20 +01:00
|
|
|
self.emit('change', eventType, filename);
|
2011-09-22 16:18:08 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
2011-09-23 00:45:41 -07:00
|
|
|
util.inherits(FSWatcher, EventEmitter);
|
2011-09-22 16:18:08 -07:00
|
|
|
|
2018-01-23 10:23:46 +08:00
|
|
|
// FIXME(joyeecheung): this method is not documented.
|
|
|
|
// At the moment if filename is undefined, we
|
|
|
|
// 1. Throw an Error from C++ land if it's the first time .start() is called
|
|
|
|
// 2. Return silently from C++ land if .start() has already been called
|
|
|
|
// on a valid filename and the wrap has been initialized
|
fs: Buffer and encoding enhancements to fs API
This makes several changes:
1. Allow path/filename to be passed in as a Buffer on fs methods
2. Add `options.encoding` to fs.readdir, fs.readdirSync, fs.readlink,
fs.readlinkSync and fs.watch.
3. Documentation updates
For 1... it's now possible to do:
```js
fs.open(Buffer('/fs/foo/bar'), 'w+', (err, fd) => { });
```
For 2...
```js
fs.readdir('/fs/foo/bar', {encoding:'hex'}, (err,list) => { });
fs.readdir('/fs/foo/bar', {encoding:'buffer'}, (err, list) => { });
```
encoding can also be passed as a string
```js
fs.readdir('/fs/foo/bar', 'hex', (err,list) => { });
```
The default encoding is set to UTF8 so this addresses the
discrepency that existed previously between fs.readdir and
fs.watch handling filenames differently.
Fixes: https://github.com/nodejs/node/issues/2088
Refs: https://github.com/nodejs/node/issues/3519
PR-URL: https://github.com/nodejs/node/pull/5616
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
2016-03-08 20:58:45 -08:00
|
|
|
FSWatcher.prototype.start = function(filename,
|
|
|
|
persistent,
|
|
|
|
recursive,
|
|
|
|
encoding) {
|
2018-01-23 10:23:46 +08:00
|
|
|
filename = getPathFromURL(filename);
|
|
|
|
nullCheck(filename, 'filename');
|
2017-08-20 22:44:47 -07:00
|
|
|
var err = this._handle.start(pathModule.toNamespacedPath(filename),
|
2013-10-21 22:08:28 -04:00
|
|
|
persistent,
|
fs: Buffer and encoding enhancements to fs API
This makes several changes:
1. Allow path/filename to be passed in as a Buffer on fs methods
2. Add `options.encoding` to fs.readdir, fs.readdirSync, fs.readlink,
fs.readlinkSync and fs.watch.
3. Documentation updates
For 1... it's now possible to do:
```js
fs.open(Buffer('/fs/foo/bar'), 'w+', (err, fd) => { });
```
For 2...
```js
fs.readdir('/fs/foo/bar', {encoding:'hex'}, (err,list) => { });
fs.readdir('/fs/foo/bar', {encoding:'buffer'}, (err, list) => { });
```
encoding can also be passed as a string
```js
fs.readdir('/fs/foo/bar', 'hex', (err,list) => { });
```
The default encoding is set to UTF8 so this addresses the
discrepency that existed previously between fs.readdir and
fs.watch handling filenames differently.
Fixes: https://github.com/nodejs/node/issues/2088
Refs: https://github.com/nodejs/node/issues/3519
PR-URL: https://github.com/nodejs/node/pull/5616
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
2016-03-08 20:58:45 -08:00
|
|
|
recursive,
|
|
|
|
encoding);
|
2013-07-18 23:18:50 +02:00
|
|
|
if (err) {
|
2011-09-22 16:18:08 -07:00
|
|
|
this._handle.close();
|
2015-09-29 22:03:27 -07:00
|
|
|
const error = errnoException(err, `watch ${filename}`);
|
|
|
|
error.filename = filename;
|
|
|
|
throw error;
|
2011-09-22 16:18:08 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2011-09-23 00:45:41 -07:00
|
|
|
FSWatcher.prototype.close = function() {
|
2011-09-22 16:18:08 -07:00
|
|
|
this._handle.close();
|
|
|
|
};
|
|
|
|
|
fs: Buffer and encoding enhancements to fs API
This makes several changes:
1. Allow path/filename to be passed in as a Buffer on fs methods
2. Add `options.encoding` to fs.readdir, fs.readdirSync, fs.readlink,
fs.readlinkSync and fs.watch.
3. Documentation updates
For 1... it's now possible to do:
```js
fs.open(Buffer('/fs/foo/bar'), 'w+', (err, fd) => { });
```
For 2...
```js
fs.readdir('/fs/foo/bar', {encoding:'hex'}, (err,list) => { });
fs.readdir('/fs/foo/bar', {encoding:'buffer'}, (err, list) => { });
```
encoding can also be passed as a string
```js
fs.readdir('/fs/foo/bar', 'hex', (err,list) => { });
```
The default encoding is set to UTF8 so this addresses the
discrepency that existed previously between fs.readdir and
fs.watch handling filenames differently.
Fixes: https://github.com/nodejs/node/issues/2088
Refs: https://github.com/nodejs/node/issues/3519
PR-URL: https://github.com/nodejs/node/pull/5616
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
2016-03-08 20:58:45 -08:00
|
|
|
fs.watch = function(filename, options, listener) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
listener = options;
|
2011-09-22 16:18:08 -07:00
|
|
|
}
|
2016-06-26 00:03:05 +05:30
|
|
|
options = getOptions(options, {});
|
2011-09-22 16:18:08 -07:00
|
|
|
|
2016-10-09 19:37:59 +05:30
|
|
|
// Don't make changes directly on options object
|
|
|
|
options = copyObject(options);
|
|
|
|
|
2015-01-28 20:05:53 -05:00
|
|
|
if (options.persistent === undefined) options.persistent = true;
|
|
|
|
if (options.recursive === undefined) options.recursive = false;
|
2011-09-22 16:18:08 -07:00
|
|
|
|
fs: Buffer and encoding enhancements to fs API
This makes several changes:
1. Allow path/filename to be passed in as a Buffer on fs methods
2. Add `options.encoding` to fs.readdir, fs.readdirSync, fs.readlink,
fs.readlinkSync and fs.watch.
3. Documentation updates
For 1... it's now possible to do:
```js
fs.open(Buffer('/fs/foo/bar'), 'w+', (err, fd) => { });
```
For 2...
```js
fs.readdir('/fs/foo/bar', {encoding:'hex'}, (err,list) => { });
fs.readdir('/fs/foo/bar', {encoding:'buffer'}, (err, list) => { });
```
encoding can also be passed as a string
```js
fs.readdir('/fs/foo/bar', 'hex', (err,list) => { });
```
The default encoding is set to UTF8 so this addresses the
discrepency that existed previously between fs.readdir and
fs.watch handling filenames differently.
Fixes: https://github.com/nodejs/node/issues/2088
Refs: https://github.com/nodejs/node/issues/3519
PR-URL: https://github.com/nodejs/node/pull/5616
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
2016-03-08 20:58:45 -08:00
|
|
|
const watcher = new FSWatcher();
|
|
|
|
watcher.start(filename,
|
|
|
|
options.persistent,
|
|
|
|
options.recursive,
|
|
|
|
options.encoding);
|
2011-09-22 16:18:08 -07:00
|
|
|
|
2012-05-19 23:05:43 +02:00
|
|
|
if (listener) {
|
|
|
|
watcher.addListener('change', listener);
|
|
|
|
}
|
|
|
|
|
2011-09-22 16:18:08 -07:00
|
|
|
return watcher;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2010-03-01 10:42:37 -08:00
|
|
|
// Stat Change Watchers
|
|
|
|
|
2016-09-06 17:34:18 +01:00
|
|
|
function emitStop(self) {
|
|
|
|
self.emit('stop');
|
|
|
|
}
|
|
|
|
|
2017-03-11 19:41:20 -05:00
|
|
|
function statsFromPrevValues() {
|
|
|
|
return new Stats(statValues[14], statValues[15], statValues[16],
|
|
|
|
statValues[17], statValues[18], statValues[19],
|
|
|
|
statValues[20] < 0 ? undefined : statValues[20],
|
|
|
|
statValues[21], statValues[22],
|
|
|
|
statValues[23] < 0 ? undefined : statValues[23],
|
|
|
|
statValues[24], statValues[25], statValues[26],
|
|
|
|
statValues[27]);
|
|
|
|
}
|
2011-07-19 01:23:50 -07:00
|
|
|
function StatWatcher() {
|
2012-06-12 21:53:08 +02:00
|
|
|
EventEmitter.call(this);
|
|
|
|
|
2011-07-19 01:23:50 -07:00
|
|
|
var self = this;
|
|
|
|
this._handle = new binding.StatWatcher();
|
|
|
|
|
2012-06-16 22:41:37 +02:00
|
|
|
// uv_fs_poll is a little more powerful than ev_stat but we curb it for
|
|
|
|
// the sake of backwards compatibility
|
|
|
|
var oldStatus = -1;
|
|
|
|
|
2017-03-11 19:41:20 -05:00
|
|
|
this._handle.onchange = function(newStatus) {
|
2012-09-17 17:40:23 +02:00
|
|
|
if (oldStatus === -1 &&
|
|
|
|
newStatus === -1 &&
|
2017-03-11 19:41:20 -05:00
|
|
|
statValues[2/*new nlink*/] === statValues[16/*old nlink*/]) return;
|
2012-09-17 17:40:23 +02:00
|
|
|
|
2012-06-16 22:41:37 +02:00
|
|
|
oldStatus = newStatus;
|
2017-03-11 19:41:20 -05:00
|
|
|
self.emit('change', statsFromValues(), statsFromPrevValues());
|
2011-07-19 01:23:50 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
this._handle.onstop = function() {
|
2016-09-06 17:34:18 +01:00
|
|
|
process.nextTick(emitStop, self);
|
2011-07-19 01:23:50 -07:00
|
|
|
};
|
|
|
|
}
|
|
|
|
util.inherits(StatWatcher, EventEmitter);
|
|
|
|
|
|
|
|
|
2018-01-23 10:23:46 +08:00
|
|
|
// FIXME(joyeecheung): this method is not documented.
|
|
|
|
// At the moment if filename is undefined, we
|
|
|
|
// 1. Throw an Error from C++ land if it's the first time .start() is called
|
|
|
|
// 2. Return silently from C++ land if .start() has already been called
|
|
|
|
// on a valid filename and the wrap has been initialized
|
2011-07-19 01:23:50 -07:00
|
|
|
StatWatcher.prototype.start = function(filename, persistent, interval) {
|
2018-01-23 10:23:46 +08:00
|
|
|
filename = getPathFromURL(filename);
|
|
|
|
nullCheck(filename, 'filename');
|
2017-08-20 22:44:47 -07:00
|
|
|
this._handle.start(pathModule.toNamespacedPath(filename),
|
|
|
|
persistent, interval);
|
2011-07-19 01:23:50 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
StatWatcher.prototype.stop = function() {
|
|
|
|
this._handle.stop();
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2015-06-02 22:32:15 +05:30
|
|
|
const statWatchers = new Map();
|
2011-07-19 01:23:50 -07:00
|
|
|
|
2015-07-01 08:14:52 -07:00
|
|
|
fs.watchFile = function(filename, options, listener) {
|
2018-01-23 10:23:46 +08:00
|
|
|
filename = getPathFromURL(filename);
|
|
|
|
validatePath(filename);
|
2014-02-06 08:29:58 +02:00
|
|
|
filename = pathModule.resolve(filename);
|
2010-03-01 10:42:37 -08:00
|
|
|
var stat;
|
|
|
|
|
2015-07-01 08:14:52 -07:00
|
|
|
var defaults = {
|
2012-06-21 15:03:21 +02:00
|
|
|
// 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,
|
2012-06-21 11:42:33 -07:00
|
|
|
persistent: true
|
2012-06-21 15:03:21 +02:00
|
|
|
};
|
|
|
|
|
2015-07-01 08:14:52 -07:00
|
|
|
if (options !== null && typeof options === 'object') {
|
|
|
|
options = util._extend(defaults, options);
|
2010-03-01 10:42:37 -08:00
|
|
|
} else {
|
2016-07-22 16:38:36 -07:00
|
|
|
listener = options;
|
2015-07-01 08:14:52 -07:00
|
|
|
options = defaults;
|
2010-03-01 10:42:37 -08:00
|
|
|
}
|
|
|
|
|
2016-07-22 16:38:36 -07:00
|
|
|
if (typeof listener !== 'function') {
|
2017-08-26 07:46:47 -04:00
|
|
|
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
|
|
|
|
'listener',
|
2017-10-28 17:39:55 +08:00
|
|
|
'Function',
|
2017-08-26 07:46:47 -04:00
|
|
|
listener);
|
2016-07-22 16:38:36 -07:00
|
|
|
}
|
|
|
|
|
2015-06-02 22:32:15 +05:30
|
|
|
stat = statWatchers.get(filename);
|
|
|
|
|
|
|
|
if (stat === undefined) {
|
|
|
|
stat = new StatWatcher();
|
2010-03-01 10:42:37 -08:00
|
|
|
stat.start(filename, options.persistent, options.interval);
|
2015-06-02 22:32:15 +05:30
|
|
|
statWatchers.set(filename, stat);
|
2010-03-01 10:42:37 -08:00
|
|
|
}
|
2015-06-02 22:32:15 +05:30
|
|
|
|
2010-12-01 17:43:30 -08:00
|
|
|
stat.addListener('change', listener);
|
2010-03-01 10:42:37 -08:00
|
|
|
return stat;
|
|
|
|
};
|
|
|
|
|
2012-07-08 16:31:07 +02:00
|
|
|
fs.unwatchFile = function(filename, listener) {
|
2018-01-23 10:23:46 +08:00
|
|
|
filename = getPathFromURL(filename);
|
|
|
|
validatePath(filename);
|
2014-02-06 08:29:58 +02:00
|
|
|
filename = pathModule.resolve(filename);
|
2015-06-02 22:32:15 +05:30
|
|
|
var stat = statWatchers.get(filename);
|
2012-07-08 16:31:07 +02:00
|
|
|
|
2015-06-02 22:32:15 +05:30
|
|
|
if (stat === undefined) return;
|
2012-07-08 16:31:07 +02:00
|
|
|
|
2015-01-28 20:05:53 -05:00
|
|
|
if (typeof listener === 'function') {
|
2012-07-08 16:31:07 +02:00
|
|
|
stat.removeListener('change', listener);
|
|
|
|
} else {
|
|
|
|
stat.removeAllListeners('change');
|
|
|
|
}
|
|
|
|
|
2015-08-12 00:01:50 +05:30
|
|
|
if (stat.listenerCount('change') === 0) {
|
2010-03-01 10:42:37 -08:00
|
|
|
stat.stop();
|
2015-06-02 22:32:15 +05:30
|
|
|
statWatchers.delete(filename);
|
2010-03-01 10:42:37 -08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2010-09-09 18:49:28 -07:00
|
|
|
|
2017-03-11 23:59:37 -05:00
|
|
|
var 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) !== 47/*'/'*/)
|
|
|
|
return str.slice(0, i);
|
|
|
|
}
|
|
|
|
return str;
|
|
|
|
};
|
|
|
|
}
|
2016-07-27 00:18:35 +02:00
|
|
|
|
2017-01-18 11:50:33 +08:00
|
|
|
function encodeRealpathResult(result, options) {
|
|
|
|
if (!options || !options.encoding || options.encoding === 'utf8')
|
2016-07-27 00:18:35 +02:00
|
|
|
return result;
|
|
|
|
const asBuffer = Buffer.from(result);
|
|
|
|
if (options.encoding === 'buffer') {
|
|
|
|
return asBuffer;
|
|
|
|
} else {
|
|
|
|
return asBuffer.toString(options.encoding);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-13 05:09:52 -05:00
|
|
|
// Finds the next portion of a (partial) path, up to the next path delimiter
|
|
|
|
var nextPart;
|
|
|
|
if (isWindows) {
|
|
|
|
nextPart = function nextPart(p, i) {
|
|
|
|
for (; i < p.length; ++i) {
|
|
|
|
const ch = p.charCodeAt(i);
|
|
|
|
if (ch === 92/*'\'*/ || ch === 47/*'/'*/)
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
nextPart = function nextPart(p, i) { return p.indexOf('/', i); };
|
|
|
|
}
|
|
|
|
|
2017-03-19 16:11:10 -07:00
|
|
|
const emptyObj = Object.create(null);
|
2016-07-27 00:18:35 +02:00
|
|
|
fs.realpathSync = function realpathSync(p, options) {
|
2017-03-11 23:59:37 -05:00
|
|
|
if (!options)
|
|
|
|
options = emptyObj;
|
|
|
|
else
|
|
|
|
options = getOptions(options, emptyObj);
|
2018-01-23 10:23:46 +08:00
|
|
|
p = getPathFromURL(p);
|
2017-03-11 23:59:37 -05:00
|
|
|
if (typeof p !== 'string') {
|
2018-01-23 10:23:46 +08:00
|
|
|
p += '';
|
2017-03-11 23:59:37 -05:00
|
|
|
}
|
2018-01-23 10:23:46 +08:00
|
|
|
validatePath(p);
|
2016-07-27 00:18:35 +02:00
|
|
|
p = pathModule.resolve(p);
|
|
|
|
|
2016-09-30 16:16:28 +02:00
|
|
|
const cache = options[internalFS.realpathCacheKey];
|
2016-08-14 18:41:28 +02:00
|
|
|
const maybeCachedResult = cache && cache.get(p);
|
|
|
|
if (maybeCachedResult) {
|
|
|
|
return maybeCachedResult;
|
|
|
|
}
|
2016-07-27 00:18:35 +02:00
|
|
|
|
2017-03-19 16:11:10 -07:00
|
|
|
const seenLinks = Object.create(null);
|
|
|
|
const knownHard = Object.create(null);
|
2017-01-13 05:07:18 -05:00
|
|
|
const original = p;
|
|
|
|
|
2016-07-27 00:18:35 +02:00
|
|
|
// current character position in p
|
|
|
|
var pos;
|
|
|
|
// the partial path so far, including a trailing slash if any
|
|
|
|
var current;
|
|
|
|
// the partial path without a trailing slash (except when pointing at a root)
|
|
|
|
var base;
|
|
|
|
// the partial path scanned in the previous round, with slash
|
|
|
|
var previous;
|
|
|
|
|
2017-01-13 05:04:18 -05:00
|
|
|
// Skip over roots
|
2017-03-11 23:59:37 -05:00
|
|
|
current = base = splitRoot(p);
|
|
|
|
pos = current.length;
|
2016-07-27 00:18:35 +02:00
|
|
|
|
2017-01-13 05:04:18 -05:00
|
|
|
// On windows, check that the root exists. On unix there is no need.
|
|
|
|
if (isWindows && !knownHard[base]) {
|
2017-12-28 14:51:05 +08:00
|
|
|
const ctx = { path: base };
|
|
|
|
binding.lstat(pathModule.toNamespacedPath(base), undefined, ctx);
|
|
|
|
if (ctx.errno !== undefined) {
|
2018-02-03 17:35:04 +08:00
|
|
|
throw errors.uvException(ctx);
|
2017-12-28 14:51:05 +08:00
|
|
|
}
|
2017-01-13 05:04:18 -05:00
|
|
|
knownHard[base] = true;
|
2016-07-27 00:18:35 +02:00
|
|
|
}
|
|
|
|
|
2017-06-17 15:11:45 +02:00
|
|
|
// walk down the path, swapping out linked path parts for their real
|
2016-07-27 00:18:35 +02:00
|
|
|
// values
|
|
|
|
// NB: p.length changes.
|
|
|
|
while (pos < p.length) {
|
|
|
|
// find the next part
|
2017-01-13 05:09:52 -05:00
|
|
|
var result = nextPart(p, pos);
|
2016-07-27 00:18:35 +02:00
|
|
|
previous = current;
|
2017-01-13 05:09:52 -05:00
|
|
|
if (result === -1) {
|
|
|
|
var 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;
|
|
|
|
}
|
2016-07-27 00:18:35 +02:00
|
|
|
|
2017-05-15 03:27:54 +04:30
|
|
|
// continue if not a symlink, break if a pipe/socket
|
2016-08-14 18:41:28 +02:00
|
|
|
if (knownHard[base] || (cache && cache.get(base) === base)) {
|
2017-05-15 03:27:54 +04:30
|
|
|
if ((statValues[1/*mode*/] & S_IFMT) === S_IFIFO ||
|
|
|
|
(statValues[1/*mode*/] & S_IFMT) === S_IFSOCK) {
|
|
|
|
break;
|
|
|
|
}
|
2016-07-27 00:18:35 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
var resolvedLink;
|
2017-01-13 05:11:11 -05:00
|
|
|
var maybeCachedResolved = cache && cache.get(base);
|
2016-08-14 18:41:28 +02:00
|
|
|
if (maybeCachedResolved) {
|
|
|
|
resolvedLink = maybeCachedResolved;
|
|
|
|
} else {
|
2017-03-11 19:41:20 -05:00
|
|
|
// Use stats array directly to avoid creating an fs.Stats instance just
|
|
|
|
// for our internal use.
|
|
|
|
|
2017-08-20 22:44:47 -07:00
|
|
|
var baseLong = pathModule.toNamespacedPath(base);
|
2017-12-28 14:51:05 +08:00
|
|
|
const ctx = { path: base };
|
|
|
|
binding.lstat(baseLong, undefined, ctx);
|
|
|
|
if (ctx.errno !== undefined) {
|
2018-02-03 17:35:04 +08:00
|
|
|
throw errors.uvException(ctx);
|
2017-12-28 14:51:05 +08:00
|
|
|
}
|
2017-03-11 19:41:20 -05:00
|
|
|
|
|
|
|
if ((statValues[1/*mode*/] & S_IFMT) !== S_IFLNK) {
|
2016-08-14 18:41:28 +02:00
|
|
|
knownHard[base] = true;
|
2016-12-12 15:56:36 +00:00
|
|
|
if (cache) cache.set(base, base);
|
2016-08-14 18:41:28 +02:00
|
|
|
continue;
|
|
|
|
}
|
2016-07-27 00:18:35 +02:00
|
|
|
|
2016-08-14 18:41:28 +02:00
|
|
|
// read the link if it wasn't read before
|
|
|
|
// dev/ino always return 0 on windows, so skip the check.
|
2017-01-13 05:11:11 -05:00
|
|
|
var linkTarget = null;
|
|
|
|
var id;
|
2016-08-14 18:41:28 +02:00
|
|
|
if (!isWindows) {
|
2017-03-11 19:41:20 -05:00
|
|
|
var dev = statValues[0/*dev*/].toString(32);
|
|
|
|
var ino = statValues[7/*ino*/].toString(32);
|
|
|
|
id = `${dev}:${ino}`;
|
2017-03-11 23:59:37 -05:00
|
|
|
if (seenLinks[id]) {
|
2016-08-14 18:41:28 +02:00
|
|
|
linkTarget = seenLinks[id];
|
|
|
|
}
|
2016-07-27 00:18:35 +02:00
|
|
|
}
|
2016-08-14 18:41:28 +02:00
|
|
|
if (linkTarget === null) {
|
2017-12-28 04:01:45 +08:00
|
|
|
const ctx = { path: base };
|
|
|
|
binding.stat(baseLong, undefined, ctx);
|
|
|
|
if (ctx.errno !== undefined) {
|
2018-02-03 17:35:04 +08:00
|
|
|
throw errors.uvException(ctx);
|
2017-12-28 04:01:45 +08:00
|
|
|
}
|
2018-01-24 07:51:08 +08:00
|
|
|
linkTarget = binding.readlink(baseLong, undefined, undefined, ctx);
|
|
|
|
if (ctx.errno !== undefined) {
|
2018-02-03 17:35:04 +08:00
|
|
|
throw errors.uvException(ctx);
|
2018-01-24 07:51:08 +08:00
|
|
|
}
|
2016-08-14 18:41:28 +02:00
|
|
|
}
|
|
|
|
resolvedLink = pathModule.resolve(previous, linkTarget);
|
2016-07-27 00:18:35 +02:00
|
|
|
|
2016-08-14 18:41:28 +02:00
|
|
|
if (cache) cache.set(base, resolvedLink);
|
|
|
|
if (!isWindows) seenLinks[id] = linkTarget;
|
|
|
|
}
|
2016-07-27 00:18:35 +02:00
|
|
|
|
|
|
|
// resolve the link, then start over
|
|
|
|
p = pathModule.resolve(resolvedLink, p.slice(pos));
|
2017-01-13 05:04:18 -05:00
|
|
|
|
|
|
|
// Skip over roots
|
2017-03-11 23:59:37 -05:00
|
|
|
current = base = splitRoot(p);
|
|
|
|
pos = current.length;
|
2017-01-13 05:04:18 -05:00
|
|
|
|
|
|
|
// On windows, check that the root exists. On unix there is no need.
|
|
|
|
if (isWindows && !knownHard[base]) {
|
2017-12-28 14:51:05 +08:00
|
|
|
const ctx = { path: base };
|
|
|
|
binding.lstat(pathModule.toNamespacedPath(base), undefined, ctx);
|
|
|
|
if (ctx.errno !== undefined) {
|
2018-02-03 17:35:04 +08:00
|
|
|
throw errors.uvException(ctx);
|
2017-12-28 14:51:05 +08:00
|
|
|
}
|
2017-01-13 05:04:18 -05:00
|
|
|
knownHard[base] = true;
|
|
|
|
}
|
2016-07-27 00:18:35 +02:00
|
|
|
}
|
|
|
|
|
2016-08-14 18:41:28 +02:00
|
|
|
if (cache) cache.set(original, p);
|
2016-07-27 00:18:35 +02:00
|
|
|
return encodeRealpathResult(p, options);
|
2012-06-08 15:26:04 -07:00
|
|
|
};
|
2010-09-09 18:49:28 -07:00
|
|
|
|
2011-02-08 16:54:25 -08:00
|
|
|
|
2017-10-05 13:10:03 +02:00
|
|
|
fs.realpathSync.native = function(path, options) {
|
|
|
|
options = getOptions(options, {});
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
|
|
|
validatePath(path);
|
2017-10-05 13:10:03 +02:00
|
|
|
return binding.realpath(path, options.encoding);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2016-07-27 00:18:35 +02:00
|
|
|
fs.realpath = function realpath(p, options, callback) {
|
2016-06-26 00:03:05 +05:30
|
|
|
callback = maybeCallback(typeof options === 'function' ? options : callback);
|
2017-03-11 23:59:37 -05:00
|
|
|
if (!options)
|
|
|
|
options = emptyObj;
|
|
|
|
else
|
|
|
|
options = getOptions(options, emptyObj);
|
2018-01-23 10:23:46 +08:00
|
|
|
p = getPathFromURL(p);
|
2017-03-11 23:59:37 -05:00
|
|
|
if (typeof p !== 'string') {
|
2018-01-23 10:23:46 +08:00
|
|
|
p += '';
|
2017-03-11 23:59:37 -05:00
|
|
|
}
|
2018-01-23 10:23:46 +08:00
|
|
|
validatePath(p);
|
2016-07-27 00:18:35 +02:00
|
|
|
p = pathModule.resolve(p);
|
|
|
|
|
2017-03-19 16:11:10 -07:00
|
|
|
const seenLinks = Object.create(null);
|
|
|
|
const knownHard = Object.create(null);
|
2016-07-27 00:18:35 +02:00
|
|
|
|
|
|
|
// current character position in p
|
|
|
|
var pos;
|
|
|
|
// the partial path so far, including a trailing slash if any
|
|
|
|
var current;
|
|
|
|
// the partial path without a trailing slash (except when pointing at a root)
|
|
|
|
var base;
|
|
|
|
// the partial path scanned in the previous round, with slash
|
|
|
|
var previous;
|
|
|
|
|
2017-03-11 23:59:37 -05:00
|
|
|
current = base = splitRoot(p);
|
|
|
|
pos = current.length;
|
2016-07-27 00:18:35 +02:00
|
|
|
|
2017-01-13 05:04:18 -05:00
|
|
|
// On windows, check that the root exists. On unix there is no need.
|
|
|
|
if (isWindows && !knownHard[base]) {
|
|
|
|
fs.lstat(base, function(err) {
|
|
|
|
if (err) return callback(err);
|
|
|
|
knownHard[base] = true;
|
|
|
|
LOOP();
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
process.nextTick(LOOP);
|
2016-07-27 00:18:35 +02:00
|
|
|
}
|
|
|
|
|
2017-06-17 15:11:45 +02:00
|
|
|
// walk down the path, swapping out linked path parts for their real
|
2016-07-27 00:18:35 +02:00
|
|
|
// values
|
|
|
|
function LOOP() {
|
|
|
|
// stop if scanned past end of path
|
|
|
|
if (pos >= p.length) {
|
|
|
|
return callback(null, encodeRealpathResult(p, options));
|
|
|
|
}
|
|
|
|
|
|
|
|
// find the next part
|
2017-01-13 05:09:52 -05:00
|
|
|
var result = nextPart(p, pos);
|
2016-07-27 00:18:35 +02:00
|
|
|
previous = current;
|
2017-01-13 05:09:52 -05:00
|
|
|
if (result === -1) {
|
|
|
|
var 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;
|
|
|
|
}
|
2016-07-27 00:18:35 +02:00
|
|
|
|
2017-05-15 03:27:54 +04:30
|
|
|
// continue if not a symlink, break if a pipe/socket
|
2016-07-27 00:18:35 +02:00
|
|
|
if (knownHard[base]) {
|
2017-05-15 03:27:54 +04:30
|
|
|
if ((statValues[1/*mode*/] & S_IFMT) === S_IFIFO ||
|
|
|
|
(statValues[1/*mode*/] & S_IFMT) === S_IFSOCK) {
|
|
|
|
return callback(null, encodeRealpathResult(p, options));
|
|
|
|
}
|
2016-07-27 00:18:35 +02:00
|
|
|
return process.nextTick(LOOP);
|
|
|
|
}
|
|
|
|
|
|
|
|
return fs.lstat(base, gotStat);
|
|
|
|
}
|
|
|
|
|
2017-03-11 19:41:20 -05:00
|
|
|
function gotStat(err) {
|
2016-07-27 00:18:35 +02:00
|
|
|
if (err) return callback(err);
|
|
|
|
|
2017-03-11 19:41:20 -05:00
|
|
|
// Use stats array directly to avoid creating an fs.Stats instance just for
|
|
|
|
// our internal use.
|
|
|
|
|
2016-07-27 00:18:35 +02:00
|
|
|
// if not a symlink, skip to the next path part
|
2017-03-11 19:41:20 -05:00
|
|
|
if ((statValues[1/*mode*/] & S_IFMT) !== S_IFLNK) {
|
2016-07-27 00:18:35 +02:00
|
|
|
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.
|
2016-08-14 18:41:28 +02:00
|
|
|
let id;
|
2016-07-27 00:18:35 +02:00
|
|
|
if (!isWindows) {
|
2017-03-11 19:41:20 -05:00
|
|
|
var dev = statValues[0/*ino*/].toString(32);
|
|
|
|
var ino = statValues[7/*ino*/].toString(32);
|
|
|
|
id = `${dev}:${ino}`;
|
2017-03-11 23:59:37 -05:00
|
|
|
if (seenLinks[id]) {
|
2016-07-27 00:18:35 +02:00
|
|
|
return gotTarget(null, seenLinks[id], base);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fs.stat(base, function(err) {
|
|
|
|
if (err) return callback(err);
|
|
|
|
|
|
|
|
fs.readlink(base, function(err, target) {
|
|
|
|
if (!isWindows) seenLinks[id] = target;
|
|
|
|
gotTarget(err, target);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function gotTarget(err, target, base) {
|
|
|
|
if (err) return callback(err);
|
|
|
|
|
|
|
|
var resolvedLink = pathModule.resolve(previous, target);
|
|
|
|
gotResolvedLink(resolvedLink);
|
|
|
|
}
|
|
|
|
|
|
|
|
function gotResolvedLink(resolvedLink) {
|
|
|
|
// resolve the link, then start over
|
|
|
|
p = pathModule.resolve(resolvedLink, p.slice(pos));
|
2017-03-11 23:59:37 -05:00
|
|
|
current = base = splitRoot(p);
|
|
|
|
pos = current.length;
|
2017-01-13 05:04:18 -05:00
|
|
|
|
|
|
|
// On windows, check that the root exists. On unix there is no need.
|
|
|
|
if (isWindows && !knownHard[base]) {
|
|
|
|
fs.lstat(base, function(err) {
|
|
|
|
if (err) return callback(err);
|
|
|
|
knownHard[base] = true;
|
|
|
|
LOOP();
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
process.nextTick(LOOP);
|
|
|
|
}
|
2016-07-27 00:18:35 +02:00
|
|
|
}
|
|
|
|
};
|
2010-05-24 15:47:40 -07:00
|
|
|
|
2017-10-05 13:10:03 +02:00
|
|
|
|
|
|
|
fs.realpath.native = function(path, options, callback) {
|
|
|
|
callback = maybeCallback(callback || options);
|
|
|
|
options = getOptions(options, {});
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
|
|
|
validatePath(path);
|
2017-10-05 13:10:03 +02:00
|
|
|
const req = new FSReqWrap();
|
|
|
|
req.oncomplete = callback;
|
|
|
|
return binding.realpath(path, options.encoding, req);
|
|
|
|
};
|
|
|
|
|
2016-05-30 23:15:01 +05:30
|
|
|
fs.mkdtemp = function(prefix, options, callback) {
|
2016-06-26 00:03:05 +05:30
|
|
|
callback = makeCallback(typeof options === 'function' ? options : callback);
|
|
|
|
options = getOptions(options, {});
|
2017-08-26 07:46:47 -04:00
|
|
|
if (!prefix || typeof prefix !== 'string') {
|
|
|
|
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
|
|
|
|
'prefix',
|
|
|
|
'string',
|
|
|
|
prefix);
|
|
|
|
}
|
2018-01-23 10:23:46 +08:00
|
|
|
nullCheck(prefix, 'prefix');
|
2016-05-18 17:47:07 +05:30
|
|
|
var req = new FSReqWrap();
|
|
|
|
req.oncomplete = callback;
|
2017-11-10 18:17:17 +05:30
|
|
|
binding.mkdtemp(`${prefix}XXXXXX`, options.encoding, req);
|
2016-05-18 17:47:07 +05:30
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
fs.mkdtempSync = function(prefix, options) {
|
2018-01-23 10:23:46 +08:00
|
|
|
options = getOptions(options, {});
|
2017-08-26 07:46:47 -04:00
|
|
|
if (!prefix || typeof prefix !== 'string') {
|
|
|
|
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
|
|
|
|
'prefix',
|
|
|
|
'string',
|
|
|
|
prefix);
|
|
|
|
}
|
2018-01-23 10:23:46 +08:00
|
|
|
nullCheck(prefix, 'prefix');
|
2017-11-10 18:17:17 +05:30
|
|
|
return binding.mkdtemp(`${prefix}XXXXXX`, options.encoding);
|
2016-05-18 17:47:07 +05:30
|
|
|
};
|
|
|
|
|
|
|
|
|
2017-09-06 12:54:29 -04:00
|
|
|
// Define copyFile() flags.
|
|
|
|
Object.defineProperties(fs.constants, {
|
|
|
|
COPYFILE_EXCL: { enumerable: true, value: constants.UV_FS_COPYFILE_EXCL }
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
fs.copyFile = function(src, dest, flags, callback) {
|
|
|
|
if (typeof flags === 'function') {
|
|
|
|
callback = flags;
|
|
|
|
flags = 0;
|
|
|
|
} else if (typeof callback !== 'function') {
|
2017-10-28 17:39:55 +08:00
|
|
|
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'callback', 'Function');
|
2017-09-06 12:54:29 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
src = getPathFromURL(src);
|
|
|
|
dest = getPathFromURL(dest);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(src, 'src');
|
|
|
|
validatePath(dest, 'dest');
|
2017-12-13 14:24:34 -08:00
|
|
|
|
2017-09-06 12:54:29 -04:00
|
|
|
src = pathModule._makeLong(src);
|
|
|
|
dest = pathModule._makeLong(dest);
|
|
|
|
flags = flags | 0;
|
|
|
|
const req = new FSReqWrap();
|
|
|
|
req.oncomplete = makeCallback(callback);
|
|
|
|
binding.copyFile(src, dest, flags, req);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
fs.copyFileSync = function(src, dest, flags) {
|
|
|
|
src = getPathFromURL(src);
|
|
|
|
dest = getPathFromURL(dest);
|
2017-12-22 18:42:21 -05:00
|
|
|
validatePath(src, 'src');
|
|
|
|
validatePath(dest, 'dest');
|
2017-12-13 14:24:34 -08:00
|
|
|
|
2017-09-06 12:54:29 -04:00
|
|
|
src = pathModule._makeLong(src);
|
|
|
|
dest = pathModule._makeLong(dest);
|
|
|
|
flags = flags | 0;
|
|
|
|
binding.copyFile(src, dest, flags);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2010-05-24 15:47:40 -07:00
|
|
|
var pool;
|
2010-12-01 17:43:30 -08:00
|
|
|
|
2013-01-05 01:05:27 +09:00
|
|
|
function allocNewPool(poolSize) {
|
2016-01-25 15:00:06 -08:00
|
|
|
pool = Buffer.allocUnsafe(poolSize);
|
2010-05-24 15:47:40 -07:00
|
|
|
pool.used = 0;
|
2010-03-02 03:01:44 +01:00
|
|
|
}
|
2010-03-02 23:12:52 +01:00
|
|
|
|
2010-05-24 15:47:40 -07:00
|
|
|
|
2010-04-08 10:22:55 -07:00
|
|
|
fs.createReadStream = function(path, options) {
|
2010-04-27 18:51:41 -07:00
|
|
|
return new ReadStream(path, options);
|
2010-03-03 12:39:41 +01:00
|
|
|
};
|
|
|
|
|
2012-10-04 17:44:48 -07:00
|
|
|
util.inherits(ReadStream, Readable);
|
|
|
|
fs.ReadStream = ReadStream;
|
2010-03-03 12:39:41 +01:00
|
|
|
|
2012-10-04 17:44:48 -07:00
|
|
|
function ReadStream(path, options) {
|
|
|
|
if (!(this instanceof ReadStream))
|
|
|
|
return new ReadStream(path, options);
|
2010-03-03 12:39:41 +01:00
|
|
|
|
2012-10-04 17:44:48 -07:00
|
|
|
// a little bit bigger buffer and water marks by default
|
2016-10-09 19:37:59 +05:30
|
|
|
options = copyObject(getOptions(options, {}));
|
2015-01-27 20:18:08 -08:00
|
|
|
if (options.highWaterMark === undefined)
|
|
|
|
options.highWaterMark = 64 * 1024;
|
2010-04-12 11:57:24 -05:00
|
|
|
|
2012-10-04 17:44:48 -07:00
|
|
|
Readable.call(this, options);
|
2010-03-03 12:39:41 +01:00
|
|
|
|
2018-01-23 10:23:46 +08:00
|
|
|
// path will be ignored when fd is specified, so it can be falsy
|
|
|
|
this.path = getPathFromURL(path);
|
2015-01-27 20:18:08 -08:00
|
|
|
this.fd = options.fd === undefined ? null : options.fd;
|
|
|
|
this.flags = options.flags === undefined ? 'r' : options.flags;
|
|
|
|
this.mode = options.mode === undefined ? 0o666 : options.mode;
|
|
|
|
|
2018-01-12 23:21:44 +08:00
|
|
|
this.start = typeof this.fd !== 'number' && options.start === undefined ?
|
|
|
|
0 : options.start;
|
2015-06-03 03:41:04 +05:30
|
|
|
this.end = options.end;
|
2015-01-27 20:18:08 -08:00
|
|
|
this.autoClose = options.autoClose === undefined ? true : options.autoClose;
|
2012-10-04 17:44:48 -07:00
|
|
|
this.pos = undefined;
|
2016-08-01 21:31:16 +02:00
|
|
|
this.bytesRead = 0;
|
2018-01-05 10:41:14 +01:00
|
|
|
this.closed = false;
|
2010-09-27 13:34:16 -04:00
|
|
|
|
2015-01-28 20:05:53 -05:00
|
|
|
if (this.start !== undefined) {
|
|
|
|
if (typeof this.start !== 'number') {
|
2017-08-26 07:46:47 -04:00
|
|
|
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
|
|
|
|
'start',
|
|
|
|
'number',
|
|
|
|
this.start);
|
2012-02-17 18:53:57 -07:00
|
|
|
}
|
2015-01-28 20:05:53 -05:00
|
|
|
if (this.end === undefined) {
|
2011-04-05 23:37:40 +02:00
|
|
|
this.end = Infinity;
|
2015-01-28 20:05:53 -05:00
|
|
|
} else if (typeof this.end !== 'number') {
|
2017-08-26 07:46:47 -04:00
|
|
|
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
|
|
|
|
'end',
|
|
|
|
'number',
|
|
|
|
this.end);
|
2011-04-05 23:37:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (this.start > this.end) {
|
2017-08-26 07:46:47 -04:00
|
|
|
const errVal = `{start: ${this.start}, end: ${this.end}}`;
|
2017-12-13 20:48:43 +08:00
|
|
|
throw new errors.RangeError('ERR_OUT_OF_RANGE',
|
2017-08-26 07:46:47 -04:00
|
|
|
'start',
|
|
|
|
'<= "end"',
|
|
|
|
errVal);
|
2010-07-20 18:16:27 +05:30
|
|
|
}
|
2011-09-08 18:19:28 -05:00
|
|
|
|
|
|
|
this.pos = this.start;
|
2010-07-20 18:16:27 +05:30
|
|
|
}
|
|
|
|
|
2015-01-28 20:05:53 -05:00
|
|
|
if (typeof this.fd !== 'number')
|
2012-10-04 17:44:48 -07:00
|
|
|
this.open();
|
2010-03-03 12:39:41 +01:00
|
|
|
|
2012-10-04 17:44:48 -07:00
|
|
|
this.on('end', function() {
|
2012-12-17 23:03:19 +08:00
|
|
|
if (this.autoClose) {
|
|
|
|
this.destroy();
|
|
|
|
}
|
2012-10-04 17:44:48 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
fs.FileReadStream = fs.ReadStream; // support the legacy name
|
|
|
|
|
|
|
|
ReadStream.prototype.open = function() {
|
|
|
|
var self = this;
|
|
|
|
fs.open(this.path, this.flags, this.mode, function(er, fd) {
|
|
|
|
if (er) {
|
2013-08-20 15:53:54 +02:00
|
|
|
if (self.autoClose) {
|
2012-12-17 23:03:19 +08:00
|
|
|
self.destroy();
|
|
|
|
}
|
2012-10-04 17:44:48 -07:00
|
|
|
self.emit('error', er);
|
2010-03-03 12:39:41 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-04-08 10:37:10 -07:00
|
|
|
self.fd = fd;
|
|
|
|
self.emit('open', fd);
|
2012-10-04 17:44:48 -07:00
|
|
|
// start the flow of data.
|
|
|
|
self.read();
|
2010-04-08 10:37:10 -07:00
|
|
|
});
|
|
|
|
};
|
2010-03-04 22:06:06 +01:00
|
|
|
|
stream: There is no _read cb, there is only push
This makes it so that `stream.push(chunk)` is the only way to signal the
end of reading, removing the confusing disparity between the
callback-style _read method, and the fact that most real-world streams
do not have a 1:1 corollation between the "please give me data" event,
and the actual arrival of a chunk of data.
It is still possible, of course, to implement a `CallbackReadable` on
top of this. Simply provide a method like this as the callback:
function readCallback(er, chunk) {
if (er)
stream.emit('error', er);
else
stream.push(chunk);
}
However, *only* fs streams actually would behave in this way, so it
makes not a lot of sense to make TCP, TLS, HTTP, and all the rest have
to bend into this uncomfortable paradigm.
2013-02-28 15:32:32 -08:00
|
|
|
ReadStream.prototype._read = function(n) {
|
2017-06-20 14:37:00 -07:00
|
|
|
if (typeof this.fd !== 'number') {
|
2012-10-04 17:44:48 -07:00
|
|
|
return this.once('open', function() {
|
stream: There is no _read cb, there is only push
This makes it so that `stream.push(chunk)` is the only way to signal the
end of reading, removing the confusing disparity between the
callback-style _read method, and the fact that most real-world streams
do not have a 1:1 corollation between the "please give me data" event,
and the actual arrival of a chunk of data.
It is still possible, of course, to implement a `CallbackReadable` on
top of this. Simply provide a method like this as the callback:
function readCallback(er, chunk) {
if (er)
stream.emit('error', er);
else
stream.push(chunk);
}
However, *only* fs streams actually would behave in this way, so it
makes not a lot of sense to make TCP, TLS, HTTP, and all the rest have
to bend into this uncomfortable paradigm.
2013-02-28 15:32:32 -08:00
|
|
|
this._read(n);
|
2012-10-04 17:44:48 -07:00
|
|
|
});
|
2017-06-20 14:37:00 -07:00
|
|
|
}
|
2010-05-24 15:47:40 -07:00
|
|
|
|
2012-10-04 17:44:48 -07:00
|
|
|
if (this.destroyed)
|
|
|
|
return;
|
2010-03-03 12:39:41 +01:00
|
|
|
|
2010-05-24 15:47:40 -07:00
|
|
|
if (!pool || pool.length - pool.used < kMinPoolSpace) {
|
2013-03-05 22:57:15 -08:00
|
|
|
// discard the old pool.
|
2017-05-05 15:57:57 +02:00
|
|
|
allocNewPool(this.readableHighWaterMark);
|
2010-05-24 15:47:40 -07:00
|
|
|
}
|
|
|
|
|
2012-10-04 17:44:48 -07:00
|
|
|
// Grab another reference to the pool in the case that while we're
|
|
|
|
// in the thread pool another read() finishes up the pool, and
|
|
|
|
// allocates a new one.
|
2010-05-24 15:47:40 -07:00
|
|
|
var thisPool = pool;
|
2012-10-04 17:44:48 -07:00
|
|
|
var toRead = Math.min(pool.length - pool.used, n);
|
2010-05-24 15:47:40 -07:00
|
|
|
var start = pool.used;
|
|
|
|
|
2015-01-28 20:05:53 -05:00
|
|
|
if (this.pos !== undefined)
|
2010-07-20 18:16:27 +05:30
|
|
|
toRead = Math.min(this.end - this.pos + 1, toRead);
|
|
|
|
|
2012-10-04 17:44:48 -07:00
|
|
|
// already read everything we were supposed to read!
|
|
|
|
// treat as EOF.
|
|
|
|
if (toRead <= 0)
|
stream: There is no _read cb, there is only push
This makes it so that `stream.push(chunk)` is the only way to signal the
end of reading, removing the confusing disparity between the
callback-style _read method, and the fact that most real-world streams
do not have a 1:1 corollation between the "please give me data" event,
and the actual arrival of a chunk of data.
It is still possible, of course, to implement a `CallbackReadable` on
top of this. Simply provide a method like this as the callback:
function readCallback(er, chunk) {
if (er)
stream.emit('error', er);
else
stream.push(chunk);
}
However, *only* fs streams actually would behave in this way, so it
makes not a lot of sense to make TCP, TLS, HTTP, and all the rest have
to bend into this uncomfortable paradigm.
2013-02-28 15:32:32 -08:00
|
|
|
return this.push(null);
|
2010-03-03 12:39:41 +01:00
|
|
|
|
2012-10-04 17:44:48 -07:00
|
|
|
// the actual read.
|
2017-11-20 00:11:24 +08:00
|
|
|
fs.read(this.fd, pool, pool.used, toRead, this.pos, (er, bytesRead) => {
|
2012-10-04 17:44:48 -07:00
|
|
|
if (er) {
|
2017-11-20 00:11:24 +08:00
|
|
|
if (this.autoClose) {
|
|
|
|
this.destroy();
|
2012-12-17 23:03:19 +08:00
|
|
|
}
|
2017-11-20 00:11:24 +08:00
|
|
|
this.emit('error', er);
|
stream: There is no _read cb, there is only push
This makes it so that `stream.push(chunk)` is the only way to signal the
end of reading, removing the confusing disparity between the
callback-style _read method, and the fact that most real-world streams
do not have a 1:1 corollation between the "please give me data" event,
and the actual arrival of a chunk of data.
It is still possible, of course, to implement a `CallbackReadable` on
top of this. Simply provide a method like this as the callback:
function readCallback(er, chunk) {
if (er)
stream.emit('error', er);
else
stream.push(chunk);
}
However, *only* fs streams actually would behave in this way, so it
makes not a lot of sense to make TCP, TLS, HTTP, and all the rest have
to bend into this uncomfortable paradigm.
2013-02-28 15:32:32 -08:00
|
|
|
} else {
|
|
|
|
var b = null;
|
2016-08-01 21:31:16 +02:00
|
|
|
if (bytesRead > 0) {
|
2017-11-20 00:11:24 +08:00
|
|
|
this.bytesRead += bytesRead;
|
stream: There is no _read cb, there is only push
This makes it so that `stream.push(chunk)` is the only way to signal the
end of reading, removing the confusing disparity between the
callback-style _read method, and the fact that most real-world streams
do not have a 1:1 corollation between the "please give me data" event,
and the actual arrival of a chunk of data.
It is still possible, of course, to implement a `CallbackReadable` on
top of this. Simply provide a method like this as the callback:
function readCallback(er, chunk) {
if (er)
stream.emit('error', er);
else
stream.push(chunk);
}
However, *only* fs streams actually would behave in this way, so it
makes not a lot of sense to make TCP, TLS, HTTP, and all the rest have
to bend into this uncomfortable paradigm.
2013-02-28 15:32:32 -08:00
|
|
|
b = thisPool.slice(start, start + bytesRead);
|
2016-08-01 21:31:16 +02:00
|
|
|
}
|
2010-04-08 10:37:10 -07:00
|
|
|
|
2017-11-20 00:11:24 +08:00
|
|
|
this.push(b);
|
stream: There is no _read cb, there is only push
This makes it so that `stream.push(chunk)` is the only way to signal the
end of reading, removing the confusing disparity between the
callback-style _read method, and the fact that most real-world streams
do not have a 1:1 corollation between the "please give me data" event,
and the actual arrival of a chunk of data.
It is still possible, of course, to implement a `CallbackReadable` on
top of this. Simply provide a method like this as the callback:
function readCallback(er, chunk) {
if (er)
stream.emit('error', er);
else
stream.push(chunk);
}
However, *only* fs streams actually would behave in this way, so it
makes not a lot of sense to make TCP, TLS, HTTP, and all the rest have
to bend into this uncomfortable paradigm.
2013-02-28 15:32:32 -08:00
|
|
|
}
|
2017-11-20 00:11:24 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
// move the pool positions, and internal position for reading.
|
|
|
|
if (this.pos !== undefined)
|
|
|
|
this.pos += toRead;
|
|
|
|
pool.used += toRead;
|
2010-05-24 15:47:40 -07:00
|
|
|
};
|
|
|
|
|
2017-05-06 14:20:52 +02:00
|
|
|
ReadStream.prototype._destroy = function(err, cb) {
|
2018-01-05 10:41:14 +01:00
|
|
|
const isOpen = typeof this.fd !== 'number';
|
|
|
|
if (isOpen) {
|
|
|
|
this.once('open', closeFsStream.bind(null, this, cb, err));
|
|
|
|
return;
|
2012-10-04 17:44:48 -07:00
|
|
|
}
|
2017-02-07 19:38:33 +01:00
|
|
|
|
2017-09-14 13:46:02 +02:00
|
|
|
closeFsStream(this, cb);
|
2017-02-07 19:38:33 +01:00
|
|
|
this.fd = null;
|
2010-04-08 10:37:10 -07:00
|
|
|
};
|
2010-03-03 12:39:41 +01:00
|
|
|
|
2017-09-14 13:46:02 +02:00
|
|
|
function closeFsStream(stream, cb, err) {
|
|
|
|
fs.close(stream.fd, (er) => {
|
|
|
|
er = er || err;
|
|
|
|
cb(er);
|
2018-01-05 10:41:14 +01:00
|
|
|
stream.closed = true;
|
2017-09-14 13:46:02 +02:00
|
|
|
if (!er)
|
|
|
|
stream.emit('close');
|
|
|
|
});
|
2017-06-05 16:46:55 +02:00
|
|
|
}
|
2010-03-03 12:39:41 +01:00
|
|
|
|
2017-09-14 13:46:02 +02:00
|
|
|
ReadStream.prototype.close = function(cb) {
|
|
|
|
this.destroy(null, cb);
|
|
|
|
};
|
|
|
|
|
2010-04-08 10:22:55 -07:00
|
|
|
fs.createWriteStream = function(path, options) {
|
2010-04-27 18:51:41 -07:00
|
|
|
return new WriteStream(path, options);
|
2010-03-02 23:12:52 +01:00
|
|
|
};
|
|
|
|
|
2012-10-04 17:44:48 -07:00
|
|
|
util.inherits(WriteStream, Writable);
|
|
|
|
fs.WriteStream = WriteStream;
|
|
|
|
function WriteStream(path, options) {
|
|
|
|
if (!(this instanceof WriteStream))
|
|
|
|
return new WriteStream(path, options);
|
2010-09-16 19:04:09 -07:00
|
|
|
|
2016-10-09 19:37:59 +05:30
|
|
|
options = copyObject(getOptions(options, {}));
|
2012-10-04 17:44:48 -07:00
|
|
|
|
|
|
|
Writable.call(this, options);
|
2010-03-05 19:24:20 +01:00
|
|
|
|
2018-01-23 10:23:46 +08:00
|
|
|
// path will be ignored when fd is specified, so it can be falsy
|
|
|
|
this.path = getPathFromURL(path);
|
2015-01-27 20:18:08 -08:00
|
|
|
this.fd = options.fd === undefined ? null : options.fd;
|
|
|
|
this.flags = options.flags === undefined ? 'w' : options.flags;
|
|
|
|
this.mode = options.mode === undefined ? 0o666 : options.mode;
|
2010-04-12 11:57:24 -05:00
|
|
|
|
2015-06-03 03:41:04 +05:30
|
|
|
this.start = options.start;
|
2015-11-29 20:41:19 +05:30
|
|
|
this.autoClose = options.autoClose === undefined ? true : !!options.autoClose;
|
2012-10-04 17:44:48 -07:00
|
|
|
this.pos = undefined;
|
|
|
|
this.bytesWritten = 0;
|
2018-01-05 10:41:14 +01:00
|
|
|
this.closed = false;
|
2010-03-02 23:12:52 +01:00
|
|
|
|
2015-01-28 20:05:53 -05:00
|
|
|
if (this.start !== undefined) {
|
|
|
|
if (typeof this.start !== 'number') {
|
2017-08-26 07:46:47 -04:00
|
|
|
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
|
|
|
|
'start',
|
|
|
|
'number',
|
|
|
|
this.start);
|
2012-02-17 18:53:57 -07:00
|
|
|
}
|
2011-09-11 15:30:01 -05:00
|
|
|
if (this.start < 0) {
|
2017-08-26 07:46:47 -04:00
|
|
|
const errVal = `{start: ${this.start}}`;
|
2017-12-13 20:48:43 +08:00
|
|
|
throw new errors.RangeError('ERR_OUT_OF_RANGE',
|
2017-08-26 07:46:47 -04:00
|
|
|
'start',
|
|
|
|
'>= 0',
|
|
|
|
errVal);
|
2011-09-11 15:30:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
this.pos = this.start;
|
|
|
|
}
|
|
|
|
|
2015-05-31 02:12:07 +09:00
|
|
|
if (options.encoding)
|
|
|
|
this.setDefaultEncoding(options.encoding);
|
|
|
|
|
2015-01-28 20:05:53 -05:00
|
|
|
if (typeof this.fd !== 'number')
|
2012-10-04 17:44:48 -07:00
|
|
|
this.open();
|
2010-03-02 23:12:52 +01:00
|
|
|
|
2012-10-04 17:44:48 -07:00
|
|
|
// dispose on finish.
|
2015-11-29 20:41:19 +05:30
|
|
|
this.once('finish', function() {
|
|
|
|
if (this.autoClose) {
|
2017-09-14 13:46:02 +02:00
|
|
|
this.destroy();
|
2015-11-29 20:41:19 +05:30
|
|
|
}
|
|
|
|
});
|
2012-10-04 17:44:48 -07:00
|
|
|
}
|
2010-03-02 23:12:52 +01:00
|
|
|
|
2010-04-27 18:51:41 -07:00
|
|
|
fs.FileWriteStream = fs.WriteStream; // support the legacy name
|
2010-03-02 23:12:52 +01:00
|
|
|
|
2010-03-07 16:33:21 +01:00
|
|
|
|
2012-10-04 17:44:48 -07:00
|
|
|
WriteStream.prototype.open = function() {
|
2017-11-20 00:11:24 +08:00
|
|
|
fs.open(this.path, this.flags, this.mode, (er, fd) => {
|
2012-10-04 17:44:48 -07:00
|
|
|
if (er) {
|
2015-11-29 20:41:19 +05:30
|
|
|
if (this.autoClose) {
|
|
|
|
this.destroy();
|
|
|
|
}
|
2012-10-04 17:44:48 -07:00
|
|
|
this.emit('error', er);
|
2010-04-08 10:37:10 -07:00
|
|
|
return;
|
|
|
|
}
|
2010-03-02 23:12:52 +01:00
|
|
|
|
2012-10-04 17:44:48 -07:00
|
|
|
this.fd = fd;
|
|
|
|
this.emit('open', fd);
|
2017-11-20 00:11:24 +08:00
|
|
|
});
|
2010-04-08 10:37:10 -07:00
|
|
|
};
|
2010-03-02 23:12:52 +01:00
|
|
|
|
2010-03-07 16:33:21 +01:00
|
|
|
|
2013-03-03 19:14:06 -08:00
|
|
|
WriteStream.prototype._write = function(data, encoding, cb) {
|
2017-12-17 14:55:22 -05:00
|
|
|
if (!(data instanceof Buffer)) {
|
|
|
|
const err = new errors.TypeError('ERR_INVALID_ARG_TYPE',
|
|
|
|
'data',
|
|
|
|
'Buffer',
|
|
|
|
data);
|
|
|
|
return this.emit('error', err);
|
|
|
|
}
|
2010-05-17 15:22:09 -07:00
|
|
|
|
2017-06-20 14:37:00 -07:00
|
|
|
if (typeof this.fd !== 'number') {
|
2013-03-03 19:14:06 -08:00
|
|
|
return this.once('open', function() {
|
|
|
|
this._write(data, encoding, cb);
|
|
|
|
});
|
2017-06-20 14:37:00 -07:00
|
|
|
}
|
2010-05-17 15:22:09 -07:00
|
|
|
|
2017-11-20 00:11:24 +08:00
|
|
|
fs.write(this.fd, data, 0, data.length, this.pos, (er, bytes) => {
|
2012-10-04 17:44:48 -07:00
|
|
|
if (er) {
|
2017-11-20 00:11:24 +08:00
|
|
|
if (this.autoClose) {
|
|
|
|
this.destroy();
|
2015-11-29 20:41:19 +05:30
|
|
|
}
|
2012-10-04 17:44:48 -07:00
|
|
|
return cb(er);
|
|
|
|
}
|
2017-11-20 00:11:24 +08:00
|
|
|
this.bytesWritten += bytes;
|
2012-10-04 17:44:48 -07:00
|
|
|
cb();
|
|
|
|
});
|
2011-09-11 15:30:01 -05:00
|
|
|
|
2015-01-28 20:05:53 -05:00
|
|
|
if (this.pos !== undefined)
|
2011-09-11 15:30:01 -05:00
|
|
|
this.pos += data.length;
|
2010-04-08 10:37:10 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2015-07-28 14:59:35 +09:00
|
|
|
function writev(fd, chunks, position, callback) {
|
|
|
|
function wrapper(err, written) {
|
|
|
|
// Retain a reference to chunks so that they can't be GC'ed too soon.
|
|
|
|
callback(err, written || 0, chunks);
|
|
|
|
}
|
|
|
|
|
|
|
|
const req = new FSReqWrap();
|
|
|
|
req.oncomplete = wrapper;
|
|
|
|
binding.writeBuffers(fd, chunks, position, req);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
WriteStream.prototype._writev = function(data, cb) {
|
2017-06-20 14:37:00 -07:00
|
|
|
if (typeof this.fd !== 'number') {
|
2015-07-28 14:59:35 +09:00
|
|
|
return this.once('open', function() {
|
|
|
|
this._writev(data, cb);
|
|
|
|
});
|
2017-06-20 14:37:00 -07:00
|
|
|
}
|
2015-07-28 14:59:35 +09:00
|
|
|
|
|
|
|
const self = this;
|
|
|
|
const len = data.length;
|
|
|
|
const chunks = new Array(len);
|
|
|
|
var size = 0;
|
|
|
|
|
|
|
|
for (var i = 0; i < len; i++) {
|
|
|
|
var chunk = data[i].chunk;
|
|
|
|
|
|
|
|
chunks[i] = chunk;
|
|
|
|
size += chunk.length;
|
|
|
|
}
|
|
|
|
|
|
|
|
writev(this.fd, chunks, this.pos, function(er, bytes) {
|
|
|
|
if (er) {
|
|
|
|
self.destroy();
|
|
|
|
return cb(er);
|
|
|
|
}
|
|
|
|
self.bytesWritten += bytes;
|
|
|
|
cb();
|
|
|
|
});
|
|
|
|
|
|
|
|
if (this.pos !== undefined)
|
|
|
|
this.pos += size;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2017-05-06 14:20:52 +02:00
|
|
|
WriteStream.prototype._destroy = ReadStream.prototype._destroy;
|
2017-09-14 13:46:02 +02:00
|
|
|
WriteStream.prototype.close = function(cb) {
|
2018-01-05 10:41:14 +01:00
|
|
|
if (cb) {
|
|
|
|
if (this.closed) {
|
|
|
|
process.nextTick(cb);
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
this.on('close', cb);
|
|
|
|
}
|
2017-09-14 13:46:02 +02:00
|
|
|
}
|
|
|
|
|
2018-01-05 10:41:14 +01:00
|
|
|
// If we are not autoClosing, we should call
|
|
|
|
// destroy on 'finish'.
|
|
|
|
if (!this.autoClose) {
|
|
|
|
this.on('finish', this.destroy.bind(this));
|
2017-09-14 13:46:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// we use end() instead of destroy() because of
|
|
|
|
// https://github.com/nodejs/node/issues/2006
|
2018-01-05 10:41:14 +01:00
|
|
|
this.end();
|
2017-09-14 13:46:02 +02:00
|
|
|
};
|
2010-04-08 10:37:10 -07:00
|
|
|
|
2011-01-04 11:39:12 -08:00
|
|
|
// There is no shutdown() for files.
|
|
|
|
WriteStream.prototype.destroySoon = WriteStream.prototype.end;
|
2011-01-04 11:22:17 -08:00
|
|
|
|
2011-11-03 13:27:26 -07:00
|
|
|
// SyncWriteStream is internal. DO NOT USE.
|
2016-12-26 18:38:05 -08:00
|
|
|
// This undocumented API was never intended to be made public.
|
|
|
|
var SyncWriteStream = internalFS.SyncWriteStream;
|
2015-06-03 20:27:44 +05:30
|
|
|
Object.defineProperty(fs, 'SyncWriteStream', {
|
2016-01-12 22:04:50 +01:00
|
|
|
configurable: true,
|
2017-05-08 17:02:28 -07:00
|
|
|
get: internalUtil.deprecate(() => SyncWriteStream,
|
|
|
|
'fs.SyncWriteStream is deprecated.', 'DEP0061'),
|
|
|
|
set: internalUtil.deprecate((val) => { SyncWriteStream = val; },
|
|
|
|
'fs.SyncWriteStream is deprecated.', 'DEP0061')
|
2015-06-03 20:27:44 +05:30
|
|
|
});
|
2018-01-21 10:21:25 -08:00
|
|
|
|
|
|
|
// Promises API
|
|
|
|
|
|
|
|
class FileHandle {
|
|
|
|
constructor(filehandle) {
|
|
|
|
this[kHandle] = filehandle;
|
|
|
|
}
|
|
|
|
|
|
|
|
getAsyncId() {
|
|
|
|
return this[kHandle].getAsyncId();
|
|
|
|
}
|
|
|
|
|
|
|
|
get fd() {
|
|
|
|
return this[kHandle].fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
appendFile(data, options) {
|
|
|
|
return promises.appendFile(this, data, options);
|
|
|
|
}
|
|
|
|
|
|
|
|
chmod(mode) {
|
|
|
|
return promises.fchmod(this, mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
chown(uid, gid) {
|
|
|
|
return promises.fchown(this, uid, gid);
|
|
|
|
}
|
|
|
|
|
|
|
|
datasync() {
|
|
|
|
return promises.fdatasync(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
sync() {
|
|
|
|
return promises.fsync(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
read(buffer, offset, length, position) {
|
|
|
|
return promises.read(this, buffer, offset, length, position);
|
|
|
|
}
|
|
|
|
|
|
|
|
readFile(options) {
|
|
|
|
return promises.readFile(this, options);
|
|
|
|
}
|
|
|
|
|
|
|
|
stat() {
|
|
|
|
return promises.fstat(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
truncate(len = 0) {
|
|
|
|
return promises.ftruncate(this, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
utimes(atime, mtime) {
|
|
|
|
return promises.futimes(this, atime, mtime);
|
|
|
|
}
|
|
|
|
|
|
|
|
write(buffer, offset, length, position) {
|
|
|
|
return promises.write(this, buffer, offset, length, position);
|
|
|
|
}
|
|
|
|
|
|
|
|
writeFile(data, options) {
|
|
|
|
return promises.writeFile(this, data, options);
|
|
|
|
}
|
|
|
|
|
|
|
|
close() {
|
|
|
|
return this[kHandle].close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function validateFileHandle(handle) {
|
|
|
|
if (!(handle instanceof FileHandle))
|
|
|
|
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
|
|
|
|
'filehandle', 'FileHandle');
|
|
|
|
}
|
|
|
|
|
|
|
|
async function writeFileHandle(filehandle, data, options) {
|
|
|
|
let buffer = isUint8Array(data) ?
|
|
|
|
data : Buffer.from('' + data, options.encoding || 'utf8');
|
|
|
|
let remaining = buffer.length;
|
|
|
|
if (remaining === 0) return;
|
|
|
|
do {
|
|
|
|
const { bytesWritten } =
|
|
|
|
await promises.write(filehandle, buffer, 0,
|
|
|
|
Math.min(16384, buffer.length));
|
|
|
|
remaining -= bytesWritten;
|
|
|
|
buffer = buffer.slice(bytesWritten);
|
|
|
|
} while (remaining > 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
async function readFileHandle(filehandle, options) {
|
|
|
|
const statFields = await binding.fstat(filehandle.fd, kUsePromises);
|
|
|
|
|
|
|
|
let size;
|
|
|
|
if ((statFields[1/*mode*/] & S_IFMT) === S_IFREG) {
|
|
|
|
size = statFields[8/*size*/];
|
|
|
|
} else {
|
|
|
|
size = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (size === 0)
|
|
|
|
return Buffer.alloc(0);
|
|
|
|
|
|
|
|
if (size > kMaxLength)
|
|
|
|
throw new errors.RangeError('ERR_BUFFER_TOO_LARGE');
|
|
|
|
|
|
|
|
const chunks = [];
|
|
|
|
const chunkSize = Math.min(size, 16384);
|
|
|
|
const buf = Buffer.alloc(chunkSize);
|
|
|
|
let read = 0;
|
|
|
|
do {
|
|
|
|
const { bytesRead, buffer } =
|
|
|
|
await promises.read(filehandle, buf, 0, buf.length);
|
|
|
|
read = bytesRead;
|
|
|
|
if (read > 0)
|
|
|
|
chunks.push(buffer.slice(0, read));
|
|
|
|
} while (read === chunkSize);
|
|
|
|
|
|
|
|
return Buffer.concat(chunks);
|
|
|
|
}
|
|
|
|
|
|
|
|
// All of the functions in fs.promises are defined as async in order to
|
|
|
|
// ensure that errors thrown cause promise rejections rather than being
|
|
|
|
// thrown synchronously
|
|
|
|
const promises = {
|
|
|
|
async access(path, mode = fs.F_OK) {
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2018-01-21 10:21:25 -08:00
|
|
|
validatePath(path);
|
|
|
|
|
|
|
|
mode = mode | 0;
|
|
|
|
return binding.access(pathModule.toNamespacedPath(path), mode,
|
|
|
|
kUsePromises);
|
|
|
|
},
|
|
|
|
|
|
|
|
async copyFile(src, dest, flags) {
|
2018-01-23 10:23:46 +08:00
|
|
|
src = getPathFromURL(src);
|
|
|
|
dest = getPathFromURL(dest);
|
2018-01-21 10:21:25 -08:00
|
|
|
validatePath(src, 'src');
|
|
|
|
validatePath(dest, 'dest');
|
|
|
|
flags = flags | 0;
|
|
|
|
return binding.copyFile(pathModule.toNamespacedPath(src),
|
|
|
|
pathModule.toNamespacedPath(dest),
|
|
|
|
flags, kUsePromises);
|
|
|
|
},
|
|
|
|
|
|
|
|
// Note that unlike fs.open() which uses numeric file descriptors,
|
|
|
|
// promises.open() uses the fs.FileHandle class.
|
|
|
|
async open(path, flags, mode) {
|
|
|
|
mode = modeNum(mode, 0o666);
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2018-01-21 10:21:25 -08:00
|
|
|
validatePath(path);
|
|
|
|
validateUint32(mode, 'mode');
|
|
|
|
return new FileHandle(
|
|
|
|
await binding.openFileHandle(pathModule.toNamespacedPath(path),
|
|
|
|
stringToFlags(flags),
|
|
|
|
mode, kUsePromises));
|
|
|
|
},
|
|
|
|
|
|
|
|
async read(handle, buffer, offset, length, position) {
|
|
|
|
validateFileHandle(handle);
|
|
|
|
validateBuffer(buffer);
|
|
|
|
|
|
|
|
offset |= 0;
|
|
|
|
length |= 0;
|
|
|
|
|
|
|
|
if (length === 0)
|
|
|
|
return { bytesRead: length, buffer };
|
|
|
|
|
|
|
|
validateOffsetLengthRead(offset, length, buffer.length);
|
|
|
|
|
|
|
|
if (!isUint32(position))
|
|
|
|
position = -1;
|
|
|
|
|
|
|
|
const bytesRead = (await binding.read(handle.fd, buffer, offset, length,
|
|
|
|
position, kUsePromises)) || 0;
|
|
|
|
|
|
|
|
return { bytesRead, buffer };
|
|
|
|
},
|
|
|
|
|
|
|
|
async write(handle, buffer, offset, length, position) {
|
|
|
|
validateFileHandle(handle);
|
|
|
|
|
|
|
|
if (buffer.length === 0)
|
|
|
|
return { bytesWritten: 0, buffer };
|
|
|
|
|
|
|
|
if (isUint8Array(buffer)) {
|
|
|
|
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);
|
|
|
|
const bytesWritten =
|
|
|
|
(await binding.writeBuffer(handle.fd, buffer, offset,
|
|
|
|
length, position, kUsePromises)) || 0;
|
|
|
|
return { bytesWritten, buffer };
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof buffer !== 'string')
|
|
|
|
buffer += '';
|
|
|
|
if (typeof position !== 'function') {
|
|
|
|
if (typeof offset === 'function') {
|
|
|
|
position = offset;
|
|
|
|
offset = null;
|
|
|
|
} else {
|
|
|
|
position = length;
|
|
|
|
}
|
|
|
|
length = 'utf8';
|
|
|
|
}
|
|
|
|
const bytesWritten = (await binding.writeString(handle.fd, buffer, offset,
|
|
|
|
length, kUsePromises)) || 0;
|
|
|
|
return { bytesWritten, buffer };
|
|
|
|
},
|
|
|
|
|
|
|
|
async rename(oldPath, newPath) {
|
2018-01-23 10:23:46 +08:00
|
|
|
oldPath = getPathFromURL(oldPath);
|
|
|
|
newPath = getPathFromURL(newPath);
|
2018-01-21 10:21:25 -08:00
|
|
|
validatePath(oldPath, 'oldPath');
|
|
|
|
validatePath(newPath, 'newPath');
|
|
|
|
return binding.rename(pathModule.toNamespacedPath(oldPath),
|
|
|
|
pathModule.toNamespacedPath(newPath),
|
|
|
|
kUsePromises);
|
|
|
|
},
|
|
|
|
|
|
|
|
async truncate(path, len = 0) {
|
|
|
|
return promises.ftruncate(await promises.open(path, 'r+'), len);
|
|
|
|
},
|
|
|
|
|
|
|
|
async ftruncate(handle, len = 0) {
|
|
|
|
validateFileHandle(handle);
|
|
|
|
validateLen(len);
|
|
|
|
len = Math.max(0, len);
|
|
|
|
return binding.ftruncate(handle.fd, len, kUsePromises);
|
|
|
|
},
|
|
|
|
|
|
|
|
async rmdir(path) {
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2018-01-21 10:21:25 -08:00
|
|
|
validatePath(path);
|
|
|
|
return binding.rmdir(pathModule.toNamespacedPath(path), kUsePromises);
|
|
|
|
},
|
|
|
|
|
|
|
|
async fdatasync(handle) {
|
|
|
|
validateFileHandle(handle);
|
|
|
|
return binding.fdatasync(handle.fd, kUsePromises);
|
|
|
|
},
|
|
|
|
|
|
|
|
async fsync(handle) {
|
|
|
|
validateFileHandle(handle);
|
|
|
|
return binding.fsync(handle.fd, kUsePromises);
|
|
|
|
},
|
|
|
|
|
|
|
|
async mkdir(path, mode) {
|
|
|
|
mode = modeNum(mode, 0o777);
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2018-01-21 10:21:25 -08:00
|
|
|
validatePath(path);
|
|
|
|
validateUint32(mode, 'mode');
|
|
|
|
return binding.mkdir(pathModule.toNamespacedPath(path), mode, kUsePromises);
|
|
|
|
},
|
|
|
|
|
|
|
|
async readdir(path, options) {
|
|
|
|
options = getOptions(options, {});
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2018-01-21 10:21:25 -08:00
|
|
|
validatePath(path);
|
|
|
|
return binding.readdir(pathModule.toNamespacedPath(path),
|
|
|
|
options.encoding, kUsePromises);
|
|
|
|
},
|
|
|
|
|
|
|
|
async readlink(path, options) {
|
|
|
|
options = getOptions(options, {});
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2018-01-21 10:21:25 -08:00
|
|
|
validatePath(path, 'oldPath');
|
|
|
|
return binding.readlink(pathModule.toNamespacedPath(path),
|
|
|
|
options.encoding, kUsePromises);
|
|
|
|
},
|
|
|
|
|
|
|
|
async symlink(target, path, type_) {
|
|
|
|
const type = (typeof type_ === 'string' ? type_ : null);
|
2018-01-23 10:23:46 +08:00
|
|
|
target = getPathFromURL(target);
|
|
|
|
path = getPathFromURL(path);
|
2018-01-21 10:21:25 -08:00
|
|
|
validatePath(target, 'target');
|
|
|
|
validatePath(path);
|
|
|
|
return binding.symlink(preprocessSymlinkDestination(target, type, path),
|
|
|
|
pathModule.toNamespacedPath(path),
|
|
|
|
stringToSymlinkType(type),
|
|
|
|
kUsePromises);
|
|
|
|
},
|
|
|
|
|
|
|
|
async fstat(handle) {
|
|
|
|
validateFileHandle(handle);
|
|
|
|
return statsFromValues(await binding.fstat(handle.fd, kUsePromises));
|
|
|
|
},
|
|
|
|
|
|
|
|
async lstat(path) {
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2018-01-21 10:21:25 -08:00
|
|
|
validatePath(path);
|
|
|
|
return statsFromValues(
|
|
|
|
await binding.lstat(pathModule.toNamespacedPath(path), kUsePromises));
|
|
|
|
},
|
|
|
|
|
|
|
|
async stat(path) {
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2018-01-21 10:21:25 -08:00
|
|
|
validatePath(path);
|
|
|
|
return statsFromValues(
|
|
|
|
await binding.stat(pathModule.toNamespacedPath(path), kUsePromises));
|
|
|
|
},
|
|
|
|
|
|
|
|
async link(existingPath, newPath) {
|
2018-01-23 10:23:46 +08:00
|
|
|
existingPath = getPathFromURL(existingPath);
|
|
|
|
newPath = getPathFromURL(newPath);
|
2018-01-21 10:21:25 -08:00
|
|
|
validatePath(existingPath, 'existingPath');
|
|
|
|
validatePath(newPath, 'newPath');
|
|
|
|
return binding.link(pathModule.toNamespacedPath(existingPath),
|
|
|
|
pathModule.toNamespacedPath(newPath),
|
|
|
|
kUsePromises);
|
|
|
|
},
|
|
|
|
|
|
|
|
async unlink(path) {
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2018-01-21 10:21:25 -08:00
|
|
|
validatePath(path);
|
|
|
|
return binding.unlink(pathModule.toNamespacedPath(path), kUsePromises);
|
|
|
|
},
|
|
|
|
|
|
|
|
async fchmod(handle, mode) {
|
|
|
|
mode = modeNum(mode);
|
|
|
|
validateFileHandle(handle);
|
|
|
|
validateUint32(mode, 'mode');
|
|
|
|
if (mode < 0 || mode > 0o777)
|
|
|
|
throw new errors.RangeError('ERR_OUT_OF_RANGE', 'mode');
|
|
|
|
return binding.fchmod(handle.fd, mode, kUsePromises);
|
|
|
|
},
|
|
|
|
|
|
|
|
async chmod(path, mode) {
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2018-01-21 10:21:25 -08:00
|
|
|
validatePath(path);
|
|
|
|
mode = modeNum(mode);
|
|
|
|
validateUint32(mode, 'mode');
|
|
|
|
return binding.chmod(pathModule.toNamespacedPath(path), mode, kUsePromises);
|
|
|
|
},
|
|
|
|
|
|
|
|
async lchmod(path, mode) {
|
|
|
|
if (constants.O_SYMLINK !== undefined) {
|
|
|
|
const fd = await promises.open(path,
|
|
|
|
constants.O_WRONLY | constants.O_SYMLINK);
|
|
|
|
return promises.fschmod(fd, mode).finally(fd.close.bind(fd));
|
|
|
|
}
|
|
|
|
throw new errors.Error('ERR_METHOD_NOT_IMPLEMENTED');
|
|
|
|
},
|
|
|
|
|
|
|
|
async lchown(path, uid, gid) {
|
|
|
|
if (constants.O_SYMLINK !== undefined) {
|
|
|
|
const fd = await promises.open(path,
|
|
|
|
constants.O_WRONLY | constants.O_SYMLINK);
|
|
|
|
return promises.fschmod(fd, uid, gid).finally(fd.close.bind(fd));
|
|
|
|
}
|
|
|
|
throw new errors.Error('ERR_METHOD_NOT_IMPLEMENTED');
|
|
|
|
},
|
|
|
|
|
|
|
|
async fchown(handle, uid, gid) {
|
|
|
|
validateFileHandle(handle);
|
|
|
|
validateUint32(uid, 'uid');
|
|
|
|
validateUint32(gid, 'gid');
|
|
|
|
return binding.fchown(handle.fd, uid, gid, kUsePromises);
|
|
|
|
},
|
|
|
|
|
|
|
|
async chown(path, uid, gid) {
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2018-01-21 10:21:25 -08:00
|
|
|
validatePath(path);
|
|
|
|
validateUint32(uid, 'uid');
|
|
|
|
validateUint32(gid, 'gid');
|
|
|
|
return binding.chown(pathModule.toNamespacedPath(path),
|
|
|
|
uid, gid, kUsePromises);
|
|
|
|
},
|
|
|
|
|
|
|
|
async utimes(path, atime, mtime) {
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2018-01-21 10:21:25 -08:00
|
|
|
validatePath(path);
|
|
|
|
return binding.utimes(pathModule.toNamespacedPath(path),
|
|
|
|
toUnixTimestamp(atime),
|
|
|
|
toUnixTimestamp(mtime),
|
|
|
|
kUsePromises);
|
|
|
|
},
|
|
|
|
|
|
|
|
async futimes(handle, atime, mtime) {
|
|
|
|
validateFileHandle(handle);
|
|
|
|
atime = toUnixTimestamp(atime, 'atime');
|
|
|
|
mtime = toUnixTimestamp(mtime, 'mtime');
|
|
|
|
return binding.futimes(handle.fd, atime, mtime, kUsePromises);
|
|
|
|
},
|
|
|
|
|
|
|
|
async realpath(path, options) {
|
|
|
|
options = getOptions(options, {});
|
2018-01-23 10:23:46 +08:00
|
|
|
path = getPathFromURL(path);
|
2018-01-21 10:21:25 -08:00
|
|
|
validatePath(path);
|
|
|
|
return binding.realpath(path, options.encoding, kUsePromises);
|
|
|
|
},
|
|
|
|
|
|
|
|
async mkdtemp(prefix, options) {
|
|
|
|
options = getOptions(options, {});
|
|
|
|
if (!prefix || typeof prefix !== 'string') {
|
|
|
|
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
|
|
|
|
'prefix',
|
|
|
|
'string',
|
|
|
|
prefix);
|
|
|
|
}
|
|
|
|
nullCheck(prefix);
|
|
|
|
return binding.mkdtemp(`${prefix}XXXXXX`, options.encoding, kUsePromises);
|
|
|
|
},
|
|
|
|
|
|
|
|
async writeFile(path, data, options) {
|
|
|
|
options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'w' });
|
|
|
|
const flag = options.flag || 'w';
|
|
|
|
|
|
|
|
if (path instanceof FileHandle)
|
|
|
|
return writeFileHandle(path, data, options);
|
|
|
|
|
|
|
|
const fd = await promises.open(path, flag, options.mode);
|
|
|
|
return writeFileHandle(fd, data, options).finally(fd.close.bind(fd));
|
|
|
|
},
|
|
|
|
|
|
|
|
async appendFile(path, data, options) {
|
|
|
|
options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'a' });
|
|
|
|
options = copyObject(options);
|
|
|
|
options.flag = options.flag || 'a';
|
|
|
|
return promises.writeFile(path, data, options);
|
|
|
|
},
|
|
|
|
|
|
|
|
async readFile(path, options) {
|
|
|
|
options = getOptions(options, { flag: 'r' });
|
|
|
|
|
|
|
|
if (path instanceof FileHandle)
|
|
|
|
return readFileHandle(path, options);
|
|
|
|
|
|
|
|
const fd = await promises.open(path, options.flag, 0o666);
|
|
|
|
return readFileHandle(fd, options).finally(fd.close.bind(fd));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let warn = true;
|
|
|
|
|
|
|
|
// TODO(jasnell): Exposing this as a property with a getter works fine with
|
|
|
|
// commonjs but is going to be problematic for named imports support under
|
|
|
|
// ESM. A different approach will have to be followed there.
|
|
|
|
Object.defineProperty(fs, 'promises', {
|
|
|
|
configurable: true,
|
|
|
|
enumerable: true,
|
|
|
|
get() {
|
|
|
|
if (warn) {
|
|
|
|
warn = false;
|
|
|
|
process.emitWarning('The fs.promises API is experimental',
|
|
|
|
'ExperimentalWarning');
|
|
|
|
}
|
|
|
|
return promises;
|
|
|
|
}
|
|
|
|
});
|