1var constants = require('constants') 2 3var origCwd = process.cwd 4var cwd = null 5 6var platform = process.env.GRACEFUL_FS_PLATFORM || process.platform 7 8process.cwd = function() { 9 if (!cwd) 10 cwd = origCwd.call(process) 11 return cwd 12} 13try { 14 process.cwd() 15} catch (er) {} 16 17var chdir = process.chdir 18process.chdir = function(d) { 19 cwd = null 20 chdir.call(process, d) 21} 22 23module.exports = patch 24 25function 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} 343