toolkit/components/osfile/modules/osfile_unix_front.jsm

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/components/osfile/modules/osfile_unix_front.jsm	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1176 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +/**
     1.9 + * Synchronous front-end for the JavaScript OS.File library.
    1.10 + * Unix implementation.
    1.11 + *
    1.12 + * This front-end is meant to be imported by a worker thread.
    1.13 + */
    1.14 +
    1.15 +{
    1.16 +  if (typeof Components != "undefined") {
    1.17 +    // We do not wish osfile_unix_front.jsm to be used directly as a main thread
    1.18 +    // module yet.
    1.19 +
    1.20 +    throw new Error("osfile_unix_front.jsm cannot be used from the main thread yet");
    1.21 +  }
    1.22 +  (function(exports) {
    1.23 +     "use strict";
    1.24 +
    1.25 +     // exports.OS.Unix is created by osfile_unix_back.jsm
    1.26 +     if (exports.OS && exports.OS.File) {
    1.27 +       return; // Avoid double-initialization
    1.28 +     }
    1.29 +
    1.30 +     let SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm");
    1.31 +     let Path = require("resource://gre/modules/osfile/ospath.jsm");
    1.32 +     let SysAll = require("resource://gre/modules/osfile/osfile_unix_allthreads.jsm");
    1.33 +     exports.OS.Unix.File._init();
    1.34 +     let LOG = SharedAll.LOG.bind(SharedAll, "Unix front-end");
    1.35 +     let Const = SharedAll.Constants.libc;
    1.36 +     let UnixFile = exports.OS.Unix.File;
    1.37 +     let Type = UnixFile.Type;
    1.38 +
    1.39 +     /**
    1.40 +      * Representation of a file.
    1.41 +      *
    1.42 +      * You generally do not need to call this constructor yourself. Rather,
    1.43 +      * to open a file, use function |OS.File.open|.
    1.44 +      *
    1.45 +      * @param fd A OS-specific file descriptor.
    1.46 +      * @param {string} path File path of the file handle, used for error-reporting.
    1.47 +      * @constructor
    1.48 +      */
    1.49 +     let File = function File(fd, path) {
    1.50 +       exports.OS.Shared.AbstractFile.call(this, fd, path);
    1.51 +       this._closeResult = null;
    1.52 +     };
    1.53 +     File.prototype = Object.create(exports.OS.Shared.AbstractFile.prototype);
    1.54 +
    1.55 +     /**
    1.56 +      * Close the file.
    1.57 +      *
    1.58 +      * This method has no effect if the file is already closed. However,
    1.59 +      * if the first call to |close| has thrown an error, further calls
    1.60 +      * will throw the same error.
    1.61 +      *
    1.62 +      * @throws File.Error If closing the file revealed an error that could
    1.63 +      * not be reported earlier.
    1.64 +      */
    1.65 +     File.prototype.close = function close() {
    1.66 +       if (this._fd) {
    1.67 +         let fd = this._fd;
    1.68 +         this._fd = null;
    1.69 +        // Call |close(fd)|, detach finalizer if any
    1.70 +         // (|fd| may not be a CDataFinalizer if it has been
    1.71 +         // instantiated from a controller thread).
    1.72 +         let result = UnixFile._close(fd);
    1.73 +         if (typeof fd == "object" && "forget" in fd) {
    1.74 +           fd.forget();
    1.75 +         }
    1.76 +         if (result == -1) {
    1.77 +           this._closeResult = new File.Error("close", ctypes.errno, this._path);
    1.78 +         }
    1.79 +       }
    1.80 +       if (this._closeResult) {
    1.81 +         throw this._closeResult;
    1.82 +       }
    1.83 +       return;
    1.84 +     };
    1.85 +
    1.86 +     /**
    1.87 +      * Read some bytes from a file.
    1.88 +      *
    1.89 +      * @param {C pointer} buffer A buffer for holding the data
    1.90 +      * once it is read.
    1.91 +      * @param {number} nbytes The number of bytes to read. It must not
    1.92 +      * exceed the size of |buffer| in bytes but it may exceed the number
    1.93 +      * of bytes unread in the file.
    1.94 +      * @param {*=} options Additional options for reading. Ignored in
    1.95 +      * this implementation.
    1.96 +      *
    1.97 +      * @return {number} The number of bytes effectively read. If zero,
    1.98 +      * the end of the file has been reached.
    1.99 +      * @throws {OS.File.Error} In case of I/O error.
   1.100 +      */
   1.101 +     File.prototype._read = function _read(buffer, nbytes, options = {}) {
   1.102 +      // Populate the page cache with data from a file so the subsequent reads
   1.103 +      // from that file will not block on disk I/O.
   1.104 +       if (typeof(UnixFile.posix_fadvise) === 'function' &&
   1.105 +           (options.sequential || !("sequential" in options))) {
   1.106 +         UnixFile.posix_fadvise(this.fd, 0, nbytes,
   1.107 +          OS.Constants.libc.POSIX_FADV_SEQUENTIAL);
   1.108 +       }
   1.109 +       return throw_on_negative("read",
   1.110 +         UnixFile.read(this.fd, buffer, nbytes),
   1.111 +         this._path
   1.112 +       );
   1.113 +     };
   1.114 +
   1.115 +     /**
   1.116 +      * Write some bytes to a file.
   1.117 +      *
   1.118 +      * @param {C pointer} buffer A buffer holding the data that must be
   1.119 +      * written.
   1.120 +      * @param {number} nbytes The number of bytes to write. It must not
   1.121 +      * exceed the size of |buffer| in bytes.
   1.122 +      * @param {*=} options Additional options for writing. Ignored in
   1.123 +      * this implementation.
   1.124 +      *
   1.125 +      * @return {number} The number of bytes effectively written.
   1.126 +      * @throws {OS.File.Error} In case of I/O error.
   1.127 +      */
   1.128 +     File.prototype._write = function _write(buffer, nbytes, options = {}) {
   1.129 +       return throw_on_negative("write",
   1.130 +         UnixFile.write(this.fd, buffer, nbytes),
   1.131 +         this._path
   1.132 +       );
   1.133 +     };
   1.134 +
   1.135 +     /**
   1.136 +      * Return the current position in the file.
   1.137 +      */
   1.138 +     File.prototype.getPosition = function getPosition(pos) {
   1.139 +         return this.setPosition(0, File.POS_CURRENT);
   1.140 +     };
   1.141 +
   1.142 +     /**
   1.143 +      * Change the current position in the file.
   1.144 +      *
   1.145 +      * @param {number} pos The new position. Whether this position
   1.146 +      * is considered from the current position, from the start of
   1.147 +      * the file or from the end of the file is determined by
   1.148 +      * argument |whence|.  Note that |pos| may exceed the length of
   1.149 +      * the file.
   1.150 +      * @param {number=} whence The reference position. If omitted
   1.151 +      * or |OS.File.POS_START|, |pos| is relative to the start of the
   1.152 +      * file.  If |OS.File.POS_CURRENT|, |pos| is relative to the
   1.153 +      * current position in the file. If |OS.File.POS_END|, |pos| is
   1.154 +      * relative to the end of the file.
   1.155 +      *
   1.156 +      * @return The new position in the file.
   1.157 +      */
   1.158 +     File.prototype.setPosition = function setPosition(pos, whence) {
   1.159 +       if (whence === undefined) {
   1.160 +         whence = Const.SEEK_SET;
   1.161 +       }
   1.162 +       return throw_on_negative("setPosition",
   1.163 +         UnixFile.lseek(this.fd, pos, whence),
   1.164 +         this._path
   1.165 +       );
   1.166 +     };
   1.167 +
   1.168 +     /**
   1.169 +      * Fetch the information on the file.
   1.170 +      *
   1.171 +      * @return File.Info The information on |this| file.
   1.172 +      */
   1.173 +     File.prototype.stat = function stat() {
   1.174 +       throw_on_negative("stat", UnixFile.fstat(this.fd, gStatDataPtr),
   1.175 +                         this._path);
   1.176 +       return new File.Info(gStatData, this._path);
   1.177 +     };
   1.178 +
   1.179 +     /**
   1.180 +      * Set the file's access permissions.  Without any options, the
   1.181 +      * permissions are set to an approximation of what they would
   1.182 +      * have been if the file had been created in its current
   1.183 +      * directory in the "most typical" fashion for the operating
   1.184 +      * system.  In the current implementation, this means we set
   1.185 +      * the POSIX file mode to (0666 & ~umask).
   1.186 +      *
   1.187 +      * This operation is likely to fail if applied to a file that was
   1.188 +      * not created by the currently running program (more precisely,
   1.189 +      * if it was created by a program running under a different OS-level
   1.190 +      * user account).  It may also fail, or silently do nothing, if the
   1.191 +      * filesystem containing the file does not support access permissions.
   1.192 +      *
   1.193 +      * @param {*=} options
   1.194 +      * - {number} unixMode     If present, the POSIX file mode is set to
   1.195 +      *                         exactly this value, unless |unixHonorUmask| is
   1.196 +      *                         also present.
   1.197 +      * - {bool} unixHonorUmask If true, any |unixMode| value is modified by
   1.198 +      *                         the process umask, as open() would have done.
   1.199 +      */
   1.200 +     File.prototype.setPermissions = function setPermissions(options = {}) {
   1.201 +       throw_on_negative("setPermissions",
   1.202 +                         UnixFile.fchmod(this.fd, unixMode(options)),
   1.203 +                         this._path);
   1.204 +     };
   1.205 +
   1.206 +     /**
   1.207 +      * Set the last access and modification date of the file.
   1.208 +      * The time stamp resolution is 1 second at best, but might be worse
   1.209 +      * depending on the platform.
   1.210 +      *
   1.211 +      * @param {Date,number=} accessDate The last access date. If numeric,
   1.212 +      * milliseconds since epoch. If omitted or null, then the current date
   1.213 +      * will be used.
   1.214 +      * @param {Date,number=} modificationDate The last modification date. If
   1.215 +      * numeric, milliseconds since epoch. If omitted or null, then the current
   1.216 +      * date will be used.
   1.217 +      *
   1.218 +      * @throws {TypeError} In case of invalid parameters.
   1.219 +      * @throws {OS.File.Error} In case of I/O error.
   1.220 +      */
   1.221 +     File.prototype.setDates = function setDates(accessDate, modificationDate) {
   1.222 +       accessDate = normalizeDate("File.prototype.setDates", accessDate);
   1.223 +       modificationDate = normalizeDate("File.prototype.setDates",
   1.224 +                                        modificationDate);
   1.225 +       gTimevals[0].tv_sec = (accessDate / 1000) | 0;
   1.226 +       gTimevals[0].tv_usec = 0;
   1.227 +       gTimevals[1].tv_sec = (modificationDate / 1000) | 0;
   1.228 +       gTimevals[1].tv_usec = 0;
   1.229 +       throw_on_negative("setDates",
   1.230 +                         UnixFile.futimes(this.fd, gTimevalsPtr),
   1.231 +                         this._path);
   1.232 +     };
   1.233 +
   1.234 +     /**
   1.235 +      * Flushes the file's buffers and causes all buffered data
   1.236 +      * to be written.
   1.237 +      * Disk flushes are very expensive and therefore should be used carefully,
   1.238 +      * sparingly and only in scenarios where it is vital that data survives
   1.239 +      * system crashes. Even though the function will be executed off the
   1.240 +      * main-thread, it might still affect the overall performance of any
   1.241 +      * running application.
   1.242 +      *
   1.243 +      * @throws {OS.File.Error} In case of I/O error.
   1.244 +      */
   1.245 +     File.prototype.flush = function flush() {
   1.246 +       throw_on_negative("flush", UnixFile.fsync(this.fd), this._path);
   1.247 +     };
   1.248 +
   1.249 +     // The default unix mode for opening (0600)
   1.250 +     const DEFAULT_UNIX_MODE = 384;
   1.251 +
   1.252 +     /**
   1.253 +      * Open a file
   1.254 +      *
   1.255 +      * @param {string} path The path to the file.
   1.256 +      * @param {*=} mode The opening mode for the file, as
   1.257 +      * an object that may contain the following fields:
   1.258 +      *
   1.259 +      * - {bool} truncate If |true|, the file will be opened
   1.260 +      *  for writing. If the file does not exist, it will be
   1.261 +      *  created. If the file exists, its contents will be
   1.262 +      *  erased. Cannot be specified with |create|.
   1.263 +      * - {bool} create If |true|, the file will be opened
   1.264 +      *  for writing. If the file exists, this function fails.
   1.265 +      *  If the file does not exist, it will be created. Cannot
   1.266 +      *  be specified with |truncate| or |existing|.
   1.267 +      * - {bool} existing. If the file does not exist, this function
   1.268 +      *  fails. Cannot be specified with |create|.
   1.269 +      * - {bool} read If |true|, the file will be opened for
   1.270 +      *  reading. The file may also be opened for writing, depending
   1.271 +      *  on the other fields of |mode|.
   1.272 +      * - {bool} write If |true|, the file will be opened for
   1.273 +      *  writing. The file may also be opened for reading, depending
   1.274 +      *  on the other fields of |mode|.
   1.275 +      * - {bool} append If |true|, the file will be opened for appending,
   1.276 +      *  meaning the equivalent of |.setPosition(0, POS_END)| is executed
   1.277 +      *  before each write. The default is |true|, i.e. opening a file for
   1.278 +      *  appending. Specify |append: false| to open the file in regular mode.
   1.279 +      *
   1.280 +      * If neither |truncate|, |create| or |write| is specified, the file
   1.281 +      * is opened for reading.
   1.282 +      *
   1.283 +      * Note that |false|, |null| or |undefined| flags are simply ignored.
   1.284 +      *
   1.285 +      * @param {*=} options Additional options for file opening. This
   1.286 +      * implementation interprets the following fields:
   1.287 +      *
   1.288 +      * - {number} unixFlags If specified, file opening flags, as
   1.289 +      *  per libc function |open|. Replaces |mode|.
   1.290 +      * - {number} unixMode If specified, a file creation mode,
   1.291 +      *  as per libc function |open|. If unspecified, files are
   1.292 +      *  created with a default mode of 0600 (file is private to the
   1.293 +      *  user, the user can read and write).
   1.294 +      *
   1.295 +      * @return {File} A file object.
   1.296 +      * @throws {OS.File.Error} If the file could not be opened.
   1.297 +      */
   1.298 +     File.open = function Unix_open(path, mode, options = {}) {
   1.299 +       let omode = options.unixMode !== undefined ?
   1.300 +                     options.unixMode : DEFAULT_UNIX_MODE;
   1.301 +       let flags;
   1.302 +       if (options.unixFlags !== undefined) {
   1.303 +         flags = options.unixFlags;
   1.304 +       } else {
   1.305 +         mode = OS.Shared.AbstractFile.normalizeOpenMode(mode);
   1.306 +         // Handle read/write
   1.307 +         if (!mode.write) {
   1.308 +           flags = Const.O_RDONLY;
   1.309 +         } else if (mode.read) {
   1.310 +           flags = Const.O_RDWR;
   1.311 +         } else {
   1.312 +           flags = Const.O_WRONLY;
   1.313 +         }
   1.314 +         // Finally, handle create/existing/trunc
   1.315 +         if (mode.trunc) {
   1.316 +           if (mode.existing) {
   1.317 +             flags |= Const.O_TRUNC;
   1.318 +           } else {
   1.319 +             flags |= Const.O_CREAT | Const.O_TRUNC;
   1.320 +           }
   1.321 +         } else if (mode.create) {
   1.322 +           flags |= Const.O_CREAT | Const.O_EXCL;
   1.323 +         } else if (mode.read && !mode.write) {
   1.324 +           // flags are sufficient
   1.325 +         } else if (!mode.existing) {
   1.326 +           flags |= Const.O_CREAT;
   1.327 +         }
   1.328 +         if (mode.append) {
   1.329 +           flags |= Const.O_APPEND;
   1.330 +         }
   1.331 +       }
   1.332 +       return error_or_file(UnixFile.open(path, flags, omode), path);
   1.333 +     };
   1.334 +
   1.335 +     /**
   1.336 +      * Checks if a file exists
   1.337 +      *
   1.338 +      * @param {string} path The path to the file.
   1.339 +      *
   1.340 +      * @return {bool} true if the file exists, false otherwise.
   1.341 +      */
   1.342 +     File.exists = function Unix_exists(path) {
   1.343 +       if (UnixFile.access(path, Const.F_OK) == -1) {
   1.344 +         return false;
   1.345 +       } else {
   1.346 +         return true;
   1.347 +       }
   1.348 +     };
   1.349 +
   1.350 +     /**
   1.351 +      * Remove an existing file.
   1.352 +      *
   1.353 +      * @param {string} path The name of the file.
   1.354 +      * @param {*=} options Additional options.
   1.355 +      *   - {bool} ignoreAbsent If |false|, throw an error if the file does
   1.356 +      *     not exist. |true| by default.
   1.357 +      *
   1.358 +      * @throws {OS.File.Error} In case of I/O error.
   1.359 +      */
   1.360 +     File.remove = function remove(path, options = {}) {
   1.361 +       let result = UnixFile.unlink(path);
   1.362 +       if (result == -1) {
   1.363 +         if ((!("ignoreAbsent" in options) || options.ignoreAbsent) &&
   1.364 +             ctypes.errno == Const.ENOENT) {
   1.365 +           return;
   1.366 +         }
   1.367 +         throw new File.Error("remove", ctypes.errno, path);
   1.368 +       }
   1.369 +     };
   1.370 +
   1.371 +     /**
   1.372 +      * Remove an empty directory.
   1.373 +      *
   1.374 +      * @param {string} path The name of the directory to remove.
   1.375 +      * @param {*=} options Additional options.
   1.376 +      *   - {bool} ignoreAbsent If |false|, throw an error if the directory
   1.377 +      *     does not exist. |true| by default
   1.378 +      */
   1.379 +     File.removeEmptyDir = function removeEmptyDir(path, options = {}) {
   1.380 +       let result = UnixFile.rmdir(path);
   1.381 +       if (result == -1) {
   1.382 +         if ((!("ignoreAbsent" in options) || options.ignoreAbsent) &&
   1.383 +             ctypes.errno == Const.ENOENT) {
   1.384 +           return;
   1.385 +         }
   1.386 +         throw new File.Error("removeEmptyDir", ctypes.errno, path);
   1.387 +       }
   1.388 +     };
   1.389 +
   1.390 +     /**
   1.391 +      * Gets the number of bytes available on disk to the current user.
   1.392 +      *
   1.393 +      * @param {string} sourcePath Platform-specific path to a directory on 
   1.394 +      * the disk to query for free available bytes.
   1.395 +      *
   1.396 +      * @return {number} The number of bytes available for the current user.
   1.397 +      * @throws {OS.File.Error} In case of any error.
   1.398 +      */
   1.399 +     File.getAvailableFreeSpace = function Unix_getAvailableFreeSpace(sourcePath) {
   1.400 +       let fileSystemInfo = new Type.statvfs.implementation();
   1.401 +       let fileSystemInfoPtr = fileSystemInfo.address();
   1.402 +
   1.403 +       throw_on_negative("statvfs",  UnixFile.statvfs(sourcePath, fileSystemInfoPtr));
   1.404 +
   1.405 +       let bytes = new Type.uint64_t.implementation(
   1.406 +                        fileSystemInfo.f_bsize * fileSystemInfo.f_bavail);
   1.407 +
   1.408 +       return bytes.value;
   1.409 +     };
   1.410 +
   1.411 +     /**
   1.412 +      * Default mode for opening directories.
   1.413 +      */
   1.414 +     const DEFAULT_UNIX_MODE_DIR = Const.S_IRWXU;
   1.415 +
   1.416 +     /**
   1.417 +      * Create a directory.
   1.418 +      *
   1.419 +      * @param {string} path The name of the directory.
   1.420 +      * @param {*=} options Additional options. This
   1.421 +      * implementation interprets the following fields:
   1.422 +      *
   1.423 +      * - {number} unixMode If specified, a file creation mode,
   1.424 +      * as per libc function |mkdir|. If unspecified, dirs are
   1.425 +      * created with a default mode of 0700 (dir is private to
   1.426 +      * the user, the user can read, write and execute).
   1.427 +      * - {bool} ignoreExisting If |false|, throw error if the directory
   1.428 +      * already exists. |true| by default
   1.429 +      * - {string} from If specified, the call to |makeDir| creates all the
   1.430 +      * ancestors of |path| that are descendants of |from|. Note that |from|
   1.431 +      * and its existing descendants must be user-writeable and that |path|
   1.432 +      * must be a descendant of |from|.
   1.433 +      * Example:
   1.434 +      *   makeDir(Path.join(profileDir, "foo", "bar"), { from: profileDir });
   1.435 +      *  creates directories profileDir/foo, profileDir/foo/bar
   1.436 +       */
   1.437 +     File._makeDir = function makeDir(path, options = {}) {
   1.438 +       let omode = options.unixMode !== undefined ? options.unixMode : DEFAULT_UNIX_MODE_DIR;
   1.439 +       let result = UnixFile.mkdir(path, omode);
   1.440 +       if (result == -1) {
   1.441 +         if ((!("ignoreExisting" in options) || options.ignoreExisting) &&
   1.442 +             (ctypes.errno == Const.EEXIST || ctypes.errno == Const.EISDIR)) {
   1.443 +           return;
   1.444 +         }
   1.445 +         throw new File.Error("makeDir", ctypes.errno, path);
   1.446 +       }
   1.447 +     };
   1.448 +
   1.449 +     /**
   1.450 +      * Copy a file to a destination.
   1.451 +      *
   1.452 +      * @param {string} sourcePath The platform-specific path at which
   1.453 +      * the file may currently be found.
   1.454 +      * @param {string} destPath The platform-specific path at which the
   1.455 +      * file should be copied.
   1.456 +      * @param {*=} options An object which may contain the following fields:
   1.457 +      *
   1.458 +      * @option {bool} noOverwrite - If set, this function will fail if
   1.459 +      * a file already exists at |destPath|. Otherwise, if this file exists,
   1.460 +      * it will be erased silently.
   1.461 +      *
   1.462 +      * @throws {OS.File.Error} In case of any error.
   1.463 +      *
   1.464 +      * General note: The behavior of this function is defined only when
   1.465 +      * it is called on a single file. If it is called on a directory, the
   1.466 +      * behavior is undefined and may not be the same across all platforms.
   1.467 +      *
   1.468 +      * General note: The behavior of this function with respect to metadata
   1.469 +      * is unspecified. Metadata may or may not be copied with the file. The
   1.470 +      * behavior may not be the same across all platforms.
   1.471 +      */
   1.472 +     File.copy = null;
   1.473 +
   1.474 +     /**
   1.475 +      * Move a file to a destination.
   1.476 +      *
   1.477 +      * @param {string} sourcePath The platform-specific path at which
   1.478 +      * the file may currently be found.
   1.479 +      * @param {string} destPath The platform-specific path at which the
   1.480 +      * file should be moved.
   1.481 +      * @param {*=} options An object which may contain the following fields:
   1.482 +      *
   1.483 +      * @option {bool} noOverwrite - If set, this function will fail if
   1.484 +      * a file already exists at |destPath|. Otherwise, if this file exists,
   1.485 +      * it will be erased silently.
   1.486 +      * @option {bool} noCopy - If set, this function will fail if the
   1.487 +      * operation is more sophisticated than a simple renaming, i.e. if
   1.488 +      * |sourcePath| and |destPath| are not situated on the same device.
   1.489 +      *
   1.490 +      * @throws {OS.File.Error} In case of any error.
   1.491 +      *
   1.492 +      * General note: The behavior of this function is defined only when
   1.493 +      * it is called on a single file. If it is called on a directory, the
   1.494 +      * behavior is undefined and may not be the same across all platforms.
   1.495 +      *
   1.496 +      * General note: The behavior of this function with respect to metadata
   1.497 +      * is unspecified. Metadata may or may not be moved with the file. The
   1.498 +      * behavior may not be the same across all platforms.
   1.499 +      */
   1.500 +     File.move = null;
   1.501 +
   1.502 +     if (UnixFile.copyfile) {
   1.503 +       // This implementation uses |copyfile(3)|, from the BSD library.
   1.504 +       // Adding copying of hierarchies and/or attributes is just a flag
   1.505 +       // away.
   1.506 +       File.copy = function copyfile(sourcePath, destPath, options = {}) {
   1.507 +         let flags = Const.COPYFILE_DATA;
   1.508 +         if (options.noOverwrite) {
   1.509 +           flags |= Const.COPYFILE_EXCL;
   1.510 +         }
   1.511 +         throw_on_negative("copy",
   1.512 +           UnixFile.copyfile(sourcePath, destPath, null, flags),
   1.513 +           sourcePath
   1.514 +         );
   1.515 +       };
   1.516 +     } else {
   1.517 +       // If the OS does not implement file copying for us, we need to
   1.518 +       // implement it ourselves. For this purpose, we need to define
   1.519 +       // a pumping function.
   1.520 +
   1.521 +       /**
   1.522 +        * Copy bytes from one file to another one.
   1.523 +        *
   1.524 +        * @param {File} source The file containing the data to be copied. It
   1.525 +        * should be opened for reading.
   1.526 +        * @param {File} dest The file to which the data should be written. It
   1.527 +        * should be opened for writing.
   1.528 +        * @param {*=} options An object which may contain the following fields:
   1.529 +        *
   1.530 +        * @option {number} nbytes The maximal number of bytes to
   1.531 +        * copy. If unspecified, copy everything from the current
   1.532 +        * position.
   1.533 +        * @option {number} bufSize A hint regarding the size of the
   1.534 +        * buffer to use for copying. The implementation may decide to
   1.535 +        * ignore this hint.
   1.536 +        * @option {bool} unixUserland Will force the copy operation to be
   1.537 +        * caried out in user land, instead of using optimized syscalls such
   1.538 +        * as splice(2).
   1.539 +        *
   1.540 +        * @throws {OS.File.Error} In case of error.
   1.541 +        */
   1.542 +       let pump;
   1.543 +
   1.544 +       // A buffer used by |pump_userland|
   1.545 +       let pump_buffer = null;
   1.546 +
   1.547 +       // An implementation of |pump| using |read|/|write|
   1.548 +       let pump_userland = function pump_userland(source, dest, options = {}) {
   1.549 +         let bufSize = options.bufSize > 0 ? options.bufSize : 4096;
   1.550 +         let nbytes = options.nbytes > 0 ? options.nbytes : Infinity;
   1.551 +         if (!pump_buffer || pump_buffer.length < bufSize) {
   1.552 +           pump_buffer = new (ctypes.ArrayType(ctypes.char))(bufSize);
   1.553 +         }
   1.554 +         let read = source._read.bind(source);
   1.555 +         let write = dest._write.bind(dest);
   1.556 +         // Perform actual copy
   1.557 +         let total_read = 0;
   1.558 +         while (true) {
   1.559 +           let chunk_size = Math.min(nbytes, bufSize);
   1.560 +           let bytes_just_read = read(pump_buffer, bufSize);
   1.561 +           if (bytes_just_read == 0) {
   1.562 +             return total_read;
   1.563 +           }
   1.564 +           total_read += bytes_just_read;
   1.565 +           let bytes_written = 0;
   1.566 +           do {
   1.567 +             bytes_written += write(
   1.568 +               pump_buffer.addressOfElement(bytes_written),
   1.569 +               bytes_just_read - bytes_written
   1.570 +             );
   1.571 +           } while (bytes_written < bytes_just_read);
   1.572 +           nbytes -= bytes_written;
   1.573 +           if (nbytes <= 0) {
   1.574 +             return total_read;
   1.575 +           }
   1.576 +         }
   1.577 +       };
   1.578 +
   1.579 +       // Fortunately, under Linux, that pumping function can be optimized.
   1.580 +       if (UnixFile.splice) {
   1.581 +         const BUFSIZE = 1 << 17;
   1.582 +
   1.583 +         // An implementation of |pump| using |splice| (for Linux/Android)
   1.584 +         pump = function pump_splice(source, dest, options = {}) {
   1.585 +           let nbytes = options.nbytes > 0 ? options.nbytes : Infinity;
   1.586 +           let pipe = [];
   1.587 +           throw_on_negative("pump", UnixFile.pipe(pipe));
   1.588 +           let pipe_read = pipe[0];
   1.589 +           let pipe_write = pipe[1];
   1.590 +           let source_fd = source.fd;
   1.591 +           let dest_fd = dest.fd;
   1.592 +           let total_read = 0;
   1.593 +           let total_written = 0;
   1.594 +           try {
   1.595 +             while (true) {
   1.596 +               let chunk_size = Math.min(nbytes, BUFSIZE);
   1.597 +               let bytes_read = throw_on_negative("pump",
   1.598 +                 UnixFile.splice(source_fd, null,
   1.599 +                 pipe_write, null, chunk_size, 0)
   1.600 +               );
   1.601 +               if (!bytes_read) {
   1.602 +                 break;
   1.603 +               }
   1.604 +               total_read += bytes_read;
   1.605 +               let bytes_written = throw_on_negative(
   1.606 +                 "pump",
   1.607 +                 UnixFile.splice(pipe_read, null,
   1.608 +                   dest_fd, null, bytes_read,
   1.609 +                     (bytes_read == chunk_size)?Const.SPLICE_F_MORE:0
   1.610 +               ));
   1.611 +               if (!bytes_written) {
   1.612 +                 // This should never happen
   1.613 +                 throw new Error("Internal error: pipe disconnected");
   1.614 +               }
   1.615 +               total_written += bytes_written;
   1.616 +               nbytes -= bytes_read;
   1.617 +               if (!nbytes) {
   1.618 +                 break;
   1.619 +               }
   1.620 +             }
   1.621 +             return total_written;
   1.622 +           } catch (x) {
   1.623 +             if (x.unixErrno == Const.EINVAL) {
   1.624 +               // We *might* be on a file system that does not support splice.
   1.625 +               // Try again with a fallback pump.
   1.626 +               if (total_read) {
   1.627 +                 source.setPosition(-total_read, File.POS_CURRENT);
   1.628 +               }
   1.629 +               if (total_written) {
   1.630 +                 dest.setPosition(-total_written, File.POS_CURRENT);
   1.631 +               }
   1.632 +               return pump_userland(source, dest, options);
   1.633 +             }
   1.634 +             throw x;
   1.635 +           } finally {
   1.636 +             pipe_read.dispose();
   1.637 +             pipe_write.dispose();
   1.638 +           }
   1.639 +         };
   1.640 +       } else {
   1.641 +         // Fallback implementation of pump for other Unix platforms.
   1.642 +         pump = pump_userland;
   1.643 +       }
   1.644 +
   1.645 +       // Implement |copy| using |pump|.
   1.646 +       // This implementation would require some work before being able to
   1.647 +       // copy directories
   1.648 +       File.copy = function copy(sourcePath, destPath, options = {}) {
   1.649 +         let source, dest;
   1.650 +         let result;
   1.651 +         try {
   1.652 +           source = File.open(sourcePath);
   1.653 +           // Need to open the output file with |append:false|, or else |splice|
   1.654 +           // won't work.
   1.655 +           if (options.noOverwrite) {
   1.656 +             dest = File.open(destPath, {create:true, append:false});
   1.657 +           } else {
   1.658 +             dest = File.open(destPath, {trunc:true, append:false});
   1.659 +           }
   1.660 +           if (options.unixUserland) {
   1.661 +             result = pump_userland(source, dest, options);
   1.662 +           } else {
   1.663 +             result = pump(source, dest, options);
   1.664 +           }
   1.665 +         } catch (x) {
   1.666 +           if (dest) {
   1.667 +             dest.close();
   1.668 +           }
   1.669 +           if (source) {
   1.670 +             source.close();
   1.671 +           }
   1.672 +           throw x;
   1.673 +         }
   1.674 +       };
   1.675 +     } // End of definition of copy
   1.676 +
   1.677 +     // Implement |move| using |rename| (wherever possible) or |copy|
   1.678 +     // (if files are on distinct devices).
   1.679 +     File.move = function move(sourcePath, destPath, options = {}) {
   1.680 +       // An implementation using |rename| whenever possible or
   1.681 +       // |File.pump| when required, for other Unices.
   1.682 +       // It can move directories on one file system, not
   1.683 +       // across file systems
   1.684 +
   1.685 +       // If necessary, fail if the destination file exists
   1.686 +       if (options.noOverwrite) {
   1.687 +         let fd = UnixFile.open(destPath, Const.O_RDONLY, 0);
   1.688 +         if (fd != -1) {
   1.689 +           fd.dispose();
   1.690 +           // The file exists and we have access
   1.691 +           throw new File.Error("move", Const.EEXIST, sourcePath);
   1.692 +         } else if (ctypes.errno == Const.EACCESS) {
   1.693 +           // The file exists and we don't have access
   1.694 +           throw new File.Error("move", Const.EEXIST, sourcePath);
   1.695 +         }
   1.696 +       }
   1.697 +
   1.698 +       // If we can, rename the file
   1.699 +       let result = UnixFile.rename(sourcePath, destPath);
   1.700 +       if (result != -1)
   1.701 +         return;
   1.702 +
   1.703 +       // If the error is not EXDEV ("not on the same device"),
   1.704 +       // or if the error is EXDEV and we have passed an option
   1.705 +       // that prevents us from crossing devices, throw the
   1.706 +       // error.
   1.707 +       if (ctypes.errno != Const.EXDEV || options.noCopy) {
   1.708 +         throw new File.Error("move", ctypes.errno, sourcePath);
   1.709 +       }
   1.710 +
   1.711 +       // Otherwise, copy and remove.
   1.712 +       File.copy(sourcePath, destPath, options);
   1.713 +       // FIXME: Clean-up in case of copy error?
   1.714 +       File.remove(sourcePath);
   1.715 +     };
   1.716 +
   1.717 +     File.unixSymLink = function unixSymLink(sourcePath, destPath) {
   1.718 +       throw_on_negative("symlink", UnixFile.symlink(sourcePath, destPath),
   1.719 +           sourcePath);
   1.720 +     };
   1.721 +
   1.722 +     /**
   1.723 +      * Iterate on one directory.
   1.724 +      *
   1.725 +      * This iterator will not enter subdirectories.
   1.726 +      *
   1.727 +      * @param {string} path The directory upon which to iterate.
   1.728 +      * @param {*=} options Ignored in this implementation.
   1.729 +      *
   1.730 +      * @throws {File.Error} If |path| does not represent a directory or
   1.731 +      * if the directory cannot be iterated.
   1.732 +      * @constructor
   1.733 +      */
   1.734 +     File.DirectoryIterator = function DirectoryIterator(path, options) {
   1.735 +       exports.OS.Shared.AbstractFile.AbstractIterator.call(this);
   1.736 +       this._path = path;
   1.737 +       this._dir = UnixFile.opendir(this._path);
   1.738 +       if (this._dir == null) {
   1.739 +         let error = ctypes.errno;
   1.740 +         if (error != Const.ENOENT) {
   1.741 +           throw new File.Error("DirectoryIterator", error, path);
   1.742 +         }
   1.743 +         this._exists = false;
   1.744 +         this._closed = true;
   1.745 +       } else {
   1.746 +         this._exists = true;
   1.747 +         this._closed = false;
   1.748 +       }
   1.749 +     };
   1.750 +     File.DirectoryIterator.prototype = Object.create(exports.OS.Shared.AbstractFile.AbstractIterator.prototype);
   1.751 +
   1.752 +     /**
   1.753 +      * Return the next entry in the directory, if any such entry is
   1.754 +      * available.
   1.755 +      *
   1.756 +      * Skip special directories "." and "..".
   1.757 +      *
   1.758 +      * @return {File.Entry} The next entry in the directory.
   1.759 +      * @throws {StopIteration} Once all files in the directory have been
   1.760 +      * encountered.
   1.761 +      */
   1.762 +     File.DirectoryIterator.prototype.next = function next() {
   1.763 +       if (!this._exists) {
   1.764 +         throw File.Error.noSuchFile("DirectoryIterator.prototype.next", this._path);
   1.765 +       }
   1.766 +       if (this._closed) {
   1.767 +         throw StopIteration;
   1.768 +       }
   1.769 +       for (let entry = UnixFile.readdir(this._dir);
   1.770 +            entry != null && !entry.isNull();
   1.771 +            entry = UnixFile.readdir(this._dir)) {
   1.772 +         let contents = entry.contents;
   1.773 +         let name = contents.d_name.readString();
   1.774 +         if (name == "." || name == "..") {
   1.775 +           continue;
   1.776 +         }
   1.777 +
   1.778 +         let isDir, isSymLink;
   1.779 +         if (!("d_type" in contents)) {
   1.780 +           // |dirent| doesn't have d_type on some platforms (e.g. Solaris).
   1.781 +           let path = Path.join(this._path, name);
   1.782 +           throw_on_negative("lstat", UnixFile.lstat(path, gStatDataPtr), this._path);
   1.783 +           isDir = (gStatData.st_mode & Const.S_IFMT) == Const.S_IFDIR;
   1.784 +           isSymLink = (gStatData.st_mode & Const.S_IFMT) == Const.S_IFLNK;
   1.785 +         } else {
   1.786 +           isDir = contents.d_type == Const.DT_DIR;
   1.787 +           isSymLink = contents.d_type == Const.DT_LNK;
   1.788 +         }
   1.789 +
   1.790 +         return new File.DirectoryIterator.Entry(isDir, isSymLink, name, this._path);
   1.791 +       }
   1.792 +       this.close();
   1.793 +       throw StopIteration;
   1.794 +     };
   1.795 +
   1.796 +     /**
   1.797 +      * Close the iterator and recover all resources.
   1.798 +      * You should call this once you have finished iterating on a directory.
   1.799 +      */
   1.800 +     File.DirectoryIterator.prototype.close = function close() {
   1.801 +       if (this._closed) return;
   1.802 +       this._closed = true;
   1.803 +       UnixFile.closedir(this._dir);
   1.804 +       this._dir = null;
   1.805 +     };
   1.806 +
   1.807 +    /**
   1.808 +     * Determine whether the directory exists.
   1.809 +     *
   1.810 +     * @return {boolean}
   1.811 +     */
   1.812 +     File.DirectoryIterator.prototype.exists = function exists() {
   1.813 +       return this._exists;
   1.814 +     };
   1.815 +
   1.816 +     /**
   1.817 +      * Return directory as |File|
   1.818 +      */
   1.819 +     File.DirectoryIterator.prototype.unixAsFile = function unixAsFile() {
   1.820 +       if (!this._dir) throw File.Error.closed("unixAsFile", this._path);
   1.821 +       return error_or_file(UnixFile.dirfd(this._dir), this._path);
   1.822 +     };
   1.823 +
   1.824 +     /**
   1.825 +      * An entry in a directory.
   1.826 +      */
   1.827 +     File.DirectoryIterator.Entry = function Entry(isDir, isSymLink, name, parent) {
   1.828 +       // Copy the relevant part of |unix_entry| to ensure that
   1.829 +       // our data is not overwritten prematurely.
   1.830 +       this._parent = parent;
   1.831 +       let path = Path.join(this._parent, name);
   1.832 +
   1.833 +       SysAll.AbstractEntry.call(this, isDir, isSymLink, name, path);
   1.834 +     };
   1.835 +     File.DirectoryIterator.Entry.prototype = Object.create(SysAll.AbstractEntry.prototype);
   1.836 +
   1.837 +     /**
   1.838 +      * Return a version of an instance of
   1.839 +      * File.DirectoryIterator.Entry that can be sent from a worker
   1.840 +      * thread to the main thread. Note that deserialization is
   1.841 +      * asymmetric and returns an object with a different
   1.842 +      * implementation.
   1.843 +      */
   1.844 +     File.DirectoryIterator.Entry.toMsg = function toMsg(value) {
   1.845 +       if (!value instanceof File.DirectoryIterator.Entry) {
   1.846 +         throw new TypeError("parameter of " +
   1.847 +           "File.DirectoryIterator.Entry.toMsg must be a " +
   1.848 +           "File.DirectoryIterator.Entry");
   1.849 +       }
   1.850 +       let serialized = {};
   1.851 +       for (let key in File.DirectoryIterator.Entry.prototype) {
   1.852 +         serialized[key] = value[key];
   1.853 +       }
   1.854 +       return serialized;
   1.855 +     };
   1.856 +
   1.857 +     let gStatData = new Type.stat.implementation();
   1.858 +     let gStatDataPtr = gStatData.address();
   1.859 +     let gTimevals = new Type.timevals.implementation();
   1.860 +     let gTimevalsPtr = gTimevals.address();
   1.861 +     let MODE_MASK = 4095 /*= 07777*/;
   1.862 +     File.Info = function Info(stat, path) {
   1.863 +       let isDir = (stat.st_mode & Const.S_IFMT) == Const.S_IFDIR;
   1.864 +       let isSymLink = (stat.st_mode & Const.S_IFMT) == Const.S_IFLNK;
   1.865 +       let size = Type.off_t.importFromC(stat.st_size);
   1.866 +
   1.867 +       let lastAccessDate = new Date(stat.st_atime * 1000);
   1.868 +       let lastModificationDate = new Date(stat.st_mtime * 1000);
   1.869 +       let unixLastStatusChangeDate = new Date(stat.st_ctime * 1000);
   1.870 +
   1.871 +       let unixOwner = Type.uid_t.importFromC(stat.st_uid);
   1.872 +       let unixGroup = Type.gid_t.importFromC(stat.st_gid);
   1.873 +       let unixMode = Type.mode_t.importFromC(stat.st_mode & MODE_MASK);
   1.874 +
   1.875 +       SysAll.AbstractInfo.call(this, path, isDir, isSymLink, size,
   1.876 +           lastAccessDate, lastModificationDate, unixLastStatusChangeDate,
   1.877 +           unixOwner, unixGroup, unixMode);
   1.878 +
   1.879 +       // Some platforms (e.g. MacOS X, some BSDs) store a file creation date
   1.880 +       if ("OSFILE_OFFSETOF_STAT_ST_BIRTHTIME" in Const) {
   1.881 +         let date = new Date(stat.st_birthtime * 1000);
   1.882 +
   1.883 +        /**
   1.884 +         * The date of creation of this file.
   1.885 +         *
   1.886 +         * Note that the date returned by this method is not always
   1.887 +         * reliable. Not all file systems are able to provide this
   1.888 +         * information.
   1.889 +         *
   1.890 +         * @type {Date}
   1.891 +         */
   1.892 +         this.macBirthDate = date;
   1.893 +       }
   1.894 +     };
   1.895 +     File.Info.prototype = Object.create(SysAll.AbstractInfo.prototype);
   1.896 +
   1.897 +     // Deprecated, use macBirthDate/winBirthDate instead
   1.898 +     Object.defineProperty(File.Info.prototype, "creationDate", {
   1.899 +      get: function creationDate() {
   1.900 +        // On the Macintosh, returns the birth date if available.
   1.901 +        // On other Unix, as the birth date is not available,
   1.902 +        // returns the epoch.
   1.903 +        return this.macBirthDate || new Date(0);
   1.904 +      }
   1.905 +     });
   1.906 +
   1.907 +     /**
   1.908 +      * Return a version of an instance of File.Info that can be sent
   1.909 +      * from a worker thread to the main thread. Note that deserialization
   1.910 +      * is asymmetric and returns an object with a different implementation.
   1.911 +      */
   1.912 +     File.Info.toMsg = function toMsg(stat) {
   1.913 +       if (!stat instanceof File.Info) {
   1.914 +         throw new TypeError("parameter of File.Info.toMsg must be a File.Info");
   1.915 +       }
   1.916 +       let serialized = {};
   1.917 +       for (let key in File.Info.prototype) {
   1.918 +         serialized[key] = stat[key];
   1.919 +       }
   1.920 +       return serialized;
   1.921 +     };
   1.922 +
   1.923 +     /**
   1.924 +      * Fetch the information on a file.
   1.925 +      *
   1.926 +      * @param {string} path The full name of the file to open.
   1.927 +      * @param {*=} options Additional options. In this implementation:
   1.928 +      *
   1.929 +      * - {bool} unixNoFollowingLinks If set and |true|, if |path|
   1.930 +      * represents a symbolic link, the call will return the information
   1.931 +      * of the link itself, rather than that of the target file.
   1.932 +      *
   1.933 +      * @return {File.Information}
   1.934 +      */
   1.935 +     File.stat = function stat(path, options = {}) {
   1.936 +       if (options.unixNoFollowingLinks) {
   1.937 +         throw_on_negative("stat", UnixFile.lstat(path, gStatDataPtr), path);
   1.938 +       } else {
   1.939 +         throw_on_negative("stat", UnixFile.stat(path, gStatDataPtr), path);
   1.940 +       }
   1.941 +       return new File.Info(gStatData, path);
   1.942 +     };
   1.943 +
   1.944 +     /**
   1.945 +      * Set the file's access permissions.  Without any options, the
   1.946 +      * permissions are set to an approximation of what they would
   1.947 +      * have been if the file had been created in its current
   1.948 +      * directory in the "most typical" fashion for the operating
   1.949 +      * system.  In the current implementation, this means we set
   1.950 +      * the POSIX file mode to (0666 & ~umask).
   1.951 +      *
   1.952 +      * This operation is likely to fail if applied to a file that was
   1.953 +      * not created by the currently running program (more precisely,
   1.954 +      * if it was created by a program running under a different OS-level
   1.955 +      * user account).  It may also fail, or silently do nothing, if the
   1.956 +      * filesystem containing the file does not support access permissions.
   1.957 +      *
   1.958 +      * @param {string} path   The name of the file to reset the permissions of.
   1.959 +      * @param {*=} options
   1.960 +      * - {number} unixMode     If present, the POSIX file mode is set to
   1.961 +      *                         exactly this value, unless |unixHonorUmask| is
   1.962 +      *                         also present.
   1.963 +      * - {bool} unixHonorUmask If true, any |unixMode| value is modified by
   1.964 +      *                         the process umask, as open() would have done.
   1.965 +      */
   1.966 +     File.setPermissions = function setPermissions(path, options = {}) {
   1.967 +       throw_on_negative("setPermissions",
   1.968 +                         UnixFile.chmod(path, unixMode(options)),
   1.969 +                         path);
   1.970 +     };
   1.971 +
   1.972 +     /**
   1.973 +      * Set the last access and modification date of the file.
   1.974 +      * The time stamp resolution is 1 second at best, but might be worse
   1.975 +      * depending on the platform.
   1.976 +      *
   1.977 +      * @param {string} path The full name of the file to set the dates for.
   1.978 +      * @param {Date,number=} accessDate The last access date. If numeric,
   1.979 +      * milliseconds since epoch. If omitted or null, then the current date
   1.980 +      * will be used.
   1.981 +      * @param {Date,number=} modificationDate The last modification date. If
   1.982 +      * numeric, milliseconds since epoch. If omitted or null, then the current
   1.983 +      * date will be used.
   1.984 +      *
   1.985 +      * @throws {TypeError} In case of invalid paramters.
   1.986 +      * @throws {OS.File.Error} In case of I/O error.
   1.987 +      */
   1.988 +     File.setDates = function setDates(path, accessDate, modificationDate) {
   1.989 +       accessDate = normalizeDate("File.setDates", accessDate);
   1.990 +       modificationDate = normalizeDate("File.setDates", modificationDate);
   1.991 +       gTimevals[0].tv_sec = (accessDate / 1000) | 0;
   1.992 +       gTimevals[0].tv_usec = 0;
   1.993 +       gTimevals[1].tv_sec = (modificationDate / 1000) | 0;
   1.994 +       gTimevals[1].tv_usec = 0;
   1.995 +       throw_on_negative("setDates",
   1.996 +                         UnixFile.utimes(path, gTimevalsPtr),
   1.997 +                         path);
   1.998 +     };
   1.999 +
  1.1000 +     File.read = exports.OS.Shared.AbstractFile.read;
  1.1001 +     File.writeAtomic = exports.OS.Shared.AbstractFile.writeAtomic;
  1.1002 +     File.openUnique = exports.OS.Shared.AbstractFile.openUnique;
  1.1003 +     File.makeDir = exports.OS.Shared.AbstractFile.makeDir;
  1.1004 +
  1.1005 +     /**
  1.1006 +      * Remove an existing directory and its contents.
  1.1007 +      *
  1.1008 +      * @param {string} path The name of the directory.
  1.1009 +      * @param {*=} options Additional options.
  1.1010 +      *   - {bool} ignoreAbsent If |false|, throw an error if the directory doesn't
  1.1011 +      *     exist. |true| by default.
  1.1012 +      *   - {boolean} ignorePermissions If |true|, remove the file even when lacking write
  1.1013 +      *     permission.
  1.1014 +      *
  1.1015 +      * @throws {OS.File.Error} In case of I/O error, in particular if |path| is
  1.1016 +      *         not a directory.
  1.1017 +      *
  1.1018 +      * Note: This function will remove a symlink even if it points a directory.
  1.1019 +      */
  1.1020 +     File.removeDir = function(path, options) {
  1.1021 +       let isSymLink;
  1.1022 +       try {
  1.1023 +         let info = File.stat(path, {unixNoFollowingLinks: true});
  1.1024 +         isSymLink = info.isSymLink;
  1.1025 +       } catch (e) {
  1.1026 +         if ((!("ignoreAbsent" in options) || options.ignoreAbsent) &&
  1.1027 +             ctypes.errno == Const.ENOENT) {
  1.1028 +           return;
  1.1029 +         }
  1.1030 +         throw e;
  1.1031 +       }
  1.1032 +       if (isSymLink) {
  1.1033 +         // A Unix symlink itself is not a directory even if it points
  1.1034 +         // a directory.
  1.1035 +         File.remove(path, options);
  1.1036 +         return;
  1.1037 +       }
  1.1038 +       exports.OS.Shared.AbstractFile.removeRecursive(path, options);
  1.1039 +     };
  1.1040 +
  1.1041 +     /**
  1.1042 +      * Get the current directory by getCurrentDirectory.
  1.1043 +      */
  1.1044 +     File.getCurrentDirectory = function getCurrentDirectory() {
  1.1045 +       let path = UnixFile.get_current_dir_name?UnixFile.get_current_dir_name():
  1.1046 +         UnixFile.getwd_auto(null);
  1.1047 +       throw_on_null("getCurrentDirectory", path);
  1.1048 +       return path.readString();
  1.1049 +     };
  1.1050 +
  1.1051 +     /**
  1.1052 +      * Set the current directory by setCurrentDirectory.
  1.1053 +      */
  1.1054 +     File.setCurrentDirectory = function setCurrentDirectory(path) {
  1.1055 +       throw_on_negative("setCurrentDirectory",
  1.1056 +         UnixFile.chdir(path),
  1.1057 +         path
  1.1058 +       );
  1.1059 +     };
  1.1060 +
  1.1061 +     /**
  1.1062 +      * Get/set the current directory.
  1.1063 +      */
  1.1064 +     Object.defineProperty(File, "curDir", {
  1.1065 +         set: function(path) {
  1.1066 +           this.setCurrentDirectory(path);
  1.1067 +         },
  1.1068 +         get: function() {
  1.1069 +           return this.getCurrentDirectory();
  1.1070 +         }
  1.1071 +       }
  1.1072 +     );
  1.1073 +
  1.1074 +     // Utility functions
  1.1075 +
  1.1076 +     /**
  1.1077 +      * Turn the result of |open| into an Error or a File
  1.1078 +      * @param {number} maybe The result of the |open| operation that may
  1.1079 +      * represent either an error or a success. If -1, this function raises
  1.1080 +      * an error holding ctypes.errno, otherwise it returns the opened file.
  1.1081 +      * @param {string=} path The path of the file.
  1.1082 +      */
  1.1083 +     function error_or_file(maybe, path) {
  1.1084 +       if (maybe == -1) {
  1.1085 +         throw new File.Error("open", ctypes.errno, path);
  1.1086 +       }
  1.1087 +       return new File(maybe, path);
  1.1088 +     }
  1.1089 +
  1.1090 +     /**
  1.1091 +      * Utility function to sort errors represented as "-1" from successes.
  1.1092 +      *
  1.1093 +      * @param {string=} operation The name of the operation. If unspecified,
  1.1094 +      * the name of the caller function.
  1.1095 +      * @param {number} result The result of the operation that may
  1.1096 +      * represent either an error or a success. If -1, this function raises
  1.1097 +      * an error holding ctypes.errno, otherwise it returns |result|.
  1.1098 +      * @param {string=} path The path of the file.
  1.1099 +      */
  1.1100 +     function throw_on_negative(operation, result, path) {
  1.1101 +       if (result < 0) {
  1.1102 +         throw new File.Error(operation, ctypes.errno, path);
  1.1103 +       }
  1.1104 +       return result;
  1.1105 +     }
  1.1106 +
  1.1107 +     /**
  1.1108 +      * Utility function to sort errors represented as |null| from successes.
  1.1109 +      *
  1.1110 +      * @param {string=} operation The name of the operation. If unspecified,
  1.1111 +      * the name of the caller function.
  1.1112 +      * @param {pointer} result The result of the operation that may
  1.1113 +      * represent either an error or a success. If |null|, this function raises
  1.1114 +      * an error holding ctypes.errno, otherwise it returns |result|.
  1.1115 +      * @param {string=} path The path of the file.
  1.1116 +      */
  1.1117 +     function throw_on_null(operation, result, path) {
  1.1118 +       if (result == null || (result.isNull && result.isNull())) {
  1.1119 +         throw new File.Error(operation, ctypes.errno, path);
  1.1120 +       }
  1.1121 +       return result;
  1.1122 +     }
  1.1123 +
  1.1124 +     /**
  1.1125 +      * Normalize and verify a Date or numeric date value.
  1.1126 +      *
  1.1127 +      * @param {string} fn Function name of the calling function.
  1.1128 +      * @param {Date,number} date The date to normalize. If omitted or null,
  1.1129 +      * then the current date will be used.
  1.1130 +      *
  1.1131 +      * @throws {TypeError} Invalid date provided.
  1.1132 +      *
  1.1133 +      * @return {number} Sanitized, numeric date in milliseconds since epoch.
  1.1134 +      */
  1.1135 +     function normalizeDate(fn, date) {
  1.1136 +       if (typeof date !== "number" && !date) {
  1.1137 +         // |date| was Omitted or null.
  1.1138 +         date = Date.now();
  1.1139 +       } else if (typeof date.getTime === "function") {
  1.1140 +         // Input might be a date or date-like object.
  1.1141 +         date = date.getTime();
  1.1142 +       }
  1.1143 +
  1.1144 +       if (isNaN(date)) {
  1.1145 +         throw new TypeError("|date| parameter of " + fn + " must be a " +
  1.1146 +                             "|Date| instance or number");
  1.1147 +       }
  1.1148 +       return date;
  1.1149 +     };
  1.1150 +
  1.1151 +     /**
  1.1152 +      * Helper used by both versions of setPermissions.
  1.1153 +      */
  1.1154 +     function unixMode(options) {
  1.1155 +       let mode = 438; /* 0666 */
  1.1156 +       let unixHonorUmask = true;
  1.1157 +       if ("unixMode" in options) {
  1.1158 +         unixHonorUmask = false;
  1.1159 +         mode = options.unixMode;
  1.1160 +       }
  1.1161 +       if ("unixHonorUmask" in options) {
  1.1162 +         unixHonorUmask = options.unixHonorUmask;
  1.1163 +       }
  1.1164 +       if (unixHonorUmask) {
  1.1165 +         mode &= ~SharedAll.Constants.Sys.umask;
  1.1166 +       }
  1.1167 +       return mode;
  1.1168 +     }
  1.1169 +
  1.1170 +     File.Unix = exports.OS.Unix.File;
  1.1171 +     File.Error = SysAll.Error;
  1.1172 +     exports.OS.File = File;
  1.1173 +     exports.OS.Shared.Type = Type;
  1.1174 +
  1.1175 +     Object.defineProperty(File, "POS_START", { value: SysAll.POS_START });
  1.1176 +     Object.defineProperty(File, "POS_CURRENT", { value: SysAll.POS_CURRENT });
  1.1177 +     Object.defineProperty(File, "POS_END", { value: SysAll.POS_END });
  1.1178 +   })(this);
  1.1179 +}

mercurial