mirror of
https://github.com/fuse-friends/fuse-native
synced 2024-10-27 18:34:01 +00:00
added unmount + better example
This commit is contained in:
parent
d5747dd20f
commit
9121e8822c
84
example.js
Normal file
84
example.js
Normal file
@ -0,0 +1,84 @@
|
||||
const Fuse = require('./')
|
||||
|
||||
const ops = {
|
||||
readdir: function (path, cb) {
|
||||
console.log('readdir(%s)', path)
|
||||
if (path === '/') return process.nextTick(cb, 0, ['test'])
|
||||
return process.nextTick(cb, 0)
|
||||
},
|
||||
/*
|
||||
access: function (path, cb) {
|
||||
return process.nextTick(cb, 0)
|
||||
},
|
||||
*/
|
||||
getattr: function (path, cb) {
|
||||
console.log('getattr(%s)', path)
|
||||
if (path === '/') {
|
||||
return process.nextTick(cb, 0, {
|
||||
mtime: new Date(),
|
||||
atime: new Date(),
|
||||
ctime: new Date(),
|
||||
nlink: 1,
|
||||
size: 100,
|
||||
mode: 16877,
|
||||
uid: process.getuid ? process.getuid() : 0,
|
||||
gid: process.getgid ? process.getgid() : 0
|
||||
})
|
||||
}
|
||||
|
||||
if (path === '/test') {
|
||||
return process.nextTick(cb, 0, {
|
||||
mtime: new Date(),
|
||||
atime: new Date(),
|
||||
ctime: new Date(),
|
||||
nlink: 1,
|
||||
size: 12,
|
||||
mode: 33188,
|
||||
uid: process.getuid ? process.getuid() : 0,
|
||||
gid: process.getgid ? process.getgid() : 0
|
||||
})
|
||||
}
|
||||
|
||||
return process.nextTick(cb, Fuse.ENOENT)
|
||||
},
|
||||
open: function (path, flags, cb) {
|
||||
console.log('open(%s, %d)', path, flags)
|
||||
return process.nextTick(cb, 0, 42) // 42 is an fd
|
||||
},
|
||||
read: function (path, fd, buf, len, pos, cb) {
|
||||
console.log('read(%s, %d, %d, %d)', path, fd, len, pos)
|
||||
var str = 'hello world\n'.slice(pos)
|
||||
if (!str) return process.nextTick(cb, 0)
|
||||
buf.write(str)
|
||||
return process.nextTick(cb, str.length)
|
||||
}
|
||||
}
|
||||
|
||||
const fuse = new Fuse('./mnt', ops, { debug: true })
|
||||
fuse.mount(err => {
|
||||
if (err) throw err
|
||||
console.log('filesystem mounted on ' + fuse.mnt)
|
||||
})
|
||||
|
||||
process.on('SIGINT', function () {
|
||||
fuse.unmount(err => {
|
||||
if (err) {
|
||||
console.log('filesystem at ' + fuse.mnt + ' not unmounted', err)
|
||||
} else {
|
||||
console.log('filesystem at ' + fuse.mnt + ' unmounted')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
function emptyStat (mode) {
|
||||
return {
|
||||
mtime: new Date(),
|
||||
atime: new Date(),
|
||||
ctime: new Date(),
|
||||
nlink: 1,
|
||||
size: 100,
|
||||
mode: mode,
|
||||
uid: process.getuid ? process.getuid() : 0,
|
||||
gid: process.getgid ? process.getgid() : 0
|
||||
}
|
||||
}
|
@ -90,6 +90,8 @@ typedef struct {
|
||||
napi_ref on_symlink;
|
||||
|
||||
struct fuse *fuse;
|
||||
struct fuse_chan *ch;
|
||||
bool mounted;
|
||||
uv_async_t async;
|
||||
} fuse_thread_t;
|
||||
|
||||
@ -637,7 +639,7 @@ NAPI_METHOD(fuse_native_signal_readdir) {
|
||||
}
|
||||
|
||||
NAPI_METHOD(fuse_native_mount) {
|
||||
NAPI_ARGV(10)
|
||||
NAPI_ARGV(11)
|
||||
|
||||
NAPI_ARGV_UTF8(mnt, 1024, 0);
|
||||
NAPI_ARGV_UTF8(mntopts, 1024, 1);
|
||||
@ -711,6 +713,8 @@ NAPI_METHOD(fuse_native_mount) {
|
||||
struct fuse *fuse = fuse_new(ch, &args, &ops, sizeof(struct fuse_operations), ft);
|
||||
|
||||
ft->fuse = fuse;
|
||||
ft->ch = ch;
|
||||
ft->mounted = true;
|
||||
|
||||
if (fuse == NULL) {
|
||||
napi_throw_error(env, "fuse failed", "fuse failed");
|
||||
@ -723,10 +727,27 @@ NAPI_METHOD(fuse_native_mount) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NAPI_METHOD(fuse_native_unmount) {
|
||||
NAPI_ARGV(2)
|
||||
NAPI_ARGV_UTF8(mnt, 1024, 0);
|
||||
NAPI_ARGV_BUFFER_CAST(fuse_thread_t *, ft, 1);
|
||||
|
||||
if (ft != NULL && ft->mounted) {
|
||||
fuse_unmount(mnt, ft->ch);
|
||||
printf("joining\n");
|
||||
pthread_join(ft->thread, NULL);
|
||||
printf("joined\n");
|
||||
}
|
||||
ft->mounted = false;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NAPI_INIT() {
|
||||
pthread_key_create(&(thread_locals_key), NULL); // TODO: add destructor
|
||||
|
||||
NAPI_EXPORT_FUNCTION(fuse_native_mount)
|
||||
NAPI_EXPORT_FUNCTION(fuse_native_unmount)
|
||||
NAPI_EXPORT_FUNCTION(fuse_native_signal_path)
|
||||
NAPI_EXPORT_FUNCTION(fuse_native_signal_stat)
|
||||
NAPI_EXPORT_FUNCTION(fuse_native_signal_statfs)
|
||||
|
300
index.js
300
index.js
@ -1,5 +1,13 @@
|
||||
const binding = require('node-gyp-build')(__dirname)
|
||||
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 ],
|
||||
@ -41,12 +49,14 @@ const Opcodes = new Map([
|
||||
class Fuse {
|
||||
constructor (mnt, ops, opts = {}) {
|
||||
this.opts = opts
|
||||
this.mnt = path.resolve(mnt)
|
||||
|
||||
this.ops = ops
|
||||
this.mnt = mnt
|
||||
|
||||
this._thread = Buffer.alloc(binding.sizeof_fuse_thread_t)
|
||||
// Keep the process alive while fuse is mounted.
|
||||
this._timer = null
|
||||
|
||||
const implemented = []
|
||||
const implemented = ['init', 'error', 'getattr']
|
||||
if (ops) {
|
||||
for (const [name, code] of Opcodes) {
|
||||
if (ops[name]) implemented.push(code)
|
||||
@ -58,6 +68,74 @@ class Fuse {
|
||||
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))
|
||||
@ -66,14 +144,43 @@ class Fuse {
|
||||
process.nextTick(() => signalFunc.apply(null, args))
|
||||
}
|
||||
|
||||
mount () {
|
||||
binding.fuse_native_mount(this.mnt, '-odebug', 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)
|
||||
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 () {
|
||||
binding.fuse_native_unmount(this.mnt)
|
||||
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) {
|
||||
@ -201,6 +308,133 @@ class Fuse {
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
@ -251,47 +485,5 @@ function getStatArray (stat) {
|
||||
return ints
|
||||
}
|
||||
|
||||
function emptyStat (mode) {
|
||||
return {
|
||||
mtime: new Date(),
|
||||
atime: new Date(),
|
||||
ctime: new Date(),
|
||||
nlink: 1,
|
||||
size: 100,
|
||||
mode: mode,
|
||||
uid: process.getuid ? process.getuid() : 0,
|
||||
gid: process.getgid ? process.getgid() : 0
|
||||
}
|
||||
}
|
||||
|
||||
const f = new Fuse('mnt', {
|
||||
getattr: (path, cb) => {
|
||||
if (path === '/') return cb(0, emptyStat(16877))
|
||||
return cb(0, emptyStat(33188))
|
||||
},
|
||||
access: (path, mode, cb) => {
|
||||
return cb(0, 0)
|
||||
},
|
||||
setxattr: (path, name, buffer, length, offset, cb) => {
|
||||
return cb(0)
|
||||
},
|
||||
utimens: (path, atim, mtim, cb) => {
|
||||
return cb(0)
|
||||
},
|
||||
readdir: (path, cb) => {
|
||||
if (path === '/') {
|
||||
return cb(0, ['a', 'b', 'c'], Array(3).fill('a').map(() => emptyStat(16877)))
|
||||
}
|
||||
return cb(0, [], [])
|
||||
}
|
||||
})
|
||||
f.mount()
|
||||
|
||||
setInterval(() => {
|
||||
if (global.gc) gc()
|
||||
}, 1000)
|
||||
|
||||
// setTimeout(function () {
|
||||
// foo = null
|
||||
// console.log('now!')
|
||||
// }, 5000)
|
||||
function noop () {}
|
||||
function call (cb) { cb() }
|
||||
|
Loading…
Reference in New Issue
Block a user