addon-sdk/source/lib/sdk/io/fs.js

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4 "use strict";
michael@0 5
michael@0 6 module.metadata = {
michael@0 7 "stability": "experimental"
michael@0 8 };
michael@0 9
michael@0 10 const { Cc, Ci, CC } = require("chrome");
michael@0 11
michael@0 12 const { setTimeout } = require("../timers");
michael@0 13 const { Stream, InputStream, OutputStream } = require("./stream");
michael@0 14 const { emit, on } = require("../event/core");
michael@0 15 const { Buffer } = require("./buffer");
michael@0 16 const { ns } = require("../core/namespace");
michael@0 17 const { Class } = require("../core/heritage");
michael@0 18
michael@0 19
michael@0 20 const nsILocalFile = CC("@mozilla.org/file/local;1", "nsILocalFile",
michael@0 21 "initWithPath");
michael@0 22 const FileOutputStream = CC("@mozilla.org/network/file-output-stream;1",
michael@0 23 "nsIFileOutputStream", "init");
michael@0 24 const FileInputStream = CC("@mozilla.org/network/file-input-stream;1",
michael@0 25 "nsIFileInputStream", "init");
michael@0 26 const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
michael@0 27 "nsIBinaryInputStream", "setInputStream");
michael@0 28 const BinaryOutputStream = CC("@mozilla.org/binaryoutputstream;1",
michael@0 29 "nsIBinaryOutputStream", "setOutputStream");
michael@0 30 const StreamPump = CC("@mozilla.org/network/input-stream-pump;1",
michael@0 31 "nsIInputStreamPump", "init");
michael@0 32
michael@0 33 const { createOutputTransport, createInputTransport } =
michael@0 34 Cc["@mozilla.org/network/stream-transport-service;1"].
michael@0 35 getService(Ci.nsIStreamTransportService);
michael@0 36
michael@0 37 const { OPEN_UNBUFFERED } = Ci.nsITransport;
michael@0 38
michael@0 39
michael@0 40 const { REOPEN_ON_REWIND, DEFER_OPEN } = Ci.nsIFileInputStream;
michael@0 41 const { DIRECTORY_TYPE, NORMAL_FILE_TYPE } = Ci.nsIFile;
michael@0 42 const { NS_SEEK_SET, NS_SEEK_CUR, NS_SEEK_END } = Ci.nsISeekableStream;
michael@0 43
michael@0 44 const FILE_PERMISSION = parseInt("0666", 8);
michael@0 45 const PR_UINT32_MAX = 0xfffffff;
michael@0 46 // Values taken from:
michael@0 47 // http://mxr.mozilla.org/mozilla-central/source/nsprpub/pr/include/prio.h#615
michael@0 48 const PR_RDONLY = 0x01;
michael@0 49 const PR_WRONLY = 0x02;
michael@0 50 const PR_RDWR = 0x04;
michael@0 51 const PR_CREATE_FILE = 0x08;
michael@0 52 const PR_APPEND = 0x10;
michael@0 53 const PR_TRUNCATE = 0x20;
michael@0 54 const PR_SYNC = 0x40;
michael@0 55 const PR_EXCL = 0x80;
michael@0 56
michael@0 57 const FLAGS = {
michael@0 58 "r": PR_RDONLY,
michael@0 59 "r+": PR_RDWR,
michael@0 60 "w": PR_CREATE_FILE | PR_TRUNCATE | PR_WRONLY,
michael@0 61 "w+": PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR,
michael@0 62 "a": PR_APPEND | PR_CREATE_FILE | PR_WRONLY,
michael@0 63 "a+": PR_APPEND | PR_CREATE_FILE | PR_RDWR
michael@0 64 };
michael@0 65
michael@0 66 function accessor() {
michael@0 67 let map = new WeakMap();
michael@0 68 return function(fd, value) {
michael@0 69 if (value === null) map.delete(fd);
michael@0 70 if (value !== undefined) map.set(fd, value);
michael@0 71 return map.get(fd);
michael@0 72 }
michael@0 73 }
michael@0 74
michael@0 75 let nsIFile = accessor();
michael@0 76 let nsIFileInputStream = accessor();
michael@0 77 let nsIFileOutputStream = accessor();
michael@0 78 let nsIBinaryInputStream = accessor();
michael@0 79 let nsIBinaryOutputStream = accessor();
michael@0 80
michael@0 81 // Just a contstant object used to signal that all of the file
michael@0 82 // needs to be read.
michael@0 83 const ALL = new String("Read all of the file");
michael@0 84
michael@0 85 function isWritable(mode) !!(mode & PR_WRONLY || mode & PR_RDWR)
michael@0 86 function isReadable(mode) !!(mode & PR_RDONLY || mode & PR_RDWR)
michael@0 87
michael@0 88 function isString(value) typeof(value) === "string"
michael@0 89 function isFunction(value) typeof(value) === "function"
michael@0 90
michael@0 91 function toArray(enumerator) {
michael@0 92 let value = [];
michael@0 93 while(enumerator.hasMoreElements())
michael@0 94 value.push(enumerator.getNext())
michael@0 95 return value
michael@0 96 }
michael@0 97
michael@0 98 function getFileName(file) file.QueryInterface(Ci.nsIFile).leafName
michael@0 99
michael@0 100
michael@0 101 function remove(path, recursive) {
michael@0 102 let fd = new nsILocalFile(path)
michael@0 103 if (fd.exists()) {
michael@0 104 fd.remove(recursive || false);
michael@0 105 }
michael@0 106 else {
michael@0 107 throw FSError("remove", "ENOENT", 34, path);
michael@0 108 }
michael@0 109 }
michael@0 110
michael@0 111 /**
michael@0 112 * Utility function to convert either an octal number or string
michael@0 113 * into an octal number
michael@0 114 * 0777 => 0777
michael@0 115 * "0644" => 0644
michael@0 116 */
michael@0 117 function Mode(mode, fallback) {
michael@0 118 return isString(mode) ? parseInt(mode, 8) : mode || fallback;
michael@0 119 }
michael@0 120 function Flags(flag) {
michael@0 121 return !isString(flag) ? flag :
michael@0 122 FLAGS[flag] || Error("Unknown file open flag: " + flag);
michael@0 123 }
michael@0 124
michael@0 125
michael@0 126 function FSError(op, code, errno, path, file, line) {
michael@0 127 let error = Error(code + ", " + op + " " + path, file, line);
michael@0 128 error.code = code;
michael@0 129 error.path = path;
michael@0 130 error.errno = errno;
michael@0 131 return error;
michael@0 132 }
michael@0 133
michael@0 134 const ReadStream = Class({
michael@0 135 extends: InputStream,
michael@0 136 initialize: function initialize(path, options) {
michael@0 137 this.position = -1;
michael@0 138 this.length = -1;
michael@0 139 this.flags = "r";
michael@0 140 this.mode = FILE_PERMISSION;
michael@0 141 this.bufferSize = 64 * 1024;
michael@0 142
michael@0 143 options = options || {};
michael@0 144
michael@0 145 if ("flags" in options && options.flags)
michael@0 146 this.flags = options.flags;
michael@0 147 if ("bufferSize" in options && options.bufferSize)
michael@0 148 this.bufferSize = options.bufferSize;
michael@0 149 if ("length" in options && options.length)
michael@0 150 this.length = options.length;
michael@0 151 if ("position" in options && options.position !== undefined)
michael@0 152 this.position = options.position;
michael@0 153
michael@0 154 let { flags, mode, position, length } = this;
michael@0 155 let fd = isString(path) ? openSync(path, flags, mode) : path;
michael@0 156 this.fd = fd;
michael@0 157
michael@0 158 let input = nsIFileInputStream(fd);
michael@0 159 // Setting a stream position, unless it"s `-1` which means current position.
michael@0 160 if (position >= 0)
michael@0 161 input.QueryInterface(Ci.nsISeekableStream).seek(NS_SEEK_SET, position);
michael@0 162 // We use `nsIStreamTransportService` service to transform blocking
michael@0 163 // file input stream into a fully asynchronous stream that can be written
michael@0 164 // without blocking the main thread.
michael@0 165 let transport = createInputTransport(input, position, length, false);
michael@0 166 // Open an input stream on a transport. We don"t pass flags to guarantee
michael@0 167 // non-blocking stream semantics. Also we use defaults for segment size &
michael@0 168 // count.
michael@0 169 InputStream.prototype.initialize.call(this, {
michael@0 170 asyncInputStream: transport.openInputStream(null, 0, 0)
michael@0 171 });
michael@0 172
michael@0 173 // Close file descriptor on end and destroy the stream.
michael@0 174 on(this, "end", _ => {
michael@0 175 this.destroy();
michael@0 176 emit(this, "close");
michael@0 177 });
michael@0 178
michael@0 179 this.read();
michael@0 180 },
michael@0 181 destroy: function() {
michael@0 182 closeSync(this.fd);
michael@0 183 InputStream.prototype.destroy.call(this);
michael@0 184 }
michael@0 185 });
michael@0 186 exports.ReadStream = ReadStream;
michael@0 187 exports.createReadStream = function createReadStream(path, options) {
michael@0 188 return new ReadStream(path, options);
michael@0 189 };
michael@0 190
michael@0 191 const WriteStream = Class({
michael@0 192 extends: OutputStream,
michael@0 193 initialize: function initialize(path, options) {
michael@0 194 this.drainable = true;
michael@0 195 this.flags = "w";
michael@0 196 this.position = -1;
michael@0 197 this.mode = FILE_PERMISSION;
michael@0 198
michael@0 199 options = options || {};
michael@0 200
michael@0 201 if ("flags" in options && options.flags)
michael@0 202 this.flags = options.flags;
michael@0 203 if ("mode" in options && options.mode)
michael@0 204 this.mode = options.mode;
michael@0 205 if ("position" in options && options.position !== undefined)
michael@0 206 this.position = options.position;
michael@0 207
michael@0 208 let { position, flags, mode } = this;
michael@0 209 // If pass was passed we create a file descriptor out of it. Otherwise
michael@0 210 // we just use given file descriptor.
michael@0 211 let fd = isString(path) ? openSync(path, flags, mode) : path;
michael@0 212 this.fd = fd;
michael@0 213
michael@0 214 let output = nsIFileOutputStream(fd);
michael@0 215 // Setting a stream position, unless it"s `-1` which means current position.
michael@0 216 if (position >= 0)
michael@0 217 output.QueryInterface(Ci.nsISeekableStream).seek(NS_SEEK_SET, position);
michael@0 218 // We use `nsIStreamTransportService` service to transform blocking
michael@0 219 // file output stream into a fully asynchronous stream that can be written
michael@0 220 // without blocking the main thread.
michael@0 221 let transport = createOutputTransport(output, position, -1, false);
michael@0 222 // Open an output stream on a transport. We don"t pass flags to guarantee
michael@0 223 // non-blocking stream semantics. Also we use defaults for segment size &
michael@0 224 // count.
michael@0 225 OutputStream.prototype.initialize.call(this, {
michael@0 226 asyncOutputStream: transport.openOutputStream(OPEN_UNBUFFERED, 0, 0),
michael@0 227 output: output
michael@0 228 });
michael@0 229
michael@0 230 // For write streams "finish" basically means close.
michael@0 231 on(this, "finish", _ => {
michael@0 232 this.destroy();
michael@0 233 emit(this, "close");
michael@0 234 });
michael@0 235 },
michael@0 236 destroy: function() {
michael@0 237 OutputStream.prototype.destroy.call(this);
michael@0 238 closeSync(this.fd);
michael@0 239 }
michael@0 240 });
michael@0 241 exports.WriteStream = WriteStream;
michael@0 242 exports.createWriteStream = function createWriteStream(path, options) {
michael@0 243 return new WriteStream(path, options);
michael@0 244 };
michael@0 245
michael@0 246 const Stats = Class({
michael@0 247 initialize: function initialize(path) {
michael@0 248 let file = new nsILocalFile(path);
michael@0 249 if (!file.exists()) throw FSError("stat", "ENOENT", 34, path);
michael@0 250 nsIFile(this, file);
michael@0 251 },
michael@0 252 isDirectory: function() nsIFile(this).isDirectory(),
michael@0 253 isFile: function() nsIFile(this).isFile(),
michael@0 254 isSymbolicLink: function() nsIFile(this).isSymlink(),
michael@0 255 get mode() nsIFile(this).permissions,
michael@0 256 get size() nsIFile(this).fileSize,
michael@0 257 get mtime() nsIFile(this).lastModifiedTime,
michael@0 258 isBlockDevice: function() nsIFile(this).isSpecial(),
michael@0 259 isCharacterDevice: function() nsIFile(this).isSpecial(),
michael@0 260 isFIFO: function() nsIFile(this).isSpecial(),
michael@0 261 isSocket: function() nsIFile(this).isSpecial(),
michael@0 262 // non standard
michael@0 263 get exists() nsIFile(this).exists(),
michael@0 264 get hidden() nsIFile(this).isHidden(),
michael@0 265 get writable() nsIFile(this).isWritable(),
michael@0 266 get readable() nsIFile(this).isReadable()
michael@0 267 });
michael@0 268 exports.Stats = Stats;
michael@0 269
michael@0 270 const LStats = Class({
michael@0 271 extends: Stats,
michael@0 272 get size() this.isSymbolicLink() ? nsIFile(this).fileSizeOfLink :
michael@0 273 nsIFile(this).fileSize,
michael@0 274 get mtime() this.isSymbolicLink() ? nsIFile(this).lastModifiedTimeOfLink :
michael@0 275 nsIFile(this).lastModifiedTime,
michael@0 276 // non standard
michael@0 277 get permissions() this.isSymbolicLink() ? nsIFile(this).permissionsOfLink :
michael@0 278 nsIFile(this).permissions
michael@0 279 });
michael@0 280
michael@0 281 const FStat = Class({
michael@0 282 extends: Stats,
michael@0 283 initialize: function initialize(fd) {
michael@0 284 nsIFile(this, nsIFile(fd));
michael@0 285 }
michael@0 286 });
michael@0 287
michael@0 288 function noop() {}
michael@0 289 function Async(wrapped) {
michael@0 290 return function (path, callback) {
michael@0 291 let args = Array.slice(arguments);
michael@0 292 callback = args.pop();
michael@0 293 // If node is not given a callback argument
michael@0 294 // it just does not calls it.
michael@0 295 if (typeof(callback) !== "function") {
michael@0 296 args.push(callback);
michael@0 297 callback = noop;
michael@0 298 }
michael@0 299 setTimeout(function() {
michael@0 300 try {
michael@0 301 var result = wrapped.apply(this, args);
michael@0 302 if (result === undefined) callback(null);
michael@0 303 else callback(null, result);
michael@0 304 } catch (error) {
michael@0 305 callback(error);
michael@0 306 }
michael@0 307 }, 0);
michael@0 308 }
michael@0 309 }
michael@0 310
michael@0 311
michael@0 312 /**
michael@0 313 * Synchronous rename(2)
michael@0 314 */
michael@0 315 function renameSync(oldPath, newPath) {
michael@0 316 let source = new nsILocalFile(oldPath);
michael@0 317 let target = new nsILocalFile(newPath);
michael@0 318 if (!source.exists()) throw FSError("rename", "ENOENT", 34, oldPath);
michael@0 319 return source.moveTo(target.parent, target.leafName);
michael@0 320 };
michael@0 321 exports.renameSync = renameSync;
michael@0 322
michael@0 323 /**
michael@0 324 * Asynchronous rename(2). No arguments other than a possible exception are
michael@0 325 * given to the completion callback.
michael@0 326 */
michael@0 327 let rename = Async(renameSync);
michael@0 328 exports.rename = rename;
michael@0 329
michael@0 330 /**
michael@0 331 * Test whether or not the given path exists by checking with the file system.
michael@0 332 */
michael@0 333 function existsSync(path) {
michael@0 334 return new nsILocalFile(path).exists();
michael@0 335 }
michael@0 336 exports.existsSync = existsSync;
michael@0 337
michael@0 338 let exists = Async(existsSync);
michael@0 339 exports.exists = exists;
michael@0 340
michael@0 341 /**
michael@0 342 * Synchronous ftruncate(2).
michael@0 343 */
michael@0 344 function truncateSync(path, length) {
michael@0 345 let fd = openSync(path, "w");
michael@0 346 ftruncateSync(fd, length);
michael@0 347 closeSync(fd);
michael@0 348 }
michael@0 349 exports.truncateSync = truncateSync;
michael@0 350
michael@0 351 /**
michael@0 352 * Asynchronous ftruncate(2). No arguments other than a possible exception are
michael@0 353 * given to the completion callback.
michael@0 354 */
michael@0 355 function truncate(path, length, callback) {
michael@0 356 open(path, "w", function(error, fd) {
michael@0 357 if (error) return callback(error);
michael@0 358 ftruncate(fd, length, function(error) {
michael@0 359 if (error) {
michael@0 360 closeSync(fd);
michael@0 361 callback(error);
michael@0 362 }
michael@0 363 else {
michael@0 364 close(fd, callback);
michael@0 365 }
michael@0 366 });
michael@0 367 });
michael@0 368 }
michael@0 369 exports.truncate = truncate;
michael@0 370
michael@0 371 function ftruncate(fd, length, callback) {
michael@0 372 write(fd, new Buffer(length), 0, length, 0, function(error) {
michael@0 373 callback(error);
michael@0 374 });
michael@0 375 }
michael@0 376 exports.ftruncate = ftruncate;
michael@0 377
michael@0 378 function ftruncateSync(fd, length = 0) {
michael@0 379 writeSync(fd, new Buffer(length), 0, length, 0);
michael@0 380 }
michael@0 381 exports.ftruncateSync = ftruncateSync;
michael@0 382
michael@0 383 function chownSync(path, uid, gid) {
michael@0 384 throw Error("Not implemented yet!!");
michael@0 385 }
michael@0 386 exports.chownSync = chownSync;
michael@0 387
michael@0 388 let chown = Async(chownSync);
michael@0 389 exports.chown = chown;
michael@0 390
michael@0 391 function lchownSync(path, uid, gid) {
michael@0 392 throw Error("Not implemented yet!!");
michael@0 393 }
michael@0 394 exports.lchownSync = chownSync;
michael@0 395
michael@0 396 let lchown = Async(lchown);
michael@0 397 exports.lchown = lchown;
michael@0 398
michael@0 399 /**
michael@0 400 * Synchronous chmod(2).
michael@0 401 */
michael@0 402 function chmodSync (path, mode) {
michael@0 403 let file;
michael@0 404 try {
michael@0 405 file = new nsILocalFile(path);
michael@0 406 } catch(e) {
michael@0 407 throw FSError("chmod", "ENOENT", 34, path);
michael@0 408 }
michael@0 409
michael@0 410 file.permissions = Mode(mode);
michael@0 411 }
michael@0 412 exports.chmodSync = chmodSync;
michael@0 413 /**
michael@0 414 * Asynchronous chmod(2). No arguments other than a possible exception are
michael@0 415 * given to the completion callback.
michael@0 416 */
michael@0 417 let chmod = Async(chmodSync);
michael@0 418 exports.chmod = chmod;
michael@0 419
michael@0 420 /**
michael@0 421 * Synchronous chmod(2).
michael@0 422 */
michael@0 423 function fchmodSync(fd, mode) {
michael@0 424 throw Error("Not implemented yet!!");
michael@0 425 };
michael@0 426 exports.fchmodSync = fchmodSync;
michael@0 427 /**
michael@0 428 * Asynchronous chmod(2). No arguments other than a possible exception are
michael@0 429 * given to the completion callback.
michael@0 430 */
michael@0 431 let fchmod = Async(fchmodSync);
michael@0 432 exports.fchmod = fchmod;
michael@0 433
michael@0 434
michael@0 435 /**
michael@0 436 * Synchronous stat(2). Returns an instance of `fs.Stats`
michael@0 437 */
michael@0 438 function statSync(path) {
michael@0 439 return new Stats(path);
michael@0 440 };
michael@0 441 exports.statSync = statSync;
michael@0 442
michael@0 443 /**
michael@0 444 * Asynchronous stat(2). The callback gets two arguments (err, stats) where
michael@0 445 * stats is a `fs.Stats` object. It looks like this:
michael@0 446 */
michael@0 447 let stat = Async(statSync);
michael@0 448 exports.stat = stat;
michael@0 449
michael@0 450 /**
michael@0 451 * Synchronous lstat(2). Returns an instance of `fs.Stats`.
michael@0 452 */
michael@0 453 function lstatSync(path) {
michael@0 454 return new LStats(path);
michael@0 455 };
michael@0 456 exports.lstatSync = lstatSync;
michael@0 457
michael@0 458 /**
michael@0 459 * Asynchronous lstat(2). The callback gets two arguments (err, stats) where
michael@0 460 * stats is a fs.Stats object. lstat() is identical to stat(), except that if
michael@0 461 * path is a symbolic link, then the link itself is stat-ed, not the file that
michael@0 462 * it refers to.
michael@0 463 */
michael@0 464 let lstat = Async(lstatSync);
michael@0 465 exports.lstat = lstat;
michael@0 466
michael@0 467 /**
michael@0 468 * Synchronous fstat(2). Returns an instance of `fs.Stats`.
michael@0 469 */
michael@0 470 function fstatSync(fd) {
michael@0 471 return new FStat(fd);
michael@0 472 };
michael@0 473 exports.fstatSync = fstatSync;
michael@0 474
michael@0 475 /**
michael@0 476 * Asynchronous fstat(2). The callback gets two arguments (err, stats) where
michael@0 477 * stats is a fs.Stats object.
michael@0 478 */
michael@0 479 let fstat = Async(fstatSync);
michael@0 480 exports.fstat = fstat;
michael@0 481
michael@0 482 /**
michael@0 483 * Synchronous link(2).
michael@0 484 */
michael@0 485 function linkSync(source, target) {
michael@0 486 throw Error("Not implemented yet!!");
michael@0 487 };
michael@0 488 exports.linkSync = linkSync;
michael@0 489
michael@0 490 /**
michael@0 491 * Asynchronous link(2). No arguments other than a possible exception are given
michael@0 492 * to the completion callback.
michael@0 493 */
michael@0 494 let link = Async(linkSync);
michael@0 495 exports.link = link;
michael@0 496
michael@0 497 /**
michael@0 498 * Synchronous symlink(2).
michael@0 499 */
michael@0 500 function symlinkSync(source, target) {
michael@0 501 throw Error("Not implemented yet!!");
michael@0 502 };
michael@0 503 exports.symlinkSync = symlinkSync;
michael@0 504
michael@0 505 /**
michael@0 506 * Asynchronous symlink(2). No arguments other than a possible exception are
michael@0 507 * given to the completion callback.
michael@0 508 */
michael@0 509 let symlink = Async(symlinkSync);
michael@0 510 exports.symlink = symlink;
michael@0 511
michael@0 512 /**
michael@0 513 * Synchronous readlink(2). Returns the resolved path.
michael@0 514 */
michael@0 515 function readlinkSync(path) {
michael@0 516 return new nsILocalFile(path).target;
michael@0 517 };
michael@0 518 exports.readlinkSync = readlinkSync;
michael@0 519
michael@0 520 /**
michael@0 521 * Asynchronous readlink(2). The callback gets two arguments
michael@0 522 * `(error, resolvedPath)`.
michael@0 523 */
michael@0 524 let readlink = Async(readlinkSync);
michael@0 525 exports.readlink = readlink;
michael@0 526
michael@0 527 /**
michael@0 528 * Synchronous realpath(2). Returns the resolved path.
michael@0 529 */
michael@0 530 function realpathSync(path) {
michael@0 531 return new nsILocalFile(path).path;
michael@0 532 };
michael@0 533 exports.realpathSync = realpathSync;
michael@0 534
michael@0 535 /**
michael@0 536 * Asynchronous realpath(2). The callback gets two arguments
michael@0 537 * `(err, resolvedPath)`.
michael@0 538 */
michael@0 539 let realpath = Async(realpathSync);
michael@0 540 exports.realpath = realpath;
michael@0 541
michael@0 542 /**
michael@0 543 * Synchronous unlink(2).
michael@0 544 */
michael@0 545 let unlinkSync = remove;
michael@0 546 exports.unlinkSync = unlinkSync;
michael@0 547
michael@0 548 /**
michael@0 549 * Asynchronous unlink(2). No arguments other than a possible exception are
michael@0 550 * given to the completion callback.
michael@0 551 */
michael@0 552 let unlink = Async(remove);
michael@0 553 exports.unlink = unlink;
michael@0 554
michael@0 555 /**
michael@0 556 * Synchronous rmdir(2).
michael@0 557 */
michael@0 558 let rmdirSync = remove;
michael@0 559 exports.rmdirSync = rmdirSync;
michael@0 560
michael@0 561 /**
michael@0 562 * Asynchronous rmdir(2). No arguments other than a possible exception are
michael@0 563 * given to the completion callback.
michael@0 564 */
michael@0 565 let rmdir = Async(rmdirSync);
michael@0 566 exports.rmdir = rmdir;
michael@0 567
michael@0 568 /**
michael@0 569 * Synchronous mkdir(2).
michael@0 570 */
michael@0 571 function mkdirSync(path, mode) {
michael@0 572 try {
michael@0 573 return nsILocalFile(path).create(DIRECTORY_TYPE, Mode(mode));
michael@0 574 } catch (error) {
michael@0 575 // Adjust exception thorw to match ones thrown by node.
michael@0 576 if (error.name === "NS_ERROR_FILE_ALREADY_EXISTS") {
michael@0 577 let { fileName, lineNumber } = error;
michael@0 578 error = FSError("mkdir", "EEXIST", 47, path, fileName, lineNumber);
michael@0 579 }
michael@0 580 throw error;
michael@0 581 }
michael@0 582 };
michael@0 583 exports.mkdirSync = mkdirSync;
michael@0 584
michael@0 585 /**
michael@0 586 * Asynchronous mkdir(2). No arguments other than a possible exception are
michael@0 587 * given to the completion callback.
michael@0 588 */
michael@0 589 let mkdir = Async(mkdirSync);
michael@0 590 exports.mkdir = mkdir;
michael@0 591
michael@0 592 /**
michael@0 593 * Synchronous readdir(3). Returns an array of filenames excluding `"."` and
michael@0 594 * `".."`.
michael@0 595 */
michael@0 596 function readdirSync(path) {
michael@0 597 try {
michael@0 598 return toArray(new nsILocalFile(path).directoryEntries).map(getFileName);
michael@0 599 }
michael@0 600 catch (error) {
michael@0 601 // Adjust exception thorw to match ones thrown by node.
michael@0 602 if (error.name === "NS_ERROR_FILE_TARGET_DOES_NOT_EXIST" ||
michael@0 603 error.name === "NS_ERROR_FILE_NOT_FOUND")
michael@0 604 {
michael@0 605 let { fileName, lineNumber } = error;
michael@0 606 error = FSError("readdir", "ENOENT", 34, path, fileName, lineNumber);
michael@0 607 }
michael@0 608 throw error;
michael@0 609 }
michael@0 610 };
michael@0 611 exports.readdirSync = readdirSync;
michael@0 612
michael@0 613 /**
michael@0 614 * Asynchronous readdir(3). Reads the contents of a directory. The callback
michael@0 615 * gets two arguments `(error, files)` where `files` is an array of the names
michael@0 616 * of the files in the directory excluding `"."` and `".."`.
michael@0 617 */
michael@0 618 let readdir = Async(readdirSync);
michael@0 619 exports.readdir = readdir;
michael@0 620
michael@0 621 /**
michael@0 622 * Synchronous close(2).
michael@0 623 */
michael@0 624 function closeSync(fd) {
michael@0 625 let input = nsIFileInputStream(fd);
michael@0 626 let output = nsIFileOutputStream(fd);
michael@0 627
michael@0 628 // Closing input stream and removing reference.
michael@0 629 if (input) input.close();
michael@0 630 // Closing output stream and removing reference.
michael@0 631 if (output) output.close();
michael@0 632
michael@0 633 nsIFile(fd, null);
michael@0 634 nsIFileInputStream(fd, null);
michael@0 635 nsIFileOutputStream(fd, null);
michael@0 636 nsIBinaryInputStream(fd, null);
michael@0 637 nsIBinaryOutputStream(fd, null);
michael@0 638 };
michael@0 639 exports.closeSync = closeSync;
michael@0 640 /**
michael@0 641 * Asynchronous close(2). No arguments other than a possible exception are
michael@0 642 * given to the completion callback.
michael@0 643 */
michael@0 644 let close = Async(closeSync);
michael@0 645 exports.close = close;
michael@0 646
michael@0 647 /**
michael@0 648 * Synchronous open(2).
michael@0 649 */
michael@0 650 function openSync(path, flags, mode) {
michael@0 651 let [ fd, flags, mode, file ] =
michael@0 652 [ { path: path }, Flags(flags), Mode(mode), nsILocalFile(path) ];
michael@0 653
michael@0 654 nsIFile(fd, file);
michael@0 655
michael@0 656 // If trying to open file for just read that does not exists
michael@0 657 // need to throw exception as node does.
michael@0 658 if (!file.exists() && !isWritable(flags))
michael@0 659 throw FSError("open", "ENOENT", 34, path);
michael@0 660
michael@0 661 // If we want to open file in read mode we initialize input stream.
michael@0 662 if (isReadable(flags)) {
michael@0 663 let input = FileInputStream(file, flags, mode, DEFER_OPEN);
michael@0 664 nsIFileInputStream(fd, input);
michael@0 665 }
michael@0 666
michael@0 667 // If we want to open file in write mode we initialize output stream for it.
michael@0 668 if (isWritable(flags)) {
michael@0 669 let output = FileOutputStream(file, flags, mode, DEFER_OPEN);
michael@0 670 nsIFileOutputStream(fd, output);
michael@0 671 }
michael@0 672
michael@0 673 return fd;
michael@0 674 }
michael@0 675 exports.openSync = openSync;
michael@0 676 /**
michael@0 677 * Asynchronous file open. See open(2). Flags can be
michael@0 678 * `"r", "r+", "w", "w+", "a"`, or `"a+"`. mode defaults to `0666`.
michael@0 679 * The callback gets two arguments `(error, fd).
michael@0 680 */
michael@0 681 let open = Async(openSync);
michael@0 682 exports.open = open;
michael@0 683
michael@0 684 /**
michael@0 685 * Synchronous version of buffer-based fs.write(). Returns the number of bytes
michael@0 686 * written.
michael@0 687 */
michael@0 688 function writeSync(fd, buffer, offset, length, position) {
michael@0 689 if (length + offset > buffer.length) {
michael@0 690 throw Error("Length is extends beyond buffer");
michael@0 691 }
michael@0 692 else if (length + offset !== buffer.length) {
michael@0 693 buffer = buffer.slice(offset, offset + length);
michael@0 694 }
michael@0 695 let writeStream = new WriteStream(fd, { position: position,
michael@0 696 length: length });
michael@0 697
michael@0 698 let output = BinaryOutputStream(nsIFileOutputStream(fd));
michael@0 699 nsIBinaryOutputStream(fd, output);
michael@0 700 // We write content as a byte array as this will avoid any transcoding
michael@0 701 // if content was a buffer.
michael@0 702 output.writeByteArray(buffer.valueOf(), buffer.length);
michael@0 703 output.flush();
michael@0 704 };
michael@0 705 exports.writeSync = writeSync;
michael@0 706
michael@0 707 /**
michael@0 708 * Write buffer to the file specified by fd.
michael@0 709 *
michael@0 710 * `offset` and `length` determine the part of the buffer to be written.
michael@0 711 *
michael@0 712 * `position` refers to the offset from the beginning of the file where this
michael@0 713 * data should be written. If `position` is `null`, the data will be written
michael@0 714 * at the current position. See pwrite(2).
michael@0 715 *
michael@0 716 * The callback will be given three arguments `(error, written, buffer)` where
michael@0 717 * written specifies how many bytes were written into buffer.
michael@0 718 *
michael@0 719 * Note that it is unsafe to use `fs.write` multiple times on the same file
michael@0 720 * without waiting for the callback.
michael@0 721 */
michael@0 722 function write(fd, buffer, offset, length, position, callback) {
michael@0 723 if (!Buffer.isBuffer(buffer)) {
michael@0 724 // (fd, data, position, encoding, callback)
michael@0 725 let encoding = null;
michael@0 726 [ position, encoding, callback ] = Array.slice(arguments, 1);
michael@0 727 buffer = new Buffer(String(buffer), encoding);
michael@0 728 offset = 0;
michael@0 729 } else if (length + offset > buffer.length) {
michael@0 730 throw Error("Length is extends beyond buffer");
michael@0 731 } else if (length + offset !== buffer.length) {
michael@0 732 buffer = buffer.slice(offset, offset + length);
michael@0 733 }
michael@0 734
michael@0 735 let writeStream = new WriteStream(fd, { position: position,
michael@0 736 length: length });
michael@0 737 writeStream.on("error", callback);
michael@0 738 writeStream.write(buffer, function onEnd() {
michael@0 739 writeStream.destroy();
michael@0 740 if (callback)
michael@0 741 callback(null, buffer.length, buffer);
michael@0 742 });
michael@0 743 };
michael@0 744 exports.write = write;
michael@0 745
michael@0 746 /**
michael@0 747 * Synchronous version of string-based fs.read. Returns the number of
michael@0 748 * bytes read.
michael@0 749 */
michael@0 750 function readSync(fd, buffer, offset, length, position) {
michael@0 751 let input = nsIFileInputStream(fd);
michael@0 752 // Setting a stream position, unless it"s `-1` which means current position.
michael@0 753 if (position >= 0)
michael@0 754 input.QueryInterface(Ci.nsISeekableStream).seek(NS_SEEK_SET, position);
michael@0 755 // We use `nsIStreamTransportService` service to transform blocking
michael@0 756 // file input stream into a fully asynchronous stream that can be written
michael@0 757 // without blocking the main thread.
michael@0 758 let binaryInputStream = BinaryInputStream(input);
michael@0 759 let count = length === ALL ? binaryInputStream.available() : length;
michael@0 760 if (offset === 0) binaryInputStream.readArrayBuffer(count, buffer.buffer);
michael@0 761 else {
michael@0 762 let chunk = new Buffer(count);
michael@0 763 binaryInputStream.readArrayBuffer(count, chunk.buffer);
michael@0 764 chunk.copy(buffer, offset);
michael@0 765 }
michael@0 766
michael@0 767 return buffer.slice(offset, offset + count);
michael@0 768 };
michael@0 769 exports.readSync = readSync;
michael@0 770
michael@0 771 /**
michael@0 772 * Read data from the file specified by `fd`.
michael@0 773 *
michael@0 774 * `buffer` is the buffer that the data will be written to.
michael@0 775 * `offset` is offset within the buffer where writing will start.
michael@0 776 *
michael@0 777 * `length` is an integer specifying the number of bytes to read.
michael@0 778 *
michael@0 779 * `position` is an integer specifying where to begin reading from in the file.
michael@0 780 * If `position` is `null`, data will be read from the current file position.
michael@0 781 *
michael@0 782 * The callback is given the three arguments, `(error, bytesRead, buffer)`.
michael@0 783 */
michael@0 784 function read(fd, buffer, offset, length, position, callback) {
michael@0 785 let bytesRead = 0;
michael@0 786 let readStream = new ReadStream(fd, { position: position, length: length });
michael@0 787 readStream.on("data", function onData(data) {
michael@0 788 data.copy(buffer, offset + bytesRead);
michael@0 789 bytesRead += data.length;
michael@0 790 });
michael@0 791 readStream.on("end", function onEnd() {
michael@0 792 callback(null, bytesRead, buffer);
michael@0 793 readStream.destroy();
michael@0 794 });
michael@0 795 };
michael@0 796 exports.read = read;
michael@0 797
michael@0 798 /**
michael@0 799 * Asynchronously reads the entire contents of a file.
michael@0 800 * The callback is passed two arguments `(error, data)`, where data is the
michael@0 801 * contents of the file.
michael@0 802 */
michael@0 803 function readFile(path, encoding, callback) {
michael@0 804 if (isFunction(encoding)) {
michael@0 805 callback = encoding
michael@0 806 encoding = null
michael@0 807 }
michael@0 808
michael@0 809 let buffer = null;
michael@0 810 try {
michael@0 811 let readStream = new ReadStream(path);
michael@0 812 readStream.on("data", function(data) {
michael@0 813 if (!buffer) buffer = data;
michael@0 814 else buffer = Buffer.concat([buffer, data], 2);
michael@0 815 });
michael@0 816 readStream.on("error", function onError(error) {
michael@0 817 callback(error);
michael@0 818 });
michael@0 819 readStream.on("end", function onEnd() {
michael@0 820 // Note: Need to destroy before invoking a callback
michael@0 821 // so that file descriptor is released.
michael@0 822 readStream.destroy();
michael@0 823 callback(null, buffer);
michael@0 824 });
michael@0 825 } catch (error) {
michael@0 826 setTimeout(callback, 0, error);
michael@0 827 }
michael@0 828 };
michael@0 829 exports.readFile = readFile;
michael@0 830
michael@0 831 /**
michael@0 832 * Synchronous version of `fs.readFile`. Returns the contents of the path.
michael@0 833 * If encoding is specified then this function returns a string.
michael@0 834 * Otherwise it returns a buffer.
michael@0 835 */
michael@0 836 function readFileSync(path, encoding) {
michael@0 837 let fd = openSync(path, "r");
michael@0 838 let size = fstatSync(fd).size;
michael@0 839 let buffer = new Buffer(size);
michael@0 840 try {
michael@0 841 readSync(fd, buffer, 0, ALL, 0);
michael@0 842 }
michael@0 843 finally {
michael@0 844 closeSync(fd);
michael@0 845 }
michael@0 846 return buffer;
michael@0 847 };
michael@0 848 exports.readFileSync = readFileSync;
michael@0 849
michael@0 850 /**
michael@0 851 * Asynchronously writes data to a file, replacing the file if it already
michael@0 852 * exists. data can be a string or a buffer.
michael@0 853 */
michael@0 854 function writeFile(path, content, encoding, callback) {
michael@0 855 if (!isString(path))
michael@0 856 throw new TypeError('path must be a string');
michael@0 857
michael@0 858 try {
michael@0 859 if (isFunction(encoding)) {
michael@0 860 callback = encoding
michael@0 861 encoding = null
michael@0 862 }
michael@0 863 if (isString(content))
michael@0 864 content = new Buffer(content, encoding);
michael@0 865
michael@0 866 let writeStream = new WriteStream(path);
michael@0 867 let error = null;
michael@0 868
michael@0 869 writeStream.end(content, function() {
michael@0 870 writeStream.destroy();
michael@0 871 callback(error);
michael@0 872 });
michael@0 873
michael@0 874 writeStream.on("error", function onError(reason) {
michael@0 875 error = reason;
michael@0 876 writeStream.destroy();
michael@0 877 });
michael@0 878 } catch (error) {
michael@0 879 callback(error);
michael@0 880 }
michael@0 881 };
michael@0 882 exports.writeFile = writeFile;
michael@0 883
michael@0 884 /**
michael@0 885 * The synchronous version of `fs.writeFile`.
michael@0 886 */
michael@0 887 function writeFileSync(filename, data, encoding) {
michael@0 888 throw Error("Not implemented");
michael@0 889 };
michael@0 890 exports.writeFileSync = writeFileSync;
michael@0 891
michael@0 892
michael@0 893 function utimesSync(path, atime, mtime) {
michael@0 894 throw Error("Not implemented");
michael@0 895 }
michael@0 896 exports.utimesSync = utimesSync;
michael@0 897
michael@0 898 let utimes = Async(utimesSync);
michael@0 899 exports.utimes = utimes;
michael@0 900
michael@0 901 function futimesSync(fd, atime, mtime, callback) {
michael@0 902 throw Error("Not implemented");
michael@0 903 }
michael@0 904 exports.futimesSync = futimesSync;
michael@0 905
michael@0 906 let futimes = Async(futimesSync);
michael@0 907 exports.futimes = futimes;
michael@0 908
michael@0 909 function fsyncSync(fd, atime, mtime, callback) {
michael@0 910 throw Error("Not implemented");
michael@0 911 }
michael@0 912 exports.fsyncSync = fsyncSync;
michael@0 913
michael@0 914 let fsync = Async(fsyncSync);
michael@0 915 exports.fsync = fsync;
michael@0 916
michael@0 917
michael@0 918 /**
michael@0 919 * Watch for changes on filename. The callback listener will be called each
michael@0 920 * time the file is accessed.
michael@0 921 *
michael@0 922 * The second argument is optional. The options if provided should be an object
michael@0 923 * containing two members a boolean, persistent, and interval, a polling value
michael@0 924 * in milliseconds. The default is { persistent: true, interval: 0 }.
michael@0 925 */
michael@0 926 function watchFile(path, options, listener) {
michael@0 927 throw Error("Not implemented");
michael@0 928 };
michael@0 929 exports.watchFile = watchFile;
michael@0 930
michael@0 931
michael@0 932 function unwatchFile(path, listener) {
michael@0 933 throw Error("Not implemented");
michael@0 934 }
michael@0 935 exports.unwatchFile = unwatchFile;
michael@0 936
michael@0 937 function watch(path, options, listener) {
michael@0 938 throw Error("Not implemented");
michael@0 939 }
michael@0 940 exports.watch = watch;

mercurial