Bug 1001849: expose general chmod, and chmod-to-umask, support in OS.File. r=yoric

This commit is contained in:
Zack Weinberg 2014-06-10 09:12:45 -04:00
parent 00cd340bac
commit cf0cc1e3c0
7 changed files with 263 additions and 0 deletions

View File

@ -823,6 +823,27 @@ File.prototype = {
flush: function flush() { flush: function flush() {
return Scheduler.post("File_prototype_flush", return Scheduler.post("File_prototype_flush",
[this._fdmsg]); [this._fdmsg]);
},
/**
* Set the file's access permissions. Without any options, the
* permissions are set to an approximation of what they would have
* been if the file had been created in its current directory in the
* "most typical" fashion for the operating system. In the current
* implementation, this means that on Unix-like systems (including
* Android, B2G, etc) we set the POSIX file mode to (0666 & ~umask),
* and on Windows, we do nothing.
*
* @param {*=} options
* - {number} unixMode If present, the POSIX file mode is set to exactly
* this value, unless |unixHonorUmask| is also
* present.
* - {bool} unixHonorUmask If true, any |unixMode| value is modified by the
* process umask, as open() would have done.
*/
setPermissions: function setPermissions(options = {}) {
return Scheduler.post("File_prototype_setPermissions",
[this._fdmsg, options]);
} }
}; };
@ -923,6 +944,29 @@ File.setDates = function setDates(path, accessDate, modificationDate) {
this); this);
}; };
/**
* Set the file's access permissions. Without any options, the
* permissions are set to an approximation of what they would have
* been if the file had been created in its current directory in the
* "most typical" fashion for the operating system. In the current
* implementation, this means that on Unix-like systems (including
* Android, B2G, etc) we set the POSIX file mode to (0666 & ~umask),
* and on Windows, we do nothing.
*
* @param {string} path The path to the file.
*
* @param {*=} options
* - {number} unixMode If present, the POSIX file mode is set to exactly
* this value, unless |unixHonorUmask| is also
* present.
* - {bool} unixHonorUmask If true, any |unixMode| value is modified by the
* process umask, as open() would have done.
*/
File.setPermissions = function setPermissions(path, options = {}) {
return Scheduler.post("setPermissions",
[Type.path.toMsg(path), options]);
};
/** /**
* Fetch the current directory * Fetch the current directory
* *

View File

@ -297,6 +297,9 @@ const EXCEPTION_NAMES = {
return exports.OS.File.Info.toMsg( return exports.OS.File.Info.toMsg(
exports.OS.File.stat(Type.path.fromMsg(path), options)); exports.OS.File.stat(Type.path.fromMsg(path), options));
}, },
setPermissions: function setPermissions(path, options = {}) {
return exports.OS.File.setPermissions(Type.path.fromMsg(path), options);
},
setDates: function setDates(path, accessDate, modificationDate) { setDates: function setDates(path, accessDate, modificationDate) {
return exports.OS.File.setDates(Type.path.fromMsg(path), accessDate, return exports.OS.File.setDates(Type.path.fromMsg(path), accessDate,
modificationDate); modificationDate);
@ -405,6 +408,12 @@ const EXCEPTION_NAMES = {
return exports.OS.File.Info.toMsg(this.stat()); return exports.OS.File.Info.toMsg(this.stat());
}); });
}, },
File_prototype_setPermissions: function setPermissions(fd, options = {}) {
return withFile(fd,
function do_setPermissions() {
return this.setPermissions(options);
});
},
File_prototype_setDates: function setDates(fd, accessTime, modificationTime) { File_prototype_setDates: function setDates(fd, accessTime, modificationTime) {
return withFile(fd, return withFile(fd,
function do_setDates() { function do_setDates() {

View File

@ -348,6 +348,12 @@
/*return*/ Type.negativeone_or_nothing, /*return*/ Type.negativeone_or_nothing,
/*fd*/ Type.fd); /*fd*/ Type.fd);
libc.declareLazyFFI(SysFile, "fchmod",
"fchmod", ctypes.default_abi,
/*return*/ Type.negativeone_or_nothing,
/*fd*/ Type.fd,
/*mode*/ Type.mode_t);
libc.declareLazyFFI(SysFile, "fchown", libc.declareLazyFFI(SysFile, "fchown",
"fchown", ctypes.default_abi, "fchown", ctypes.default_abi,
/*return*/ Type.negativeone_or_nothing, /*return*/ Type.negativeone_or_nothing,

View File

@ -173,6 +173,27 @@
return new File.Info(gStatData, this._path); return new File.Info(gStatData, this._path);
}; };
/**
* Set the file's access permissions. Without any options, the
* permissions are set to an approximation of what they would
* have been if the file had been created in its current
* directory in the "most typical" fashion for the operating
* system. In the current implementation, this means we set
* the POSIX file mode to (0666 & ~umask).
*
* @param {*=} options
* - {number} unixMode If present, the POSIX file mode is set to
* exactly this value, unless |unixHonorUmask| is
* also present.
* - {bool} unixHonorUmask If true, any |unixMode| value is modified by
* the process umask, as open() would have done.
*/
File.prototype.setPermissions = function setPermissions(options = {}) {
throw_on_negative("setPermissions",
UnixFile.fchmod(this.fd, unixMode(options)),
this._path);
};
/** /**
* Set the last access and modification date of the file. * Set the last access and modification date of the file.
* The time stamp resolution is 1 second at best, but might be worse * The time stamp resolution is 1 second at best, but might be worse
@ -909,6 +930,28 @@
return new File.Info(gStatData, path); return new File.Info(gStatData, path);
}; };
/**
* Set the file's access permissions. Without any options, the
* permissions are set to an approximation of what they would
* have been if the file had been created in its current
* directory in the "most typical" fashion for the operating
* system. In the current implementation, this means we set
* the POSIX file mode to (0666 & ~umask).
*
* @param {string} path The name of the file to reset the permissions of.
* @param {*=} options
* - {number} unixMode If present, the POSIX file mode is set to
* exactly this value, unless |unixHonorUmask| is
* also present.
* - {bool} unixHonorUmask If true, any |unixMode| value is modified by
* the process umask, as open() would have done.
*/
File.setPermissions = function setPermissions(path, options = {}) {
throw_on_negative("setPermissions",
UnixFile.chmod(path, unixMode(options)),
path);
};
/** /**
* Convert an access date and a modification date to an array * Convert an access date and a modification date to an array
* of two |timeval|. * of two |timeval|.
@ -1111,6 +1154,25 @@
return date; return date;
}; };
/**
* Helper used by both versions of setPermissions.
*/
function unixMode(options) {
let mode = 438; /* 0666 */
let unixHonorUmask = true;
if ("unixMode" in options) {
unixHonorUmask = false;
mode = options.unixMode;
}
if ("unixHonorUmask" in options) {
unixHonorUmask = options.unixHonorUmask;
}
if (unixHonorUmask) {
mode &= ~SharedAll.Constants.Sys.umask;
}
return mode;
}
File.Unix = exports.OS.Unix.File; File.Unix = exports.OS.Unix.File;
File.Error = SysAll.Error; File.Error = SysAll.Error;
exports.OS.File = File; exports.OS.File = File;

