graceful-fs
Advanced tools
+228
| var fs = require('fs') | ||
| var constants = require('constants') | ||
| var origCwd = process.cwd | ||
| var cwd = null | ||
| process.cwd = function() { | ||
| if (!cwd) | ||
| cwd = origCwd.call(process) | ||
| return cwd | ||
| } | ||
| var chdir = process.chdir | ||
| process.chdir = function(d) { | ||
| cwd = null | ||
| chdir.call(process, d) | ||
| } | ||
| // (re-)implement some things that are known busted or missing. | ||
| // lchmod, broken prior to 0.6.2 | ||
| // back-port the fix here. | ||
| if (constants.hasOwnProperty('O_SYMLINK') && | ||
| process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) { | ||
| fs.lchmod = function (path, mode, callback) { | ||
| callback = callback || noop | ||
| fs.open( path | ||
| , constants.O_WRONLY | constants.O_SYMLINK | ||
| , mode | ||
| , function (err, fd) { | ||
| if (err) { | ||
| callback(err) | ||
| return | ||
| } | ||
| // prefer to return the chmod error, if one occurs, | ||
| // but still try to close, and report closing errors if they occur. | ||
| fs.fchmod(fd, mode, function (err) { | ||
| fs.close(fd, function(err2) { | ||
| callback(err || err2) | ||
| }) | ||
| }) | ||
| }) | ||
| } | ||
| fs.lchmodSync = function (path, mode) { | ||
| var fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK, mode) | ||
| // prefer to return the chmod error, if one occurs, | ||
| // but still try to close, and report closing errors if they occur. | ||
| var err, err2 | ||
| try { | ||
| var ret = fs.fchmodSync(fd, mode) | ||
| } catch (er) { | ||
| err = er | ||
| } | ||
| try { | ||
| fs.closeSync(fd) | ||
| } catch (er) { | ||
| err2 = er | ||
| } | ||
| if (err || err2) throw (err || err2) | ||
| return ret | ||
| } | ||
| } | ||
| // lutimes implementation, or no-op | ||
| if (!fs.lutimes) { | ||
| if (constants.hasOwnProperty("O_SYMLINK")) { | ||
| fs.lutimes = function (path, at, mt, cb) { | ||
| fs.open(path, constants.O_SYMLINK, function (er, fd) { | ||
| cb = cb || noop | ||
| if (er) return cb(er) | ||
| fs.futimes(fd, at, mt, function (er) { | ||
| fs.close(fd, function (er2) { | ||
| return cb(er || er2) | ||
| }) | ||
| }) | ||
| }) | ||
| } | ||
| fs.lutimesSync = function (path, at, mt) { | ||
| var fd = fs.openSync(path, constants.O_SYMLINK) | ||
| , err | ||
| , err2 | ||
| , ret | ||
| try { | ||
| var ret = fs.futimesSync(fd, at, mt) | ||
| } catch (er) { | ||
| err = er | ||
| } | ||
| try { | ||
| fs.closeSync(fd) | ||
| } catch (er) { | ||
| err2 = er | ||
| } | ||
| if (err || err2) throw (err || err2) | ||
| return ret | ||
| } | ||
| } else if (fs.utimensat && constants.hasOwnProperty("AT_SYMLINK_NOFOLLOW")) { | ||
| // maybe utimensat will be bound soonish? | ||
| fs.lutimes = function (path, at, mt, cb) { | ||
| fs.utimensat(path, at, mt, constants.AT_SYMLINK_NOFOLLOW, cb) | ||
| } | ||
| fs.lutimesSync = function (path, at, mt) { | ||
| return fs.utimensatSync(path, at, mt, constants.AT_SYMLINK_NOFOLLOW) | ||
| } | ||
| } else { | ||
| fs.lutimes = function (_a, _b, _c, cb) { process.nextTick(cb) } | ||
| fs.lutimesSync = function () {} | ||
| } | ||
| } | ||
| // https://github.com/isaacs/node-graceful-fs/issues/4 | ||
| // Chown should not fail on einval or eperm if non-root. | ||
| fs.chown = chownFix(fs.chown) | ||
| fs.fchown = chownFix(fs.fchown) | ||
| fs.lchown = chownFix(fs.lchown) | ||
| fs.chownSync = chownFixSync(fs.chownSync) | ||
| fs.fchownSync = chownFixSync(fs.fchownSync) | ||
| fs.lchownSync = chownFixSync(fs.lchownSync) | ||
| function chownFix (orig) { | ||
| if (!orig) return orig | ||
| return function (target, uid, gid, cb) { | ||
| return orig.call(fs, target, uid, gid, function (er, res) { | ||
| if (chownErOk(er)) er = null | ||
| cb(er, res) | ||
| }) | ||
| } | ||
| } | ||
| function chownFixSync (orig) { | ||
| if (!orig) return orig | ||
| return function (target, uid, gid) { | ||
| try { | ||
| return orig.call(fs, target, uid, gid) | ||
| } catch (er) { | ||
| if (!chownErOk(er)) throw er | ||
| } | ||
| } | ||
| } | ||
| function chownErOk (er) { | ||
| // if there's no getuid, or if getuid() is something other than 0, | ||
| // and the error is EINVAL or EPERM, then just ignore it. | ||
| // This specific case is a silent failure in cp, install, tar, | ||
| // and most other unix tools that manage permissions. | ||
| // When running as root, or if other types of errors are encountered, | ||
| // then it's strict. | ||
| if (!er || (!process.getuid || process.getuid() !== 0) | ||
| && (er.code === "EINVAL" || er.code === "EPERM")) return true | ||
| } | ||
| // if lchmod/lchown do not exist, then make them no-ops | ||
| if (!fs.lchmod) { | ||
| fs.lchmod = function (path, mode, cb) { | ||
| process.nextTick(cb) | ||
| } | ||
| fs.lchmodSync = function () {} | ||
| } | ||
| if (!fs.lchown) { | ||
| fs.lchown = function (path, uid, gid, cb) { | ||
| process.nextTick(cb) | ||
| } | ||
| fs.lchownSync = function () {} | ||
| } | ||
| // on Windows, A/V software can lock the directory, causing this | ||
| // to fail with an EACCES or EPERM if the directory contains newly | ||
| // created files. Try again on failure, for up to 1 second. | ||
| if (process.platform === "win32") { | ||
| var rename_ = fs.rename | ||
| fs.rename = function rename (from, to, cb) { | ||
| var start = Date.now() | ||
| rename_(from, to, function CB (er) { | ||
| if (er | ||
| && (er.code === "EACCES" || er.code === "EPERM") | ||
| && Date.now() - start < 1000) { | ||
| return rename_(from, to, CB) | ||
| } | ||
| cb(er) | ||
| }) | ||
| } | ||
| } | ||
| // if read() returns EAGAIN, then just try it again. | ||
| var read = fs.read | ||
| fs.read = function (fd, buffer, offset, length, position, callback_) { | ||
| var callback | ||
| if (callback_ && typeof callback_ === 'function') { | ||
| var eagCounter = 0 | ||
| callback = function (er, _, __) { | ||
| if (er && er.code === 'EAGAIN' && eagCounter < 10) { | ||
| eagCounter ++ | ||
| return read.call(fs, fd, buffer, offset, length, position, callback) | ||
| } | ||
| callback_.apply(this, arguments) | ||
| } | ||
| } | ||
| return read.call(fs, fd, buffer, offset, length, position, callback) | ||
| } | ||
| var readSync = fs.readSync | ||
| fs.readSync = function (fd, buffer, offset, length, position) { | ||
| var eagCounter = 0 | ||
| while (true) { | ||
| try { | ||
| return readSync.call(fs, fd, buffer, offset, length, position) | ||
| } catch (er) { | ||
| if (er.code === 'EAGAIN' && eagCounter < 10) { | ||
| eagCounter ++ | ||
| continue | ||
| } | ||
| throw er | ||
| } | ||
| } | ||
| } | ||
+94
-384
@@ -1,217 +0,102 @@ | ||
| // this keeps a queue of opened file descriptors, and will make | ||
| // fs operations wait until some have closed before trying to open more. | ||
| // Monkey-patching the fs module. | ||
| // It's ugly, but there is simply no other way to do this. | ||
| var fs = module.exports = require('fs') | ||
| var fs = exports = module.exports = {} | ||
| fs._originalFs = require("fs") | ||
| var assert = require('assert') | ||
| Object.getOwnPropertyNames(fs._originalFs).forEach(function(prop) { | ||
| var desc = Object.getOwnPropertyDescriptor(fs._originalFs, prop) | ||
| Object.defineProperty(fs, prop, desc) | ||
| }) | ||
| // fix up some busted stuff, mostly on windows and old nodes | ||
| require('./polyfills.js') | ||
| var queue = [] | ||
| , constants = require("constants") | ||
| // The EMFILE enqueuing stuff | ||
| fs._curOpen = 0 | ||
| var util = require('util') | ||
| fs.MIN_MAX_OPEN = 64 | ||
| fs.MAX_OPEN = 1024 | ||
| function noop () {} | ||
| // prevent EMFILE errors | ||
| function OpenReq (path, flags, mode, cb) { | ||
| this.path = path | ||
| this.flags = flags | ||
| this.mode = mode | ||
| this.cb = cb | ||
| var debug = noop | ||
| var util = require('util') | ||
| if (util.debuglog) | ||
| debug = util.debuglog('gfs') | ||
| else if (/\bgfs\b/i.test(process.env.NODE_DEBUG || '')) | ||
| debug = function() { | ||
| var m = util.format.apply(util, arguments) | ||
| m = 'GFS: ' + m.split(/\n/).join('\nGFS: ') | ||
| console.error(m) | ||
| } | ||
| if (/\bgfs\b/i.test(process.env.NODE_DEBUG || '')) { | ||
| process.on('exit', function() { | ||
| debug('fds', fds) | ||
| debug(queue) | ||
| assert.equal(queue.length, 0) | ||
| }) | ||
| } | ||
| function noop () {} | ||
| fs.open = gracefulOpen | ||
| var originalOpen = fs.open | ||
| fs.open = open | ||
| function gracefulOpen (path, flags, mode, cb) { | ||
| function open(path, flags, mode, cb) { | ||
| if (typeof mode === "function") cb = mode, mode = null | ||
| if (typeof cb !== "function") cb = noop | ||
| if (fs._curOpen >= fs.MAX_OPEN) { | ||
| queue.push(new OpenReq(path, flags, mode, cb)) | ||
| setTimeout(flush) | ||
| return | ||
| } | ||
| open(path, flags, mode, function (er, fd) { | ||
| if (er && er.code === "EMFILE" && fs._curOpen > fs.MIN_MAX_OPEN) { | ||
| // that was too many. reduce max, get back in queue. | ||
| // this should only happen once in a great while, and only | ||
| // if the ulimit -n is set lower than 1024. | ||
| fs.MAX_OPEN = fs._curOpen - 1 | ||
| return fs.open(path, flags, mode, cb) | ||
| } | ||
| cb(er, fd) | ||
| }) | ||
| new OpenReq(path, flags, mode, cb) | ||
| } | ||
| function open (path, flags, mode, cb) { | ||
| cb = cb || noop | ||
| fs._curOpen ++ | ||
| fs._originalFs.open.call(fs, path, flags, mode, function (er, fd) { | ||
| if (er) onclose() | ||
| cb(er, fd) | ||
| }) | ||
| function OpenReq(path, flags, mode, cb) { | ||
| this.path = path | ||
| this.flags = flags | ||
| this.mode = mode | ||
| this.cb = cb | ||
| Req.call(this) | ||
| } | ||
| fs.openSync = function (path, flags, mode) { | ||
| var ret | ||
| ret = fs._originalFs.openSync.call(fs, path, flags, mode) | ||
| fs._curOpen ++ | ||
| return ret | ||
| } | ||
| util.inherits(OpenReq, Req) | ||
| function onclose () { | ||
| fs._curOpen -- | ||
| flush() | ||
| OpenReq.prototype.process = function() { | ||
| originalOpen.call(fs, this.path, this.flags, this.mode, this.done) | ||
| } | ||
| function flush () { | ||
| while (fs._curOpen < fs.MAX_OPEN) { | ||
| var req = queue.shift() | ||
| if (!req) return | ||
| switch (req.constructor.name) { | ||
| case 'OpenReq': | ||
| open(req.path, req.flags || "r", req.mode || 0777, req.cb) | ||
| break | ||
| case 'ReaddirReq': | ||
| readdir(req.path, req.cb) | ||
| break | ||
| case 'ReadFileReq': | ||
| readFile(req.path, req.options, req.cb) | ||
| break | ||
| case 'WriteFileReq': | ||
| writeFile(req.path, req.data, req.options, req.cb) | ||
| break | ||
| default: | ||
| throw new Error('Unknown req type: ' + req.constructor.name) | ||
| } | ||
| } | ||
| var fds = {} | ||
| OpenReq.prototype.done = function(er, fd) { | ||
| debug('open done', er, fd) | ||
| if (fd) | ||
| fds['fd' + fd] = this.path | ||
| Req.prototype.done.call(this, er, fd) | ||
| } | ||
| fs.close = function (fd, cb) { | ||
| cb = cb || noop | ||
| fs._originalFs.close.call(fs, fd, function (er) { | ||
| onclose() | ||
| cb(er) | ||
| }) | ||
| } | ||
| fs.closeSync = function (fd) { | ||
| try { | ||
| return fs._originalFs.closeSync.call(fs, fd) | ||
| } finally { | ||
| onclose() | ||
| } | ||
| } | ||
| var originalReaddir = fs.readdir | ||
| fs.readdir = readdir | ||
| // readdir takes a fd as well. | ||
| // however, the sync version closes it right away, so | ||
| // there's no need to wrap. | ||
| // It would be nice to catch when it throws an EMFILE, | ||
| // but that's relatively rare anyway. | ||
| fs.readdir = gracefulReaddir | ||
| function gracefulReaddir (path, cb) { | ||
| if (fs._curOpen >= fs.MAX_OPEN) { | ||
| queue.push(new ReaddirReq(path, cb)) | ||
| setTimeout(flush) | ||
| return | ||
| } | ||
| readdir(path, function (er, files) { | ||
| if (er && er.code === "EMFILE" && fs._curOpen > fs.MIN_MAX_OPEN) { | ||
| fs.MAX_OPEN = fs._curOpen - 1 | ||
| return fs.readdir(path, cb) | ||
| } | ||
| cb(er, files) | ||
| }) | ||
| function readdir(path, cb) { | ||
| if (typeof cb !== "function") cb = noop | ||
| new ReaddirReq(path, cb) | ||
| } | ||
| function readdir (path, cb) { | ||
| cb = cb || noop | ||
| fs._curOpen ++ | ||
| fs._originalFs.readdir.call(fs, path, function (er, files) { | ||
| onclose() | ||
| cb(er, files) | ||
| }) | ||
| } | ||
| function ReaddirReq (path, cb) { | ||
| function ReaddirReq(path, cb) { | ||
| this.path = path | ||
| this.cb = cb | ||
| Req.call(this) | ||
| } | ||
| util.inherits(ReaddirReq, Req) | ||
| fs.readFile = gracefulReadFile | ||
| function gracefulReadFile(path, options, cb) { | ||
| if (typeof options === "function") cb = options, options = null | ||
| if (typeof cb !== "function") cb = noop | ||
| if (fs._curOpen >= fs.MAX_OPEN) { | ||
| queue.push(new ReadFileReq(path, options, cb)) | ||
| setTimeout(flush) | ||
| return | ||
| } | ||
| readFile(path, options, function (er, data) { | ||
| if (er && er.code === "EMFILE" && fs._curOpen > fs.MIN_MAX_OPEN) { | ||
| fs.MAX_OPEN = fs._curOpen - 1 | ||
| return fs.readFile(path, options, cb) | ||
| } | ||
| cb(er, data) | ||
| }) | ||
| ReaddirReq.prototype.process = function() { | ||
| originalReaddir.call(fs, this.path, this.done) | ||
| } | ||
| function readFile (path, options, cb) { | ||
| cb = cb || noop | ||
| fs._curOpen ++ | ||
| fs._originalFs.readFile.call(fs, path, options, function (er, data) { | ||
| onclose() | ||
| cb(er, data) | ||
| }) | ||
| ReaddirReq.prototype.done = function(er, files) { | ||
| Req.prototype.done.call(this, er, files) | ||
| onclose() | ||
| } | ||
| function ReadFileReq (path, options, cb) { | ||
| this.path = path | ||
| this.options = options | ||
| this.cb = cb | ||
| } | ||
| var originalClose = fs.close | ||
| fs.close = close | ||
| fs.writeFile = gracefulWriteFile | ||
| function gracefulWriteFile(path, data, options, cb) { | ||
| if (typeof options === "function") cb = options, options = null | ||
| function close (fd, cb) { | ||
| debug('close', fd) | ||
| if (typeof cb !== "function") cb = noop | ||
| if (fs._curOpen >= fs.MAX_OPEN) { | ||
| queue.push(new WriteFileReq(path, data, options, cb)) | ||
| setTimeout(flush) | ||
| return | ||
| } | ||
| writeFile(path, data, options, function (er) { | ||
| if (er && er.code === "EMFILE" && fs._curOpen > fs.MIN_MAX_OPEN) { | ||
| fs.MAX_OPEN = fs._curOpen - 1 | ||
| return fs.writeFile(path, data, options, cb) | ||
| } | ||
| cb(er) | ||
| }) | ||
| } | ||
| function writeFile (path, data, options, cb) { | ||
| cb = cb || noop | ||
| fs._curOpen ++ | ||
| fs._originalFs.writeFile.call(fs, path, data, options, function (er) { | ||
| delete fds['fd' + fd] | ||
| originalClose.call(fs, fd, function(er) { | ||
| onclose() | ||
@@ -222,222 +107,47 @@ cb(er) | ||
| function WriteFileReq (path, data, options, cb) { | ||
| this.path = path | ||
| this.data = data | ||
| this.options = options | ||
| this.cb = cb | ||
| } | ||
| var originalCloseSync = fs.closeSync | ||
| fs.closeSync = closeSync | ||
| // (re-)implement some things that are known busted or missing. | ||
| var constants = require("constants") | ||
| // lchmod, broken prior to 0.6.2 | ||
| // back-port the fix here. | ||
| if (constants.hasOwnProperty('O_SYMLINK') && | ||
| process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) { | ||
| fs.lchmod = function (path, mode, callback) { | ||
| callback = callback || noop | ||
| fs.open( path | ||
| , constants.O_WRONLY | constants.O_SYMLINK | ||
| , mode | ||
| , function (err, fd) { | ||
| if (err) { | ||
| callback(err) | ||
| return | ||
| } | ||
| // prefer to return the chmod error, if one occurs, | ||
| // but still try to close, and report closing errors if they occur. | ||
| fs.fchmod(fd, mode, function (err) { | ||
| fs.close(fd, function(err2) { | ||
| callback(err || err2) | ||
| }) | ||
| }) | ||
| }) | ||
| function closeSync (fd) { | ||
| try { | ||
| return originalCloseSync(fd) | ||
| } finally { | ||
| onclose() | ||
| } | ||
| } | ||
| fs.lchmodSync = function (path, mode) { | ||
| var fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK, mode) | ||
| // prefer to return the chmod error, if one occurs, | ||
| // but still try to close, and report closing errors if they occur. | ||
| var err, err2 | ||
| try { | ||
| var ret = fs.fchmodSync(fd, mode) | ||
| } catch (er) { | ||
| err = er | ||
| } | ||
| try { | ||
| fs.closeSync(fd) | ||
| } catch (er) { | ||
| err2 = er | ||
| } | ||
| if (err || err2) throw (err || err2) | ||
| return ret | ||
| } | ||
| // Req class | ||
| function Req () { | ||
| // start processing | ||
| this.done = this.done.bind(this) | ||
| this.failures = 0 | ||
| this.process() | ||
| } | ||
| // lutimes implementation, or no-op | ||
| if (!fs.lutimes) { | ||
| if (constants.hasOwnProperty("O_SYMLINK")) { | ||
| fs.lutimes = function (path, at, mt, cb) { | ||
| fs.open(path, constants.O_SYMLINK, function (er, fd) { | ||
| cb = cb || noop | ||
| if (er) return cb(er) | ||
| fs.futimes(fd, at, mt, function (er) { | ||
| fs.close(fd, function (er2) { | ||
| return cb(er || er2) | ||
| }) | ||
| }) | ||
| }) | ||
| } | ||
| fs.lutimesSync = function (path, at, mt) { | ||
| var fd = fs.openSync(path, constants.O_SYMLINK) | ||
| , err | ||
| , err2 | ||
| , ret | ||
| try { | ||
| var ret = fs.futimesSync(fd, at, mt) | ||
| } catch (er) { | ||
| err = er | ||
| } | ||
| try { | ||
| fs.closeSync(fd) | ||
| } catch (er) { | ||
| err2 = er | ||
| } | ||
| if (err || err2) throw (err || err2) | ||
| return ret | ||
| } | ||
| } else if (fs.utimensat && constants.hasOwnProperty("AT_SYMLINK_NOFOLLOW")) { | ||
| // maybe utimensat will be bound soonish? | ||
| fs.lutimes = function (path, at, mt, cb) { | ||
| fs.utimensat(path, at, mt, constants.AT_SYMLINK_NOFOLLOW, cb) | ||
| } | ||
| fs.lutimesSync = function (path, at, mt) { | ||
| return fs.utimensatSync(path, at, mt, constants.AT_SYMLINK_NOFOLLOW) | ||
| } | ||
| Req.prototype.done = function (er, result) { | ||
| // if an error, and the code is EMFILE, then get in the queue | ||
| if (er && er.code === "EMFILE") { | ||
| this.failures ++ | ||
| enqueue(this) | ||
| } else { | ||
| fs.lutimes = function (_a, _b, _c, cb) { process.nextTick(cb) } | ||
| fs.lutimesSync = function () {} | ||
| var cb = this.cb | ||
| cb(er, result) | ||
| } | ||
| } | ||
| var queue = [] | ||
| // https://github.com/isaacs/node-graceful-fs/issues/4 | ||
| // Chown should not fail on einval or eperm if non-root. | ||
| fs.chown = chownFix(fs.chown) | ||
| fs.fchown = chownFix(fs.fchown) | ||
| fs.lchown = chownFix(fs.lchown) | ||
| fs.chownSync = chownFixSync(fs.chownSync) | ||
| fs.fchownSync = chownFixSync(fs.fchownSync) | ||
| fs.lchownSync = chownFixSync(fs.lchownSync) | ||
| function chownFix (orig) { | ||
| if (!orig) return orig | ||
| return function (target, uid, gid, cb) { | ||
| return orig.call(fs, target, uid, gid, function (er, res) { | ||
| if (chownErOk(er)) er = null | ||
| cb(er, res) | ||
| }) | ||
| } | ||
| function enqueue(req) { | ||
| queue.push(req) | ||
| debug('enqueue %d %s', queue.length, req.constructor.name, req) | ||
| } | ||
| function chownFixSync (orig) { | ||
| if (!orig) return orig | ||
| return function (target, uid, gid) { | ||
| try { | ||
| return orig.call(fs, target, uid, gid) | ||
| } catch (er) { | ||
| if (!chownErOk(er)) throw er | ||
| } | ||
| function onclose() { | ||
| var req = queue.shift() | ||
| if (req) { | ||
| debug('process', req.constructor.name, req) | ||
| req.process() | ||
| } | ||
| } | ||
| function chownErOk (er) { | ||
| // if there's no getuid, or if getuid() is something other than 0, | ||
| // and the error is EINVAL or EPERM, then just ignore it. | ||
| // This specific case is a silent failure in cp, install, tar, | ||
| // and most other unix tools that manage permissions. | ||
| // When running as root, or if other types of errors are encountered, | ||
| // then it's strict. | ||
| if (!er || (!process.getuid || process.getuid() !== 0) | ||
| && (er.code === "EINVAL" || er.code === "EPERM")) return true | ||
| } | ||
| // if lchmod/lchown do not exist, then make them no-ops | ||
| if (!fs.lchmod) { | ||
| fs.lchmod = function (path, mode, cb) { | ||
| process.nextTick(cb) | ||
| } | ||
| fs.lchmodSync = function () {} | ||
| } | ||
| if (!fs.lchown) { | ||
| fs.lchown = function (path, uid, gid, cb) { | ||
| process.nextTick(cb) | ||
| } | ||
| fs.lchownSync = function () {} | ||
| } | ||
| // on Windows, A/V software can lock the directory, causing this | ||
| // to fail with an EACCES or EPERM if the directory contains newly | ||
| // created files. Try again on failure, for up to 1 second. | ||
| if (process.platform === "win32") { | ||
| var rename_ = fs.rename | ||
| fs.rename = function rename (from, to, cb) { | ||
| var start = Date.now() | ||
| rename_(from, to, function CB (er) { | ||
| if (er | ||
| && (er.code === "EACCES" || er.code === "EPERM") | ||
| && Date.now() - start < 1000) { | ||
| return rename_(from, to, CB) | ||
| } | ||
| cb(er) | ||
| }) | ||
| } | ||
| } | ||
| // if read() returns EAGAIN, then just try it again. | ||
| var read = fs.read | ||
| fs.read = function (fd, buffer, offset, length, position, callback_) { | ||
| var callback | ||
| if (callback_ && typeof callback_ === 'function') { | ||
| var eagCounter = 0 | ||
| callback = function (er, _, __) { | ||
| if (er && er.code === 'EAGAIN' && eagCounter < 10) { | ||
| eagCounter ++ | ||
| return read.call(fs, fd, buffer, offset, length, position, callback) | ||
| } | ||
| callback_.apply(this, arguments) | ||
| } | ||
| } | ||
| return read.call(fs, fd, buffer, offset, length, position, callback) | ||
| } | ||
| var readSync = fs.readSync | ||
| fs.readSync = function (fd, buffer, offset, length, position) { | ||
| var eagCounter = 0 | ||
| while (true) { | ||
| try { | ||
| return readSync.call(fs, fd, buffer, offset, length, position) | ||
| } catch (er) { | ||
| if (er.code === 'EAGAIN' && eagCounter < 10) { | ||
| eagCounter ++ | ||
| continue | ||
| } | ||
| throw er | ||
| } | ||
| } | ||
| } |
+1
-1
@@ -5,3 +5,3 @@ { | ||
| "description": "A drop-in replacement for fs, making various improvements.", | ||
| "version": "1.2.3", | ||
| "version": "2.0.0", | ||
| "repository": { | ||
@@ -8,0 +8,0 @@ "type": "git", |
+3
-10
| var test = require('tap').test | ||
| var fs = require('../graceful-fs.js') | ||
| test('graceful fs is not fs', function (t) { | ||
| t.notEqual(fs, require('fs')) | ||
| test('graceful fs is monkeypatched fs', function (t) { | ||
| t.equal(fs, require('fs')) | ||
| t.end() | ||
@@ -10,13 +10,9 @@ }) | ||
| test('open an existing file works', function (t) { | ||
| var start = fs._curOpen | ||
| var fd = fs.openSync(__filename, 'r') | ||
| t.equal(fs._curOpen, start + 1) | ||
| fs.closeSync(fd) | ||
| t.equal(fs._curOpen, start) | ||
| fs.open(__filename, 'r', function (er, fd) { | ||
| if (er) throw er | ||
| t.equal(fs._curOpen, start + 1) | ||
| fs.close(fd, function (er) { | ||
| if (er) throw er | ||
| t.equal(fs._curOpen, start) | ||
| t.pass('works') | ||
| t.end() | ||
@@ -28,3 +24,2 @@ }) | ||
| test('open a non-existing file throws', function (t) { | ||
| var start = fs._curOpen | ||
| var er | ||
@@ -39,3 +34,2 @@ try { | ||
| t.equal(er.code, 'ENOENT') | ||
| t.equal(fs._curOpen, start) | ||
@@ -46,5 +40,4 @@ fs.open('neither does this file', 'r', function (er, fd) { | ||
| t.equal(er.code, 'ENOENT') | ||
| t.equal(fs._curOpen, start) | ||
| t.end() | ||
| }) | ||
| }) |
-158
| var test = require('tap').test | ||
| // simulated ulimit | ||
| // this is like graceful-fs, but in reverse | ||
| var fs_ = require('fs') | ||
| var fs = require('../graceful-fs.js') | ||
| var files = fs.readdirSync(__dirname) | ||
| // Ok, no more actual file reading! | ||
| var fds = 0 | ||
| var nextFd = 60 | ||
| var limit = 8 | ||
| fs_.open = function (path, flags, mode, cb) { | ||
| process.nextTick(function() { | ||
| ++fds | ||
| if (fds >= limit) { | ||
| --fds | ||
| var er = new Error('EMFILE Curses!') | ||
| er.code = 'EMFILE' | ||
| er.path = path | ||
| return cb(er) | ||
| } else { | ||
| cb(null, nextFd++) | ||
| } | ||
| }) | ||
| } | ||
| fs_.openSync = function (path, flags, mode) { | ||
| if (fds >= limit) { | ||
| var er = new Error('EMFILE Curses!') | ||
| er.code = 'EMFILE' | ||
| er.path = path | ||
| throw er | ||
| } else { | ||
| ++fds | ||
| return nextFd++ | ||
| } | ||
| } | ||
| fs_.close = function (fd, cb) { | ||
| process.nextTick(function () { | ||
| --fds | ||
| cb() | ||
| }) | ||
| } | ||
| fs_.closeSync = function (fd) { | ||
| --fds | ||
| } | ||
| fs_.readdir = function (path, cb) { | ||
| process.nextTick(function() { | ||
| if (fds >= limit) { | ||
| var er = new Error('EMFILE Curses!') | ||
| er.code = 'EMFILE' | ||
| er.path = path | ||
| return cb(er) | ||
| } else { | ||
| ++fds | ||
| process.nextTick(function () { | ||
| --fds | ||
| cb(null, [__filename, "some-other-file.js"]) | ||
| }) | ||
| } | ||
| }) | ||
| } | ||
| fs_.readdirSync = function (path) { | ||
| if (fds >= limit) { | ||
| var er = new Error('EMFILE Curses!') | ||
| er.code = 'EMFILE' | ||
| er.path = path | ||
| throw er | ||
| } else { | ||
| return [__filename, "some-other-file.js"] | ||
| } | ||
| } | ||
| test('open emfile autoreduce', function (t) { | ||
| fs.MIN_MAX_OPEN = 4 | ||
| t.equal(fs.MAX_OPEN, 1024) | ||
| var max = 12 | ||
| for (var i = 0; i < max; i++) { | ||
| fs.open(__filename, 'r', next(i)) | ||
| } | ||
| var phase = 0 | ||
| var expect = | ||
| [ [ 0, 60, null, 1024, 4, 12, 1 ], | ||
| [ 1, 61, null, 1024, 4, 12, 2 ], | ||
| [ 2, 62, null, 1024, 4, 12, 3 ], | ||
| [ 3, 63, null, 1024, 4, 12, 4 ], | ||
| [ 4, 64, null, 1024, 4, 12, 5 ], | ||
| [ 5, 65, null, 1024, 4, 12, 6 ], | ||
| [ 6, 66, null, 1024, 4, 12, 7 ], | ||
| [ 7, 67, null, 6, 4, 5, 1 ], | ||
| [ 8, 68, null, 6, 4, 5, 2 ], | ||
| [ 9, 69, null, 6, 4, 5, 3 ], | ||
| [ 10, 70, null, 6, 4, 5, 4 ], | ||
| [ 11, 71, null, 6, 4, 5, 5 ] ] | ||
| var actual = [] | ||
| function next (i) { return function (er, fd) { | ||
| if (er) | ||
| throw er | ||
| actual.push([i, fd, er, fs.MAX_OPEN, fs.MIN_MAX_OPEN, fs._curOpen, fds]) | ||
| if (i === max - 1) { | ||
| t.same(actual, expect) | ||
| t.ok(fs.MAX_OPEN < limit) | ||
| t.end() | ||
| } | ||
| fs.close(fd) | ||
| } } | ||
| }) | ||
| test('readdir emfile autoreduce', function (t) { | ||
| fs.MAX_OPEN = 1024 | ||
| var max = 12 | ||
| for (var i = 0; i < max; i ++) { | ||
| fs.readdir(__dirname, next(i)) | ||
| } | ||
| var expect = | ||
| [ [0,[__filename,"some-other-file.js"],null,7,4,7,7], | ||
| [1,[__filename,"some-other-file.js"],null,7,4,7,6], | ||
| [2,[__filename,"some-other-file.js"],null,7,4,7,5], | ||
| [3,[__filename,"some-other-file.js"],null,7,4,7,4], | ||
| [4,[__filename,"some-other-file.js"],null,7,4,7,3], | ||
| [5,[__filename,"some-other-file.js"],null,7,4,6,2], | ||
| [6,[__filename,"some-other-file.js"],null,7,4,5,1], | ||
| [7,[__filename,"some-other-file.js"],null,7,4,4,0], | ||
| [8,[__filename,"some-other-file.js"],null,7,4,3,3], | ||
| [9,[__filename,"some-other-file.js"],null,7,4,2,2], | ||
| [10,[__filename,"some-other-file.js"],null,7,4,1,1], | ||
| [11,[__filename,"some-other-file.js"],null,7,4,0,0] ] | ||
| var actual = [] | ||
| function next (i) { return function (er, files) { | ||
| if (er) | ||
| throw er | ||
| var line = [i, files, er, fs.MAX_OPEN, fs.MIN_MAX_OPEN, fs._curOpen, fds ] | ||
| actual.push(line) | ||
| if (i === max - 1) { | ||
| t.ok(fs.MAX_OPEN < limit) | ||
| t.same(actual, expect) | ||
| t.end() | ||
| } | ||
| } } | ||
| }) |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
13050
-31%354
-35.99%5
66.67%1
Infinity%