Skip to content

Commit

Permalink
fix: support path input from Buffer on many fs APIs
Browse files Browse the repository at this point in the history
There is missing support of "path <string> | <Buffer> | <URL>" on many fs APIs.

closes #292
  • Loading branch information
3cp committed Apr 20, 2020
1 parent 83216f3 commit 371df21
Show file tree
Hide file tree
Showing 17 changed files with 373 additions and 22 deletions.
60 changes: 60 additions & 0 deletions lib/binding.js
Expand Up @@ -450,6 +450,9 @@ Binding.prototype.stat = function(filepath, options, callback, ctx) {
markSyscall(ctx, 'stat');

return maybeCallback(wrapStatsCallback(callback), ctx, this, function() {
if (Buffer.isBuffer(filepath)) {
filepath = filepath.toString();
}
let item = _system.getItem(filepath);
if (item instanceof SymbolicLink) {
item = _system.getItem(
Expand Down Expand Up @@ -539,6 +542,9 @@ Binding.prototype.open = function(pathname, flags, mode, callback, ctx) {
markSyscall(ctx, 'open');

return maybeCallback(normalizeCallback(callback), ctx, this, function() {
if (Buffer.isBuffer(pathname)) {
pathname = pathname.toString();
}
const descriptor = new FileDescriptor(flags);
let item = _system.getItem(pathname);
while (item instanceof SymbolicLink) {
Expand Down Expand Up @@ -674,6 +680,12 @@ Binding.prototype.copyFile = function(src, dest, flags, callback, ctx) {
markSyscall(ctx, 'copyfile');

return maybeCallback(normalizeCallback(callback), ctx, this, function() {
if (Buffer.isBuffer(src)) {
src = src.toString();
}
if (Buffer.isBuffer(dest)) {
dest = dest.toString();
}
const srcFd = this.open(src, constants.O_RDONLY);

try {
Expand Down Expand Up @@ -873,6 +885,12 @@ Binding.prototype.rename = function(oldPath, newPath, callback, ctx) {
markSyscall(ctx, 'rename');

return maybeCallback(normalizeCallback(callback), ctx, this, function() {
if (Buffer.isBuffer(oldPath)) {
oldPath = oldPath.toString();
}
if (Buffer.isBuffer(newPath)) {
newPath = newPath.toString();
}
const oldItem = _system.getItem(oldPath);
if (!oldItem) {
throw new FSError('ENOENT', oldPath);
Expand Down Expand Up @@ -938,6 +956,9 @@ Binding.prototype.readdir = function(
markSyscall(ctx, 'scandir');

return maybeCallback(normalizeCallback(callback), ctx, this, function() {
if (Buffer.isBuffer(dirpath)) {
dirpath = dirpath.toString();
}
let dpath = dirpath;
let dir = _system.getItem(dirpath);
while (dir instanceof SymbolicLink) {
Expand Down Expand Up @@ -990,6 +1011,9 @@ Binding.prototype.mkdir = function(pathname, mode, recursive, callback, ctx) {
markSyscall(ctx, 'mkdir');

return maybeCallback(normalizeCallback(callback), ctx, this, function() {
if (Buffer.isBuffer(pathname)) {
pathname = pathname.toString();
}
const item = _system.getItem(pathname);
if (item) {
if (recursive && item instanceof Directory) {
Expand Down Expand Up @@ -1030,6 +1054,9 @@ Binding.prototype.rmdir = function(pathname, callback, ctx) {
markSyscall(ctx, 'rmdir');

return maybeCallback(normalizeCallback(callback), ctx, this, function() {
if (Buffer.isBuffer(pathname)) {
pathname = pathname.toString();
}
const item = _system.getItem(pathname);
if (!item) {
throw new FSError('ENOENT', pathname);
Expand Down Expand Up @@ -1158,6 +1185,9 @@ Binding.prototype.chown = function(pathname, uid, gid, callback, ctx) {
markSyscall(ctx, 'chown');

return maybeCallback(normalizeCallback(callback), ctx, this, function() {
if (Buffer.isBuffer(pathname)) {
pathname = pathname.toString();
}
const item = _system.getItem(pathname);
if (!item) {
throw new FSError('ENOENT', pathname);
Expand Down Expand Up @@ -1197,6 +1227,9 @@ Binding.prototype.chmod = function(pathname, mode, callback, ctx) {
markSyscall(ctx, 'chmod');

return maybeCallback(normalizeCallback(callback), ctx, this, function() {
if (Buffer.isBuffer(pathname)) {
pathname = pathname.toString();
}
const item = _system.getItem(pathname);
if (!item) {
throw new FSError('ENOENT', pathname);
Expand Down Expand Up @@ -1232,6 +1265,9 @@ Binding.prototype.unlink = function(pathname, callback, ctx) {
markSyscall(ctx, 'unlink');

return maybeCallback(normalizeCallback(callback), ctx, this, function() {
if (Buffer.isBuffer(pathname)) {
pathname = pathname.toString();
}
const item = _system.getItem(pathname);
if (!item) {
throw new FSError('ENOENT', pathname);
Expand All @@ -1256,6 +1292,9 @@ Binding.prototype.utimes = function(pathname, atime, mtime, callback, ctx) {
markSyscall(ctx, 'utimes');

return maybeCallback(normalizeCallback(callback), ctx, this, function() {
if (Buffer.isBuffer(pathname)) {
pathname = pathname.toString();
}
const item = _system.getItem(pathname);
if (!item) {
throw new FSError('ENOENT', pathname);
Expand Down Expand Up @@ -1323,6 +1362,12 @@ Binding.prototype.link = function(srcPath, destPath, callback, ctx) {
markSyscall(ctx, 'link');

return maybeCallback(normalizeCallback(callback), ctx, this, function() {
if (Buffer.isBuffer(srcPath)) {
srcPath = srcPath.toString();
}
if (Buffer.isBuffer(destPath)) {
destPath = destPath.toString();
}
const item = _system.getItem(srcPath);
if (!item) {
throw new FSError('ENOENT', srcPath);
Expand Down Expand Up @@ -1356,6 +1401,12 @@ Binding.prototype.symlink = function(srcPath, destPath, type, callback, ctx) {
markSyscall(ctx, 'symlink');

return maybeCallback(normalizeCallback(callback), ctx, this, function() {
if (Buffer.isBuffer(srcPath)) {
srcPath = srcPath.toString();
}
if (Buffer.isBuffer(destPath)) {
destPath = destPath.toString();
}
if (_system.getItem(destPath)) {
throw new FSError('EEXIST', destPath);
}
Expand Down Expand Up @@ -1390,6 +1441,9 @@ Binding.prototype.readlink = function(pathname, encoding, callback, ctx) {
markSyscall(ctx, 'readlink');

return maybeCallback(normalizeCallback(callback), ctx, this, function() {
if (Buffer.isBuffer(pathname)) {
pathname = pathname.toString();
}
const link = _system.getItem(pathname);
if (!link) {
throw new FSError('ENOENT', pathname);
Expand Down Expand Up @@ -1423,6 +1477,9 @@ Binding.prototype.lstat = function(filepath, options, callback, ctx) {
markSyscall(ctx, 'lstat');

return maybeCallback(wrapStatsCallback(callback), ctx, this, function() {
if (Buffer.isBuffer(filepath)) {
filepath = filepath.toString();
}
const item = _system.getItem(filepath);
if (!item) {
throw new FSError('ENOENT', filepath);
Expand Down Expand Up @@ -1455,6 +1512,9 @@ Binding.prototype.access = function(filepath, mode, callback, ctx) {
markSyscall(ctx, 'access');

return maybeCallback(normalizeCallback(callback), ctx, this, function() {
if (Buffer.isBuffer(filepath)) {
filepath = filepath.toString();
}
let item = _system.getItem(filepath);
let links = 0;
while (item instanceof SymbolicLink) {
Expand Down
23 changes: 21 additions & 2 deletions test/helper.js
Expand Up @@ -15,8 +15,27 @@ chai.config.includeStack = true;
*/
exports.assert = chai.assert;

const TEST = {it: it, xit: xit, describe: describe, xdescribe: xdescribe};
const NO_TEST = {it: xit, xit: xit, describe: xdescribe, xdescribe: xdescribe};
function run(func) {
func();
}

function noRun() {}

const TEST = {
it: it,
xit: xit,
describe: describe,
xdescribe: xdescribe,
run: run
};

const NO_TEST = {
it: xit,
xit: xit,
describe: xdescribe,
xdescribe: xdescribe,
run: noRun
};

exports.inVersion = function(range) {
if (semver.satisfies(process.version, range)) {
Expand Down
4 changes: 4 additions & 0 deletions test/lib/fs.access.spec.js
Expand Up @@ -63,6 +63,10 @@ if (fs.access && fs.accessSync && process.getuid && process.getgid) {
fs.access('path/to/accessible/file', done);
});

it('supports Buffer input', function(done) {
fs.access(Buffer.from('path/to/accessible/file'), done);
});

withPromise.it('promise works for an accessible file', function(done) {
fs.promises.access('path/to/accessible/file').then(done, done);
});
Expand Down
11 changes: 11 additions & 0 deletions test/lib/fs.chmod-fchmod.spec.js
Expand Up @@ -26,6 +26,17 @@ describe('fs.chmod(path, mode, callback)', function() {
});
});

it('supports Buffer input', function(done) {
fs.chmod(Buffer.from('file.txt'), parseInt('0664', 8), function(err) {
if (err) {
return done(err);
}
const stats = fs.statSync(Buffer.from('file.txt'));
assert.equal(stats.mode & parseInt('0777', 8), parseInt('0664', 8));
done();
});
});

withPromise.it('promise changes permissions of a file', function(done) {
fs.promises.chmod('file.txt', parseInt('0664', 8)).then(function() {
const stats = fs.statSync('file.txt');
Expand Down
4 changes: 4 additions & 0 deletions test/lib/fs.chown-fchown.spec.js
Expand Up @@ -20,6 +20,10 @@ describe('fs.chown(path, uid, gid, callback)', function() {
fs.chown('file.txt', 42, 43, done);
});

it('supports Buffer input', function(done) {
fs.chown(Buffer.from('file.txt'), 42, 43, done);
});

withPromise.it('promise changes ownership of a file', function(done) {
fs.promises.chown('file.txt', 42, 43).then(done, done);
});
Expand Down
16 changes: 16 additions & 0 deletions test/lib/fs.copyFile.spec.js
Expand Up @@ -27,6 +27,22 @@ if (fs.copyFile && fs.copyFileSync) {
});
});

it('supports Buffer input', function(done) {
fs.copyFile(
Buffer.from('path/to/src.txt'),
Buffer.from('empty/dest.txt'),
function(err) {
assert.isTrue(!err);
assert.isTrue(fs.existsSync('empty/dest.txt'));
assert.equal(
String(fs.readFileSync('empty/dest.txt')),
'file content'
);
done();
}
);
});

withPromise.it('promise copies a file to an empty directory', function(
done
) {
Expand Down
30 changes: 30 additions & 0 deletions test/lib/fs.link-symlink.spec.js
Expand Up @@ -31,6 +31,21 @@ describe('fs.link(srcpath, dstpath, callback)', function() {
});
});

it('supports Buffer input', function(done) {
assert.equal(fs.statSync('file.txt').nlink, 1);

fs.link(Buffer.from('file.txt'), Buffer.from('link.txt'), function(err) {
if (err) {
return done(err);
}
assert.isTrue(fs.statSync('link.txt').isFile());
assert.equal(fs.statSync('link.txt').nlink, 2);
assert.equal(fs.statSync('file.txt').nlink, 2);
assert.equal(String(fs.readFileSync('link.txt')), 'content');
done();
});
});

withPromise.it('promise creates a link to a file', function(done) {
assert.equal(fs.statSync('file.txt').nlink, 1);

Expand Down Expand Up @@ -173,6 +188,21 @@ describe('fs.symlink(srcpath, dstpath, [type], callback)', function() {
});
});

it('supports Buffer input', function(done) {
fs.symlink(
Buffer.from('../file.txt'),
Buffer.from('dir/link.txt'),
function(err) {
if (err) {
return done(err);
}
assert.isTrue(fs.statSync('dir/link.txt').isFile());
assert.equal(String(fs.readFileSync('dir/link.txt')), 'content');
done();
}
);
});

withPromise.it('promise creates a symbolic link to a file', function(done) {
fs.promises.symlink('../file.txt', 'dir/link.txt').then(function() {
assert.isTrue(fs.statSync('dir/link.txt').isFile());
Expand Down
12 changes: 12 additions & 0 deletions test/lib/fs.lstat.spec.js
Expand Up @@ -34,6 +34,18 @@ describe('fs.lstat(path, callback)', function() {
});
});

it('suports Buffer input', function(done) {
fs.lstat(Buffer.from('link'), function(err, stats) {
if (err) {
return done(err);
}
assert.isTrue(stats.isSymbolicLink());
assert.isFalse(stats.isFile());
assert.equal(stats.mtime.getTime(), 2);
done();
});
});

withPromise.it('promise stats a symbolic link', function(done) {
fs.promises.lstat('link').then(function(stats) {
assert.isTrue(stats.isSymbolicLink());
Expand Down
11 changes: 11 additions & 0 deletions test/lib/fs.mkdir.spec.js
Expand Up @@ -35,6 +35,17 @@ describe('fs.mkdir(path, [mode], callback)', function() {
});
});

it('supports Buffer input', function(done) {
fs.mkdir(Buffer.from('parent/dir'), function(err) {
if (err) {
return done(err);
}
const stats = fs.statSync('parent/dir');
assert.isTrue(stats.isDirectory());
done();
});
});

withPromise.it('promise creates a new directory', function(done) {
fs.promises.mkdir('parent/dir').then(function() {
const stats = fs.statSync('parent/dir');
Expand Down
10 changes: 10 additions & 0 deletions test/lib/fs.open-close.spec.js
Expand Up @@ -34,6 +34,16 @@ describe('fs.open(path, flags, [mode], callback)', function() {
});
});

it('supports Buffer input', function(done) {
fs.open(Buffer.from('nested/sub/dir/one.txt'), 'r', function(err, fd) {
if (err) {
return done(err);
}
assert.isNumber(fd);
done();
});
});

withPromise.it('promise opens an existing file for reading (r)', function(
done
) {
Expand Down
9 changes: 9 additions & 0 deletions test/lib/fs.readdir.spec.js
Expand Up @@ -35,6 +35,15 @@ describe('fs.readdir(path, callback)', function() {
});
});

it('supports Buffer input', function(done) {
fs.readdir(Buffer.from(path.join('path', 'to')), function(err, items) {
assert.isNull(err);
assert.isArray(items);
assert.deepEqual(items, ['file.txt']);
done();
});
});

withPromise.it('promise lists directory contents', function(done) {
fs.promises.readdir(path.join('path', 'to')).then(function(items) {
assert.isArray(items);
Expand Down
10 changes: 10 additions & 0 deletions test/lib/fs.readlink.spec.js
Expand Up @@ -26,6 +26,16 @@ describe('fs.readlink(path, callback)', function() {
});
});

it('supports Buffer input', function(done) {
fs.readlink(Buffer.from('link'), function(err, srcPath) {
if (err) {
return done(err);
}
assert.equal(srcPath, './file.txt');
done();
});
});

withPromise.it('promise reads a symbolic link', function(done) {
fs.promises.readlink('link').then(function(srcPath) {
assert.equal(srcPath, './file.txt');
Expand Down

0 comments on commit 371df21

Please sign in to comment.