UNPKG

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