blob: e15042da910306d4216416036c39585e47cb0018 [file] [log] [blame]
Yang Guo4fd355c2019-09-19 08:59:031var fs = require('fs')
2var polyfills = require('./polyfills.js')
3var legacy = require('./legacy-streams.js')
4var clone = require('./clone.js')
5
6var util = require('util')
7
8/* istanbul ignore next - node 0.x polyfill */
9var gracefulQueue
10var previousSymbol
11
12/* istanbul ignore else - node 0.x polyfill */
13if (typeof Symbol === 'function' && typeof Symbol.for === 'function') {
14 gracefulQueue = Symbol.for('graceful-fs.queue')
15 // This is used in testing by future versions
16 previousSymbol = Symbol.for('graceful-fs.previous')
17} else {
18 gracefulQueue = '___graceful-fs.queue'
19 previousSymbol = '___graceful-fs.previous'
20}
21
22function noop () {}
23
Tim van der Lippe459f4022021-02-19 12:09:5724function publishQueue(context, queue) {
25 Object.defineProperty(context, gracefulQueue, {
26 get: function() {
27 return queue
28 }
29 })
30}
31
Yang Guo4fd355c2019-09-19 08:59:0332var debug = noop
33if (util.debuglog)
34 debug = util.debuglog('gfs4')
35else if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || ''))
36 debug = function() {
37 var m = util.format.apply(util, arguments)
38 m = 'GFS4: ' + m.split(/\n/).join('\nGFS4: ')
39 console.error(m)
40 }
41
42// Once time initialization
Tim van der Lippe459f4022021-02-19 12:09:5743if (!fs[gracefulQueue]) {
Yang Guo4fd355c2019-09-19 08:59:0344 // This queue can be shared by multiple loaded instances
Tim van der Lippe459f4022021-02-19 12:09:5745 var queue = global[gracefulQueue] || []
46 publishQueue(fs, queue)
Yang Guo4fd355c2019-09-19 08:59:0347
48 // Patch fs.close/closeSync to shared queue version, because we need
49 // to retry() whenever a close happens *anywhere* in the program.
50 // This is essential when multiple graceful-fs instances are
51 // in play at the same time.
52 fs.close = (function (fs$close) {
53 function close (fd, cb) {
54 return fs$close.call(fs, fd, function (err) {
55 // This function uses the graceful-fs shared queue
56 if (!err) {
57 retry()
58 }
59
60 if (typeof cb === 'function')
61 cb.apply(this, arguments)
62 })
63 }
64
65 Object.defineProperty(close, previousSymbol, {
66 value: fs$close
67 })
68 return close
69 })(fs.close)
70
71 fs.closeSync = (function (fs$closeSync) {
72 function closeSync (fd) {
73 // This function uses the graceful-fs shared queue
74 fs$closeSync.apply(fs, arguments)
75 retry()
76 }
77
78 Object.defineProperty(closeSync, previousSymbol, {
79 value: fs$closeSync
80 })
81 return closeSync
82 })(fs.closeSync)
83
84 if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || '')) {
85 process.on('exit', function() {
Tim van der Lippe459f4022021-02-19 12:09:5786 debug(fs[gracefulQueue])
87 require('assert').equal(fs[gracefulQueue].length, 0)
Yang Guo4fd355c2019-09-19 08:59:0388 })
89 }
90}
91
Tim van der Lippe459f4022021-02-19 12:09:5792if (!global[gracefulQueue]) {
93 publishQueue(global, fs[gracefulQueue]);
94}
95
Yang Guo4fd355c2019-09-19 08:59:0396module.exports = patch(clone(fs))
97if (process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH && !fs.__patched) {
98 module.exports = patch(fs)
99 fs.__patched = true;
100}
101
102function patch (fs) {
103 // Everything that references the open() function needs to be in here
104 polyfills(fs)
105 fs.gracefulify = patch
106
107 fs.createReadStream = createReadStream
108 fs.createWriteStream = createWriteStream
109 var fs$readFile = fs.readFile
110 fs.readFile = readFile
111 function readFile (path, options, cb) {
112 if (typeof options === 'function')
113 cb = options, options = null
114
115 return go$readFile(path, options, cb)
116
117 function go$readFile (path, options, cb) {
118 return fs$readFile(path, options, function (err) {
119 if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
120 enqueue([go$readFile, [path, options, cb]])
121 else {
122 if (typeof cb === 'function')
123 cb.apply(this, arguments)
124 retry()
125 }
126 })
127 }
128 }
129
130 var fs$writeFile = fs.writeFile
131 fs.writeFile = writeFile
132 function writeFile (path, data, options, cb) {
133 if (typeof options === 'function')
134 cb = options, options = null
135
136 return go$writeFile(path, data, options, cb)
137
138 function go$writeFile (path, data, options, cb) {
139 return fs$writeFile(path, data, options, function (err) {
140 if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
141 enqueue([go$writeFile, [path, data, options, cb]])
142 else {
143 if (typeof cb === 'function')
144 cb.apply(this, arguments)
145 retry()
146 }
147 })
148 }
149 }
150
151 var fs$appendFile = fs.appendFile
152 if (fs$appendFile)
153 fs.appendFile = appendFile
154 function appendFile (path, data, options, cb) {
155 if (typeof options === 'function')
156 cb = options, options = null
157
158 return go$appendFile(path, data, options, cb)
159
160 function go$appendFile (path, data, options, cb) {
161 return fs$appendFile(path, data, options, function (err) {
162 if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
163 enqueue([go$appendFile, [path, data, options, cb]])
164 else {
165 if (typeof cb === 'function')
166 cb.apply(this, arguments)
167 retry()
168 }
169 })
170 }
171 }
172
Tim van der Lippe459f4022021-02-19 12:09:57173 var fs$copyFile = fs.copyFile
174 if (fs$copyFile)
175 fs.copyFile = copyFile
176 function copyFile (src, dest, flags, cb) {
177 if (typeof flags === 'function') {
178 cb = flags
179 flags = 0
180 }
181 return fs$copyFile(src, dest, flags, function (err) {
182 if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
183 enqueue([fs$copyFile, [src, dest, flags, cb]])
184 else {
185 if (typeof cb === 'function')
186 cb.apply(this, arguments)
187 retry()
188 }
189 })
190 }
191
Yang Guo4fd355c2019-09-19 08:59:03192 var fs$readdir = fs.readdir
193 fs.readdir = readdir
194 function readdir (path, options, cb) {
195 var args = [path]
196 if (typeof options !== 'function') {
197 args.push(options)
198 } else {
199 cb = options
200 }
201 args.push(go$readdir$cb)
202
203 return go$readdir(args)
204
205 function go$readdir$cb (err, files) {
206 if (files && files.sort)
207 files.sort()
208
209 if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
210 enqueue([go$readdir, [args]])
211
212 else {
213 if (typeof cb === 'function')
214 cb.apply(this, arguments)
215 retry()
216 }
217 }
218 }
219
220 function go$readdir (args) {
221 return fs$readdir.apply(fs, args)
222 }
223
224 if (process.version.substr(0, 4) === 'v0.8') {
225 var legStreams = legacy(fs)
226 ReadStream = legStreams.ReadStream
227 WriteStream = legStreams.WriteStream
228 }
229
230 var fs$ReadStream = fs.ReadStream
231 if (fs$ReadStream) {
232 ReadStream.prototype = Object.create(fs$ReadStream.prototype)
233 ReadStream.prototype.open = ReadStream$open
234 }
235
236 var fs$WriteStream = fs.WriteStream
237 if (fs$WriteStream) {
238 WriteStream.prototype = Object.create(fs$WriteStream.prototype)
239 WriteStream.prototype.open = WriteStream$open
240 }
241
242 Object.defineProperty(fs, 'ReadStream', {
243 get: function () {
244 return ReadStream
245 },
246 set: function (val) {
247 ReadStream = val
248 },
249 enumerable: true,
250 configurable: true
251 })
252 Object.defineProperty(fs, 'WriteStream', {
253 get: function () {
254 return WriteStream
255 },
256 set: function (val) {
257 WriteStream = val
258 },
259 enumerable: true,
260 configurable: true
261 })
262
263 // legacy names
Paul Lewis75090cf2019-10-25 13:13:11264 var FileReadStream = ReadStream
Yang Guo4fd355c2019-09-19 08:59:03265 Object.defineProperty(fs, 'FileReadStream', {
266 get: function () {
Paul Lewis75090cf2019-10-25 13:13:11267 return FileReadStream
Yang Guo4fd355c2019-09-19 08:59:03268 },
269 set: function (val) {
Paul Lewis75090cf2019-10-25 13:13:11270 FileReadStream = val
Yang Guo4fd355c2019-09-19 08:59:03271 },
272 enumerable: true,
273 configurable: true
274 })
Paul Lewis75090cf2019-10-25 13:13:11275 var FileWriteStream = WriteStream
Yang Guo4fd355c2019-09-19 08:59:03276 Object.defineProperty(fs, 'FileWriteStream', {
277 get: function () {
Paul Lewis75090cf2019-10-25 13:13:11278 return FileWriteStream
Yang Guo4fd355c2019-09-19 08:59:03279 },
280 set: function (val) {
Paul Lewis75090cf2019-10-25 13:13:11281 FileWriteStream = val
Yang Guo4fd355c2019-09-19 08:59:03282 },
283 enumerable: true,
284 configurable: true
285 })
286
287 function ReadStream (path, options) {
288 if (this instanceof ReadStream)
289 return fs$ReadStream.apply(this, arguments), this
290 else
291 return ReadStream.apply(Object.create(ReadStream.prototype), arguments)
292 }
293
294 function ReadStream$open () {
295 var that = this
296 open(that.path, that.flags, that.mode, function (err, fd) {
297 if (err) {
298 if (that.autoClose)
299 that.destroy()
300
301 that.emit('error', err)
302 } else {
303 that.fd = fd
304 that.emit('open', fd)
305 that.read()
306 }
307 })
308 }
309
310 function WriteStream (path, options) {
311 if (this instanceof WriteStream)
312 return fs$WriteStream.apply(this, arguments), this
313 else
314 return WriteStream.apply(Object.create(WriteStream.prototype), arguments)
315 }
316
317 function WriteStream$open () {
318 var that = this
319 open(that.path, that.flags, that.mode, function (err, fd) {
320 if (err) {
321 that.destroy()
322 that.emit('error', err)
323 } else {
324 that.fd = fd
325 that.emit('open', fd)
326 }
327 })
328 }
329
330 function createReadStream (path, options) {
331 return new fs.ReadStream(path, options)
332 }
333
334 function createWriteStream (path, options) {
335 return new fs.WriteStream(path, options)
336 }
337
338 var fs$open = fs.open
339 fs.open = open
340 function open (path, flags, mode, cb) {
341 if (typeof mode === 'function')
342 cb = mode, mode = null
343
344 return go$open(path, flags, mode, cb)
345
346 function go$open (path, flags, mode, cb) {
347 return fs$open(path, flags, mode, function (err, fd) {
348 if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
349 enqueue([go$open, [path, flags, mode, cb]])
350 else {
351 if (typeof cb === 'function')
352 cb.apply(this, arguments)
353 retry()
354 }
355 })
356 }
357 }
358
359 return fs
360}
361
362function enqueue (elem) {
363 debug('ENQUEUE', elem[0].name, elem[1])
Tim van der Lippe459f4022021-02-19 12:09:57364 fs[gracefulQueue].push(elem)
Yang Guo4fd355c2019-09-19 08:59:03365}
366
367function retry () {
Tim van der Lippe459f4022021-02-19 12:09:57368 var elem = fs[gracefulQueue].shift()
Yang Guo4fd355c2019-09-19 08:59:03369 if (elem) {
370 debug('RETRY', elem[0].name, elem[1])
371 elem[0].apply(null, elem[1])
372 }
373}