michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /** michael@0: * This module defines the thread-agnostic components of the Win version michael@0: * of OS.File. It depends on the thread-agnostic cross-platform components michael@0: * of OS.File. michael@0: * michael@0: * It serves the following purposes: michael@0: * - open kernel32; michael@0: * - define OS.Shared.Win.Error; michael@0: * - define a few constants and types that need to be defined on all platforms. michael@0: * michael@0: * This module can be: michael@0: * - opened from the main thread as a jsm module; michael@0: * - opened from a chrome worker through require(). michael@0: */ michael@0: michael@0: "use strict"; michael@0: michael@0: let SharedAll; michael@0: if (typeof Components != "undefined") { michael@0: let Cu = Components.utils; michael@0: // Module is opened as a jsm module michael@0: Cu.import("resource://gre/modules/ctypes.jsm", this); michael@0: michael@0: SharedAll = {}; michael@0: Cu.import("resource://gre/modules/osfile/osfile_shared_allthreads.jsm", SharedAll); michael@0: this.exports = {}; michael@0: } else if (typeof "module" != "undefined" && typeof "require" != "undefined") { michael@0: // Module is loaded with require() michael@0: SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm"); michael@0: } else { michael@0: throw new Error("Please open this module with Component.utils.import or with require()"); michael@0: } michael@0: michael@0: let LOG = SharedAll.LOG.bind(SharedAll, "Win", "allthreads"); michael@0: let Const = SharedAll.Constants.Win; michael@0: michael@0: // Open libc michael@0: let libc = new SharedAll.Library("libc", "kernel32.dll"); michael@0: exports.libc = libc; michael@0: michael@0: // Define declareFFI michael@0: let declareFFI = SharedAll.declareFFI.bind(null, libc); michael@0: exports.declareFFI = declareFFI; michael@0: michael@0: let Scope = {}; michael@0: michael@0: // Define Error michael@0: libc.declareLazy(Scope, "FormatMessage", michael@0: "FormatMessageW", ctypes.winapi_abi, michael@0: /*return*/ ctypes.uint32_t, michael@0: /*flags*/ ctypes.uint32_t, michael@0: /*source*/ ctypes.voidptr_t, michael@0: /*msgid*/ ctypes.uint32_t, michael@0: /*langid*/ ctypes.uint32_t, michael@0: /*buf*/ ctypes.jschar.ptr, michael@0: /*size*/ ctypes.uint32_t, michael@0: /*Arguments*/ctypes.voidptr_t); michael@0: michael@0: /** michael@0: * A File-related error. michael@0: * michael@0: * To obtain a human-readable error message, use method |toString|. michael@0: * To determine the cause of the error, use the various |becauseX| michael@0: * getters. To determine the operation that failed, use field michael@0: * |operation|. michael@0: * michael@0: * Additionally, this implementation offers a field michael@0: * |winLastError|, which holds the OS-specific error michael@0: * constant. If you need this level of detail, you may match the value michael@0: * of this field against the error constants of |OS.Constants.Win|. michael@0: * michael@0: * @param {string=} operation The operation that failed. If unspecified, michael@0: * the name of the calling function is taken to be the operation that michael@0: * failed. michael@0: * @param {number=} lastError The OS-specific constant detailing the michael@0: * reason of the error. If unspecified, this is fetched from the system michael@0: * status. michael@0: * @param {string=} path The file path that manipulated. If unspecified, michael@0: * assign the empty string. michael@0: * michael@0: * @constructor michael@0: * @extends {OS.Shared.Error} michael@0: */ michael@0: let OSError = function OSError(operation = "unknown operation", michael@0: lastError = ctypes.winLastError, path = "") { michael@0: operation = operation; michael@0: SharedAll.OSError.call(this, operation, path); michael@0: this.winLastError = lastError; michael@0: }; michael@0: OSError.prototype = Object.create(SharedAll.OSError.prototype); michael@0: OSError.prototype.toString = function toString() { michael@0: let buf = new (ctypes.ArrayType(ctypes.jschar, 1024))(); michael@0: let result = Scope.FormatMessage( michael@0: Const.FORMAT_MESSAGE_FROM_SYSTEM | michael@0: Const.FORMAT_MESSAGE_IGNORE_INSERTS, michael@0: null, michael@0: /* The error number */ this.winLastError, michael@0: /* Default language */ 0, michael@0: /* Output buffer*/ buf, michael@0: /* Minimum size of buffer */ 1024, michael@0: /* Format args*/ null michael@0: ); michael@0: if (!result) { michael@0: buf = "additional error " + michael@0: ctypes.winLastError + michael@0: " while fetching system error message"; michael@0: } michael@0: return "Win error " + this.winLastError + " during operation " michael@0: + this.operation + (this.path? " on file " + this.path : "") + michael@0: " (" + buf.readString() + ")"; michael@0: }; michael@0: michael@0: /** michael@0: * |true| if the error was raised because a file or directory michael@0: * already exists, |false| otherwise. michael@0: */ michael@0: Object.defineProperty(OSError.prototype, "becauseExists", { michael@0: get: function becauseExists() { michael@0: return this.winLastError == Const.ERROR_FILE_EXISTS || michael@0: this.winLastError == Const.ERROR_ALREADY_EXISTS; michael@0: } michael@0: }); michael@0: /** michael@0: * |true| if the error was raised because a file or directory michael@0: * does not exist, |false| otherwise. michael@0: */ michael@0: Object.defineProperty(OSError.prototype, "becauseNoSuchFile", { michael@0: get: function becauseNoSuchFile() { michael@0: return this.winLastError == Const.ERROR_FILE_NOT_FOUND || michael@0: this.winLastError == Const.ERROR_PATH_NOT_FOUND; michael@0: } michael@0: }); michael@0: /** michael@0: * |true| if the error was raised because a directory is not empty michael@0: * does not exist, |false| otherwise. michael@0: */ michael@0: Object.defineProperty(OSError.prototype, "becauseNotEmpty", { michael@0: get: function becauseNotEmpty() { michael@0: return this.winLastError == Const.ERROR_DIR_NOT_EMPTY; michael@0: } michael@0: }); michael@0: /** michael@0: * |true| if the error was raised because a file or directory michael@0: * is closed, |false| otherwise. michael@0: */ michael@0: Object.defineProperty(OSError.prototype, "becauseClosed", { michael@0: get: function becauseClosed() { michael@0: return this.winLastError == Const.ERROR_INVALID_HANDLE; michael@0: } michael@0: }); michael@0: /** michael@0: * |true| if the error was raised because permission is denied to michael@0: * access a file or directory, |false| otherwise. michael@0: */ michael@0: Object.defineProperty(OSError.prototype, "becauseAccessDenied", { michael@0: get: function becauseAccessDenied() { michael@0: return this.winLastError == Const.ERROR_ACCESS_DENIED; michael@0: } michael@0: }); michael@0: /** michael@0: * |true| if the error was raised because some invalid argument was passed, michael@0: * |false| otherwise. michael@0: */ michael@0: Object.defineProperty(OSError.prototype, "becauseInvalidArgument", { michael@0: get: function becauseInvalidArgument() { michael@0: return this.winLastError == Const.ERROR_NOT_SUPPORTED || michael@0: this.winLastError == Const.ERROR_BAD_ARGUMENTS; michael@0: } michael@0: }); michael@0: michael@0: /** michael@0: * Serialize an instance of OSError to something that can be michael@0: * transmitted across threads (not necessarily a string). michael@0: */ michael@0: OSError.toMsg = function toMsg(error) { michael@0: return { michael@0: operation: error.operation, michael@0: winLastError: error.winLastError, michael@0: path: error.path michael@0: }; michael@0: }; michael@0: michael@0: /** michael@0: * Deserialize a message back to an instance of OSError michael@0: */ michael@0: OSError.fromMsg = function fromMsg(msg) { michael@0: return new OSError(msg.operation, msg.winLastError, msg.path); michael@0: }; michael@0: exports.Error = OSError; michael@0: michael@0: /** michael@0: * Code shared by implementation of File.Info on Windows michael@0: * michael@0: * @constructor michael@0: */ michael@0: let AbstractInfo = function AbstractInfo(path, isDir, isSymLink, size, michael@0: winBirthDate, michael@0: lastAccessDate, lastWriteDate) { michael@0: this._path = path; michael@0: this._isDir = isDir; michael@0: this._isSymLink = isSymLink; michael@0: this._size = size; michael@0: this._winBirthDate = winBirthDate; michael@0: this._lastAccessDate = lastAccessDate; michael@0: this._lastModificationDate = lastWriteDate; michael@0: }; michael@0: michael@0: AbstractInfo.prototype = { michael@0: /** michael@0: * The path of the file, used for error-reporting. michael@0: * michael@0: * @type {string} michael@0: */ michael@0: get path() { michael@0: return this._path; michael@0: }, michael@0: /** michael@0: * |true| if this file is a directory, |false| otherwise michael@0: */ michael@0: get isDir() { michael@0: return this._isDir; michael@0: }, michael@0: /** michael@0: * |true| if this file is a symbolic link, |false| otherwise michael@0: */ michael@0: get isSymLink() { michael@0: return this._isSymLink; michael@0: }, michael@0: /** michael@0: * The size of the file, in bytes. michael@0: * michael@0: * Note that the result may be |NaN| if the size of the file cannot be michael@0: * represented in JavaScript. michael@0: * michael@0: * @type {number} michael@0: */ michael@0: get size() { michael@0: return this._size; michael@0: }, michael@0: // Deprecated michael@0: get creationDate() { michael@0: return this._winBirthDate; michael@0: }, michael@0: /** michael@0: * The date of creation of this file. michael@0: * michael@0: * @type {Date} michael@0: */ michael@0: get winBirthDate() { michael@0: return this._winBirthDate; michael@0: }, michael@0: /** michael@0: * The date of last access to this file. michael@0: * michael@0: * Note that the definition of last access may depend on the underlying michael@0: * operating system and file system. michael@0: * michael@0: * @type {Date} michael@0: */ michael@0: get lastAccessDate() { michael@0: return this._lastAccessDate; michael@0: }, michael@0: /** michael@0: * The date of last modification of this file. michael@0: * michael@0: * Note that the definition of last access may depend on the underlying michael@0: * operating system and file system. michael@0: * michael@0: * @type {Date} michael@0: */ michael@0: get lastModificationDate() { michael@0: return this._lastModificationDate; michael@0: } michael@0: }; michael@0: exports.AbstractInfo = AbstractInfo; michael@0: michael@0: /** michael@0: * Code shared by implementation of File.DirectoryIterator.Entry on Windows michael@0: * michael@0: * @constructor michael@0: */ michael@0: let AbstractEntry = function AbstractEntry(isDir, isSymLink, name, michael@0: winCreationDate, winLastWriteDate, michael@0: winLastAccessDate, path) { michael@0: this._isDir = isDir; michael@0: this._isSymLink = isSymLink; michael@0: this._name = name; michael@0: this._winCreationDate = winCreationDate; michael@0: this._winLastWriteDate = winLastWriteDate; michael@0: this._winLastAccessDate = winLastAccessDate; michael@0: this._path = path; michael@0: }; michael@0: michael@0: AbstractEntry.prototype = { michael@0: /** michael@0: * |true| if the entry is a directory, |false| otherwise michael@0: */ michael@0: get isDir() { michael@0: return this._isDir; michael@0: }, michael@0: /** michael@0: * |true| if the entry is a symbolic link, |false| otherwise michael@0: */ michael@0: get isSymLink() { michael@0: return this._isSymLink; michael@0: }, michael@0: /** michael@0: * The name of the entry. michael@0: * @type {string} michael@0: */ michael@0: get name() { michael@0: return this._name; michael@0: }, michael@0: /** michael@0: * The creation time of this file. michael@0: * @type {Date} michael@0: */ michael@0: get winCreationDate() { michael@0: return this._winCreationDate; michael@0: }, michael@0: /** michael@0: * The last modification time of this file. michael@0: * @type {Date} michael@0: */ michael@0: get winLastWriteDate() { michael@0: return this._winLastWriteDate; michael@0: }, michael@0: /** michael@0: * The last access time of this file. michael@0: * @type {Date} michael@0: */ michael@0: get winLastAccessDate() { michael@0: return this._winLastAccessDate; michael@0: }, michael@0: /** michael@0: * The full path of the entry michael@0: * @type {string} michael@0: */ michael@0: get path() { michael@0: return this._path; michael@0: } michael@0: }; michael@0: exports.AbstractEntry = AbstractEntry; michael@0: michael@0: // Special constants that need to be defined on all platforms michael@0: michael@0: exports.POS_START = Const.FILE_BEGIN; michael@0: exports.POS_CURRENT = Const.FILE_CURRENT; michael@0: exports.POS_END = Const.FILE_END; michael@0: michael@0: // Special types that need to be defined for communication michael@0: // between threads michael@0: let Type = Object.create(SharedAll.Type); michael@0: exports.Type = Type; michael@0: michael@0: /** michael@0: * Native paths michael@0: * michael@0: * Under Windows, expressed as wide strings michael@0: */ michael@0: Type.path = Type.wstring.withName("[in] path"); michael@0: Type.out_path = Type.out_wstring.withName("[out] path"); michael@0: michael@0: // Special constructors that need to be defined on all threads michael@0: OSError.closed = function closed(operation, path) { michael@0: return new OSError(operation, Const.ERROR_INVALID_HANDLE, path); michael@0: }; michael@0: michael@0: OSError.exists = function exists(operation, path) { michael@0: return new OSError(operation, Const.ERROR_FILE_EXISTS, path); michael@0: }; michael@0: michael@0: OSError.noSuchFile = function noSuchFile(operation, path) { michael@0: return new OSError(operation, Const.ERROR_FILE_NOT_FOUND, path); michael@0: }; michael@0: michael@0: OSError.invalidArgument = function invalidArgument(operation) { michael@0: return new OSError(operation, Const.ERROR_NOT_SUPPORTED); michael@0: }; michael@0: michael@0: let EXPORTED_SYMBOLS = [ michael@0: "declareFFI", michael@0: "libc", michael@0: "Error", michael@0: "AbstractInfo", michael@0: "AbstractEntry", michael@0: "Type", michael@0: "POS_START", michael@0: "POS_CURRENT", michael@0: "POS_END" michael@0: ]; michael@0: michael@0: //////////// Boilerplate michael@0: if (typeof Components != "undefined") { michael@0: this.EXPORTED_SYMBOLS = EXPORTED_SYMBOLS; michael@0: for (let symbol of EXPORTED_SYMBOLS) { michael@0: this[symbol] = exports[symbol]; michael@0: } michael@0: }