// 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 assert = require('assert') // fix up some busted stuff, mostly on windows and old nodes require('./polyfills.js') // The EMFILE enqueuing stuff var util = require('util') function noop () {} var debug = noop 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) }) } var originalOpen = fs.open 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) } function OpenReq(path, flags, mode, cb) { this.path = path this.flags = flags this.mode = mode this.cb = cb Req.call(this) } util.inherits(OpenReq, Req) OpenReq.prototype.process = function() { originalOpen.call(fs, this.path, this.flags, this.mode, this.done) } 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) } var originalReaddir = fs.readdir fs.readdir = readdir function readdir(path, cb) { if (typeof cb !== "function") cb = noop new ReaddirReq(path, cb) } function ReaddirReq(path, cb) { this.path = path this.cb = cb Req.call(this) } util.inherits(ReaddirReq, Req) ReaddirReq.prototype.process = function() { originalReaddir.call(fs, this.path, this.done) } ReaddirReq.prototype.done = function(er, files) { if (files && files.sort) files = files.sort() Req.prototype.done.call(this, er, files) onclose() } var originalClose = fs.close fs.close = close 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) }) } var originalCloseSync = fs.closeSync fs.closeSync = closeSync function closeSync (fd) { try { return originalCloseSync(fd) } finally { onclose() } } // Req class function Req () { // start processing this.done = this.done.bind(this) this.failures = 0 this.process() } Req.prototype.done = function (er, result) { var tryAgain = false if (er) { var code = er.code var tryAgain = code === "EMFILE" if (process.platform === "win32") tryAgain = tryAgain || code === "OK" } if (tryAgain) { this.failures ++ enqueue(this) } else { var cb = this.cb cb(er, result) } } var queue = [] function enqueue(req) { queue.push(req) debug('enqueue %d %s', queue.length, req.constructor.name, req) } function onclose() { var req = queue.shift() if (req) { debug('process', req.constructor.name, req) req.process() } }