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 +}