diff --git a/lib/_stream_readable.js b/lib/_stream_readable.js index 1a0bc8902ac802..78e5f8c783b3c9 100644 --- a/lib/_stream_readable.js +++ b/lib/_stream_readable.js @@ -66,7 +66,7 @@ function ReadableState(options, stream) { // However, some cases require setting options to different // values for the readable and the writable sides of the duplex stream. // These options can be provided separately as readableXXX and writableXXX. - var isDuplex = stream instanceof Stream.Duplex; + const isDuplex = stream instanceof Stream.Duplex; // object stream flag. Used to make read(n) ignore n and to // make all the buffer merging and length checks go away @@ -77,19 +77,15 @@ function ReadableState(options, stream) { // the point at which it stops calling _read() to fill the buffer // Note: 0 is a valid value, means "don't call _read preemptively ever" - var hwm = options.highWaterMark; - var readableHwm = options.readableHighWaterMark; - var defaultHwm = this.objectMode ? 16 : 16 * 1024; + const hwm = options.highWaterMark; + const readableHwm = options.readableHighWaterMark; if (hwm || hwm === 0) - this.highWaterMark = hwm; + this.highWaterMark = Math.floor(hwm); else if (isDuplex && (readableHwm || readableHwm === 0)) - this.highWaterMark = readableHwm; + this.highWaterMark = Math.floor(readableHwm); else - this.highWaterMark = defaultHwm; - - // cast to ints. - this.highWaterMark = Math.floor(this.highWaterMark); + this.highWaterMark = this.objectMode ? 16 : 16 * 1024; // A linked list is used to store data chunks instead of an array because the // linked list can remove elements from the beginning faster than diff --git a/lib/_stream_writable.js b/lib/_stream_writable.js index 7de77958d56b4c..76de1422b4ea47 100644 --- a/lib/_stream_writable.js +++ b/lib/_stream_writable.js @@ -46,7 +46,7 @@ function WritableState(options, stream) { // However, some cases require setting options to different // values for the readable and the writable sides of the duplex stream. // These options can be provided separately as readableXXX and writableXXX. - var isDuplex = stream instanceof Stream.Duplex; + const isDuplex = stream instanceof Stream.Duplex; // object stream flag to indicate whether or not this stream // contains buffers or objects. @@ -58,19 +58,15 @@ function WritableState(options, stream) { // the point at which write() starts returning false // Note: 0 is a valid value, means that we always return false if // the entire buffer is not flushed immediately on write() - var hwm = options.highWaterMark; - var writableHwm = options.writableHighWaterMark; - var defaultHwm = this.objectMode ? 16 : 16 * 1024; + const hwm = options.highWaterMark; + const writableHwm = options.writableHighWaterMark; if (hwm || hwm === 0) - this.highWaterMark = hwm; + this.highWaterMark = Math.floor(hwm); else if (isDuplex && (writableHwm || writableHwm === 0)) - this.highWaterMark = writableHwm; + this.highWaterMark = Math.floor(writableHwm); else - this.highWaterMark = defaultHwm; - - // cast to ints. - this.highWaterMark = Math.floor(this.highWaterMark); + this.highWaterMark = this.objectMode ? 16 : 16 * 1024; // if _final has been called this.finalCalled = false; @@ -90,7 +86,7 @@ function WritableState(options, stream) { // should we decode strings into buffers before passing to _write? // this is here so that some node-core streams can optimize string // handling at a lower level. - var noDecode = options.decodeStrings === false; + const noDecode = options.decodeStrings === false; this.decodeStrings = !noDecode; // Crypto is kind of old and crusty. Historically, its default string @@ -148,7 +144,7 @@ function WritableState(options, stream) { // allocate the first CorkedRequest, there is always // one allocated and free to use, and we maintain at most two - var corkReq = { next: null, entry: null, finish: undefined }; + const corkReq = { next: null, entry: null, finish: undefined }; corkReq.finish = onCorkedFinish.bind(undefined, corkReq, this); this.corkedRequestsFree = corkReq; } diff --git a/lib/buffer.js b/lib/buffer.js index 7a44ca0f260539..c338a5e74363fe 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -441,7 +441,7 @@ Buffer.compare = function compare(a, b) { Buffer.isEncoding = function isEncoding(encoding) { return typeof encoding === 'string' && - typeof normalizeEncoding(encoding) === 'string'; + normalizeEncoding(encoding) !== undefined; }; Buffer[kIsEncodingSymbol] = Buffer.isEncoding; @@ -465,7 +465,7 @@ Buffer.concat = function concat(list, length) { length = length >>> 0; } - var buffer = Buffer.allocUnsafe(length); + const buffer = Buffer.allocUnsafe(length); var pos = 0; for (i = 0; i < list.length; i++) { var buf = list[i]; diff --git a/lib/fs.js b/lib/fs.js index fb1001c8c09d6f..464411391c90a7 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -24,26 +24,84 @@ 'use strict'; +const { + FSReqWrap, + getStatValues, + access, + stat, + open, + read, + close, + fstat, + writeBuffer, + writeString, + rename, + ftruncate, + rmdir, + fdatasync, + fsync, + mkdir, + readdir, + lstat, + readlink, + symlink, + link, + unlink, + fchmod, + chmod, + fchown, + chown, + utimes, + futimes, + StatWatcher: BindingStatWatcher, + mkdtemp, + copyFile, + writeBuffers +} = process.binding('fs'); + const constants = process.binding('constants').fs; -const { S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK } = constants; + +const { + S_IFBLK, + S_IFCHR, + S_IFDIR, + S_IFIFO, + S_IFLNK, + S_IFMT, + S_IFREG, + S_IFSOCK, + O_APPEND, + O_CREAT, + O_RDONLY, + O_RDWR, + O_TRUNC, + O_WRONLY, + O_SYMLINK, + F_OK, + R_OK, + W_OK, + X_OK, + UV_FS_COPYFILE_EXCL +} = constants; + const util = require('util'); const pathModule = require('path'); const { isUint8Array, createPromise, promiseResolve } = process.binding('util'); - -const binding = process.binding('fs'); const fs = exports; -const Buffer = require('buffer').Buffer; +const { Buffer } = require('buffer'); const errors = require('internal/errors'); -const Stream = require('stream').Stream; +const { Readable, Writable } = require('stream').Stream; const EventEmitter = require('events'); -const FSReqWrap = binding.FSReqWrap; -const FSEvent = process.binding('fs_event_wrap').FSEvent; -const internalFS = require('internal/fs'); -const internalURL = require('internal/url'); +const { FSEvent } = process.binding('fs_event_wrap'); + +const { + realpathCacheKey, + stringToFlags, + SyncWriteStream: InternalSyncWriteStream +} = require('internal/fs'); + +const { getPathFromURL } = require('internal/url'); const internalUtil = require('internal/util'); -const assertEncoding = internalFS.assertEncoding; -const stringToFlags = internalFS.stringToFlags; -const getPathFromURL = internalURL.getPathFromURL; Object.defineProperty(exports, 'constants', { configurable: false, @@ -51,9 +109,6 @@ Object.defineProperty(exports, 'constants', { value: constants }); -const Readable = Stream.Readable; -const Writable = Stream.Writable; - const kMinPoolSpace = 128; const kMaxLength = require('buffer').kMaxLength; @@ -62,33 +117,33 @@ const isWindows = process.platform === 'win32'; const DEBUG = process.env.NODE_DEBUG && /fs/.test(process.env.NODE_DEBUG); const errnoException = util._errnoException; -function getOptions(options, defaultOptions) { - if (options === null || options === undefined || - typeof options === 'function') { - return defaultOptions; - } +const writeFlags = O_TRUNC | O_CREAT | O_WRONLY; +const appendFlags = O_APPEND | O_CREAT | O_WRONLY; + +const kReadFileBufferLength = 8 * 1024; + +const WriteStreamCbSym = Symbol('WriteStreamCallback'); +const ReqWrapSym = Symbol('ReqWrapInstance'); +const ReqWrapEvSym = Symbol('ReqWrapInstanceEv'); +const StreamPoolSym = Symbol('StreamPool'); +const StreamStartSym = Symbol('StreamStart'); - if (typeof options === 'string') { - defaultOptions = util._extend({}, defaultOptions); - defaultOptions.encoding = options; - options = defaultOptions; - } else if (typeof options !== 'object') { +var pool; +var poolUsed = 0; + +function assertOptions(options) { + if (typeof options !== 'object') { throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'options', ['string', 'object'], options); } - - if (options.encoding !== 'buffer') - assertEncoding(options.encoding); - return options; } -function copyObject(source) { - var target = {}; - for (var key in source) - target[key] = source[key]; - return target; +function assertEncoding(encoding) { + if (encoding && !Buffer.isEncoding(encoding) && encoding !== 'buffer') { + throw new errors.TypeError('ERR_INVALID_OPT_VALUE_ENCODING', encoding); + } } function rethrow() { @@ -101,10 +156,10 @@ function 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(); + const backtrace = new Error(); return function(err) { if (err) { - backtrace.stack = err.name + ': ' + err.message + + backtrace.stack = `${err.name}: ${err.message}` + backtrace.stack.substr(backtrace.name.length); throw backtrace; } @@ -138,6 +193,11 @@ function makeCallback(cb) { return cb.apply(undefined, arguments); }; } +function makeCallbackNoCheck(cb) { + return function() { + return cb.apply(undefined, arguments); + }; +} // 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 @@ -157,23 +217,8 @@ function makeStatsCallback(cb) { }; } -function nullCheck(path, callback) { - if (('' + path).indexOf('\u0000') !== -1) { - const er = new errors.Error('ERR_INVALID_ARG_TYPE', - 'path', - 'string without null bytes', - path); - - if (typeof callback !== 'function') - throw er; - process.nextTick(callback, er); - return false; - } - return true; -} - function isFd(path) { - return (path >>> 0) === path; + return typeof path === 'number' && path | 0 === path; } // Constructor for file stats. @@ -219,7 +264,7 @@ Stats.prototype._checkModeProperty = function(property) { }; Stats.prototype.isDirectory = function() { - return this._checkModeProperty(constants.S_IFDIR); + return this._checkModeProperty(S_IFDIR); }; Stats.prototype.isFile = function() { @@ -227,11 +272,11 @@ Stats.prototype.isFile = function() { }; Stats.prototype.isBlockDevice = function() { - return this._checkModeProperty(constants.S_IFBLK); + return this._checkModeProperty(S_IFBLK); }; Stats.prototype.isCharacterDevice = function() { - return this._checkModeProperty(constants.S_IFCHR); + return this._checkModeProperty(S_IFCHR); }; Stats.prototype.isSymbolicLink = function() { @@ -246,7 +291,7 @@ Stats.prototype.isSocket = function() { return this._checkModeProperty(S_IFSOCK); }; -const statValues = binding.getStatValues(); +const statValues = getStatValues(); function statsFromValues() { return new Stats(statValues[0], statValues[1], statValues[2], statValues[3], @@ -259,61 +304,79 @@ function statsFromValues() { // Don't allow mode to accidentally be overwritten. Object.defineProperties(fs, { - 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 }, + F_OK: { enumerable: true, value: F_OK | 0 }, + R_OK: { enumerable: true, value: R_OK | 0 }, + W_OK: { enumerable: true, value: W_OK | 0 }, + X_OK: { enumerable: true, value: X_OK | 0 }, }); -function handleError(val, callback) { - if (val instanceof Error) { - if (typeof callback === 'function') { - process.nextTick(callback, val); - return true; - } else throw val; +function handleError(path, callback, err) { + err = err || new errors.Error('ERR_INVALID_ARG_TYPE', + 'path', 'string without null bytes', path); + if (typeof callback !== 'function') + throw err; + process.nextTick(callback, err); +} + +function pathCheck(path, cb) { + if (typeof path !== 'string') { + if (isUint8Array(path)) { + if (path.indexOf(0) !== -1) + return handleError(path, cb); + return path; + } + const normPath = getPathFromURL(path); + if (typeof normPath !== 'string') { + if (normPath instanceof Error) { + return handleError(path, cb, normPath); + } + // TODO: this should likely throw. Only strings and buffers should + // be accepted + if (path === undefined) + return null; + return normPath; + } + path = normPath; } - return false; + + if (path.indexOf('\u0000') !== -1) + return handleError(path, cb); + return path; } fs.access = function(path, mode, callback) { if (typeof mode === 'function') { callback = mode; - mode = fs.F_OK; + mode = F_OK; } else if (typeof callback !== 'function') { throw new errors.TypeError('ERR_INVALID_CALLBACK'); } - if (handleError((path = getPathFromURL(path)), callback)) - return; - - if (!nullCheck(path, callback)) + path = pathCheck(path, callback); + if (path === undefined) return; - mode = mode | 0; - var req = new FSReqWrap(); - req.oncomplete = makeCallback(callback); - binding.access(pathModule._makeLong(path), mode, req); + const req = new FSReqWrap(); + req.oncomplete = makeCallbackNoCheck(callback); + access(pathModule._makeLong(path), mode | 0, req); }; fs.accessSync = function(path, mode) { - handleError((path = getPathFromURL(path))); - nullCheck(path); - if (mode === undefined) - mode = fs.F_OK; + mode = F_OK; else mode = mode | 0; - binding.access(pathModule._makeLong(path), mode); + access(pathModule._makeLong(pathCheck(path)), mode); }; fs.exists = function(path, callback) { - if (handleError((path = getPathFromURL(path)), cb)) + path = pathCheck(path, cb); + if (path === undefined) return; - if (!nullCheck(path, cb)) return; - var req = new FSReqWrap(); + const req = new FSReqWrap(); req.oncomplete = cb; - binding.stat(pathModule._makeLong(path), req); + stat(pathModule._makeLong(path), req); function cb(err) { if (callback) callback(err ? false : true); } @@ -327,12 +390,9 @@ Object.defineProperty(fs.exists, internalUtil.promisify.custom, { } }); - fs.existsSync = function(path) { try { - handleError((path = getPathFromURL(path))); - nullCheck(path); - binding.stat(pathModule._makeLong(path)); + stat(pathModule._makeLong(pathCheck(path))); return true; } catch (e) { return false; @@ -340,39 +400,45 @@ fs.existsSync = function(path) { }; fs.readFile = function(path, options, callback) { - callback = maybeCallback(callback || options); - options = getOptions(options, { flag: 'r' }); + var encoding, flag; + if (typeof options === 'function') { + callback = options; + flag = O_RDONLY; + } else { + if (typeof options === 'string' || options === null || + options === undefined) { + encoding = options; + flag = O_RDONLY; + } else { + assertOptions(options); + encoding = options.encoding; + flag = options.flag ? stringToFlags(options.flag) : O_RDONLY; + } + assertEncoding(encoding); + callback = maybeCallback(callback); + } - if (handleError((path = getPathFromURL(path)), callback)) - return; - if (!nullCheck(path, callback)) + path = pathCheck(path, callback); + if (path === undefined) return; - var context = new ReadFileContext(callback, options.encoding); - context.isUserFd = isFd(path); // file descriptor ownership - var req = new FSReqWrap(); - req.context = context; - req.oncomplete = readFileAfterOpen; - + const context = new ReadFileContext(callback, encoding, isFd(path)); if (context.isUserFd) { - process.nextTick(function() { - req.oncomplete(null, path); + process.nextTick(() => { + context.req.oncomplete(null, path); }); - return; + } else { + open(pathModule._makeLong(path), flag, 0o666, context.req); } - - binding.open(pathModule._makeLong(path), - stringToFlags(options.flag || 'r'), - 0o666, - req); }; -const kReadFileBufferLength = 8 * 1024; - -function ReadFileContext(callback, encoding) { +function ReadFileContext(callback, encoding, isUserFd) { + this.req = new FSReqWrap(); + this.req.oncomplete = readFileAfterOpen; + this.req.context = this; this.fd = undefined; - this.isUserFd = undefined; - this.size = undefined; + this.isUserFd = isUserFd; + this.size = 0; this.callback = callback; this.buffers = null; this.buffer = null; @@ -396,31 +462,25 @@ ReadFileContext.prototype.read = function() { length = this.size - this.pos; } - var req = new FSReqWrap(); - req.oncomplete = readFileAfterRead; - req.context = this; - - binding.read(this.fd, buffer, offset, length, -1, req); + this.req.oncomplete = readFileAfterRead; + read(this.fd, buffer, offset, length, -1, this.req); }; ReadFileContext.prototype.close = function(err) { - var req = new FSReqWrap(); - req.oncomplete = readFileAfterClose; - req.context = this; + this.req.oncomplete = readFileAfterClose; this.err = err; if (this.isUserFd) { - process.nextTick(function() { - req.oncomplete(null); + process.nextTick(() => { + this.req.oncomplete(null); }); - return; + } else { + close(this.fd, this.req); } - - binding.close(this.fd, req); }; function readFileAfterOpen(err, fd) { - var context = this.context; + const context = this.context; if (err) { context.callback(err); @@ -428,34 +488,28 @@ function readFileAfterOpen(err, fd) { } context.fd = fd; - - var req = new FSReqWrap(); - req.oncomplete = readFileAfterStat; - req.context = context; - binding.fstat(fd, req); + context.req.oncomplete = readFileAfterStat; + fstat(fd, context.req); } function readFileAfterStat(err) { - var context = this.context; + const context = this.context; if (err) return context.close(err); // 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; + // statValues[1] === mode + // statValues[8] === size + const size = (statValues[1] & S_IFMT) === S_IFREG ? statValues[8] : 0; + context.size = size; if (size === 0) { context.buffers = []; context.read(); return; - } - - if (size > kMaxLength) { + } else if (size > kMaxLength) { err = new RangeError('File size is greater than possible Buffer: ' + `0x${kMaxLength.toString(16)} bytes`); return context.close(err); @@ -466,7 +520,7 @@ function readFileAfterStat(err) { } function readFileAfterRead(err, bytesRead) { - var context = this.context; + const context = this.context; if (err) return context.close(err); @@ -482,16 +536,16 @@ function readFileAfterRead(err, bytesRead) { else context.read(); } else { - // unknown size, just read until we don't get bytes. + // 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; + const context = this.context; + const callback = context.callback; + var buffer; if (context.err || err) return callback(context.err || err); @@ -504,124 +558,88 @@ function readFileAfterClose(err) { buffer = context.buffer; if (context.encoding) { - return tryToString(buffer, context.encoding, callback); + try { + return callback(null, buffer.toString(context.encoding)); + } catch (err) { + return callback(err); + } } callback(null, buffer); } -function tryToString(buf, encoding, callback) { - try { - buf = buf.toString(encoding); - } catch (err) { - return callback(err); - } - callback(null, buf); -} - -function tryStatSync(fd, isUserFd) { - var threw = true; - try { - binding.fstat(fd); - threw = false; - } finally { - if (threw && !isUserFd) fs.closeSync(fd); - } -} - -function tryCreateBuffer(size, fd, isUserFd) { - var threw = true; - var buffer; - try { - buffer = Buffer.allocUnsafe(size); - threw = false; - } finally { - if (threw && !isUserFd) fs.closeSync(fd); +function singleRead(size, fd) { + var pos = 0; + const buffer = Buffer.allocUnsafe(size); + do { + var bytesRead = read(fd, buffer, pos, size - pos); + pos += bytesRead; + } while (bytesRead !== 0 && pos < size); + if (pos < size) { + return buffer.slice(0, pos); } return buffer; } -function tryReadSync(fd, isUserFd, buffer, pos, len) { - var threw = true; - var bytesRead; - try { - bytesRead = fs.readSync(fd, buffer, pos, len); - threw = false; - } finally { - if (threw && !isUserFd) fs.closeSync(fd); - } - return bytesRead; +function multipleReads(fd) { + const buffers = []; + var pos = 0; + do { + // The kernel lies about many files. + // Go ahead and try to read some bytes. + const newBuffer = Buffer.allocUnsafe(8192); + var bytesRead = read(fd, newBuffer, 0, 8192); + if (bytesRead !== 0) { + buffers.push(newBuffer.slice(0, bytesRead)); + } + pos += bytesRead; + } while (bytesRead !== 0); + // Data was collected into the buffers list. + return Buffer.concat(buffers, pos); } fs.readFileSync = function(path, options) { - options = getOptions(options, { flag: 'r' }); - var isUserFd = isFd(path); // file descriptor ownership - var fd = isUserFd ? path : fs.openSync(path, options.flag || 'r', 0o666); - - tryStatSync(fd, isUserFd); - // 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 = statValues[8/*size*/]; - else - size = 0; - var pos = 0; - var buffer; // single buffer with file data - var buffers; // list for when size is unknown - - if (size === 0) { - buffers = []; + var encoding, flag; + if (options === undefined || typeof options === 'string' || + options === null) { + encoding = options; + flag = O_RDONLY; } else { - buffer = tryCreateBuffer(size, fd, isUserFd); + assertOptions(options); + encoding = options.encoding; + flag = options.flag || O_RDONLY; } - var bytesRead; - - if (size !== 0) { - do { - bytesRead = tryReadSync(fd, isUserFd, buffer, pos, size - pos); - pos += bytesRead; - } while (bytesRead !== 0 && pos < size); - } else { - do { - // the kernel lies about many files. - // Go ahead and try to read some bytes. - buffer = Buffer.allocUnsafe(8192); - bytesRead = tryReadSync(fd, isUserFd, buffer, 0, 8192); - if (bytesRead !== 0) { - buffers.push(buffer.slice(0, bytesRead)); - } - pos += bytesRead; - } while (bytesRead !== 0); - } + assertEncoding(encoding); - if (!isUserFd) - fs.closeSync(fd); + const isUserFd = isFd(path); // file descriptor ownership + const fd = isUserFd ? + path : open(pathModule._makeLong(pathCheck(path)), flag, 0o666); - if (size === 0) { - // data was collected into the buffers list. - buffer = Buffer.concat(buffers, pos); - } else if (pos < size) { - buffer = buffer.slice(0, pos); + try { + fstat(fd); + // Use stats array directly to avoid creating an fs.Stats instance just for + // our internal use. + // statValues[1] === mode + // statValues[8] === size + const size = (statValues[1] & S_IFMT) === S_IFREG ? statValues[8] : 0; + var buffer = size === 0 ? multipleReads(fd) : singleRead(size, fd); + } finally { + if (!isUserFd) close(fd); } - if (options.encoding) buffer = buffer.toString(options.encoding); + if (encoding) return buffer.toString(encoding); return buffer; }; - -// Yes, the follow could be easily DRYed up but I provide the explicit -// list to make the arguments clear. - fs.close = function(fd, callback) { - var req = new FSReqWrap(); + const req = new FSReqWrap(); req.oncomplete = makeCallback(callback); - binding.close(fd, req); + close(fd, req); }; fs.closeSync = function(fd) { - return binding.close(fd); + return close(fd); }; function modeNum(m, def) { @@ -630,50 +648,44 @@ function modeNum(m, def) { if (typeof m === 'string') return parseInt(m, 8); if (def) - return modeNum(def); + return def; return undefined; } fs.open = function(path, flags, mode, callback_) { - var callback = makeCallback(arguments[arguments.length - 1]); - mode = modeNum(mode, 0o666); + const callback = makeCallback(arguments[arguments.length - 1]); - if (handleError((path = getPathFromURL(path)), callback)) + path = pathCheck(path, callback); + if (path === undefined) return; - if (!nullCheck(path, callback)) return; - var req = new FSReqWrap(); + const req = new FSReqWrap(); req.oncomplete = callback; - binding.open(pathModule._makeLong(path), - stringToFlags(flags), - mode, - req); + open(pathModule._makeLong(path), + stringToFlags(flags), + modeNum(mode, 0o666), + req); }; fs.openSync = function(path, flags, mode) { mode = modeNum(mode, 0o666); - handleError((path = getPathFromURL(path))); - nullCheck(path); - return binding.open(pathModule._makeLong(path), stringToFlags(flags), mode); + path = pathCheck(path); + return open(pathModule._makeLong(path), stringToFlags(flags), mode); }; fs.read = function(fd, buffer, offset, length, position, callback) { if (length === 0) { - return process.nextTick(function() { + return process.nextTick(() => { callback && callback(null, 0, buffer); }); } - - function wrapper(err, bytesRead) { + const req = new FSReqWrap(); + req.oncomplete = (err, bytesRead) => { // Retain a reference to buffer so that it can't be GC'ed too soon. callback && callback(err, bytesRead || 0, buffer); - } - - var req = new FSReqWrap(); - req.oncomplete = wrapper; - - binding.read(fd, buffer, offset, length, position, req); + }; + read(fd, buffer, offset, length, position, req); }; Object.defineProperty(fs.read, internalUtil.customPromisifyArgs, @@ -684,7 +696,7 @@ fs.readSync = function(fd, buffer, offset, length, position) { return 0; } - return binding.read(fd, buffer, offset, length, position); + return read(fd, buffer, offset, length, position); }; // usage: @@ -697,11 +709,11 @@ fs.write = function(fd, buffer, offset, length, position, callback) { callback(err, written || 0, buffer); } - var req = new FSReqWrap(); + const req = new FSReqWrap(); req.oncomplete = wrapper; - if (isUint8Array(buffer)) { - callback = maybeCallback(callback || position || length || offset); + if (typeof buffer === 'object' && isUint8Array(buffer)) { + callback = maybeCallback(arguments[arguments.length - 1]); if (typeof offset !== 'number') { offset = 0; } @@ -711,9 +723,10 @@ fs.write = function(fd, buffer, offset, length, position, callback) { if (typeof position !== 'number') { position = null; } - return binding.writeBuffer(fd, buffer, offset, length, position, req); + return writeBuffer(fd, buffer, offset, length, position, req); } + // TODO: this should probably throw instead of being coerced to a string if (typeof buffer !== 'string') buffer += ''; if (typeof position !== 'function') { @@ -726,7 +739,7 @@ fs.write = function(fd, buffer, offset, length, position, callback) { length = 'utf8'; } callback = maybeCallback(position); - return binding.writeString(fd, buffer, offset, length, req); + return writeString(fd, buffer, offset, length, req); }; Object.defineProperty(fs.write, internalUtil.customPromisifyArgs, @@ -737,46 +750,43 @@ Object.defineProperty(fs.write, internalUtil.customPromisifyArgs, // OR // fs.writeSync(fd, string[, position[, encoding]]); fs.writeSync = function(fd, buffer, offset, length, position) { - if (isUint8Array(buffer)) { + if (typeof buffer === 'object' && isUint8Array(buffer)) { if (position === undefined) position = null; if (typeof offset !== 'number') offset = 0; if (typeof length !== 'number') length = buffer.length - offset; - return binding.writeBuffer(fd, buffer, offset, length, position); + return writeBuffer(fd, buffer, offset, length, position); } + // TODO: this should probably throw instead of being coerced to a string if (typeof buffer !== 'string') buffer += ''; if (offset === undefined) offset = null; - return binding.writeString(fd, buffer, offset, length, position); + return writeString(fd, buffer, offset, length, position); }; fs.rename = function(oldPath, newPath, callback) { callback = makeCallback(callback); - if (handleError((oldPath = getPathFromURL(oldPath)), callback)) + oldPath = pathCheck(oldPath, callback); + if (oldPath === undefined) return; - if (handleError((newPath = getPathFromURL(newPath)), callback)) + newPath = pathCheck(newPath, callback); + if (newPath === undefined) return; - if (!nullCheck(oldPath, callback)) return; - if (!nullCheck(newPath, callback)) return; - var req = new FSReqWrap(); + const req = new FSReqWrap(); req.oncomplete = callback; - binding.rename(pathModule._makeLong(oldPath), - pathModule._makeLong(newPath), - req); + rename(pathModule._makeLong(oldPath), + pathModule._makeLong(newPath), + req); }; fs.renameSync = function(oldPath, newPath) { - handleError((oldPath = getPathFromURL(oldPath))); - handleError((newPath = getPathFromURL(newPath))); - nullCheck(oldPath); - nullCheck(newPath); - return binding.rename(pathModule._makeLong(oldPath), - pathModule._makeLong(newPath)); + return rename(pathModule._makeLong(pathCheck(oldPath)), + pathModule._makeLong(pathCheck(newPath))); }; fs.truncate = function(path, len, callback) { @@ -786,417 +796,449 @@ fs.truncate = function(path, len, callback) { if (typeof len === 'function') { callback = len; len = 0; - } else if (len === undefined) { - len = 0; + } else { + if (len === undefined) { + len = 0; + } + callback = maybeCallback(callback); } - callback = maybeCallback(callback); - fs.open(path, 'r+', function(er, fd) { - if (er) return callback(er); - var req = new FSReqWrap(); - req.oncomplete = function oncomplete(er) { - fs.close(fd, function(er2) { - callback(er || er2); - }); + path = pathCheck(path, callback); + if (path === undefined) + return; + + const req = new FSReqWrap(); + req.oncomplete = (err1, fd) => { + if (err1) return callback(err1); + req.oncomplete = (err2) => { + req.oncomplete = (err3) => { + callback(err2 || err3); + }; + close(fd, req); }; - binding.ftruncate(fd, len, req); - }); + ftruncate(fd, len, req); + }; + open(pathModule._makeLong(path), O_RDWR, 0o666, req); }; fs.truncateSync = function(path, len) { - if (typeof path === 'number') { - // legacy - return fs.ftruncateSync(path, len); - } if (len === undefined) { len = 0; } - // allow error to be thrown, but still close fd. - var fd = fs.openSync(path, 'r+'); + if (typeof path === 'number') { + // Legacy + return ftruncate(path, len); + } + // Allow error to be thrown, but still close fd. + const fd = open(pathModule._makeLong(pathCheck(path)), O_RDWR, 0o666); var ret; try { - ret = fs.ftruncateSync(fd, len); + ret = ftruncate(fd, len); } finally { - fs.closeSync(fd); + close(fd); } return ret; }; fs.ftruncate = function(fd, len, callback) { if (typeof len === 'function') { - callback = len; - len = 0; - } else if (len === undefined) { + callback = makeCallbackNoCheck(len); len = 0; + } else { + if (len === undefined) { + len = 0; + } + callback = makeCallback(callback); } - var req = new FSReqWrap(); - req.oncomplete = makeCallback(callback); - binding.ftruncate(fd, len, req); + const req = new FSReqWrap(); + req.oncomplete = callback; + ftruncate(fd, len, req); }; fs.ftruncateSync = function(fd, len) { if (len === undefined) { len = 0; } - return binding.ftruncate(fd, len); + return ftruncate(fd, len); }; fs.rmdir = function(path, callback) { callback = maybeCallback(callback); - if (handleError((path = getPathFromURL(path)), callback)) + path = pathCheck(path, callback); + if (path === undefined) return; - if (!nullCheck(path, callback)) return; - var req = new FSReqWrap(); + const req = new FSReqWrap(); req.oncomplete = callback; - binding.rmdir(pathModule._makeLong(path), req); + rmdir(pathModule._makeLong(path), req); }; fs.rmdirSync = function(path) { - handleError((path = getPathFromURL(path))); - nullCheck(path); - return binding.rmdir(pathModule._makeLong(path)); + return rmdir(pathModule._makeLong(pathCheck(path))); }; fs.fdatasync = function(fd, callback) { - var req = new FSReqWrap(); + const req = new FSReqWrap(); req.oncomplete = makeCallback(callback); - binding.fdatasync(fd, req); + fdatasync(fd, req); }; fs.fdatasyncSync = function(fd) { - return binding.fdatasync(fd); + return fdatasync(fd); }; fs.fsync = function(fd, callback) { - var req = new FSReqWrap(); + const req = new FSReqWrap(); req.oncomplete = makeCallback(callback); - binding.fsync(fd, req); + fsync(fd, req); }; fs.fsyncSync = function(fd) { - return binding.fsync(fd); + return fsync(fd); }; fs.mkdir = function(path, mode, callback) { - if (typeof mode === 'function') callback = mode; - callback = makeCallback(callback); - if (handleError((path = getPathFromURL(path)), callback)) + if (typeof mode === 'function') { + callback = makeCallbackNoCheck(mode); + } else { + callback = makeCallback(callback); + } + path = pathCheck(path, callback); + if (path === undefined) return; - if (!nullCheck(path, callback)) return; - var req = new FSReqWrap(); + const req = new FSReqWrap(); req.oncomplete = callback; - binding.mkdir(pathModule._makeLong(path), - modeNum(mode, 0o777), - req); + mkdir(pathModule._makeLong(path), modeNum(mode, 0o777), req); }; fs.mkdirSync = function(path, mode) { - handleError((path = getPathFromURL(path))); - nullCheck(path); - return binding.mkdir(pathModule._makeLong(path), - modeNum(mode, 0o777)); + return mkdir(pathModule._makeLong(pathCheck(path)), modeNum(mode, 0o777)); }; fs.readdir = function(path, options, callback) { - callback = makeCallback(typeof options === 'function' ? options : callback); - options = getOptions(options, {}); - if (handleError((path = getPathFromURL(path)), callback)) + var encoding; + if (typeof options === 'function') { + callback = makeCallbackNoCheck(options); + } else { + if (typeof options === 'string' || options === null || + options === undefined) { + encoding = options; + } else { + assertOptions(options); + encoding = options.encoding; + } + assertEncoding(encoding); + callback = makeCallback(callback); + } + path = pathCheck(path, callback); + if (path === undefined) return; - if (!nullCheck(path, callback)) return; - var req = new FSReqWrap(); + const req = new FSReqWrap(); req.oncomplete = callback; - binding.readdir(pathModule._makeLong(path), options.encoding, req); + readdir(pathModule._makeLong(path), encoding, req); }; fs.readdirSync = function(path, options) { - options = getOptions(options, {}); - handleError((path = getPathFromURL(path))); - nullCheck(path); - return binding.readdir(pathModule._makeLong(path), options.encoding); + var encoding; + if (options === undefined || typeof options === 'string' || + options === null) { + encoding = options; + } else { + assertOptions(options); + encoding = options.encoding; + } + assertEncoding(encoding); + return readdir(pathModule._makeLong(pathCheck(path)), encoding); }; fs.fstat = function(fd, callback) { - var req = new FSReqWrap(); + const req = new FSReqWrap(); req.oncomplete = makeStatsCallback(callback); - binding.fstat(fd, req); + fstat(fd, req); }; fs.lstat = function(path, callback) { callback = makeStatsCallback(callback); - if (handleError((path = getPathFromURL(path)), callback)) + path = pathCheck(path, callback); + if (path === undefined) return; - if (!nullCheck(path, callback)) return; - var req = new FSReqWrap(); + const req = new FSReqWrap(); req.oncomplete = callback; - binding.lstat(pathModule._makeLong(path), req); + lstat(pathModule._makeLong(path), req); }; fs.stat = function(path, callback) { callback = makeStatsCallback(callback); - if (handleError((path = getPathFromURL(path)), callback)) + path = pathCheck(path, callback); + if (path === undefined) return; - if (!nullCheck(path, callback)) return; - var req = new FSReqWrap(); + const req = new FSReqWrap(); req.oncomplete = callback; - binding.stat(pathModule._makeLong(path), req); + stat(pathModule._makeLong(path), req); }; fs.fstatSync = function(fd) { - binding.fstat(fd); + fstat(fd); return statsFromValues(); }; fs.lstatSync = function(path) { - handleError((path = getPathFromURL(path))); - nullCheck(path); - binding.lstat(pathModule._makeLong(path)); + lstat(pathModule._makeLong(pathCheck(path))); return statsFromValues(); }; fs.statSync = function(path) { - handleError((path = getPathFromURL(path))); - nullCheck(path); - binding.stat(pathModule._makeLong(path)); + stat(pathModule._makeLong(pathCheck(path))); return statsFromValues(); }; fs.readlink = function(path, options, callback) { - callback = makeCallback(typeof options === 'function' ? options : callback); - options = getOptions(options, {}); - if (handleError((path = getPathFromURL(path)), callback)) + var encoding; + if (typeof options === 'function') { + callback = makeCallbackNoCheck(options); + } else { + if (typeof options === 'string' || options === null || + options === undefined) { + encoding = options; + } else { + assertOptions(options); + encoding = options.encoding; + } + assertEncoding(encoding); + callback = makeCallback(callback); + } + path = pathCheck(path, callback); + if (path === undefined) return; - if (!nullCheck(path, callback)) return; - var req = new FSReqWrap(); + const req = new FSReqWrap(); req.oncomplete = callback; - binding.readlink(pathModule._makeLong(path), options.encoding, req); + readlink(pathModule._makeLong(path), encoding, req); }; fs.readlinkSync = function(path, options) { - options = getOptions(options, {}); - handleError((path = getPathFromURL(path))); - nullCheck(path); - return binding.readlink(pathModule._makeLong(path), options.encoding); + var encoding; + if (options === undefined || typeof options === 'string' || + options === null) { + encoding = options; + } else { + assertOptions(options); + encoding = options.encoding; + } + assertEncoding(encoding); + return readlink(pathModule._makeLong(pathCheck(path)), encoding); }; function preprocessSymlinkDestination(path, type, linkPath) { if (!isWindows) { // No preprocessing is needed on Unix. return path; - } else if (type === 'junction') { + } + if (type === 'junction') { // Junctions paths need to be absolute and \\?\-prefixed. // A relative target is relative to the link's parent directory. - path = pathModule.resolve(linkPath, '..', path); - return pathModule._makeLong(path); - } else { - // Windows symlinks don't tolerate forward slashes. - return ('' + path).replace(/\//g, '\\'); + return pathModule._makeLong(pathModule.resolve(linkPath, '..', path)); } + // Windows symlinks don't tolerate forward slashes. + return path.replace(/\//g, '\\'); } fs.symlink = function(target, path, type_, callback_) { - var type = (typeof type_ === 'string' ? type_ : null); - var callback = makeCallback(arguments[arguments.length - 1]); + const type = typeof type_ === 'string' ? type_ : null; + const callback = makeCallback(arguments[arguments.length - 1]); - if (handleError((target = getPathFromURL(target)), callback)) + target = pathCheck(target, callback); + if (target === undefined) return; - - if (handleError((path = getPathFromURL(path)), callback)) + path = pathCheck(path, callback); + if (path === undefined) return; - if (!nullCheck(target, callback)) return; - if (!nullCheck(path, callback)) return; - - var req = new FSReqWrap(); + const req = new FSReqWrap(); req.oncomplete = callback; - binding.symlink(preprocessSymlinkDestination(target, type, path), - pathModule._makeLong(path), - type, - req); + symlink(preprocessSymlinkDestination(target, type, path), + pathModule._makeLong(path), + type, + req); }; fs.symlinkSync = function(target, path, type) { - type = (typeof type === 'string' ? type : null); - handleError((target = getPathFromURL(target))); - handleError((path = getPathFromURL(path))); - nullCheck(target); - nullCheck(path); + type = typeof type === 'string' ? type : null; + target = pathCheck(target); + path = pathCheck(path); - return binding.symlink(preprocessSymlinkDestination(target, type, path), - pathModule._makeLong(path), - type); + return symlink(preprocessSymlinkDestination(target, type, path), + pathModule._makeLong(path), + type); }; fs.link = function(existingPath, newPath, callback) { callback = makeCallback(callback); - if (handleError((existingPath = getPathFromURL(existingPath)), callback)) + existingPath = pathCheck(existingPath, callback); + if (existingPath === undefined) return; - - if (handleError((newPath = getPathFromURL(newPath)), callback)) + newPath = pathCheck(newPath, callback); + if (newPath === undefined) return; - if (!nullCheck(existingPath, callback)) return; - if (!nullCheck(newPath, callback)) return; - - var req = new FSReqWrap(); + const req = new FSReqWrap(); req.oncomplete = callback; - binding.link(pathModule._makeLong(existingPath), - pathModule._makeLong(newPath), - req); + link(pathModule._makeLong(existingPath), + pathModule._makeLong(newPath), + req); }; fs.linkSync = function(existingPath, newPath) { - handleError((existingPath = getPathFromURL(existingPath))); - handleError((newPath = getPathFromURL(newPath))); - nullCheck(existingPath); - nullCheck(newPath); - return binding.link(pathModule._makeLong(existingPath), - pathModule._makeLong(newPath)); + return link(pathModule._makeLong(pathCheck(existingPath)), + pathModule._makeLong(pathCheck(newPath))); }; fs.unlink = function(path, callback) { callback = makeCallback(callback); - if (handleError((path = getPathFromURL(path)), callback)) + path = pathCheck(path, callback); + if (path === undefined) return; - if (!nullCheck(path, callback)) return; - var req = new FSReqWrap(); + const req = new FSReqWrap(); req.oncomplete = callback; - binding.unlink(pathModule._makeLong(path), req); + unlink(pathModule._makeLong(path), req); }; fs.unlinkSync = function(path) { - handleError((path = getPathFromURL(path))); - nullCheck(path); - return binding.unlink(pathModule._makeLong(path)); + return unlink(pathModule._makeLong(pathCheck(path))); }; fs.fchmod = function(fd, mode, callback) { - var req = new FSReqWrap(); + const req = new FSReqWrap(); req.oncomplete = makeCallback(callback); - binding.fchmod(fd, modeNum(mode), req); + fchmod(fd, modeNum(mode), req); }; fs.fchmodSync = function(fd, mode) { - return binding.fchmod(fd, modeNum(mode)); + return fchmod(fd, modeNum(mode)); }; -if (constants.O_SYMLINK !== undefined) { +if (O_SYMLINK !== undefined) { + const chownFlag = O_WRONLY | O_SYMLINK; fs.lchmod = function(path, mode, callback) { callback = maybeCallback(callback); - fs.open(path, constants.O_WRONLY | constants.O_SYMLINK, function(err, fd) { - if (err) { - callback(err); - return; - } + path = pathCheck(path, callback); + if (path === undefined) + return; + + const req = new FSReqWrap(); + req.oncomplete = (err, fd) => { + if (err) return callback(err); // Prefer to return the chmod error, if one occurs, // but still try to close, and report closing errors if they occur. - fs.fchmod(fd, mode, function(err) { - fs.close(fd, function(err2) { + req.oncomplete = (err) => { + req.oncomplete = (err2) => { callback(err || err2); - }); - }); - }); + }; + close(fd, req); + }; + fchmod(fd, modeNum(mode), req); + }; + + open(pathModule._makeLong(path), chownFlag, 0o666, req); }; fs.lchmodSync = function(path, mode) { - var fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK); + const flag = chownFlag; + const fd = open(pathModule._makeLong(pathCheck(path)), flag, 0o666); // Prefer to return the chmod error, if one occurs, // but still try to close, and report closing errors if they occur. var ret; try { - ret = fs.fchmodSync(fd, mode); + ret = fchmod(fd, modeNum(mode)); } catch (err) { try { - fs.closeSync(fd); + close(fd); } catch (ignore) {} throw err; } - fs.closeSync(fd); + close(fd); return ret; }; -} + fs.lchown = function(path, uid, gid, callback) { + callback = maybeCallback(callback); + path = pathCheck(path, callback); + if (path === undefined) + return; + + const req = new FSReqWrap(); + req.oncomplete = (err, fd) => { + if (err) return callback(err); + req.oncomplete = (err) => { callback(err); }; + fchown(fd, uid, gid, req); + }; + + open(pathModule._makeLong(path), chownFlag, 0o666, req); + }; + + fs.lchownSync = function(path, uid, gid) { + const fd = open(pathModule._makeLong(pathCheck(path)), chownFlag, 0o666); + return fchown(fd, uid, gid); + }; +} fs.chmod = function(path, mode, callback) { callback = makeCallback(callback); - if (handleError((path = getPathFromURL(path)), callback)) + path = pathCheck(path, callback); + if (path === undefined) return; - if (!nullCheck(path, callback)) return; - var req = new FSReqWrap(); + const req = new FSReqWrap(); req.oncomplete = callback; - binding.chmod(pathModule._makeLong(path), - modeNum(mode), - req); + chmod(pathModule._makeLong(path), modeNum(mode), req); }; fs.chmodSync = function(path, mode) { - handleError((path = getPathFromURL(path))); - nullCheck(path); - return binding.chmod(pathModule._makeLong(path), modeNum(mode)); + path = pathCheck(path); + return chmod(pathModule._makeLong(path), modeNum(mode)); }; -if (constants.O_SYMLINK !== undefined) { - fs.lchown = function(path, uid, gid, callback) { - callback = maybeCallback(callback); - fs.open(path, constants.O_WRONLY | constants.O_SYMLINK, function(err, fd) { - if (err) { - callback(err); - return; - } - fs.fchown(fd, uid, gid, callback); - }); - }; - - fs.lchownSync = function(path, uid, gid) { - var fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK); - return fs.fchownSync(fd, uid, gid); - }; -} - fs.fchown = function(fd, uid, gid, callback) { - var req = new FSReqWrap(); + const req = new FSReqWrap(); req.oncomplete = makeCallback(callback); - binding.fchown(fd, uid, gid, req); + fchown(fd, uid, gid, req); }; fs.fchownSync = function(fd, uid, gid) { - return binding.fchown(fd, uid, gid); + return fchown(fd, uid, gid); }; fs.chown = function(path, uid, gid, callback) { callback = makeCallback(callback); - if (handleError((path = getPathFromURL(path)), callback)) + path = pathCheck(path, callback); + if (path === undefined) return; - if (!nullCheck(path, callback)) return; - var req = new FSReqWrap(); + const req = new FSReqWrap(); req.oncomplete = callback; - binding.chown(pathModule._makeLong(path), uid, gid, req); + chown(pathModule._makeLong(path), uid, gid, req); }; fs.chownSync = function(path, uid, gid) { - handleError((path = getPathFromURL(path))); - nullCheck(path); - return binding.chown(pathModule._makeLong(path), uid, gid); + return chown(pathModule._makeLong(pathCheck(path)), uid, gid); }; -// converts Date or number to a fractional UNIX timestamp +// Converts Date or number to a fractional UNIX timestamp function toUnixTimestamp(time) { - // eslint-disable-next-line eqeqeq - if (typeof time === 'string' && +time == time) { - return +time; - } - if (Number.isFinite(time)) { + if (typeof time === 'string') { + const timeNumber = +time; + // eslint-disable-next-line eqeqeq + if (timeNumber == time) + return timeNumber; + } else if (Number.isFinite(time)) { if (time < 0) { return Date.now() / 1000; } return time; } if (util.isDate(time)) { - // convert to 123.456 UNIX timestamp + // Convert to 123.456 UNIX timestamp return time.getTime() / 1000; } throw new errors.Error('ERR_INVALID_ARG_TYPE', @@ -1205,117 +1247,164 @@ function toUnixTimestamp(time) { time); } -// exported for unit tests, not for public consumption +// Exported for unit tests, not for public consumption fs._toUnixTimestamp = toUnixTimestamp; fs.utimes = function(path, atime, mtime, callback) { callback = makeCallback(callback); - if (handleError((path = getPathFromURL(path)), callback)) + path = pathCheck(path, callback); + if (path === undefined) return; - if (!nullCheck(path, callback)) return; - var req = new FSReqWrap(); + const req = new FSReqWrap(); req.oncomplete = callback; - binding.utimes(pathModule._makeLong(path), - toUnixTimestamp(atime), - toUnixTimestamp(mtime), - req); + utimes(pathModule._makeLong(path), + toUnixTimestamp(atime), + toUnixTimestamp(mtime), + req); }; fs.utimesSync = function(path, atime, mtime) { - handleError((path = getPathFromURL(path))); - nullCheck(path); - atime = toUnixTimestamp(atime); - mtime = toUnixTimestamp(mtime); - binding.utimes(pathModule._makeLong(path), atime, mtime); + utimes(pathModule._makeLong(pathCheck(path)), + toUnixTimestamp(atime), + toUnixTimestamp(mtime)); }; fs.futimes = function(fd, atime, mtime, callback) { - atime = toUnixTimestamp(atime); - mtime = toUnixTimestamp(mtime); - var req = new FSReqWrap(); + const req = new FSReqWrap(); req.oncomplete = makeCallback(callback); - binding.futimes(fd, atime, mtime, req); + futimes(fd, toUnixTimestamp(atime), toUnixTimestamp(mtime), req); }; fs.futimesSync = function(fd, atime, mtime) { - atime = toUnixTimestamp(atime); - mtime = toUnixTimestamp(mtime); - binding.futimes(fd, atime, mtime); + futimes(fd, toUnixTimestamp(atime), toUnixTimestamp(mtime)); }; -function writeAll(fd, isUserFd, buffer, offset, length, position, callback) { - // write(fd, buffer, offset, length, position, callback) - fs.write(fd, buffer, offset, length, position, function(writeErr, written) { - if (writeErr) { +function writeAll(fd, isUserFd, data, encoding, flag, callback) { + const buffer = typeof data === 'object' && isUint8Array(data) ? + data : Buffer.from('' + data, encoding || 'utf8'); + var position = flag & O_APPEND ? null : 0; + var offset = 0; + var length = buffer.length; + const req = new FSReqWrap(); + req.oncomplete = (err, written = 0) => { + if (err) { if (isUserFd) { - callback(writeErr); + callback(err); } else { - fs.close(fd, function() { - callback(writeErr); - }); + req.oncomplete = () => { callback(err); }; + close(fd, req); } - } else { - if (written === length) { - if (isUserFd) { - callback(null); - } else { - fs.close(fd, callback); - } + } else if (written === length) { + if (isUserFd) { + callback(null); } else { - offset += written; - length -= written; - if (position !== null) { - position += written; - } - writeAll(fd, isUserFd, buffer, offset, length, position, callback); + req.oncomplete = callback; + close(fd, req); + } + } else { + offset += written; + length -= written; + if (position !== null) { + position += written; } + writeBuffer(fd, buffer, offset, length, position, req); } - }); + }; + writeBuffer(fd, buffer, offset, length, position, req); } fs.writeFile = function(path, data, options, callback) { - callback = maybeCallback(callback || options); - options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'w' }); - const flag = options.flag || 'w'; + _writeFile(path, data, options, callback, writeFlags, false); +}; + +fs.appendFile = function(path, data, options, callback) { + _writeFile(path, data, options, callback, appendFlags, true); +}; + +function _writeFile(path, data, options, callback, defaultFlag, checkFD) { + var encoding, mode, flag; + if (typeof options === 'function') { + callback = options; + mode = 0o666; + flag = defaultFlag; + encoding = 'utf8'; + } else { + if (typeof options === 'string' || options === null || + options === undefined) { + encoding = options; + mode = 0o666; + flag = defaultFlag; + } else { + assertOptions(options); + encoding = options.encoding; + mode = modeNum(options.mode, 0o666); + flag = (!options.flag || checkFD && isFd(path)) ? + defaultFlag : stringToFlags(options.flag); + } + assertEncoding(encoding); + callback = maybeCallback(callback); + } if (isFd(path)) { - writeFd(path, true); + writeAll(path, true, data, encoding, flag, callback); return; } - fs.open(path, flag, options.mode, function(openErr, fd) { - if (openErr) { - callback(openErr); + path = pathCheck(path, callback); + if (path === undefined) + return; + + const req = new FSReqWrap(); + req.oncomplete = (err, fd) => { + if (err) { + callback(err); } else { - writeFd(fd, false); + writeAll(fd, false, data, encoding, flag, callback); } - }); + }; - function writeFd(fd, isUserFd) { - var buffer = isUint8Array(data) ? - data : Buffer.from('' + data, options.encoding || 'utf8'); - var position = /a/.test(flag) ? null : 0; + open(pathModule._makeLong(path), flag, mode, req); +} - writeAll(fd, isUserFd, buffer, 0, buffer.length, position, callback); - } +fs.writeFileSync = function(path, data, options) { + _writeFileSync(path, data, options, writeFlags, false); }; -fs.writeFileSync = function(path, data, options) { - options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'w' }); - const flag = options.flag || 'w'; +fs.appendFileSync = function(path, data, options) { + _writeFileSync(path, data, options, appendFlags, true); +}; - var isUserFd = isFd(path); // file descriptor ownership - var fd = isUserFd ? path : fs.openSync(path, flag, options.mode); +function _writeFileSync(path, data, options, defaultFlag, checkFD) { + var encoding, mode, flag; + if (options === undefined || typeof options === 'string' || + options === null) { + encoding = options; + mode = 0o666; + flag = defaultFlag; + } else { + assertOptions(options); + encoding = options.encoding; + mode = modeNum(options.mode, 0o666); + // force append behavior when using a supplied file descriptor + flag = (!options.flag || checkFD && isFd(path)) ? + defaultFlag : stringToFlags(options.flag); + } - if (!isUint8Array(data)) { - data = Buffer.from('' + data, options.encoding || 'utf8'); + if (typeof data !== 'object' || !isUint8Array(data)) { + assertEncoding(encoding); + data = Buffer.from('' + data, encoding || 'utf8'); } + + const isUserFd = isFd(path); // file descriptor ownership + const fd = isUserFd ? + path : open(pathModule._makeLong(pathCheck(path)), flag, mode); + var offset = 0; var length = data.length; - var position = /a/.test(flag) ? null : 0; + var position = flag & O_APPEND ? null : 0; try { while (length > 0) { - var written = fs.writeSync(fd, data, offset, length, position); + const written = writeBuffer(fd, data, offset, length, position); offset += written; length -= written; if (position !== null) { @@ -1323,54 +1412,26 @@ fs.writeFileSync = function(path, data, options) { } } } finally { - if (!isUserFd) fs.closeSync(fd); + if (!isUserFd) close(fd); } -}; - -fs.appendFile = function(path, data, options, callback) { - callback = maybeCallback(callback || options); - options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'a' }); - - // Don't make changes directly on options object - options = copyObject(options); - - // force append behavior when using a supplied file descriptor - if (!options.flag || isFd(path)) - options.flag = 'a'; - - fs.writeFile(path, data, options, callback); -}; - -fs.appendFileSync = function(path, data, options) { - options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'a' }); - - // Don't make changes directly on options object - options = copyObject(options); - - // force append behavior when using a supplied file descriptor - if (!options.flag || isFd(path)) - options.flag = 'a'; - - fs.writeFileSync(path, data, options); -}; +} function FSWatcher() { EventEmitter.call(this); - var self = this; this._handle = new FSEvent(); this._handle.owner = this; - this._handle.onchange = function(status, eventType, filename) { + this._handle.onchange = (status, eventType, filename) => { if (status < 0) { - self._handle.close(); + this._handle.close(); const error = !filename ? errnoException(status, 'Error watching file for changes:') : errnoException(status, `Error watching file ${filename} for changes:`); error.filename = filename; - self.emit('error', error); + this.emit('error', error); } else { - self.emit('change', eventType, filename); + this.emit('change', eventType, filename); } }; } @@ -1380,12 +1441,11 @@ FSWatcher.prototype.start = function(filename, persistent, recursive, encoding) { - handleError((filename = getPathFromURL(filename))); - nullCheck(filename); - var err = this._handle.start(pathModule._makeLong(filename), - persistent, - recursive, - encoding); + filename = pathCheck(filename); + const err = this._handle.start(pathModule._makeLong(filename), + persistent, + recursive, + encoding); if (err) { this._handle.close(); const error = errnoException(err, `watch ${filename}`); @@ -1399,25 +1459,27 @@ FSWatcher.prototype.close = function() { }; fs.watch = function(filename, options, listener) { - handleError((filename = getPathFromURL(filename))); - nullCheck(filename); - - if (typeof options === 'function') { + filename = pathCheck(filename); + + var persistent = true; + var recursive = false; + var encoding; + if (options === undefined || typeof options === 'string' || + options === null) { + encoding = options; + } else if (typeof options === 'function') { listener = options; + encoding = 'utf8'; + } else { + assertOptions(options); + encoding = options.encoding; + persistent = options.persistent === undefined ? true : options.persistent; + recursive = options.recursive === undefined ? false : options.recursive; } - options = getOptions(options, {}); - - // Don't make changes directly on options object - options = copyObject(options); - - if (options.persistent === undefined) options.persistent = true; - if (options.recursive === undefined) options.recursive = false; + assertEncoding(encoding); const watcher = new FSWatcher(); - watcher.start(filename, - options.persistent, - options.recursive, - options.encoding); + watcher.start(filename, persistent, recursive, encoding); if (listener) { watcher.addListener('change', listener); @@ -1426,7 +1488,6 @@ fs.watch = function(filename, options, listener) { return watcher; }; - // Stat Change Watchers function emitStop(self) { @@ -1445,64 +1506,52 @@ function statsFromPrevValues() { function StatWatcher() { EventEmitter.call(this); - var self = this; - this._handle = new binding.StatWatcher(); + this._handle = new BindingStatWatcher(); // uv_fs_poll is a little more powerful than ev_stat but we curb it for // the sake of backwards compatibility var oldStatus = -1; - this._handle.onchange = function(newStatus) { + this._handle.onchange = (newStatus) => { if (oldStatus === -1 && newStatus === -1 && statValues[2/*new nlink*/] === statValues[16/*old nlink*/]) return; oldStatus = newStatus; - self.emit('change', statsFromValues(), statsFromPrevValues()); + this.emit('change', statsFromValues(), statsFromPrevValues()); }; - this._handle.onstop = function() { - process.nextTick(emitStop, self); + this._handle.onstop = () => { + process.nextTick(emitStop, this); }; } util.inherits(StatWatcher, EventEmitter); - StatWatcher.prototype.start = function(filename, persistent, interval) { - handleError((filename = getPathFromURL(filename))); - nullCheck(filename); + filename = pathCheck(filename); this._handle.start(pathModule._makeLong(filename), persistent, interval); }; - StatWatcher.prototype.stop = function() { this._handle.stop(); }; - const statWatchers = new Map(); fs.watchFile = function(filename, options, listener) { - handleError((filename = getPathFromURL(filename))); - nullCheck(filename); - filename = pathModule.resolve(filename); - var stat; - - var defaults = { - // Poll interval in milliseconds. 5007 is what libev used to use. It's - // a little on the slow side but let's stick with it for now to keep - // behavioral changes to a minimum. - interval: 5007, - persistent: true - }; - - if (options !== null && typeof options === 'object') { - options = util._extend(defaults, options); + filename = pathModule.resolve(pathCheck(filename)); + var interval = 5007; + var persistent = true; + + // 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. + if (typeof options === 'object' && options !== null) { + interval = options.interval === undefined ? 5007 : options.interval; + persistent = options.persistent === undefined ? 5007 : options.persistent; } else { listener = options; - options = defaults; } - if (typeof listener !== 'function') { throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'listener', @@ -1510,11 +1559,11 @@ fs.watchFile = function(filename, options, listener) { listener); } - stat = statWatchers.get(filename); + var stat = statWatchers.get(filename); if (stat === undefined) { stat = new StatWatcher(); - stat.start(filename, options.persistent, options.interval); + stat.start(filename, persistent, interval); statWatchers.set(filename, stat); } @@ -1523,10 +1572,8 @@ fs.watchFile = function(filename, options, listener) { }; fs.unwatchFile = function(filename, listener) { - handleError((filename = getPathFromURL(filename))); - nullCheck(filename); - filename = pathModule.resolve(filename); - var stat = statWatchers.get(filename); + filename = pathModule.resolve(pathCheck(filename)); + const stat = statWatchers.get(filename); if (stat === undefined) return; @@ -1542,7 +1589,6 @@ fs.unwatchFile = function(filename, listener) { } }; - var splitRoot; if (isWindows) { // Regex to find the device root on Windows (e.g. 'c:\\'), including trailing @@ -1554,22 +1600,21 @@ if (isWindows) { } else { splitRoot = function splitRoot(str) { for (var i = 0; i < str.length; ++i) { - if (str.charCodeAt(i) !== 47/*'/'*/) + if (str.charCodeAt(i) !== 47) // '/' return str.slice(0, i); } return str; }; } -function encodeRealpathResult(result, options) { - if (!options || !options.encoding || options.encoding === 'utf8') +function encodeRealpathResult(result, encoding) { + if (!encoding || encoding === 'utf8') return result; const asBuffer = Buffer.from(result); - if (options.encoding === 'buffer') { + if (encoding === 'buffer') { return asBuffer; - } else { - return asBuffer.toString(options.encoding); } + return asBuffer.toString(encoding); } // Finds the next portion of a (partial) path, up to the next path delimiter @@ -1587,58 +1632,57 @@ if (isWindows) { nextPart = function nextPart(p, i) { return p.indexOf('/', i); }; } -const emptyObj = Object.create(null); fs.realpathSync = function realpathSync(p, options) { - if (!options) - options = emptyObj; - else - options = getOptions(options, emptyObj); - if (typeof p !== 'string') { - handleError((p = getPathFromURL(p))); - if (typeof p !== 'string') - p += ''; - } - nullCheck(p); - p = pathModule.resolve(p); + var encoding, cache; + + p = pathModule.resolve(`${pathCheck(p)}`); - const cache = options[internalFS.realpathCacheKey]; - const maybeCachedResult = cache && cache.get(p); - if (maybeCachedResult) { - return maybeCachedResult; + if (options === undefined || typeof options === 'string' || + options === null) { + encoding = options; + } else { + assertOptions(options); + cache = options[realpathCacheKey]; + const maybeCachedResult = cache !== undefined && cache.get(p); + if (maybeCachedResult) { + return maybeCachedResult; + } + encoding = options.encoding; } + assertEncoding(encoding); - const seenLinks = Object.create(null); + var seenLinks; const knownHard = Object.create(null); const original = p; - // 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) + // 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 + // The partial path scanned in the previous round, with slash var previous; + // The partial path so far, including a trailing slash if any // Skip over roots - current = base = splitRoot(p); - pos = current.length; + var current = base = splitRoot(p); + // current character position in p + var pos = current.length; // On windows, check that the root exists. On unix there is no need. - if (isWindows && !knownHard[base]) { - binding.lstat(pathModule._makeLong(base)); + if (isWindows) { + lstat(pathModule._makeLong(base)); knownHard[base] = true; + } else { + seenLinks = Object.create(null); } - // walk down the path, swapping out linked path parts for their real + // Walk down the path, swapping out linked path parts for their real // values // NB: p.length changes. while (pos < p.length) { // find the next part - var result = nextPart(p, pos); + const result = nextPart(p, pos); previous = current; if (result === -1) { - var last = p.slice(pos); + const last = p.slice(pos); current += last; base = previous + last; pos = p.length; @@ -1648,8 +1692,8 @@ fs.realpathSync = function realpathSync(p, options) { pos = result + 1; } - // continue if not a symlink, break if a pipe/socket - if (knownHard[base] || (cache && cache.get(base) === base)) { + // Continue if not a symlink, break if a pipe/socket + if (knownHard[base] || (cache !== undefined && cache.get(base) === base)) { if ((statValues[1/*mode*/] & S_IFMT) === S_IFIFO || (statValues[1/*mode*/] & S_IFMT) === S_IFSOCK) { break; @@ -1658,45 +1702,45 @@ fs.realpathSync = function realpathSync(p, options) { } var resolvedLink; - var maybeCachedResolved = cache && cache.get(base); + const maybeCachedResolved = cache !== undefined && cache.get(base); if (maybeCachedResolved) { resolvedLink = maybeCachedResolved; } else { // Use stats array directly to avoid creating an fs.Stats instance just // for our internal use. - var baseLong = pathModule._makeLong(base); - binding.lstat(baseLong); + const baseLong = pathModule._makeLong(base); + lstat(baseLong); if ((statValues[1/*mode*/] & S_IFMT) !== S_IFLNK) { knownHard[base] = true; - if (cache) cache.set(base, base); + if (cache !== undefined) cache.set(base, base); continue; } - // read the link if it wasn't read before + // Read the link if it wasn't read before // dev/ino always return 0 on windows, so skip the check. var linkTarget = null; var id; if (!isWindows) { - var dev = statValues[0/*dev*/].toString(32); - var ino = statValues[7/*ino*/].toString(32); + const dev = statValues[0/*dev*/].toString(32); + const ino = statValues[7/*ino*/].toString(32); id = `${dev}:${ino}`; if (seenLinks[id]) { linkTarget = seenLinks[id]; } } if (linkTarget === null) { - binding.stat(baseLong); - linkTarget = binding.readlink(baseLong); + stat(baseLong); + linkTarget = readlink(baseLong); } resolvedLink = pathModule.resolve(previous, linkTarget); - if (cache) cache.set(base, resolvedLink); + if (cache !== undefined) cache.set(base, resolvedLink); if (!isWindows) seenLinks[id] = linkTarget; } - // resolve the link, then start over + // Resolve the link, then start over p = pathModule.resolve(resolvedLink, p.slice(pos)); // Skip over roots @@ -1705,90 +1749,97 @@ fs.realpathSync = function realpathSync(p, options) { // On windows, check that the root exists. On unix there is no need. if (isWindows && !knownHard[base]) { - binding.lstat(pathModule._makeLong(base)); + lstat(pathModule._makeLong(base)); knownHard[base] = true; } } - if (cache) cache.set(original, p); - return encodeRealpathResult(p, options); + if (cache !== undefined) cache.set(original, p); + return encodeRealpathResult(p, encoding); }; - fs.realpath = function realpath(p, options, callback) { - callback = maybeCallback(typeof options === 'function' ? options : callback); - if (!options) - options = emptyObj; - else - options = getOptions(options, emptyObj); - if (typeof p !== 'string') { - if (handleError((p = getPathFromURL(p)), callback)) - return; - if (typeof p !== 'string') - p += ''; + var encoding; + if (typeof options === 'function') { + callback = options; + } else { + if (typeof options === 'string' || options === null || + options === undefined) { + encoding = options; + } else { + assertOptions(options); + encoding = options.encoding; + } + assertEncoding(encoding); + callback = maybeCallback(callback); } - if (!nullCheck(p, callback)) + + p = pathCheck(p, callback); + if (p === undefined) return; - p = pathModule.resolve(p); + var path = pathModule.resolve(`${p}`); - const seenLinks = Object.create(null); + var seenLinks; const knownHard = Object.create(null); - // 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) + // 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 + // The partial path scanned in the previous round, with slash var previous; - current = base = splitRoot(p); - pos = current.length; + // The partial path so far, including a trailing slash if any + var current = base = splitRoot(path); + // Current character position in p + var pos = current.length; + + const req = new FSReqWrap(); // On windows, check that the root exists. On unix there is no need. - if (isWindows && !knownHard[base]) { - fs.lstat(base, function(err) { + if (isWindows) { + req.oncomplete = (err) => { if (err) return callback(err); knownHard[base] = true; LOOP(); - }); + }; + lstat(pathModule._makeLong(base), req); } else { + seenLinks = Object.create(null); process.nextTick(LOOP); } - // walk down the path, swapping out linked path parts for their real + // Walk down the path, swapping out linked path parts for their real // values function LOOP() { - // stop if scanned past end of path - if (pos >= p.length) { - return callback(null, encodeRealpathResult(p, options)); + // Stop if scanned past end of path + if (pos >= path.length) { + return callback(null, encodeRealpathResult(path, encoding)); } - // find the next part - var result = nextPart(p, pos); + // Find the next part + const result = nextPart(path, pos); previous = current; if (result === -1) { - var last = p.slice(pos); + const last = path.slice(pos); current += last; base = previous + last; - pos = p.length; + pos = path.length; } else { - current += p.slice(pos, result + 1); - base = previous + p.slice(pos, result); + current += path.slice(pos, result + 1); + base = previous + path.slice(pos, result); pos = result + 1; } - // continue if not a symlink, break if a pipe/socket + // Continue if not a symlink, break if a pipe/socket if (knownHard[base]) { if ((statValues[1/*mode*/] & S_IFMT) === S_IFIFO || (statValues[1/*mode*/] & S_IFMT) === S_IFSOCK) { - return callback(null, encodeRealpathResult(p, options)); + return callback(null, encodeRealpathResult(path, encoding)); } return process.nextTick(LOOP); } - return fs.lstat(base, gotStat); + req.oncomplete = gotStat; + lstat(pathModule._makeLong(base), req); } function gotStat(err) { @@ -1797,7 +1848,7 @@ fs.realpath = function realpath(p, options, callback) { // Use stats array directly to avoid creating an fs.Stats instance just for // our internal use. - // if not a symlink, skip to the next path part + // If not a symlink, skip to the next path part if ((statValues[1/*mode*/] & S_IFMT) !== S_IFLNK) { knownHard[base] = true; return process.nextTick(LOOP); @@ -1808,43 +1859,41 @@ fs.realpath = function realpath(p, options, callback) { // dev/ino always return 0 on windows, so skip the check. let id; if (!isWindows) { - var dev = statValues[0/*ino*/].toString(32); - var ino = statValues[7/*ino*/].toString(32); + const dev = statValues[0/*ino*/].toString(32); + const ino = statValues[7/*ino*/].toString(32); id = `${dev}:${ino}`; if (seenLinks[id]) { return gotTarget(null, seenLinks[id], base); } } - fs.stat(base, function(err) { + req.oncomplete = (err) => { if (err) return callback(err); - - fs.readlink(base, function(err, target) { + req.oncomplete = (err, target) => { if (!isWindows) seenLinks[id] = target; gotTarget(err, target); - }); - }); + }; + readlink(pathModule._makeLong(base), encoding, req); + }; + stat(pathModule._makeLong(base), req); } 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)); - current = base = splitRoot(p); + const resolvedLink = pathModule.resolve(previous, target); + // Resolve the link, then start over + path = pathModule.resolve(resolvedLink, path.slice(pos)); + current = base = splitRoot(path); pos = current.length; // On windows, check that the root exists. On unix there is no need. if (isWindows && !knownHard[base]) { - fs.lstat(base, function(err) { + req.oncomplete = (err) => { if (err) return callback(err); knownHard[base] = true; LOOP(); - }); + }; + lstat(pathModule._makeLong(base), req); } else { process.nextTick(LOOP); } @@ -1852,44 +1901,79 @@ fs.realpath = function realpath(p, options, callback) { }; fs.mkdtemp = function(prefix, options, callback) { - callback = makeCallback(typeof options === 'function' ? options : callback); - options = getOptions(options, {}); - if (!prefix || typeof prefix !== 'string') { + if (typeof prefix !== 'string' || prefix === '') { throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'prefix', 'string', prefix); } - if (!nullCheck(prefix, callback)) { - return; + var encoding; + if (typeof options === 'function') { + callback = makeCallbackNoCheck(options); + } else { + if (typeof options === 'string' || options === null || + options === undefined) { + encoding = options; + } else { + assertOptions(options); + encoding = options.encoding; + } + + assertEncoding(encoding); + callback = makeCallback(callback); + } + + var offset = 0; + while (offset++ < prefix.length) { + if (prefix.charCodeAt(offset) === 0) { + process.nextTick(callback, new errors.Error('ERR_INVALID_ARG_TYPE', + 'prefix', + 'string without null bytes', + prefix)); + return; + } } - var req = new FSReqWrap(); + const req = new FSReqWrap(); req.oncomplete = callback; - binding.mkdtemp(prefix + 'XXXXXX', options.encoding, req); + mkdtemp(prefix + 'XXXXXX', encoding, req); }; - fs.mkdtempSync = function(prefix, options) { - if (!prefix || typeof prefix !== 'string') { + if (typeof prefix !== 'string' || prefix === '') { throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'prefix', 'string', prefix); } - options = getOptions(options, {}); - nullCheck(prefix); - return binding.mkdtemp(prefix + 'XXXXXX', options.encoding); -}; + var encoding; + if (options === undefined || typeof options === 'string' || + options === null) { + encoding = options; + } else { + assertOptions(options); + encoding = options.encoding; + } + assertEncoding(encoding); + var offset = 0; + while (offset++ < prefix.length) { + if (prefix.charCodeAt(offset) === 0) { + throw new errors.Error('ERR_INVALID_ARG_TYPE', + 'prefix', + 'string without null bytes', + prefix); + } + } + return mkdtemp(prefix + 'XXXXXX', encoding); +}; // Define copyFile() flags. Object.defineProperties(fs.constants, { - COPYFILE_EXCL: { enumerable: true, value: constants.UV_FS_COPYFILE_EXCL } + COPYFILE_EXCL: { enumerable: true, value: UV_FS_COPYFILE_EXCL } }); - fs.copyFile = function(src, dest, flags, callback) { if (typeof flags === 'function') { callback = flags; @@ -1898,144 +1982,158 @@ fs.copyFile = function(src, dest, flags, callback) { throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'callback', 'function'); } - src = getPathFromURL(src); - - if (handleError(src, callback)) + src = pathCheck(src, callback); + if (src === undefined) return; - - if (!nullCheck(src, callback)) - return; - - dest = getPathFromURL(dest); - - if (handleError(dest, callback)) + dest = pathCheck(dest, callback); + if (dest === undefined) return; - if (!nullCheck(dest, callback)) - return; - - 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); + req.oncomplete = makeCallbackNoCheck(callback); + copyFile(pathModule._makeLong(src), + pathModule._makeLong(dest), + flags | 0, + req); }; - fs.copyFileSync = function(src, dest, flags) { - src = getPathFromURL(src); - handleError(src); - nullCheck(src); - - dest = getPathFromURL(dest); - handleError(dest); - nullCheck(dest); - - src = pathModule._makeLong(src); - dest = pathModule._makeLong(dest); - flags = flags | 0; - binding.copyFile(src, dest, flags); + copyFile(pathModule._makeLong(pathCheck(src)), + pathModule._makeLong(pathCheck(dest)), + flags | 0); }; - -var pool; - -function allocNewPool(poolSize) { - pool = Buffer.allocUnsafe(poolSize); - pool.used = 0; -} - - fs.createReadStream = function(path, options) { - return new ReadStream(path, options); + return new ReadStream(path, options, StreamStartSym); }; util.inherits(ReadStream, Readable); fs.ReadStream = ReadStream; -function ReadStream(path, options) { - if (!(this instanceof ReadStream)) - return new ReadStream(path, options); - - // a little bit bigger buffer and water marks by default - options = copyObject(getOptions(options, {})); - if (options.highWaterMark === undefined) - options.highWaterMark = 64 * 1024; - - Readable.call(this, options); - - handleError((this.path = getPathFromURL(path))); - this.fd = options.fd === undefined ? null : options.fd; - this.flags = options.flags === undefined ? 'r' : options.flags; - this.mode = options.mode === undefined ? 0o666 : options.mode; +function ReadStream(path, options, instanceSymbol) { + if (instanceSymbol !== StreamStartSym && !(this instanceof ReadStream)) + return new ReadStream(path, options, StreamStartSym); - this.start = options.start; - this.end = options.end; - this.autoClose = options.autoClose === undefined ? true : options.autoClose; - this.pos = undefined; this.bytesRead = 0; - - if (this.start !== undefined) { - if (typeof this.start !== 'number') { - throw new errors.TypeError('ERR_INVALID_ARG_TYPE', - 'start', - 'number', - this.start); - } - if (this.end === undefined) { - this.end = Infinity; - } else if (typeof this.end !== 'number') { - throw new errors.TypeError('ERR_INVALID_ARG_TYPE', - 'end', - 'number', - this.end); + this.start = undefined; + this.end = undefined; + this.pos = undefined; + if (options === undefined || typeof options === 'string' || + options === null) { + options = { encoding: options }; + this.fd = null; + this.flags = O_RDONLY; + this.mode = 0o666; + this.autoClose = true; + } else { + assertOptions(options); + this.fd = options.fd === undefined ? null : options.fd; + this.flags = options.flags === undefined ? + O_RDONLY : stringToFlags(options.flags); + this.mode = options.mode === undefined ? + 0o666 : modeNum(options.mode, 0o666); + this.autoClose = options.autoClose === undefined ? true : options.autoClose; + + if (options.start !== undefined) { + if (typeof options.start !== 'number') { + throw new errors.TypeError('ERR_INVALID_ARG_TYPE', + 'start', + 'number', + options.start); + } + if (options.end === undefined) { + this.end = Infinity; + } else { + if (typeof options.end !== 'number') { + throw new errors.TypeError('ERR_INVALID_ARG_TYPE', + 'end', + 'number', + options.end); + } + this.end = options.end; + } + if (options.start > this.end) { + const errVal = `{start: ${options.start}, end: ${this.end}}`; + throw new errors.RangeError('ERR_VALUE_OUT_OF_RANGE', + 'start', + '<= "end"', + errVal); + } + this.pos = this.start = options.start; } + } + assertEncoding(options.encoding); - if (this.start > this.end) { - const errVal = `{start: ${this.start}, end: ${this.end}}`; - throw new errors.RangeError('ERR_VALUE_OUT_OF_RANGE', - 'start', - '<= "end"', - errVal); - } + Readable.call(this, options); - this.pos = this.start; - } + // A little bit higher water marks by default + if (options.highWaterMark === undefined) + this._readableState.highWaterMark = 64 * 1024; + + const req = new FSReqWrap(); + req.stream = this; + req.oncomplete = onRSRead; + this[ReqWrapSym] = req; + this[StreamPoolSym] = undefined; + this[StreamStartSym] = undefined; - if (typeof this.fd !== 'number') + if (typeof this.fd !== 'number') { + this.path = pathCheck(path); this.open(); + } else { + this.path = path; + } - this.on('end', function() { + this.on('end', () => { if (this.autoClose) { this.destroy(); } }); } -fs.FileReadStream = fs.ReadStream; // support the legacy name +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) { - if (self.autoClose) { - self.destroy(); - } - self.emit('error', er); - return; +function onRSOpen(er, fd) { + const stream = this.stream; + if (er) { + if (stream.autoClose) { + stream.destroy(); } + stream.emit('error', er); + return; + } - self.fd = fd; - self.emit('open', fd); - // start the flow of data. - self.read(); - }); -}; + this.oncomplete = onRSRead; + stream.fd = fd; + stream.emit('open', fd); + // Start the flow of data. + stream.read(); +} + +ReadStream.prototype.open = function() { + this[ReqWrapSym].oncomplete = onRSOpen; + open( + pathModule._makeLong(this.path), this.flags, this.mode, this[ReqWrapSym]); +}; + +function onRSRead(er, bytesRead) { + const stream = this.stream; + if (er) { + if (stream.autoClose) + stream.destroy(); + stream.emit('error', er); + } else if (bytesRead > 0) { + stream.bytesRead += bytesRead; + const start = stream[StreamStartSym]; + stream.push(stream[StreamPoolSym].slice(start, start + bytesRead)); + } else { + stream.push(null); + } +} ReadStream.prototype._read = function(n) { if (typeof this.fd !== 'number') { - return this.once('open', function() { + return this.once('open', () => { this._read(n); }); } @@ -2043,60 +2141,48 @@ ReadStream.prototype._read = function(n) { if (this.destroyed) return; - if (!pool || pool.length - pool.used < kMinPoolSpace) { - // discard the old pool. - allocNewPool(this._readableState.highWaterMark); + if (pool === undefined || pool.length - poolUsed < kMinPoolSpace) { + // Discard the old pool. + pool = Buffer.allocUnsafe(this._readableState.highWaterMark); + poolUsed = 0; } // 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. - var thisPool = pool; - var toRead = Math.min(pool.length - pool.used, n); - var start = pool.used; + this[StreamPoolSym] = pool; + this[StreamStartSym] = poolUsed; + var toRead = Math.min(pool.length - poolUsed, n); if (this.pos !== undefined) toRead = Math.min(this.end - this.pos + 1, toRead); - // already read everything we were supposed to read! + // Already read everything we were supposed to read! // treat as EOF. if (toRead <= 0) return this.push(null); - // the actual read. - var self = this; - fs.read(this.fd, pool, pool.used, toRead, this.pos, onread); + // The actual read. + read(this.fd, pool, poolUsed, toRead, this.pos, this[ReqWrapSym]); - // move the pool positions, and internal position for reading. + // Move the pool positions, and internal position for reading. if (this.pos !== undefined) this.pos += toRead; - pool.used += toRead; - - function onread(er, bytesRead) { - if (er) { - if (self.autoClose) { - self.destroy(); - } - self.emit('error', er); - } else { - var b = null; - if (bytesRead > 0) { - self.bytesRead += bytesRead; - b = thisPool.slice(start, start + bytesRead); - } - - self.push(b); - } - } + poolUsed += toRead; }; - ReadStream.prototype._destroy = function(err, cb) { - this.close(function(err2) { + this.close((err2) => { cb(err || err2); }); }; +function onRSClose(er) { + if (er) + this.stream.emit('error', er); + else + this.stream.emit('close'); +} ReadStream.prototype.close = function(cb) { if (cb) @@ -2112,169 +2198,190 @@ ReadStream.prototype.close = function(cb) { this.closed = true; - fs.close(this.fd, (er) => { - if (er) - this.emit('error', er); - else - this.emit('close'); - }); + this[ReqWrapSym].oncomplete = onRSClose; + close(this.fd, this[ReqWrapSym]); this.fd = null; }; -// needed because as it will be called with arguments -// that does not match this.close() signature +// Needed because it will be called with arguments +// that do not match the this.close() signature function closeOnOpen(fd) { this.close(); } fs.createWriteStream = function(path, options) { - return new WriteStream(path, options); + return new WriteStream(path, options, StreamStartSym); }; util.inherits(WriteStream, Writable); fs.WriteStream = WriteStream; -function WriteStream(path, options) { - if (!(this instanceof WriteStream)) - return new WriteStream(path, options); - - options = copyObject(getOptions(options, {})); - - Writable.call(this, options); - - handleError((this.path = getPathFromURL(path))); - this.fd = options.fd === undefined ? null : options.fd; - this.flags = options.flags === undefined ? 'w' : options.flags; - this.mode = options.mode === undefined ? 0o666 : options.mode; +function WriteStream(path, options, instanceSymbol) { + if (instanceSymbol !== StreamStartSym && !(this instanceof WriteStream)) + return new WriteStream(path, options, StreamStartSym); - this.start = options.start; - this.autoClose = options.autoClose === undefined ? true : !!options.autoClose; - this.pos = undefined; this.bytesWritten = 0; - - if (this.start !== undefined) { - if (typeof this.start !== 'number') { - throw new errors.TypeError('ERR_INVALID_ARG_TYPE', - 'start', - 'number', - this.start); - } - if (this.start < 0) { - const errVal = `{start: ${this.start}}`; - throw new errors.RangeError('ERR_VALUE_OUT_OF_RANGE', - 'start', - '>= 0', - errVal); + this.pos = undefined; + this.start = undefined; + var encoding; + if (options === undefined || typeof options === 'string' || + options === null) { + encoding = options; + options = undefined; + this.fd = null; + this.flags = writeFlags; + this.mode = 0o666; + this.autoClose = true; + } else { + assertOptions(options); + encoding = options.encoding; + this.fd = options.fd === undefined ? null : options.fd; + this.flags = options.flags === undefined ? + writeFlags : stringToFlags(options.flags); + this.mode = options.mode === undefined ? + 0o666 : modeNum(options.mode, 0o666); + this.autoClose = options.autoClose === undefined ? true : options.autoClose; + + if (options.start !== undefined) { + if (typeof options.start !== 'number') { + throw new errors.TypeError('ERR_INVALID_ARG_TYPE', + 'start', + 'number', + options.start); + } + if (options.start < 0) { + const errVal = `{start: ${options.start}}`; + throw new errors.RangeError('ERR_VALUE_OUT_OF_RANGE', + 'start', + '>= 0', + errVal); + } + this.pos = this.start = options.start; } - - this.pos = this.start; } + assertEncoding(encoding); + + Writable.call(this, options); - if (options.encoding) - this.setDefaultEncoding(options.encoding); + const req = new FSReqWrap(); + req.stream = this; + req.oncomplete = onWSWrite; + this[ReqWrapSym] = req; + this[ReqWrapEvSym] = undefined; + this[WriteStreamCbSym] = undefined; - if (typeof this.fd !== 'number') + if (encoding) + this.setDefaultEncoding(encoding); + + if (typeof this.fd !== 'number') { + this.path = pathCheck(path); this.open(); + } else { + this.path = path; + } - // dispose on finish. - this.once('finish', function() { + // Dispose on finish. + this.once('finish', () => { if (this.autoClose) { this.close(); } }); } -fs.FileWriteStream = fs.WriteStream; // support the legacy name - +fs.FileWriteStream = fs.WriteStream; // Support the legacy name -WriteStream.prototype.open = function() { - fs.open(this.path, this.flags, this.mode, function(er, fd) { - if (er) { - if (this.autoClose) { - this.destroy(); - } - this.emit('error', er); - return; +function onWSOpen(er, fd) { + const stream = this.stream; + if (er) { + if (stream.autoClose) { + stream.destroy(); } + stream.emit('error', er); + return; + } - this.fd = fd; - this.emit('open', fd); - }.bind(this)); + this.oncomplete = onWSWrite; + stream.fd = fd; + stream.emit('open', fd); +} + +WriteStream.prototype.open = function() { + this[ReqWrapSym].oncomplete = onWSOpen; + open( + pathModule._makeLong(this.path), this.flags, this.mode, this[ReqWrapSym]); }; +function onWSWrite(er, bytes) { + const stream = this.stream; + if (er) { + if (stream.autoClose) { + stream.destroy(); + } + return stream[WriteStreamCbSym](er); + } + stream.bytesWritten += bytes; + stream[WriteStreamCbSym](); +} WriteStream.prototype._write = function(data, encoding, cb) { if (!(data instanceof Buffer)) return this.emit('error', new Error('Invalid data')); if (typeof this.fd !== 'number') { - return this.once('open', function() { + return this.once('open', () => { this._write(data, encoding, cb); }); } - - var self = this; - fs.write(this.fd, data, 0, data.length, this.pos, function(er, bytes) { - if (er) { - if (self.autoClose) { - self.destroy(); - } - return cb(er); - } - self.bytesWritten += bytes; - cb(); - }); + this[WriteStreamCbSym] = cb; + writeBuffer(this.fd, data, 0, data.length, this.pos, + this[ReqWrapSym]); if (this.pos !== undefined) this.pos += data.length; }; - -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); +function onWSWritev(er, bytes) { + const stream = this.stream; + if (er) { + stream.destroy(); + return stream[WriteStreamCbSym](er); } - - const req = new FSReqWrap(); - req.oncomplete = wrapper; - binding.writeBuffers(fd, chunks, position, req); + stream.bytesWritten += bytes; + stream[WriteStreamCbSym](); } - WriteStream.prototype._writev = function(data, cb) { if (typeof this.fd !== 'number') { - return this.once('open', function() { + return this.once('open', () => { this._writev(data, cb); }); } - const self = this; - const len = data.length; - const chunks = new Array(len); + // Lazily initialize the second FSReqWrap + if (this[ReqWrapEvSym] === undefined) { + const req = new FSReqWrap(); + req.stream = this; + req.oncomplete = onWSWritev; + this[ReqWrapEvSym] = req; + } + + const chunks = new Array(data.length); var size = 0; - for (var i = 0; i < len; i++) { - var chunk = data[i].chunk; + for (var i = 0; i < data.length; i++) { + const 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(); - }); + this[WriteStreamCbSym] = cb; + writeBuffers(this.fd, chunks, this.pos, this[ReqWrapEvSym]); if (this.pos !== undefined) this.pos += size; }; - WriteStream.prototype._destroy = ReadStream.prototype._destroy; WriteStream.prototype.close = ReadStream.prototype.close; @@ -2283,7 +2390,7 @@ WriteStream.prototype.destroySoon = WriteStream.prototype.end; // SyncWriteStream is internal. DO NOT USE. // This undocumented API was never intended to be made public. -var SyncWriteStream = internalFS.SyncWriteStream; +var SyncWriteStream = InternalSyncWriteStream; Object.defineProperty(fs, 'SyncWriteStream', { configurable: true, get: internalUtil.deprecate(() => SyncWriteStream, diff --git a/lib/internal/fs.js b/lib/internal/fs.js index b8f710f04b32cd..3cf1a9a05131b7 100644 --- a/lib/internal/fs.js +++ b/lib/internal/fs.js @@ -1,6 +1,5 @@ 'use strict'; -const Buffer = require('buffer').Buffer; const Writable = require('stream').Writable; const errors = require('internal/errors'); const fs = require('fs'); @@ -17,12 +16,6 @@ const { O_WRONLY } = process.binding('constants').fs; -function assertEncoding(encoding) { - if (encoding && !Buffer.isEncoding(encoding)) { - throw new errors.TypeError('ERR_INVALID_OPT_VALUE_ENCODING', encoding); - } -} - function stringToFlags(flags) { if (typeof flags === 'number') { return flags; @@ -96,7 +89,6 @@ SyncWriteStream.prototype.destroy = function() { }; module.exports = { - assertEncoding, stringToFlags, SyncWriteStream, realpathCacheKey: Symbol('realpathCacheKey') diff --git a/lib/internal/url.js b/lib/internal/url.js index cf0271691a5cd0..925276182fcf29 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -1368,7 +1368,7 @@ function getPathFromURLPosix(url) { } function getPathFromURL(path) { - if (path == null || !path[searchParams] || + if (path === null || path === undefined || !path[searchParams] || !path[searchParams][searchParams]) { return path; } diff --git a/test/parallel/test-fs-link.js b/test/parallel/test-fs-link.js index 525392aa2be01c..8ce2f94c66eb24 100644 --- a/test/parallel/test-fs-link.js +++ b/test/parallel/test-fs-link.js @@ -6,7 +6,7 @@ const fs = require('fs'); common.refreshTmpDir(); -// test creating and reading hard link +// Test creating and reading hard link const srcPath = path.join(common.tmpDir, 'hardlink-target.txt'); const dstPath = path.join(common.tmpDir, 'link1.js'); fs.writeFileSync(srcPath, 'hello world'); @@ -19,18 +19,8 @@ function callback(err) { fs.link(srcPath, dstPath, common.mustCall(callback)); -// test error outputs - -assert.throws( - function() { - fs.link(); - }, - /src must be a string or Buffer/ -); - -assert.throws( - function() { - fs.link('abc'); - }, - /dest must be a string or Buffer/ -); +// Test error outputs +assert.throws(() => { fs.link(); }, /src must be a string or Buffer/); +assert.throws(() => { fs.link('abc'); }, /dest must be a string or Buffer/); +assert.throws(() => { fs.linkSync(); }, /src must be a string or Buffer/); +assert.throws(() => { fs.linkSync('abc'); }, /dest must be a string or Buffer/); diff --git a/test/parallel/test-fs-read-stream-err.js b/test/parallel/test-fs-read-stream-err.js deleted file mode 100644 index a9b4838cf1834f..00000000000000 --- a/test/parallel/test-fs-read-stream-err.js +++ /dev/null @@ -1,63 +0,0 @@ -// 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. - -'use strict'; -const common = require('../common'); -const assert = require('assert'); -const fs = require('fs'); - -const stream = fs.createReadStream(__filename, { - bufferSize: 64 -}); -const err = new Error('BAM'); - -stream.on('error', common.mustCall((err_) => { - process.nextTick(common.mustCall(() => { - assert.strictEqual(stream.fd, null); - assert.strictEqual(err_, err); - })); -})); - -fs.close = common.mustCall((fd_, cb) => { - assert.strictEqual(fd_, stream.fd); - process.nextTick(cb); -}); - -const read = fs.read; -fs.read = function() { - // first time is ok. - read.apply(fs, arguments); - // then it breaks - fs.read = common.mustCall(function() { - const cb = arguments[arguments.length - 1]; - process.nextTick(() => { - cb(err); - }); - // and should not be called again! - fs.read = () => { - throw new Error('BOOM!'); - }; - }); -}; - -stream.on('data', (buf) => { - stream.on('data', common.mustNotCall("no more 'data' events should follow")); -}); diff --git a/test/parallel/test-fs-sync-fd-leak.js b/test/parallel/test-fs-sync-fd-leak.js deleted file mode 100644 index 7e785ea3a2a82b..00000000000000 --- a/test/parallel/test-fs-sync-fd-leak.js +++ /dev/null @@ -1,70 +0,0 @@ -// 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. - -'use strict'; -require('../common'); -const assert = require('assert'); -const fs = require('fs'); - -// ensure that (read|write|append)FileSync() closes the file descriptor -fs.openSync = function() { - return 42; -}; -fs.closeSync = function(fd) { - assert.strictEqual(fd, 42); - close_called++; -}; -fs.readSync = function() { - throw new Error('BAM'); -}; -fs.writeSync = function() { - throw new Error('BAM'); -}; - -process.binding('fs').fstat = function() { - throw new Error('BAM'); -}; - -let close_called = 0; -ensureThrows(function() { - fs.readFileSync('dummy'); -}); -ensureThrows(function() { - fs.writeFileSync('dummy', 'xxx'); -}); -ensureThrows(function() { - fs.appendFileSync('dummy', 'xxx'); -}); - -function ensureThrows(cb) { - let got_exception = false; - - close_called = 0; - try { - cb(); - } catch (e) { - assert.strictEqual(e.message, 'BAM'); - got_exception = true; - } - - assert.strictEqual(close_called, 1); - assert.strictEqual(got_exception, true); -} diff --git a/test/parallel/test-fs-write-stream-change-open.js b/test/parallel/test-fs-write-stream-change-open.js deleted file mode 100644 index 50860f2e405f18..00000000000000 --- a/test/parallel/test-fs-write-stream-change-open.js +++ /dev/null @@ -1,54 +0,0 @@ -// 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. - -'use strict'; -const common = require('../common'); -const assert = require('assert'); -const path = require('path'); -const fs = require('fs'); - -const file = path.join(common.tmpDir, 'write.txt'); - -common.refreshTmpDir(); - -const stream = fs.WriteStream(file); -const _fs_close = fs.close; -const _fs_open = fs.open; - -// change the fs.open with an identical function after the WriteStream -// has pushed it onto its internal action queue, but before it's -// returned. This simulates AOP-style extension of the fs lib. -fs.open = function() { - return _fs_open.apply(fs, arguments); -}; - -fs.close = function(fd) { - assert.ok(fd, 'fs.close must not be called with an undefined fd.'); - fs.close = _fs_close; - fs.open = _fs_open; -}; - -stream.write('foo'); -stream.end(); - -process.on('exit', function() { - assert.strictEqual(fs.open, _fs_open); -}); diff --git a/test/parallel/test-fs-write-stream-err.js b/test/parallel/test-fs-write-stream-err.js deleted file mode 100644 index 077bfb24b75cff..00000000000000 --- a/test/parallel/test-fs-write-stream-err.js +++ /dev/null @@ -1,74 +0,0 @@ -// 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. - -'use strict'; -const common = require('../common'); -const assert = require('assert'); -const fs = require('fs'); - -common.refreshTmpDir(); - -const stream = fs.createWriteStream(`${common.tmpDir}/out`, { - highWaterMark: 10 -}); -const err = new Error('BAM'); - -const write = fs.write; -let writeCalls = 0; -fs.write = function() { - switch (writeCalls++) { - case 0: - console.error('first write'); - // first time is ok. - return write.apply(fs, arguments); - case 1: - // then it breaks - console.error('second write'); - const cb = arguments[arguments.length - 1]; - return process.nextTick(function() { - cb(err); - }); - default: - // and should not be called again! - throw new Error('BOOM!'); - } -}; - -fs.close = common.mustCall(function(fd_, cb) { - console.error('fs.close', fd_, stream.fd); - assert.strictEqual(fd_, stream.fd); - process.nextTick(cb); -}); - -stream.on('error', common.mustCall(function(err_) { - console.error('error handler'); - assert.strictEqual(stream.fd, null); - assert.strictEqual(err_, err); -})); - - -stream.write(Buffer.allocUnsafe(256), function() { - console.error('first cb'); - stream.write(Buffer.allocUnsafe(256), common.mustCall(function(err_) { - console.error('second cb'); - assert.strictEqual(err_, err); - })); -}); diff --git a/test/parallel/test-internal-fs.js b/test/parallel/test-internal-fs.js deleted file mode 100644 index fe6c50194d1c72..00000000000000 --- a/test/parallel/test-internal-fs.js +++ /dev/null @@ -1,13 +0,0 @@ -// Flags: --expose-internals -'use strict'; - -const common = require('../common'); -const assert = require('assert'); -const fs = require('internal/fs'); - -assert.doesNotThrow(() => fs.assertEncoding()); -assert.doesNotThrow(() => fs.assertEncoding('utf8')); -common.expectsError( - () => fs.assertEncoding('foo'), - { code: 'ERR_INVALID_OPT_VALUE_ENCODING', type: TypeError } -);