|
1 | | -// Monkey-patching the fs module. |
2 | | -// It's ugly, but there is simply no other way to do this. |
3 | | -var fs = module.exports = require('./fs.js') |
4 | | - |
5 | | -var assert = require('assert') |
| 1 | +var fs = require('fs') |
| 2 | +module.exports = require('./fs.js') |
6 | 3 |
|
7 | 4 | // fix up some busted stuff, mostly on windows and old nodes |
8 | 5 | require('./polyfills.js') |
9 | 6 |
|
10 | | -var util = require('util') |
| 7 | +module.exports.FileReadStream = ReadStream; // Legacy name. |
| 8 | +module.exports.FileWriteStream = WriteStream; // Legacy name. |
| 9 | +module.exports.ReadStream = ReadStream |
| 10 | +module.exports.WriteStream = WriteStream |
| 11 | +module.exports.close = close |
| 12 | +module.exports.closeSync = closeSync |
| 13 | +module.exports.createReadStream = createReadStream |
| 14 | +module.exports.createWriteStream = createWriteStream |
| 15 | +module.exports.open = open |
| 16 | +module.exports.readFile = readFile |
| 17 | +module.exports.readdir = readdir |
11 | 18 |
|
12 | | -function noop () {} |
| 19 | +ReadStream.prototype = Object.create(fs.ReadStream.prototype) |
| 20 | +ReadStream.prototype.open = ReadStream$open |
13 | 21 |
|
14 | | -var debug = noop |
15 | | -if (util.debuglog) |
16 | | - debug = util.debuglog('gfs') |
17 | | -else if (/\bgfs\b/i.test(process.env.NODE_DEBUG || '')) |
18 | | - debug = function() { |
19 | | - var m = util.format.apply(util, arguments) |
20 | | - m = 'GFS: ' + m.split(/\n/).join('\nGFS: ') |
21 | | - console.error(m) |
22 | | - } |
| 22 | +WriteStream.prototype = Object.create(fs.WriteStream.prototype) |
| 23 | +WriteStream.prototype.open = WriteStream$open |
23 | 24 |
|
24 | | -if (/\bgfs\b/i.test(process.env.NODE_DEBUG || '')) { |
25 | | - process.on('exit', function() { |
26 | | - debug('fds', fds) |
27 | | - debug(queue) |
28 | | - assert.equal(queue.length, 0) |
29 | | - }) |
30 | | -} |
31 | | - |
32 | | - |
33 | | -var originalOpen = fs.open |
34 | | -fs.open = open |
| 25 | +var queue = [] |
35 | 26 |
|
36 | | -function open(path, flags, mode, cb) { |
37 | | - if (typeof mode === "function") cb = mode, mode = null |
38 | | - if (typeof cb !== "function") cb = noop |
39 | | - new OpenReq(path, flags, mode, cb) |
| 27 | +function ReadStream (path, options) { |
| 28 | + if (this instanceof ReadStream) |
| 29 | + return fs.ReadStream.apply(this, arguments), this |
| 30 | + else |
| 31 | + return ReadStream.apply(Object.create(ReadStream.prototype), arguments) |
40 | 32 | } |
41 | 33 |
|
42 | | -function OpenReq(path, flags, mode, cb) { |
43 | | - this.path = path |
44 | | - this.flags = flags |
45 | | - this.mode = mode |
46 | | - this.cb = cb |
47 | | - Req.call(this) |
| 34 | +function ReadStream$open () { |
| 35 | + var that = this |
| 36 | + open(that.path, that.flags, that.mode, function (err, fd) { |
| 37 | + if (err) { |
| 38 | + if (that.autoClose) |
| 39 | + that.destroy() |
| 40 | + |
| 41 | + that.emit('error', err) |
| 42 | + } else { |
| 43 | + that.fd = fd |
| 44 | + that.emit('open', fd) |
| 45 | + that.read() |
| 46 | + } |
| 47 | + }) |
48 | 48 | } |
49 | 49 |
|
50 | | -util.inherits(OpenReq, Req) |
51 | | - |
52 | | -OpenReq.prototype.process = function() { |
53 | | - originalOpen.call(fs, this.path, this.flags, this.mode, this.done) |
| 50 | +function WriteStream (path, options) { |
| 51 | + if (this instanceof WriteStream) |
| 52 | + return fs.WriteStream.apply(this, arguments), this |
| 53 | + else |
| 54 | + return WriteStream.apply(Object.create(WriteStream.prototype), arguments) |
54 | 55 | } |
55 | 56 |
|
56 | | -var fds = {} |
57 | | -OpenReq.prototype.done = function(er, fd) { |
58 | | - debug('open done', er, fd) |
59 | | - if (fd) |
60 | | - fds['fd' + fd] = this.path |
61 | | - Req.prototype.done.call(this, er, fd) |
| 57 | +function WriteStream$open () { |
| 58 | + var that = this |
| 59 | + open(that.path, that.flags, that.mode, function (err, fd) { |
| 60 | + if (err) { |
| 61 | + that.destroy() |
| 62 | + that.emit('error', err) |
| 63 | + } else { |
| 64 | + that.fd = fd |
| 65 | + that.emit('open', fd) |
| 66 | + } |
| 67 | + }) |
62 | 68 | } |
63 | 69 |
|
| 70 | +function close (fd, cb) { |
| 71 | + return fs.close(fd, function (err) { |
| 72 | + if (!err) |
| 73 | + retry() |
64 | 74 |
|
65 | | -var originalReaddir = fs.readdir |
66 | | -fs.readdir = readdir |
67 | | - |
68 | | -function readdir(path, cb) { |
69 | | - if (typeof cb !== "function") cb = noop |
70 | | - new ReaddirReq(path, cb) |
| 75 | + if (typeof cb === 'function') |
| 76 | + cb.apply(this, arguments) |
| 77 | + }) |
71 | 78 | } |
72 | 79 |
|
73 | | -function ReaddirReq(path, cb) { |
74 | | - this.path = path |
75 | | - this.cb = cb |
76 | | - Req.call(this) |
| 80 | +function closeSync () { |
| 81 | + // Note that graceful-fs also retries when fs.closeSync() fails. |
| 82 | + // Looks like a bug to me, although it's probably a harmless one. |
| 83 | + var rval = fs.closeSync.apply(fs, arguments) |
| 84 | + retry() |
| 85 | + return rval |
77 | 86 | } |
78 | 87 |
|
79 | | -util.inherits(ReaddirReq, Req) |
80 | | - |
81 | | -ReaddirReq.prototype.process = function() { |
82 | | - originalReaddir.call(fs, this.path, this.done) |
| 88 | +function createReadStream (path, options) { |
| 89 | + return new ReadStream(path, options) |
83 | 90 | } |
84 | 91 |
|
85 | | -ReaddirReq.prototype.done = function(er, files) { |
86 | | - if (files && files.sort) |
87 | | - files = files.sort() |
88 | | - Req.prototype.done.call(this, er, files) |
89 | | - onclose() |
| 92 | +function createWriteStream (path, options) { |
| 93 | + return new WriteStream(path, options) |
90 | 94 | } |
91 | 95 |
|
| 96 | +function open (path, flags, mode, cb) { |
| 97 | + if (typeof mode === 'function') |
| 98 | + cb = mode, mode = null |
92 | 99 |
|
93 | | -var originalClose = fs.close |
94 | | -fs.close = close |
| 100 | + return go(path, flags, mode, cb) |
95 | 101 |
|
96 | | -function close (fd, cb) { |
97 | | - debug('close', fd) |
98 | | - if (typeof cb !== "function") cb = noop |
99 | | - delete fds['fd' + fd] |
100 | | - originalClose.call(fs, fd, function(er) { |
101 | | - onclose() |
102 | | - cb(er) |
103 | | - }) |
| 102 | + function go (path, flags, mode, cb) { |
| 103 | + return fs.open(path, flags, mode, function (err, fd) { |
| 104 | + if (err && (err.code === 'EMFILE' || err.code === 'ENFILE')) |
| 105 | + queue.push([go, [path, flags, mode, cb]]) |
| 106 | + else if (typeof cb === 'function') |
| 107 | + cb.apply(this, arguments) |
| 108 | + }) |
| 109 | + } |
104 | 110 | } |
105 | 111 |
|
| 112 | +function readFile (path, options, cb) { |
| 113 | + if (typeof options === 'function') |
| 114 | + cb = options, options = null |
106 | 115 |
|
107 | | -var originalCloseSync = fs.closeSync |
108 | | -fs.closeSync = closeSync |
| 116 | + return go(path, options, cb) |
109 | 117 |
|
110 | | -function closeSync (fd) { |
111 | | - try { |
112 | | - return originalCloseSync(fd) |
113 | | - } finally { |
114 | | - onclose() |
| 118 | + function go (path, flags, mode, cb) { |
| 119 | + return fs.readFile(path, options, function (err, fd) { |
| 120 | + if (err && (err.code === 'EMFILE' || err.code === 'ENFILE')) |
| 121 | + queue.push([go, [path, options, cb]]) |
| 122 | + else if (typeof cb === 'function') |
| 123 | + cb.apply(this, arguments) |
| 124 | + }) |
115 | 125 | } |
116 | 126 | } |
117 | 127 |
|
| 128 | +function readdir (path, cb) { |
| 129 | + return go(path, cb) |
118 | 130 |
|
119 | | -// Req class |
120 | | -function Req () { |
121 | | - // start processing |
122 | | - this.done = this.done.bind(this) |
123 | | - this.failures = 0 |
124 | | - this.process() |
125 | | -} |
126 | | - |
127 | | -Req.prototype.done = function (er, result) { |
128 | | - var tryAgain = false |
129 | | - if (er) { |
130 | | - var code = er.code |
131 | | - var tryAgain = code === "EMFILE" || code === "ENFILE" |
132 | | - if (process.platform === "win32") |
133 | | - tryAgain = tryAgain || code === "OK" |
134 | | - } |
| 131 | + function go () { |
| 132 | + return fs.readdir(path, function (err, files) { |
| 133 | + if (files && files.sort) |
| 134 | + files.sort(); // Backwards compatibility with graceful-fs. |
135 | 135 |
|
136 | | - if (tryAgain) { |
137 | | - this.failures ++ |
138 | | - enqueue(this) |
139 | | - } else { |
140 | | - var cb = this.cb |
141 | | - cb(er, result) |
| 136 | + if (err && (err.code === 'EMFILE' || err.code === 'ENFILE')) |
| 137 | + queue.push([go, [path, cb]]) |
| 138 | + else if (typeof cb === 'function') |
| 139 | + cb.apply(this, arguments) |
| 140 | + }) |
142 | 141 | } |
143 | 142 | } |
144 | 143 |
|
145 | | -var queue = [] |
146 | | - |
147 | | -function enqueue(req) { |
148 | | - queue.push(req) |
149 | | - debug('enqueue %d %s', queue.length, req.constructor.name, req) |
150 | | -} |
151 | | - |
152 | | -function onclose() { |
153 | | - var req = queue.shift() |
154 | | - if (req) { |
155 | | - debug('process', req.constructor.name, req) |
156 | | - req.process() |
157 | | - } |
| 144 | +function retry () { |
| 145 | + var elem = queue.shift() |
| 146 | + if (elem) |
| 147 | + elem[0].apply(null, elem[1]) |
158 | 148 | } |
0 commit comments