diff options
Diffstat (limited to 'deps/npm/node_modules/graceful-fs/graceful-fs.js')
-rw-r--r-- | deps/npm/node_modules/graceful-fs/graceful-fs.js | 366 |
1 files changed, 101 insertions, 265 deletions
diff --git a/deps/npm/node_modules/graceful-fs/graceful-fs.js b/deps/npm/node_modules/graceful-fs/graceful-fs.js index fe9c3f4cad..a46b5b2a18 100644 --- a/deps/npm/node_modules/graceful-fs/graceful-fs.js +++ b/deps/npm/node_modules/graceful-fs/graceful-fs.js @@ -1,316 +1,152 @@ -// 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_ = require("fs") +var assert = require('assert') -var fs = module.exports = {} +// fix up some busted stuff, mostly on windows and old nodes +require('./polyfills.js') -Object.getOwnPropertyNames(fs_).forEach(function(prop) { - var desc = Object.getOwnPropertyDescriptor(fs_, prop) - Object.defineProperty(fs, prop, desc) -}) +// The EMFILE enqueuing stuff -var queue = [] - , constants = require("constants") +var util = require('util') + +function noop () {} -exports = module.exports = fs -fs._curOpen = 0 +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) + }) +} -fs.MIN_MAX_OPEN = 64 -fs.MAX_OPEN = 1024 var originalOpen = fs.open - , originalOpenSync = fs.openSync - , originalClose = fs.close - , originalCloseSync = fs.closeSync +fs.open = open +function open(path, flags, mode, cb) { + if (typeof mode === "function") cb = mode, mode = null + if (typeof cb !== "function") cb = noop + new OpenReq(path, flags, mode, cb) +} -// prevent EMFILE errors -function OpenReq (path, flags, mode, cb) { +function OpenReq(path, flags, mode, cb) { this.path = path this.flags = flags this.mode = mode this.cb = cb + Req.call(this) } -function noop () {} - -fs.open = gracefulOpen - -function gracefulOpen (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) - }) -} +util.inherits(OpenReq, Req) -function open (path, flags, mode, cb) { - cb = cb || noop - fs._curOpen ++ - originalOpen.call(fs, path, flags, mode, function (er, fd) { - if (er) onclose() - cb(er, fd) - }) +OpenReq.prototype.process = function() { + originalOpen.call(fs, this.path, this.flags, this.mode, this.done) } -fs.openSync = function (path, flags, mode) { - var ret - ret = originalOpenSync.call(fs, path, flags, mode) - fs._curOpen ++ - return ret +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) } -function onclose () { - fs._curOpen -- - flush() -} -function flush () { - while (fs._curOpen < fs.MAX_OPEN) { - var req = queue.shift() - if (!req) return - open(req.path, req.flags || "r", req.mode || 0777, req.cb) - } -} +var originalReaddir = fs.readdir +fs.readdir = readdir -fs.close = function (fd, cb) { - cb = cb || noop - originalClose.call(fs, fd, function (er) { - onclose() - cb(er) - }) +function readdir(path, cb) { + if (typeof cb !== "function") cb = noop + new ReaddirReq(path, cb) } -fs.closeSync = function (fd) { - onclose() - return originalCloseSync.call(fs, fd) +function ReaddirReq(path, cb) { + this.path = path + this.cb = cb + Req.call(this) } +util.inherits(ReaddirReq, Req) -// (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) - }) - }) - }) - } - - 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 - } +ReaddirReq.prototype.process = function() { + originalReaddir.call(fs, this.path, this.done) } - -// 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 () {} - } +ReaddirReq.prototype.done = function(er, files) { + Req.prototype.done.call(this, er, files) + onclose() } -// 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) - }) - } -} +var originalClose = fs.close +fs.close = close -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 close (fd, cb) { + debug('close', fd) + if (typeof cb !== "function") cb = noop + delete fds['fd' + fd] + originalClose.call(fs, fd, function(er) { + onclose() + cb(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 -} +var originalCloseSync = fs.closeSync +fs.closeSync = closeSync -// 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) +function closeSync (fd) { + try { + return originalCloseSync(fd) + } finally { + onclose() } - fs.lchownSync = function () {} } +// Req class +function Req () { + // start processing + this.done = this.done.bind(this) + this.failures = 0 + this.process() +} -// 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) - }) +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 { + var cb = this.cb + cb(er, result) } } +var queue = [] -// 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) +function enqueue(req) { + queue.push(req) + debug('enqueue %d %s', queue.length, req.constructor.name, req) } -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 - } +function onclose() { + var req = queue.shift() + if (req) { + debug('process', req.constructor.name, req) + req.process() } } |