View File

@ -234,6 +234,14 @@
this._path); this._path);
}; };
/**
* Set the file's access permission bits.
* Not implemented for Windows (bug 1022816).
*/
File.prototype.setPermissions = function setPermissions(options = {}) {
// do nothing
};
/** /**
* Flushes the file's buffers and causes all buffered data * Flushes the file's buffers and causes all buffered data
* to be written. * to be written.
@ -953,6 +961,14 @@
winDisposition: Const.OPEN_EXISTING winDisposition: Const.OPEN_EXISTING
}; };
/**
* Set the file's access permission bits.
* Not implemented for Windows (bug 1022816).
*/
File.setPermissions = function setPermissions(path, options = {}) {
// do nothing
};
/** /**
* Set the last access and modification date of the file. * Set the last access and modification date of the file.
* The time stamp resolution is 1 second at best, but might be worse * The time stamp resolution is 1 second at best, but might be worse

View File

@ -0,0 +1,123 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* A test to ensure that OS.File.setPermissions and
* OS.File.prototype.setPermissions are all working correctly.
* (see bug 1001849)
* These functions are currently Unix-specific. The manifest skips
* the test on Windows.
*/
/**
* Helper function for test logging: prints a POSIX file permission mode as an
* octal number, with a leading '0' per C (not JS) convention. When the
* numeric value is 0777 or lower, it is padded on the left with zeroes to
* four digits wide.
* Sample outputs: 0022, 0644, 04755.
*/
function format_mode(mode) {
if (mode <= 0o777) {
return ("0000" + mode.toString(8)).slice(-4);
} else {
return "0" + mode.toString(8);
}
}
/**
* Use this function to compare two mode values; it prints both values as
* octal numbers in the log.
*/
function do_check_modes_eq(left, right, text) {
text = text + ": " + format_mode(left) + " === " + format_mode(right);
do_report_result(left === right, text, Components.stack.caller, false);
}
const _umask = OS.Constants.Sys.umask;
do_print("umask: " + format_mode(_umask));
/**
* Compute the mode that a file should have after applying the umask,
* whatever it happens to be.
*/
function apply_umask(mode) {
return mode & ~_umask;
}
// Test application to paths.
add_task(function*() {
let path = OS.Path.join(OS.Constants.Path.tmpDir,
"test_osfile_async_setPerms_nonproto.tmp");
yield OS.File.writeAtomic(path, new Uint8Array(1));
try {
let stat;
yield OS.File.setPermissions(path, {unixMode: 0o4777});
stat = yield OS.File.stat(path);
do_check_modes_eq(stat.unixMode, 0o4777,
"setPermissions(path, 04777)");
yield OS.File.setPermissions(path, {unixMode: 0o4777,
unixHonorUmask: true});
stat = yield OS.File.stat(path);
do_check_modes_eq(stat.unixMode, apply_umask(0o4777),
"setPermissions(path, 04777&~umask)");
yield OS.File.setPermissions(path);
stat = yield OS.File.stat(path);
do_check_modes_eq(stat.unixMode, apply_umask(0o666),
"setPermissions(path, {})");
yield OS.File.setPermissions(path, {unixMode: 0});
stat = yield OS.File.stat(path);
do_check_modes_eq(stat.unixMode, 0,
"setPermissions(path, 0000)");
} finally {
yield OS.File.remove(path);
}
});
// Test application to open files.
add_task(function*() {
// First, create a file we can mess with.
let path = OS.Path.join(OS.Constants.Path.tmpDir,
"test_osfile_async_setDates_proto.tmp");
yield OS.File.writeAtomic(path, new Uint8Array(1));
try {
let fd = yield OS.File.open(path, {write: true});
let stat;
yield fd.setPermissions({unixMode: 0o4777});
stat = yield fd.stat();
do_check_modes_eq(stat.unixMode, 0o4777,
"fd.setPermissions(04777)");
yield fd.setPermissions({unixMode: 0o4777, unixHonorUmask: true});
stat = yield fd.stat();
do_check_modes_eq(stat.unixMode, apply_umask(0o4777),
"fd.setPermissions(04777&~umask)");
yield fd.setPermissions();
stat = yield fd.stat();
do_check_modes_eq(stat.unixMode, apply_umask(0o666),
"fd.setPermissions({})");
yield fd.setPermissions({unixMode: 0});
stat = yield fd.stat();
do_check_modes_eq(stat.unixMode, 0,
"fd.setPermissions(0000)");
yield fd.close();
} finally {
yield OS.File.remove(path);
}
});
function run_test() {
run_next_test();
}

View File

@ -36,3 +36,6 @@ support-files =
[test_queue.js] [test_queue.js]
[test_loader.js] [test_loader.js]
[test_constants.js] [test_constants.js]
[test_osfile_async_setPerms.js]
skip-if = os == 'win'