toolkit/components/osfile/modules/osfile_win_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_win_front.jsm	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1183 @@
     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 + * Windows 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_win_front.jsm to be used directly as a main thread
    1.18 +    // module yet.
    1.19 +    throw new Error("osfile_win_front.jsm cannot be used from the main thread yet");
    1.20 +  }
    1.21 +
    1.22 +  (function(exports) {
    1.23 +     "use strict";
    1.24 +
    1.25 +
    1.26 +      // exports.OS.Win is created by osfile_win_back.jsm
    1.27 +     if (exports.OS && exports.OS.File) {
    1.28 +        return; // Avoid double-initialization
    1.29 +     }
    1.30 +
    1.31 +     let SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm");
    1.32 +     let Path = require("resource://gre/modules/osfile/ospath.jsm");
    1.33 +     let SysAll = require("resource://gre/modules/osfile/osfile_win_allthreads.jsm");
    1.34 +     exports.OS.Win.File._init();
    1.35 +     let Const = exports.OS.Constants.Win;
    1.36 +     let WinFile = exports.OS.Win.File;
    1.37 +     let Type = WinFile.Type;
    1.38 +
    1.39 +     // Mutable thread-global data
    1.40 +     // In the Windows implementation, methods |read| and |write|
    1.41 +     // require passing a pointer to an uint32 to determine how many
    1.42 +     // bytes have been read/written. In C, this is a benigne operation,
    1.43 +     // but in js-ctypes, this has a cost. Rather than re-allocating a
    1.44 +     // C uint32 and a C uint32* for each |read|/|write|, we take advantage
    1.45 +     // of the fact that the state is thread-private -- hence that two
    1.46 +     // |read|/|write| operations cannot take place at the same time --
    1.47 +     // and we use the following global mutable values:
    1.48 +     let gBytesRead = new ctypes.uint32_t(0);
    1.49 +     let gBytesReadPtr = gBytesRead.address();
    1.50 +     let gBytesWritten = new ctypes.uint32_t(0);
    1.51 +     let gBytesWrittenPtr = gBytesWritten.address();
    1.52 +
    1.53 +     // Same story for GetFileInformationByHandle
    1.54 +     let gFileInfo = new Type.FILE_INFORMATION.implementation();
    1.55 +     let gFileInfoPtr = gFileInfo.address();
    1.56 +
    1.57 +     /**
    1.58 +      * Representation of a file.
    1.59 +      *
    1.60 +      * You generally do not need to call this constructor yourself. Rather,
    1.61 +      * to open a file, use function |OS.File.open|.
    1.62 +      *
    1.63 +      * @param fd A OS-specific file descriptor.
    1.64 +      * @param {string} path File path of the file handle, used for error-reporting.
    1.65 +      * @constructor
    1.66 +      */
    1.67 +     let File = function File(fd, path) {
    1.68 +       exports.OS.Shared.AbstractFile.call(this, fd, path);
    1.69 +       this._closeResult = null;
    1.70 +     };
    1.71 +     File.prototype = Object.create(exports.OS.Shared.AbstractFile.prototype);
    1.72 +
    1.73 +     /**
    1.74 +      * Close the file.
    1.75 +      *
    1.76 +      * This method has no effect if the file is already closed. However,
    1.77 +      * if the first call to |close| has thrown an error, further calls
    1.78 +      * will throw the same error.
    1.79 +      *
    1.80 +      * @throws File.Error If closing the file revealed an error that could
    1.81 +      * not be reported earlier.
    1.82 +      */
    1.83 +     File.prototype.close = function close() {
    1.84 +       if (this._fd) {
    1.85 +         let fd = this._fd;
    1.86 +         this._fd = null;
    1.87 +         // Call |close(fd)|, detach finalizer if any
    1.88 +         // (|fd| may not be a CDataFinalizer if it has been
    1.89 +         // instantiated from a controller thread).
    1.90 +         let result = WinFile._CloseHandle(fd);
    1.91 +         if (typeof fd == "object" && "forget" in fd) {
    1.92 +           fd.forget();
    1.93 +         }
    1.94 +         if (result == -1) {
    1.95 +           this._closeResult = new File.Error("close", ctypes.winLastError, this._path);
    1.96 +         }
    1.97 +       }
    1.98 +       if (this._closeResult) {
    1.99 +         throw this._closeResult;
   1.100 +       }
   1.101 +       return;
   1.102 +     };
   1.103 +
   1.104 +     /**
   1.105 +      * Read some bytes from a file.
   1.106 +      *
   1.107 +      * @param {C pointer} buffer A buffer for holding the data
   1.108 +      * once it is read.
   1.109 +      * @param {number} nbytes The number of bytes to read. It must not
   1.110 +      * exceed the size of |buffer| in bytes but it may exceed the number
   1.111 +      * of bytes unread in the file.
   1.112 +      * @param {*=} options Additional options for reading. Ignored in
   1.113 +      * this implementation.
   1.114 +      *
   1.115 +      * @return {number} The number of bytes effectively read. If zero,
   1.116 +      * the end of the file has been reached.
   1.117 +      * @throws {OS.File.Error} In case of I/O error.
   1.118 +      */
   1.119 +     File.prototype._read = function _read(buffer, nbytes, options) {
   1.120 +       // |gBytesReadPtr| is a pointer to |gBytesRead|.
   1.121 +       throw_on_zero("read",
   1.122 +         WinFile.ReadFile(this.fd, buffer, nbytes, gBytesReadPtr, null),
   1.123 +         this._path
   1.124 +       );
   1.125 +       return gBytesRead.value;
   1.126 +     };
   1.127 +
   1.128 +     /**
   1.129 +      * Write some bytes to a file.
   1.130 +      *
   1.131 +      * @param {C pointer} buffer A buffer holding the data that must be
   1.132 +      * written.
   1.133 +      * @param {number} nbytes The number of bytes to write. It must not
   1.134 +      * exceed the size of |buffer| in bytes.
   1.135 +      * @param {*=} options Additional options for writing. Ignored in
   1.136 +      * this implementation.
   1.137 +      *
   1.138 +      * @return {number} The number of bytes effectively written.
   1.139 +      * @throws {OS.File.Error} In case of I/O error.
   1.140 +      */
   1.141 +     File.prototype._write = function _write(buffer, nbytes, options) {
   1.142 +       if (this._appendMode) {
   1.143 +         // Need to manually seek on Windows, as O_APPEND is not supported.
   1.144 +         // This is, of course, a race, but there is no real way around this.
   1.145 +         this.setPosition(0, File.POS_END);
   1.146 +       }
   1.147 +       // |gBytesWrittenPtr| is a pointer to |gBytesWritten|.
   1.148 +       throw_on_zero("write",
   1.149 +         WinFile.WriteFile(this.fd, buffer, nbytes, gBytesWrittenPtr, null),
   1.150 +         this._path
   1.151 +       );
   1.152 +       return gBytesWritten.value;
   1.153 +     };
   1.154 +
   1.155 +     /**
   1.156 +      * Return the current position in the file.
   1.157 +      */
   1.158 +     File.prototype.getPosition = function getPosition(pos) {
   1.159 +       return this.setPosition(0, File.POS_CURRENT);
   1.160 +     };
   1.161 +
   1.162 +     /**
   1.163 +      * Change the current position in the file.
   1.164 +      *
   1.165 +      * @param {number} pos The new position. Whether this position
   1.166 +      * is considered from the current position, from the start of
   1.167 +      * the file or from the end of the file is determined by
   1.168 +      * argument |whence|.  Note that |pos| may exceed the length of
   1.169 +      * the file.
   1.170 +      * @param {number=} whence The reference position. If omitted
   1.171 +      * or |OS.File.POS_START|, |pos| is relative to the start of the
   1.172 +      * file.  If |OS.File.POS_CURRENT|, |pos| is relative to the
   1.173 +      * current position in the file. If |OS.File.POS_END|, |pos| is
   1.174 +      * relative to the end of the file.
   1.175 +      *
   1.176 +      * @return The new position in the file.
   1.177 +      */
   1.178 +     File.prototype.setPosition = function setPosition(pos, whence) {
   1.179 +       if (whence === undefined) {
   1.180 +         whence = Const.FILE_BEGIN;
   1.181 +       }
   1.182 +       let pos64 = ctypes.Int64(pos);
   1.183 +       // Per MSDN, while |lDistanceToMove| (low) is declared as int32_t, when
   1.184 +       // providing |lDistanceToMoveHigh| as well, it should countain the
   1.185 +       // bottom 32 bits of the 64-bit integer. Hence the following |posLo|
   1.186 +       // cast is OK.
   1.187 +       let posLo = new ctypes.uint32_t(ctypes.Int64.lo(pos64));
   1.188 +       posLo = ctypes.cast(posLo, ctypes.int32_t);
   1.189 +       let posHi = new ctypes.int32_t(ctypes.Int64.hi(pos64));
   1.190 +       let result = WinFile.SetFilePointer(
   1.191 +         this.fd, posLo.value, posHi.address(), whence);
   1.192 +       // INVALID_SET_FILE_POINTER might be still a valid result, as it
   1.193 +       // represents the lower 32 bit of the int64 result. MSDN says to check
   1.194 +       // both, INVALID_SET_FILE_POINTER and a non-zero winLastError.
   1.195 +       if (result == Const.INVALID_SET_FILE_POINTER && ctypes.winLastError) {
   1.196 +         throw new File.Error("setPosition", ctypes.winLastError, this._path);
   1.197 +       }
   1.198 +       pos64 = ctypes.Int64.join(posHi.value, result);
   1.199 +       return Type.int64_t.project(pos64);
   1.200 +     };
   1.201 +
   1.202 +     /**
   1.203 +      * Fetch the information on the file.
   1.204 +      *
   1.205 +      * @return File.Info The information on |this| file.
   1.206 +      */
   1.207 +     File.prototype.stat = function stat() {
   1.208 +       throw_on_zero("stat",
   1.209 +         WinFile.GetFileInformationByHandle(this.fd, gFileInfoPtr),
   1.210 +         this._path);
   1.211 +       return new File.Info(gFileInfo, this._path);
   1.212 +     };
   1.213 +
   1.214 +     /**
   1.215 +      * Set the last access and modification date of the file.
   1.216 +      * The time stamp resolution is 1 second at best, but might be worse
   1.217 +      * depending on the platform.
   1.218 +      *
   1.219 +      * @param {Date,number=} accessDate The last access date. If numeric,
   1.220 +      * milliseconds since epoch. If omitted or null, then the current date
   1.221 +      * will be used.
   1.222 +      * @param {Date,number=} modificationDate The last modification date. If
   1.223 +      * numeric, milliseconds since epoch. If omitted or null, then the current
   1.224 +      * date will be used.
   1.225 +      *
   1.226 +      * @throws {TypeError} In case of invalid parameters.
   1.227 +      * @throws {OS.File.Error} In case of I/O error.
   1.228 +      */
   1.229 +     File.prototype.setDates = function setDates(accessDate, modificationDate) {
   1.230 +       accessDate = Date_to_FILETIME("File.prototype.setDates", accessDate, this._path);
   1.231 +       modificationDate = Date_to_FILETIME("File.prototype.setDates",
   1.232 +                                           modificationDate,
   1.233 +                                           this._path);
   1.234 +       throw_on_zero("setDates",
   1.235 +                     WinFile.SetFileTime(this.fd, null, accessDate.address(),
   1.236 +                                         modificationDate.address()),
   1.237 +                     this._path);
   1.238 +     };
   1.239 +
   1.240 +     /**
   1.241 +      * Set the file's access permission bits.
   1.242 +      * Not implemented for Windows (bug 1022816).
   1.243 +      */
   1.244 +     File.prototype.setPermissions = function setPermissions(options = {}) {
   1.245 +         // do nothing
   1.246 +     };
   1.247 +
   1.248 +     /**
   1.249 +      * Flushes the file's buffers and causes all buffered data
   1.250 +      * to be written.
   1.251 +      * Disk flushes are very expensive and therefore should be used carefully,
   1.252 +      * sparingly and only in scenarios where it is vital that data survives
   1.253 +      * system crashes. Even though the function will be executed off the
   1.254 +      * main-thread, it might still affect the overall performance of any
   1.255 +      * running application.
   1.256 +      *
   1.257 +      * @throws {OS.File.Error} In case of I/O error.
   1.258 +      */
   1.259 +     File.prototype.flush = function flush() {
   1.260 +       throw_on_zero("flush", WinFile.FlushFileBuffers(this.fd), this._path);
   1.261 +     };
   1.262 +
   1.263 +     // The default sharing mode for opening files: files are not
   1.264 +     // locked against being reopened for reading/writing or against
   1.265 +     // being deleted by the same process or another process.
   1.266 +     // This is consistent with the default Unix policy.
   1.267 +     const DEFAULT_SHARE = Const.FILE_SHARE_READ |
   1.268 +       Const.FILE_SHARE_WRITE | Const.FILE_SHARE_DELETE;
   1.269 +
   1.270 +     // The default flags for opening files.
   1.271 +     const DEFAULT_FLAGS = Const.FILE_ATTRIBUTE_NORMAL;
   1.272 +
   1.273 +     /**
   1.274 +      * Open a file
   1.275 +      *
   1.276 +      * @param {string} path The path to the file.
   1.277 +      * @param {*=} mode The opening mode for the file, as
   1.278 +      * an object that may contain the following fields:
   1.279 +      *
   1.280 +      * - {bool} truncate If |true|, the file will be opened
   1.281 +      *  for writing. If the file does not exist, it will be
   1.282 +      *  created. If the file exists, its contents will be
   1.283 +      *  erased. Cannot be specified with |create|.
   1.284 +      * - {bool} create If |true|, the file will be opened
   1.285 +      *  for writing. If the file exists, this function fails.
   1.286 +      *  If the file does not exist, it will be created. Cannot
   1.287 +      *  be specified with |truncate| or |existing|.
   1.288 +      * - {bool} existing. If the file does not exist, this function
   1.289 +      *  fails. Cannot be specified with |create|.
   1.290 +      * - {bool} read If |true|, the file will be opened for
   1.291 +      *  reading. The file may also be opened for writing, depending
   1.292 +      *  on the other fields of |mode|.
   1.293 +      * - {bool} write If |true|, the file will be opened for
   1.294 +      *  writing. The file may also be opened for reading, depending
   1.295 +      *  on the other fields of |mode|.
   1.296 +      * - {bool} append If |true|, the file will be opened for appending,
   1.297 +      *  meaning the equivalent of |.setPosition(0, POS_END)| is executed
   1.298 +      *  before each write. The default is |true|, i.e. opening a file for
   1.299 +      *  appending. Specify |append: false| to open the file in regular mode.
   1.300 +      *
   1.301 +      * If neither |truncate|, |create| or |write| is specified, the file
   1.302 +      * is opened for reading.
   1.303 +      *
   1.304 +      * Note that |false|, |null| or |undefined| flags are simply ignored.
   1.305 +      *
   1.306 +      * @param {*=} options Additional options for file opening. This
   1.307 +      * implementation interprets the following fields:
   1.308 +      *
   1.309 +      * - {number} winShare If specified, a share mode, as per
   1.310 +      * Windows function |CreateFile|. You can build it from
   1.311 +      * constants |OS.Constants.Win.FILE_SHARE_*|. If unspecified,
   1.312 +      * the file uses the default sharing policy: it can be opened
   1.313 +      * for reading and/or writing and it can be removed by other
   1.314 +      * processes and by the same process.
   1.315 +      * - {number} winSecurity If specified, Windows security
   1.316 +      * attributes, as per Windows function |CreateFile|. If unspecified,
   1.317 +      * no security attributes.
   1.318 +      * - {number} winAccess If specified, Windows access mode, as
   1.319 +      * per Windows function |CreateFile|. This also requires option
   1.320 +      * |winDisposition| and this replaces argument |mode|. If unspecified,
   1.321 +      * uses the string |mode|.
   1.322 +      * - {number} winDisposition If specified, Windows disposition mode,
   1.323 +      * as per Windows function |CreateFile|. This also requires option
   1.324 +      * |winAccess| and this replaces argument |mode|. If unspecified,
   1.325 +      * uses the string |mode|.
   1.326 +      *
   1.327 +      * @return {File} A file object.
   1.328 +      * @throws {OS.File.Error} If the file could not be opened.
   1.329 +      */
   1.330 +     File.open = function Win_open(path, mode = {}, options = {}) {
   1.331 +       let share = options.winShare !== undefined ? options.winShare : DEFAULT_SHARE;
   1.332 +       let security = options.winSecurity || null;
   1.333 +       let flags = options.winFlags !== undefined ? options.winFlags : DEFAULT_FLAGS;
   1.334 +       let template = options.winTemplate ? options.winTemplate._fd : null;
   1.335 +       let access;
   1.336 +       let disposition;
   1.337 +
   1.338 +       mode = OS.Shared.AbstractFile.normalizeOpenMode(mode);
   1.339 +
   1.340 +       if ("winAccess" in options && "winDisposition" in options) {
   1.341 +         access = options.winAccess;
   1.342 +         disposition = options.winDisposition;
   1.343 +       } else if (("winAccess" in options && !("winDisposition" in options))
   1.344 +                 ||(!("winAccess" in options) && "winDisposition" in options)) {
   1.345 +         throw new TypeError("OS.File.open requires either both options " +
   1.346 +           "winAccess and winDisposition or neither");
   1.347 +       } else {
   1.348 +         if (mode.read) {
   1.349 +           access |= Const.GENERIC_READ;
   1.350 +         }
   1.351 +         if (mode.write) {
   1.352 +           access |= Const.GENERIC_WRITE;
   1.353 +         }
   1.354 +         // Finally, handle create/existing/trunc
   1.355 +         if (mode.trunc) {
   1.356 +           if (mode.existing) {
   1.357 +             // It seems that Const.TRUNCATE_EXISTING is broken
   1.358 +             // in presence of links (source, anyone?). We need
   1.359 +             // to open normally, then perform truncation manually.
   1.360 +             disposition = Const.OPEN_EXISTING;
   1.361 +           } else {
   1.362 +             disposition = Const.CREATE_ALWAYS;
   1.363 +           }
   1.364 +         } else if (mode.create) {
   1.365 +           disposition = Const.CREATE_NEW;
   1.366 +         } else if (mode.read && !mode.write) {
   1.367 +           disposition = Const.OPEN_EXISTING;
   1.368 +         } else if (mode.existing) {
   1.369 +           disposition = Const.OPEN_EXISTING;
   1.370 +         } else {
   1.371 +           disposition = Const.OPEN_ALWAYS;
   1.372 +         }
   1.373 +       }
   1.374 +
   1.375 +       let file = error_or_file(WinFile.CreateFile(path,
   1.376 +         access, share, security, disposition, flags, template), path);
   1.377 +
   1.378 +       file._appendMode = !!mode.append;
   1.379 +
   1.380 +       if (!(mode.trunc && mode.existing)) {
   1.381 +         return file;
   1.382 +       }
   1.383 +       // Now, perform manual truncation
   1.384 +       file.setPosition(0, File.POS_START);
   1.385 +       throw_on_zero("open",
   1.386 +         WinFile.SetEndOfFile(file.fd),
   1.387 +         path);
   1.388 +       return file;
   1.389 +     };
   1.390 +
   1.391 +     /**
   1.392 +      * Checks if a file or directory exists
   1.393 +      *
   1.394 +      * @param {string} path The path to the file.
   1.395 +      *
   1.396 +      * @return {bool} true if the file exists, false otherwise.
   1.397 +      */
   1.398 +     File.exists = function Win_exists(path) {
   1.399 +       try {
   1.400 +         let file = File.open(path, FILE_STAT_MODE, FILE_STAT_OPTIONS);
   1.401 +         file.close();
   1.402 +         return true;
   1.403 +       } catch (x) {
   1.404 +         return false;
   1.405 +       }
   1.406 +     };
   1.407 +
   1.408 +     /**
   1.409 +      * Remove an existing file.
   1.410 +      *
   1.411 +      * @param {string} path The name of the file.
   1.412 +      * @param {*=} options Additional options.
   1.413 +      *   - {bool} ignoreAbsent If |false|, throw an error if the file does
   1.414 +      *     not exist. |true| by default.
   1.415 +      *
   1.416 +      * @throws {OS.File.Error} In case of I/O error.
   1.417 +      */
   1.418 +     File.remove = function remove(path, options = {}) {
   1.419 +       if (WinFile.DeleteFile(path)) {
   1.420 +         return;
   1.421 +       }
   1.422 +
   1.423 +       if (ctypes.winLastError == Const.ERROR_FILE_NOT_FOUND) {
   1.424 +         if ((!("ignoreAbsent" in options) || options.ignoreAbsent)) {
   1.425 +           return;
   1.426 +         }
   1.427 +       } else if (ctypes.winLastError == Const.ERROR_ACCESS_DENIED) {
   1.428 +         let attributes = WinFile.GetFileAttributes(path);
   1.429 +         if (attributes != Const.INVALID_FILE_ATTRIBUTES &&
   1.430 +             attributes & Const.FILE_ATTRIBUTE_READONLY) {
   1.431 +           let newAttributes = attributes & ~Const.FILE_ATTRIBUTE_READONLY;
   1.432 +           if (WinFile.SetFileAttributes(path, newAttributes) &&
   1.433 +               WinFile.DeleteFile(path)) {
   1.434 +             return;
   1.435 +           }
   1.436 +         }
   1.437 +       }
   1.438 +
   1.439 +       throw new File.Error("remove", ctypes.winLastError, path);
   1.440 +     };
   1.441 +
   1.442 +     /**
   1.443 +      * Remove an empty directory.
   1.444 +      *
   1.445 +      * @param {string} path The name of the directory to remove.
   1.446 +      * @param {*=} options Additional options.
   1.447 +      *   - {bool} ignoreAbsent If |false|, throw an error if the directory
   1.448 +      *     does not exist. |true| by default
   1.449 +      */
   1.450 +     File.removeEmptyDir = function removeEmptyDir(path, options = {}) {
   1.451 +       let result = WinFile.RemoveDirectory(path);
   1.452 +       if (!result) {
   1.453 +         if ((!("ignoreAbsent" in options) || options.ignoreAbsent) &&
   1.454 +             ctypes.winLastError == Const.ERROR_FILE_NOT_FOUND) {
   1.455 +           return;
   1.456 +         }
   1.457 +         throw new File.Error("removeEmptyDir", ctypes.winLastError, path);
   1.458 +       }
   1.459 +     };
   1.460 +
   1.461 +     /**
   1.462 +      * Create a directory and, optionally, its parent directories.
   1.463 +      *
   1.464 +      * @param {string} path The name of the directory.
   1.465 +      * @param {*=} options Additional options. This
   1.466 +      * implementation interprets the following fields:
   1.467 +      *
   1.468 +      * - {C pointer} winSecurity If specified, security attributes
   1.469 +      * as per winapi function |CreateDirectory|. If unspecified,
   1.470 +      * use the default security descriptor, inherited from the
   1.471 +      * parent directory.
   1.472 +      * - {bool} ignoreExisting If |false|, throw an error if the directory
   1.473 +      * already exists. |true| by default
   1.474 +      * - {string} from If specified, the call to |makeDir| creates all the
   1.475 +      * ancestors of |path| that are descendants of |from|. Note that |from|
   1.476 +      * and its existing descendants must be user-writeable and that |path|
   1.477 +      * must be a descendant of |from|.
   1.478 +      * Example:
   1.479 +      *   makeDir(Path.join(profileDir, "foo", "bar"), { from: profileDir });
   1.480 +      *  creates directories profileDir/foo, profileDir/foo/bar
   1.481 +       */
   1.482 +     File._makeDir = function makeDir(path, options = {}) {
   1.483 +       let security = options.winSecurity || null;
   1.484 +       let result = WinFile.CreateDirectory(path, security);
   1.485 +
   1.486 +       if (result) {
   1.487 +         return;
   1.488 +       }
   1.489 +
   1.490 +       if (("ignoreExisting" in options) && !options.ignoreExisting) {
   1.491 +         throw new File.Error("makeDir", ctypes.winLastError, path);
   1.492 +       }
   1.493 +
   1.494 +       if (ctypes.winLastError == Const.ERROR_ALREADY_EXISTS) {
   1.495 +         return;
   1.496 +       }
   1.497 +
   1.498 +       // If the user has no access, but it's a root directory, no error should be thrown
   1.499 +       let splitPath = OS.Path.split(path);
   1.500 +       // Removing last component if it's empty
   1.501 +       // An empty last component is caused by trailing slashes in path
   1.502 +       // This is always the case with root directories
   1.503 +       if( splitPath.components[splitPath.components.length - 1].length === 0 ) {
   1.504 +         splitPath.components.pop();
   1.505 +       }
   1.506 +       // One component consisting of a drive letter implies a directory root.
   1.507 +       if (ctypes.winLastError == Const.ERROR_ACCESS_DENIED &&
   1.508 +           splitPath.winDrive &&
   1.509 +           splitPath.components.length === 1 ) {
   1.510 +         return;
   1.511 +       }
   1.512 +
   1.513 +       throw new File.Error("makeDir", ctypes.winLastError, path);
   1.514 +     };
   1.515 +
   1.516 +     /**
   1.517 +      * Copy a file to a destination.
   1.518 +      *
   1.519 +      * @param {string} sourcePath The platform-specific path at which
   1.520 +      * the file may currently be found.
   1.521 +      * @param {string} destPath The platform-specific path at which the
   1.522 +      * file should be copied.
   1.523 +      * @param {*=} options An object which may contain the following fields:
   1.524 +      *
   1.525 +      * @option {bool} noOverwrite - If true, this function will fail if
   1.526 +      * a file already exists at |destPath|. Otherwise, if this file exists,
   1.527 +      * it will be erased silently.
   1.528 +      *
   1.529 +      * @throws {OS.File.Error} In case of any error.
   1.530 +      *
   1.531 +      * General note: The behavior of this function is defined only when
   1.532 +      * it is called on a single file. If it is called on a directory, the
   1.533 +      * behavior is undefined and may not be the same across all platforms.
   1.534 +      *
   1.535 +      * General note: The behavior of this function with respect to metadata
   1.536 +      * is unspecified. Metadata may or may not be copied with the file. The
   1.537 +      * behavior may not be the same across all platforms.
   1.538 +     */
   1.539 +     File.copy = function copy(sourcePath, destPath, options = {}) {
   1.540 +       throw_on_zero("copy",
   1.541 +         WinFile.CopyFile(sourcePath, destPath, options.noOverwrite || false),
   1.542 +         sourcePath
   1.543 +       );
   1.544 +     };
   1.545 +
   1.546 +     /**
   1.547 +      * Move a file to a destination.
   1.548 +      *
   1.549 +      * @param {string} sourcePath The platform-specific path at which
   1.550 +      * the file may currently be found.
   1.551 +      * @param {string} destPath The platform-specific path at which the
   1.552 +      * file should be moved.
   1.553 +      * @param {*=} options An object which may contain the following fields:
   1.554 +      *
   1.555 +      * @option {bool} noOverwrite - If set, this function will fail if
   1.556 +      * a file already exists at |destPath|. Otherwise, if this file exists,
   1.557 +      * it will be erased silently.
   1.558 +      * @option {bool} noCopy - If set, this function will fail if the
   1.559 +      * operation is more sophisticated than a simple renaming, i.e. if
   1.560 +      * |sourcePath| and |destPath| are not situated on the same drive.
   1.561 +      *
   1.562 +      * @throws {OS.File.Error} In case of any error.
   1.563 +      *
   1.564 +      * General note: The behavior of this function is defined only when
   1.565 +      * it is called on a single file. If it is called on a directory, the
   1.566 +      * behavior is undefined and may not be the same across all platforms.
   1.567 +      *
   1.568 +      * General note: The behavior of this function with respect to metadata
   1.569 +      * is unspecified. Metadata may or may not be moved with the file. The
   1.570 +      * behavior may not be the same across all platforms.
   1.571 +      */
   1.572 +     File.move = function move(sourcePath, destPath, options = {}) {
   1.573 +       let flags = 0;
   1.574 +       if (!options.noCopy) {
   1.575 +         flags = Const.MOVEFILE_COPY_ALLOWED;
   1.576 +       }
   1.577 +       if (!options.noOverwrite) {
   1.578 +         flags = flags | Const.MOVEFILE_REPLACE_EXISTING;
   1.579 +       }
   1.580 +       throw_on_zero("move",
   1.581 +         WinFile.MoveFileEx(sourcePath, destPath, flags),
   1.582 +         sourcePath
   1.583 +       );
   1.584 +
   1.585 +       // Inherit NTFS permissions from the destination directory
   1.586 +       // if possible.
   1.587 +       if (Path.dirname(sourcePath) === Path.dirname(destPath)) {
   1.588 +         // Skip if the move operation was the simple rename,
   1.589 +         return;
   1.590 +       }
   1.591 +       // The function may fail for various reasons (e.g. not all
   1.592 +       // filesystems support NTFS permissions or the user may not
   1.593 +       // have the enough rights to read/write permissions).
   1.594 +       // However we can safely ignore errors. The file was already
   1.595 +       // moved. Setting permissions is not mandatory.
   1.596 +       let dacl = new ctypes.voidptr_t();
   1.597 +       let sd = new ctypes.voidptr_t();
   1.598 +       WinFile.GetNamedSecurityInfo(destPath, Const.SE_FILE_OBJECT,
   1.599 +                                    Const.DACL_SECURITY_INFORMATION,
   1.600 +                                    null /*sidOwner*/, null /*sidGroup*/,
   1.601 +                                    dacl.address(), null /*sacl*/,
   1.602 +                                    sd.address());
   1.603 +       // dacl will be set only if the function succeeds.
   1.604 +       if (!dacl.isNull()) {
   1.605 +         WinFile.SetNamedSecurityInfo(destPath, Const.SE_FILE_OBJECT,
   1.606 +                                      Const.DACL_SECURITY_INFORMATION |
   1.607 +                                      Const.UNPROTECTED_DACL_SECURITY_INFORMATION,
   1.608 +                                      null /*sidOwner*/, null /*sidGroup*/,
   1.609 +                                      dacl, null /*sacl*/);
   1.610 +       }
   1.611 +       // sd will be set only if the function succeeds.
   1.612 +       if (!sd.isNull()) {
   1.613 +           WinFile.LocalFree(Type.HLOCAL.cast(sd));
   1.614 +       }
   1.615 +     };
   1.616 +
   1.617 +     /**
   1.618 +      * Gets the number of bytes available on disk to the current user.
   1.619 +      *
   1.620 +      * @param {string} sourcePath Platform-specific path to a directory on 
   1.621 +      * the disk to query for free available bytes.
   1.622 +      *
   1.623 +      * @return {number} The number of bytes available for the current user.
   1.624 +      * @throws {OS.File.Error} In case of any error.
   1.625 +      */
   1.626 +     File.getAvailableFreeSpace = function Win_getAvailableFreeSpace(sourcePath) {
   1.627 +       let freeBytesAvailableToUser = new Type.uint64_t.implementation(0);
   1.628 +       let freeBytesAvailableToUserPtr = freeBytesAvailableToUser.address();
   1.629 +
   1.630 +       throw_on_zero("getAvailableFreeSpace",
   1.631 +         WinFile.GetDiskFreeSpaceEx(sourcePath, freeBytesAvailableToUserPtr, null, null)
   1.632 +       );
   1.633 +
   1.634 +       return freeBytesAvailableToUser.value;
   1.635 +     };
   1.636 +
   1.637 +     /**
   1.638 +      * A global value used to receive data during time conversions.
   1.639 +      */
   1.640 +     let gSystemTime = new Type.SystemTime.implementation();
   1.641 +     let gSystemTimePtr = gSystemTime.address();
   1.642 +
   1.643 +     /**
   1.644 +      * Utility function: convert a FILETIME to a JavaScript Date.
   1.645 +      */
   1.646 +     let FILETIME_to_Date = function FILETIME_to_Date(fileTime, path) {
   1.647 +       if (fileTime == null) {
   1.648 +         throw new TypeError("Expecting a non-null filetime");
   1.649 +       }
   1.650 +       throw_on_zero("FILETIME_to_Date",
   1.651 +                     WinFile.FileTimeToSystemTime(fileTime.address(),
   1.652 +                                                  gSystemTimePtr),
   1.653 +                     path);
   1.654 +       // Windows counts hours, minutes, seconds from UTC,
   1.655 +       // JS counts from local time, so we need to go through UTC.
   1.656 +       let utc = Date.UTC(gSystemTime.wYear,
   1.657 +                          gSystemTime.wMonth - 1
   1.658 +                          /*Windows counts months from 1, JS from 0*/,
   1.659 +                          gSystemTime.wDay, gSystemTime.wHour,
   1.660 +                          gSystemTime.wMinute, gSystemTime.wSecond,
   1.661 +                          gSystemTime.wMilliSeconds);
   1.662 +       return new Date(utc);
   1.663 +     };
   1.664 +
   1.665 +     /**
   1.666 +      * Utility function: convert Javascript Date to FileTime.
   1.667 +      *
   1.668 +      * @param {string} fn Name of the calling function.
   1.669 +      * @param {Date,number} date The date to be converted. If omitted or null,
   1.670 +      * then the current date will be used. If numeric, assumed to be the date
   1.671 +      * in milliseconds since epoch.
   1.672 +      */
   1.673 +     let Date_to_FILETIME = function Date_to_FILETIME(fn, date, path) {
   1.674 +       if (typeof date === "number") {
   1.675 +         date = new Date(date);
   1.676 +       } else if (!date) {
   1.677 +         date = new Date();
   1.678 +       } else if (typeof date.getUTCFullYear !== "function") {
   1.679 +         throw new TypeError("|date| parameter of " + fn + " must be a " +
   1.680 +                             "|Date| instance or number");
   1.681 +       }
   1.682 +       gSystemTime.wYear = date.getUTCFullYear();
   1.683 +       // Windows counts months from 1, JS from 0.
   1.684 +       gSystemTime.wMonth = date.getUTCMonth() + 1;
   1.685 +       gSystemTime.wDay = date.getUTCDate();
   1.686 +       gSystemTime.wHour = date.getUTCHours();
   1.687 +       gSystemTime.wMinute = date.getUTCMinutes();
   1.688 +       gSystemTime.wSecond = date.getUTCSeconds();
   1.689 +       gSystemTime.wMilliseconds = date.getUTCMilliseconds();
   1.690 +       let result = new OS.Shared.Type.FILETIME.implementation();
   1.691 +       throw_on_zero("Date_to_FILETIME",
   1.692 +                     WinFile.SystemTimeToFileTime(gSystemTimePtr,
   1.693 +                                                  result.address()),
   1.694 +                     path);
   1.695 +       return result;
   1.696 +     };
   1.697 +
   1.698 +     /**
   1.699 +      * Iterate on one directory.
   1.700 +      *
   1.701 +      * This iterator will not enter subdirectories.
   1.702 +      *
   1.703 +      * @param {string} path The directory upon which to iterate.
   1.704 +      * @param {*=} options An object that may contain the following field:
   1.705 +      * @option {string} winPattern Windows file name pattern; if set,
   1.706 +      * only files matching this pattern are returned.
   1.707 +      *
   1.708 +      * @throws {File.Error} If |path| does not represent a directory or
   1.709 +      * if the directory cannot be iterated.
   1.710 +      * @constructor
   1.711 +      */
   1.712 +     File.DirectoryIterator = function DirectoryIterator(path, options) {
   1.713 +       exports.OS.Shared.AbstractFile.AbstractIterator.call(this);
   1.714 +       if (options && options.winPattern) {
   1.715 +         this._pattern = path + "\\" + options.winPattern;
   1.716 +       } else {
   1.717 +         this._pattern = path + "\\*";
   1.718 +       }
   1.719 +       this._path = path;
   1.720 +
   1.721 +       // Pre-open the first item.
   1.722 +       this._first = true;
   1.723 +       this._findData = new Type.FindData.implementation();
   1.724 +       this._findDataPtr = this._findData.address();
   1.725 +       this._handle = WinFile.FindFirstFile(this._pattern, this._findDataPtr);
   1.726 +       if (this._handle == Const.INVALID_HANDLE_VALUE) {
   1.727 +         let error = ctypes.winLastError;
   1.728 +         this._findData = null;
   1.729 +         this._findDataPtr = null;
   1.730 +         if (error == Const.ERROR_FILE_NOT_FOUND) {
   1.731 +           // Directory is empty, let's behave as if it were closed
   1.732 +           SharedAll.LOG("Directory is empty");
   1.733 +           this._closed = true;
   1.734 +           this._exists = true;
   1.735 +         } else if (error == Const.ERROR_PATH_NOT_FOUND) {
   1.736 +           // Directory does not exist, let's throw if we attempt to walk it
   1.737 +           SharedAll.LOG("Directory does not exist");
   1.738 +           this._closed = true;
   1.739 +           this._exists = false;
   1.740 +         } else {
   1.741 +           throw new File.Error("DirectoryIterator", error, this._path);
   1.742 +         }
   1.743 +       } else {
   1.744 +         this._closed = false;
   1.745 +         this._exists = true;
   1.746 +       }
   1.747 +     };
   1.748 +
   1.749 +     File.DirectoryIterator.prototype = Object.create(exports.OS.Shared.AbstractFile.AbstractIterator.prototype);
   1.750 +
   1.751 +
   1.752 +     /**
   1.753 +      * Fetch the next entry in the directory.
   1.754 +      *
   1.755 +      * @return null If we have reached the end of the directory.
   1.756 +      */
   1.757 +     File.DirectoryIterator.prototype._next = function _next() {
   1.758 +       // Bailout if the directory does not exist
   1.759 +       if (!this._exists) {
   1.760 +         throw File.Error.noSuchFile("DirectoryIterator.prototype.next", this._path);
   1.761 +       }
   1.762 +       // Bailout if the iterator is closed.
   1.763 +       if (this._closed) {
   1.764 +         return null;
   1.765 +       }
   1.766 +       // If this is the first entry, we have obtained it already
   1.767 +       // during construction.
   1.768 +       if (this._first) {
   1.769 +         this._first = false;
   1.770 +         return this._findData;
   1.771 +       }
   1.772 +
   1.773 +       if (WinFile.FindNextFile(this._handle, this._findDataPtr)) {
   1.774 +         return this._findData;
   1.775 +       } else {
   1.776 +         let error = ctypes.winLastError;
   1.777 +         this.close();
   1.778 +         if (error == Const.ERROR_NO_MORE_FILES) {
   1.779 +            return null;
   1.780 +         } else {
   1.781 +            throw new File.Error("iter (FindNextFile)", error, this._path);
   1.782 +         }
   1.783 +       }
   1.784 +     },
   1.785 +
   1.786 +     /**
   1.787 +      * Return the next entry in the directory, if any such entry is
   1.788 +      * available.
   1.789 +      *
   1.790 +      * Skip special directories "." and "..".
   1.791 +      *
   1.792 +      * @return {File.Entry} The next entry in the directory.
   1.793 +      * @throws {StopIteration} Once all files in the directory have been
   1.794 +      * encountered.
   1.795 +      */
   1.796 +     File.DirectoryIterator.prototype.next = function next() {
   1.797 +         // FIXME: If we start supporting "\\?\"-prefixed paths, do not forget
   1.798 +         // that "." and ".." are absolutely normal file names if _path starts
   1.799 +         // with such prefix
   1.800 +         for (let entry = this._next(); entry != null; entry = this._next()) {
   1.801 +           let name = entry.cFileName.readString();
   1.802 +           if (name == "." || name == "..") {
   1.803 +             continue;
   1.804 +           }
   1.805 +           return new File.DirectoryIterator.Entry(entry, this._path);
   1.806 +         }
   1.807 +         throw StopIteration;
   1.808 +     };
   1.809 +
   1.810 +     File.DirectoryIterator.prototype.close = function close() {
   1.811 +       if (this._closed) {
   1.812 +         return;
   1.813 +       }
   1.814 +       this._closed = true;
   1.815 +       if (this._handle) {
   1.816 +         // We might not have a handle if the iterator is closed
   1.817 +         // before being used.
   1.818 +         throw_on_zero("FindClose",
   1.819 +           WinFile.FindClose(this._handle),
   1.820 +           this._path);
   1.821 +         this._handle = null;
   1.822 +       }
   1.823 +     };
   1.824 +
   1.825 +    /**
   1.826 +     * Determine whether the directory exists.
   1.827 +     *
   1.828 +     * @return {boolean}
   1.829 +     */
   1.830 +     File.DirectoryIterator.prototype.exists = function exists() {
   1.831 +       return this._exists;
   1.832 +     };
   1.833 +
   1.834 +     File.DirectoryIterator.Entry = function Entry(win_entry, parent) {
   1.835 +       if (!win_entry.dwFileAttributes || !win_entry.ftCreationTime ||
   1.836 +           !win_entry.ftLastAccessTime || !win_entry.ftLastWriteTime)
   1.837 +        throw new TypeError();
   1.838 +
   1.839 +       // Copy the relevant part of |win_entry| to ensure that
   1.840 +       // our data is not overwritten prematurely.
   1.841 +       let isDir = !!(win_entry.dwFileAttributes & Const.FILE_ATTRIBUTE_DIRECTORY);
   1.842 +       let isSymLink = !!(win_entry.dwFileAttributes & Const.FILE_ATTRIBUTE_REPARSE_POINT);
   1.843 +
   1.844 +       let winCreationDate = FILETIME_to_Date(win_entry.ftCreationTime, this._path);
   1.845 +       let winLastWriteDate = FILETIME_to_Date(win_entry.ftLastWriteTime, this._path);
   1.846 +       let winLastAccessDate = FILETIME_to_Date(win_entry.ftLastAccessTime, this._path);
   1.847 +
   1.848 +       let name = win_entry.cFileName.readString();
   1.849 +       if (!name) {
   1.850 +         throw new TypeError("Empty name");
   1.851 +       }
   1.852 +
   1.853 +       if (!parent) {
   1.854 +         throw new TypeError("Empty parent");
   1.855 +       }
   1.856 +       this._parent = parent;
   1.857 +
   1.858 +       let path = Path.join(this._parent, name);
   1.859 +
   1.860 +       SysAll.AbstractEntry.call(this, isDir, isSymLink, name,
   1.861 +         winCreationDate, winLastWriteDate,
   1.862 +         winLastAccessDate, path);
   1.863 +     };
   1.864 +     File.DirectoryIterator.Entry.prototype = Object.create(SysAll.AbstractEntry.prototype);
   1.865 +
   1.866 +     /**
   1.867 +      * Return a version of an instance of
   1.868 +      * File.DirectoryIterator.Entry that can be sent from a worker
   1.869 +      * thread to the main thread. Note that deserialization is
   1.870 +      * asymmetric and returns an object with a different
   1.871 +      * implementation.
   1.872 +      */
   1.873 +     File.DirectoryIterator.Entry.toMsg = function toMsg(value) {
   1.874 +       if (!value instanceof File.DirectoryIterator.Entry) {
   1.875 +         throw new TypeError("parameter of " +
   1.876 +           "File.DirectoryIterator.Entry.toMsg must be a " +
   1.877 +           "File.DirectoryIterator.Entry");
   1.878 +       }
   1.879 +       let serialized = {};
   1.880 +       for (let key in File.DirectoryIterator.Entry.prototype) {
   1.881 +         serialized[key] = value[key];
   1.882 +       }
   1.883 +       return serialized;
   1.884 +     };
   1.885 +
   1.886 +
   1.887 +     /**
   1.888 +      * Information on a file.
   1.889 +      *
   1.890 +      * To obtain the latest information on a file, use |File.stat|
   1.891 +      * (for an unopened file) or |File.prototype.stat| (for an
   1.892 +      * already opened file).
   1.893 +      *
   1.894 +      * @constructor
   1.895 +      */
   1.896 +     File.Info = function Info(stat, path) {
   1.897 +       let isDir = !!(stat.dwFileAttributes & Const.FILE_ATTRIBUTE_DIRECTORY);
   1.898 +       let isSymLink = !!(stat.dwFileAttributes & Const.FILE_ATTRIBUTE_REPARSE_POINT);
   1.899 +
   1.900 +       let winBirthDate = FILETIME_to_Date(stat.ftCreationTime, this._path);
   1.901 +       let lastAccessDate = FILETIME_to_Date(stat.ftLastAccessTime, this._path);
   1.902 +       let lastWriteDate = FILETIME_to_Date(stat.ftLastWriteTime, this._path);
   1.903 +
   1.904 +       let value = ctypes.UInt64.join(stat.nFileSizeHigh, stat.nFileSizeLow);
   1.905 +       let size = Type.uint64_t.importFromC(value);
   1.906 +
   1.907 +       SysAll.AbstractInfo.call(this, path, isDir, isSymLink, size,
   1.908 +         winBirthDate, lastAccessDate, lastWriteDate);
   1.909 +     };
   1.910 +     File.Info.prototype = Object.create(SysAll.AbstractInfo.prototype);
   1.911 +
   1.912 +     /**
   1.913 +      * Return a version of an instance of File.Info that can be sent
   1.914 +      * from a worker thread to the main thread. Note that deserialization
   1.915 +      * is asymmetric and returns an object with a different implementation.
   1.916 +      */
   1.917 +     File.Info.toMsg = function toMsg(stat) {
   1.918 +       if (!stat instanceof File.Info) {
   1.919 +         throw new TypeError("parameter of File.Info.toMsg must be a File.Info");
   1.920 +       }
   1.921 +       let serialized = {};
   1.922 +       for (let key in File.Info.prototype) {
   1.923 +         serialized[key] = stat[key];
   1.924 +       }
   1.925 +       return serialized;
   1.926 +     };
   1.927 +
   1.928 +
   1.929 +     /**
   1.930 +      * Fetch the information on a file.
   1.931 +      *
   1.932 +      * Performance note: if you have opened the file already,
   1.933 +      * method |File.prototype.stat| is generally much faster
   1.934 +      * than method |File.stat|.
   1.935 +      *
   1.936 +      * Platform-specific note: under Windows, if the file is
   1.937 +      * already opened without sharing of the read capability,
   1.938 +      * this function will fail.
   1.939 +      *
   1.940 +      * @return {File.Information}
   1.941 +      */
   1.942 +     File.stat = function stat(path) {
   1.943 +       let file = File.open(path, FILE_STAT_MODE, FILE_STAT_OPTIONS);
   1.944 +       try {
   1.945 +         return file.stat();
   1.946 +       } finally {
   1.947 +         file.close();
   1.948 +       }
   1.949 +     };
   1.950 +     // All of the following is required to ensure that File.stat
   1.951 +     // also works on directories.
   1.952 +     const FILE_STAT_MODE = {
   1.953 +       read: true
   1.954 +     };
   1.955 +     const FILE_STAT_OPTIONS = {
   1.956 +       // Directories can be opened neither for reading(!) nor for writing
   1.957 +       winAccess: 0,
   1.958 +       // Directories can only be opened with backup semantics(!)
   1.959 +       winFlags: Const.FILE_FLAG_BACKUP_SEMANTICS,
   1.960 +       winDisposition: Const.OPEN_EXISTING
   1.961 +     };
   1.962 +
   1.963 +     /**
   1.964 +      * Set the file's access permission bits.
   1.965 +      * Not implemented for Windows (bug 1022816).
   1.966 +      */
   1.967 +     File.setPermissions = function setPermissions(path, options = {}) {
   1.968 +         // do nothing
   1.969 +     };
   1.970 +
   1.971 +     /**
   1.972 +      * Set the last access and modification date of the file.
   1.973 +      * The time stamp resolution is 1 second at best, but might be worse
   1.974 +      * depending on the platform.
   1.975 +      *
   1.976 +      * Performance note: if you have opened the file already in write mode,
   1.977 +      * method |File.prototype.stat| is generally much faster
   1.978 +      * than method |File.stat|.
   1.979 +      *
   1.980 +      * Platform-specific note: under Windows, if the file is
   1.981 +      * already opened without sharing of the write capability,
   1.982 +      * this function will fail.
   1.983 +      *
   1.984 +      * @param {string} path The full name of the file to set the dates for.
   1.985 +      * @param {Date,number=} accessDate The last access date. If numeric,
   1.986 +      * milliseconds since epoch. If omitted or null, then the current date
   1.987 +      * will be used.
   1.988 +      * @param {Date,number=} modificationDate The last modification date. If
   1.989 +      * numeric, milliseconds since epoch. If omitted or null, then the current
   1.990 +      * date will be used.
   1.991 +      *
   1.992 +      * @throws {TypeError} In case of invalid paramters.
   1.993 +      * @throws {OS.File.Error} In case of I/O error.
   1.994 +      */
   1.995 +     File.setDates = function setDates(path, accessDate, modificationDate) {
   1.996 +       let file = File.open(path, FILE_SETDATES_MODE, FILE_SETDATES_OPTIONS);
   1.997 +       try {
   1.998 +         return file.setDates(accessDate, modificationDate);
   1.999 +       } finally {
  1.1000 +         file.close();
  1.1001 +       }
  1.1002 +     };
  1.1003 +     // All of the following is required to ensure that File.setDates
  1.1004 +     // also works on directories.
  1.1005 +     const FILE_SETDATES_MODE = {
  1.1006 +       write: true
  1.1007 +     };
  1.1008 +     const FILE_SETDATES_OPTIONS = {
  1.1009 +       winAccess: Const.GENERIC_WRITE,
  1.1010 +       // Directories can only be opened with backup semantics(!)
  1.1011 +       winFlags: Const.FILE_FLAG_BACKUP_SEMANTICS,
  1.1012 +       winDisposition: Const.OPEN_EXISTING
  1.1013 +     };
  1.1014 +
  1.1015 +     File.read = exports.OS.Shared.AbstractFile.read;
  1.1016 +     File.writeAtomic = exports.OS.Shared.AbstractFile.writeAtomic;
  1.1017 +     File.openUnique = exports.OS.Shared.AbstractFile.openUnique;
  1.1018 +     File.makeDir = exports.OS.Shared.AbstractFile.makeDir;
  1.1019 +
  1.1020 +     /**
  1.1021 +      * Remove an existing directory and its contents.
  1.1022 +      *
  1.1023 +      * @param {string} path The name of the directory.
  1.1024 +      * @param {*=} options Additional options.
  1.1025 +      *   - {bool} ignoreAbsent If |false|, throw an error if the directory doesn't
  1.1026 +      *     exist. |true| by default.
  1.1027 +      *   - {boolean} ignorePermissions If |true|, remove the file even when lacking write
  1.1028 +      *     permission.
  1.1029 +      *
  1.1030 +      * @throws {OS.File.Error} In case of I/O error, in particular if |path| is
  1.1031 +      *         not a directory.
  1.1032 +      */
  1.1033 +     File.removeDir = function(path, options) {
  1.1034 +       // We can't use File.stat here because it will follow the symlink.
  1.1035 +       let attributes = WinFile.GetFileAttributes(path);
  1.1036 +       if (attributes == Const.INVALID_FILE_ATTRIBUTES) {
  1.1037 +         if ((!("ignoreAbsent" in options) || options.ignoreAbsent) &&
  1.1038 +             ctypes.winLastError == Const.ERROR_FILE_NOT_FOUND) {
  1.1039 +           return;
  1.1040 +         }
  1.1041 +         throw new File.Error("removeEmptyDir", ctypes.winLastError, path);
  1.1042 +       }
  1.1043 +       if (attributes & Const.FILE_ATTRIBUTE_REPARSE_POINT) {
  1.1044 +         // Unlike Unix symlinks, NTFS junctions or NTFS symlinks to
  1.1045 +         // directories are directories themselves. OS.File.remove()
  1.1046 +         // will not work for them.
  1.1047 +         OS.File.removeEmptyDir(path, options);
  1.1048 +         return;
  1.1049 +       }
  1.1050 +       exports.OS.Shared.AbstractFile.removeRecursive(path, options);
  1.1051 +     };
  1.1052 +
  1.1053 +     /**
  1.1054 +      * Get the current directory by getCurrentDirectory.
  1.1055 +      */
  1.1056 +     File.getCurrentDirectory = function getCurrentDirectory() {
  1.1057 +       // This function is more complicated than one could hope.
  1.1058 +       //
  1.1059 +       // This is due to two facts:
  1.1060 +       // - the maximal length of a path under Windows is not completely
  1.1061 +       //  specified (there is a constant MAX_PATH, but it is quite possible
  1.1062 +       //  to create paths that are much larger, see bug 744413);
  1.1063 +       // - if we attempt to call |GetCurrentDirectory| with a buffer that
  1.1064 +       //  is too short, it returns the length of the current directory, but
  1.1065 +       //  this length might be insufficient by the time we can call again
  1.1066 +       //  the function with a larger buffer, in the (unlikely but possible)
  1.1067 +       //  case in which the process changes directory to a directory with
  1.1068 +       //  a longer name between both calls.
  1.1069 +       //
  1.1070 +       let buffer_size = 4096;
  1.1071 +       while (true) {
  1.1072 +         let array = new (ctypes.ArrayType(ctypes.jschar, buffer_size))();
  1.1073 +         let expected_size = throw_on_zero("getCurrentDirectory",
  1.1074 +           WinFile.GetCurrentDirectory(buffer_size, array)
  1.1075 +         );
  1.1076 +         if (expected_size <= buffer_size) {
  1.1077 +           return array.readString();
  1.1078 +         }
  1.1079 +         // At this point, we are in a case in which our buffer was not
  1.1080 +         // large enough to hold the name of the current directory.
  1.1081 +         // Consequently, we need to increase the size of the buffer.
  1.1082 +         // Note that, even in crazy scenarios, the loop will eventually
  1.1083 +         // converge, as the length of the paths cannot increase infinitely.
  1.1084 +         buffer_size = expected_size + 1 /* to store \0 */;
  1.1085 +       }
  1.1086 +     };
  1.1087 +
  1.1088 +     /**
  1.1089 +      * Set the current directory by setCurrentDirectory.
  1.1090 +      */
  1.1091 +     File.setCurrentDirectory = function setCurrentDirectory(path) {
  1.1092 +       throw_on_zero("setCurrentDirectory",
  1.1093 +         WinFile.SetCurrentDirectory(path),
  1.1094 +         path);
  1.1095 +     };
  1.1096 +
  1.1097 +     /**
  1.1098 +      * Get/set the current directory by |curDir|.
  1.1099 +      */
  1.1100 +     Object.defineProperty(File, "curDir", {
  1.1101 +         set: function(path) {
  1.1102 +           this.setCurrentDirectory(path);
  1.1103 +         },
  1.1104 +         get: function() {
  1.1105 +           return this.getCurrentDirectory();
  1.1106 +         }
  1.1107 +       }
  1.1108 +     );
  1.1109 +
  1.1110 +     // Utility functions, used for error-handling
  1.1111 +
  1.1112 +     /**
  1.1113 +      * Turn the result of |open| into an Error or a File
  1.1114 +      * @param {number} maybe The result of the |open| operation that may
  1.1115 +      * represent either an error or a success. If -1, this function raises
  1.1116 +      * an error holding ctypes.winLastError, otherwise it returns the opened file.
  1.1117 +      * @param {string=} path The path of the file.
  1.1118 +      */
  1.1119 +     function error_or_file(maybe, path) {
  1.1120 +       if (maybe == Const.INVALID_HANDLE_VALUE) {
  1.1121 +         throw new File.Error("open", ctypes.winLastError, path);
  1.1122 +       }
  1.1123 +       return new File(maybe, path);
  1.1124 +     }
  1.1125 +
  1.1126 +     /**
  1.1127 +      * Utility function to sort errors represented as "0" from successes.
  1.1128 +      *
  1.1129 +      * @param {string=} operation The name of the operation. If unspecified,
  1.1130 +      * the name of the caller function.
  1.1131 +      * @param {number} result The result of the operation that may
  1.1132 +      * represent either an error or a success. If 0, this function raises
  1.1133 +      * an error holding ctypes.winLastError, otherwise it returns |result|.
  1.1134 +      * @param {string=} path The path of the file.
  1.1135 +      */
  1.1136 +     function throw_on_zero(operation, result, path) {
  1.1137 +       if (result == 0) {
  1.1138 +         throw new File.Error(operation, ctypes.winLastError, path);
  1.1139 +       }
  1.1140 +       return result;
  1.1141 +     }
  1.1142 +
  1.1143 +     /**
  1.1144 +      * Utility function to sort errors represented as "-1" from successes.
  1.1145 +      *
  1.1146 +      * @param {string=} operation The name of the operation. If unspecified,
  1.1147 +      * the name of the caller function.
  1.1148 +      * @param {number} result The result of the operation that may
  1.1149 +      * represent either an error or a success. If -1, this function raises
  1.1150 +      * an error holding ctypes.winLastError, otherwise it returns |result|.
  1.1151 +      * @param {string=} path The path of the file.
  1.1152 +      */
  1.1153 +     function throw_on_negative(operation, result, path) {
  1.1154 +       if (result < 0) {
  1.1155 +         throw new File.Error(operation, ctypes.winLastError, path);
  1.1156 +       }
  1.1157 +       return result;
  1.1158 +     }
  1.1159 +
  1.1160 +     /**
  1.1161 +      * Utility function to sort errors represented as |null| from successes.
  1.1162 +      *
  1.1163 +      * @param {string=} operation The name of the operation. If unspecified,
  1.1164 +      * the name of the caller function.
  1.1165 +      * @param {pointer} result The result of the operation that may
  1.1166 +      * represent either an error or a success. If |null|, this function raises
  1.1167 +      * an error holding ctypes.winLastError, otherwise it returns |result|.
  1.1168 +      * @param {string=} path The path of the file.
  1.1169 +      */
  1.1170 +     function throw_on_null(operation, result, path) {
  1.1171 +       if (result == null || (result.isNull && result.isNull())) {
  1.1172 +         throw new File.Error(operation, ctypes.winLastError, path);
  1.1173 +       }
  1.1174 +       return result;
  1.1175 +     }
  1.1176 +
  1.1177 +     File.Win = exports.OS.Win.File;
  1.1178 +     File.Error = SysAll.Error;
  1.1179 +     exports.OS.File = File;
  1.1180 +     exports.OS.Shared.Type = Type;
  1.1181 +
  1.1182 +     Object.defineProperty(File, "POS_START", { value: SysAll.POS_START });
  1.1183 +     Object.defineProperty(File, "POS_CURRENT", { value: SysAll.POS_CURRENT });
  1.1184 +     Object.defineProperty(File, "POS_END", { value: SysAll.POS_END });
  1.1185 +   })(this);
  1.1186 +}

mercurial