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

tests all pass

This commit is contained in:
Andrew Osheroff 2019-08-06 10:55:08 +02:00
parent 97e42d4fb9
commit 2961856b5b
9 changed files with 250 additions and 195 deletions

View File

@ -10,7 +10,8 @@
], ],
"sources": [ "sources": [
"fuse-native.c" "fuse-native.c"
] ],
"cflags": ["-rdynamic"]
}, { }, {
"target_name": "postinstall", "target_name": "postinstall",
"type": "none", "type": "none",

View File

@ -60,7 +60,7 @@ fuse.mount(err => {
console.log('filesystem mounted on ' + fuse.mnt) console.log('filesystem mounted on ' + fuse.mnt)
}) })
process.on('SIGINT', function () { process.once('SIGINT', function () {
fuse.unmount(err => { fuse.unmount(err => {
if (err) { if (err) {
console.log('filesystem at ' + fuse.mnt + ' not unmounted', err) console.log('filesystem at ' + fuse.mnt + ' not unmounted', err)

View File

@ -1,16 +1,17 @@
#define FUSE_USE_VERSION 29 #define FUSE_USE_VERSION 29
#include "semaphore.h"
#include <uv.h> #include <uv.h>
#include <node_api.h> #include <node_api.h>
#include <napi-macros.h> #include <napi-macros.h>
#include <string.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <fuse.h> #include <fuse.h>
#include <fuse_opt.h> #include <fuse_opt.h>
#include <fuse_common.h>
#include <fuse_lowlevel.h> #include <fuse_lowlevel.h>
#include <unistd.h> #include <unistd.h>
@ -29,14 +30,11 @@
napi_close_handle_scope(env, scope); napi_close_handle_scope(env, scope);
#define FUSE_NATIVE_HANDLER(name, blk)\ #define FUSE_NATIVE_HANDLER(name, blk)\
struct fuse_context *ctx = fuse_get_context();\
fuse_thread_t *ft = (fuse_thread_t *) ctx->private_data;\
fuse_thread_locals_t *l = get_thread_locals();\ fuse_thread_locals_t *l = get_thread_locals();\
l->fuse = ft;\
l->op = op_##name;\ l->op = op_##name;\
blk\ blk\
uv_async_send(&(l->async));\ uv_async_send(&(l->async));\
fuse_native_semaphore_wait(&(l->sem));\ uv_sem_wait(&(l->sem));\
return l->res; return l->res;
#define FUSE_METHOD(name, callbackArgs, signalArgs, signature, callBlk, callbackBlk, signalBlk)\ #define FUSE_METHOD(name, callbackArgs, signalArgs, signature, callBlk, callbackBlk, signalBlk)\
@ -57,7 +55,7 @@
int ret = NULL;\ int ret = NULL;\
signalBlk\ signalBlk\
l->res = ret ? ret : res;\ l->res = ret ? ret : res;\
fuse_native_semaphore_signal(&(l->sem));\ uv_sem_post(&(l->sem));\
return ret;\ return ret;\
}\ }\
static int fuse_native_##name signature {\ static int fuse_native_##name signature {\
@ -115,8 +113,13 @@ typedef struct {
struct fuse *fuse; struct fuse *fuse;
struct fuse_chan *ch; struct fuse_chan *ch;
bool mounted; char* mnt;
char* mntopts;
int mounted;
uv_async_t async; uv_async_t async;
uv_mutex_t mut;
uv_sem_t sem;
} fuse_thread_t; } fuse_thread_t;
typedef struct { typedef struct {
@ -156,7 +159,7 @@ typedef struct {
// Internal bookkeeping // Internal bookkeeping
fuse_thread_t *fuse; fuse_thread_t *fuse;
fuse_native_semaphore_t sem; uv_sem_t sem;
uv_async_t async; uv_async_t async;
} fuse_thread_locals_t; } fuse_thread_locals_t;
@ -764,7 +767,28 @@ static void fuse_native_dispatch (uv_async_t* handle, int status) {
} }
} }
static void fuse_native_async_init (uv_async_t* handle, int status) {
fuse_thread_locals_t *l = (fuse_thread_locals_t *) handle->data;
fuse_thread_t *ft = l->fuse;
int err = uv_async_init(uv_default_loop(), &(l->async), (uv_async_cb) fuse_native_dispatch);
uv_unref(&(l->async));
uv_sem_init(&(l->sem), 0);
l->async.data = l;
uv_sem_post(&(ft->sem));
if (err < 0) {
printf("uv_async_init failed: %i\n", err);
return NULL;
}
}
static fuse_thread_locals_t* get_thread_locals () { static fuse_thread_locals_t* get_thread_locals () {
struct fuse_context *ctx = fuse_get_context();
fuse_thread_t *ft = (fuse_thread_t *) ctx->private_data;
void *data = pthread_getspecific(thread_locals_key); void *data = pthread_getspecific(thread_locals_key);
if (data != NULL) { if (data != NULL) {
@ -772,19 +796,20 @@ static fuse_thread_locals_t* get_thread_locals () {
} }
fuse_thread_locals_t* l = (fuse_thread_locals_t *) malloc(sizeof(fuse_thread_locals_t)); fuse_thread_locals_t* l = (fuse_thread_locals_t *) malloc(sizeof(fuse_thread_locals_t));
l->fuse = ft;
// TODO: mutex me?? // Need to lock the mutation of l->async.
int err = uv_async_init(uv_default_loop(), &(l->async), (uv_async_cb) fuse_native_dispatch); uv_mutex_lock(&(ft->mut));
ft->async.data = l;
// Notify the main thread to uv_async_init l->async.
uv_async_send(&(ft->async));
uv_sem_wait(&(ft->sem));
l->async.data = l; l->async.data = l;
if (err < 0) {
printf("uv_async failed: %i\n", err);
return NULL;
}
fuse_native_semaphore_init(&(l->sem));
pthread_setspecific(thread_locals_key, (void *) l); pthread_setspecific(thread_locals_key, (void *) l);
uv_mutex_unlock(&(ft->mut));
return l; return l;
} }
@ -793,10 +818,9 @@ static void* start_fuse_thread (void *data) {
fuse_thread_t *ft = (fuse_thread_t *) data; fuse_thread_t *ft = (fuse_thread_t *) data;
fuse_loop_mt(ft->fuse); fuse_loop_mt(ft->fuse);
// printf("her nu\n"); fuse_unmount(ft->mnt, ft->ch);
// fuse_unmount(mnt, ch); fuse_session_remove_chan(ft->ch);
// fuse_session_remove_chan(ch); fuse_destroy(ft->fuse);
// fuse_destroy(fuse);
return NULL; return NULL;
} }
@ -816,14 +840,12 @@ NAPI_METHOD(fuse_native_mount) {
} }
NAPI_FOR_EACH(handlers, handler) { NAPI_FOR_EACH(handlers, handler) {
printf("creating reference for handler: %d\n", i);
napi_create_reference(env, handler, 1, &ft->handlers[i]); napi_create_reference(env, handler, 1, &ft->handlers[i]);
} }
ft->env = env; ft->env = env;
struct fuse_operations ops = { }; struct fuse_operations ops = { };
if (implemented[op_access]) ops.access = fuse_native_access; if (implemented[op_access]) ops.access = fuse_native_access;
if (implemented[op_truncate]) ops.truncate = fuse_native_truncate; if (implemented[op_truncate]) ops.truncate = fuse_native_truncate;
if (implemented[op_ftruncate]) ops.ftruncate = fuse_native_ftruncate; if (implemented[op_ftruncate]) ops.ftruncate = fuse_native_ftruncate;
@ -875,11 +897,18 @@ NAPI_METHOD(fuse_native_mount) {
struct fuse *fuse = fuse_new(ch, &args, &ops, sizeof(struct fuse_operations), ft); struct fuse *fuse = fuse_new(ch, &args, &ops, sizeof(struct fuse_operations), ft);
uv_mutex_init(&(ft->mut));
uv_sem_init(&(ft->sem), 0);
ft->mnt = mnt;
ft->mntopts = mntopts;
ft->fuse = fuse; ft->fuse = fuse;
ft->ch = ch; ft->ch = ch;
ft->mounted = true; ft->mounted++;
if (fuse == NULL) { int err = uv_async_init(uv_default_loop(), &(ft->async), (uv_async_cb) fuse_native_async_init);
if (fuse == NULL || err < 0) {
napi_throw_error(env, "fuse failed", "fuse failed"); napi_throw_error(env, "fuse failed", "fuse failed");
return NULL; return NULL;
} }
@ -896,12 +925,11 @@ NAPI_METHOD(fuse_native_unmount) {
NAPI_ARGV_BUFFER_CAST(fuse_thread_t *, ft, 1); NAPI_ARGV_BUFFER_CAST(fuse_thread_t *, ft, 1);
if (ft != NULL && ft->mounted) { if (ft != NULL && ft->mounted) {
fuse_unmount(mnt, ft->ch);
printf("joining\n");
pthread_join(ft->thread, NULL); pthread_join(ft->thread, NULL);
printf("joined\n");
} }
ft->mounted = false;
uv_unref(&(ft->async));
ft->mounted--;
return NULL; return NULL;
} }

247
index.js
View File

@ -1,142 +1,143 @@
const os = require('os') const os = require('os')
const fs = require('fs') const fs = require('fs')
const path = require('path') const path = require('path')
const { exec } = require('child_process')
const Nanoresource = require('nanoresource')
const binding = require('node-gyp-build')(__dirname)
const IS_OSX = os.platform() === 'darwin' const IS_OSX = os.platform() === 'darwin'
const OSX_FOLDER_ICON = '/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/GenericFolderIcon.icns' 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 HAS_FOLDER_ICON = IS_OSX && fs.existsSync(OSX_FOLDER_ICON)
const binding = require('node-gyp-build')(__dirname)
const OpcodesAndDefaults = new Map([ const OpcodesAndDefaults = new Map([
['init', { ['init', {
op: 0 op: binding.op_init
}], }],
['error', { ['error', {
op: 1 op: binding.op_error
}], }],
['access', { ['access', {
op: 2, op: binding.op_access,
defaults: [0] defaults: [0]
}], }],
['statfs', { ['statfs', {
op: 3, op: binding.op_statfs,
defaults: [getStatfsArray()] defaults: [getStatfsArray()]
}], }],
['fgetattr', { ['fgetattr', {
op: 4, op: binding.op_fgetattr,
defaults: [getStatArray()] defaults: [getStatArray()]
}], }],
['getattr', { ['getattr', {
op: 5, op: binding.op_getattr,
defaults: [getStatArray()] defaults: [getStatArray()]
}], }],
['flush', { ['flush', {
op: 6 op: binding.op_flush
}], }],
['fsync', { ['fsync', {
op: 7 op: binding.op_fsync
}], }],
['fsyncdir', { ['fsyncdir', {
op: 8 op: binding.op_fsyncdir
}], }],
['readdir', { ['readdir', {
op: 9 op: binding.op_readdir
}], }],
['truncate', { ['truncate', {
op: 10 op: binding.op_truncate
}], }],
['ftruncate', { ['ftruncate', {
op: 11 op: binding.op_ftruncate
}], }],
['utimens', { ['utimens', {
op: 12 op: binding.op_utimens
}], }],
['readlink', { ['readlink', {
op: 13, op: binding.op_readlink,
defaults: [''] defaults: ['']
}], }],
['chown', { ['chown', {
op: 14 op: binding.op_chown
}], }],
['chmod', { ['chmod', {
op: 15 op: binding.op_chmod
}], }],
['mknod', { ['mknod', {
op: 16 op: binding.op_mknod
}], }],
['setxattr', { ['setxattr', {
op: 17 op: binding.op_setxattr
}], }],
['getxattr', { ['getxattr', {
op: 18 op: binding.op_getxattr
}], }],
['listxattr', { ['listxattr', {
op: 19 op: binding.op_listxattr
}], }],
['removexattr', { ['removexattr', {
op: 20 op: binding.op_removexattr
}], }],
['open', { ['open', {
op: 21, op: binding.op_open,
defaults: [0] defaults: [0]
}], }],
['opendir', { ['opendir', {
op: 22, op: binding.op_opendir,
defaults: [0] defaults: [0]
}], }],
['read', { ['read', {
op: 23, op: binding.op_read,
defaults: [0] defaults: [0]
}], }],
['write', { ['write', {
op: 24, op: binding.op_write,
defaults: [0] defaults: [0]
}], }],
['release', { ['release', {
op: 25 op: binding.op_release
}], }],
['releasedir', { ['releasedir', {
op: 26 op: binding.op_releasedir
}], }],
['create', { ['create', {
op: 27 op: binding.op_create
}], }],
['unlink', { ['unlink', {
op: 28 op: binding.op_unlink
}], }],
['rename', { ['rename', {
op: 29 op: binding.op_rename
}], }],
['link', { ['link', {
op: 30 op: binding.op_link
}], }],
['symlink', { ['symlink', {
op: 31 op: binding.op_symlink
}], }],
['mkdir', { ['mkdir', {
op: 32 op: binding.op_mkdir
}], }],
['rmdir', { ['rmdir', {
op: 33 op: binding.op_rmdir
}], }],
['destroy', { ['destroy', {
op: 34 op: binding.op_destroy
}] }]
]) ])
class Fuse { class Fuse extends Nanoresource {
constructor (mnt, ops, opts = {}) { constructor (mnt, ops, opts = {}) {
super()
this.opts = opts this.opts = opts
this.mnt = path.resolve(mnt) this.mnt = path.resolve(mnt)
this.ops = ops this.ops = ops
this._thread = Buffer.alloc(binding.sizeof_fuse_thread_t) this._thread = null
this._handlers = this._makeHandlerArray() this._handlers = this._makeHandlerArray()
// Keep the process alive while fuse is mounted.
this._timer = null
const implemented = [0, 1, 5] const implemented = [binding.op_init, binding.op_error, binding.op_getattr]
if (ops) { if (ops) {
for (const [name, { op }] of OpcodesAndDefaults) { for (const [name, { op }] of OpcodesAndDefaults) {
if (ops[name]) implemented.push(op) if (ops[name]) implemented.push(op)
@ -150,7 +151,7 @@ class Fuse {
_getImplementedArray () { _getImplementedArray () {
const implemented = new Uint32Array(35) const implemented = new Uint32Array(35)
for (const impl of [...this._implemented]) { for (const impl of this._implemented) {
implemented[impl] = 1 implemented[impl] = 1
} }
return implemented return implemented
@ -191,16 +192,6 @@ class Fuse {
return options.map(o => '-o' + o).join(' ') 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))
}
// Handlers
_makeHandlerArray () { _makeHandlerArray () {
const self = this const self = this
const handlers = new Array(OpcodesAndDefaults.size) const handlers = new Array(OpcodesAndDefaults.size)
@ -217,7 +208,7 @@ class Fuse {
function makeHandler (name, op, defaults, nativeSignal) { function makeHandler (name, op, defaults, nativeSignal) {
return function () { return function () {
const boundSignal = signal.bind(null, arguments[0]) const boundSignal = signal.bind(null, arguments[0])
const funcName = `_${name}` const funcName = `_op_${name}`
if (!self[funcName] || !self._implemented.has(op)) return boundSignal(-1, ...defaults) if (!self[funcName] || !self._implemented.has(op)) return boundSignal(-1, ...defaults)
return self[funcName].apply(self, [boundSignal, ...[...arguments].slice(2)]) return self[funcName].apply(self, [boundSignal, ...[...arguments].slice(2)])
} }
@ -230,7 +221,57 @@ class Fuse {
} }
} }
_init (signal) { // Lifecycle methods
_open (cb) {
this._thread = Buffer.alloc(binding.sizeof_fuse_thread_t)
this._openCallback = cb
const opts = this._fuseOptions()
const implemented = this._getImplementedArray()
return 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'))
return 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._handlers, implemented)
} catch (err) {
return cb(err)
}
})
})
}
_close (cb) {
const self = this
const mnt = JSON.stringify(this.mnt)
const cmd = os.platform() === 'darwin' ? `umount ${mnt}` : `fusermount -q -u ${mnt}`
exec(cmd, err => {
if (err) return cb(err)
return nativeUnmount()
})
function nativeUnmount () {
try {
binding.fuse_native_unmount(self.mnt, self._thread)
} catch (err) {
return cb(err)
}
return cb(null)
}
}
// Handlers
_op_init (signal) {
if (this._openCallback) {
process.nextTick(this._openCallback, null)
this._openCallback = null
}
if (!this.ops.init) { if (!this.ops.init) {
signal(0) signal(0)
return return
@ -240,7 +281,7 @@ class Fuse {
}) })
} }
_error (signal) { _op_error (signal) {
if (!this.ops.error) { if (!this.ops.error) {
signal(0) signal(0)
return return
@ -250,7 +291,7 @@ class Fuse {
}) })
} }
_statfs (signal) { _op_statfs (signal) {
this.ops.statfs((err, statfs) => { this.ops.statfs((err, statfs) => {
if (err) return signal(err) if (err) return signal(err)
const arr = getStatfsArray(statfs) const arr = getStatfsArray(statfs)
@ -258,7 +299,7 @@ class Fuse {
}) })
} }
_getattr (signal, path) { _op_getattr (signal, path) {
if (!this.ops.getattr) { if (!this.ops.getattr) {
if (path !== '/') { if (path !== '/') {
signal(Fuse.EPERM) signal(Fuse.EPERM)
@ -273,7 +314,7 @@ class Fuse {
}) })
} }
_fgetattr (signal, path, fd) { _op_fgetattr (signal, path, fd) {
if (!this.ops.fgetattr) { if (!this.ops.fgetattr) {
if (path !== '/') { if (path !== '/') {
signal(Fuse.EPERM) signal(Fuse.EPERM)
@ -288,31 +329,31 @@ class Fuse {
}) })
} }
_access (signal, path, mode) { _op_access (signal, path, mode) {
this.ops.access(path, mode, err => { this.ops.access(path, mode, err => {
return signal(err) return signal(err)
}) })
} }
_open (signal, path, flags) { _op_open (signal, path, flags) {
this.ops.open(path, flags, (err, fd) => { this.ops.open(path, flags, (err, fd) => {
return signal(err, fd) return signal(err, fd)
}) })
} }
_opendir (signal, path, flags) { _op_opendir (signal, path, flags) {
this.ops.opendir(path, flags, (err, fd) => { this.ops.opendir(path, flags, (err, fd) => {
return signal(err, fd) return signal(err, fd)
}) })
} }
_create (signal, path, mode) { _op_create (signal, path, mode) {
this.ops.create(path, mode, (err, fd) => { this.ops.create(path, mode, (err, fd) => {
return signal(err, fd) return signal(err, fd)
}) })
} }
_utimens (signal, path, atim, mtim) { _op_utimens (signal, path, atim, mtim) {
atim = getDoubleInt(atim, 0) atim = getDoubleInt(atim, 0)
mtim = getDoubleInt(mtim, 0) mtim = getDoubleInt(mtim, 0)
this.ops.utimens(path, atim, mtim, err => { this.ops.utimens(path, atim, mtim, err => {
@ -320,31 +361,31 @@ class Fuse {
}) })
} }
_release (signal, path, fd) { _op_release (signal, path, fd) {
this.ops.release(path, fd, err => { this.ops.release(path, fd, err => {
return signal(err) return signal(err)
}) })
} }
_releasedir (signal, path, fd) { _op_releasedir (signal, path, fd) {
this.ops.releasedir(path, fd, err => { this.ops.releasedir(path, fd, err => {
return signal(err) return signal(err)
}) })
} }
_read (signal, path, fd, buf, len, offset) { _op_read (signal, path, fd, buf, len, offset) {
this.ops.read(path, fd, buf, len, offset, (err, bytesRead) => { this.ops.read(path, fd, buf, len, offset, (err, bytesRead) => {
return signal(err, bytesRead) return signal(err, bytesRead)
}) })
} }
_write (signal, path, fd, buf, len, offset) { _op_write (signal, path, fd, buf, len, offset) {
this.ops.write(path, fd, buf, len, offset, (err, bytesWritten) => { this.ops.write(path, fd, buf, len, offset, (err, bytesWritten) => {
return signal(err, bytesWritten) return signal(err, bytesWritten)
}) })
} }
_readdir (signal, path) { _op_readdir (signal, path) {
this.ops.readdir(path, (err, names, stats) => { this.ops.readdir(path, (err, names, stats) => {
if (err) return signal(err) if (err) return signal(err)
if (stats) stats = stats.map(getStatArray) if (stats) stats = stats.map(getStatArray)
@ -352,121 +393,121 @@ class Fuse {
}) })
} }
_setxattr (signal, path, name, value, size, position, flags) { _op_setxattr (signal, path, name, value, size, position, flags) {
this.ops.setxattr(path, name, value, size, position, flags, err => { this.ops.setxattr(path, name, value, size, position, flags, err => {
return signal(err) return signal(err)
}) })
} }
_getxattr (signal, path, name, value, size, position) { _op_getxattr (signal, path, name, value, size, position) {
this.ops.getxattr(path, name, value, size, position, err => { this.ops.getxattr(path, name, value, size, position, err => {
return signal(err) return signal(err)
}) })
} }
_listxattr (signal, path, list, size) { _op_listxattr (signal, path, list, size) {
this.ops.listxattr(path, list, size, err => { this.ops.listxattr(path, list, size, err => {
return signal(err) return signal(err)
}) })
} }
_removexattr (signal, path, name) { _op_removexattr (signal, path, name) {
this.ops.removexattr(path, name, err => { this.ops.removexattr(path, name, err => {
return signal(err) return signal(err)
}) })
} }
_flush (signal, path, datasync, fd) { _op_flush (signal, path, datasync, fd) {
this.ops.flush(path, datasync, fd, err => { this.ops.flush(path, datasync, fd, err => {
return signal(err) return signal(err)
}) })
} }
_fsync (signal, path, datasync, fd) { _op_fsync (signal, path, datasync, fd) {
this.ops.fsync(path, datasync, fd, err => { this.ops.fsync(path, datasync, fd, err => {
return signal(err) return signal(err)
}) })
} }
_fsyncdir (signal, path, datasync, fd) { _op_fsyncdir (signal, path, datasync, fd) {
this.ops.fsyncdir(path, datasync, fd, err => { this.ops.fsyncdir(path, datasync, fd, err => {
return signal(err) return signal(err)
}) })
} }
_truncate (signal, path, size) { _op_truncate (signal, path, size) {
this.ops.truncate(path, size, err => { this.ops.truncate(path, size, err => {
return signal(err) return signal(err)
}) })
} }
_ftruncate (signal, path, size, fd) { _op_ftruncate (signal, path, size, fd) {
this.ops.ftruncate(path, size, fd, err => { this.ops.ftruncate(path, size, fd, err => {
return signal(err) return signal(err)
}) })
} }
_readlink (signal, path) { _op_readlink (signal, path) {
this.ops.readlink(path, (err, linkname) => { this.ops.readlink(path, (err, linkname) => {
return signal(err, linkname) return signal(err, linkname)
}) })
} }
_chown (signal, path, uid, gid) { _op_chown (signal, path, uid, gid) {
this.ops.chown(path, uid, gid, err => { this.ops.chown(path, uid, gid, err => {
return signal(err) return signal(err)
}) })
} }
_chmod (signal, path, mode) { _op_chmod (signal, path, mode) {
this.ops.chmod(path, mode, err => { this.ops.chmod(path, mode, err => {
return signal(err) return signal(err)
}) })
} }
_mknod (signal, path, mode, dev) { _op_mknod (signal, path, mode, dev) {
this.ops.mknod(path, mode, dev, err => { this.ops.mknod(path, mode, dev, err => {
return signal(err) return signal(err)
}) })
} }
_unlink (signal, path) { _op_unlink (signal, path) {
this.ops.unlink(path, err => { this.ops.unlink(path, err => {
return signal(err) return signal(err)
}) })
} }
_rename (signal, src, dest, flags) { _op_rename (signal, src, dest, flags) {
this.ops.rename(src, dest, flags, err => { this.ops.rename(src, dest, flags, err => {
return signal(err) return signal(err)
}) })
} }
_link (signal, src, dest) { _op_link (signal, src, dest) {
this.ops.link(src, dest, err => { this.ops.link(src, dest, err => {
return signal(err) return signal(err)
}) })
} }
_symlink (signal, src, dest) { _op_symlink (signal, src, dest) {
this.ops.symlink(src, dest, err => { this.ops.symlink(src, dest, err => {
return signal(err) return signal(err)
}) })
} }
_mkdir (signal, path, mode) { _op_mkdir (signal, path, mode) {
this.ops.mkdir(path, mode, err => { this.ops.mkdir(path, mode, err => {
return signal(err) return signal(err)
}) })
} }
_rmdir (signal, path) { _op_rmdir (signal, path) {
this.ops.rmdir(path, err => { this.ops.rmdir(path, err => {
return signal(err) return signal(err)
}) })
} }
_destroy (signal) { _op_destroy (signal) {
this.ops.destroy(err => { this.ops.destroy(err => {
return signal(err) return signal(err)
}) })
@ -475,35 +516,11 @@ class Fuse {
// Public API // Public API
mount (cb) { mount (cb) {
const opts = this._fuseOptions() return this._open(cb)
const implemented = this._getImplementedArray()
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._handlers, implemented)
} catch (err) {
return cb(err)
}
this._timer = setInterval(() => {}, 10000)
return cb(null)
})
})
} }
unmount (cb) { unmount (cb) {
// TODO: asyncify return this._close(cb)
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) { errno (code) {

View File

@ -5,15 +5,17 @@
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"install": "node-gyp-build", "install": "node-gyp-build",
"test": "standard && test/*.js", "test": "tape test/*.js",
"prebuild": "prebuildify -a --strip", "prebuild": "prebuildify -a --strip",
"prebuild-ia32": "prebuildify -a --strip --arch=ia32" "prebuild-ia32": "prebuildify -a --strip --arch=ia32"
}, },
"gypfile": true, "gypfile": true,
"dependencies": { "dependencies": {
"fuse-shared-library": "^1.0.1", "fuse-shared-library": "^1.0.1",
"nanoresource": "^1.2.0",
"napi-macros": "^2.0.0", "napi-macros": "^2.0.0",
"node-gyp-build": "^3.2.2" "node-gyp-build": "^3.2.2",
"why-is-node-running": "^2.1.0"
}, },
"devDependencies": { "devDependencies": {
"concat-stream": "^2.0.0", "concat-stream": "^2.0.0",

View File

@ -10,26 +10,26 @@ tape('readlink', function (t) {
var ops = { var ops = {
force: true, force: true,
readdir: function (path, cb) { readdir: function (path, cb) {
if (path === '/') return cb(null, ['hello', 'link']) if (path === '/') return process.nextTick(cb, null, ['hello', 'link'])
return cb(Fuse.ENOENT) return process.nextTick(cb, Fuse.ENOENT)
}, },
readlink: function (path, cb) { readlink: function (path, cb) {
cb(0, 'hello') process.nextTick(cb, 0, 'hello')
}, },
getattr: function (path, cb) { getattr: function (path, cb) {
if (path === '/') return cb(null, stat({ mode: 'dir', size: 4096 })) if (path === '/') return process.nextTick(cb, null, stat({ mode: 'dir', size: 4096 }))
if (path === '/hello') return cb(null, stat({ mode: 'file', size: 11 })) if (path === '/hello') return process.nextTick(cb, null, stat({ mode: 'file', size: 11 }))
if (path === '/link') return cb(null, stat({ mode: 'link', size: 5 })) if (path === '/link') return process.nextTick(cb, null, stat({ mode: 'link', size: 5 }))
return cb(Fuse.ENOENT) return process.nextTick(cb, Fuse.ENOENT)
}, },
open: function (path, flags, cb) { open: function (path, flags, cb) {
cb(0, 42) process.nextTick(cb, 0, 42)
}, },
read: function (path, fd, buf, len, pos, cb) { read: function (path, fd, buf, len, pos, cb) {
var str = 'hello world'.slice(pos, pos + len) var str = 'hello world'.slice(pos, pos + len)
if (!str) return cb(0) if (!str) return process.nextTick(cb, 0)
buf.write(str) buf.write(str)
return cb(str.length) return process.nextTick(cb, str.length)
} }
} }
@ -51,7 +51,7 @@ tape('readlink', function (t) {
fs.readFile(path.join(mnt, 'link'), function (err, buf) { fs.readFile(path.join(mnt, 'link'), function (err, buf) {
t.error(err, 'no error') t.error(err, 'no error')
t.same(buf, new Buffer('hello world'), 'can read link content') t.same(buf, Buffer.from('hello world'), 'can read link content')
fuse.unmount(function () { fuse.unmount(function () {
t.end() t.end()

View File

@ -1,26 +1,31 @@
var mnt = require('./fixtures/mnt') var mnt = require('./fixtures/mnt')
var fuse = require('../')
var tape = require('tape') var tape = require('tape')
var Fuse = require('../')
tape('mount', function (t) { tape('mount', function (t) {
fuse.mount(mnt, { force: true }, function (err) { const fuse = new Fuse(mnt, {}, { force: true })
fuse.mount(function (err) {
t.error(err, 'no error') t.error(err, 'no error')
t.ok(true, 'works') t.ok(true, 'works')
fuse.unmount(mnt, function () { fuse.unmount(function () {
t.end() t.end()
}) })
}) })
}) })
tape('mount + unmount + mount', function (t) { tape('mount + unmount + mount', function (t) {
fuse.mount(mnt, { force: true }, function (err) { const fuse1 = new Fuse(mnt, {}, { force: true, debug: false })
const fuse2 = new Fuse(mnt, {}, { force: true, debug: false })
fuse1.mount(function (err) {
t.error(err, 'no error') t.error(err, 'no error')
t.ok(true, 'works') t.ok(true, 'works')
fuse.unmount(mnt, function () { fuse1.unmount(function () {
fuse.mount(mnt, { force: true }, function (err) { fuse2.mount(function (err) {
t.error(err, 'no error') t.error(err, 'no error')
t.ok(true, 'works') t.ok(true, 'works')
fuse.unmount(mnt, function () { fuse2.unmount(function () {
t.end() t.end()
}) })
}) })
@ -29,14 +34,16 @@ tape('mount + unmount + mount', function (t) {
}) })
tape('mnt point must exist', function (t) { tape('mnt point must exist', function (t) {
fuse.mount(mnt + '.does-not-exist', {}, function (err) { const fuse = new Fuse('.does-not-exist', {}, { debug: false })
fuse.mount(function (err) {
t.ok(err, 'had error') t.ok(err, 'had error')
t.end() t.end()
}) })
}) })
tape('mnt point must be directory', function (t) { tape('mnt point must be directory', function (t) {
fuse.mount(__filename, {}, function (err) { const fuse = new Fuse(__filename, {}, { debug: false })
fuse.mount(function (err) {
t.ok(err, 'had error') t.ok(err, 'had error')
t.end() t.end()
}) })

View File

@ -11,27 +11,27 @@ tape('read', function (t) {
var ops = { var ops = {
force: true, force: true,
readdir: function (path, cb) { readdir: function (path, cb) {
if (path === '/') return cb(null, ['test']) if (path === '/') return process.nextTick(cb, null, ['test'])
return cb(Fuse.ENOENT) return process.nextTick(cb, Fuse.ENOENT)
}, },
getattr: function (path, cb) { getattr: function (path, cb) {
if (path === '/') return cb(null, stat({ mode: 'dir', size: 4096 })) if (path === '/') return process.nextTick(cb, null, stat({ mode: 'dir', size: 4096 }))
if (path === '/test') return cb(null, stat({ mode: 'file', size: 11 })) if (path === '/test') return process.nextTick(cb, null, stat({ mode: 'file', size: 11 }))
return cb(Fuse.ENOENT) return process.nextTick(cb, Fuse.ENOENT)
}, },
open: function (path, flags, cb) { open: function (path, flags, cb) {
cb(0, 42) return process.nextTick(cb, 0, 42)
}, },
release: function (path, fd, cb) { release: function (path, fd, cb) {
console.log('IN JS RELEASE')
t.same(fd, 42, 'fd was passed to release') t.same(fd, 42, 'fd was passed to release')
cb(0) return process.nextTick(cb, 0)
}, },
read: function (path, fd, buf, len, pos, cb) { read: function (path, fd, buf, len, pos, cb) {
var str = 'hello world'.slice(pos, pos + len) var str = 'hello world'.slice(pos, pos + len)
if (!str) return cb(0) if (!str) return process.nextTick(cb, 0)
buf.write(str) buf.write(str)
return cb(str.length) return process.nextTick(cb, str.length)
} }
} }
@ -41,17 +41,17 @@ tape('read', function (t) {
fs.readFile(path.join(mnt, 'test'), function (err, buf) { fs.readFile(path.join(mnt, 'test'), function (err, buf) {
t.error(err, 'no error') t.error(err, 'no error')
t.same(buf, new Buffer('hello world'), 'read file') t.same(buf, Buffer.from('hello world'), 'read file')
fs.readFile(path.join(mnt, 'test'), function (err, buf) { fs.readFile(path.join(mnt, 'test'), function (err, buf) {
t.error(err, 'no error') t.error(err, 'no error')
t.same(buf, new Buffer('hello world'), 'read file again') t.same(buf, Buffer.from('hello world'), 'read file again')
fs.createReadStream(path.join(mnt, 'test'), { start: 0, end: 4 }).pipe(concat(function (buf) { fs.createReadStream(path.join(mnt, 'test'), { start: 0, end: 4 }).pipe(concat(function (buf) {
t.same(buf, new Buffer('hello'), 'partial read file') t.same(buf, Buffer.from('hello'), 'partial read file')
fs.createReadStream(path.join(mnt, 'test'), { start: 6, end: 10 }).pipe(concat(function (buf) { fs.createReadStream(path.join(mnt, 'test'), { start: 6, end: 10 }).pipe(concat(function (buf) {
t.same(buf, new Buffer('world'), 'partial read file + start offset') t.same(buf, Buffer.from('world'), 'partial read file + start offset')
fuse.unmount(function () { fuse.unmount(function () {
t.end() t.end()

View File

@ -8,35 +8,35 @@ const stat = require('./fixtures/stat')
tape('write', function (t) { tape('write', function (t) {
var created = false var created = false
var data = new Buffer(1024) var data = Buffer.alloc(1024)
var size = 0 var size = 0
var ops = { var ops = {
force: true, force: true,
readdir: function (path, cb) { readdir: function (path, cb) {
if (path === '/') return cb(null, created ? ['hello'] : []) if (path === '/') return process.nextTick(cb, null, created ? ['hello'] : [], [])
return cb(Fuse.ENOENT) return process.nextTick(cb, Fuse.ENOENT)
}, },
truncate: function (path, size, cb) { truncate: function (path, size, cb) {
cb(0) process.nextTick(cb, 0)
}, },
getattr: function (path, cb) { getattr: function (path, cb) {
if (path === '/') return cb(null, stat({ mode: 'dir', size: 4096 })) if (path === '/') return process.nextTick(cb, null, stat({ mode: 'dir', size: 4096 }))
if (path === '/hello' && created) return cb(0, stat({ mode: 'file', size: size })) if (path === '/hello' && created) return process.nextTick(cb, 0, stat({ mode: 'file', size: size }))
return cb(Fuse.ENOENT) return process.nextTick(cb, Fuse.ENOENT)
}, },
create: function (path, flags, cb) { create: function (path, flags, cb) {
t.ok(!created, 'file not created yet') t.ok(!created, 'file not created yet')
created = true created = true
cb(0, 42) process.nextTick(cb, 0, 42)
}, },
release: function (path, fd, cb) { release: function (path, fd, cb) {
cb(0) process.nextTick(cb, 0)
}, },
write: function (path, fd, buf, len, pos, cb) { write: function (path, fd, buf, len, pos, cb) {
buf.slice(0, len).copy(data, pos) buf.slice(0, len).copy(data, pos)
size = Math.max(pos + len, size) size = Math.max(pos + len, size)
cb(buf.length) process.nextTick(cb, buf.length)
} }
} }
@ -46,7 +46,7 @@ tape('write', function (t) {
fs.writeFile(path.join(mnt, 'hello'), 'hello world', function (err) { fs.writeFile(path.join(mnt, 'hello'), 'hello world', function (err) {
t.error(err, 'no error') t.error(err, 'no error')
t.same(data.slice(0, size), new Buffer('hello world'), 'data was written') t.same(data.slice(0, size), Buffer.from('hello world'), 'data was written')
fuse.unmount(function () { fuse.unmount(function () {
t.end() t.end()