Skip to content

Commit 2a9a617

Browse files
committed
Support global patching
This adds support for patching the global fs object, which is occasionally required at the application level when many other modules throughout the system are set to use require('fs') instead of require('graceful-fs'). Additionally, the fs builtin 'close()' and 'closeSync()' methods are instrumented, so that a retry() is attempted on any close, rather than only a close() by that particular instance of graceful-fs.
1 parent 91759a3 commit 2a9a617

File tree

5 files changed

+377
-321
lines changed

5 files changed

+377
-321
lines changed

graceful-fs.js

+135-116
Original file line numberDiff line numberDiff line change
@@ -1,153 +1,172 @@
11
var fs = require('fs')
2-
module.exports = require('./fs.js')
3-
4-
// fix up some busted stuff, mostly on windows and old nodes
5-
require('./polyfills.js')
6-
7-
module.exports.open = open
8-
9-
if (process.version.substr(0, 4) === 'v0.8') {
10-
var legacy = require('./legacy-streams.js')
11-
ReadStream = legacy.ReadStream
12-
WriteStream = legacy.WriteStream
13-
}
14-
15-
module.exports.FileReadStream = ReadStream; // Legacy name.
16-
module.exports.FileWriteStream = WriteStream; // Legacy name.
17-
module.exports.ReadStream = ReadStream
18-
module.exports.WriteStream = WriteStream
19-
module.exports.close = close
20-
module.exports.closeSync = closeSync
21-
module.exports.createReadStream = createReadStream
22-
module.exports.createWriteStream = createWriteStream
23-
module.exports.readFile = readFile
24-
module.exports.readdir = readdir
25-
26-
ReadStream.prototype = Object.create(fs.ReadStream.prototype)
27-
ReadStream.prototype.open = ReadStream$open
28-
29-
WriteStream.prototype = Object.create(fs.WriteStream.prototype)
30-
WriteStream.prototype.open = WriteStream$open
31-
2+
var polyfills = require('./polyfills.js')
3+
var legacy = require('./legacy-streams.js')
324
var queue = []
335

