1 |
3a515b92
|
cagy
|
var constants = require('constants')
|
2 |
|
|
|
3 |
|
|
var origCwd = process.cwd
|
4 |
|
|
var cwd = null
|
5 |
|
|
|
6 |
|
|
var platform = process.env.GRACEFUL_FS_PLATFORM || process.platform
|
7 |
|
|
|
8 |
|
|
process.cwd = function() {
|
9 |
|
|
if (!cwd)
|
10 |
|
|
cwd = origCwd.call(process)
|
11 |
|
|
return cwd
|
12 |
|
|
}
|
13 |
|
|
try {
|
14 |
|
|
process.cwd()
|
15 |
|
|
} catch (er) {}
|
16 |
|
|
|
17 |
|
|
var chdir = process.chdir
|
18 |
|
|
process.chdir = function(d) {
|
19 |
|
|
cwd = null
|
20 |
|
|
chdir.call(process, d)
|
21 |
|
|
}
|
22 |
|
|
|
23 |
|
|
module.exports = patch
|
24 |
|
|
|
25 |
|
|
function patch (fs) {
|
26 |
|
|
// (re-)implement some things that are known busted or missing.
|
27 |
|
|
|
28 |
|
|
// lchmod, broken prior to 0.6.2
|
29 |
|
|
// back-port the fix here.
|
30 |
|
|
if (constants.hasOwnProperty('O_SYMLINK') &&
|
31 |
|
|
process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) {
|
32 |
|
|
patchLchmod(fs)
|
33 |
|
|
}
|
34 |
|
|
|
35 |
|
|
// lutimes implementation, or no-op
|
36 |
|
|
if (!fs.lutimes) {
|
37 |
|
|
patchLutimes(fs)
|
38 |
|
|
}
|
39 |
|
|
|
40 |
|
|
// https://github.com/isaacs/node-graceful-fs/issues/4
|
41 |
|
|
// Chown should not fail on einval or eperm if non-root.
|
42 |
|
|
// It should not fail on enosys ever, as this just indicates
|
43 |
|
|
// that a fs doesn't support the intended operation.
|
44 |
|
|
|
45 |
|
|
fs.chown = chownFix(fs.chown)
|
46 |
|
|
fs.fchown = chownFix(fs.fchown)
|
47 |
|
|
fs.lchown = chownFix(fs.lchown)
|
48 |
|
|
|
49 |
|
|
fs.chmod = chmodFix(fs.chmod)
|
50 |
|
|
fs.fchmod = chmodFix(fs.fchmod)
|
51 |
|
|
fs.lchmod = chmodFix(fs.lchmod)
|
52 |
|
|
|
53 |
|
|
fs.chownSync = chownFixSync(fs.chownSync)
|
54 |
|
|
fs.fchownSync = chownFixSync(fs.fchownSync)
|
55 |
|
|
fs.lchownSync = chownFixSync(fs.lchownSync)
|
56 |
|
|
|
57 |
|
|
fs.chmodSync = chmodFixSync(fs.chmodSync)
|
58 |
|
|
fs.fchmodSync = chmodFixSync(fs.fchmodSync)
|
59 |
|
|
fs.lchmodSync = chmodFixSync(fs.lchmodSync)
|
60 |
|
|
|
61 |
|
|
fs.stat = statFix(fs.stat)
|
62 |
|
|
fs.fstat = statFix(fs.fstat)
|
63 |
|
|
fs.lstat = statFix(fs.lstat)
|
64 |
|
|
|
65 |
|
|
fs.statSync = statFixSync(fs.statSync)
|
66 |
|
|
fs.fstatSync = statFixSync(fs.fstatSync)
|
67 |
|
|
fs.lstatSync = statFixSync(fs.lstatSync)
|
68 |
|
|
|
69 |
|
|
// if lchmod/lchown do not exist, then make them no-ops
|
70 |
|
|
if (!fs.lchmod) {
|
71 |
|
|
fs.lchmod = function (path, mode, cb) {
|
72 |
|
|
if (cb) process.nextTick(cb)
|
73 |
|
|
}
|
74 |
|
|
fs.lchmodSync = function () {}
|
75 |
|
|
}
|
76 |
|
|
if (!fs.lchown) {
|
77 |
|
|
fs.lchown = function (path, uid, gid, cb) {
|
78 |
|
|
if (cb) process.nextTick(cb)
|
79 |
|
|
}
|
80 |
|
|
fs.lchownSync = function () {}
|
81 |
|
|
}
|
82 |
|
|
|
83 |
|
|
// on Windows, A/V software can lock the directory, causing this
|
84 |
|
|
// to fail with an EACCES or EPERM if the directory contains newly
|
85 |
|
|
// created files. Try again on failure, for up to 60 seconds.
|
86 |
|
|
|
87 |
|
|
// Set the timeout this long because some Windows Anti-Virus, such as Parity
|
88 |
|
|
// bit9, may lock files for up to a minute, causing npm package install
|
89 |
|
|
// failures. Also, take care to yield the scheduler. Windows scheduling gives
|
90 |
|
|
// CPU to a busy looping process, which can cause the program causing the lock
|
91 |
|
|
// contention to be starved of CPU by node, so the contention doesn't resolve.
|
92 |
|
|
if (platform === "win32") {
|
93 |
|
|
fs.rename = (function (fs$rename) { return function (from, to, cb) {
|
94 |
|
|
var start = Date.now()
|
95 |
|
|
var backoff = 0;
|
96 |
|
|
fs$rename(from, to, function CB (er) {
|
97 |
|
|
if (er
|
98 |
|
|
&& (er.code === "EACCES" || er.code === "EPERM")
|
99 |
|
|
&& Date.now() - start < 60000) {
|
100 |
|
|
setTimeout(function() {
|
101 |
|
|
fs.stat(to, function (stater, st) {
|
102 |
|
|
if (stater && stater.code === "ENOENT")
|
103 |
|
|
fs$rename(from, to, CB);
|
104 |
|
|
else
|
105 |
|
|
cb(er)
|
106 |
|
|
})
|
107 |
|
|
}, backoff)
|
108 |
|
|
if (backoff < 100)
|
109 |
|
|
backoff += 10;
|
110 |
|
|
return;
|
111 |
|
|
}
|
112 |
|
|
if (cb) cb(er)
|
113 |
|
|
})
|
114 |
|
|
}})(fs.rename)
|
115 |
|
|
}
|
116 |
|
|
|
117 |
|
|
// if read() returns EAGAIN, then just try it again.
|
118 |
|
|
fs.read = (function (fs$read) {
|
119 |
|
|
function read (fd, buffer, offset, length, position, callback_) {
|
120 |
|
|
var callback
|
121 |
|
|
if (callback_ && typeof callback_ === 'function') {
|
122 |
|
|
var eagCounter = 0
|
123 |
|
|
callback = function (er, _, __) {
|
124 |
|
|
if (er && er.code === 'EAGAIN' && eagCounter < 10) {
|
125 |
|
|
eagCounter ++
|
126 |
|
|
return fs$read.call(fs, fd, buffer, offset, length, position, callback)
|
127 |
|
|
}
|
128 |
|
|
callback_.apply(this, arguments)
|
129 |
|
|
}
|
130 |
|
|
}
|
131 |
|
|
return fs$read.call(fs, fd, buffer, offset, length, position, callback)
|
132 |
|
|
}
|
133 |
|
|
|
134 |
|
|
// This ensures `util.promisify` works as it does for native `fs.read`.
|
135 |
|
|
read.__proto__ = fs$read
|
136 |
|
|
return read
|
137 |
|
|
})(fs.read)
|
138 |
|
|
|
139 |
|
|
fs.readSync = (function (fs$readSync) { return function (fd, buffer, offset, length, position) {
|
140 |
|
|
var eagCounter = 0
|
141 |
|
|
while (true) {
|
142 |
|
|
try {
|
143 |
|
|
return fs$readSync.call(fs, fd, buffer, offset, length, position)
|
144 |
|
|
} catch (er) {
|
145 |
|
|
if (er.code === 'EAGAIN' && eagCounter < 10) {
|
146 |
|
|
eagCounter ++
|
147 |
|
|
continue
|
148 |
|
|
}
|
149 |
|
|
throw er
|
150 |
|
|
}
|
151 |
|
|
}
|
152 |
|
|
}})(fs.readSync)
|
153 |
|
|
|
154 |
|
|
function patchLchmod (fs) {
|
155 |
|
|
fs.lchmod = function (path, mode, callback) {
|
156 |
|
|
fs.open( path
|
157 |
|
|
, constants.O_WRONLY | constants.O_SYMLINK
|
158 |
|
|
, mode
|
159 |
|
|
, function (err, fd) {
|
160 |
|
|
if (err) {
|
161 |
|
|
if (callback) callback(err)
|
162 |
|
|
return
|
163 |
|
|
}
|
164 |
|
|
// prefer to return the chmod error, if one occurs,
|
165 |
|
|
// but still try to close, and report closing errors if they occur.
|
166 |
|
|
fs.fchmod(fd, mode, function (err) {
|
167 |
|
|
fs.close(fd, function(err2) {
|
168 |
|
|
if (callback) callback(err || err2)
|
169 |
|
|
})
|
170 |
|
|
})
|
171 |
|
|
})
|
172 |
|
|
}
|
173 |
|
|
|
174 |
|
|
fs.lchmodSync = function (path, mode) {
|
175 |
|
|
var fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK, mode)
|
176 |
|
|
|
177 |
|
|
// prefer to return the chmod error, if one occurs,
|
178 |
|
|
// but still try to close, and report closing errors if they occur.
|
179 |
|
|
var threw = true
|
180 |
|
|
var ret
|
181 |
|
|
try {
|
182 |
|
|
ret = fs.fchmodSync(fd, mode)
|
183 |
|
|
threw = false
|
184 |
|
|
} finally {
|
185 |
|
|
if (threw) {
|
186 |
|
|
try {
|
187 |
|
|
fs.closeSync(fd)
|
188 |
|
|
} catch (er) {}
|
189 |
|
|
} else {
|
190 |
|
|
fs.closeSync(fd)
|
191 |
|
|
}
|
192 |
|
|
}
|
193 |
|
|
return ret
|
194 |
|
|
}
|
195 |
|
|
}
|
196 |
|
|
|
197 |
|
|
function patchLutimes (fs) {
|
198 |
|
|
if (constants.hasOwnProperty("O_SYMLINK")) {
|
199 |
|
|
fs.lutimes = function (path, at, mt, cb) {
|
200 |
|
|
fs.open(path, constants.O_SYMLINK, function (er, fd) {
|
201 |
|
|
if (er) {
|
202 |
|
|
if (cb) cb(er)
|
203 |
|
|
return
|
204 |
|
|
}
|
205 |
|
|
fs.futimes(fd, at, mt, function (er) {
|
206 |
|
|
fs.close(fd, function (er2) {
|
207 |
|
|
if (cb) cb(er || er2)
|
208 |
|
|
})
|
209 |
|
|
})
|
210 |
|
|
})
|
211 |
|
|
}
|
212 |
|
|
|
213 |
|
|
fs.lutimesSync = function (path, at, mt) {
|
214 |
|
|
var fd = fs.openSync(path, constants.O_SYMLINK)
|
215 |
|
|
var ret
|
216 |
|
|
var threw = true
|
217 |
|
|
try {
|
218 |
|
|
ret = fs.futimesSync(fd, at, mt)
|
219 |
|
|
threw = false
|
220 |
|
|
} finally {
|
221 |
|
|
if (threw) {
|
222 |
|
|
try {
|
223 |
|
|
fs.closeSync(fd)
|
224 |
|
|
} catch (er) {}
|
225 |
|
|
} else {
|
226 |
|
|
fs.closeSync(fd)
|
227 |
|
|
}
|
228 |
|
|
}
|
229 |
|
|
return ret
|
230 |
|
|
}
|
231 |
|
|
|
232 |
|
|
} else {
|
233 |
|
|
fs.lutimes = function (_a, _b, _c, cb) { if (cb) process.nextTick(cb) }
|
234 |
|
|
fs.lutimesSync = function () {}
|
235 |
|
|
}
|
236 |
|
|
}
|
237 |
|
|
|
238 |
|
|
function chmodFix (orig) {
|
239 |
|
|
if (!orig) return orig
|
240 |
|
|
return function (target, mode, cb) {
|
241 |
|
|
return orig.call(fs, target, mode, function (er) {
|
242 |
|
|
if (chownErOk(er)) er = null
|
243 |
|
|
if (cb) cb.apply(this, arguments)
|
244 |
|
|
})
|
245 |
|
|
}
|
246 |
|
|
}
|
247 |
|
|
|
248 |
|
|
function chmodFixSync (orig) {
|
249 |
|
|
if (!orig) return orig
|
250 |
|
|
return function (target, mode) {
|
251 |
|
|
try {
|
252 |
|
|
return orig.call(fs, target, mode)
|
253 |
|
|
} catch (er) {
|
254 |
|
|
if (!chownErOk(er)) throw er
|
255 |
|
|
}
|
256 |
|
|
}
|
257 |
|
|
}
|
258 |
|
|
|
259 |
|
|
|
260 |
|
|
function chownFix (orig) {
|
261 |
|
|
if (!orig) return orig
|
262 |
|
|
return function (target, uid, gid, cb) {
|
263 |
|
|
return orig.call(fs, target, uid, gid, function (er) {
|
264 |
|
|
if (chownErOk(er)) er = null
|
265 |
|
|
if (cb) cb.apply(this, arguments)
|
266 |
|
|
})
|
267 |
|
|
}
|
268 |
|
|
}
|
269 |
|
|
|
270 |
|
|
function chownFixSync (orig) {
|
271 |
|
|
if (!orig) return orig
|
272 |
|
|
return function (target, uid, gid) {
|
273 |
|
|
try {
|
274 |
|
|
return orig.call(fs, target, uid, gid)
|
275 |
|
|
} catch (er) {
|
276 |
|
|
if (!chownErOk(er)) throw er
|
277 |
|
|
}
|
278 |
|
|
}
|
279 |
|
|
}
|
280 |
|
|
|
281 |
|
|
function statFix (orig) {
|
282 |
|
|
if (!orig) return orig
|
283 |
|
|
// Older versions of Node erroneously returned signed integers for
|
284 |
|
|
// uid + gid.
|
285 |
|
|
return function (target, options, cb) {
|
286 |
|
|
if (typeof options === 'function') {
|
287 |
|
|
cb = options
|
288 |
|
|
options = null
|
289 |
|
|
}
|
290 |
|
|
function callback (er, stats) {
|
291 |
|
|
if (stats) {
|
292 |
|
|
if (stats.uid < 0) stats.uid += 0x100000000
|
293 |
|
|
if (stats.gid < 0) stats.gid += 0x100000000
|
294 |
|
|
}
|
295 |
|
|
if (cb) cb.apply(this, arguments)
|
296 |
|
|
}
|
297 |
|
|
return options ? orig.call(fs, target, options, callback)
|
298 |
|
|
: orig.call(fs, target, callback)
|
299 |
|
|
}
|
300 |
|
|
}
|
301 |
|
|
|
302 |
|
|
function statFixSync (orig) {
|
303 |
|
|
if (!orig) return orig
|
304 |
|
|
// Older versions of Node erroneously returned signed integers for
|
305 |
|
|
// uid + gid.
|
306 |
|
|
return function (target, options) {
|
307 |
|
|
var stats = options ? orig.call(fs, target, options)
|
308 |
|
|
: orig.call(fs, target)
|
309 |
|
|
if (stats.uid < 0) stats.uid += 0x100000000
|
310 |
|
|
if (stats.gid < 0) stats.gid += 0x100000000
|
311 |
|
|
return stats;
|
312 |
|
|
}
|
313 |
|
|
}
|
314 |
|
|
|
315 |
|
|
// ENOSYS means that the fs doesn't support the op. Just ignore
|
316 |
|
|
// that, because it doesn't matter.
|
317 |
|
|
//
|
318 |
|
|
// if there's no getuid, or if getuid() is something other
|
319 |
|
|
// than 0, and the error is EINVAL or EPERM, then just ignore
|
320 |
|
|
// it.
|
321 |
|
|
//
|
322 |
|
|
// This specific case is a silent failure in cp, install, tar,
|
323 |
|
|
// and most other unix tools that manage permissions.
|
324 |
|
|
//
|
325 |
|
|
// When running as root, or if other types of errors are
|
326 |
|
|
// encountered, then it's strict.
|
327 |
|
|
function chownErOk (er) {
|
328 |
|
|
if (!er)
|
329 |
|
|
return true
|
330 |
|
|
|
331 |
|
|
if (er.code === "ENOSYS")
|
332 |
|
|
return true
|
333 |
|
|
|
334 |
|
|
var nonroot = !process.getuid || process.getuid() !== 0
|
335 |
|
|
if (nonroot) {
|
336 |
|
|
if (er.code === "EINVAL" || er.code === "EPERM")
|
337 |
|
|
return true
|
338 |
|
|
}
|
339 |
|
|
|
340 |
|
|
return false
|
341 |
|
|
}
|
342 |
|
|
}
|