Merge branch 'v0.4'
[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 var util = require('util');
23
24 var binding = process.binding('fs');
25 var constants = process.binding('constants');
26 var fs = exports;
27 var Stream = require('stream').Stream;
28
29 var kMinPoolSpace = 128;
30 var kPoolSize = 40 * 1024;
31
32 fs.Stats = binding.Stats;
33
34 fs.Stats.prototype._checkModeProperty = function(property) {
35   return ((this.mode & constants.S_IFMT) === property);
36 };
37
38 fs.Stats.prototype.isDirectory = function() {
39   return this._checkModeProperty(constants.S_IFDIR);
40 };
41
42 fs.Stats.prototype.isFile = function() {
43   return this._checkModeProperty(constants.S_IFREG);
44 };
45
46 fs.Stats.prototype.isBlockDevice = function() {
47   return this._checkModeProperty(constants.S_IFBLK);
48 };
49
50 fs.Stats.prototype.isCharacterDevice = function() {
51   return this._checkModeProperty(constants.S_IFCHR);
52 };
53
54 fs.Stats.prototype.isSymbolicLink = function() {
55   return this._checkModeProperty(constants.S_IFLNK);
56 };
57
58 fs.Stats.prototype.isFIFO = function() {
59   return this._checkModeProperty(constants.S_IFIFO);
60 };
61
62 fs.Stats.prototype.isSocket = function() {
63   return this._checkModeProperty(constants.S_IFSOCK);
64 };
65
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);
71   var buffers = [];
72   var nread = 0;
73
74   readStream.on('data', function(chunk) {
75     buffers.push(chunk);
76     nread += chunk.length;
77   });
78
79   readStream.on('error', function(er) {
80     callback(er);
81     readStream.destroy();
82   });
83
84   readStream.on('end', function() {
85     // copy all the buffers into one
86     var buffer;
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);
92         var n = 0;
93         buffers.forEach(function(b) {
94           var l = b.length;
95           b.copy(buffer, n, 0, l);
96           n += l;
97         });
98         break;
99     }
100     if (encoding) {
101       try {
102         buffer = buffer.toString(encoding);
103       } catch (er) {
104         return callback(er);
105       }
106     }
107     callback(null, buffer);
108   });
109 };
110
111 fs.readFileSync = function(path, encoding) {
112   var fd = fs.openSync(path, constants.O_RDONLY, 0666);
113   var buffer = new Buffer(4048);
114   var buffers = [];
115   var nread = 0;
116   var lastRead = 0;
117
118   do {
119     if (lastRead) {
120       buffer._bytesRead = lastRead;
121       nread += lastRead;
122       buffers.push(buffer);
123     }
124     var buffer = new Buffer(4048);
125     lastRead = fs.readSync(fd, buffer, 0, buffer.length, null);
126   } while (lastRead > 0);
127
128   fs.closeSync(fd);
129
130   if (buffers.length > 1) {
131     var offset = 0;
132     var i;
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;
138     });
139   } else if (buffers.length) {
140     // buffers has exactly 1 (possibly zero length) buffer, so this should
141     // be a shortcut
142     buffer = buffers[0].slice(0, buffers[0]._bytesRead);
143   } else {
144     buffer = new Buffer(0);
145   }
146
147   if (encoding) buffer = buffer.toString(encoding);
148   return buffer;
149 };
150
151
152 // Used by binding.open and friends
153 function stringToFlags(flag) {
154   // Only mess with strings
155   if (typeof flag !== 'string') {
156     return flag;
157   }
158   switch (flag) {
159     case 'r':
160       return constants.O_RDONLY;
161
162     case 'r+':
163       return constants.O_RDWR;
164
165     case 'w':
166       return constants.O_CREAT | constants.O_TRUNC | constants.O_WRONLY;
167
168     case 'w+':
169       return constants.O_CREAT | constants.O_TRUNC | constants.O_RDWR;
170
171     case 'a':
172       return constants.O_APPEND | constants.O_CREAT | constants.O_WRONLY;
173
174     case 'a+':
175       return constants.O_APPEND | constants.O_CREAT | constants.O_RDWR;
176
177     default:
178       throw new Error('Unknown file open flag: ' + flag);
179   }
180 }
181
182 function noop() {}
183
184 // Yes, the follow could be easily DRYed up but I provide the explicit
185 // list to make the arguments clear.
186
187 fs.close = function(fd, callback) {
188   binding.close(fd, callback || noop);
189 };
190
191 fs.closeSync = function(fd) {
192   return binding.close(fd);
193 };
194
195 function modeNum(m, def) {
196   switch(typeof m) {
197     case 'number': return m;
198     case 'string': return parseInt(m, 8);
199     default:
200       if (def) {
201         return modeNum(def);
202       } else {
203         return undefined;
204       }
205   }
206 }
207
208 fs.open = function(path, flags, mode, callback) {
209   callback = arguments[arguments.length - 1];
210   if (typeof(callback) !== 'function') {
211     callback = noop;
212   }
213
214   mode = modeNum(mode, '0666');
215
216   binding.open(path, stringToFlags(flags), mode, callback);
217 };
218
219 fs.openSync = function(path, flags, mode) {
220   mode = modeNum(mode, '0666');
221   return binding.open(path, stringToFlags(flags), mode);
222 };
223
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);
232     offset = 0;
233
234     callback = function(err, bytesRead) {
235       if (!cb) return;
236
237       var str = (bytesRead > 0) ? buffer.toString(encoding, 0, bytesRead) : '';
238
239       (cb)(err, str, bytesRead);
240     };
241   }
242
243   binding.read(fd, buffer, offset, length, position, callback || noop);
244 };
245
246 fs.readSync = function(fd, buffer, offset, length, position) {
247   var legacy = false;
248   if (!Buffer.isBuffer(buffer)) {
249     // legacy string interface (fd, length, position, encoding, callback)
250     legacy = true;
251     var encoding = arguments[3];
252     position = arguments[2];
253     length = arguments[1];
254     buffer = new Buffer(length);
255
256     offset = 0;
257   }
258
259   var r = binding.read(fd, buffer, offset, length, position);
260   if (!legacy) {
261     return r;
262   }
263
264   var str = (r > 0) ? buffer.toString(encoding, 0, r) : '';
265   return [str, r];
266 };
267
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];
273
274     buffer = new Buffer('' + arguments[1], arguments[3]);
275     offset = 0;
276     length = buffer.length;
277   }
278
279   if (!length) {
280     if (typeof callback == 'function') {
281       process.nextTick(function() {
282         callback(undefined, 0);
283       });
284     }
285     return;
286   }
287
288   binding.write(fd, buffer, offset, length, position, callback || noop);
289 };
290
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];
295
296     buffer = new Buffer('' + arguments[1], arguments[3]);
297     offset = 0;
298     length = buffer.length;
299   }
300   if (!length) return 0;
301
302   return binding.write(fd, buffer, offset, length, position);
303 };
304
305 fs.rename = function(oldPath, newPath, callback) {
306   binding.rename(oldPath, newPath, callback || noop);
307 };
308
309 fs.renameSync = function(oldPath, newPath) {
310   return binding.rename(oldPath, newPath);
311 };
312
313 fs.truncate = function(fd, len, callback) {
314   binding.truncate(fd, len, callback || noop);
315 };
316
317 fs.truncateSync = function(fd, len) {
318   return binding.truncate(fd, len);
319 };
320
321 fs.rmdir = function(path, callback) {
322   binding.rmdir(path, callback || noop);
323 };
324
325 fs.rmdirSync = function(path) {
326   return binding.rmdir(path);
327 };
328
329 fs.fdatasync = function(fd, callback) {
330   binding.fdatasync(fd, callback || noop);
331 };
332
333 fs.fdatasyncSync = function(fd) {
334   return binding.fdatasync(fd);
335 };
336
337 fs.fsync = function(fd, callback) {
338   binding.fsync(fd, callback || noop);
339 };
340
341 fs.fsyncSync = function(fd) {
342   return binding.fsync(fd);
343 };
344
345 fs.mkdir = function(path, mode, callback) {
346   binding.mkdir(path, modeNum(mode), callback || noop);
347 };
348
349 fs.mkdirSync = function(path, mode) {
350   return binding.mkdir(path, modeNum(mode));
351 };
352
353 fs.sendfile = function(outFd, inFd, inOffset, length, callback) {
354   binding.sendfile(outFd, inFd, inOffset, length, callback || noop);
355 };
356
357 fs.sendfileSync = function(outFd, inFd, inOffset, length) {
358   return binding.sendfile(outFd, inFd, inOffset, length);
359 };
360
361 fs.readdir = function(path, callback) {
362   binding.readdir(path, callback || noop);
363 };
364
365 fs.readdirSync = function(path) {
366   return binding.readdir(path);
367 };
368
369 fs.fstat = function(fd, callback) {
370   binding.fstat(fd, callback || noop);
371 };
372
373 fs.lstat = function(path, callback) {
374   binding.lstat(path, callback || noop);
375 };
376
377 fs.stat = function(path, callback) {
378   binding.stat(path, callback || noop);
379 };
380
381 fs.fstatSync = function(fd) {
382   return binding.fstat(fd);
383 };
384
385 fs.lstatSync = function(path) {
386   return binding.lstat(path);
387 };
388
389 fs.statSync = function(path) {
390   return binding.stat(path);
391 };
392
393 fs.readlink = function(path, callback) {
394   binding.readlink(path, callback || noop);
395 };
396
397 fs.readlinkSync = function(path) {
398   return binding.readlink(path);
399 };
400
401 fs.symlink = function(destination, path, callback) {
402   binding.symlink(destination, path, callback || noop);
403 };
404
405 fs.symlinkSync = function(destination, path) {
406   return binding.symlink(destination, path);
407 };
408
409 fs.link = function(srcpath, dstpath, callback) {
410   binding.link(srcpath, dstpath, callback || noop);
411 };
412
413 fs.linkSync = function(srcpath, dstpath) {
414   return binding.link(srcpath, dstpath);
415 };
416
417 fs.unlink = function(path, callback) {
418   binding.unlink(path, callback || noop);
419 };
420
421 fs.unlinkSync = function(path) {
422   return binding.unlink(path);
423 };
424
425 fs.chmod = function(path, mode, callback) {
426   binding.chmod(path, modeNum(mode), callback || noop);
427 };
428
429 fs.chmodSync = function(path, mode) {
430   return binding.chmod(path, modeNum(mode));
431 };
432
433 fs.chown = function(path, uid, gid, callback) {
434   binding.chown(path, uid, gid, callback || noop);
435 };
436
437 fs.chownSync = function(path, uid, gid) {
438   return binding.chown(path, uid, gid);
439 };
440
441 // converts Date or number to a fractional UNIX timestamp
442 function toUnixTimestamp(time) {
443   if (typeof time == 'number') {
444     return time;
445   }
446   if (time instanceof Date) {
447     // convert to 123.456 UNIX timestamp
448     return time.getTime() / 1000;
449   }
450   throw new Error("Cannot parse time: " + time);
451 }
452
453 // exported for unit tests, not for public consumption
454 fs._toUnixTimestamp = toUnixTimestamp;
455
456 fs.utimes = function(path, atime, mtime, callback) {
457   atime = toUnixTimestamp(atime);
458   mtime = toUnixTimestamp(mtime);
459   binding.utimes(path, atime, mtime, callback || noop);
460 };
461
462 fs.utimesSync = function(path, atime, mtime) {
463   atime = toUnixTimestamp(atime);
464   mtime = toUnixTimestamp(mtime);
465   binding.utimes(path, atime, mtime);
466 };
467
468 fs.futimes = function(fd, atime, mtime, callback) {
469   atime = toUnixTimestamp(atime);
470   mtime = toUnixTimestamp(mtime);
471   binding.futimes(fd, atime, mtime, callback || noop);
472 };
473
474 fs.futimesSync = function(fd, atime, mtime) {
475   atime = toUnixTimestamp(atime);
476   mtime = toUnixTimestamp(mtime);
477   binding.futimes(fd, atime, mtime);
478 };
479
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) {
483     if (writeErr) {
484       fs.close(fd, function() {
485         if (callback) callback(writeErr);
486       });
487     } else {
488       if (written === length) {
489         fs.close(fd, callback);
490       } else {
491         writeAll(fd, buffer, offset + written, length - written, callback);
492       }
493     }
494   });
495 }
496
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) {
502     if (openErr) {
503       if (callback) callback(openErr);
504     } else {
505       var buffer = Buffer.isBuffer(data) ? data : new Buffer(data, encoding);
506       writeAll(fd, buffer, 0, buffer.length, callback);
507     }
508   });
509 };
510
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');
515   }
516   var written = 0;
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);
521   }
522   fs.closeSync(fd);
523 };
524
525 // Stat Change Watchers
526
527 var statWatchers = {};
528
529 fs.watchFile = function(filename) {
530   var stat;
531   var options;
532   var listener;
533
534   if ('object' == typeof arguments[1]) {
535     options = arguments[1];
536     listener = arguments[2];
537   } else {
538     options = {};
539     listener = arguments[1];
540   }
541
542   if (options.persistent === undefined) options.persistent = true;
543   if (options.interval === undefined) options.interval = 0;
544
545   if (statWatchers[filename]) {
546     stat = statWatchers[filename];
547   } else {
548     statWatchers[filename] = new binding.StatWatcher();
549     stat = statWatchers[filename];
550     stat.start(filename, options.persistent, options.interval);
551   }
552   stat.addListener('change', listener);
553   return stat;
554 };
555
556 fs.unwatchFile = function(filename) {
557   var stat;
558   if (statWatchers[filename]) {
559     stat = statWatchers[filename];
560     stat.stop();
561     statWatchers[filename] = undefined;
562   }
563 };
564
565 // Realpath
566 // Not using realpath(2) because it's bad.
567 // See: http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html
568
569 var path = require('path'),
570     normalize = path.normalize,
571     isWindows = process.platform === 'win32';
572
573 if (isWindows) {
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.
576
577   // windows version
578   fs.realpathSync = function realpathSync(p, cache) {
579     var p = path.resolve(p);
580     if (cache && Object.prototype.hasOwnProperty.call(cache, p)) {
581       return cache[p];
582     }
583     fs.statSync(p);
584     if (cache) cache[p] = p;
585     return p;
586   };
587
588   // windows version
589   fs.realpath = function(p, cache, cb) {
590     if (typeof cb !== 'function') {
591       cb = cache;
592       cache = null;
593     }
594     var p = path.resolve(p);
595     if (cache && Object.prototype.hasOwnProperty.call(cache, p)) {
596       return cb(null, cache[p]);
597     }
598     fs.stat(p, function(err) {
599       if (err) cb(err);
600       if (cache) cache[p] = p;
601       cb(null, p);
602     });
603   };
604
605
606 } else /* posix */ {
607
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;
611
612   // posix version
613   fs.realpathSync = function realpathSync(p, cache) {
614     // make p is absolute
615     p = path.resolve(p);
616
617     if (cache && Object.prototype.hasOwnProperty.call(cache, p)) {
618       return cache[p];
619     }
620
621     var original = p,
622         seenLinks = {},
623         knownHard = {};
624
625     // current character position in p
626     var pos = 0;
627     // the partial path so far, including a trailing slash if any
628     var current = '';
629     // the partial path without a trailing slash
630     var base = '';
631     // the partial path scanned in the previous round, with slash
632     var previous = '';
633
634     // walk down the path, swapping out linked pathparts for their real
635     // values
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);
641       previous = current;
642       current += result[0];
643       base = previous + result[1];
644       pos = nextPartRe.lastIndex;
645
646       // continue if not a symlink, or if root
647       if (!base || knownHard[base] || (cache && cache[base] === base)) {
648         continue;
649       }
650
651       var resolvedLink;
652       if (cache && Object.prototype.hasOwnProperty.call(cache, base)) {
653         // some known symbolic link.  no need to stat again.
654         resolvedLink = cache[base];
655       } else {
656         var stat = fs.lstatSync(base);
657         if (!stat.isSymbolicLink()) {
658           knownHard[base] = true;
659           if (cache) cache[base] = base;
660           continue;
661         }
662
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]) {
666           fs.statSync(base);
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;
671         }
672       }
673
674       // resolve the link, then start over
675       p = path.resolve(resolvedLink, p.slice(pos));
676       pos = 0;
677       previous = base = current = '';
678     }
679
680     if (cache) cache[original] = p;
681
682     return p;
683   };
684
685
686   // posix version
687   fs.realpath = function realpath(p, cache, cb) {
688     if (typeof cb !== 'function') {
689       cb = cache;
690       cache = null;
691     }
692
693     // make p is absolute
694     p = path.resolve(p);
695
696     if (cache && Object.prototype.hasOwnProperty.call(cache, p)) {
697       return cb(null, cache[p]);
698     }
699
700     var original = p,
701         seenLinks = {},
702         knownHard = {};
703
704     // current character position in p
705     var pos = 0;
706     // the partial path so far, including a trailing slash if any
707     var current = '';
708     // the partial path without a trailing slash
709     var base = '';
710     // the partial path scanned in the previous round, with slash
711     var previous = '';
712
713     // walk down the path, swapping out linked pathparts for their real
714     // values
715     LOOP();
716     function LOOP() {
717       // stop if scanned past end of path
718       if (pos >= p.length) {
719         if (cache) cache[original] = p;
720         return cb(null, p);
721       }
722
723       // find the next part
724       nextPartRe.lastIndex = pos;
725       var result = nextPartRe.exec(p);
726       previous = current;
727       current += result[0];
728       base = previous + result[1];
729       pos = nextPartRe.lastIndex;
730
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);
734       }
735
736       if (cache && Object.prototype.hasOwnProperty.call(cache, base)) {
737         // known symbolic link.  no need to stat again.
738         return gotResolvedLink(cache[base]);
739       }
740
741       return fs.lstat(base, gotStat);
742     }
743
744     function gotStat(err, stat) {
745       if (err) return cb(err);
746
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);
752       }
753
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);
757       if (seenLinks[id]) {
758         return gotTarget(null, seenLinks[id], base);
759       }
760       fs.stat(base, function(err) {
761         if (err) return cb(err);
762
763         fs.readlink(base, function(err, target) {
764           gotTarget(err, seenLinks[id] = target);
765         });
766       });
767     }
768
769     function gotTarget(err, target, base) {
770       if (err) return cb(err);
771
772       var resolvedLink = path.resolve(previous, target);
773       if (cache) cache[base] = resolvedLink;
774       gotResolvedLink(resolvedLink);
775     }
776
777     function gotResolvedLink(resolvedLink) {
778
779       // resolve the link, then start over
780       p = path.resolve(resolvedLink, p.slice(pos));
781       pos = 0;
782       previous = base = current = '';
783
784       return process.nextTick(LOOP);
785     }
786   };
787
788 }
789
790
791 var pool;
792
793 function allocNewPool() {
794   pool = new Buffer(kPoolSize);
795   pool.used = 0;
796 }
797
798
799
800 fs.createReadStream = function(path, options) {
801   return new ReadStream(path, options);
802 };
803
804 var ReadStream = fs.ReadStream = function(path, options) {
805   if (!(this instanceof ReadStream)) return new ReadStream(path, options);
806
807   Stream.call(this);
808
809   var self = this;
810
811   this.path = path;
812   this.fd = null;
813   this.readable = true;
814   this.paused = false;
815
816   this.flags = 'r';
817   this.mode = parseInt('0666', 8);
818   this.bufferSize = 64 * 1024;
819
820   options = options || {};
821
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];
827   }
828
829   if (this.encoding) this.setEncoding(this.encoding);
830
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'));
837     } else {
838       this._firstRead = true;
839     }
840   }
841
842   if (this.fd !== null) {
843     return;
844   }
845
846   fs.open(this.path, this.flags, this.mode, function(err, fd) {
847     if (err) {
848       self.emit('error', err);
849       self.readable = false;
850       return;
851     }
852
853     self.fd = fd;
854     self.emit('open', fd);
855     self._read();
856   });
857 };
858 util.inherits(ReadStream, Stream);
859
860 fs.FileReadStream = fs.ReadStream; // support the legacy name
861
862 ReadStream.prototype.setEncoding = function(encoding) {
863   var StringDecoder = require('string_decoder').StringDecoder; // lazy load
864   this._decoder = new StringDecoder(encoding);
865 };
866
867
868 ReadStream.prototype._read = function() {
869   var self = this;
870   if (!self.readable || self.paused) return;
871
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.
875     pool = null;
876     allocNewPool();
877   }
878
879   if (self.start !== undefined && self._firstRead) {
880     self.pos = self.start;
881     self._firstRead = false;
882   }
883
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
886   // one.
887   var thisPool = pool;
888   var toRead = Math.min(pool.length - pool.used, this.bufferSize);
889   var start = pool.used;
890
891   if (this.pos !== undefined) {
892     toRead = Math.min(this.end - this.pos + 1, toRead);
893   }
894
895   function afterRead(err, bytesRead) {
896     if (err) {
897       self.emit('error', err);
898       self.readable = false;
899       return;
900     }
901
902     if (bytesRead === 0) {
903       self.emit('end');
904       self.destroy();
905       return;
906     }
907
908     var b = thisPool.slice(start, start + bytesRead);
909
910     // Possible optimizition here?
911     // Reclaim some bytes if bytesRead < toRead?
912     // Would need to ensure that pool === thisPool.
913
914     // do not emit events if the stream is paused
915     if (self.paused) {
916       self.buffer = b;
917       return;
918     }
919
920     // do not emit events anymore after we declared the stream unreadable
921     if (!self.readable) return;
922
923     self._emitData(b);
924     self._read();
925   }
926
927   fs.read(self.fd, pool, pool.used, toRead, self.pos, afterRead);
928
929   if (self.pos !== undefined) {
930     self.pos += toRead;
931   }
932   pool.used += toRead;
933 };
934
935
936 ReadStream.prototype._emitData = function(d) {
937   if (this._decoder) {
938     var string = this._decoder.write(d);
939     if (string.length) this.emit('data', string);
940   } else {
941     this.emit('data', d);
942   }
943 };
944
945
946 ReadStream.prototype.destroy = function(cb) {
947   var self = this;
948   this.readable = false;
949
950   function close() {
951     fs.close(self.fd, function(err) {
952       if (err) {
953         if (cb) cb(err);
954         self.emit('error', err);
955         return;
956       }
957
958       if (cb) cb(null);
959       self.emit('close');
960     });
961   }
962
963   if (this.fd) {
964     close();
965   } else {
966     this.addListener('open', close);
967   }
968 };
969
970
971 ReadStream.prototype.pause = function() {
972   this.paused = true;
973 };
974
975
976 ReadStream.prototype.resume = function() {
977   this.paused = false;
978
979   if (this.buffer) {
980     this._emitData(this.buffer);
981     this.buffer = null;
982   }
983
984   // hasn't opened yet.
985   if (null == this.fd) return;
986
987   this._read();
988 };
989
990
991
992 fs.createWriteStream = function(path, options) {
993   return new WriteStream(path, options);
994 };
995
996 var WriteStream = fs.WriteStream = function(path, options) {
997   if (!(this instanceof WriteStream)) return new WriteStream(path, options);
998
999   Stream.call(this);
1000
1001   this.path = path;
1002   this.fd = null;
1003   this.writable = true;
1004
1005   this.flags = 'w';
1006   this.encoding = 'binary';
1007   this.mode = parseInt('0666', 8);
1008
1009   options = options || {};
1010
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];
1016   }
1017
1018   this.busy = false;
1019   this._queue = [];
1020
1021   if (this.fd === null) {
1022     this._queue.push([fs.open, this.path, this.flags, this.mode, undefined]);
1023     this.flush();
1024   }
1025 };
1026 util.inherits(WriteStream, Stream);
1027
1028 fs.FileWriteStream = fs.WriteStream; // support the legacy name
1029
1030 WriteStream.prototype.flush = function() {
1031   if (this.busy) return;
1032   var self = this;
1033
1034   var args = this._queue.shift();
1035   if (!args) {
1036     if (this.drainable) { self.emit('drain'); }
1037     return;
1038   }
1039
1040   this.busy = true;
1041
1042   var method = args.shift(),
1043       cb = args.pop();
1044
1045   var self = this;
1046
1047   args.push(function(err) {
1048     self.busy = false;
1049
1050     if (err) {
1051       self.writable = false;
1052       if (cb) {
1053         cb(err);
1054       }
1055       self.emit('error', err);
1056       return;
1057     }
1058
1059     // stop flushing after close
1060     if (method === fs.close) {
1061       if (cb) {
1062         cb(null);
1063       }
1064       self.emit('close');
1065       return;
1066     }
1067
1068     // save reference for file pointer
1069     if (method === fs.open) {
1070       self.fd = arguments[1];
1071       self.emit('open', self.fd);
1072     } else if (cb) {
1073       // write callback
1074       cb(null, arguments[1]);
1075     }
1076
1077     self.flush();
1078   });
1079
1080   // Inject the file pointer
1081   if (method !== fs.open) {
1082     args.unshift(self.fd);
1083   }
1084
1085   method.apply(this, args);
1086 };
1087
1088 WriteStream.prototype.write = function(data) {
1089   if (!this.writable) {
1090     this.emit("error", new Error('stream not writable'));
1091     return false;
1092   }
1093
1094   this.drainable = true;
1095
1096   var cb;
1097   if (typeof(arguments[arguments.length - 1]) == 'function') {
1098     cb = arguments[arguments.length - 1];
1099   }
1100
1101   if (Buffer.isBuffer(data)) {
1102     this._queue.push([fs.write, data, 0, data.length, null, cb]);
1103   } else {
1104     var encoding = 'utf8';
1105     if (typeof(arguments[1]) == 'string') encoding = arguments[1];
1106     this._queue.push([fs.write, data, undefined, encoding, cb]);
1107   }
1108
1109
1110   this.flush();
1111
1112   return false;
1113 };
1114
1115 WriteStream.prototype.end = function(data, encoding, cb) {
1116   if (typeof(data) === 'function') {
1117     cb = data;
1118   } else if (typeof(encoding) === 'function') {
1119     cb = encoding;
1120     this.write(data);
1121   } else if (arguments.length > 0) {
1122     this.write(data, encoding);
1123   }
1124   this.writable = false;
1125   this._queue.push([fs.close, cb]);
1126   this.flush();
1127 };
1128
1129 WriteStream.prototype.destroy = function(cb) {
1130   var self = this;
1131   this.writable = false;
1132
1133   function close() {
1134     fs.close(self.fd, function(err) {
1135       if (err) {
1136         if (cb) { cb(err); }
1137         self.emit('error', err);
1138         return;
1139       }
1140
1141       if (cb) { cb(null); }
1142       self.emit('close');
1143     });
1144   }
1145
1146   if (this.fd) {
1147     close();
1148   } else {
1149     this.addListener('open', close);
1150   }
1151 };
1152
1153 // There is no shutdown() for files.
1154 WriteStream.prototype.destroySoon = WriteStream.prototype.end;
1155