Bug 934283 - Add option to OS.File.makeDir to recursively make directories;r=froydnj

This commit is contained in:
David Rajchenbach-Teller 2014-03-28 17:33:36 -07:00
parent 24b2d6d7cd
commit 8b3b95d6a4
5 changed files with 163 additions and 28 deletions

View File

@ -971,18 +971,30 @@ File.remove = function remove(path) {
/** /**
* Create a directory. * Create a directory and, optionally, its parent directories.
* *
* @param {string} path The name of the directory. * @param {string} path The name of the directory.
* @param {*=} options Additional options. * @param {*=} options Additional options.
* Implementations may interpret the following fields:
* *
* - {C pointer} winSecurity If specified, security attributes * - {string} from If specified, the call to |makeDir| creates all the
* as per winapi function |CreateDirectory|. If unspecified, * ancestors of |path| that are descendants of |from|. Note that |path|
* use the default security descriptor, inherited from the * must be a descendant of |from|, and that |from| and its existing
* parent directory. * subdirectories present in |path| must be user-writeable.
* - {bool} ignoreExisting If |true|, do not fail if the * Example:
* directory already exists. * makeDir(Path.join(profileDir, "foo", "bar"), { from: profileDir });
* creates directories profileDir/foo, profileDir/foo/bar
* - {bool} ignoreExisting If |false|, throw an error if the directory
* already exists. |true| by default. Ignored if |from| is specified.
* - {number} unixMode Under Unix, if specified, a file creation mode,
* as per libc function |mkdir|. If unspecified, dirs are
* created with a default mode of 0700 (dir is private to
* the user, the user can read, write and execute). Ignored under Windows
* or if the file system does not support file creation modes.
* - {C pointer} winSecurity Under Windows, if specified, security
* attributes as per winapi function |CreateDirectory|. If
* unspecified, use the default security descriptor, inherited from
* the parent directory. Ignored under Unix or if the file system
* does not support security descriptors.
*/ */
File.makeDir = function makeDir(path, options) { File.makeDir = function makeDir(path, options) {
return Scheduler.post("makeDir", return Scheduler.post("makeDir",

View File

@ -16,6 +16,7 @@ if (typeof Components != "undefined") {
let SharedAll = let SharedAll =
require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm"); require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm");
let Path = require("resource://gre/modules/osfile/ospath.jsm");
let Lz4 = let Lz4 =
require("resource://gre/modules/workers/lz4.js"); require("resource://gre/modules/workers/lz4.js");
let LOG = SharedAll.LOG.bind(SharedAll, "Shared front-end"); let LOG = SharedAll.LOG.bind(SharedAll, "Shared front-end");
@ -154,8 +155,8 @@ AbstractFile.openUnique = function openUnique(path, options = {}) {
create : true create : true
}; };
let dirName = OS.Path.dirname(path); let dirName = Path.dirname(path);
let leafName = OS.Path.basename(path); let leafName = Path.basename(path);
let lastDotCharacter = leafName.lastIndexOf('.'); let lastDotCharacter = leafName.lastIndexOf('.');
let fileName = leafName.substring(0, lastDotCharacter != -1 ? lastDotCharacter : leafName.length); let fileName = leafName.substring(0, lastDotCharacter != -1 ? lastDotCharacter : leafName.length);
let suffix = (lastDotCharacter != -1 ? leafName.substring(lastDotCharacter) : ""); let suffix = (lastDotCharacter != -1 ? leafName.substring(lastDotCharacter) : "");
@ -175,10 +176,10 @@ AbstractFile.openUnique = function openUnique(path, options = {}) {
for (let i = 0; i < maxAttempts; ++i) { for (let i = 0; i < maxAttempts; ++i) {
try { try {
if (humanReadable) { if (humanReadable) {
uniquePath = OS.Path.join(dirName, fileName + "-" + (i + 1) + suffix); uniquePath = Path.join(dirName, fileName + "-" + (i + 1) + suffix);
} else { } else {
let hexNumber = Math.floor(Math.random() * MAX_HEX_NUMBER).toString(HEX_RADIX); let hexNumber = Math.floor(Math.random() * MAX_HEX_NUMBER).toString(HEX_RADIX);
uniquePath = OS.Path.join(dirName, fileName + "-" + hexNumber + suffix); uniquePath = Path.join(dirName, fileName + "-" + hexNumber + suffix);
} }
return { return {
path: uniquePath, path: uniquePath,
@ -521,6 +522,53 @@ AbstractFile.removeRecursive = function(path, options = {}) {
OS.File.removeEmptyDir(path); OS.File.removeEmptyDir(path);
}; };
/**
* Create a directory and, optionally, its parent directories.
*
* @param {string} path The name of the directory.
* @param {*=} options Additional options.
*
* - {string} from If specified, the call to |makeDir| creates all the
* ancestors of |path| that are descendants of |from|. Note that |path|
* must be a descendant of |from|, and that |from| and its existing
* subdirectories present in |path| must be user-writeable.
* Example:
* makeDir(Path.join(profileDir, "foo", "bar"), { from: profileDir });
* creates directories profileDir/foo, profileDir/foo/bar
* - {bool} ignoreExisting If |false|, throw an error if the directory
* already exists. |true| by default. Ignored if |from| is specified.
* - {number} unixMode Under Unix, if specified, a file creation mode,
* as per libc function |mkdir|. If unspecified, dirs are
* created with a default mode of 0700 (dir is private to
* the user, the user can read, write and execute). Ignored under Windows
* or if the file system does not support file creation modes.
* - {C pointer} winSecurity Under Windows, if specified, security
* attributes as per winapi function |CreateDirectory|. If
* unspecified, use the default security descriptor, inherited from
* the parent directory. Ignored under Unix or if the file system
* does not support security descriptors.
*/
AbstractFile.makeDir = function(path, options = {}) {
if (!options.from) {
return OS.File._makeDir(path, options);
}
if (!path.startsWith(options.from)) {
throw new Error("Incorrect use of option |from|: " + path + " is not a descendant of " + options.from);
}
let innerOptions = Object.create(options, {
ignoreExisting: {
value: true
}
});
// Compute the elements that appear in |path| but not in |options.from|.
let items = Path.split(path).components.slice(Path.split(options.from).components.length);
let current = options.from;
for (let item of items) {
current = Path.join(current, item);
OS.File._makeDir(current, innerOptions);
}
};
if (!exports.OS.Shared) { if (!exports.OS.Shared) {
exports.OS.Shared = {}; exports.OS.Shared = {};
} }

View File

@ -396,8 +396,15 @@
* the user, the user can read, write and execute). * the user, the user can read, write and execute).
* - {bool} ignoreExisting If |false|, throw error if the directory * - {bool} ignoreExisting If |false|, throw error if the directory
* already exists. |true| by default * already exists. |true| by default
*/ * - {string} from If specified, the call to |makeDir| creates all the
File.makeDir = function makeDir(path, options = {}) { * ancestors of |path| that are descendants of |from|. Note that |from|
* and its existing descendants must be user-writeable and that |path|
* must be a descendant of |from|.
* Example:
* makeDir(Path.join(profileDir, "foo", "bar"), { from: profileDir });
* creates directories profileDir/foo, profileDir/foo/bar
*/
File._makeDir = function makeDir(path, options = {}) {
let omode = options.unixMode !== undefined ? options.unixMode : DEFAULT_UNIX_MODE_DIR; let omode = options.unixMode !== undefined ? options.unixMode : DEFAULT_UNIX_MODE_DIR;
let result = UnixFile.mkdir(path, omode); let result = UnixFile.mkdir(path, omode);
if (result == -1) { if (result == -1) {
@ -935,6 +942,7 @@
File.read = exports.OS.Shared.AbstractFile.read; File.read = exports.OS.Shared.AbstractFile.read;
File.writeAtomic = exports.OS.Shared.AbstractFile.writeAtomic; File.writeAtomic = exports.OS.Shared.AbstractFile.writeAtomic;
File.openUnique = exports.OS.Shared.AbstractFile.openUnique; File.openUnique = exports.OS.Shared.AbstractFile.openUnique;
File.makeDir = exports.OS.Shared.AbstractFile.makeDir;
/** /**
* Remove an existing directory and its contents. * Remove an existing directory and its contents.

View File

@ -433,7 +433,7 @@
}; };
/** /**
* Create a directory. * Create a directory and, optionally, its parent directories.
* *
* @param {string} path The name of the directory. * @param {string} path The name of the directory.
* @param {*=} options Additional options. This * @param {*=} options Additional options. This
@ -445,8 +445,15 @@
* parent directory. * parent directory.
* - {bool} ignoreExisting If |false|, throw an error if the directory * - {bool} ignoreExisting If |false|, throw an error if the directory
* already exists. |true| by default * already exists. |true| by default
*/ * - {string} from If specified, the call to |makeDir| creates all the
File.makeDir = function makeDir(path, options = {}) { * ancestors of |path| that are descendants of |from|. Note that |from|
* and its existing descendants must be user-writeable and that |path|
* must be a descendant of |from|.
* Example:
* makeDir(Path.join(profileDir, "foo", "bar"), { from: profileDir });
* creates directories profileDir/foo, profileDir/foo/bar
*/
File._makeDir = function makeDir(path, options = {}) {
let security = options.winSecurity || null; let security = options.winSecurity || null;
let result = WinFile.CreateDirectory(path, security); let result = WinFile.CreateDirectory(path, security);
@ -974,6 +981,7 @@
File.read = exports.OS.Shared.AbstractFile.read; File.read = exports.OS.Shared.AbstractFile.read;
File.writeAtomic = exports.OS.Shared.AbstractFile.writeAtomic; File.writeAtomic = exports.OS.Shared.AbstractFile.writeAtomic;
File.openUnique = exports.OS.Shared.AbstractFile.openUnique; File.openUnique = exports.OS.Shared.AbstractFile.openUnique;
File.makeDir = exports.OS.Shared.AbstractFile.makeDir;
/** /**
* Remove an existing directory and its contents. * Remove an existing directory and its contents.

View File

@ -7,25 +7,35 @@
Components.utils.import("resource://gre/modules/osfile.jsm"); Components.utils.import("resource://gre/modules/osfile.jsm");
Components.utils.import("resource://gre/modules/Services.jsm"); Components.utils.import("resource://gre/modules/Services.jsm");
let Path = OS.Path;
let profileDir;
do_register_cleanup(function() { do_register_cleanup(function() {
Services.prefs.setBoolPref("toolkit.osfile.log", false); Services.prefs.setBoolPref("toolkit.osfile.log", false);
}); });
function run_test() { function run_test() {
Services.prefs.setBoolPref("toolkit.osfile.log", true);
run_next_test(); run_next_test();
} }
/** /**
* Test OS.File.makeDir * Test OS.File.makeDir
*/ */
add_task(function() {
add_task(function init() {
// Set up profile. We create the directory in the profile, because the profile // Set up profile. We create the directory in the profile, because the profile
// is removed after every test run. // is removed after every test run.
do_get_profile(); do_get_profile();
profileDir = OS.Constants.Path.profileDir;
Services.prefs.setBoolPref("toolkit.osfile.log", true);
});
let dir = OS.Path.join(OS.Constants.Path.profileDir, "directory"); /**
* Basic use
*/
add_task(function* test_basic() {
let dir = Path.join(profileDir, "directory");
// Sanity checking for the test // Sanity checking for the test
do_check_false((yield OS.File.exists(dir))); do_check_false((yield OS.File.exists(dir)));
@ -36,7 +46,51 @@ add_task(function() {
//check if the directory exists //check if the directory exists
yield OS.File.stat(dir); yield OS.File.stat(dir);
// Make a directory that already exists // Make a directory that already exists, this should succeed
yield OS.File.makeDir(dir);
// Make a directory with ignoreExisting
yield OS.File.makeDir(dir, {ignoreExisting: true});
// Make a directory with ignoreExisting false
let exception = null;
try {
yield OS.File.makeDir(dir, {ignoreExisting: false});
} catch (ex) {
exception = ex;
}
do_check_true(!!exception);
do_check_true(exception instanceof OS.File.Error);
do_check_true(exception.becauseExists);
});
// Make a root directory that already exists
add_task(function* test_root() {
if (OS.Constants.Win) {
yield OS.File.makeDir("C:");
yield OS.File.makeDir("C:\\");
} else {
yield OS.File.makeDir("/");
}
});
/**
* Creating subdirectories
*/
add_task(function test_option_from() {
let dir = Path.join(profileDir, "a", "b", "c");
// Sanity checking for the test
do_check_false((yield OS.File.exists(dir)));
// Make a directory
yield OS.File.makeDir(dir, {from: profileDir});
//check if the directory exists
yield OS.File.stat(dir);
// Make a directory that already exists, this should succeed
yield OS.File.makeDir(dir); yield OS.File.makeDir(dir);
// Make a directory with ignoreExisting // Make a directory with ignoreExisting
@ -54,11 +108,16 @@ add_task(function() {
do_check_true(exception instanceof OS.File.Error); do_check_true(exception instanceof OS.File.Error);
do_check_true(exception.becauseExists); do_check_true(exception.becauseExists);
// Make a root directory that already exists // Make a directory without |from| and fail
if (OS.Constants.Win) { let dir2 = Path.join(profileDir, "g", "h", "i");
yield OS.File.makeDir("C:"); exception = null;
yield OS.File.makeDir("C:\\"); try {
} else { yield OS.File.makeDir(dir2);
yield OS.File.makeDir("/"); } catch (ex) {
exception = ex;
} }
do_check_true(!!exception);
do_check_true(exception instanceof OS.File.Error);
do_check_true(exception.becauseNoSuchFile);
}); });