UNPKG

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