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 Unix 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 libc; michael@0: * - define OS.Unix.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, "Unix", "allthreads"); michael@0: let Const = SharedAll.Constants.libc; michael@0: michael@0: // Open libc michael@0: let libc = new SharedAll.Library("libc", michael@0: "libc.so", "libSystem.B.dylib", "a.out"); 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: // Define lazy binding michael@0: let LazyBindings = {}; michael@0: libc.declareLazy(LazyBindings, "strerror", michael@0: "strerror", ctypes.default_abi, michael@0: /*return*/ ctypes.char.ptr, michael@0: /*errnum*/ ctypes.int); 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: * |unixErrno|, 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.libc|. 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: errno = ctypes.errno, path = "") { michael@0: operation = operation; michael@0: SharedAll.OSError.call(this, operation, path); michael@0: this.unixErrno = errno; michael@0: }; michael@0: OSError.prototype = Object.create(SharedAll.OSError.prototype); michael@0: OSError.prototype.toString = function toString() { michael@0: return "Unix error " + this.unixErrno + michael@0: " during operation " + this.operation + michael@0: (this.path? " on file " + this.path : "") + michael@0: " (" + LazyBindings.strerror(this.unixErrno).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.unixErrno == Const.EEXIST; 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.unixErrno == Const.ENOENT; michael@0: } 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.unixErrno == Const.ENOTEMPTY; 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.unixErrno == Const.EBADF; 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.unixErrno == Const.EACCES; 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.unixErrno == Const.EINVAL; 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: unixErrno: error.unixErrno, 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.unixErrno, msg.path); michael@0: }; michael@0: exports.Error = OSError; michael@0: michael@0: /** michael@0: * Code shared by implementations of File.Info on Unix michael@0: * michael@0: * @constructor michael@0: */ michael@0: let AbstractInfo = function AbstractInfo(path, isDir, isSymLink, size, lastAccessDate, michael@0: lastModificationDate, unixLastStatusChangeDate, michael@0: unixOwner, unixGroup, unixMode) { michael@0: this._path = path; michael@0: this._isDir = isDir; michael@0: this._isSymlLink = isSymLink; michael@0: this._size = size; michael@0: this._lastAccessDate = lastAccessDate; michael@0: this._lastModificationDate = lastModificationDate; michael@0: this._unixLastStatusChangeDate = unixLastStatusChangeDate; michael@0: this._unixOwner = unixOwner; michael@0: this._unixGroup = unixGroup; michael@0: this._unixMode = unixMode; 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 symbolink link, |false| otherwise michael@0: */ michael@0: get isSymLink() { michael@0: return this._isSymlLink; 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: /** 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 michael@0: * underlying 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: * Return the date of last modification of this file. michael@0: */ michael@0: get lastModificationDate() { michael@0: return this._lastModificationDate; michael@0: }, michael@0: /** michael@0: * Return the date at which the status of this file was last modified michael@0: * (this is the date of the latest write/renaming/mode change/... michael@0: * of the file) michael@0: */ michael@0: get unixLastStatusChangeDate() { michael@0: return this._unixLastStatusChangeDate; michael@0: }, michael@0: /* michael@0: * Return the Unix owner of this file michael@0: */ michael@0: get unixOwner() { michael@0: return this._unixOwner; michael@0: }, michael@0: /* michael@0: * Return the Unix group of this file michael@0: */ michael@0: get unixGroup() { michael@0: return this._unixGroup; michael@0: }, michael@0: /* michael@0: * Return the Unix group of this file michael@0: */ michael@0: get unixMode() { michael@0: return this._unixMode; michael@0: } michael@0: }; michael@0: exports.AbstractInfo = AbstractInfo; michael@0: michael@0: /** michael@0: * Code shared by implementations of File.DirectoryIterator.Entry on Unix michael@0: * michael@0: * @constructor michael@0: */ michael@0: let AbstractEntry = function AbstractEntry(isDir, isSymLink, name, path) { michael@0: this._isDir = isDir; michael@0: this._isSymlLink = isSymLink; michael@0: this._name = name; 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 directory, |false| otherwise michael@0: */ michael@0: get isSymLink() { michael@0: return this._isSymlLink; 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 full path to the entry 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.SEEK_SET; michael@0: exports.POS_CURRENT = Const.SEEK_CUR; michael@0: exports.POS_END = Const.SEEK_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 Unix, expressed as C strings michael@0: */ michael@0: Type.path = Type.cstring.withName("[in] path"); michael@0: Type.out_path = Type.out_cstring.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.EBADF, path); michael@0: }; michael@0: michael@0: OSError.exists = function exists(operation, path) { michael@0: return new OSError(operation, Const.EEXIST, path); michael@0: }; michael@0: michael@0: OSError.noSuchFile = function noSuchFile(operation, path) { michael@0: return new OSError(operation, Const.ENOENT, path); michael@0: }; michael@0: michael@0: OSError.invalidArgument = function invalidArgument(operation) { michael@0: return new OSError(operation, Const.EINVAL); 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: }