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