|
|
|
// 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
|
|
|
|
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)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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()
|
|
|
|
}
|
|
|
|
}
|