Skip to content

Commit

Permalink
fs: move utility functions to internal/fs
Browse files Browse the repository at this point in the history
PR-URL: #18777
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
  • Loading branch information
targos committed Feb 17, 2018
1 parent 6c9774f commit 2620358
Show file tree
Hide file tree
Showing 2 changed files with 340 additions and 294 deletions.
309 changes: 18 additions & 291 deletions lib/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,29 @@ const { Buffer } = require('buffer');
const errors = require('internal/errors');
const { Readable, Writable } = require('stream');
const EventEmitter = require('events');
const { FSReqWrap } = binding;
const { FSReqWrap, statValues } = binding;
const { FSEvent } = process.binding('fs_event_wrap');
const internalFS = require('internal/fs');
const { getPathFromURL } = require('internal/url');
const internalUtil = require('internal/util');
const {
assertEncoding,
stringToFlags
copyObject,
getOptions,
isUint32,
modeNum,
nullCheck,
preprocessSymlinkDestination,
Stats,
statsFromValues,
stringToFlags,
stringToSymlinkType,
toUnixTimestamp,
validateBuffer,
validateLen,
validateOffsetLengthRead,
validateOffsetLengthWrite,
validatePath,
validateUint32
} = internalFS;
const {
CHAR_FORWARD_SLASH,
Expand All @@ -70,9 +85,6 @@ const errnoException = errors.errnoException;

let truncateWarn = true;

function isInt32(n) { return n === (n | 0); }
function isUint32(n) { return n === (n >>> 0); }

function showTruncateDeprecation() {
if (truncateWarn) {
process.emitWarning(
Expand All @@ -83,35 +95,6 @@ function showTruncateDeprecation() {
}
}

function getOptions(options, defaultOptions) {
if (options === null || options === undefined ||
typeof options === 'function') {
return defaultOptions;
}

if (typeof options === 'string') {
defaultOptions = util._extend({}, defaultOptions);
defaultOptions.encoding = options;
options = defaultOptions;
} else if (typeof options !== 'object') {
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 handleErrorFromBinding(ctx) {
if (ctx.errno !== undefined) { // libuv error numbers
const err = errors.uvException(ctx);
Expand Down Expand Up @@ -175,117 +158,6 @@ function makeCallback(cb) {
};
}

function validateBuffer(buffer) {
if (!isUint8Array(buffer)) {
const err = new errors.TypeError('ERR_INVALID_ARG_TYPE', 'buffer',
['Buffer', 'Uint8Array']);
Error.captureStackTrace(err, validateBuffer);
throw err;
}
}

function validateLen(len) {
let err;

if (!isInt32(len))
err = new errors.TypeError('ERR_INVALID_ARG_TYPE', 'len', 'integer');

if (err !== undefined) {
Error.captureStackTrace(err, validateLen);
throw err;
}
}

function validateOffsetLengthRead(offset, length, bufferLength) {
let err;

if (offset < 0 || offset >= bufferLength) {
err = new errors.RangeError('ERR_OUT_OF_RANGE', 'offset');
} else if (length < 0 || offset + length > bufferLength) {
err = new errors.RangeError('ERR_OUT_OF_RANGE', 'length');
}

if (err !== undefined) {
Error.captureStackTrace(err, validateOffsetLengthRead);
throw err;
}
}

function validateOffsetLengthWrite(offset, length, byteLength) {
let err;

if (offset > byteLength) {
err = new errors.RangeError('ERR_OUT_OF_RANGE', 'offset');
} else if (offset + length > byteLength || offset + length > kMaxLength) {
err = new errors.RangeError('ERR_OUT_OF_RANGE', 'length');
}

if (err !== undefined) {
Error.captureStackTrace(err, validateOffsetLengthWrite);
throw err;
}
}

// Check if the path contains null types if it is a string nor Uint8Array,
// otherwise return silently.
function nullCheck(path, propName, throwError = true) {
const pathIsString = typeof path === 'string';
const pathIsUint8Array = isUint8Array(path);

// We can only perform meaningful checks on strings and Uint8Arrays.
if (!pathIsString && !pathIsUint8Array) {
return;
}

if (pathIsString && path.indexOf('\u0000') === -1) {
return;
} else if (pathIsUint8Array && path.indexOf(0) === -1) {
return;
}

const err = new errors.Error(
'ERR_INVALID_ARG_VALUE', propName, path,
'must be a string or Uint8Array without null bytes');

if (throwError) {
Error.captureStackTrace(err, nullCheck);
throw err;
}
return err;
}

function validatePath(path, propName) {
let err;

if (propName === undefined) {
propName = 'path';
}

if (typeof path !== 'string' && !isUint8Array(path)) {
err = new errors.TypeError('ERR_INVALID_ARG_TYPE', propName,
['string', 'Buffer', 'URL']);
} else {
err = nullCheck(path, propName, false);
}

if (err !== undefined) {
Error.captureStackTrace(err, validatePath);
throw err;
}
}

function validateUint32(value, propName) {
let err;

if (!isUint32(value))
err = new errors.TypeError('ERR_INVALID_ARG_TYPE', propName, 'integer');

if (err !== undefined) {
Error.captureStackTrace(err, validateUint32);
throw err;
}
}

// Special case of `makeCallback()` that is specific to async `*stat()` calls as
// an optimization, since the data passed back to the callback needs to be
// transformed anyway.
Expand All @@ -308,85 +180,8 @@ function isFd(path) {
return (path >>> 0) === path;
}

// Constructor for file stats.
function Stats(
dev,
mode,
nlink,
uid,
gid,
rdev,
blksize,
ino,
size,
blocks,
atim_msec,
mtim_msec,
ctim_msec,
birthtim_msec
) {
this.dev = dev;
this.mode = mode;
this.nlink = nlink;
this.uid = uid;
this.gid = gid;
this.rdev = rdev;
this.blksize = blksize;
this.ino = ino;
this.size = size;
this.blocks = blocks;
this.atimeMs = atim_msec;
this.mtimeMs = mtim_msec;
this.ctimeMs = ctim_msec;
this.birthtimeMs = birthtim_msec;
this.atime = new Date(atim_msec + 0.5);
this.mtime = new Date(mtim_msec + 0.5);
this.ctime = new Date(ctim_msec + 0.5);
this.birthtime = new Date(birthtim_msec + 0.5);
}
fs.Stats = Stats;

Stats.prototype._checkModeProperty = function(property) {
return ((this.mode & S_IFMT) === property);
};

Stats.prototype.isDirectory = function() {
return this._checkModeProperty(constants.S_IFDIR);
};

Stats.prototype.isFile = function() {
return this._checkModeProperty(S_IFREG);
};

Stats.prototype.isBlockDevice = function() {
return this._checkModeProperty(constants.S_IFBLK);
};

Stats.prototype.isCharacterDevice = function() {
return this._checkModeProperty(constants.S_IFCHR);
};

Stats.prototype.isSymbolicLink = function() {
return this._checkModeProperty(S_IFLNK);
};

Stats.prototype.isFIFO = function() {
return this._checkModeProperty(S_IFIFO);
};

Stats.prototype.isSocket = function() {
return this._checkModeProperty(S_IFSOCK);
};

const statValues = binding.statValues;

function statsFromValues(stats = statValues) {
return new Stats(stats[0], stats[1], stats[2], stats[3], stats[4], stats[5],
stats[6] < 0 ? undefined : stats[6], stats[7], stats[8],
stats[9] < 0 ? undefined : stats[9], stats[10], stats[11],
stats[12], stats[13]);
}

// Don't allow mode to accidentally be overwritten.
Object.defineProperties(fs, {
F_OK: { enumerable: true, value: constants.F_OK || 0 },
Expand Down Expand Up @@ -760,16 +555,6 @@ fs.closeSync = function(fd) {
handleErrorFromBinding(ctx);
};

function modeNum(m, def) {
if (typeof m === 'number')
return m;
if (typeof m === 'string')
return parseInt(m, 8);
if (def)
return modeNum(def);
return undefined;
}

fs.open = function(path, flags, mode, callback_) {
var callback = makeCallback(arguments[arguments.length - 1]);
mode = modeNum(mode, 0o666);
Expand Down Expand Up @@ -1162,42 +947,6 @@ fs.readlinkSync = function(path, options) {
return result;
};

function preprocessSymlinkDestination(path, type, linkPath) {
if (!isWindows) {
// No preprocessing is needed on Unix.
return path;
} else 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.toNamespacedPath(path);
} else {
// Windows symlinks don't tolerate forward slashes.
return ('' + path).replace(/\//g, '\\');
}
}

function stringToSymlinkType(type) {
let flags = 0;
if (typeof type === 'string') {
switch (type) {
case 'dir':
flags |= constants.UV_FS_SYMLINK_DIR;
break;
case 'junction':
flags |= constants.UV_FS_SYMLINK_JUNCTION;
break;
case 'file':
break;
default:
const err = new errors.Error('ERR_FS_INVALID_SYMLINK_TYPE', type);
Error.captureStackTrace(err, stringToSymlinkType);
throw err;
}
}
return flags;
}

fs.symlink = function(target, path, type_, callback_) {
var type = (typeof type_ === 'string' ? type_ : null);
var callback = makeCallback(arguments[arguments.length - 1]);
Expand Down Expand Up @@ -1421,28 +1170,6 @@ fs.chownSync = function(path, uid, gid) {
return binding.chown(pathModule.toNamespacedPath(path), uid, gid);
};

// converts Date or number to a fractional UNIX timestamp
function toUnixTimestamp(time, name = 'time') {
// eslint-disable-next-line eqeqeq
if (typeof time === 'string' && +time == time) {
return +time;
}
if (Number.isFinite(time)) {
if (time < 0) {
return Date.now() / 1000;
}
return time;
}
if (util.isDate(time)) {
// convert to 123.456 UNIX timestamp
return time.getTime() / 1000;
}
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
name,
['Date', 'Time in seconds'],
time);
}

// exported for unit tests, not for public consumption
fs._toUnixTimestamp = toUnixTimestamp;

Expand Down
Loading

0 comments on commit 2620358

Please sign in to comment.