зеркало из https://github.com/mozilla/gecko-dev.git
Bug 967507 - [OS.File] Add |path| for error-reporting. r=Yoric
This commit is contained in:
Родитель
8453de76b1
Коммит
78963dfcf5
|
@ -701,6 +701,7 @@ static const dom::ConstantSpec gWinProperties[] =
|
|||
INT_CONSTANT(DACL_SECURITY_INFORMATION),
|
||||
|
||||
// Errors
|
||||
INT_CONSTANT(ERROR_INVALID_HANDLE),
|
||||
INT_CONSTANT(ERROR_ACCESS_DENIED),
|
||||
INT_CONSTANT(ERROR_DIR_NOT_EMPTY),
|
||||
INT_CONSTANT(ERROR_FILE_EXISTS),
|
||||
|
|
|
@ -1238,12 +1238,15 @@ exports.normalizeToPointer = normalizeToPointer;
|
|||
* codes provided by subclasses of |OS.Shared.Error|.
|
||||
*
|
||||
* @param {string} operation The operation that failed.
|
||||
* @param {string=} path The path of the file on which the operation failed,
|
||||
* or nothing if there was no file involved in the failure.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function OSError(operation) {
|
||||
function OSError(operation, path = "") {
|
||||
Error.call(this);
|
||||
this.operation = operation;
|
||||
this.path = path;
|
||||
}
|
||||
exports.OSError = OSError;
|
||||
|
||||
|
|
|
@ -25,10 +25,15 @@ let clone = SharedAll.clone;
|
|||
* Code shared by implementations of File.
|
||||
*
|
||||
* @param {*} fd An OS-specific file handle.
|
||||
* @param {string} path File path of the file handle, used for error-reporting.
|
||||
* @constructor
|
||||
*/
|
||||
let AbstractFile = function AbstractFile(fd) {
|
||||
let AbstractFile = function AbstractFile(fd, path) {
|
||||
this._fd = fd;
|
||||
if (!path) {
|
||||
throw new TypeError("path is expected");
|
||||
}
|
||||
this._path = path;
|
||||
};
|
||||
|
||||
AbstractFile.prototype = {
|
||||
|
@ -41,7 +46,7 @@ AbstractFile.prototype = {
|
|||
if (this._fd) {
|
||||
return this._fd;
|
||||
}
|
||||
throw OS.File.Error.closed();
|
||||
throw OS.File.Error.closed("accessing file", this._path);
|
||||
},
|
||||
/**
|
||||
* Read bytes from this file to a new buffer.
|
||||
|
@ -183,7 +188,7 @@ AbstractFile.openUnique = function openUnique(path, options = {}) {
|
|||
// keep trying ...
|
||||
}
|
||||
}
|
||||
throw OS.File.Error.exists("could not find an unused file name.");
|
||||
throw OS.File.Error.exists("could not find an unused file name.", path);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -395,7 +400,7 @@ AbstractFile.writeAtomic =
|
|||
}
|
||||
let noOverwrite = options.noOverwrite;
|
||||
if (noOverwrite && OS.File.exists(path)) {
|
||||
throw OS.File.Error.exists("writeAtomic");
|
||||
throw OS.File.Error.exists("writeAtomic", path);
|
||||
}
|
||||
|
||||
if (typeof buffer == "string") {
|
||||
|
|
|
@ -73,19 +73,23 @@ libc.declareLazy(LazyBindings, "strerror",
|
|||
* @param {number=} lastError The OS-specific constant detailing the
|
||||
* reason of the error. If unspecified, this is fetched from the system
|
||||
* status.
|
||||
* @param {string=} path The file path that manipulated. If unspecified,
|
||||
* assign the empty string.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {OS.Shared.Error}
|
||||
*/
|
||||
let OSError = function OSError(operation, errno) {
|
||||
operation = operation || "unknown operation";
|
||||
SharedAll.OSError.call(this, operation);
|
||||
this.unixErrno = errno || ctypes.errno;
|
||||
let OSError = function OSError(operation = "unknown operation",
|
||||
errno = ctypes.errno, path = "") {
|
||||
operation = operation;
|
||||
SharedAll.OSError.call(this, operation, path);
|
||||
this.unixErrno = errno;
|
||||
};
|
||||
OSError.prototype = Object.create(SharedAll.OSError.prototype);
|
||||
OSError.prototype.toString = function toString() {
|
||||
return "Unix error " + this.unixErrno +
|
||||
" during operation " + this.operation +
|
||||
(this.path? " on file " + this.path : "") +
|
||||
" (" + LazyBindings.strerror(this.unixErrno).readString() + ")";
|
||||
};
|
||||
|
||||
|
@ -143,7 +147,8 @@ Object.defineProperty(OSError.prototype, "becauseAccessDenied", {
|
|||
OSError.toMsg = function toMsg(error) {
|
||||
return {
|
||||
operation: error.operation,
|
||||
unixErrno: error.unixErrno
|
||||
unixErrno: error.unixErrno,
|
||||
path: error.path
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -151,7 +156,7 @@ OSError.toMsg = function toMsg(error) {
|
|||
* Deserialize a message back to an instance of OSError
|
||||
*/
|
||||
OSError.fromMsg = function fromMsg(msg) {
|
||||
return new OSError(msg.operation, msg.unixErrno);
|
||||
return new OSError(msg.operation, msg.unixErrno, msg.path);
|
||||
};
|
||||
exports.Error = OSError;
|
||||
|
||||
|
@ -160,9 +165,10 @@ exports.Error = OSError;
|
|||
*
|
||||
* @constructor
|
||||
*/
|
||||
let AbstractInfo = function AbstractInfo(isDir, isSymLink, size, lastAccessDate,
|
||||
let AbstractInfo = function AbstractInfo(path, isDir, isSymLink, size, lastAccessDate,
|
||||
lastModificationDate, unixLastStatusChangeDate,
|
||||
unixOwner, unixGroup, unixMode) {
|
||||
this._path = path;
|
||||
this._isDir = isDir;
|
||||
this._isSymlLink = isSymLink;
|
||||
this._size = size;
|
||||
|
@ -175,6 +181,14 @@ let AbstractInfo = function AbstractInfo(isDir, isSymLink, size, lastAccessDate,
|
|||
};
|
||||
|
||||
AbstractInfo.prototype = {
|
||||
/**
|
||||
* The path of the file, used for error-reporting.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get path() {
|
||||
return this._path;
|
||||
},
|
||||
/**
|
||||
* |true| if this file is a directory, |false| otherwise
|
||||
*/
|
||||
|
@ -305,16 +319,16 @@ Type.path = Type.cstring.withName("[in] path");
|
|||
Type.out_path = Type.out_cstring.withName("[out] path");
|
||||
|
||||
// Special constructors that need to be defined on all threads
|
||||
OSError.closed = function closed(operation) {
|
||||
return new OSError(operation, Const.EBADF);
|
||||
OSError.closed = function closed(operation, path) {
|
||||
return new OSError(operation, Const.EBADF, path);
|
||||
};
|
||||
|
||||
OSError.exists = function exists(operation) {
|
||||
return new OSError(operation, Const.EEXIST);
|
||||
OSError.exists = function exists(operation, path) {
|
||||
return new OSError(operation, Const.EEXIST, path);
|
||||
};
|
||||
|
||||
OSError.noSuchFile = function noSuchFile(operation) {
|
||||
return new OSError(operation, Const.ENOENT);
|
||||
OSError.noSuchFile = function noSuchFile(operation, path) {
|
||||
return new OSError(operation, Const.ENOENT, path);
|
||||
};
|
||||
|
||||
let EXPORTED_SYMBOLS = [
|
||||
|
|
|
@ -40,10 +40,11 @@
|
|||
* to open a file, use function |OS.File.open|.
|
||||
*
|
||||
* @param fd A OS-specific file descriptor.
|
||||
* @param {string} path File path of the file handle, used for error-reporting.
|
||||
* @constructor
|
||||
*/
|
||||
let File = function File(fd) {
|
||||
exports.OS.Shared.AbstractFile.call(this, fd);
|
||||
let File = function File(fd, path) {
|
||||
exports.OS.Shared.AbstractFile.call(this, fd, path);
|
||||
this._closeResult = null;
|
||||
};
|
||||
File.prototype = Object.create(exports.OS.Shared.AbstractFile.prototype);
|
||||
|
@ -70,7 +71,7 @@
|
|||
fd.forget();
|
||||
}
|
||||
if (result == -1) {
|
||||
this._closeResult = new File.Error("close");
|
||||
this._closeResult = new File.Error("close", ctypes.errno, this._path);
|
||||
}
|
||||
}
|
||||
if (this._closeResult) {
|
||||
|
@ -103,7 +104,8 @@
|
|||
OS.Constants.libc.POSIX_FADV_SEQUENTIAL);
|
||||
}
|
||||
return throw_on_negative("read",
|
||||
UnixFile.read(this.fd, buffer, nbytes)
|
||||
UnixFile.read(this.fd, buffer, nbytes),
|
||||
this._path
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -122,7 +124,8 @@
|
|||
*/
|
||||
File.prototype._write = function _write(buffer, nbytes, options = {}) {
|
||||
return throw_on_negative("write",
|
||||
UnixFile.write(this.fd, buffer, nbytes)
|
||||
UnixFile.write(this.fd, buffer, nbytes),
|
||||
this._path
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -154,7 +157,8 @@
|
|||
whence = Const.SEEK_SET;
|
||||
}
|
||||
return throw_on_negative("setPosition",
|
||||
UnixFile.lseek(this.fd, pos, whence)
|
||||
UnixFile.lseek(this.fd, pos, whence),
|
||||
this._path
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -164,8 +168,9 @@
|
|||
* @return File.Info The information on |this| file.
|
||||
*/
|
||||
File.prototype.stat = function stat() {
|
||||
throw_on_negative("stat", UnixFile.fstat(this.fd, gStatDataPtr));
|
||||
return new File.Info(gStatData);
|
||||
throw_on_negative("stat", UnixFile.fstat(this.fd, gStatDataPtr),
|
||||
this._path);
|
||||
return new File.Info(gStatData, this._path);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -192,7 +197,8 @@
|
|||
gTimevals[1].tv_sec = (modificationDate / 1000) | 0;
|
||||
gTimevals[1].tv_usec = 0;
|
||||
throw_on_negative("setDates",
|
||||
UnixFile.futimes(this.fd, gTimevalsPtr));
|
||||
UnixFile.futimes(this.fd, gTimevalsPtr),
|
||||
this._path);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -207,7 +213,7 @@
|
|||
* @throws {OS.File.Error} In case of I/O error.
|
||||
*/
|
||||
File.prototype.flush = function flush() {
|
||||
throw_on_negative("flush", UnixFile.fsync(this.fd));
|
||||
throw_on_negative("flush", UnixFile.fsync(this.fd), this._path);
|
||||
};
|
||||
|
||||
// The default unix mode for opening (0600)
|
||||
|
@ -293,7 +299,7 @@
|
|||
flags |= Const.O_APPEND;
|
||||
}
|
||||
}
|
||||
return error_or_file(UnixFile.open(path, flags, omode));
|
||||
return error_or_file(UnixFile.open(path, flags, omode), path);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -328,7 +334,7 @@
|
|||
ctypes.errno == Const.ENOENT) {
|
||||
return;
|
||||
}
|
||||
throw new File.Error("remove");
|
||||
throw new File.Error("remove", ctypes.errno, path);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -347,7 +353,7 @@
|
|||
ctypes.errno == Const.ENOENT) {
|
||||
return;
|
||||
}
|
||||
throw new File.Error("removeEmptyDir");
|
||||
throw new File.Error("removeEmptyDir", ctypes.errno, path);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -399,7 +405,7 @@
|
|||
(ctypes.errno == Const.EEXIST || ctypes.errno == Const.EISDIR)) {
|
||||
return;
|
||||
}
|
||||
throw new File.Error("makeDir");
|
||||
throw new File.Error("makeDir", ctypes.errno, path);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -466,7 +472,8 @@
|
|||
flags |= Const.COPYFILE_EXCL;
|
||||
}
|
||||
throw_on_negative("copy",
|
||||
UnixFile.copyfile(sourcePath, destPath, null, flags)
|
||||
UnixFile.copyfile(sourcePath, destPath, null, flags),
|
||||
sourcePath
|
||||
);
|
||||
};
|
||||
} else {
|
||||
|
@ -644,10 +651,10 @@
|
|||
if (fd != -1) {
|
||||
fd.dispose();
|
||||
// The file exists and we have access
|
||||
throw new File.Error("move", Const.EEXIST);
|
||||
throw new File.Error("move", Const.EEXIST, sourcePath);
|
||||
} else if (ctypes.errno == Const.EACCESS) {
|
||||
// The file exists and we don't have access
|
||||
throw new File.Error("move", Const.EEXIST);
|
||||
throw new File.Error("move", Const.EEXIST, sourcePath);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -661,7 +668,7 @@
|
|||
// that prevents us from crossing devices, throw the
|
||||
// error.
|
||||
if (ctypes.errno != Const.EXDEV || options.noCopy) {
|
||||
throw new File.Error("move");
|
||||
throw new File.Error("move", ctypes.errno, sourcePath);
|
||||
}
|
||||
|
||||
// Otherwise, copy and remove.
|
||||
|
@ -689,7 +696,7 @@
|
|||
if (this._dir == null) {
|
||||
let error = ctypes.errno;
|
||||
if (error != Const.ENOENT) {
|
||||
throw new File.Error("DirectoryIterator", error);
|
||||
throw new File.Error("DirectoryIterator", error, path);
|
||||
}
|
||||
this._exists = false;
|
||||
this._closed = true;
|
||||
|
@ -712,7 +719,7 @@
|
|||
*/
|
||||
File.DirectoryIterator.prototype.next = function next() {
|
||||
if (!this._exists) {
|
||||
throw File.Error.noSuchFile("DirectoryIterator.prototype.next");
|
||||
throw File.Error.noSuchFile("DirectoryIterator.prototype.next", this._path);
|
||||
}
|
||||
if (this._closed) {
|
||||
throw StopIteration;
|
||||
|
@ -730,7 +737,7 @@
|
|||
if (!("d_type" in contents)) {
|
||||
// |dirent| doesn't have d_type on some platforms (e.g. Solaris).
|
||||
let path = Path.join(this._path, name);
|
||||
throw_on_negative("lstat", UnixFile.lstat(path, gStatDataPtr));
|
||||
throw_on_negative("lstat", UnixFile.lstat(path, gStatDataPtr), this._path);
|
||||
isDir = (gStatData.st_mode & Const.S_IFMT) == Const.S_IFDIR;
|
||||
isSymLink = (gStatData.st_mode & Const.S_IFMT) == Const.S_IFLNK;
|
||||
} else {
|
||||
|
@ -768,8 +775,8 @@
|
|||
* Return directory as |File|
|
||||
*/
|
||||
File.DirectoryIterator.prototype.unixAsFile = function unixAsFile() {
|
||||
if (!this._dir) throw File.Error.closed();
|
||||
return error_or_file(UnixFile.dirfd(this._dir));
|
||||
if (!this._dir) throw File.Error.closed("unixAsFile", this._path);
|
||||
return error_or_file(UnixFile.dirfd(this._dir), this._path);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -810,7 +817,7 @@
|
|||
let gTimevals = new Type.timevals.implementation();
|
||||
let gTimevalsPtr = gTimevals.address();
|
||||
let MODE_MASK = 4095 /*= 07777*/;
|
||||
File.Info = function Info(stat) {
|
||||
File.Info = function Info(stat, path) {
|
||||
let isDir = (stat.st_mode & Const.S_IFMT) == Const.S_IFDIR;
|
||||
let isSymLink = (stat.st_mode & Const.S_IFMT) == Const.S_IFLNK;
|
||||
let size = Type.off_t.importFromC(stat.st_size);
|
||||
|
@ -823,8 +830,8 @@
|
|||
let unixGroup = Type.gid_t.importFromC(stat.st_gid);
|
||||
let unixMode = Type.mode_t.importFromC(stat.st_mode & MODE_MASK);
|
||||
|
||||
SysAll.AbstractInfo.call(this, isDir, isSymLink, size, lastAccessDate,
|
||||
lastModificationDate, unixLastStatusChangeDate,
|
||||
SysAll.AbstractInfo.call(this, path, isDir, isSymLink, size,
|
||||
lastAccessDate, lastModificationDate, unixLastStatusChangeDate,
|
||||
unixOwner, unixGroup, unixMode);
|
||||
|
||||
// Some platforms (e.g. MacOS X, some BSDs) store a file creation date
|
||||
|
@ -885,11 +892,11 @@
|
|||
*/
|
||||
File.stat = function stat(path, options = {}) {
|
||||
if (options.unixNoFollowingLinks) {
|
||||
throw_on_negative("stat", UnixFile.lstat(path, gStatDataPtr));
|
||||
throw_on_negative("stat", UnixFile.lstat(path, gStatDataPtr), path);
|
||||
} else {
|
||||
throw_on_negative("stat", UnixFile.stat(path, gStatDataPtr));
|
||||
throw_on_negative("stat", UnixFile.stat(path, gStatDataPtr), path);
|
||||
}
|
||||
return new File.Info(gStatData);
|
||||
return new File.Info(gStatData, path);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -916,7 +923,8 @@
|
|||
gTimevals[1].tv_sec = (modificationDate / 1000) | 0;
|
||||
gTimevals[1].tv_usec = 0;
|
||||
throw_on_negative("setDates",
|
||||
UnixFile.utimes(path, gTimevalsPtr));
|
||||
UnixFile.utimes(path, gTimevalsPtr),
|
||||
path);
|
||||
};
|
||||
|
||||
File.read = exports.OS.Shared.AbstractFile.read;
|
||||
|
@ -930,7 +938,7 @@
|
|||
File.getCurrentDirectory = function getCurrentDirectory() {
|
||||
let path = UnixFile.get_current_dir_name?UnixFile.get_current_dir_name():
|
||||
UnixFile.getwd_auto(null);
|
||||
throw_on_null("getCurrentDirectory",path);
|
||||
throw_on_null("getCurrentDirectory", path);
|
||||
return path.readString();
|
||||
};
|
||||
|
||||
|
@ -939,7 +947,8 @@
|
|||
*/
|
||||
File.setCurrentDirectory = function setCurrentDirectory(path) {
|
||||
throw_on_negative("setCurrentDirectory",
|
||||
UnixFile.chdir(path)
|
||||
UnixFile.chdir(path),
|
||||
path
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -960,33 +969,48 @@
|
|||
|
||||
/**
|
||||
* Turn the result of |open| into an Error or a File
|
||||
* @param {number} maybe The result of the |open| operation that may
|
||||
* represent either an error or a success. If -1, this function raises
|
||||
* an error holding ctypes.errno, otherwise it returns the opened file.
|
||||
* @param {string=} path The path of the file.
|
||||
*/
|
||||
function error_or_file(maybe) {
|
||||
function error_or_file(maybe, path) {
|
||||
if (maybe == -1) {
|
||||
throw new File.Error("open");
|
||||
throw new File.Error("open", ctypes.errno, path);
|
||||
}
|
||||
return new File(maybe);
|
||||
return new File(maybe, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to sort errors represented as "-1" from successes.
|
||||
*
|
||||
* @param {string=} operation The name of the operation. If unspecified,
|
||||
* the name of the caller function.
|
||||
* @param {number} result The result of the operation that may
|
||||
* represent either an error or a success. If -1, this function raises
|
||||
* an error holding ctypes.errno, otherwise it returns |result|.
|
||||
* @param {string=} operation The name of the operation. If unspecified,
|
||||
* the name of the caller function.
|
||||
* @param {string=} path The path of the file.
|
||||
*/
|
||||
function throw_on_negative(operation, result) {
|
||||
function throw_on_negative(operation, result, path) {
|
||||
if (result < 0) {
|
||||
throw new File.Error(operation);
|
||||
throw new File.Error(operation, ctypes.errno, path);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function throw_on_null(operation, result) {
|
||||
/**
|
||||
* Utility function to sort errors represented as |null| from successes.
|
||||
*
|
||||
* @param {string=} operation The name of the operation. If unspecified,
|
||||
* the name of the caller function.
|
||||
* @param {pointer} result The result of the operation that may
|
||||
* represent either an error or a success. If |null|, this function raises
|
||||
* an error holding ctypes.errno, otherwise it returns |result|.
|
||||
* @param {string=} path The path of the file.
|
||||
*/
|
||||
function throw_on_null(operation, result, path) {
|
||||
if (result == null || (result.isNull && result.isNull())) {
|
||||
throw new File.Error(operation);
|
||||
throw new File.Error(operation, ctypes.errno, path);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -79,14 +79,17 @@ libc.declareLazy(Scope, "FormatMessage",
|
|||
* @param {number=} lastError The OS-specific constant detailing the
|
||||
* reason of the error. If unspecified, this is fetched from the system
|
||||
* status.
|
||||
* @param {string=} path The file path that manipulated. If unspecified,
|
||||
* assign the empty string.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {OS.Shared.Error}
|
||||
*/
|
||||
let OSError = function OSError(operation, lastError) {
|
||||
operation = operation || "unknown operation";
|
||||
SharedAll.OSError.call(this, operation);
|
||||
this.winLastError = lastError || ctypes.winLastError;
|
||||
let OSError = function OSError(operation = "unknown operation",
|
||||
lastError = ctypes.winLastError, path = "") {
|
||||
operation = operation;
|
||||
SharedAll.OSError.call(this, operation, path);
|
||||
this.winLastError = lastError;
|
||||
};
|
||||
OSError.prototype = Object.create(SharedAll.OSError.prototype);
|
||||
OSError.prototype.toString = function toString() {
|
||||
|
@ -107,7 +110,8 @@ OSError.prototype.toString = function toString() {
|
|||
" while fetching system error message";
|
||||
}
|
||||
return "Win error " + this.winLastError + " during operation "
|
||||
+ this.operation + " (" + buf.readString() + ")";
|
||||
+ this.operation + (this.path? " on file " + this.path : "") +
|
||||
" (" + buf.readString() + ")";
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -165,7 +169,8 @@ Object.defineProperty(OSError.prototype, "becauseAccessDenied", {
|
|||
OSError.toMsg = function toMsg(error) {
|
||||
return {
|
||||
operation: error.operation,
|
||||
winLastError: error.winLastError
|
||||
winLastError: error.winLastError,
|
||||
path: error.path
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -173,7 +178,7 @@ OSError.toMsg = function toMsg(error) {
|
|||
* Deserialize a message back to an instance of OSError
|
||||
*/
|
||||
OSError.fromMsg = function fromMsg(msg) {
|
||||
return new OSError(msg.operation, msg.winLastError);
|
||||
return new OSError(msg.operation, msg.winLastError, msg.path);
|
||||
};
|
||||
exports.Error = OSError;
|
||||
|
||||
|
@ -182,8 +187,10 @@ exports.Error = OSError;
|
|||
*
|
||||
* @constructor
|
||||
*/
|
||||
let AbstractInfo = function AbstractInfo(isDir, isSymLink, size, winBirthDate,
|
||||
let AbstractInfo = function AbstractInfo(path, isDir, isSymLink, size,
|
||||
winBirthDate,
|
||||
lastAccessDate, lastWriteDate) {
|
||||
this._path = path;
|
||||
this._isDir = isDir;
|
||||
this._isSymLink = isSymLink;
|
||||
this._size = size;
|
||||
|
@ -193,6 +200,14 @@ let AbstractInfo = function AbstractInfo(isDir, isSymLink, size, winBirthDate,
|
|||
};
|
||||
|
||||
AbstractInfo.prototype = {
|
||||
/**
|
||||
* The path of the file, used for error-reporting.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get path() {
|
||||
return this._path;
|
||||
},
|
||||
/**
|
||||
* |true| if this file is a directory, |false| otherwise
|
||||
*/
|
||||
|
@ -341,16 +356,16 @@ Type.path = Type.wstring.withName("[in] path");
|
|||
Type.out_path = Type.out_wstring.withName("[out] path");
|
||||
|
||||
// Special constructors that need to be defined on all threads
|
||||
OSError.closed = function closed(operation) {
|
||||
return new OSError(operation, Const.ERROR_INVALID_HANDLE);
|
||||
OSError.closed = function closed(operation, path) {
|
||||
return new OSError(operation, Const.ERROR_INVALID_HANDLE, path);
|
||||
};
|
||||
|
||||
OSError.exists = function exists(operation) {
|
||||
return new OSError(operation, Const.ERROR_FILE_EXISTS);
|
||||
OSError.exists = function exists(operation, path) {
|
||||
return new OSError(operation, Const.ERROR_FILE_EXISTS, path);
|
||||
};
|
||||
|
||||
OSError.noSuchFile = function noSuchFile(operation) {
|
||||
return new OSError(operation, Const.ERROR_FILE_NOT_FOUND);
|
||||
OSError.noSuchFile = function noSuchFile(operation, path) {
|
||||
return new OSError(operation, Const.ERROR_FILE_NOT_FOUND, path);
|
||||
};
|
||||
|
||||
let EXPORTED_SYMBOLS = [
|
||||
|
|
|
@ -58,10 +58,11 @@
|
|||
* to open a file, use function |OS.File.open|.
|
||||
*
|
||||
* @param fd A OS-specific file descriptor.
|
||||
* @param {string} path File path of the file handle, used for error-reporting.
|
||||
* @constructor
|
||||
*/
|
||||
let File = function File(fd) {
|
||||
exports.OS.Shared.AbstractFile.call(this, fd);
|
||||
let File = function File(fd, path) {
|
||||
exports.OS.Shared.AbstractFile.call(this, fd, path);
|
||||
this._closeResult = null;
|
||||
};
|
||||
File.prototype = Object.create(exports.OS.Shared.AbstractFile.prototype);
|
||||
|
@ -88,7 +89,7 @@
|
|||
fd.forget();
|
||||
}
|
||||
if (result == -1) {
|
||||
this._closeResult = new File.Error("close");
|
||||
this._closeResult = new File.Error("close", ctypes.winLastError, this._path);
|
||||
}
|
||||
}
|
||||
if (this._closeResult) {
|
||||
|
@ -115,7 +116,8 @@
|
|||
File.prototype._read = function _read(buffer, nbytes, options) {
|
||||
// |gBytesReadPtr| is a pointer to |gBytesRead|.
|
||||
throw_on_zero("read",
|
||||
WinFile.ReadFile(this.fd, buffer, nbytes, gBytesReadPtr, null)
|
||||
WinFile.ReadFile(this.fd, buffer, nbytes, gBytesReadPtr, null),
|
||||
this._path
|
||||
);
|
||||
return gBytesRead.value;
|
||||
};
|
||||
|
@ -141,7 +143,8 @@
|
|||
}
|
||||
// |gBytesWrittenPtr| is a pointer to |gBytesWritten|.
|
||||
throw_on_zero("write",
|
||||
WinFile.WriteFile(this.fd, buffer, nbytes, gBytesWrittenPtr, null)
|
||||
WinFile.WriteFile(this.fd, buffer, nbytes, gBytesWrittenPtr, null),
|
||||
this._path
|
||||
);
|
||||
return gBytesWritten.value;
|
||||
};
|
||||
|
@ -174,7 +177,8 @@
|
|||
whence = Const.FILE_BEGIN;
|
||||
}
|
||||
return throw_on_negative("setPosition",
|
||||
WinFile.SetFilePointer(this.fd, pos, null, whence));
|
||||
WinFile.SetFilePointer(this.fd, pos, null, whence),
|
||||
this._path);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -184,8 +188,9 @@
|
|||
*/
|
||||
File.prototype.stat = function stat() {
|
||||
throw_on_zero("stat",
|
||||
WinFile.GetFileInformationByHandle(this.fd, gFileInfoPtr));
|
||||
return new File.Info(gFileInfo);
|
||||
WinFile.GetFileInformationByHandle(this.fd, gFileInfoPtr),
|
||||
this._path);
|
||||
return new File.Info(gFileInfo, this._path);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -204,12 +209,14 @@
|
|||
* @throws {OS.File.Error} In case of I/O error.
|
||||
*/
|
||||
File.prototype.setDates = function setDates(accessDate, modificationDate) {
|
||||
accessDate = Date_to_FILETIME("File.prototype.setDates", accessDate);
|
||||
accessDate = Date_to_FILETIME("File.prototype.setDates", accessDate, this._path);
|
||||
modificationDate = Date_to_FILETIME("File.prototype.setDates",
|
||||
modificationDate);
|
||||
modificationDate,
|
||||
this._path);
|
||||
throw_on_zero("setDates",
|
||||
WinFile.SetFileTime(this.fd, null, accessDate.address(),
|
||||
modificationDate.address()));
|
||||
modificationDate.address()),
|
||||
this._path);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -224,7 +231,7 @@
|
|||
* @throws {OS.File.Error} In case of I/O error.
|
||||
*/
|
||||
File.prototype.flush = function flush() {
|
||||
throw_on_zero("flush", WinFile.FlushFileBuffers(this.fd));
|
||||
throw_on_zero("flush", WinFile.FlushFileBuffers(this.fd), this._path);
|
||||
};
|
||||
|
||||
// The default sharing mode for opening files: files are not
|
||||
|
@ -340,7 +347,7 @@
|
|||
}
|
||||
|
||||
let file = error_or_file(WinFile.CreateFile(path,
|
||||
access, share, security, disposition, flags, template));
|
||||
access, share, security, disposition, flags, template), path);
|
||||
|
||||
file._appendMode = !!mode.append;
|
||||
|
||||
|
@ -350,7 +357,8 @@
|
|||
// Now, perform manual truncation
|
||||
file.setPosition(0, File.POS_START);
|
||||
throw_on_zero("open",
|
||||
WinFile.SetEndOfFile(file.fd));
|
||||
WinFile.SetEndOfFile(file.fd),
|
||||
path);
|
||||
return file;
|
||||
};
|
||||
|
||||
|
@ -402,7 +410,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
throw new File.Error("remove");
|
||||
throw new File.Error("remove", ctypes.winLastError, path);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -420,7 +428,7 @@
|
|||
ctypes.winLastError == Const.ERROR_FILE_NOT_FOUND) {
|
||||
return;
|
||||
}
|
||||
throw new File.Error("removeEmptyDir");
|
||||
throw new File.Error("removeEmptyDir", ctypes.winLastError, path);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -447,7 +455,7 @@
|
|||
}
|
||||
|
||||
if (("ignoreExisting" in options) && !options.ignoreExisting) {
|
||||
throw new File.Error("makeDir");
|
||||
throw new File.Error("makeDir", ctypes.winLastError, path);
|
||||
}
|
||||
|
||||
if (ctypes.winLastError == Const.ERROR_ALREADY_EXISTS) {
|
||||
|
@ -469,7 +477,7 @@
|
|||
return;
|
||||
}
|
||||
|
||||
throw new File.Error("makeDir");
|
||||
throw new File.Error("makeDir", ctypes.winLastError, path);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -497,7 +505,8 @@
|
|||
*/
|
||||
File.copy = function copy(sourcePath, destPath, options = {}) {
|
||||
throw_on_zero("copy",
|
||||
WinFile.CopyFile(sourcePath, destPath, options.noOverwrite || false)
|
||||
WinFile.CopyFile(sourcePath, destPath, options.noOverwrite || false),
|
||||
sourcePath
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -536,7 +545,8 @@
|
|||
flags = flags | Const.MOVEFILE_REPLACE_EXISTING;
|
||||
}
|
||||
throw_on_zero("move",
|
||||
WinFile.MoveFileEx(sourcePath, destPath, flags)
|
||||
WinFile.MoveFileEx(sourcePath, destPath, flags),
|
||||
sourcePath
|
||||
);
|
||||
|
||||
// Inherit NTFS permissions from the destination directory
|
||||
|
@ -600,13 +610,14 @@
|
|||
/**
|
||||
* Utility function: convert a FILETIME to a JavaScript Date.
|
||||
*/
|
||||
let FILETIME_to_Date = function FILETIME_to_Date(fileTime) {
|
||||
let FILETIME_to_Date = function FILETIME_to_Date(fileTime, path) {
|
||||
if (fileTime == null) {
|
||||
throw new TypeError("Expecting a non-null filetime");
|
||||
}
|
||||
throw_on_zero("FILETIME_to_Date",
|
||||
WinFile.FileTimeToSystemTime(fileTime.address(),
|
||||
gSystemTimePtr));
|
||||
gSystemTimePtr),
|
||||
path);
|
||||
// Windows counts hours, minutes, seconds from UTC,
|
||||
// JS counts from local time, so we need to go through UTC.
|
||||
let utc = Date.UTC(gSystemTime.wYear,
|
||||
|
@ -626,7 +637,7 @@
|
|||
* then the current date will be used. If numeric, assumed to be the date
|
||||
* in milliseconds since epoch.
|
||||
*/
|
||||
let Date_to_FILETIME = function Date_to_FILETIME(fn, date) {
|
||||
let Date_to_FILETIME = function Date_to_FILETIME(fn, date, path) {
|
||||
if (typeof date === "number") {
|
||||
date = new Date(date);
|
||||
} else if (!date) {
|
||||
|
@ -646,7 +657,8 @@
|
|||
let result = new OS.Shared.Type.FILETIME.implementation();
|
||||
throw_on_zero("Date_to_FILETIME",
|
||||
WinFile.SystemTimeToFileTime(gSystemTimePtr,
|
||||
result.address()));
|
||||
result.address()),
|
||||
path);
|
||||
return result;
|
||||
};
|
||||
|
||||
|
@ -693,7 +705,7 @@
|
|||
this._closed = true;
|
||||
this._exists = false;
|
||||
} else {
|
||||
throw new File.Error("DirectoryIterator", error);
|
||||
throw new File.Error("DirectoryIterator", error, this._path);
|
||||
}
|
||||
} else {
|
||||
this._closed = false;
|
||||
|
@ -712,7 +724,7 @@
|
|||
File.DirectoryIterator.prototype._next = function _next() {
|
||||
// Bailout if the directory does not exist
|
||||
if (!this._exists) {
|
||||
throw File.Error.noSuchFile("DirectoryIterator.prototype.next");
|
||||
throw File.Error.noSuchFile("DirectoryIterator.prototype.next", this._path);
|
||||
}
|
||||
// Bailout if the iterator is closed.
|
||||
if (this._closed) {
|
||||
|
@ -733,7 +745,7 @@
|
|||
if (error == Const.ERROR_NO_MORE_FILES) {
|
||||
return null;
|
||||
} else {
|
||||
throw new File.Error("iter (FindNextFile)", error);
|
||||
throw new File.Error("iter (FindNextFile)", error, this._path);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -771,7 +783,8 @@
|
|||
// We might not have a handle if the iterator is closed
|
||||
// before being used.
|
||||
throw_on_zero("FindClose",
|
||||
WinFile.FindClose(this._handle));
|
||||
WinFile.FindClose(this._handle),
|
||||
this._path);
|
||||
this._handle = null;
|
||||
}
|
||||
};
|
||||
|
@ -795,9 +808,9 @@
|
|||
let isDir = !!(win_entry.dwFileAttributes & Const.FILE_ATTRIBUTE_DIRECTORY);
|
||||
let isSymLink = !!(win_entry.dwFileAttributes & Const.FILE_ATTRIBUTE_REPARSE_POINT);
|
||||
|
||||
let winCreationDate = FILETIME_to_Date(win_entry.ftCreationTime);
|
||||
let winLastWriteDate = FILETIME_to_Date(win_entry.ftLastWriteTime);
|
||||
let winLastAccessDate = FILETIME_to_Date(win_entry.ftLastAccessTime);
|
||||
let winCreationDate = FILETIME_to_Date(win_entry.ftCreationTime, this._path);
|
||||
let winLastWriteDate = FILETIME_to_Date(win_entry.ftLastWriteTime, this._path);
|
||||
let winLastAccessDate = FILETIME_to_Date(win_entry.ftLastAccessTime, this._path);
|
||||
|
||||
let name = win_entry.cFileName.readString();
|
||||
if (!name) {
|
||||
|
@ -847,20 +860,19 @@
|
|||
*
|
||||
* @constructor
|
||||
*/
|
||||
File.Info = function Info(stat) {
|
||||
File.Info = function Info(stat, path) {
|
||||
let isDir = !!(stat.dwFileAttributes & Const.FILE_ATTRIBUTE_DIRECTORY);
|
||||
let isSymLink = !!(stat.dwFileAttributes & Const.FILE_ATTRIBUTE_REPARSE_POINT);
|
||||
|
||||
let winBirthDate = FILETIME_to_Date(stat.ftCreationTime);
|
||||
let lastAccessDate = FILETIME_to_Date(stat.ftLastAccessTime);
|
||||
let lastWriteDate = FILETIME_to_Date(stat.ftLastWriteTime);
|
||||
|
||||
let winBirthDate = FILETIME_to_Date(stat.ftCreationTime, this._path);
|
||||
let lastAccessDate = FILETIME_to_Date(stat.ftLastAccessTime, this._path);
|
||||
let lastWriteDate = FILETIME_to_Date(stat.ftLastWriteTime, this._path);
|
||||
|
||||
let value = ctypes.UInt64.join(stat.nFileSizeHigh, stat.nFileSizeLow);
|
||||
let size = Type.uint64_t.importFromC(value);
|
||||
|
||||
SysAll.AbstractInfo.call(this, isDir, isSymLink, size,
|
||||
winBirthDate, lastAccessDate,
|
||||
lastWriteDate);
|
||||
SysAll.AbstractInfo.call(this, path, isDir, isSymLink, size,
|
||||
winBirthDate, lastAccessDate, lastWriteDate);
|
||||
};
|
||||
File.Info.prototype = Object.create(SysAll.AbstractInfo.prototype);
|
||||
|
||||
|
@ -1004,7 +1016,8 @@
|
|||
*/
|
||||
File.setCurrentDirectory = function setCurrentDirectory(path) {
|
||||
throw_on_zero("setCurrentDirectory",
|
||||
WinFile.SetCurrentDirectory(path));
|
||||
WinFile.SetCurrentDirectory(path),
|
||||
path);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1021,27 +1034,68 @@
|
|||
);
|
||||
|
||||
// Utility functions, used for error-handling
|
||||
function error_or_file(maybe) {
|
||||
|
||||
/**
|
||||
* Turn the result of |open| into an Error or a File
|
||||
* @param {number} maybe The result of the |open| operation that may
|
||||
* represent either an error or a success. If -1, this function raises
|
||||
* an error holding ctypes.winLastError, otherwise it returns the opened file.
|
||||
* @param {string=} path The path of the file.
|
||||
*/
|
||||
function error_or_file(maybe, path) {
|
||||
if (maybe == Const.INVALID_HANDLE_VALUE) {
|
||||
throw new File.Error("open");
|
||||
throw new File.Error("open", ctypes.winLastError, path);
|
||||
}
|
||||
return new File(maybe);
|
||||
return new File(maybe, path);
|
||||
}
|
||||
function throw_on_zero(operation, result) {
|
||||
|
||||
/**
|
||||
* Utility function to sort errors represented as "0" from successes.
|
||||
*
|
||||
* @param {string=} operation The name of the operation. If unspecified,
|
||||
* the name of the caller function.
|
||||
* @param {number} result The result of the operation that may
|
||||
* represent either an error or a success. If 0, this function raises
|
||||
* an error holding ctypes.winLastError, otherwise it returns |result|.
|
||||
* @param {string=} path The path of the file.
|
||||
*/
|
||||
function throw_on_zero(operation, result, path) {
|
||||
if (result == 0) {
|
||||
throw new File.Error(operation);
|
||||
throw new File.Error(operation, ctypes.winLastError, path);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function throw_on_negative(operation, result) {
|
||||
|
||||
/**
|
||||
* Utility function to sort errors represented as "-1" from successes.
|
||||
*
|
||||
* @param {string=} operation The name of the operation. If unspecified,
|
||||
* the name of the caller function.
|
||||
* @param {number} result The result of the operation that may
|
||||
* represent either an error or a success. If -1, this function raises
|
||||
* an error holding ctypes.winLastError, otherwise it returns |result|.
|
||||
* @param {string=} path The path of the file.
|
||||
*/
|
||||
function throw_on_negative(operation, result, path) {
|
||||
if (result < 0) {
|
||||
throw new File.Error(operation);
|
||||
throw new File.Error(operation, ctypes.winLastError, path);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function throw_on_null(operation, result) {
|
||||
|
||||
/**
|
||||
* Utility function to sort errors represented as |null| from successes.
|
||||
*
|
||||
* @param {string=} operation The name of the operation. If unspecified,
|
||||
* the name of the caller function.
|
||||
* @param {pointer} result The result of the operation that may
|
||||
* represent either an error or a success. If |null|, this function raises
|
||||
* an error holding ctypes.winLastError, otherwise it returns |result|.
|
||||
* @param {string=} path The path of the file.
|
||||
*/
|
||||
function throw_on_null(operation, result, path) {
|
||||
if (result == null || (result.isNull && result.isNull())) {
|
||||
throw new File.Error(operation);
|
||||
throw new File.Error(operation, ctypes.winLastError, path);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
let {OS: {File, Path, Constants}} = Components.utils.import("resource://gre/modules/osfile.jsm", {});
|
||||
Components.utils.import("resource://gre/modules/Task.jsm");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* testFileError_with_writeAtomic() {
|
||||
let DEFAULT_CONTENTS = "default contents" + Math.random();
|
||||
let path = Path.join(Constants.Path.tmpDir,
|
||||
"testFileError.tmp");
|
||||
yield File.remove(path);
|
||||
yield File.writeAtomic(path, DEFAULT_CONTENTS);
|
||||
let exception;
|
||||
try {
|
||||
yield File.writeAtomic(path, DEFAULT_CONTENTS, { noOverwrite: true });
|
||||
} catch (ex) {
|
||||
exception = ex;
|
||||
}
|
||||
do_check_true(exception instanceof File.Error);
|
||||
do_check_true(exception.path == path);
|
||||
});
|
||||
|
||||
add_task(function* testFileError_with_makeDir() {
|
||||
let path = Path.join(Constants.Path.tmpDir,
|
||||
"directory");
|
||||
yield File.removeDir(path);
|
||||
yield File.makeDir(path);
|
||||
let exception;
|
||||
try {
|
||||
yield File.makeDir(path, { ignoreExisting: false });
|
||||
} catch (ex) {
|
||||
exception = ex;
|
||||
}
|
||||
do_check_true(exception instanceof File.Error);
|
||||
do_check_true(exception.path == path);
|
||||
});
|
||||
|
||||
add_task(function* testFileError_with_move() {
|
||||
let DEFAULT_CONTENTS = "default contents" + Math.random();
|
||||
let sourcePath = Path.join(Constants.Path.tmpDir,
|
||||
"src.tmp");
|
||||
let destPath = Path.join(Constants.Path.tmpDir,
|
||||
"dest.tmp");
|
||||
yield File.remove(sourcePath);
|
||||
yield File.remove(destPath);
|
||||
yield File.writeAtomic(sourcePath, DEFAULT_CONTENTS);
|
||||
yield File.writeAtomic(destPath, DEFAULT_CONTENTS);
|
||||
let exception;
|
||||
try {
|
||||
yield File.move(sourcePath, destPath, { noOverwrite: true });
|
||||
} catch (ex) {
|
||||
exception = ex;
|
||||
}
|
||||
do_print(exception);
|
||||
do_check_true(exception instanceof File.Error);
|
||||
do_check_true(exception.path == sourcePath);
|
||||
});
|
|
@ -27,3 +27,4 @@ tail =
|
|||
[test_duration.js]
|
||||
[test_compression.js]
|
||||
[test_osfile_writeAtomic_backupTo_option.js]
|
||||
[test_osfile_error.js]
|
||||
|
|
Загрузка…
Ссылка в новой задаче