98bf5c64213fafe4e5c642423176a4ef61024ff0
[platform/upstream/nodejs.git] / lib / fs.js
1 // Copyright Joyent, Inc. and other Node contributors.
2 //
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:
10 //
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
13 //
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.
21
22 // Maintainers, keep in mind that octal literals are not allowed
23 // in strict mode. Use the decimal value and add a comment with
24 // the octal value. Example:
25 //
26 //   var mode = 438; /* mode=0666 */
27
28 var util = require('util');
29 var pathModule = require('path');
30
31 var binding = process.binding('fs');
32 var constants = process.binding('constants');
33 var fs = exports;
34 var Stream = require('stream').Stream;
35 var EventEmitter = require('events').EventEmitter;
36
37 var kMinPoolSpace = 128;
38 var kPoolSize = 40 * 1024;
39
40 var O_APPEND = constants.O_APPEND || 0;
41 var O_CREAT = constants.O_CREAT || 0;
42 var O_DIRECTORY = constants.O_DIRECTORY || 0;
43 var O_EXCL = constants.O_EXCL || 0;
44 var O_NOCTTY = constants.O_NOCTTY || 0;
45 var O_NOFOLLOW = constants.O_NOFOLLOW || 0;
46 var O_RDONLY = constants.O_RDONLY || 0;
47 var O_RDWR = constants.O_RDWR || 0;
48 var O_SYMLINK = constants.O_SYMLINK || 0;
49 var O_SYNC = constants.O_SYNC || 0;
50 var O_TRUNC = constants.O_TRUNC || 0;
51 var O_WRONLY = constants.O_WRONLY || 0;
52
53 var isWindows = process.platform === 'win32';
54
55 function rethrow(err) {
56   if (err) throw err;
57 }
58
59 function maybeCallback(cb) {
60   return typeof cb === 'function' ? cb : rethrow;
61 }
62
63 // Ensure that callbacks run in the global context. Only use this function
64 // for callbacks that are passed to the binding layer, callbacks that are
65 // invoked from JS already run in the proper scope.
66 function makeCallback(cb) {
67   if (typeof cb !== 'function') {
68     return rethrow;
69   }
70
71   return function() {
72     return cb.apply(null, arguments);
73   };
74 }
75
76 function assertEncoding(encoding) {
77   if (encoding && !Buffer.isEncoding(encoding)) {
78     throw new Error('Unknown encoding: ' + encoding);
79   }
80 }
81
82
83 fs.Stats = binding.Stats;
84
85 fs.Stats.prototype._checkModeProperty = function(property) {
86   return ((this.mode & constants.S_IFMT) === property);
87 };
88
89 fs.Stats.prototype.isDirectory = function() {
90   return this._checkModeProperty(constants.S_IFDIR);
91 };
92
93 fs.Stats.prototype.isFile = function() {
94   return this._checkModeProperty(constants.S_IFREG);
95 };
96
97 fs.Stats.prototype.isBlockDevice = function() {
98   return this._checkModeProperty(constants.S_IFBLK);
99 };
100
101 fs.Stats.prototype.isCharacterDevice = function() {
102   return this._checkModeProperty(constants.S_IFCHR);
103 };
104
105 fs.Stats.prototype.isSymbolicLink = function() {
106   return this._checkModeProperty(constants.S_IFLNK);
107 };
108
109 fs.Stats.prototype.isFIFO = function() {
110   return this._checkModeProperty(constants.S_IFIFO);
111 };
112
113 fs.Stats.prototype.isSocket = function() {
114   return this._checkModeProperty(constants.S_IFSOCK);
115 };
116
117 fs.exists = function(path, callback) {
118   binding.stat(pathModule._makeLong(path), function(err, stats) {
119     if (callback) callback(err ? false : true);
120   });
121 };
122
123 fs.existsSync = function(path) {
124   try {
125     binding.stat(pathModule._makeLong(path));
126     return true;
127   } catch (e) {
128     return false;
129   }
130 };
131
132 fs.readFile = function(path, encoding_) {
133   var encoding = typeof(encoding_) === 'string' ? encoding_ : null;
134   var callback = maybeCallback(arguments[arguments.length - 1]);
135
136   assertEncoding(encoding);
137
138   // first, stat the file, so we know the size.
139   var size;
140   var buffer; // single buffer with file data
141   var buffers; // list for when size is unknown
142   var pos = 0;
143   var fd;
144
145   fs.open(path, constants.O_RDONLY, 438 /*=0666*/, function(er, fd_) {
146     if (er) return callback(er);
147     fd = fd_;
148
149     fs.fstat(fd, function(er, st) {
150       if (er) return callback(er);
151       size = st.size;
152       if (size === 0) {
153         // the kernel lies about many files.
154         // Go ahead and try to read some bytes.
155         buffers = [];
156         return read();
157       }
158
159       buffer = new Buffer(size);
160       read();
161     });
162   });
163
164   function read() {
165     if (size === 0) {
166       buffer = new Buffer(8192);
167       fs.read(fd, buffer, 0, 8192, -1, afterRead);
168     } else {
169       fs.read(fd, buffer, pos, size - pos, -1, afterRead);
170     }
171   }
172
173   function afterRead(er, bytesRead) {
174     if (er) {
175       return fs.close(fd, function(er2) {
176         return callback(er);
177       });
178     }
179
180     if (bytesRead === 0) {
181       return close();
182     }
183
184     pos += bytesRead;
185     if (size !== 0) {
186       if (pos === size) close();
187       else read();
188     } else {
189       // unknown size, just read until we don't get bytes.
190       buffers.push(buffer.slice(0, bytesRead));
191       read();
192     }
193   }
194
195   function close() {
196     fs.close(fd, function(er) {
197       if (size === 0) {
198         // collected the data into the buffers list.
199         buffer = Buffer.concat(buffers, pos);
200       } else if (pos < size) {
201         buffer = buffer.slice(0, pos);
202       }
203
204       if (encoding) buffer = buffer.toString(encoding);
205       return callback(er, buffer);
206     });
207   }
208 };
209
210 fs.readFileSync = function(path, encoding) {
211   assertEncoding(encoding);
212
213   var fd = fs.openSync(path, constants.O_RDONLY, 438 /*=0666*/);
214
215   var size;
216   var threw = true;
217   try {
218     size = fs.fstatSync(fd).size;
219     threw = false;
220   } finally {
221     if (threw) fs.closeSync(fd);
222   }
223
224   var pos = 0;
225   var buffer; // single buffer with file data
226   var buffers; // list for when size is unknown
227
228   if (size === 0) {
229     buffers = [];
230   } else {
231     buffer = new Buffer(size);
232   }
233
234   var done = false;
235   while (!done) {
236     var threw = true;
237     try {
238       if (size !== 0) {
239         var bytesRead = fs.readSync(fd, buffer, pos, size - pos);
240       } else {
241         // the kernel lies about many files.
242         // Go ahead and try to read some bytes.
243         buffer = new Buffer(8192);
244         var bytesRead = fs.readSync(fd, buffer, 0, 8192);
245         if (bytesRead) {
246           buffers.push(buffer.slice(0, bytesRead));
247         }
248       }
249       threw = false;
250     } finally {
251       if (threw) fs.closeSync(fd);
252     }
253
254     pos += bytesRead;
255     done = (bytesRead === 0) || (size !== 0 && pos >= size);
256   }
257
258   fs.closeSync(fd);
259
260   if (size === 0) {
261     // data was collected into the buffers list.
262     buffer = Buffer.concat(buffers, pos);
263   } else if (pos < size) {
264     buffer = buffer.slice(0, pos);
265   }
266
267   if (encoding) buffer = buffer.toString(encoding);
268   return buffer;
269 };
270
271
272 // Used by binding.open and friends
273 function stringToFlags(flag) {
274   // Only mess with strings
275   if (typeof flag !== 'string') {
276     return flag;
277   }
278
279   // O_EXCL is mandated by POSIX, Windows supports it too.
280   // Let's add a check anyway, just in case.
281   if (!O_EXCL && ~flag.indexOf('x')) {
282     throw errnoException('ENOSYS', 'fs.open(O_EXCL)');
283   }
284
285   switch (flag) {
286     case 'r' : return O_RDONLY;
287     case 'rs' : return O_RDONLY | O_SYNC;
288     case 'r+' : return O_RDWR;
289     case 'rs+' : return O_RDWR | O_SYNC;
290
291     case 'w' : return O_TRUNC | O_CREAT | O_WRONLY;
292     case 'wx' : // fall through
293     case 'xw' : return O_TRUNC | O_CREAT | O_WRONLY | O_EXCL;
294
295     case 'w+' : return O_TRUNC | O_CREAT | O_RDWR;
296     case 'wx+': // fall through
297     case 'xw+': return O_TRUNC | O_CREAT | O_RDWR | O_EXCL;
298
299     case 'a' : return O_APPEND | O_CREAT | O_WRONLY;
300     case 'ax' : // fall through
301     case 'xa' : return O_APPEND | O_CREAT | O_WRONLY | O_EXCL;
302
303     case 'a+' : return O_APPEND | O_CREAT | O_RDWR;
304     case 'ax+': // fall through
305     case 'xa+': return O_APPEND | O_CREAT | O_RDWR | O_EXCL;
306   }
307
308   throw new Error('Unknown file open flag: ' + flag);
309 }
310
311 // exported but hidden, only used by test/simple/test-fs-open-flags.js
312 Object.defineProperty(exports, '_stringToFlags', {
313   enumerable: false,
314   value: stringToFlags
315 });
316
317
318 // Yes, the follow could be easily DRYed up but I provide the explicit
319 // list to make the arguments clear.
320
321 fs.close = function(fd, callback) {
322   binding.close(fd, makeCallback(callback));
323 };
324
325 fs.closeSync = function(fd) {
326   return binding.close(fd);
327 };
328
329 function modeNum(m, def) {
330   switch (typeof m) {
331     case 'number': return m;
332     case 'string': return parseInt(m, 8);
333     default:
334       if (def) {
335         return modeNum(def);
336       } else {
337         return undefined;
338       }
339   }
340 }
341
342 fs.open = function(path, flags, mode, callback) {
343   callback = makeCallback(arguments[arguments.length - 1]);
344   mode = modeNum(mode, 438 /*=0666*/);
345
346   binding.open(pathModule._makeLong(path),
347                stringToFlags(flags),
348                mode,
349                callback);
350 };
351
352 fs.openSync = function(path, flags, mode) {
353   mode = modeNum(mode, 438 /*=0666*/);
354   return binding.open(pathModule._makeLong(path), stringToFlags(flags), mode);
355 };
356
357 fs.read = function(fd, buffer, offset, length, position, callback) {
358   if (!Buffer.isBuffer(buffer)) {
359     // legacy string interface (fd, length, position, encoding, callback)
360     var cb = arguments[4],
361         encoding = arguments[3];
362
363     assertEncoding(encoding);
364
365     position = arguments[2];
366     length = arguments[1];
367     buffer = new Buffer(length);
368     offset = 0;
369
370     callback = function(err, bytesRead) {
371       if (!cb) return;
372
373       var str = (bytesRead > 0) ? buffer.toString(encoding, 0, bytesRead) : '';
374
375       (cb)(err, str, bytesRead);
376     };
377   }
378
379   function wrapper(err, bytesRead) {
380     // Retain a reference to buffer so that it can't be GC'ed too soon.
381     callback && callback(err, bytesRead || 0, buffer);
382   }
383
384   binding.read(fd, buffer, offset, length, position, wrapper);
385 };
386
387 fs.readSync = function(fd, buffer, offset, length, position) {
388   var legacy = false;
389   if (!Buffer.isBuffer(buffer)) {
390     // legacy string interface (fd, length, position, encoding, callback)
391     legacy = true;
392     var encoding = arguments[3];
393
394     assertEncoding(encoding);
395
396     position = arguments[2];
397     length = arguments[1];
398     buffer = new Buffer(length);
399
400     offset = 0;
401   }
402
403   var r = binding.read(fd, buffer, offset, length, position);
404   if (!legacy) {
405     return r;
406   }
407
408   var str = (r > 0) ? buffer.toString(encoding, 0, r) : '';
409   return [str, r];
410 };
411
412 fs.write = function(fd, buffer, offset, length, position, callback) {
413   if (!Buffer.isBuffer(buffer)) {
414     // legacy string interface (fd, data, position, encoding, callback)
415     callback = arguments[4];
416     position = arguments[2];
417     assertEncoding(arguments[3]);
418
419     buffer = new Buffer('' + arguments[1], arguments[3]);
420     offset = 0;
421     length = buffer.length;
422   }
423
424   if (!length) {
425     if (typeof callback == 'function') {
426       process.nextTick(function() {
427         callback(undefined, 0);
428       });
429     }
430     return;
431   }
432
433   callback = maybeCallback(callback);
434
435   function wrapper(err, written) {
436     // Retain a reference to buffer so that it can't be GC'ed too soon.
437     callback(err, written || 0, buffer);
438   }
439
440   binding.write(fd, buffer, offset, length, position, wrapper);
441 };
442
443 fs.writeSync = function(fd, buffer, offset, length, position) {
444   if (!Buffer.isBuffer(buffer)) {
445     // legacy string interface (fd, data, position, encoding)
446     position = arguments[2];
447     assertEncoding(arguments[3]);
448
449     buffer = new Buffer('' + arguments[1], arguments[3]);
450     offset = 0;
451     length = buffer.length;
452   }
453   if (!length) return 0;
454
455   return binding.write(fd, buffer, offset, length, position);
456 };
457
458 fs.rename = function(oldPath, newPath, callback) {
459   binding.rename(pathModule._makeLong(oldPath),
460                  pathModule._makeLong(newPath),
461                  makeCallback(callback));
462 };
463
464 fs.renameSync = function(oldPath, newPath) {
465   return binding.rename(pathModule._makeLong(oldPath),
466                         pathModule._makeLong(newPath));
467 };
468
469 fs.truncate = function(path, len, callback) {
470   if (typeof path === 'number') {
471     // legacy
472     return fs.ftruncate(path, len, callback);
473   }
474   if (typeof len === 'function') {
475     callback = len;
476     len = 0;
477   } else if (typeof len === 'undefined') {
478     len = 0;
479   }
480   callback = maybeCallback(callback);
481   fs.open(path, 'w', function(er, fd) {
482     if (er) return callback(er);
483     binding.ftruncate(fd, len, function(er) {
484       fs.close(fd, function(er2) {
485         callback(er || er2);
486       });
487     });
488   });
489 };
490
491 fs.truncateSync = function(path, len) {
492   if (typeof path === 'number') {
493     // legacy
494     return fs.ftruncateSync(path, len);
495   }
496   if (typeof len === 'undefined') {
497     len = 0;
498   }
499   // allow error to be thrown, but still close fd.
500   var fd = fs.openSync(path, 'w');
501   try {
502     var ret = fs.ftruncateSync(fd, len);
503   } finally {
504     fs.closeSync(fd);
505   }
506   return ret;
507 };
508
509 fs.ftruncate = function(fd, len, callback) {
510   if (typeof len === 'function') {
511     callback = len;
512     len = 0;
513   } else if (typeof len === 'undefined') {
514     len = 0;
515   }
516   binding.ftruncate(fd, len, makeCallback(callback));
517 };
518
519 fs.ftruncateSync = function(fd, len) {
520   if (typeof len === 'undefined') {
521     len = 0;
522   }
523   return binding.ftruncate(fd, len);
524 };
525
526 fs.rmdir = function(path, callback) {
527   binding.rmdir(pathModule._makeLong(path), makeCallback(callback));
528 };
529
530 fs.rmdirSync = function(path) {
531   return binding.rmdir(pathModule._makeLong(path));
532 };
533
534 fs.fdatasync = function(fd, callback) {
535   binding.fdatasync(fd, makeCallback(callback));
536 };
537
538 fs.fdatasyncSync = function(fd) {
539   return binding.fdatasync(fd);
540 };
541
542 fs.fsync = function(fd, callback) {
543   binding.fsync(fd, makeCallback(callback));
544 };
545
546 fs.fsyncSync = function(fd) {
547   return binding.fsync(fd);
548 };
549
550 fs.mkdir = function(path, mode, callback) {
551   if (typeof mode === 'function') callback = mode;
552   binding.mkdir(pathModule._makeLong(path),
553                 modeNum(mode, 511 /*=0777*/),
554                 makeCallback(callback));
555 };
556
557 fs.mkdirSync = function(path, mode) {
558   return binding.mkdir(pathModule._makeLong(path),
559                        modeNum(mode, 511 /*=0777*/));
560 };
561
562 fs.sendfile = function(outFd, inFd, inOffset, length, callback) {
563   binding.sendfile(outFd, inFd, inOffset, length, makeCallback(callback));
564 };
565
566 fs.sendfileSync = function(outFd, inFd, inOffset, length) {
567   return binding.sendfile(outFd, inFd, inOffset, length);
568 };
569
570 fs.readdir = function(path, callback) {
571   binding.readdir(pathModule._makeLong(path), makeCallback(callback));
572 };
573
574 fs.readdirSync = function(path) {
575   return binding.readdir(pathModule._makeLong(path));
576 };
577
578 fs.fstat = function(fd, callback) {
579   binding.fstat(fd, makeCallback(callback));
580 };
581
582 fs.lstat = function(path, callback) {
583   binding.lstat(pathModule._makeLong(path), makeCallback(callback));
584 };
585
586 fs.stat = function(path, callback) {
587   binding.stat(pathModule._makeLong(path), makeCallback(callback));
588 };
589
590 fs.fstatSync = function(fd) {
591   return binding.fstat(fd);
592 };
593
594 fs.lstatSync = function(path) {
595   return binding.lstat(pathModule._makeLong(path));
596 };
597
598 fs.statSync = function(path) {
599   return binding.stat(pathModule._makeLong(path));
600 };
601
602 fs.readlink = function(path, callback) {
603   binding.readlink(pathModule._makeLong(path), makeCallback(callback));
604 };
605
606 fs.readlinkSync = function(path) {
607   return binding.readlink(pathModule._makeLong(path));
608 };
609
610 function preprocessSymlinkDestination(path, type) {
611   if (!isWindows) {
612     // No preprocessing is needed on Unix.
613     return path;
614   } else if (type === 'junction') {
615     // Junctions paths need to be absolute and \\?\-prefixed.
616     return pathModule._makeLong(path);
617   } else {
618     // Windows symlinks don't tolerate forward slashes.
619     return ('' + path).replace(/\//g, '\\');
620   }
621 }
622
623 fs.symlink = function(destination, path, type_, callback) {
624   var type = (typeof type_ === 'string' ? type_ : null);
625   var callback = makeCallback(arguments[arguments.length - 1]);
626
627   binding.symlink(preprocessSymlinkDestination(destination, type),
628                   pathModule._makeLong(path),
629                   type,
630                   callback);
631 };
632
633 fs.symlinkSync = function(destination, path, type) {
634   type = (typeof type === 'string' ? type : null);
635
636   return binding.symlink(preprocessSymlinkDestination(destination, type),
637                          pathModule._makeLong(path),
638                          type);
639 };
640
641 fs.link = function(srcpath, dstpath, callback) {
642   binding.link(pathModule._makeLong(srcpath),
643                pathModule._makeLong(dstpath),
644                makeCallback(callback));
645 };
646
647 fs.linkSync = function(srcpath, dstpath) {
648   return binding.link(pathModule._makeLong(srcpath),
649                       pathModule._makeLong(dstpath));
650 };
651
652 fs.unlink = function(path, callback) {
653   binding.unlink(pathModule._makeLong(path), makeCallback(callback));
654 };
655
656 fs.unlinkSync = function(path) {
657   return binding.unlink(pathModule._makeLong(path));
658 };
659
660 fs.fchmod = function(fd, mode, callback) {
661   binding.fchmod(fd, modeNum(mode), makeCallback(callback));
662 };
663
664 fs.fchmodSync = function(fd, mode) {
665   return binding.fchmod(fd, modeNum(mode));
666 };
667
668 if (constants.hasOwnProperty('O_SYMLINK')) {
669   fs.lchmod = function(path, mode, callback) {
670     callback = maybeCallback(callback);
671     fs.open(path, constants.O_WRONLY | constants.O_SYMLINK, function(err, fd) {
672       if (err) {
673         callback(err);
674         return;
675       }
676       // prefer to return the chmod error, if one occurs,
677       // but still try to close, and report closing errors if they occur.
678       fs.fchmod(fd, mode, function(err) {
679         fs.close(fd, function(err2) {
680           callback(err || err2);
681         });
682       });
683     });
684   };
685
686   fs.lchmodSync = function(path, mode) {
687     var fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK);
688
689     // prefer to return the chmod error, if one occurs,
690     // but still try to close, and report closing errors if they occur.
691     var err, err2;
692     try {
693       var ret = fs.fchmodSync(fd, mode);
694     } catch (er) {
695       err = er;
696     }
697     try {
698       fs.closeSync(fd);
699     } catch (er) {
700       err2 = er;
701     }
702     if (err || err2) throw (err || err2);
703     return ret;
704   };
705 }
706
707
708 fs.chmod = function(path, mode, callback) {
709   binding.chmod(pathModule._makeLong(path),
710                 modeNum(mode),
711                 makeCallback(callback));
712 };
713
714 fs.chmodSync = function(path, mode) {
715   return binding.chmod(pathModule._makeLong(path), modeNum(mode));
716 };
717
718 if (constants.hasOwnProperty('O_SYMLINK')) {
719   fs.lchown = function(path, uid, gid, callback) {
720     callback = maybeCallback(callback);
721     fs.open(path, constants.O_WRONLY | constants.O_SYMLINK, function(err, fd) {
722       if (err) {
723         callback(err);
724         return;
725       }
726       fs.fchown(fd, uid, gid, callback);
727     });
728   };
729
730   fs.lchownSync = function(path, uid, gid) {
731     var fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK);
732     return fs.fchownSync(fd, uid, gid);
733   };
734 }
735
736 fs.fchown = function(fd, uid, gid, callback) {
737   binding.fchown(fd, uid, gid, makeCallback(callback));
738 };
739
740 fs.fchownSync = function(fd, uid, gid) {
741   return binding.fchown(fd, uid, gid);
742 };
743
744 fs.chown = function(path, uid, gid, callback) {
745   binding.chown(pathModule._makeLong(path), uid, gid, makeCallback(callback));
746 };
747
748 fs.chownSync = function(path, uid, gid) {
749   return binding.chown(pathModule._makeLong(path), uid, gid);
750 };
751
752 // converts Date or number to a fractional UNIX timestamp
753 function toUnixTimestamp(time) {
754   if (typeof time == 'number') {
755     return time;
756   }
757   if (time instanceof Date) {
758     // convert to 123.456 UNIX timestamp
759     return time.getTime() / 1000;
760   }
761   throw new Error('Cannot parse time: ' + time);
762 }
763
764 // exported for unit tests, not for public consumption
765 fs._toUnixTimestamp = toUnixTimestamp;
766
767 fs.utimes = function(path, atime, mtime, callback) {
768   binding.utimes(pathModule._makeLong(path),
769                  toUnixTimestamp(atime),
770                  toUnixTimestamp(mtime),
771                  makeCallback(callback));
772 };
773
774 fs.utimesSync = function(path, atime, mtime) {
775   atime = toUnixTimestamp(atime);
776   mtime = toUnixTimestamp(mtime);
777   binding.utimes(pathModule._makeLong(path), atime, mtime);
778 };
779
780 fs.futimes = function(fd, atime, mtime, callback) {
781   atime = toUnixTimestamp(atime);
782   mtime = toUnixTimestamp(mtime);
783   binding.futimes(fd, atime, mtime, makeCallback(callback));
784 };
785
786 fs.futimesSync = function(fd, atime, mtime) {
787   atime = toUnixTimestamp(atime);
788   mtime = toUnixTimestamp(mtime);
789   binding.futimes(fd, atime, mtime);
790 };
791
792 function writeAll(fd, buffer, offset, length, position, callback) {
793   callback = maybeCallback(arguments[arguments.length - 1]);
794
795   // write(fd, buffer, offset, length, position, callback)
796   fs.write(fd, buffer, offset, length, position, function(writeErr, written) {
797     if (writeErr) {
798       fs.close(fd, function() {
799         if (callback) callback(writeErr);
800       });
801     } else {
802       if (written === length) {
803         fs.close(fd, callback);
804       } else {
805         offset += written;
806         length -= written;
807         position += written;
808         writeAll(fd, buffer, offset, length, position, callback);
809       }
810     }
811   });
812 }
813
814 fs.writeFile = function(path, data, encoding_, callback) {
815   var encoding = (typeof(encoding_) == 'string' ? encoding_ : 'utf8');
816   assertEncoding(encoding);
817
818   callback = maybeCallback(arguments[arguments.length - 1]);
819   fs.open(path, 'w', 438 /*=0666*/, function(openErr, fd) {
820     if (openErr) {
821       if (callback) callback(openErr);
822     } else {
823       var buffer = Buffer.isBuffer(data) ? data : new Buffer('' + data,
824           encoding);
825       writeAll(fd, buffer, 0, buffer.length, 0, callback);
826     }
827   });
828 };
829
830 fs.writeFileSync = function(path, data, encoding) {
831   assertEncoding(encoding);
832
833   var fd = fs.openSync(path, 'w');
834   if (!Buffer.isBuffer(data)) {
835     data = new Buffer('' + data, encoding || 'utf8');
836   }
837   var written = 0;
838   var length = data.length;
839   try {
840     while (written < length) {
841       written += fs.writeSync(fd, data, written, length - written, written);
842     }
843   } finally {
844     fs.closeSync(fd);
845   }
846 };
847
848 fs.appendFile = function(path, data, encoding_, callback) {
849   var encoding = (typeof(encoding_) == 'string' ? encoding_ : 'utf8');
850   assertEncoding(encoding);
851
852   callback = maybeCallback(arguments[arguments.length - 1]);
853
854   fs.open(path, 'a', 438 /*=0666*/, function(err, fd) {
855     if (err) return callback(err);
856     var buffer = Buffer.isBuffer(data) ? data : new Buffer('' + data, encoding);
857     writeAll(fd, buffer, 0, buffer.length, null, callback);
858   });
859 };
860
861 fs.appendFileSync = function(path, data, encoding) {
862   assertEncoding(encoding);
863
864   var fd = fs.openSync(path, 'a');
865   if (!Buffer.isBuffer(data)) {
866     data = new Buffer('' + data, encoding || 'utf8');
867   }
868   var written = 0;
869   var position = null;
870   var length = data.length;
871
872   try {
873     while (written < length) {
874       written += fs.writeSync(fd, data, written, length - written, position);
875       position += written; // XXX not safe with multiple concurrent writers?
876     }
877   } finally {
878     fs.closeSync(fd);
879   }
880 };
881
882 function errnoException(errorno, syscall) {
883   // TODO make this more compatible with ErrnoException from src/node.cc
884   // Once all of Node is using this function the ErrnoException from
885   // src/node.cc should be removed.
886   var e = new Error(syscall + ' ' + errorno);
887   e.errno = e.code = errorno;
888   e.syscall = syscall;
889   return e;
890 }
891
892
893 function FSWatcher() {
894   EventEmitter.call(this);
895
896   var self = this;
897   var FSEvent = process.binding('fs_event_wrap').FSEvent;
898   this._handle = new FSEvent();
899   this._handle.owner = this;
900
901   this._handle.onchange = function(status, event, filename) {
902     if (status) {
903       self._handle.close();
904       self.emit('error', errnoException(errno, 'watch'));
905     } else {
906       self.emit('change', event, filename);
907     }
908   };
909 }
910 util.inherits(FSWatcher, EventEmitter);
911
912 FSWatcher.prototype.start = function(filename, persistent) {
913   var r = this._handle.start(pathModule._makeLong(filename), persistent);
914
915   if (r) {
916     this._handle.close();
917     throw errnoException(errno, 'watch');
918   }
919 };
920
921 FSWatcher.prototype.close = function() {
922   this._handle.close();
923 };
924
925 fs.watch = function(filename) {
926   var watcher;
927   var options;
928   var listener;
929
930   if ('object' == typeof arguments[1]) {
931     options = arguments[1];
932     listener = arguments[2];
933   } else {
934     options = {};
935     listener = arguments[1];
936   }
937
938   if (options.persistent === undefined) options.persistent = true;
939
940   watcher = new FSWatcher();
941   watcher.start(filename, options.persistent);
942
943   if (listener) {
944     watcher.addListener('change', listener);
945   }
946
947   return watcher;
948 };
949
950
951 // Stat Change Watchers
952
953 function StatWatcher() {
954   EventEmitter.call(this);
955
956   var self = this;
957   this._handle = new binding.StatWatcher();
958
959   // uv_fs_poll is a little more powerful than ev_stat but we curb it for
960   // the sake of backwards compatibility
961   var oldStatus = -1;
962
963   this._handle.onchange = function(current, previous, newStatus) {
964     if (oldStatus === -1 &&
965         newStatus === -1 &&
966         current.nlink === previous.nlink) return;
967
968     oldStatus = newStatus;
969     self.emit('change', current, previous);
970   };
971
972   this._handle.onstop = function() {
973     self.emit('stop');
974   };
975 }
976 util.inherits(StatWatcher, EventEmitter);
977
978
979 StatWatcher.prototype.start = function(filename, persistent, interval) {
980   this._handle.start(pathModule._makeLong(filename), persistent, interval);
981 };
982
983
984 StatWatcher.prototype.stop = function() {
985   this._handle.stop();
986 };
987
988
989 var statWatchers = {};
990 function inStatWatchers(filename) {
991   return Object.prototype.hasOwnProperty.call(statWatchers, filename) &&
992       statWatchers[filename];
993 }
994
995
996 fs.watchFile = function(filename) {
997   var stat;
998   var listener;
999
1000   var options = {
1001     // Poll interval in milliseconds. 5007 is what libev used to use. It's
1002     // a little on the slow side but let's stick with it for now to keep
1003     // behavioral changes to a minimum.
1004     interval: 5007,
1005     persistent: true
1006   };
1007
1008   if ('object' == typeof arguments[1]) {
1009     options = util._extend(options, arguments[1]);
1010     listener = arguments[2];
1011   } else {
1012     listener = arguments[1];
1013   }
1014
1015   if (!listener) {
1016     throw new Error('watchFile requires a listener function');
1017   }
1018
1019   if (inStatWatchers(filename)) {
1020     stat = statWatchers[filename];
1021   } else {
1022     stat = statWatchers[filename] = new StatWatcher();
1023     stat.start(filename, options.persistent, options.interval);
1024   }
1025   stat.addListener('change', listener);
1026   return stat;
1027 };
1028
1029 fs.unwatchFile = function(filename, listener) {
1030   if (!inStatWatchers(filename)) return;
1031
1032   var stat = statWatchers[filename];
1033
1034   if (typeof listener === 'function') {
1035     stat.removeListener('change', listener);
1036   } else {
1037     stat.removeAllListeners('change');
1038   }
1039
1040   if (stat.listeners('change').length === 0) {
1041     stat.stop();
1042     statWatchers[filename] = undefined;
1043   }
1044 };
1045
1046 // Realpath
1047 // Not using realpath(2) because it's bad.
1048 // See: http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html
1049
1050 var normalize = pathModule.normalize;
1051
1052 // Regexp that finds the next partion of a (partial) path
1053 // result is [base_with_slash, base], e.g. ['somedir/', 'somedir']
1054 if (isWindows) {
1055   var nextPartRe = /(.*?)(?:[\/\\]+|$)/g;
1056 } else {
1057   var nextPartRe = /(.*?)(?:[\/]+|$)/g;
1058 }
1059
1060 // Regex to find the device root, including trailing slash. E.g. 'c:\\'.
1061 if (isWindows) {
1062   var splitRootRe = /^(?:[a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?[\\\/]*/;
1063 } else {
1064   var splitRootRe = /^[\/]*/;
1065 }
1066
1067 fs.realpathSync = function realpathSync(p, cache) {
1068   // make p is absolute
1069   p = pathModule.resolve(p);
1070
1071   if (cache && Object.prototype.hasOwnProperty.call(cache, p)) {
1072     return cache[p];
1073   }
1074
1075   var original = p,
1076       seenLinks = {},
1077       knownHard = {};
1078
1079   // current character position in p
1080   var pos;
1081   // the partial path so far, including a trailing slash if any
1082   var current;
1083   // the partial path without a trailing slash (except when pointing at a root)
1084   var base;
1085   // the partial path scanned in the previous round, with slash
1086   var previous;
1087
1088   start();
1089
1090   function start() {
1091     // Skip over roots
1092     var m = splitRootRe.exec(p);
1093     pos = m[0].length;
1094     current = m[0];
1095     base = m[0];
1096     previous = '';
1097
1098     // On windows, check that the root exists. On unix there is no need.
1099     if (isWindows && !knownHard[base]) {
1100       fs.lstatSync(base);
1101       knownHard[base] = true;
1102     }
1103   }
1104
1105   // walk down the path, swapping out linked pathparts for their real
1106   // values
1107   // NB: p.length changes.
1108   while (pos < p.length) {
1109     // find the next part
1110     nextPartRe.lastIndex = pos;
1111     var result = nextPartRe.exec(p);
1112     previous = current;
1113     current += result[0];
1114     base = previous + result[1];
1115     pos = nextPartRe.lastIndex;
1116
1117     // continue if not a symlink
1118     if (knownHard[base] || (cache && cache[base] === base)) {
1119       continue;
1120     }
1121
1122     var resolvedLink;
1123     if (cache && Object.prototype.hasOwnProperty.call(cache, base)) {
1124       // some known symbolic link.  no need to stat again.
1125       resolvedLink = cache[base];
1126     } else {
1127       var stat = fs.lstatSync(base);
1128       if (!stat.isSymbolicLink()) {
1129         knownHard[base] = true;
1130         if (cache) cache[base] = base;
1131         continue;
1132       }
1133
1134       // read the link if it wasn't read before
1135       // dev/ino always return 0 on windows, so skip the check.
1136       var linkTarget = null;
1137       if (!isWindows) {
1138         var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
1139         if (seenLinks.hasOwnProperty(id)) {
1140           linkTarget = seenLinks[id];
1141         }
1142       }
1143       if (linkTarget === null) {
1144         fs.statSync(base);
1145         linkTarget = fs.readlinkSync(base);
1146       }
1147       resolvedLink = pathModule.resolve(previous, linkTarget);
1148       // track this, if given a cache.
1149       if (cache) cache[base] = resolvedLink;
1150       if (!isWindows) seenLinks[id] = linkTarget;
1151     }
1152
1153     // resolve the link, then start over
1154     p = pathModule.resolve(resolvedLink, p.slice(pos));
1155     start();
1156   }
1157
1158   if (cache) cache[original] = p;
1159
1160   return p;
1161 };
1162
1163
1164 fs.realpath = function realpath(p, cache, cb) {
1165   if (typeof cb !== 'function') {
1166     cb = maybeCallback(cache);
1167     cache = null;
1168   }
1169
1170   // make p is absolute
1171   p = pathModule.resolve(p);
1172
1173   if (cache && Object.prototype.hasOwnProperty.call(cache, p)) {
1174     return process.nextTick(cb.bind(null, null, cache[p]));
1175   }
1176
1177   var original = p,
1178       seenLinks = {},
1179       knownHard = {};
1180
1181   // current character position in p
1182   var pos;
1183   // the partial path so far, including a trailing slash if any
1184   var current;
1185   // the partial path without a trailing slash (except when pointing at a root)
1186   var base;
1187   // the partial path scanned in the previous round, with slash
1188   var previous;
1189
1190   start();
1191
1192   function start() {
1193     // Skip over roots
1194     var m = splitRootRe.exec(p);
1195     pos = m[0].length;
1196     current = m[0];
1197     base = m[0];
1198     previous = '';
1199
1200     // On windows, check that the root exists. On unix there is no need.
1201     if (isWindows && !knownHard[base]) {
1202       fs.lstat(base, function(err) {
1203         if (err) return cb(err);
1204         knownHard[base] = true;
1205         LOOP();
1206       });
1207     } else {
1208       process.nextTick(LOOP);
1209     }
1210   }
1211
1212   // walk down the path, swapping out linked pathparts for their real
1213   // values
1214   function LOOP() {
1215     // stop if scanned past end of path
1216     if (pos >= p.length) {
1217       if (cache) cache[original] = p;
1218       return cb(null, p);
1219     }
1220
1221     // find the next part
1222     nextPartRe.lastIndex = pos;
1223     var result = nextPartRe.exec(p);
1224     previous = current;
1225     current += result[0];
1226     base = previous + result[1];
1227     pos = nextPartRe.lastIndex;
1228
1229     // continue if not a symlink
1230     if (knownHard[base] || (cache && cache[base] === base)) {
1231       return process.nextTick(LOOP);
1232     }
1233
1234     if (cache && Object.prototype.hasOwnProperty.call(cache, base)) {
1235       // known symbolic link.  no need to stat again.
1236       return gotResolvedLink(cache[base]);
1237     }
1238
1239     return fs.lstat(base, gotStat);
1240   }
1241
1242   function gotStat(err, stat) {
1243     if (err) return cb(err);
1244
1245     // if not a symlink, skip to the next path part
1246     if (!stat.isSymbolicLink()) {
1247       knownHard[base] = true;
1248       if (cache) cache[base] = base;
1249       return process.nextTick(LOOP);
1250     }
1251
1252     // stat & read the link if not read before
1253     // call gotTarget as soon as the link target is known
1254     // dev/ino always return 0 on windows, so skip the check.
1255     if (!isWindows) {
1256       var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
1257       if (seenLinks.hasOwnProperty(id)) {
1258         return gotTarget(null, seenLinks[id], base);
1259       }
1260     }
1261     fs.stat(base, function(err) {
1262       if (err) return cb(err);
1263
1264       fs.readlink(base, function(err, target) {
1265         if (!isWindows) seenLinks[id] = target;
1266         gotTarget(err, target);
1267       });
1268     });
1269   }
1270
1271   function gotTarget(err, target, base) {
1272     if (err) return cb(err);
1273
1274     var resolvedLink = pathModule.resolve(previous, target);
1275     if (cache) cache[base] = resolvedLink;
1276     gotResolvedLink(resolvedLink);
1277   }
1278
1279   function gotResolvedLink(resolvedLink) {
1280     // resolve the link, then start over
1281     p = pathModule.resolve(resolvedLink, p.slice(pos));
1282     start();
1283   }
1284 };
1285
1286
1287
1288 var pool;
1289
1290 function allocNewPool() {
1291   pool = new Buffer(kPoolSize);
1292   pool.used = 0;
1293 }
1294
1295
1296
1297 fs.createReadStream = function(path, options) {
1298   return new ReadStream(path, options);
1299 };
1300
1301 var ReadStream = fs.ReadStream = function(path, options) {
1302   if (!(this instanceof ReadStream)) return new ReadStream(path, options);
1303
1304   Stream.call(this);
1305
1306   var self = this;
1307
1308   this.path = path;
1309   this.fd = null;
1310   this.readable = true;
1311   this.paused = false;
1312
1313   this.flags = 'r';
1314   this.mode = 438; /*=0666*/
1315   this.bufferSize = 64 * 1024;
1316
1317   options = options || {};
1318
1319   // Mixin options into this
1320   var keys = Object.keys(options);
1321   for (var index = 0, length = keys.length; index < length; index++) {
1322     var key = keys[index];
1323     this[key] = options[key];
1324   }
1325
1326   assertEncoding(this.encoding);
1327
1328   if (this.encoding) this.setEncoding(this.encoding);
1329
1330   if (this.start !== undefined) {
1331     if ('number' !== typeof this.start) {
1332       throw TypeError('start must be a Number');
1333     }
1334     if (this.end === undefined) {
1335       this.end = Infinity;
1336     } else if ('number' !== typeof this.end) {
1337       throw TypeError('end must be a Number');
1338     }
1339
1340     if (this.start > this.end) {
1341       throw new Error('start must be <= end');
1342     }
1343
1344     this.pos = this.start;
1345   }
1346
1347   if (this.fd !== null) {
1348     process.nextTick(function() {
1349       self._read();
1350     });
1351     return;
1352   }
1353
1354   fs.open(this.path, this.flags, this.mode, function(err, fd) {
1355     if (err) {
1356       self.emit('error', err);
1357       self.readable = false;
1358       return;
1359     }
1360
1361     self.fd = fd;
1362     self.emit('open', fd);
1363     self._read();
1364   });
1365 };
1366 util.inherits(ReadStream, Stream);
1367
1368 fs.FileReadStream = fs.ReadStream; // support the legacy name
1369
1370 ReadStream.prototype.setEncoding = function(encoding) {
1371   assertEncoding(encoding);
1372   var StringDecoder = require('string_decoder').StringDecoder; // lazy load
1373   this._decoder = new StringDecoder(encoding);
1374 };
1375
1376
1377 ReadStream.prototype._read = function() {
1378   var self = this;
1379   if (!this.readable || this.paused || this.reading) return;
1380
1381   this.reading = true;
1382
1383   if (!pool || pool.length - pool.used < kMinPoolSpace) {
1384     // discard the old pool. Can't add to the free list because
1385     // users might have refernces to slices on it.
1386     pool = null;
1387     allocNewPool();
1388   }
1389
1390   // Grab another reference to the pool in the case that while we're in the
1391   // thread pool another read() finishes up the pool, and allocates a new
1392   // one.
1393   var thisPool = pool;
1394   var toRead = Math.min(pool.length - pool.used, ~~this.bufferSize);
1395   var start = pool.used;
1396
1397   if (this.pos !== undefined) {
1398     toRead = Math.min(this.end - this.pos + 1, toRead);
1399   }
1400
1401   function afterRead(err, bytesRead) {
1402     self.reading = false;
1403     if (err) {
1404       self.emit('error', err);
1405       self.readable = false;
1406       return;
1407     }
1408
1409     if (bytesRead === 0) {
1410       if (this._decoder) {
1411         var ret = this._decoder.end();
1412         if (ret)
1413           this.emit('data', ret);
1414       }
1415       self.emit('end');
1416       self.destroy();
1417       return;
1418     }
1419
1420     var b = thisPool.slice(start, start + bytesRead);
1421
1422     // Possible optimizition here?
1423     // Reclaim some bytes if bytesRead < toRead?
1424     // Would need to ensure that pool === thisPool.
1425
1426     // do not emit events if the stream is paused
1427     if (self.paused) {
1428       self.buffer = b;
1429       return;
1430     }
1431
1432     // do not emit events anymore after we declared the stream unreadable
1433     if (!self.readable) return;
1434
1435     self._emitData(b);
1436     self._read();
1437   }
1438
1439   fs.read(this.fd, pool, pool.used, toRead, this.pos, afterRead);
1440
1441   if (this.pos !== undefined) {
1442     this.pos += toRead;
1443   }
1444   pool.used += toRead;
1445 };
1446
1447
1448 ReadStream.prototype._emitData = function(d) {
1449   if (this._decoder) {
1450     var string = this._decoder.write(d);
1451     if (string.length) this.emit('data', string);
1452   } else {
1453     this.emit('data', d);
1454   }
1455 };
1456
1457
1458 ReadStream.prototype.destroy = function() {
1459   var self = this;
1460
1461   if (!this.readable) return;
1462   this.readable = false;
1463
1464   function close() {
1465     fs.close(self.fd, function(err) {
1466       if (err) {
1467         self.emit('error', err);
1468       } else {
1469         self.emit('close');
1470       }
1471     });
1472   }
1473
1474   if (this.fd === null) {
1475     this.addListener('open', close);
1476   } else {
1477     close();
1478   }
1479 };
1480
1481
1482 ReadStream.prototype.pause = function() {
1483   this.paused = true;
1484 };
1485
1486
1487 ReadStream.prototype.resume = function() {
1488   this.paused = false;
1489
1490   if (this.buffer) {
1491     var buffer = this.buffer;
1492     this.buffer = null;
1493     this._emitData(buffer);
1494   }
1495
1496   // hasn't opened yet.
1497   if (null == this.fd) return;
1498
1499   this._read();
1500 };
1501
1502
1503
1504 fs.createWriteStream = function(path, options) {
1505   return new WriteStream(path, options);
1506 };
1507
1508 var WriteStream = fs.WriteStream = function(path, options) {
1509   if (!(this instanceof WriteStream)) return new WriteStream(path, options);
1510
1511   Stream.call(this);
1512
1513   this.path = path;
1514   this.fd = null;
1515   this.writable = true;
1516
1517   this.flags = 'w';
1518   this.encoding = 'binary';
1519   this.mode = 438; /*=0666*/
1520   this.bytesWritten = 0;
1521
1522   options = options || {};
1523
1524   // Mixin options into this
1525   var keys = Object.keys(options);
1526   for (var index = 0, length = keys.length; index < length; index++) {
1527     var key = keys[index];
1528     this[key] = options[key];
1529   }
1530
1531   if (this.start !== undefined) {
1532     if ('number' !== typeof this.start) {
1533       throw TypeError('start must be a Number');
1534     }
1535     if (this.start < 0) {
1536       throw new Error('start must be >= zero');
1537     }
1538
1539     this.pos = this.start;
1540   }
1541
1542   this.busy = false;
1543   this._queue = [];
1544
1545   if (this.fd === null) {
1546     this._open = fs.open;
1547     this._queue.push([this._open, this.path, this.flags, this.mode, undefined]);
1548     this.flush();
1549   }
1550 };
1551 util.inherits(WriteStream, Stream);
1552
1553 fs.FileWriteStream = fs.WriteStream; // support the legacy name
1554
1555 WriteStream.prototype.flush = function() {
1556   if (this.busy) return;
1557   var self = this;
1558
1559   var args = this._queue.shift();
1560   if (!args) {
1561     if (this.drainable) { this.emit('drain'); }
1562     return;
1563   }
1564
1565   this.busy = true;
1566
1567   var method = args.shift(),
1568       cb = args.pop();
1569
1570   args.push(function(err) {
1571     self.busy = false;
1572
1573     if (err) {
1574       self.writable = false;
1575       if (cb) {
1576         cb(err);
1577       }
1578       self.emit('error', err);
1579       return;
1580     }
1581
1582     if (method == fs.write) {
1583       self.bytesWritten += arguments[1];
1584       if (cb) {
1585         // write callback
1586         cb(null, arguments[1]);
1587       }
1588
1589     } else if (method === self._open) {
1590       // save reference for file pointer
1591       self.fd = arguments[1];
1592       self.emit('open', self.fd);
1593
1594     } else if (method === fs.close) {
1595       // stop flushing after close
1596       if (cb) {
1597         cb(null);
1598       }
1599       self.emit('close');
1600       return;
1601     }
1602
1603     self.flush();
1604   });
1605
1606   // Inject the file pointer
1607   if (method !== self._open) {
1608     args.unshift(this.fd);
1609   }
1610
1611   method.apply(this, args);
1612 };
1613
1614 WriteStream.prototype.write = function(data) {
1615   if (!this.writable) {
1616     this.emit('error', new Error('stream not writable'));
1617     return false;
1618   }
1619
1620   this.drainable = true;
1621
1622   var cb;
1623   if (typeof(arguments[arguments.length - 1]) == 'function') {
1624     cb = arguments[arguments.length - 1];
1625   }
1626
1627   if (!Buffer.isBuffer(data)) {
1628     var encoding = 'utf8';
1629     if (typeof(arguments[1]) == 'string') encoding = arguments[1];
1630     assertEncoding(encoding);
1631     data = new Buffer('' + data, encoding);
1632   }
1633
1634   this._queue.push([fs.write, data, 0, data.length, this.pos, cb]);
1635
1636   if (this.pos !== undefined) {
1637     this.pos += data.length;
1638   }
1639
1640   this.flush();
1641
1642   return false;
1643 };
1644
1645 WriteStream.prototype.end = function(data, encoding, cb) {
1646   if (typeof(data) === 'function') {
1647     cb = data;
1648   } else if (typeof(encoding) === 'function') {
1649     cb = encoding;
1650     this.write(data);
1651   } else if (arguments.length > 0) {
1652     this.write(data, encoding);
1653   }
1654   this.writable = false;
1655   this._queue.push([fs.close, cb]);
1656   this.flush();
1657 };
1658
1659 WriteStream.prototype.destroy = function() {
1660   var self = this;
1661
1662   if (!this.writable) return;
1663   this.writable = false;
1664
1665   function close() {
1666     fs.close(self.fd, function(err) {
1667       if (err) {
1668         self.emit('error', err);
1669       } else {
1670         self.emit('close');
1671       }
1672     });
1673   }
1674
1675   if (this.fd === null) {
1676     this.addListener('open', close);
1677   } else {
1678     close();
1679   }
1680 };
1681
1682 // There is no shutdown() for files.
1683 WriteStream.prototype.destroySoon = WriteStream.prototype.end;
1684
1685
1686 // SyncWriteStream is internal. DO NOT USE.
1687 // Temporary hack for process.stdout and process.stderr when piped to files.
1688 function SyncWriteStream(fd) {
1689   Stream.call(this);
1690
1691   this.fd = fd;
1692   this.writable = true;
1693   this.readable = false;
1694 }
1695
1696 util.inherits(SyncWriteStream, Stream);
1697
1698
1699 // Export
1700 fs.SyncWriteStream = SyncWriteStream;
1701
1702
1703 SyncWriteStream.prototype.write = function(data, arg1, arg2) {
1704   var encoding, cb;
1705
1706   // parse arguments
1707   if (arg1) {
1708     if (typeof arg1 === 'string') {
1709       encoding = arg1;
1710       cb = arg2;
1711     } else if (typeof arg1 === 'function') {
1712       cb = arg1;
1713     } else {
1714       throw new Error('bad arg');
1715     }
1716   }
1717   assertEncoding(encoding);
1718
1719   // Change strings to buffers. SLOW
1720   if (typeof data == 'string') {
1721     data = new Buffer(data, encoding);
1722   }
1723
1724   fs.writeSync(this.fd, data, 0, data.length);
1725
1726   if (cb) {
1727     process.nextTick(cb);
1728   }
1729
1730   return true;
1731 };
1732
1733
1734 SyncWriteStream.prototype.end = function(data, arg1, arg2) {
1735   if (data) {
1736     this.write(data, arg1, arg2);
1737   }
1738   this.destroy();
1739 };
1740
1741
1742 SyncWriteStream.prototype.destroy = function() {
1743   fs.closeSync(this.fd);
1744   this.fd = null;
1745   this.emit('close');
1746   return true;
1747 };
1748
1749 SyncWriteStream.prototype.destroySoon = SyncWriteStream.prototype.destroy;