34-
function ReadStream (path, options) {
35-
if (this instanceof ReadStream)
36-
return fs.ReadStream.apply(this, arguments), this
37-
else
38-
return ReadStream.apply(Object.create(ReadStream.prototype), arguments)
39-
}
40-
41-
function ReadStream$open () {
42-
var that = this
43-
open(that.path, that.flags, that.mode, function (err, fd) {
44-
if (err) {
45-
if (that.autoClose)
46-
that.destroy()
476

48-
that.emit('error', err)
49-
} else {
50-
that.fd = fd
51-
that.emit('open', fd)
52-
that.read()
53-
}
54-
})
55-
}
56-
57-
function WriteStream (path, options) {
58-
if (this instanceof WriteStream)
59-
return fs.WriteStream.apply(this, arguments), this
60-
else
61-
return WriteStream.apply(Object.create(WriteStream.prototype), arguments)
7+
module.exports = patch(require('./fs.js'))
8+
if (process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH) {
9+
module.exports = patch(fs)
6210
}
6311

64-
function WriteStream$open () {
65-
var that = this
66-
open(that.path, that.flags, that.mode, function (err, fd) {
67-
if (err) {
68-
that.destroy()
69-
that.emit('error', err)
70-
} else {
71-
that.fd = fd
72-
that.emit('open', fd)
73-
}
74-
})
75-
}
76-
77-
function close (fd, cb) {
78-
return fs.close(fd, function (err) {
12+
// Always patch fs.close/closeSync, because we want to
13+
// retry() whenever a close happens *anywhere* in the program.
14+
// This is essential when multiple graceful-fs instances are
15+
// in play at the same time.
16+
fs.close = (function (fs$close) { return function (fd, cb) {
17+
return fs$close.call(fs, fd, function (err) {
7918
if (!err)
8019
retry()
8120

8221
if (typeof cb === 'function')
8322
cb.apply(this, arguments)
8423
})
85-
}
24+
}})(fs.close)
8625

87-
function closeSync () {
26+
fs.closeSync = (function (fs$closeSync) { return function (fd) {
8827
// Note that graceful-fs also retries when fs.closeSync() fails.
8928
// Looks like a bug to me, although it's probably a harmless one.
90-
var rval = fs.closeSync.apply(fs, arguments)
29+
var rval = fs$closeSync.apply(fs, arguments)
9130
retry()
9231
return rval
93-
}
32+
}})(fs.closeSync)
33+
34+
function patch (fs) {
35+
// Everything that references the open() function needs to be in here
36+
polyfills(fs)
37+
fs.gracefulify = patch
38+
fs.FileReadStream = ReadStream; // Legacy name.
39+
fs.FileWriteStream = WriteStream; // Legacy name.
40+
fs.createReadStream = createReadStream
41+
fs.createWriteStream = createWriteStream
42+
var fs$readFile = fs.readFile
43+
fs.readFile = readFile
44+
function readFile (path, options, cb) {
45+
if (typeof options === 'function')
46+
cb = options, options = null
47+
48+
return go(path, options, cb)
49+
50+
function go (path, flags, mode, cb) {
51+
return fs$readFile(path, options, function (err, fd) {
52+
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
53+
queue.push([go, [path, options, cb]])
54+
else if (typeof cb === 'function')
55+
cb.apply(this, arguments)
56+
})
57+
}
58+
}
9459

95-
function createReadStream (path, options) {
96-
return new ReadStream(path, options)
97-
}
9860

99-
function createWriteStream (path, options) {
100-
return new WriteStream(path, options)
101-
}
61+
var fs$readdir = fs.readdir
62+
fs.readdir = readdir
63+
function readdir (path, cb) {
64+
return go(path, cb)
65+
66+
function go () {
67+
return fs$readdir(path, function (err, files) {
68+
if (files && files.sort)
69+
files.sort(); // Backwards compatibility with graceful-fs.
10270

103-
function open (path, flags, mode, cb) {
104-
if (typeof mode === 'function')
105-
cb = mode, mode = null
71+
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
72+
queue.push([go, [path, cb]])
73+
else if (typeof cb === 'function')
74+
cb.apply(this, arguments)
75+
})
76+
}
77+
}
10678

107-
return go(path, flags, mode, cb)
10879

109-
function go (path, flags, mode, cb) {
110-
return fs.open(path, flags, mode, function (err, fd) {
111-
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
112-
queue.push([go, [path, flags, mode, cb]])
113-
else if (typeof cb === 'function')
114-
cb.apply(this, arguments)
115-
})
80+
if (process.version.substr(0, 4) === 'v0.8') {
81+
var legStreams = legacy(fs)
82+
ReadStream = legStreams.ReadStream
83+
WriteStream = legStreams.WriteStream
11684
}
117-
}
11885

119-
function readFile (path, options, cb) {
120-
if (typeof options === 'function')
121-
cb = options, options = null
86+
var fs$ReadStream = fs.ReadStream
87+
ReadStream.prototype = Object.create(fs$ReadStream.prototype)
88+
ReadStream.prototype.open = ReadStream$open
12289

123-
return go(path, options, cb)
90+
var fs$WriteStream = fs.WriteStream
91+
WriteStream.prototype = Object.create(fs$WriteStream.prototype)
92+
WriteStream.prototype.open = WriteStream$open
12493

125-
function go (path, flags, mode, cb) {
126-
return fs.readFile(path, options, function (err, fd) {
127-
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
128-
queue.push([go, [path, options, cb]])
129-
else if (typeof cb === 'function')
130-
cb.apply(this, arguments)
131-
})
94+
fs.ReadStream = ReadStream
95+
fs.WriteStream = WriteStream
96+
97+
function ReadStream (path, options) {
98+
if (this instanceof ReadStream)
99+
return fs$ReadStream.apply(this, arguments), this
100+
else
101+
return ReadStream.apply(Object.create(ReadStream.prototype), arguments)
132102
}
133-
}
134103

135-
function readdir (path, cb) {
136-
return go(path, cb)
104+
function ReadStream$open () {
105+
var that = this
106+
open(that.path, that.flags, that.mode, function (err, fd) {
107+
if (err) {
108+
if (that.autoClose)
109+
that.destroy()
110+
111+
that.emit('error', err)
112+
} else {
113+
that.fd = fd
114+
that.emit('open', fd)
115+
that.read()
116+
}
117+
})
118+
}
137119

138-
function go () {
139-
return fs.readdir(path, function (err, files) {
140-
if (files && files.sort)
141-
files.sort(); // Backwards compatibility with graceful-fs.
120+
function WriteStream (path, options) {
121+
if (this instanceof WriteStream)
122+
return fs$WriteStream.apply(this, arguments), this
123+
else
124+
return WriteStream.apply(Object.create(WriteStream.prototype), arguments)
125+
}
142126

143-
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
144-
queue.push([go, [path, cb]])
145-
else if (typeof cb === 'function')
146-
cb.apply(this, arguments)
127+
function WriteStream$open () {
128+
var that = this
129+
open(that.path, that.flags, that.mode, function (err, fd) {
130+
if (err) {
131+
that.destroy()
132+
that.emit('error', err)
133+
} else {
134+
that.fd = fd
135+
that.emit('open', fd)
136+
}
147137
})
148138
}
139+
140+
function createReadStream (path, options) {
141+
return new ReadStream(path, options)
142+
}
143+
144+
function createWriteStream (path, options) {
145+
return new WriteStream(path, options)
146+
}
147+
148+
var fs$open = fs.open
149+
fs.open = open
150+
function open (path, flags, mode, cb) {
151+
if (typeof mode === 'function')
152+
cb = mode, mode = null
153+
154+
return go(path, flags, mode, cb)
155+
156+
function go (path, flags, mode, cb) {
157+
return fs$open(path, flags, mode, function (err, fd) {
158+
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
159+
queue.push([go, [path, flags, mode, cb]])
160+
else if (typeof cb === 'function')
161+
cb.apply(this, arguments)
162+
})
163+
}
164+
}
165+
166+
return fs
149167
}
150168

169+
151170
function retry () {
152171
var elem = queue.shift()
153172
if (elem)

0 commit comments

Comments
 (0)