diff --git a/README.md b/README.md index 61e72bc..5ad3aae 100644 --- a/README.md +++ b/README.md @@ -372,6 +372,7 @@ const fs = new Filer.FileSystem(options, callback); * [fs.utimes(path, atime, mtime, callback)](#utimes) * [fs.chown(path, uid, gid, callback)](#chown) * [fs.fchown(fd, uid, gid, callback)](#fchown) +* [fs.lchown(path, uid, gid, callback)](#lchown) * [fs.chmod(path, mode, callback)](#chmod) * [fs.fchmod(fd, mode, callback)](#fchmod) * [fs.futimes(fd, atime, mtime, callback)](#fsutimes) @@ -923,6 +924,19 @@ fs.open('/myfile.txt', function(err, fd) { }); ``` +#### fs.lchown(path, uid, gid, callback) + +lchown() is like chown(), but does not dereference symbolic links. Asynchronous [lchown(2)](http://pubs.opengroup.org/onlinepubs/009695399/functions/chown.html). Callback gets no additional arguments. Both `uid` (user id) and `gid` (group id) arguments should be a JavaScript Number. By default, `0x0` is used (i.e., `root:root` ownership). + +Example: + +```javascript +fs.lchown('/myfile.txt', 500, 500, function(err) { + if(err) throw err; + // /myfile.txt is now owned by user with id 500, group 500 +}); +``` + #### fs.chmod(path, mode, callback) Changes the mode of a file. Asynchronous [chmod(2)](http://pubs.opengroup.org/onlinepubs/009695399/functions/chmod.html). Callback gets no additional arguments. The `mode` argument should be a JavaScript Number, which combines file type and permission information. Here are a list of common values useful for setting the `mode`: diff --git a/src/filesystem/implementation.js b/src/filesystem/implementation.js index b5a8f8b..e9a6a9b 100644 --- a/src/filesystem/implementation.js +++ b/src/filesystem/implementation.js @@ -278,6 +278,14 @@ function find_node(context, path, callback) { } } +/** + * find_lnode + */ +// in: file or directory path +// out: node structure, or error +function find_symbolic_node(context, path, callback) { + lstat_file(context, path, callback); +} /** * set extended attribute (refactor) @@ -2060,6 +2068,19 @@ function fchown_file(context, ofd, uid, gid, callback) { ofd.getNode(context, update_owner); } +function lchown_file(context, path, uid, gid, callback) { + 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_symbolic_node(context, path, update_owner); +} + function getxattr(context, path, name, callback) { getxattr_file(context, path, name, callback); } @@ -2244,6 +2265,17 @@ function fchown(context, fd, uid, gid, callback) { } } +function lchown(context, path, 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)); + } + + lchown_file(context, path, uid, gid, callback); +} + function rename(context, oldpath, newpath, callback) { oldpath = normalize(oldpath); newpath = normalize(newpath); @@ -2425,7 +2457,7 @@ module.exports = { ftruncate, futimes, getxattr, - // lchown - https://github.com/filerjs/filer/issues/620 + lchown, // lchmod - https://github.com/filerjs/filer/issues/619 link, lseek, diff --git a/src/filesystem/interface.js b/src/filesystem/interface.js index 824128b..d6e9390 100644 --- a/src/filesystem/interface.js +++ b/src/filesystem/interface.js @@ -340,7 +340,7 @@ function FileSystem(options, callback) { { name: 'ftruncate' }, { name: 'futimes' }, { name: 'getxattr', promises: true, absPathArgs: [0] }, - // lchown - https://github.com/filerjs/filer/issues/620 + { name: 'lchown' }, // lchmod - https://github.com/filerjs/filer/issues/619 { name: 'link', promises: true, absPathArgs: [0, 1] }, { name: 'lseek' }, diff --git a/tests/spec/fs.chown.spec.js b/tests/spec/fs.chown.spec.js index e141321..575f779 100644 --- a/tests/spec/fs.chown.spec.js +++ b/tests/spec/fs.chown.spec.js @@ -1,7 +1,7 @@ var util = require('../lib/test-utils.js'); var expect = require('chai').expect; -describe('fs.chown, fs.fchown', function() { +describe('fs.chown, fs.fchown, fs.lchown', function() { beforeEach(util.setup); afterEach(util.cleanup); @@ -27,6 +27,20 @@ describe('fs.chown, fs.fchown', function() { }); }); + it('lchown should expect an interger value for uid', function(done) { + var fs = util.fs(); + + fs.open('/file', 'w', function(err, fd) { + if(err) throw err; + + fs.lchown(fd, '1001', 1001, function(err) { + expect(err).to.exist; + expect(err.code).to.equal('EINVAL'); + fs.close(fd, done); + }); + }); + }); + it('fchown should expect an interger value for uid', function(done) { var fs = util.fs(); @@ -55,6 +69,20 @@ describe('fs.chown, fs.fchown', function() { }); }); + it('lchown should expect an interger value for gid', function(done) { + var fs = util.fs(); + + fs.open('/file', 'w', function(err, fd) { + if(err) throw err; + + fs.lchown(fd, 1001, '1001', function(err) { + expect(err).to.exist; + expect(err.code).to.equal('EINVAL'); + fs.close(fd, done); + }); + }); + }); + it('fchown should expect an interger value for gid', function(done) { var fs = util.fs(); @@ -107,6 +135,18 @@ describe('fs.chown, fs.fchown', function() { fs.stat('/file', function(err, stats) { if(err) throw err; + expect(stats.uid).to.equal(500); + expect(stats.gid).to.equal(500); + //done(); + }); + }); + + fs.lchown('/file', 500, 500, function(err) { + if(err) throw err; + + fs.lstat('/file', function(err, stats) { + if(err) throw err; + expect(stats.uid).to.equal(500); expect(stats.gid).to.equal(500); done();