1 // Copyright Joyent, Inc. and other Node contributors.
3 // Permission is hereby granted, free of charge, to any person obtaining a
4 // copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to permit
8 // persons to whom the Software is furnished to do so, subject to the
9 // following conditions:
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20 // USE OR OTHER DEALINGS IN THE SOFTWARE.
22 var util = require('util');
24 var binding = process.binding('fs');
25 var constants = process.binding('constants');
27 var Stream = require('stream').Stream;
29 var kMinPoolSpace = 128;
30 var kPoolSize = 40 * 1024;
32 fs.Stats = binding.Stats;
34 fs.Stats.prototype._checkModeProperty = function(property) {
35 return ((this.mode & constants.S_IFMT) === property);
38 fs.Stats.prototype.isDirectory = function() {
39 return this._checkModeProperty(constants.S_IFDIR);
42 fs.Stats.prototype.isFile = function() {
43 return this._checkModeProperty(constants.S_IFREG);
46 fs.Stats.prototype.isBlockDevice = function() {
47 return this._checkModeProperty(constants.S_IFBLK);
50 fs.Stats.prototype.isCharacterDevice = function() {
51 return this._checkModeProperty(constants.S_IFCHR);
54 fs.Stats.prototype.isSymbolicLink = function() {
55 return this._checkModeProperty(constants.S_IFLNK);
58 fs.Stats.prototype.isFIFO = function() {
59 return this._checkModeProperty(constants.S_IFIFO);
62 fs.Stats.prototype.isSocket = function() {
63 return this._checkModeProperty(constants.S_IFSOCK);
66 fs.readFile = function(path, encoding_) {
67 var encoding = typeof(encoding_) === 'string' ? encoding_ : null;
68 var callback = arguments[arguments.length - 1];
69 if (typeof(callback) !== 'function') callback = noop;
70 var readStream = fs.createReadStream(path);
74 readStream.on('data', function(chunk) {
76 nread += chunk.length;
79 readStream.on('error', function(er) {
84 readStream.on('end', function() {
85 // copy all the buffers into one
87 switch (buffers.length) {
88 case 0: buffer = new Buffer(0); break;
89 case 1: buffer = buffers[0]; break;
90 default: // concat together
91 buffer = new Buffer(nread);
93 buffers.forEach(function(b) {
95 b.copy(buffer, n, 0, l);
102 buffer = buffer.toString(encoding);
107 callback(null, buffer);
111 fs.readFileSync = function(path, encoding) {
112 var fd = fs.openSync(path, constants.O_RDONLY, 0666);
113 var buffer = new Buffer(4048);
120 buffer._bytesRead = lastRead;
122 buffers.push(buffer);
124 var buffer = new Buffer(4048);
125 lastRead = fs.readSync(fd, buffer, 0, buffer.length, null);
126 } while (lastRead > 0);
130 if (buffers.length > 1) {
133 buffer = new Buffer(nread);
134 buffers.forEach(function(i) {
135 if (!i._bytesRead) return;
136 i.copy(buffer, offset, 0, i._bytesRead);
137 offset += i._bytesRead;
139 } else if (buffers.length) {
140 // buffers has exactly 1 (possibly zero length) buffer, so this should
142 buffer = buffers[0].slice(0, buffers[0]._bytesRead);
144 buffer = new Buffer(0);
147 if (encoding) buffer = buffer.toString(encoding);
152 // Used by binding.open and friends
153 function stringToFlags(flag) {
154 // Only mess with strings
155 if (typeof flag !== 'string') {
160 return constants.O_RDONLY;
163 return constants.O_RDWR;
166 return constants.O_CREAT | constants.O_TRUNC | constants.O_WRONLY;
169 return constants.O_CREAT | constants.O_TRUNC | constants.O_RDWR;
172 return constants.O_APPEND | constants.O_CREAT | constants.O_WRONLY;
175 return constants.O_APPEND | constants.O_CREAT | constants.O_RDWR;
178 throw new Error('Unknown file open flag: ' + flag);
184 // Yes, the follow could be easily DRYed up but I provide the explicit
185 // list to make the arguments clear.
187 fs.close = function(fd, callback) {
188 binding.close(fd, callback || noop);
191 fs.closeSync = function(fd) {
192 return binding.close(fd);
195 function modeNum(m, def) {
197 case 'number': return m;
198 case 'string': return parseInt(m, 8);
208 fs.open = function(path, flags, mode, callback) {
209 callback = arguments[arguments.length - 1];
210 if (typeof(callback) !== 'function') {
214 mode = modeNum(mode, '0666');
216 binding.open(path, stringToFlags(flags), mode, callback);
219 fs.openSync = function(path, flags, mode) {
220 mode = modeNum(mode, '0666');
221 return binding.open(path, stringToFlags(flags), mode);
224 fs.read = function(fd, buffer, offset, length, position, callback) {
225 if (!Buffer.isBuffer(buffer)) {
226 // legacy string interface (fd, length, position, encoding, callback)
227 var cb = arguments[4],
228 encoding = arguments[3];
229 position = arguments[2];
230 length = arguments[1];
231 buffer = new Buffer(length);
234 callback = function(err, bytesRead) {
237 var str = (bytesRead > 0) ? buffer.toString(encoding, 0, bytesRead) : '';
239 (cb)(err, str, bytesRead);
243 binding.read(fd, buffer, offset, length, position, callback || noop);
246 fs.readSync = function(fd, buffer, offset, length, position) {
248 if (!Buffer.isBuffer(buffer)) {
249 // legacy string interface (fd, length, position, encoding, callback)
251 var encoding = arguments[3];
252 position = arguments[2];
253 length = arguments[1];
254 buffer = new Buffer(length);
259 var r = binding.read(fd, buffer, offset, length, position);
264 var str = (r > 0) ? buffer.toString(encoding, 0, r) : '';
268 fs.write = function(fd, buffer, offset, length, position, callback) {
269 if (!Buffer.isBuffer(buffer)) {
270 // legacy string interface (fd, data, position, encoding, callback)
271 callback = arguments[4];
272 position = arguments[2];
274 buffer = new Buffer('' + arguments[1], arguments[3]);
276 length = buffer.length;
280 if (typeof callback == 'function') {
281 process.nextTick(function() {
282 callback(undefined, 0);
288 binding.write(fd, buffer, offset, length, position, callback || noop);
291 fs.writeSync = function(fd, buffer, offset, length, position) {
292 if (!Buffer.isBuffer(buffer)) {
293 // legacy string interface (fd, data, position, encoding)
294 position = arguments[2];
296 buffer = new Buffer('' + arguments[1], arguments[3]);
298 length = buffer.length;
300 if (!length) return 0;
302 return binding.write(fd, buffer, offset, length, position);
305 fs.rename = function(oldPath, newPath, callback) {
306 binding.rename(oldPath, newPath, callback || noop);
309 fs.renameSync = function(oldPath, newPath) {
310 return binding.rename(oldPath, newPath);
313 fs.truncate = function(fd, len, callback) {
314 binding.truncate(fd, len, callback || noop);
317 fs.truncateSync = function(fd, len) {
318 return binding.truncate(fd, len);
321 fs.rmdir = function(path, callback) {
322 binding.rmdir(path, callback || noop);
325 fs.rmdirSync = function(path) {
326 return binding.rmdir(path);
329 fs.fdatasync = function(fd, callback) {
330 binding.fdatasync(fd, callback || noop);
333 fs.fdatasyncSync = function(fd) {
334 return binding.fdatasync(fd);
337 fs.fsync = function(fd, callback) {
338 binding.fsync(fd, callback || noop);
341 fs.fsyncSync = function(fd) {
342 return binding.fsync(fd);
345 fs.mkdir = function(path, mode, callback) {
346 binding.mkdir(path, modeNum(mode), callback || noop);
349 fs.mkdirSync = function(path, mode) {
350 return binding.mkdir(path, modeNum(mode));
353 fs.sendfile = function(outFd, inFd, inOffset, length, callback) {
354 binding.sendfile(outFd, inFd, inOffset, length, callback || noop);
357 fs.sendfileSync = function(outFd, inFd, inOffset, length) {
358 return binding.sendfile(outFd, inFd, inOffset, length);
361 fs.readdir = function(path, callback) {
362 binding.readdir(path, callback || noop);
365 fs.readdirSync = function(path) {
366 return binding.readdir(path);
369 fs.fstat = function(fd, callback) {
370 binding.fstat(fd, callback || noop);
373 fs.lstat = function(path, callback) {
374 binding.lstat(path, callback || noop);
377 fs.stat = function(path, callback) {
378 binding.stat(path, callback || noop);
381 fs.fstatSync = function(fd) {
382 return binding.fstat(fd);
385 fs.lstatSync = function(path) {
386 return binding.lstat(path);
389 fs.statSync = function(path) {
390 return binding.stat(path);
393 fs.readlink = function(path, callback) {
394 binding.readlink(path, callback || noop);
397 fs.readlinkSync = function(path) {
398 return binding.readlink(path);
401 fs.symlink = function(destination, path, callback) {
402 binding.symlink(destination, path, callback || noop);
405 fs.symlinkSync = function(destination, path) {
406 return binding.symlink(destination, path);
409 fs.link = function(srcpath, dstpath, callback) {
410 binding.link(srcpath, dstpath, callback || noop);
413 fs.linkSync = function(srcpath, dstpath) {
414 return binding.link(srcpath, dstpath);
417 fs.unlink = function(path, callback) {
418 binding.unlink(path, callback || noop);
421 fs.unlinkSync = function(path) {
422 return binding.unlink(path);
425 fs.chmod = function(path, mode, callback) {
426 binding.chmod(path, modeNum(mode), callback || noop);
429 fs.chmodSync = function(path, mode) {
430 return binding.chmod(path, modeNum(mode));
433 fs.chown = function(path, uid, gid, callback) {
434 binding.chown(path, uid, gid, callback || noop);
437 fs.chownSync = function(path, uid, gid) {
438 return binding.chown(path, uid, gid);
441 // converts Date or number to a fractional UNIX timestamp
442 function toUnixTimestamp(time) {
443 if (typeof time == 'number') {
446 if (time instanceof Date) {
447 // convert to 123.456 UNIX timestamp
448 return time.getTime() / 1000;
450 throw new Error("Cannot parse time: " + time);
453 // exported for unit tests, not for public consumption
454 fs._toUnixTimestamp = toUnixTimestamp;
456 fs.utimes = function(path, atime, mtime, callback) {
457 atime = toUnixTimestamp(atime);
458 mtime = toUnixTimestamp(mtime);
459 binding.utimes(path, atime, mtime, callback || noop);
462 fs.utimesSync = function(path, atime, mtime) {
463 atime = toUnixTimestamp(atime);
464 mtime = toUnixTimestamp(mtime);
465 binding.utimes(path, atime, mtime);
468 fs.futimes = function(fd, atime, mtime, callback) {
469 atime = toUnixTimestamp(atime);
470 mtime = toUnixTimestamp(mtime);
471 binding.futimes(fd, atime, mtime, callback || noop);
474 fs.futimesSync = function(fd, atime, mtime) {
475 atime = toUnixTimestamp(atime);
476 mtime = toUnixTimestamp(mtime);
477 binding.futimes(fd, atime, mtime);
480 function writeAll(fd, buffer, offset, length, callback) {
481 // write(fd, buffer, offset, length, position, callback)
482 fs.write(fd, buffer, offset, length, offset, function(writeErr, written) {
484 fs.close(fd, function() {
485 if (callback) callback(writeErr);
488 if (written === length) {
489 fs.close(fd, callback);
491 writeAll(fd, buffer, offset + written, length - written, callback);
497 fs.writeFile = function(path, data, encoding_, callback) {
498 var encoding = (typeof(encoding_) == 'string' ? encoding_ : 'utf8');
499 var callback_ = arguments[arguments.length - 1];
500 var callback = (typeof(callback_) == 'function' ? callback_ : null);
501 fs.open(path, 'w', 0666, function(openErr, fd) {
503 if (callback) callback(openErr);
505 var buffer = Buffer.isBuffer(data) ? data : new Buffer(data, encoding);
506 writeAll(fd, buffer, 0, buffer.length, callback);
511 fs.writeFileSync = function(path, data, encoding) {
512 var fd = fs.openSync(path, 'w');
513 if (!Buffer.isBuffer(data)) {
514 data = new Buffer(data, encoding || 'utf8');
517 var length = data.length;
518 //writeSync(fd, buffer, offset, length, position)
519 while (written < length) {
520 written += fs.writeSync(fd, data, written, length - written, written);
525 // Stat Change Watchers
527 var statWatchers = {};
529 fs.watchFile = function(filename) {
534 if ('object' == typeof arguments[1]) {
535 options = arguments[1];
536 listener = arguments[2];
539 listener = arguments[1];
542 if (options.persistent === undefined) options.persistent = true;
543 if (options.interval === undefined) options.interval = 0;
545 if (statWatchers[filename]) {
546 stat = statWatchers[filename];
548 statWatchers[filename] = new binding.StatWatcher();
549 stat = statWatchers[filename];
550 stat.start(filename, options.persistent, options.interval);
552 stat.addListener('change', listener);
556 fs.unwatchFile = function(filename) {
558 if (statWatchers[filename]) {
559 stat = statWatchers[filename];
561 statWatchers[filename] = undefined;
566 // Not using realpath(2) because it's bad.
567 // See: http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html
569 var path = require('path'),
570 normalize = path.normalize,
571 isWindows = process.platform === 'win32';
574 // Node doesn't support symlinks / lstat on windows. Hence realpatch is just
575 // the same as path.resolve that fails if the path doesn't exists.
578 fs.realpathSync = function realpathSync(p, cache) {
579 var p = path.resolve(p);
580 if (cache && Object.prototype.hasOwnProperty.call(cache, p)) {
584 if (cache) cache[p] = p;
589 fs.realpath = function(p, cache, cb) {
590 if (typeof cb !== 'function') {
594 var p = path.resolve(p);
595 if (cache && Object.prototype.hasOwnProperty.call(cache, p)) {
596 return cb(null, cache[p]);
598 fs.stat(p, function(err) {
600 if (cache) cache[p] = p;
608 // Regexp that finds the next partion of a (partial) path
609 // result is [base_with_slash, base], e.g. ['somedir/', 'somedir']
610 var nextPartRe = /(.*?)(?:[\/]+|$)/g;
613 fs.realpathSync = function realpathSync(p, cache) {
614 // make p is absolute
617 if (cache && Object.prototype.hasOwnProperty.call(cache, p)) {
625 // current character position in p
627 // the partial path so far, including a trailing slash if any
629 // the partial path without a trailing slash
631 // the partial path scanned in the previous round, with slash
634 // walk down the path, swapping out linked pathparts for their real
636 // NB: p.length changes.
637 while (pos < p.length) {
638 // find the next part
639 nextPartRe.lastIndex = pos;
640 var result = nextPartRe.exec(p);
642 current += result[0];
643 base = previous + result[1];
644 pos = nextPartRe.lastIndex;
646 // continue if not a symlink, or if root
647 if (!base || knownHard[base] || (cache && cache[base] === base)) {
652 if (cache && Object.prototype.hasOwnProperty.call(cache, base)) {
653 // some known symbolic link. no need to stat again.
654 resolvedLink = cache[base];
656 var stat = fs.lstatSync(base);
657 if (!stat.isSymbolicLink()) {
658 knownHard[base] = true;
659 if (cache) cache[base] = base;
663 // read the link if it wasn't read before
664 var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
665 if (!seenLinks[id]) {
667 seenLinks[id] = fs.readlinkSync(base);
668 resolvedLink = path.resolve(previous, seenLinks[id]);
669 // track this, if given a cache.
670 if (cache) cache[base] = resolvedLink;
674 // resolve the link, then start over
675 p = path.resolve(resolvedLink, p.slice(pos));
677 previous = base = current = '';
680 if (cache) cache[original] = p;
687 fs.realpath = function realpath(p, cache, cb) {
688 if (typeof cb !== 'function') {
693 // make p is absolute
696 if (cache && Object.prototype.hasOwnProperty.call(cache, p)) {
697 return cb(null, cache[p]);
704 // current character position in p
706 // the partial path so far, including a trailing slash if any
708 // the partial path without a trailing slash
710 // the partial path scanned in the previous round, with slash
713 // walk down the path, swapping out linked pathparts for their real
717 // stop if scanned past end of path
718 if (pos >= p.length) {
719 if (cache) cache[original] = p;
723 // find the next part
724 nextPartRe.lastIndex = pos;
725 var result = nextPartRe.exec(p);
727 current += result[0];
728 base = previous + result[1];
729 pos = nextPartRe.lastIndex;
731 // continue if known to be hard or if root or in cache already.
732 if (!base || knownHard[base] || (cache && cache[base] === base)) {
733 return process.nextTick(LOOP);
736 if (cache && Object.prototype.hasOwnProperty.call(cache, base)) {
737 // known symbolic link. no need to stat again.
738 return gotResolvedLink(cache[base]);
741 return fs.lstat(base, gotStat);
744 function gotStat(err, stat) {
745 if (err) return cb(err);
747 // if not a symlink, skip to the next path part
748 if (!stat.isSymbolicLink()) {
749 knownHard[base] = true;
750 if (cache) cache[base] = base;
751 return process.nextTick(LOOP);
754 // stat & read the link if not read before
755 // call gotTarget as soon as the link target is known
756 var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
758 return gotTarget(null, seenLinks[id], base);
760 fs.stat(base, function(err) {
761 if (err) return cb(err);
763 fs.readlink(base, function(err, target) {
764 gotTarget(err, seenLinks[id] = target);
769 function gotTarget(err, target, base) {
770 if (err) return cb(err);
772 var resolvedLink = path.resolve(previous, target);
773 if (cache) cache[base] = resolvedLink;
774 gotResolvedLink(resolvedLink);
777 function gotResolvedLink(resolvedLink) {
779 // resolve the link, then start over
780 p = path.resolve(resolvedLink, p.slice(pos));
782 previous = base = current = '';
784 return process.nextTick(LOOP);
793 function allocNewPool() {
794 pool = new Buffer(kPoolSize);
800 fs.createReadStream = function(path, options) {
801 return new ReadStream(path, options);
804 var ReadStream = fs.ReadStream = function(path, options) {
805 if (!(this instanceof ReadStream)) return new ReadStream(path, options);
813 this.readable = true;
817 this.mode = parseInt('0666', 8);
818 this.bufferSize = 64 * 1024;
820 options = options || {};
822 // Mixin options into this
823 var keys = Object.keys(options);
824 for (var index = 0, length = keys.length; index < length; index++) {
825 var key = keys[index];
826 this[key] = options[key];
829 if (this.encoding) this.setEncoding(this.encoding);
831 if (this.start !== undefined || this.end !== undefined) {
832 if (this.start === undefined || this.end === undefined) {
833 this.emit('error', new Error('Both start and end are needed ' +
834 'for range streaming.'));
835 } else if (this.start > this.end) {
836 this.emit('error', new Error('start must be <= end'));
838 this._firstRead = true;
842 if (this.fd !== null) {
846 fs.open(this.path, this.flags, this.mode, function(err, fd) {
848 self.emit('error', err);
849 self.readable = false;
854 self.emit('open', fd);
858 util.inherits(ReadStream, Stream);
860 fs.FileReadStream = fs.ReadStream; // support the legacy name
862 ReadStream.prototype.setEncoding = function(encoding) {
863 var StringDecoder = require('string_decoder').StringDecoder; // lazy load
864 this._decoder = new StringDecoder(encoding);
868 ReadStream.prototype._read = function() {
870 if (!self.readable || self.paused) return;
872 if (!pool || pool.length - pool.used < kMinPoolSpace) {
873 // discard the old pool. Can't add to the free list because
874 // users might have refernces to slices on it.
879 if (self.start !== undefined && self._firstRead) {
880 self.pos = self.start;
881 self._firstRead = false;
884 // Grab another reference to the pool in the case that while we're in the
885 // thread pool another read() finishes up the pool, and allocates a new
888 var toRead = Math.min(pool.length - pool.used, this.bufferSize);
889 var start = pool.used;
891 if (this.pos !== undefined) {
892 toRead = Math.min(this.end - this.pos + 1, toRead);
895 function afterRead(err, bytesRead) {
897 self.emit('error', err);
898 self.readable = false;
902 if (bytesRead === 0) {
908 var b = thisPool.slice(start, start + bytesRead);
910 // Possible optimizition here?
911 // Reclaim some bytes if bytesRead < toRead?
912 // Would need to ensure that pool === thisPool.
914 // do not emit events if the stream is paused
920 // do not emit events anymore after we declared the stream unreadable
921 if (!self.readable) return;
927 fs.read(self.fd, pool, pool.used, toRead, self.pos, afterRead);
929 if (self.pos !== undefined) {
936 ReadStream.prototype._emitData = function(d) {
938 var string = this._decoder.write(d);
939 if (string.length) this.emit('data', string);
941 this.emit('data', d);
946 ReadStream.prototype.destroy = function(cb) {
948 this.readable = false;
951 fs.close(self.fd, function(err) {
954 self.emit('error', err);
966 this.addListener('open', close);
971 ReadStream.prototype.pause = function() {
976 ReadStream.prototype.resume = function() {
980 this._emitData(this.buffer);
984 // hasn't opened yet.
985 if (null == this.fd) return;
992 fs.createWriteStream = function(path, options) {
993 return new WriteStream(path, options);
996 var WriteStream = fs.WriteStream = function(path, options) {
997 if (!(this instanceof WriteStream)) return new WriteStream(path, options);
1003 this.writable = true;
1006 this.encoding = 'binary';
1007 this.mode = parseInt('0666', 8);
1009 options = options || {};
1011 // Mixin options into this
1012 var keys = Object.keys(options);
1013 for (var index = 0, length = keys.length; index < length; index++) {
1014 var key = keys[index];
1015 this[key] = options[key];
1021 if (this.fd === null) {
1022 this._queue.push([fs.open, this.path, this.flags, this.mode, undefined]);
1026 util.inherits(WriteStream, Stream);
1028 fs.FileWriteStream = fs.WriteStream; // support the legacy name
1030 WriteStream.prototype.flush = function() {
1031 if (this.busy) return;
1034 var args = this._queue.shift();
1036 if (this.drainable) { self.emit('drain'); }
1042 var method = args.shift(),
1047 args.push(function(err) {
1051 self.writable = false;
1055 self.emit('error', err);
1059 // stop flushing after close
1060 if (method === fs.close) {
1068 // save reference for file pointer
1069 if (method === fs.open) {
1070 self.fd = arguments[1];
1071 self.emit('open', self.fd);
1074 cb(null, arguments[1]);
1080 // Inject the file pointer
1081 if (method !== fs.open) {
1082 args.unshift(self.fd);
1085 method.apply(this, args);
1088 WriteStream.prototype.write = function(data) {
1089 if (!this.writable) {
1090 this.emit("error", new Error('stream not writable'));
1094 this.drainable = true;
1097 if (typeof(arguments[arguments.length - 1]) == 'function') {
1098 cb = arguments[arguments.length - 1];
1101 if (Buffer.isBuffer(data)) {
1102 this._queue.push([fs.write, data, 0, data.length, null, cb]);
1104 var encoding = 'utf8';
1105 if (typeof(arguments[1]) == 'string') encoding = arguments[1];
1106 this._queue.push([fs.write, data, undefined, encoding, cb]);
1115 WriteStream.prototype.end = function(data, encoding, cb) {
1116 if (typeof(data) === 'function') {
1118 } else if (typeof(encoding) === 'function') {
1121 } else if (arguments.length > 0) {
1122 this.write(data, encoding);
1124 this.writable = false;
1125 this._queue.push([fs.close, cb]);
1129 WriteStream.prototype.destroy = function(cb) {
1131 this.writable = false;
1134 fs.close(self.fd, function(err) {
1136 if (cb) { cb(err); }
1137 self.emit('error', err);
1141 if (cb) { cb(null); }
1149 this.addListener('open', close);
1153 // There is no shutdown() for files.
1154 WriteStream.prototype.destroySoon = WriteStream.prototype.end;