Support file:// URLs and Buffers for path args, throw when invalid
This commit is contained in:
parent
9c13a2d248
commit
85a8c21dc1
17
README.md
17
README.md
|
@ -232,7 +232,7 @@ The node.js [path module](http://nodejs.org/api/path.html) is available via the
|
||||||
identical to the node.js (see [https://github.com/browserify/path-browserify](https://github.com/browserify/path-browserify)) version with the following differences:
|
identical to the node.js (see [https://github.com/browserify/path-browserify](https://github.com/browserify/path-browserify)) version with the following differences:
|
||||||
|
|
||||||
* The CWD always defaults to `/`
|
* The CWD always defaults to `/`
|
||||||
* No support for Windows style paths
|
* No support for Windows style paths (assume you are on a POSIX system)
|
||||||
* Additional utility methods (see below)
|
* Additional utility methods (see below)
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
@ -267,6 +267,21 @@ Filer.Path also includes the following extra methods:
|
||||||
* `addTrailing(p)` returns the path `p` with a single trailing slash added
|
* `addTrailing(p)` returns the path `p` with a single trailing slash added
|
||||||
* `removeTrailing(p)` returns the path `p` with trailing slash(es) removed
|
* `removeTrailing(p)` returns the path `p` with trailing slash(es) removed
|
||||||
|
|
||||||
|
[As with node.js](https://nodejs.org/api/fs.html#fs_file_paths), all methods below that
|
||||||
|
accept a `path` argument as a `String` can also take a [`file://` URL](https://nodejs.org/api/fs.html#fs_url_object_support)
|
||||||
|
or a `Buffer`. For example, all of the following cases will work the same way with Filer:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// 1. path as a String
|
||||||
|
fs.writeFile('/dir/file.txt', 'data', function(err) {...});
|
||||||
|
|
||||||
|
// 2. path as a URL
|
||||||
|
fs.writeFile(new URL('file:///dir/file.txt'), 'data', function(err) {...});
|
||||||
|
|
||||||
|
// 3. path as a Buffer
|
||||||
|
fs.writeFile(Buffer.from('/dir/file.txt'), 'data', function(err) {...});
|
||||||
|
```
|
||||||
|
|
||||||
#### Filer.Errors<a name="Errors"></a>
|
#### Filer.Errors<a name="Errors"></a>
|
||||||
|
|
||||||
The error objects used internally by Filer are also exposed via the `Filer.Errors` object. As much as possible
|
The error objects used internally by Filer are also exposed via the `Filer.Errors` object. As much as possible
|
||||||
|
|
|
@ -3,7 +3,6 @@ var normalize = Path.normalize;
|
||||||
var dirname = Path.dirname;
|
var dirname = Path.dirname;
|
||||||
var basename = Path.basename;
|
var basename = Path.basename;
|
||||||
var isAbsolutePath = Path.isAbsolute;
|
var isAbsolutePath = Path.isAbsolute;
|
||||||
var isNullPath = Path.isNull;
|
|
||||||
var shared = require('../shared.js');
|
var shared = require('../shared.js');
|
||||||
|
|
||||||
var Constants = require('../constants.js');
|
var Constants = require('../constants.js');
|
||||||
|
@ -1627,30 +1626,6 @@ function validate_file_options(options, enc, fileMode){
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
function pathCheck(path, allowRelative, callback) {
|
|
||||||
var err;
|
|
||||||
|
|
||||||
if(typeof allowRelative === 'function') {
|
|
||||||
callback = allowRelative;
|
|
||||||
allowRelative = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!path) {
|
|
||||||
err = new Errors.EINVAL('Path must be a string', path);
|
|
||||||
} else if(isNullPath(path)) {
|
|
||||||
err = new Errors.EINVAL('Path must be a string without null bytes.', path);
|
|
||||||
} else if(!allowRelative && !isAbsolutePath(path)) {
|
|
||||||
err = new Errors.EINVAL('Path must be absolute.', path);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(err) {
|
|
||||||
callback(err);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function open(context, path, flags, mode, callback) {
|
function open(context, path, flags, mode, callback) {
|
||||||
if (arguments.length < 5 ){
|
if (arguments.length < 5 ){
|
||||||
callback = arguments[arguments.length - 1];
|
callback = arguments[arguments.length - 1];
|
||||||
|
@ -1659,8 +1634,6 @@ function open(context, path, flags, mode, callback) {
|
||||||
else {
|
else {
|
||||||
mode = validateAndMaskMode(mode, FULL_READ_WRITE_EXEC_PERMISSIONS, callback);
|
mode = validateAndMaskMode(mode, FULL_READ_WRITE_EXEC_PERMISSIONS, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!pathCheck(path, callback)) return;
|
|
||||||
|
|
||||||
function check_result(error, fileNode) {
|
function check_result(error, fileNode) {
|
||||||
if(error) {
|
if(error) {
|
||||||
|
@ -1696,7 +1669,6 @@ function close(context, fd, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function mknod(context, path, type, callback) {
|
function mknod(context, path, type, callback) {
|
||||||
if(!pathCheck(path, callback)) return;
|
|
||||||
make_node(context, path, type, callback);
|
make_node(context, path, type, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1709,7 +1681,6 @@ function mkdir(context, path, mode, callback) {
|
||||||
if(!mode) return;
|
if(!mode) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!pathCheck(path, callback)) return;
|
|
||||||
make_directory(context, path, callback);
|
make_directory(context, path, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1719,7 +1690,6 @@ function access(context, path, mode, callback) {
|
||||||
mode = Constants.fsConstants.F_OK;
|
mode = Constants.fsConstants.F_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pathCheck(path, callback)) return;
|
|
||||||
mode = mode | Constants.fsConstants.F_OK;
|
mode = mode | Constants.fsConstants.F_OK;
|
||||||
access_file(context, path, mode, callback);
|
access_file(context, path, mode, callback);
|
||||||
}
|
}
|
||||||
|
@ -1733,20 +1703,16 @@ function mkdtemp(context, prefix, options, callback) {
|
||||||
let random = shared.randomChars(6);
|
let random = shared.randomChars(6);
|
||||||
var path = prefix + '-' + random;
|
var path = prefix + '-' + random;
|
||||||
|
|
||||||
if(!pathCheck(path, callback)) return;
|
|
||||||
make_directory(context, path, function(error) {
|
make_directory(context, path, function(error) {
|
||||||
callback(error, path);
|
callback(error, path);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function rmdir(context, path, callback) {
|
function rmdir(context, path, callback) {
|
||||||
if(!pathCheck(path, callback)) return;
|
|
||||||
remove_directory(context, path, callback);
|
remove_directory(context, path, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
function stat(context, path, callback) {
|
function stat(context, path, callback) {
|
||||||
if(!pathCheck(path, callback)) return;
|
|
||||||
|
|
||||||
function check_result(error, result) {
|
function check_result(error, result) {
|
||||||
if(error) {
|
if(error) {
|
||||||
callback(error);
|
callback(error);
|
||||||
|
@ -1778,13 +1744,10 @@ function fstat(context, fd, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function link(context, oldpath, newpath, callback) {
|
function link(context, oldpath, newpath, callback) {
|
||||||
if(!pathCheck(oldpath, callback)) return;
|
|
||||||
if(!pathCheck(newpath, callback)) return;
|
|
||||||
link_node(context, oldpath, newpath, callback);
|
link_node(context, oldpath, newpath, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
function unlink(context, path, callback) {
|
function unlink(context, path, callback) {
|
||||||
if(!pathCheck(path, callback)) return;
|
|
||||||
unlink_node(context, path, callback);
|
unlink_node(context, path, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1823,8 +1786,6 @@ function readFile(context, path, options, callback) {
|
||||||
callback = arguments[arguments.length - 1];
|
callback = arguments[arguments.length - 1];
|
||||||
options = validate_file_options(options, null, 'r');
|
options = validate_file_options(options, null, 'r');
|
||||||
|
|
||||||
if(!pathCheck(path, callback)) return;
|
|
||||||
|
|
||||||
var flags = validate_flags(options.flag || 'r');
|
var flags = validate_flags(options.flag || 'r');
|
||||||
if(!flags) {
|
if(!flags) {
|
||||||
return callback(new Errors.EINVAL('flags is not valid', path));
|
return callback(new Errors.EINVAL('flags is not valid', path));
|
||||||
|
@ -1897,8 +1858,6 @@ function writeFile(context, path, data, options, callback) {
|
||||||
callback = arguments[arguments.length - 1];
|
callback = arguments[arguments.length - 1];
|
||||||
options = validate_file_options(options, 'utf8', 'w');
|
options = validate_file_options(options, 'utf8', 'w');
|
||||||
|
|
||||||
if(!pathCheck(path, callback)) return;
|
|
||||||
|
|
||||||
var flags = validate_flags(options.flag || 'w');
|
var flags = validate_flags(options.flag || 'w');
|
||||||
if(!flags) {
|
if(!flags) {
|
||||||
return callback(new Errors.EINVAL('flags is not valid', path));
|
return callback(new Errors.EINVAL('flags is not valid', path));
|
||||||
|
@ -1934,8 +1893,6 @@ function appendFile(context, path, data, options, callback) {
|
||||||
callback = arguments[arguments.length - 1];
|
callback = arguments[arguments.length - 1];
|
||||||
options = validate_file_options(options, 'utf8', 'a');
|
options = validate_file_options(options, 'utf8', 'a');
|
||||||
|
|
||||||
if(!pathCheck(path, callback)) return;
|
|
||||||
|
|
||||||
var flags = validate_flags(options.flag || 'a');
|
var flags = validate_flags(options.flag || 'a');
|
||||||
if(!flags) {
|
if(!flags) {
|
||||||
return callback(new Errors.EINVAL('flags is not valid', path));
|
return callback(new Errors.EINVAL('flags is not valid', path));
|
||||||
|
@ -2098,7 +2055,6 @@ function fchown_file(context, ofd, uid, gid, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getxattr(context, path, name, callback) {
|
function getxattr(context, path, name, callback) {
|
||||||
if (!pathCheck(path, callback)) return;
|
|
||||||
getxattr_file(context, path, name, callback);
|
getxattr_file(context, path, name, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2118,7 +2074,6 @@ function setxattr(context, path, name, value, flag, callback) {
|
||||||
flag = null;
|
flag = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pathCheck(path, callback)) return;
|
|
||||||
setxattr_file(context, path, name, value, flag, callback);
|
setxattr_file(context, path, name, value, flag, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2141,7 +2096,6 @@ function fsetxattr(context, fd, name, value, flag, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function removexattr(context, path, name, callback) {
|
function removexattr(context, path, name, callback) {
|
||||||
if (!pathCheck(path, callback)) return;
|
|
||||||
removexattr_file(context, path, name, callback);
|
removexattr_file(context, path, name, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2199,7 +2153,6 @@ function lseek(context, fd, offset, whence, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function readdir(context, path, callback) {
|
function readdir(context, path, callback) {
|
||||||
if(!pathCheck(path, callback)) return;
|
|
||||||
read_directory(context, path, callback);
|
read_directory(context, path, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2213,8 +2166,6 @@ function toUnixTimestamp(time) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function utimes(context, path, atime, mtime, callback) {
|
function utimes(context, path, atime, mtime, callback) {
|
||||||
if(!pathCheck(path, callback)) return;
|
|
||||||
|
|
||||||
var currentTime = Date.now();
|
var currentTime = Date.now();
|
||||||
atime = (atime) ? toUnixTimestamp(atime) : toUnixTimestamp(currentTime);
|
atime = (atime) ? toUnixTimestamp(atime) : toUnixTimestamp(currentTime);
|
||||||
mtime = (mtime) ? toUnixTimestamp(mtime) : toUnixTimestamp(currentTime);
|
mtime = (mtime) ? toUnixTimestamp(mtime) : toUnixTimestamp(currentTime);
|
||||||
|
@ -2238,7 +2189,6 @@ function futimes(context, fd, atime, mtime, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function chmod(context, path, mode, callback) {
|
function chmod(context, path, mode, callback) {
|
||||||
if(!pathCheck(path, callback)) return;
|
|
||||||
mode = validateAndMaskMode(mode, callback);
|
mode = validateAndMaskMode(mode, callback);
|
||||||
if(!mode) return;
|
if(!mode) return;
|
||||||
|
|
||||||
|
@ -2260,7 +2210,6 @@ function fchmod(context, fd, mode, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function chown(context, path, uid, gid, callback) {
|
function chown(context, path, uid, gid, callback) {
|
||||||
if(!pathCheck(path, callback)) return;
|
|
||||||
if(!isUint32(uid)) {
|
if(!isUint32(uid)) {
|
||||||
return callback(new Errors.EINVAL('uid must be a valid integer', uid));
|
return callback(new Errors.EINVAL('uid must be a valid integer', uid));
|
||||||
}
|
}
|
||||||
|
@ -2290,9 +2239,6 @@ function fchown(context, fd, uid, gid, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function rename(context, oldpath, newpath, callback) {
|
function rename(context, oldpath, newpath, callback) {
|
||||||
if(!pathCheck(oldpath, callback)) return;
|
|
||||||
if(!pathCheck(newpath, callback)) return;
|
|
||||||
|
|
||||||
oldpath = normalize(oldpath);
|
oldpath = normalize(oldpath);
|
||||||
newpath = normalize(newpath);
|
newpath = normalize(newpath);
|
||||||
|
|
||||||
|
@ -2407,23 +2353,14 @@ function rename(context, oldpath, newpath, callback) {
|
||||||
function symlink(context, srcpath, dstpath, type, callback) {
|
function symlink(context, srcpath, dstpath, type, callback) {
|
||||||
// NOTE: we support passing the `type` arg, but ignore it.
|
// NOTE: we support passing the `type` arg, but ignore it.
|
||||||
callback = arguments[arguments.length - 1];
|
callback = arguments[arguments.length - 1];
|
||||||
|
|
||||||
// Special Case: allow srcpath to be relative, which we normally don't permit.
|
|
||||||
// If the srcpath is relative, we assume it's relative to the dirpath of dstpath.
|
|
||||||
if(!pathCheck(srcpath, true, callback)) return;
|
|
||||||
if(!pathCheck(dstpath, callback)) return;
|
|
||||||
|
|
||||||
make_symbolic_link(context, srcpath, dstpath, callback);
|
make_symbolic_link(context, srcpath, dstpath, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
function readlink(context, path, callback) {
|
function readlink(context, path, callback) {
|
||||||
if(!pathCheck(path, callback)) return;
|
|
||||||
read_link(context, path, callback);
|
read_link(context, path, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
function lstat(context, path, callback) {
|
function lstat(context, path, callback) {
|
||||||
if(!pathCheck(path, callback)) return;
|
|
||||||
|
|
||||||
function check_result(error, result) {
|
function check_result(error, result) {
|
||||||
if(error) {
|
if(error) {
|
||||||
callback(error);
|
callback(error);
|
||||||
|
@ -2441,7 +2378,6 @@ function truncate(context, path, length, callback) {
|
||||||
callback = arguments[arguments.length - 1];
|
callback = arguments[arguments.length - 1];
|
||||||
length = length || 0;
|
length = length || 0;
|
||||||
|
|
||||||
if(!pathCheck(path, callback)) return;
|
|
||||||
if(validateInteger(length, callback) !== length) return;
|
if(validateInteger(length, callback) !== length) return;
|
||||||
|
|
||||||
truncate_file(context, path, length, callback);
|
truncate_file(context, path, length, callback);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
var { promisify } = require('es6-promisify');
|
var { promisify } = require('es6-promisify');
|
||||||
|
|
||||||
var isNullPath = require('../path.js').isNull;
|
var Path = require('../path.js');
|
||||||
var nop = require('../shared.js').nop;
|
var nop = require('../shared.js').nop;
|
||||||
|
|
||||||
var Constants = require('../constants.js');
|
var Constants = require('../constants.js');
|
||||||
|
@ -45,6 +45,61 @@ function defaultCallback(err) {
|
||||||
console.error('Filer error: ', err);
|
console.error('Filer error: ', err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Get a path (String) from a file:// URL. Support URL() like objects
|
||||||
|
// https://github.com/nodejs/node/blob/968e901aff38a343b1de4addebf79fd8fa991c59/lib/internal/url.js#L1381
|
||||||
|
function toPathIfFileURL(fileURLOrPath) {
|
||||||
|
if(!(fileURLOrPath &&
|
||||||
|
fileURLOrPath.protocol &&
|
||||||
|
fileURLOrPath.pathname)) {
|
||||||
|
return fileURLOrPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fileURLOrPath.protocol !== 'file:') {
|
||||||
|
throw new Errors.EINVAL('only file: URLs are supported for paths', fileURLOrPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
var pathname = fileURLOrPath.pathname;
|
||||||
|
for (var n = 0; n < pathname.length; n++) {
|
||||||
|
if (pathname[n] === '%') {
|
||||||
|
var third = pathname.codePointAt(n + 2) | 0x20;
|
||||||
|
if (pathname[n + 1] === '2' && third === 102) {
|
||||||
|
throw new Errors.EINVAL('file: URLs must not include encoded / characters', fileURLOrPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return decodeURIComponent(pathname);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow Buffers for paths. Assumes we want UTF8.
|
||||||
|
function toPathIfBuffer(bufferOrPath) {
|
||||||
|
return Buffer.isBuffer(bufferOrPath) ? bufferOrPath.toString() : bufferOrPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
function validatePath(path, allowRelative) {
|
||||||
|
if(!path) {
|
||||||
|
return new Errors.EINVAL('Path must be a string', path);
|
||||||
|
} else if(Path.isNull(path)) {
|
||||||
|
return new Errors.EINVAL('Path must be a string without null bytes.', path);
|
||||||
|
} else if(!allowRelative && !Path.isAbsolute(path)) {
|
||||||
|
return new Errors.EINVAL('Path must be absolute.', path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function processPathArg(args, idx, allowRelative) {
|
||||||
|
var path = args[idx];
|
||||||
|
path = toPathIfFileURL(path);
|
||||||
|
path = toPathIfBuffer(path);
|
||||||
|
|
||||||
|
// Some methods specifically allow for rel paths (eg symlink with srcPath)
|
||||||
|
var err = validatePath(path, allowRelative);
|
||||||
|
if(err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overwrite path arg with converted and validated path
|
||||||
|
args[idx] = path;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FileSystem
|
* FileSystem
|
||||||
|
@ -124,7 +179,7 @@ function FileSystem(options, callback) {
|
||||||
|
|
||||||
// We support the optional `options` arg from node, but ignore it
|
// We support the optional `options` arg from node, but ignore it
|
||||||
this.watch = function(filename, options, listener) {
|
this.watch = function(filename, options, listener) {
|
||||||
if(isNullPath(filename)) {
|
if(Path.isNull(filename)) {
|
||||||
throw new Error('Path must be a string without null bytes.');
|
throw new Error('Path must be a string without null bytes.');
|
||||||
}
|
}
|
||||||
if(typeof options === 'function') {
|
if(typeof options === 'function') {
|
||||||
|
@ -247,50 +302,53 @@ function FileSystem(options, callback) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
FileSystem.prototype.promises = {};
|
FileSystem.prototype.promises = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Public API for FileSystem. All node.js methods that are
|
* Public API for FileSystem. All node.js methods that are exposed on fs.promises
|
||||||
* exposed on fs.promises include `promise: true`. We also
|
* include `promise: true`. We also include our own extra methods, but skip the
|
||||||
* include our own extra methods, but skip the fd versions
|
* fd versions to match node.js, which puts these on a `FileHandle` object.
|
||||||
* to match node.js, which puts these on a FileHandle object.
|
* Any method that deals with path argument(s) also includes the position of
|
||||||
|
* those args in one of `absPathArgs: [...]` or `relPathArgs: [...]`, so they
|
||||||
|
* can be processed and validated before being passed on to the method.
|
||||||
*/
|
*/
|
||||||
[
|
[
|
||||||
{ name: 'open', promises: true },
|
{ name: 'open', promises: true, absPathArgs: [ 0 ] },
|
||||||
{ name: 'access', promises: true },
|
{ name: 'access', promises: true, absPathArgs: [ 0 ] },
|
||||||
{ name: 'chmod', promises: true },
|
{ name: 'chmod', promises: true, absPathArgs: [ 0 ] },
|
||||||
{ name: 'fchmod' },
|
{ name: 'fchmod' },
|
||||||
{ name: 'chown', promises: true },
|
{ name: 'chown', promises: true, absPathArgs: [ 0 ] },
|
||||||
{ name: 'fchown' },
|
{ name: 'fchown' },
|
||||||
{ name: 'close' },
|
{ name: 'close' },
|
||||||
{ name: 'mknod', promises: true },
|
{ name: 'mknod', promises: true, absPathArgs: [ 0 ] },
|
||||||
{ name: 'mkdir', promises: true },
|
{ name: 'mkdir', promises: true, absPathArgs: [ 0 ] },
|
||||||
{ name: 'mkdtemp', promises: true },
|
{ name: 'mkdtemp', promises: true },
|
||||||
{ name: 'rmdir', promises: true },
|
{ name: 'rmdir', promises: true, absPathArgs: [ 0 ] },
|
||||||
{ name: 'stat', promises: true },
|
{ name: 'stat', promises: true, absPathArgs: [ 0 ] },
|
||||||
{ name: 'fstat' },
|
{ name: 'fstat' },
|
||||||
{ name: 'fsync' },
|
{ name: 'fsync' },
|
||||||
{ name: 'link', promises: true },
|
{ name: 'link', promises: true, absPathArgs: [0, 1] },
|
||||||
{ name: 'unlink', promises: true },
|
{ name: 'unlink', promises: true, absPathArgs: [ 0 ] },
|
||||||
{ name: 'read' },
|
{ name: 'read' },
|
||||||
{ name: 'readFile', promises: true },
|
{ name: 'readFile', promises: true, absPathArgs: [ 0 ] },
|
||||||
{ name: 'write' },
|
{ name: 'write' },
|
||||||
{ name: 'writeFile', promises: true },
|
{ name: 'writeFile', promises: true, absPathArgs: [ 0 ] },
|
||||||
{ name: 'appendFile', promises: true },
|
{ name: 'appendFile', promises: true, absPathArgs: [ 0 ] },
|
||||||
{ name: 'exists' },
|
{ name: 'exists', absPathArgs: [ 0 ] },
|
||||||
{ name: 'lseek' },
|
{ name: 'lseek' },
|
||||||
{ name: 'readdir', promises: true },
|
{ name: 'readdir', promises: true, absPathArgs: [ 0 ] },
|
||||||
{ name: 'rename', promises: true },
|
{ name: 'rename', promises: true, absPathArgs: [0, 1] },
|
||||||
{ name: 'readlink', promises: true },
|
{ name: 'readlink', promises: true, absPathArgs: [ 0 ] },
|
||||||
{ name: 'symlink', promises: true },
|
{ name: 'symlink', promises: true, relPathPargs: [ 0 ], absPathArgs: [ 1 ] },
|
||||||
{ name: 'lstat', promises: true },
|
{ name: 'lstat', promises: true },
|
||||||
{ name: 'truncate', promises: true },
|
{ name: 'truncate', promises: true, absPathArgs: [ 0 ] },
|
||||||
{ name: 'ftruncate' },
|
{ name: 'ftruncate' },
|
||||||
{ name: 'utimes', promises: true },
|
{ name: 'utimes', promises: true, absPathArgs: [ 0 ] },
|
||||||
{ name: 'futimes' },
|
{ name: 'futimes' },
|
||||||
{ name: 'setxattr', promises: true },
|
{ name: 'setxattr', promises: true, absPathArgs: [ 0 ] },
|
||||||
{ name: 'getxattr', promises: true },
|
{ name: 'getxattr', promises: true, absPathArgs: [ 0 ] },
|
||||||
{ name: 'fsetxattr' },
|
{ name: 'fsetxattr' },
|
||||||
{ name: 'fgetxattr' },
|
{ name: 'fgetxattr' },
|
||||||
{ name: 'removexattr', promises: true },
|
{ name: 'removexattr', promises: true, absPathArgs: [ 0 ] },
|
||||||
{ name: 'fremovexattr' }
|
{ name: 'fremovexattr' }
|
||||||
].forEach(function(method) {
|
].forEach(function(method) {
|
||||||
var methodName = method.name;
|
var methodName = method.name;
|
||||||
|
@ -306,6 +364,14 @@ function FileSystem(options, callback) {
|
||||||
var missingCallback = typeof args[lastArgIndex] !== 'function';
|
var missingCallback = typeof args[lastArgIndex] !== 'function';
|
||||||
var callback = maybeCallback(args[lastArgIndex]);
|
var callback = maybeCallback(args[lastArgIndex]);
|
||||||
|
|
||||||
|
// Deal with path arguments, validating and normalizing Buffer and file:// URLs
|
||||||
|
if(method.absPathArgs) {
|
||||||
|
method.absPathArgs.forEach(pathArg => processPathArg(args, pathArg, false));
|
||||||
|
}
|
||||||
|
if(method.relPathPargs) {
|
||||||
|
method.relPathPargs.forEach(pathArg => processPathArg(args, pathArg, true));
|
||||||
|
}
|
||||||
|
|
||||||
var error = fs.queueOrRun(function() {
|
var error = fs.queueOrRun(function() {
|
||||||
var context = fs.provider.openReadWriteContext();
|
var context = fs.provider.openReadWriteContext();
|
||||||
|
|
||||||
|
|
|
@ -5,23 +5,15 @@ describe('undefined and relative paths, issue270', function() {
|
||||||
beforeEach(util.setup);
|
beforeEach(util.setup);
|
||||||
afterEach(util.cleanup);
|
afterEach(util.cleanup);
|
||||||
|
|
||||||
it('should fail with EINVAL when called on an undefined path', function(done) {
|
it('should fail with EINVAL when called on an undefined path', function() {
|
||||||
var fs = util.fs();
|
var fs = util.fs();
|
||||||
|
var fn = () => fs.writeFile(undefined, 'data');
|
||||||
fs.writeFile(undefined, 'data', function(err) {
|
expect(fn).to.throw();
|
||||||
expect(err).to.exist;
|
|
||||||
expect(err.code).to.equal('EINVAL');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail with EINVAL when called on a relative path', function(done) {
|
it('should fail with EINVAL when called on a relative path', function() {
|
||||||
var fs = util.fs();
|
var fs = util.fs();
|
||||||
|
var fn = () => fs.writeFile('relpath/file.txt', 'data');
|
||||||
fs.writeFile('relpath/file.txt', 'data', function(err) {
|
expect(fn).to.throw();
|
||||||
expect(err).to.exist;
|
|
||||||
expect(err.code).to.equal('EINVAL');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,14 +10,10 @@ describe('fs.write', function() {
|
||||||
expect(fs.write).to.be.a('function');
|
expect(fs.write).to.be.a('function');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should error if file path is undefined',function(done) {
|
it('should error if file path is undefined', function() {
|
||||||
var fs = util.fs();
|
var fs = util.fs();
|
||||||
|
var fn = () => fs.writeFile(undefined, 'data');
|
||||||
fs.writeFile(undefined, 'data', function(error) {
|
expect(fn).to.throw();
|
||||||
expect(error).to.exist;
|
|
||||||
expect(error.code).to.equal('EINVAL');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should write data to a file', function(done) {
|
it('should write data to a file', function(done) {
|
||||||
|
|
|
@ -9,23 +9,18 @@ describe('node.js tests: https://github.com/joyent/node/blob/master/test/simple/
|
||||||
var checks = [];
|
var checks = [];
|
||||||
var fnCount = 0;
|
var fnCount = 0;
|
||||||
var fnTotal = 16;
|
var fnTotal = 16;
|
||||||
var expected = 'Path must be a string without null bytes.';
|
|
||||||
var fs = util.fs();
|
var fs = util.fs();
|
||||||
|
|
||||||
// Make sure function fails with null path error in callback.
|
// Make sure function fails with null path error in callback.
|
||||||
function check(fn) {
|
function check(fn) {
|
||||||
var args = Array.prototype.slice.call(arguments, 1);
|
var args = Array.prototype.slice.call(arguments, 1);
|
||||||
args = args.concat(function(err) {
|
fn = () => fn.apply(fs, args);
|
||||||
checks.push(function(){
|
expect(fn).to.throw();
|
||||||
expect(err).to.exist;
|
|
||||||
expect(err.message).to.equal(expected);
|
fnCount++;
|
||||||
});
|
if(fnCount === fnTotal) {
|
||||||
fnCount++;
|
done();
|
||||||
if(fnCount === fnTotal) {
|
}
|
||||||
done();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
fn.apply(fs, args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
check(fs.link, '/foo\u0000bar', 'foobar');
|
check(fs.link, '/foo\u0000bar', 'foobar');
|
||||||
|
|
|
@ -2,6 +2,9 @@ var Filer = require('../../src');
|
||||||
var util = require('../lib/test-utils.js');
|
var util = require('../lib/test-utils.js');
|
||||||
var expect = require('chai').expect;
|
var expect = require('chai').expect;
|
||||||
|
|
||||||
|
// Support global URL and node's url module
|
||||||
|
var URL = global.URL || require('url').URL;
|
||||||
|
|
||||||
describe('path resolution', function() {
|
describe('path resolution', function() {
|
||||||
beforeEach(util.setup);
|
beforeEach(util.setup);
|
||||||
afterEach(util.cleanup);
|
afterEach(util.cleanup);
|
||||||
|
@ -245,4 +248,52 @@ describe('path resolution', function() {
|
||||||
expect(Path.removeTrailing('/dir/')).to.equal('/dir');
|
expect(Path.removeTrailing('/dir/')).to.equal('/dir');
|
||||||
expect(Path.removeTrailing('/dir//')).to.equal('/dir');
|
expect(Path.removeTrailing('/dir//')).to.equal('/dir');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should allow using Buffer for paths', function(done) {
|
||||||
|
var fs = util.fs();
|
||||||
|
var filePath = '/file';
|
||||||
|
var bufferPath = Buffer.from(filePath);
|
||||||
|
var data = 'data';
|
||||||
|
|
||||||
|
fs.writeFile(bufferPath, data, function(err) {
|
||||||
|
if(err) throw err;
|
||||||
|
|
||||||
|
fs.readFile(filePath, 'utf8', function(err, result) {
|
||||||
|
if(err) throw err;
|
||||||
|
expect(result).to.equal(data);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow using file: URLs for paths', function(done) {
|
||||||
|
var fs = util.fs();
|
||||||
|
var filePath = '/file';
|
||||||
|
var fileUrl = new URL(`file://${filePath}`);
|
||||||
|
var data = 'data';
|
||||||
|
|
||||||
|
fs.writeFile(fileUrl, data, function(err) {
|
||||||
|
if(err) throw err;
|
||||||
|
|
||||||
|
fs.readFile(filePath, 'utf8', function(err, result) {
|
||||||
|
if(err) throw err;
|
||||||
|
expect(result).to.equal(data);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should error for non file: URLs for paths', function() {
|
||||||
|
var fs = util.fs();
|
||||||
|
var fileUrl = new URL('http://file');
|
||||||
|
var fn = () => fs.writeFile(fileUrl, 1);
|
||||||
|
expect(fn).to.throw();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should error if file: URLs include escaped / characters', function() {
|
||||||
|
var fs = util.fs();
|
||||||
|
var fileUrl = new URL('file:///p/a/t/h/%2F');
|
||||||
|
var fn = () => fs.writeFile(fileUrl, 1);
|
||||||
|
expect(fn).to.throw();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue