Add chown, fchown, chmod, fchmod and tests.
This commit is contained in:
parent
e77a8bacd3
commit
9508833b37
|
@ -13,6 +13,10 @@ var NODE_TYPE_DIRECTORY = Constants.NODE_TYPE_DIRECTORY;
|
||||||
var NODE_TYPE_SYMBOLIC_LINK = Constants.NODE_TYPE_SYMBOLIC_LINK;
|
var NODE_TYPE_SYMBOLIC_LINK = Constants.NODE_TYPE_SYMBOLIC_LINK;
|
||||||
var NODE_TYPE_META = Constants.NODE_TYPE_META;
|
var NODE_TYPE_META = Constants.NODE_TYPE_META;
|
||||||
|
|
||||||
|
var DEFAULT_FILE_PERMISSIONS = Constants.DEFAULT_FILE_PERMISSIONS;
|
||||||
|
var DEFAULT_DIR_PERMISSIONS = Constants.DEFAULT_DIR_PERMISSIONS;
|
||||||
|
var FULL_READ_WRITE_EXEC_PERMISSIONS = Constants.FULL_READ_WRITE_EXEC_PERMISSIONS;
|
||||||
|
|
||||||
var ROOT_DIRECTORY_NAME = Constants.ROOT_DIRECTORY_NAME;
|
var ROOT_DIRECTORY_NAME = Constants.ROOT_DIRECTORY_NAME;
|
||||||
var SUPER_NODE_ID = Constants.SUPER_NODE_ID;
|
var SUPER_NODE_ID = Constants.SUPER_NODE_ID;
|
||||||
var SYMLOOP_MAX = Constants.SYMLOOP_MAX;
|
var SYMLOOP_MAX = Constants.SYMLOOP_MAX;
|
||||||
|
@ -1587,8 +1591,22 @@ function pathCheck(path, callback) {
|
||||||
|
|
||||||
|
|
||||||
function open(fs, context, path, flags, mode, callback) {
|
function open(fs, context, path, flags, mode, callback) {
|
||||||
// NOTE: we support the same signature as node with a `mode` arg,
|
/**
|
||||||
// but ignore it.
|
* NOTE: we support the same signature as node with a `mode` arg,
|
||||||
|
* but ignore it. We need to add it. Here is what node.js does:
|
||||||
|
* function open(path, flags, mode, callback) {
|
||||||
|
* path = getPathFromURL(path);
|
||||||
|
* validatePath(path);
|
||||||
|
* const flagsNumber = stringToFlags(flags);
|
||||||
|
* if (arguments.length < 4) {
|
||||||
|
* callback = makeCallback(mode);
|
||||||
|
* mode = 0o666;
|
||||||
|
* } else {
|
||||||
|
* mode = validateAndMaskMode(mode, 'mode', 0o666);
|
||||||
|
* callback = makeCallback(callback);
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
callback = arguments[arguments.length - 1];
|
callback = arguments[arguments.length - 1];
|
||||||
if(!pathCheck(path, callback)) return;
|
if(!pathCheck(path, callback)) return;
|
||||||
|
|
||||||
|
@ -1631,8 +1649,14 @@ function mknod(fs, context, path, type, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function mkdir(fs, context, path, mode, callback) {
|
function mkdir(fs, context, path, mode, callback) {
|
||||||
// NOTE: we support passing a mode arg, but we ignore it internally for now.
|
if (arguments.length < 5) {
|
||||||
callback = arguments[arguments.length - 1];
|
callback = mode;
|
||||||
|
mode = FULL_READ_WRITE_EXEC_PERMISSIONS;
|
||||||
|
} else {
|
||||||
|
mode = validateAndMaskMode(mode, FULL_READ_WRITE_EXEC_PERMISSIONS, callback);
|
||||||
|
if(!mode) return;
|
||||||
|
}
|
||||||
|
|
||||||
if(!pathCheck(path, callback)) return;
|
if(!pathCheck(path, callback)) return;
|
||||||
make_directory(context, path, callback);
|
make_directory(context, path, callback);
|
||||||
}
|
}
|
||||||
|
@ -1863,6 +1887,121 @@ function exists(fs, context, path, callback) {
|
||||||
stat(fs, context, path, cb);
|
stat(fs, context, path, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Based on https://github.com/nodejs/node/blob/c700cc42da9cf73af9fec2098520a6c0a631d901/lib/internal/validators.js#L21
|
||||||
|
var octalReg = /^[0-7]+$/;
|
||||||
|
var modeDesc = 'must be a 32-bit unsigned integer or an octal string';
|
||||||
|
function isUint32(value) {
|
||||||
|
return value === (value >>> 0);
|
||||||
|
}
|
||||||
|
// Validator for mode_t (the S_* constants). Valid numbers or octal strings
|
||||||
|
// will be masked with 0o777 to be consistent with the behavior in POSIX APIs.
|
||||||
|
function validateAndMaskMode(value, def, callback) {
|
||||||
|
if(typeof def === 'function') {
|
||||||
|
callback = def;
|
||||||
|
def = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isUint32(value)) {
|
||||||
|
return value & FULL_READ_WRITE_EXEC_PERMISSIONS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === 'number') {
|
||||||
|
if (!Number.isInteger(value)) {
|
||||||
|
callback(new Errors.EINVAL('mode not a valid an integer value', value));
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// 2 ** 32 === 4294967296
|
||||||
|
callback(new Errors.EINVAL('mode not a valid an integer value', value));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
if (!octalReg.test(value)) {
|
||||||
|
callback(new Errors.EINVAL('mode not a valid octal string', value));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var parsed = parseInt(value, 8);
|
||||||
|
return parsed & FULL_READ_WRITE_EXEC_PERMISSIONS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(BridgeAR): Only return `def` in case `value == null`
|
||||||
|
if (def !== undefined) {
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(new Errors.EINVAL('mode not valid', value));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function chmod_file(context, path, mode, callback) {
|
||||||
|
path = normalize(path);
|
||||||
|
|
||||||
|
function update_mode(error, node) {
|
||||||
|
if (error) {
|
||||||
|
callback(error);
|
||||||
|
} else {
|
||||||
|
node.mode = mode;
|
||||||
|
update_node_times(context, path, node, { mtime: Date.now() }, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof mode != 'number') {
|
||||||
|
callback(new Errors.EINVAL('mode must be number', path));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
find_node(context, path, update_mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fchmod_file(context, ofd, mode, callback) {
|
||||||
|
function update_mode(error, node) {
|
||||||
|
if (error) {
|
||||||
|
callback(error);
|
||||||
|
} else {
|
||||||
|
node.mode = mode;
|
||||||
|
update_node_times(context, ofd.path, node, { mtime: Date.now() }, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof mode != 'number') {
|
||||||
|
callback(new Errors.EINVAL('mode must be a number'));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ofd.getNode(context, update_mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function chown_file(context, path, uid, gid, callback) {
|
||||||
|
path = normalize(path);
|
||||||
|
|
||||||
|
function update_owner(error, node) {
|
||||||
|
if (error) {
|
||||||
|
callback(error);
|
||||||
|
} else {
|
||||||
|
node.uid = uid;
|
||||||
|
node.gid = gid;
|
||||||
|
update_node_times(context, path, node, { mtime: Date.now() }, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
find_node(context, path, update_owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fchown_file(context, ofd, uid, gid, callback) {
|
||||||
|
function update_owner(error, node) {
|
||||||
|
if (error) {
|
||||||
|
callback(error);
|
||||||
|
} else {
|
||||||
|
node.uid = uid;
|
||||||
|
node.gid = gid;
|
||||||
|
update_node_times(context, ofd.path, node, { mtime: Date.now() }, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ofd.getNode(context, update_owner);
|
||||||
|
}
|
||||||
|
|
||||||
function getxattr(fs, context, path, name, callback) {
|
function getxattr(fs, context, path, name, callback) {
|
||||||
if (!pathCheck(path, callback)) return;
|
if (!pathCheck(path, callback)) return;
|
||||||
getxattr_file(context, path, name, callback);
|
getxattr_file(context, path, name, callback);
|
||||||
|
@ -1994,6 +2133,58 @@ function futimes(fs, context, fd, atime, mtime, callback) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function chmod(fs, context, path, mode, callback) {
|
||||||
|
if(!pathCheck(path, callback)) return;
|
||||||
|
mode = validateAndMaskMode(mode, 'mode');
|
||||||
|
if(!mode) return;
|
||||||
|
|
||||||
|
chmod_file(context, path, mode, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fchmod(fs, context, fd, mode, callback) {
|
||||||
|
mode = validateAndMaskMode(mode, 'mode');
|
||||||
|
if(!mode) return;
|
||||||
|
|
||||||
|
var ofd = fs.openFiles[fd];
|
||||||
|
if(!ofd) {
|
||||||
|
callback(new Errors.EBADF());
|
||||||
|
} else if(!_(ofd.flags).contains(O_WRITE)) {
|
||||||
|
callback(new Errors.EBADF('descriptor does not permit writing'));
|
||||||
|
} else {
|
||||||
|
fchmod_file(context, ofd, mode, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function chown(fs, context, path, uid, gid, callback) {
|
||||||
|
if(!pathCheck(path, callback)) return;
|
||||||
|
if(!isUint32(uid)) {
|
||||||
|
return callback(new Errors.EINVAL('uid must be a valid integer', uid));
|
||||||
|
}
|
||||||
|
if(!isUint32(gid)) {
|
||||||
|
return callback(new Errors.EINVAL('gid must be a valid integer', gid));
|
||||||
|
}
|
||||||
|
|
||||||
|
chown_file(context, path, uid, gid, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fchown(fs, context, fd, uid, gid, callback) {
|
||||||
|
if(!isUint32(uid)) {
|
||||||
|
return callback(new Errors.EINVAL('uid must be a valid integer', uid));
|
||||||
|
}
|
||||||
|
if(!isUint32(gid)) {
|
||||||
|
return callback(new Errors.EINVAL('gid must be a valid integer', gid));
|
||||||
|
}
|
||||||
|
|
||||||
|
var ofd = fs.openFiles[fd];
|
||||||
|
if(!ofd) {
|
||||||
|
callback(new Errors.EBADF());
|
||||||
|
} else if(!_(ofd.flags).contains(O_WRITE)) {
|
||||||
|
callback(new Errors.EBADF('descriptor does not permit writing'));
|
||||||
|
} else {
|
||||||
|
fchown_file(context, ofd, uid, gid, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function rename(fs, context, oldpath, newpath, callback) {
|
function rename(fs, context, oldpath, newpath, callback) {
|
||||||
if(!pathCheck(oldpath, callback)) return;
|
if(!pathCheck(oldpath, callback)) return;
|
||||||
if(!pathCheck(newpath, callback)) return;
|
if(!pathCheck(newpath, callback)) return;
|
||||||
|
@ -2164,6 +2355,10 @@ function ftruncate(fs, context, fd, length, callback) {
|
||||||
module.exports = {
|
module.exports = {
|
||||||
ensureRootDirectory: ensure_root_directory,
|
ensureRootDirectory: ensure_root_directory,
|
||||||
open: open,
|
open: open,
|
||||||
|
chmod: chmod,
|
||||||
|
fchmod: fchmod,
|
||||||
|
chown: chown,
|
||||||
|
fchown: fchown,
|
||||||
close: close,
|
close: close,
|
||||||
mknod: mknod,
|
mknod: mknod,
|
||||||
mkdir: mkdir,
|
mkdir: mkdir,
|
||||||
|
|
|
@ -94,6 +94,9 @@ function FileSystem(options, callback) {
|
||||||
fs.stdout = STDOUT;
|
fs.stdout = STDOUT;
|
||||||
fs.stderr = STDERR;
|
fs.stderr = STDERR;
|
||||||
|
|
||||||
|
// Expose Node's fs.constants to users
|
||||||
|
fs.constants = Constants.fsConstants;
|
||||||
|
|
||||||
// Expose Shell constructor
|
// Expose Shell constructor
|
||||||
this.Shell = Shell.bind(undefined, this);
|
this.Shell = Shell.bind(undefined, this);
|
||||||
|
|
||||||
|
@ -268,6 +271,10 @@ FileSystem.providers = providers;
|
||||||
*/
|
*/
|
||||||
[
|
[
|
||||||
'open',
|
'open',
|
||||||
|
'chmod',
|
||||||
|
'fchmod',
|
||||||
|
'chown',
|
||||||
|
'fchown',
|
||||||
'close',
|
'close',
|
||||||
'mknod',
|
'mknod',
|
||||||
'mkdir',
|
'mkdir',
|
||||||
|
|
|
@ -39,6 +39,8 @@ require("./spec/time-flags.spec");
|
||||||
require("./spec/fs.watch.spec");
|
require("./spec/fs.watch.spec");
|
||||||
require("./spec/errors.spec");
|
require("./spec/errors.spec");
|
||||||
require("./spec/fs.shell.spec");
|
require("./spec/fs.shell.spec");
|
||||||
|
require("./spec/fs.chmod.spec");
|
||||||
|
require("./spec/fs.chown.spec")
|
||||||
|
|
||||||
// Filer.FileSystem.providers.*
|
// Filer.FileSystem.providers.*
|
||||||
require("./spec/providers/providers.spec");
|
require("./spec/providers/providers.spec");
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
var Filer = require('../..');
|
||||||
|
var util = require('../lib/test-utils.js');
|
||||||
|
var expect = require('chai').expect;
|
||||||
|
|
||||||
|
describe('fs.chmod, fs.fchmod', function() {
|
||||||
|
beforeEach(util.setup);
|
||||||
|
afterEach(util.cleanup);
|
||||||
|
|
||||||
|
it('should be functions', function() {
|
||||||
|
var fs = util.fs();
|
||||||
|
expect(typeof fs.chmod).to.equal('function');
|
||||||
|
expect(typeof fs.fchmod).to.equal('function');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should automatically set mode=755 for a directory', function(done) {
|
||||||
|
var fs = util.fs();
|
||||||
|
|
||||||
|
fs.mkdir('/dir', function(err) {
|
||||||
|
if(err) throw err;
|
||||||
|
|
||||||
|
fs.stat('/dir', function(err, stats) {
|
||||||
|
if(err) throw err;
|
||||||
|
expect(stats.mode & 0o755).to.equal(0o755);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should automatically set mode=644 for a file', function(done) {
|
||||||
|
var fs = util.fs();
|
||||||
|
|
||||||
|
fs.open('/file', 'w', function(err, fd) {
|
||||||
|
if(err) throw err;
|
||||||
|
|
||||||
|
fs.fstat(fd, function(err, stats) {
|
||||||
|
if(err) throw err;
|
||||||
|
expect(stats.mode & 0o644).to.equal(0o644);
|
||||||
|
fs.close(fd, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow for updating mode of a given file', function(done) {
|
||||||
|
var fs = util.fs();
|
||||||
|
|
||||||
|
fs.open('/file', 'w', function(err, fd) {
|
||||||
|
if(err) throw err;
|
||||||
|
|
||||||
|
fs.fchmod(fd, 0o777, function(err) {
|
||||||
|
if(err) throw err;
|
||||||
|
|
||||||
|
fs.fstat(fd, function(err, stats) {
|
||||||
|
if(err) throw err;
|
||||||
|
expect(stats.mode & 0o777).to.equal(0o777);
|
||||||
|
|
||||||
|
fs.close(fd, function(err) {
|
||||||
|
if(err) throw err;
|
||||||
|
|
||||||
|
fs.chmod('/file', 0o444, function(err) {
|
||||||
|
if(err) throw err;
|
||||||
|
|
||||||
|
fs.stat('/file', function(err, stats) {
|
||||||
|
if(err) throw err;
|
||||||
|
|
||||||
|
expect(stats.mode & 0o444).to.equal(0o444);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,65 @@
|
||||||
|
var Filer = require('../..');
|
||||||
|
var util = require('../lib/test-utils.js');
|
||||||
|
var expect = require('chai').expect;
|
||||||
|
|
||||||
|
describe('fs.chown, fs.fchown', function() {
|
||||||
|
beforeEach(util.setup);
|
||||||
|
afterEach(util.cleanup);
|
||||||
|
|
||||||
|
it('should be functions', function() {
|
||||||
|
var fs = util.fs();
|
||||||
|
expect(typeof fs.chown).to.equal('function');
|
||||||
|
expect(typeof fs.fchown).to.equal('function');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should automatically set a file\s uid and gid to 0 (i.e., root)', function(done) {
|
||||||
|
var fs = util.fs();
|
||||||
|
|
||||||
|
fs.open('/file', 'w', function(err, fd) {
|
||||||
|
if(err) throw err;
|
||||||
|
|
||||||
|
fs.fstat(fd, function(err, stats) {
|
||||||
|
if(err) throw err;
|
||||||
|
|
||||||
|
expect(stats.uid).to.equal(0);
|
||||||
|
expect(stats.gid).to.equal(0);
|
||||||
|
fs.close(fd, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow updating gid and uid for a file', function(done) {
|
||||||
|
var fs = util.fs();
|
||||||
|
|
||||||
|
fs.open('/file', 'w', function(err, fd) {
|
||||||
|
if(err) throw err;
|
||||||
|
|
||||||
|
fs.fchown(fd, 1001, 1001, function(err) {
|
||||||
|
if(err) throw err;
|
||||||
|
|
||||||
|
fs.fstat(fd, function(err, stats) {
|
||||||
|
if(err) throw err;
|
||||||
|
|
||||||
|
expect(stats.uid).to.equal(1001);
|
||||||
|
expect(stats.gid).to.equal(1001);
|
||||||
|
|
||||||
|
fs.close(fd, function(err) {
|
||||||
|
if(err) throw err;
|
||||||
|
|
||||||
|
fs.chown('/file', 500, 500, function(err) {
|
||||||
|
if(err) throw err;
|
||||||
|
|
||||||
|
fs.stat('/file', function(err, stats) {
|
||||||
|
if(err) throw err;
|
||||||
|
|
||||||
|
expect(stats.uid).to.equal(500);
|
||||||
|
expect(stats.gid).to.equal(500);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue