1
0
mirror of https://github.com/fuse-friends/fuse-native synced 2024-10-27 18:34:01 +00:00

Add safe mode with timeouts

This commit is contained in:
Andrew Osheroff 2019-07-16 13:40:48 +02:00
parent 2c8540a571
commit 11be64ab33

View File

@ -13,6 +13,8 @@ var IS_OSX = os.platform() === 'darwin'
var OSX_FOLDER_ICON = '/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/GenericFolderIcon.icns'
var HAS_FOLDER_ICON = IS_OSX && fs.existsSync(OSX_FOLDER_ICON)
const TIMED_OUT = Symbol('TIMED_OUT')
var FuseBuffer = function () {
this.length = 0
this.parent = undefined
@ -36,11 +38,16 @@ exports.context = function () {
exports.mount = function (mnt, ops, opts, cb) {
if (typeof opts === 'function') return exports.mount(mnt, ops, null, opts)
if (!cb) cb = noop
var destroyed = false
ops = xtend(ops, opts) // clone
if (/\*|(^,)fuse-bindings(,$)/.test(process.env.DEBUG)) ops.options = ['debug'].concat(ops.options || [])
mnt = path.resolve(mnt)
if (opts && (opts.safe || opts.timeout)) {
var stopTimeouts = wrapSafe(ops, opts.timeout || 3000)
}
if (ops.displayFolder && IS_OSX) { // only works on osx
if (!ops.options) ops.options = []
ops.options.push('volname=' + path.basename(mnt))
@ -49,7 +56,7 @@ exports.mount = function (mnt, ops, opts, cb) {
var callback = function (err) {
callback = noop
setImmediate(cb.bind(null, err))
setImmediate(cb.bind(null, err, destroy))
}
var init = ops.init || call
@ -92,6 +99,15 @@ exports.mount = function (mnt, ops, opts, cb) {
if (!ops.force) return mount()
exports.unmount(mnt, mount)
function destroy (cb) {
if (destroyed) return null
destroyed = true
if (stopTimeouts) stopTimeouts()
return exports.unmount(mnt, err => {
return cb(err)
})
}
}
exports.unmount = function (mnt, cb) {
@ -116,6 +132,74 @@ exports.errno = function (code) {
return (code && exports[code.toUpperCase()]) || -1
}
function wrapSafe (ops, timeout) {
const pending = []
var timeoutChecker = setInterval(checkTimeouts, timeout / 4)
var destroyed = false
const wrapped = {}
for (const name of Object.keys(ops)) {
const op = ops[name]
if (typeof op !== 'function') {
wrapped[name] = op
continue
}
wrapped[name] = wrapOp(op)
}
Object.assign(ops, wrapped)
return destroy
function wrapOp (op) {
return function () {
const cb = arguments[arguments.length - 1]
var called = false
const idx = pending.indexOf(null)
const cbInfo = { cb, tick: 0 }
pending[idx === -1 ? pending.length : idx] = cbInfo
try {
op.apply(null, [...Array.prototype.slice.call(arguments, 0, -1), safeCb])
} catch (err) {
if (!cb[TIMED_OUT] && !called) return cb(exports.EIO)
}
function safeCb () {
called = true
pending[pending.indexOf(cbInfo)] = null
if (!cb[TIMED_OUT]) cb.apply(null, arguments)
cb[TIMED_OUT] = false
}
}
}
function checkTimeouts () {
for (const cbInfo of pending) {
if (!cbInfo) continue
if (++cbInfo.tick >= 4) {
killCallback(cbInfo)
}
}
}
function killCallback (cbInfo) {
cbInfo.cb(exports.EIO)
cbInfo.cb[TIMED_OUT] = true
pending[pending.indexOf(cbInfo)] = null
}
function destroy () {
clearInterval(timeoutChecker)
for (const cbInfo of [...pending]) {
if (cbInfo) killCallback(cbInfo)
}
destroyed = true
}
}
exports.EPERM = -1
exports.ENOENT = -2
exports.ESRCH = -3