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