|
|
|
const os = require('os')
|
|
|
|
const fs = require('fs')
|
|
|
|
const path = require('path')
|
|
|
|
|
|
|
|
const IS_OSX = os.platform() === 'darwin'
|
|
|
|
const OSX_FOLDER_ICON = '/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/GenericFolderIcon.icns'
|
|
|
|
|
|
|
|
const HAS_FOLDER_ICON = IS_OSX && fs.existsSync(OSX_FOLDER_ICON)
|
|
|
|
|
|
|
|
const binding = require('node-gyp-build')(__dirname)
|
|
|
|
const Opcodes = new Map([
|
|
|
|
[ 'init', 0 ],
|
|
|
|
[ 'error', 1 ],
|
|
|
|
[ 'access', 2 ],
|
|
|
|
[ 'statfs', 3 ],
|
|
|
|
[ 'fgetattr', 4 ],
|
|
|
|
[ 'getattr', 5 ],
|
|
|
|
[ 'flush', 6 ],
|
|
|
|
[ 'fsync', 7 ],
|
|
|
|
[ 'fsyncdir', 8 ],
|
|
|
|
[ 'readdir', 9 ],
|
|
|
|
[ 'truncate', 10 ],
|
|
|
|
[ 'ftruncate', 11 ],
|
|
|
|
[ 'utimens', 12 ],
|
|
|
|
[ 'readlink', 13 ],
|
|
|
|
[ 'chown', 14 ],
|
|
|
|
[ 'chmod', 15 ],
|
|
|
|
[ 'mknod', 16 ],
|
|
|
|
[ 'setxattr', 17 ],
|
|
|
|
[ 'getxattr', 18 ],
|
|
|
|
[ 'listxattr', 19 ],
|
|
|
|
[ 'removexattr', 20 ],
|
|
|
|
[ 'open', 21 ],
|
|
|
|
[ 'opendir', 22 ],
|
|
|
|
[ 'read', 23 ],
|
|
|
|
[ 'write', 24 ],
|
|
|
|
[ 'release', 25 ],
|
|
|
|
[ 'releasedir', 26 ],
|
|
|
|
[ 'create', 27 ],
|
|
|
|
[ 'unlink', 28 ],
|
|
|
|
[ 'rename', 29 ],
|
|
|
|
[ 'link', 30 ],
|
|
|
|
[ 'symlink', 31 ],
|
|
|
|
[ 'mkdir', 32 ],
|
|
|
|
[ 'rmdir', 33 ],
|
|
|
|
[ 'destroy', 34 ]
|
|
|
|
])
|
|
|
|
|
|
|
|
class Fuse {
|
|
|
|
constructor (mnt, ops, opts = {}) {
|
|
|
|
this.opts = opts
|
|
|
|
this.mnt = path.resolve(mnt)
|
|
|
|
|
|
|
|
this.ops = ops
|
|
|
|
this._thread = Buffer.alloc(binding.sizeof_fuse_thread_t)
|
|
|
|
// Keep the process alive while fuse is mounted.
|
|
|
|
this._timer = null
|
|
|
|
|
|
|
|
const implemented = ['init', 'error', 'getattr']
|
|
|
|
if (ops) {
|
|
|
|
for (const [name, code] of Opcodes) {
|
|
|
|
if (ops[name]) implemented.push(code)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this._implemented = new Set(implemented)
|
|
|
|
|
|
|
|
// Used to determine if the user-defined callback needs to be nextTick'd.
|
|
|
|
this._sync = true
|
|
|
|
}
|
|
|
|
|
|
|
|
_withDefaultOps (cb) {
|
|
|
|
const withDefaults = { ...this.ops }
|
|
|
|
|
|
|
|
const callback = function (err) {
|
|
|
|
callback = noop
|
|
|
|
setImmediate(cb.bind(null, err))
|
|
|
|
}
|
|
|
|
|
|
|
|
var init = this.ops.init || call
|
|
|
|
withDefaults.init = function (next) {
|
|
|
|
console.log('IN INIT')
|
|
|
|
callback()
|
|
|
|
if (init.length > 1) init(this.mnt, next) // backwards compat for now
|
|
|
|
else init(next)
|
|
|
|
}
|
|
|
|
|
|
|
|
var error = this.ops.error || call
|
|
|
|
withDefaults.error = function (next) {
|
|
|
|
console.log('IN ERROR')
|
|
|
|
callback(new Error('Mount failed'))
|
|
|
|
error(next)
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!this.ops.getattr) { // we need this for unmount to work on osx
|
|
|
|
withDefaults.getattr = function (path, cb) {
|
|
|
|
if (path !== '/') return cb(Fuse.EPERM)
|
|
|
|
cb(null, { mtime: new Date(0), atime: new Date(0), ctime: new Date(0), mode: 16877, size: 4096 })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return withDefaults
|
|
|
|
}
|
|
|
|
|
|
|
|
_fuseOptions () {
|
|
|
|
const options = []
|
|
|
|
|
|
|
|
if ((/\*|(^,)fuse-bindings(,$)/.test(process.env.DEBUG)) || this.opts.debug) options.push('debug')
|
|
|
|
if (this.opts.allowOther) options.push('allow_other')
|
|
|
|
if (this.opts.allowRoot) options.push('allow_root')
|
|
|
|
if (this.opts.autoUnmount) options.push('auto_unmount')
|
|
|
|
if (this.opts.defaultPermissions) options.push('default_permissions')
|
|
|
|
if (this.opts.blkdev) options.push('blkdev')
|
|
|
|
if (this.opts.blksize) options.push('blksize=' + this.opts.blksize)
|
|
|
|
if (this.opts.maxRead) options.push('max_read=' + this.opts.maxRead)
|
|
|
|
if (this.opts.fd) options.push('fd=' + this.opts.fd)
|
|
|
|
if (this.opts.userId) options.push('user_id=', this.opts.userId)
|
|
|
|
if (this.opts.fsname) options.push('fsname=' + this.opts.fsname)
|
|
|
|
if (this.opts.subtype) options.push('subtype=' + this.opts.subtype)
|
|
|
|
if (this.opts.kernelCache) options.push('kernel_cache')
|
|
|
|
if (this.opts.autoCache) options.push('auto_cache')
|
|
|
|
if (this.opts.umask) options.push('umask=' + this.opts.umask)
|
|
|
|
if (this.opts.uid) options.push('uid=' + this.opts.uid)
|
|
|
|
if (this.opts.gid) options.push('gid=' + this.opts.gid)
|
|
|
|
if (this.opts.entryTimeout) options.push('entry_timeout=' + this.opts.entryTimeout)
|
|
|
|
if (this.opts.attrTimeout) options.push('attr_timeout=' + this.opts.attrTimeout)
|
|
|
|
if (this.opts.acAttrTimeout) options.push('ac_attr_timeout=' + this.opts.acAttrTimeout)
|
|
|
|
if (this.opts.noforget) options.push('noforget')
|
|
|
|
if (this.opts.remember) options.push('remember=' + this.opts.remember)
|
|
|
|
if (this.opts.modules) options.push('modules=' + this.opts.modules)
|
|
|
|
|
|
|
|
if (this.opts.displayFolder && IS_OSX) { // only works on osx
|
|
|
|
options.push('volname=' + path.basename(this.mnt))
|
|
|
|
if (HAS_FOLDER_ICON) options.push('volicon=' + OSX_FOLDER_ICON)
|
|
|
|
}
|
|
|
|
|
|
|
|
return options.map(o => '-o' + o).join(' ')
|
|
|
|
}
|
|
|
|
|
|
|
|
_signal (signalFunc, args) {
|
|
|
|
/*
|
|
|
|
if (this._sync) process.nextTick(() => signalFunc.apply(null, args))
|
|
|
|
else signalFunc.apply(null, args)
|
|
|
|
*/
|
|
|
|
process.nextTick(() => signalFunc.apply(null, args))
|
|
|
|
}
|
|
|
|
|
|
|
|
mount (cb) {
|
|
|
|
this.ops = this._withDefaultOps(this.ops, cb)
|
|
|
|
const opts = this._fuseOptions()
|
|
|
|
|
|
|
|
fs.stat(this.mnt, (err, stat) => {
|
|
|
|
if (err) return cb(new Error('Mountpoint does not exist'))
|
|
|
|
if (!stat.isDirectory()) return cb(new Error('Mountpoint is not a directory'))
|
|
|
|
fs.stat(path.join(this.mnt, '..'), (_, parent) => {
|
|
|
|
if (parent && parent.dev !== stat.dev) return cb(new Error('Mountpoint in use'))
|
|
|
|
try {
|
|
|
|
// TODO: asyncify
|
|
|
|
binding.fuse_native_mount(this.mnt, opts, this._thread, this,
|
|
|
|
this.on_path_op, this.on_stat_op, this.on_fd_op, this.on_xattr_op,
|
|
|
|
this.on_statfs, this.on_readdir, this.on_symlink)
|
|
|
|
} catch (err) {
|
|
|
|
return cb(err)
|
|
|
|
}
|
|
|
|
this._timer = setInterval(() => {}, 10000)
|
|
|
|
return cb(null)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
unmount (cb) {
|
|
|
|
// TODO: asyncify
|
|
|
|
try {
|
|
|
|
binding.fuse_native_unmount(this.mnt, this._thread)
|
|
|
|
} catch (err) {
|
|
|
|
clearInterval(this._timer)
|
|
|
|
return process.nextTick(cb, err)
|
|
|
|
}
|
|
|
|
clearInterval(this._timer)
|
|
|
|
return process.nextTick(cb, null)
|
|
|
|
}
|
|
|
|
|
|
|
|
errno (code) {
|
|
|
|
return (code && Fuse[code.toUpperCase()]) || -1
|
|
|
|
}
|
|
|
|
|
|
|
|
on_symlink (handle, op, path, target) {
|
|
|
|
const signalFunc = binding.fuse_native_signal_path.bind(binding)
|
|
|
|
if (!this._implemented.has(op)) return this._signal(signalFunc, [handle, -1])
|
|
|
|
}
|
|
|
|
|
|
|
|
on_readdir (handle, op, path) {
|
|
|
|
const signalFunc = binding.fuse_native_signal_readdir.bind(binding)
|
|
|
|
|
|
|
|
if (!this._implemented.has(op)) return this._signal(signalFunc, [handle, -1])
|
|
|
|
|
|
|
|
this.ops.readdir(path, (err, names, stats) => {
|
|
|
|
if (stats) stats = stats.map(getStatArray)
|
|
|
|
return this._signal(signalFunc, [handle, err, names, stats || []])
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
on_statfs (handle, op) {
|
|
|
|
const signalFunc = binding.fuse_native_signal_statfs.bind(binding)
|
|
|
|
if (!this._implemented.has(op)) return this._signal(signalFunc, [handle, -1, ...getStatfsArray()])
|
|
|
|
|
|
|
|
this.ops.statfs((err, statfs) => {
|
|
|
|
const arr = getStatfsArray(statfs)
|
|
|
|
return this._signal(signalFunc, [handle, err, arr])
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
on_fd_op (handle, op, path, fd, buf, len, offset) {
|
|
|
|
const signalFunc = binding.fuse_native_signal_buffer.bind(binding)
|
|
|
|
if (!this._implemented.has(op)) return this._signal(signalFunc, [handle, -1])
|
|
|
|
|
|
|
|
const cb = (bytesProcessed) => {
|
|
|
|
return this._signal(signalFunc, [handle, bytesProcessed || 0])
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (op) {
|
|
|
|
case (binding.op_read):
|
|
|
|
this.ops.read(path, fd, buf, len, offset, cb)
|
|
|
|
break
|
|
|
|
case (binding.op_write):
|
|
|
|
this.ops.write(path, fd, buf, len, offset, cb)
|
|
|
|
break
|
|
|
|
case(binding.op_release):
|
|
|
|
this.ops.release(path, fd, cb)
|
|
|
|
break
|
|
|
|
case(binding.op_releasedir):
|
|
|
|
this.ops.releasedir(path, fd, cb)
|
|
|
|
break
|
|
|
|
default:
|
|
|
|
return this._signal(signalFunc, [handle, 0])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
on_stat_op (handle, op, path, fd) {
|
|
|
|
const signalFunc = binding.fuse_native_signal_stat.bind(binding)
|
|
|
|
if (!this._implemented.has(op)) return this._signal(signalFunc, [handle, -1, getStatArray()])
|
|
|
|
|
|
|
|
const cb = (err, stat) => {
|
|
|
|
const arr = getStatArray(stat)
|
|
|
|
return this._signal(signalFunc, [handle, err, arr])
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (op) {
|
|
|
|
case (binding.op_getattr):
|
|
|
|
this.ops.getattr(path, cb)
|
|
|
|
break
|
|
|
|
case (binding.op_fgetattr):
|
|
|
|
this.ops.fgetattr(path, fd, cb)
|
|
|
|
default:
|
|
|
|
return this._signal(signalFunc, [handle, -1, getStatArray()])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
on_path_op (handle, op, path, mode, flags, atim, mtim) {
|
|
|
|
const signalFunc = binding.fuse_native_signal_path.bind(binding)
|
|
|
|
if (!this._implemented.has(op)) return this._signal(signalFunc, [handle, -1, 0])
|
|
|
|
|
|
|
|
const cb = (err, fd) => {
|
|
|
|
return this._signal(signalFunc, [handle, err, fd || 0])
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (op) {
|
|
|
|
case (binding.op_open):
|
|
|
|
this.ops.open(path, flags, cb)
|
|
|
|
break
|
|
|
|
case (binding.op_create):
|
|
|
|
this.ops.create(path, mode, cb)
|
|
|
|
break
|
|
|
|
case (binding.op_access):
|
|
|
|
this.ops.access(path, mode, cb)
|
|
|
|
break
|
|
|
|
case (binding.op_utimens):
|
|
|
|
this.ops.utimens(path, getDoubleInt(atim, 0), getDoubleInt(mtim, 0), cb)
|
|
|
|
break
|
|
|
|
default:
|
|
|
|
return this._signal(signalFunc, [handle, -1, 0])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
on_xattr_op (handle, op, path, name, value, list, size, flags, position) {
|
|
|
|
const signalFunc = binding.fuse_native_signal_xattr.bind(binding)
|
|
|
|
if (!this._implemented.has(op)) return this._signal(signalFunc, [handle, -1, 0])
|
|
|
|
|
|
|
|
const cb = err => {
|
|
|
|
return this._signal(signalFunc, [handle, -1])
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (op) {
|
|
|
|
case (binding.op_setxattr):
|
|
|
|
this.ops.setxattr(path, name, value, size, position || 0, flags, cb)
|
|
|
|
break
|
|
|
|
case (binding.op_getxattr):
|
|
|
|
this.ops.getxattr(path, name, value, size, position || 0, cb)
|
|
|
|
break
|
|
|
|
case (binding.op_listxattr):
|
|
|
|
this.ops.listxattr(path, list, size, cb)
|
|
|
|
break
|
|
|
|
case (binding.op_removexattr):
|
|
|
|
this.ops.removexattr(path, name, cb)
|
|
|
|
break
|
|
|
|
default:
|
|
|
|
return this._signal(signalFunc, [handle, -1])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Fuse.EPERM = -1
|
|
|
|
Fuse.ENOENT = -2
|
|
|
|
Fuse.ESRCH = -3
|
|
|
|
Fuse.EINTR = -4
|
|
|
|
Fuse.EIO = -5
|
|
|
|
Fuse.ENXIO = -6
|
|
|
|
Fuse.E2BIG = -7
|
|
|
|
Fuse.ENOEXEC = -8
|
|
|
|
Fuse.EBADF = -9
|
|
|
|
Fuse.ECHILD = -10
|
|
|
|
Fuse.EAGAIN = -11
|
|
|
|
Fuse.ENOMEM = -12
|
|
|
|
Fuse.EACCES = -13
|
|
|
|
Fuse.EFAULT = -14
|
|
|
|
Fuse.ENOTBLK = -15
|
|
|
|
Fuse.EBUSY = -16
|
|
|
|
Fuse.EEXIST = -17
|
|
|
|
Fuse.EXDEV = -18
|
|
|
|
Fuse.ENODEV = -19
|
|
|
|
Fuse.ENOTDIR = -20
|
|
|
|
Fuse.EISDIR = -21
|
|
|
|
Fuse.EINVAL = -22
|
|
|
|
Fuse.ENFILE = -23
|
|
|
|
Fuse.EMFILE = -24
|
|
|
|
Fuse.ENOTTY = -25
|
|
|
|
Fuse.ETXTBSY = -26
|
|
|
|
Fuse.EFBIG = -27
|
|
|
|
Fuse.ENOSPC = -28
|
|
|
|
Fuse.ESPIPE = -29
|
|
|
|
Fuse.EROFS = -30
|
|
|
|
Fuse.EMLINK = -31
|
|
|
|
Fuse.EPIPE = -32
|
|
|
|
Fuse.EDOM = -33
|
|
|
|
Fuse.ERANGE = -34
|
|
|
|
Fuse.EDEADLK = -35
|
|
|
|
Fuse.ENAMETOOLONG = -36
|
|
|
|
Fuse.ENOLCK = -37
|
|
|
|
Fuse.ENOSYS = -38
|
|
|
|
Fuse.ENOTEMPTY = -39
|
|
|
|
Fuse.ELOOP = -40
|
|
|
|
Fuse.EWOULDBLOCK = -11
|
|
|
|
Fuse.ENOMSG = -42
|
|
|
|
Fuse.EIDRM = -43
|
|
|
|
Fuse.ECHRNG = -44
|
|
|
|
Fuse.EL2NSYNC = -45
|
|
|
|
Fuse.EL3HLT = -46
|
|
|
|
Fuse.EL3RST = -47
|
|
|
|
Fuse.ELNRNG = -48
|
|
|
|
Fuse.EUNATCH = -49
|
|
|
|
Fuse.ENOCSI = -50
|
|
|
|
Fuse.EL2HLT = -51
|
|
|
|
Fuse.EBADE = -52
|
|
|
|
Fuse.EBADR = -53
|
|
|
|
Fuse.EXFULL = -54
|
|
|
|
Fuse.ENOANO = -55
|
|
|
|
Fuse.EBADRQC = -56
|
|
|
|
Fuse.EBADSLT = -57
|
|
|
|
Fuse.EDEADLOCK = -35
|
|
|
|
Fuse.EBFONT = -59
|
|
|
|
Fuse.ENOSTR = -60
|
|
|
|
Fuse.ENODATA = -61
|
|
|
|
Fuse.ETIME = -62
|
|
|
|
Fuse.ENOSR = -63
|
|
|
|
Fuse.ENONET = -64
|
|
|
|
Fuse.ENOPKG = -65
|
|
|
|
Fuse.EREMOTE = -66
|
|
|
|
Fuse.ENOLINK = -67
|
|
|
|
Fuse.EADV = -68
|
|
|
|
Fuse.ESRMNT = -69
|
|
|
|
Fuse.ECOMM = -70
|
|
|
|
Fuse.EPROTO = -71
|
|
|
|
Fuse.EMULTIHOP = -72
|
|
|
|
Fuse.EDOTDOT = -73
|
|
|
|
Fuse.EBADMSG = -74
|
|
|
|
Fuse.EOVERFLOW = -75
|
|
|
|
Fuse.ENOTUNIQ = -76
|
|
|
|
Fuse.EBADFD = -77
|
|
|
|
Fuse.EREMCHG = -78
|
|
|
|
Fuse.ELIBACC = -79
|
|
|
|
Fuse.ELIBBAD = -80
|
|
|
|
Fuse.ELIBSCN = -81
|
|
|
|
Fuse.ELIBMAX = -82
|
|
|
|
Fuse.ELIBEXEC = -83
|
|
|
|
Fuse.EILSEQ = -84
|
|
|
|
Fuse.ERESTART = -85
|
|
|
|
Fuse.ESTRPIPE = -86
|
|
|
|
Fuse.EUSERS = -87
|
|
|
|
Fuse.ENOTSOCK = -88
|
|
|
|
Fuse.EDESTADDRREQ = -89
|
|
|
|
Fuse.EMSGSIZE = -90
|
|
|
|
Fuse.EPROTOTYPE = -91
|
|
|
|
Fuse.ENOPROTOOPT = -92
|
|
|
|
Fuse.EPROTONOSUPPORT = -93
|
|
|
|
Fuse.ESOCKTNOSUPPORT = -94
|
|
|
|
Fuse.EOPNOTSUPP = -95
|
|
|
|
Fuse.EPFNOSUPPORT = -96
|
|
|
|
Fuse.EAFNOSUPPORT = -97
|
|
|
|
Fuse.EADDRINUSE = -98
|
|
|
|
Fuse.EADDRNOTAVAIL = -99
|
|
|
|
Fuse.ENETDOWN = -100
|
|
|
|
Fuse.ENETUNREACH = -101
|
|
|
|
Fuse.ENETRESET = -102
|
|
|
|
Fuse.ECONNABORTED = -103
|
|
|
|
Fuse.ECONNRESET = -104
|
|
|
|
Fuse.ENOBUFS = -105
|
|
|
|
Fuse.EISCONN = -106
|
|
|
|
Fuse.ENOTCONN = -107
|
|
|
|
Fuse.ESHUTDOWN = -108
|
|
|
|
Fuse.ETOOMANYREFS = -109
|
|
|
|
Fuse.ETIMEDOUT = -110
|
|
|
|
Fuse.ECONNREFUSED = -111
|
|
|
|
Fuse.EHOSTDOWN = -112
|
|
|
|
Fuse.EHOSTUNREACH = -113
|
|
|
|
Fuse.EALREADY = -114
|
|
|
|
Fuse.EINPROGRESS = -115
|
|
|
|
Fuse.ESTALE = -116
|
|
|
|
Fuse.EUCLEAN = -117
|
|
|
|
Fuse.ENOTNAM = -118
|
|
|
|
Fuse.ENAVAIL = -119
|
|
|
|
Fuse.EISNAM = -120
|
|
|
|
Fuse.EREMOTEIO = -121
|
|
|
|
Fuse.EDQUOT = -122
|
|
|
|
Fuse.ENOMEDIUM = -123
|
|
|
|
Fuse.EMEDIUMTYPE = -124
|
|
|
|
|
|
|
|
module.exports = Fuse
|
|
|
|
|
|
|
|
function getStatfsArray (statfs) {
|
|
|
|
const ints = new Uint32Array(11)
|
|
|
|
|
|
|
|
ints[0] = (statfs && statfs.bsize) || 0
|
|
|
|
ints[1] = (statfs && statfs.frsize) || 0
|
|
|
|
ints[2] = (statfs && statfs.blocks) || 0
|
|
|
|
ints[3] = (statfs && statfs.bfree) || 0
|
|
|
|
ints[4] = (statfs && statfs.bavail) || 0
|
|
|
|
ints[5] = (statfs && statfs.files) || 0
|
|
|
|
ints[6] = (statfs && statfs.ffree) || 0
|
|
|
|
ints[7] = (statfs && statfs.favail) || 0
|
|
|
|
ints[8] = (statfs && statfs.fsid) || 0
|
|
|
|
ints[9] = (statfs && statfs.flag) || 0
|
|
|
|
ints[10] = (statfs && statfs.namemax) || 0
|
|
|
|
|
|
|
|
return ints
|
|
|
|
}
|
|
|
|
|
|
|
|
function setDoubleInt (arr, idx, num) {
|
|
|
|
arr[idx] = num % 4294967296
|
|
|
|
arr[idx + 1] = (num - arr[idx]) / 4294967296
|
|
|
|
}
|
|
|
|
|
|
|
|
function getDoubleInt(arr, idx) {
|
|
|
|
arr = new Uint32Array(arr)
|
|
|
|
var num = arr[idx + 1] * 4294967296
|
|
|
|
num += arr[idx]
|
|
|
|
return num
|
|
|
|
}
|
|
|
|
|
|
|
|
function getStatArray (stat) {
|
|
|
|
const ints = new Uint32Array(16)
|
|
|
|
|
|
|
|
ints[0] = (stat && stat.mode) || 0
|
|
|
|
ints[1] = (stat && stat.uid) || 0
|
|
|
|
ints[2] = (stat && stat.gid) || 0
|
|
|
|
ints[3] = (stat && stat.size) || 0
|
|
|
|
ints[4] = (stat && stat.dev) || 0
|
|
|
|
ints[5] = (stat && stat.nlink) || 1
|
|
|
|
ints[6] = (stat && stat.ino) || 0
|
|
|
|
ints[7] = (stat && stat.rdev) || 0
|
|
|
|
ints[8] = (stat && stat.blksize) || 0
|
|
|
|
ints[9] = (stat && stat.blocks) || 0
|
|
|
|
setDoubleInt(ints, 10, (stat && stat.atim) || Date.now())
|
|
|
|
setDoubleInt(ints, 12, (stat && stat.atim) || Date.now())
|
|
|
|
setDoubleInt(ints, 14, (stat && stat.atim) || Date.now())
|
|
|
|
|
|
|
|
return ints
|
|
|
|
}
|
|
|
|
|
|
|
|
function noop () {}
|
|
|
|
function call (cb) { cb() }
|