diff --git a/dom/system/OSFileConstants.cpp b/dom/system/OSFileConstants.cpp index 38aab2e7f51e..489d7eedaae5 100644 --- a/dom/system/OSFileConstants.cpp +++ b/dom/system/OSFileConstants.cpp @@ -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), diff --git a/toolkit/components/osfile/modules/osfile_shared_allthreads.jsm b/toolkit/components/osfile/modules/osfile_shared_allthreads.jsm index fe6269f70f68..c7c155e7e24b 100644 --- a/toolkit/components/osfile/modules/osfile_shared_allthreads.jsm +++ b/toolkit/components/osfile/modules/osfile_shared_allthreads.jsm @@ -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; diff --git a/toolkit/components/osfile/modules/osfile_shared_front.jsm b/toolkit/components/osfile/modules/osfile_shared_front.jsm index bfed073152fc..5c88177ddde8 100644 --- a/toolkit/components/osfile/modules/osfile_shared_front.jsm +++ b/toolkit/components/osfile/modules/osfile_shared_front.jsm @@ -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") { diff --git a/toolkit/components/osfile/modules/osfile_unix_allthreads.jsm b/toolkit/components/osfile/modules/osfile_unix_allthreads.jsm index cf400860199f..5202c5fe080a 100644 --- a/toolkit/components/osfile/modules/osfile_unix_allthreads.jsm +++ b/toolkit/components/osfile/modules/osfile_unix_allthreads.jsm @@ -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 = [ diff --git a/toolkit/components/osfile/modules/osfile_unix_front.jsm b/toolkit/components/osfile/modules/osfile_unix_front.jsm index f1ccfe11e424..99e5e9b0d59f 100644 --- a/toolkit/components/osfile/modules/osfile_unix_front.jsm +++ b/toolkit/components/osfile/modules/osfile_unix_front.jsm @@ -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; } diff --git a/toolkit/components/osfile/modules/osfile_win_allthreads.jsm b/toolkit/components/osfile/modules/osfile_win_allthreads.jsm index fe80078828a8..3ef7d610a2e4 100644 --- a/toolkit/components/osfile/modules/osfile_win_allthreads.jsm +++ b/toolkit/components/osfile/modules/osfile_win_allthreads.jsm @@ -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 = [ diff --git a/toolkit/components/osfile/modules/osfile_win_front.jsm b/toolkit/components/osfile/modules/osfile_win_front.jsm index 7fd71248ab0d..9e600930c4fb 100644 --- a/toolkit/components/osfile/modules/osfile_win_front.jsm +++ b/toolkit/components/osfile/modules/osfile_win_front.jsm @@ -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; } diff --git a/toolkit/components/osfile/tests/xpcshell/test_osfile_error.js b/toolkit/components/osfile/tests/xpcshell/test_osfile_error.js new file mode 100644 index 000000000000..72643102aa3c --- /dev/null +++ b/toolkit/components/osfile/tests/xpcshell/test_osfile_error.js @@ -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); +}); diff --git a/toolkit/components/osfile/tests/xpcshell/xpcshell.ini b/toolkit/components/osfile/tests/xpcshell/xpcshell.ini index 645a66df9775..67c88548284d 100644 --- a/toolkit/components/osfile/tests/xpcshell/xpcshell.ini +++ b/toolkit/components/osfile/tests/xpcshell/xpcshell.ini @@ -27,3 +27,4 @@ tail = [test_duration.js] [test_compression.js] [test_osfile_writeAtomic_backupTo_option.js] +[test_osfile_error.js]