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

Compare commits

..

No commits in common. "master" and "v2.1.0" have entirely different histories.

14 changed files with 108 additions and 335 deletions

View File

@ -33,19 +33,5 @@ before_deploy:
- ARCHIVE_NAME="${TRAVIS_TAG:-latest}-$TRAVIS_OS_NAME.tar" - ARCHIVE_NAME="${TRAVIS_TAG:-latest}-$TRAVIS_OS_NAME.tar"
- npm run prebuild - npm run prebuild
- if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then PREBUILD_ARCH=ia32 npm run prebuild; fi - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then PREBUILD_ARCH=ia32 npm run prebuild; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then PREBUILD_ARCH=ia32 npm run prebuild; fi
- cd prebuilds && tar cvf "../$ARCHIVE_NAME" . && cd .. - cd prebuilds && tar cvf "../$ARCHIVE_NAME" . && cd ..
cache:
npm: false
deploy:
provider: releases
draft: false
prerelease: true
api_key:
secure: "KPn3xR4LWcg/H259aSZh26XX0eapR88xSNUkBmEri/sCJSyZ0+asLZSv/HDD3KJP4HeuIKsQc0v8fcebD83fkvaSvlUzSppMQgniwuGC1cAefbrgZDwmJJ/n+lE8Wr9x4adOBTgICS5Uc8LlZ1PuJGm4mmequVs29BEw9738LzN4+3NpoCoWd0FAgGF0tDTsaYL1tJqERAyNIxHS+adUPe0F2r0d2UJ7mOrW7s8Ai6e6QryFsFvA2m0Xn/pQmNO/mcq+LPcw57pWuI3Hm3Cu3W8VPJXYp/yJaFqTAn3D9Fwz4tkmbfmca4ETwZYOS3lvL/rjLQ+69SJlRRu/QfPECkZven+wwsLX/DmyGHgEWqeGWjKj/NxYOIKUKEZZCVrF8cy4j9mac+LK6bAeDZERKSxUJ9GT5WsjvV3RNKgp3MZF7mtmj4IWXfgcuwFX49oIqhzSJsucBBXlB74J7Qua5VJPEAo/7X7Q+Y9IT9JHwxXsXVF5ZNj1PMlJicVD6oKi4XCFOVxSE9wdzlBwMOlUyBGhAIzS6lmxHOELYO9C7l8t/8Zvi4a+YGvOwn0dzLb9zuA1bzqJmEB1fkQMZXHvcEY1o5jSTQ0cNn1Wx4Ck9zyLyhnZ5KRXKzGQ1du55iVOThcbl/8j6zT218SiZMMtv8ZwPy4pJt4skMGsoOZtYlE="
file: "$ARCHIVE_NAME"
skip_cleanup: true
on:
tags: true
node: 'node'

View File

@ -1,5 +1,4 @@
# fuse-native # fuse-native
[![Build Status](https://travis-ci.org/fuse-friends/fuse-native.svg?branch=master)](https://travis-ci.org/fuse-friends/fuse-native)
Multithreaded FUSE bindings for Node JS. Multithreaded FUSE bindings for Node JS.
@ -73,8 +72,6 @@ Create a new `Fuse` object.
``` ```
displayFolder: 'Folder Name', // Add a name/icon to the mount volume on OSX, displayFolder: 'Folder Name', // Add a name/icon to the mount volume on OSX,
debug: false, // Enable detailed tracing of operations. debug: false, // Enable detailed tracing of operations.
force: false, // Attempt to unmount a the mountpoint before remounting.
mkdir: false // Create the mountpoint before mounting.
``` ```
Additionally, all (FUSE-specific options)[http://man7.org/linux/man-pages/man8/mount.fuse.8.html] will be passed to the underlying FUSE module (though we use camel casing instead of snake casing). Additionally, all (FUSE-specific options)[http://man7.org/linux/man-pages/man8/mount.fuse.8.html] will be passed to the underlying FUSE module (though we use camel casing instead of snake casing).

View File

@ -11,18 +11,7 @@
"sources": [ "sources": [
"fuse-native.c" "fuse-native.c"
], ],
'xcode_settings': { "cflags": ["-rdynamic"]
'OTHER_CFLAGS': [
'-g',
'-O3',
'-Wall'
]
},
'cflags': [
'-g',
'-O3',
'-Wall'
],
}, { }, {
"target_name": "postinstall", "target_name": "postinstall",
"type": "none", "type": "none",

View File

@ -18,10 +18,6 @@
#include <sys/wait.h> #include <sys/wait.h>
#include <pthread.h> #include <pthread.h>
static int IS_ARRAY_BUFFER_DETACH_SUPPORTED = 0;
napi_status napi_detach_arraybuffer(napi_env env, napi_value buf);
#define FUSE_NATIVE_CALLBACK(fn, blk)\ #define FUSE_NATIVE_CALLBACK(fn, blk)\
napi_env env = ft->env;\ napi_env env = ft->env;\
napi_handle_scope scope;\ napi_handle_scope scope;\
@ -47,7 +43,7 @@ napi_status napi_detach_arraybuffer(napi_env env, napi_value buf);
uint32_t op = op_##name;\ uint32_t op = op_##name;\
FUSE_NATIVE_CALLBACK(ft->handlers[op], {\ FUSE_NATIVE_CALLBACK(ft->handlers[op], {\
napi_value argv[callbackArgs + 2];\ napi_value argv[callbackArgs + 2];\
napi_get_reference_value(env, l->self, &(argv[0]));\ napi_create_external_buffer(env, sizeof(fuse_thread_locals_t), l, &fin, NULL, &(argv[0]));\
napi_create_uint32(env, l->op, &(argv[1]));\ napi_create_uint32(env, l->op, &(argv[1]));\
callbackBlk\ callbackBlk\
NAPI_MAKE_CALLBACK(env, NULL, ctx, callback, callbackArgs + 2, argv, NULL)\ NAPI_MAKE_CALLBACK(env, NULL, ctx, callback, callbackArgs + 2, argv, NULL)\
@ -120,7 +116,6 @@ typedef struct {
pthread_t thread; pthread_t thread;
pthread_attr_t attr; pthread_attr_t attr;
napi_ref ctx; napi_ref ctx;
napi_ref malloc;
// Operation handlers // Operation handlers
napi_ref handlers[35]; napi_ref handlers[35];
@ -137,8 +132,6 @@ typedef struct {
} fuse_thread_t; } fuse_thread_t;
typedef struct { typedef struct {
napi_ref self;
// Opcode // Opcode
uint32_t op; uint32_t op;
void *op_fn; void *op_fn;
@ -182,11 +175,15 @@ typedef struct {
} fuse_thread_locals_t; } fuse_thread_locals_t;
static pthread_key_t thread_locals_key; static pthread_key_t thread_locals_key;
static fuse_thread_locals_t* get_thread_locals(); static fuse_thread_locals_t* get_thread_locals ();
// Helpers // Helpers
// TODO: Extract into a separate file. // TODO: Extract into a separate file.
static void fin (napi_env env, void *fin_data, void* fin_hint) {
// noop
}
static uint64_t uint32s_to_uint64 (uint32_t **ints) { static uint64_t uint32s_to_uint64 (uint32_t **ints) {
uint64_t low = *((*ints)++); uint64_t low = *((*ints)++);
uint64_t high = *((*ints)++); uint64_t high = *((*ints)++);
@ -370,7 +367,7 @@ FUSE_METHOD_VOID(releasedir, 2, 0, (const char *path, struct fuse_file_info *inf
} }
}) })
FUSE_METHOD(read, 6, 2, (const char *path, char *buf, size_t len, off_t offset, struct fuse_file_info *info), { FUSE_METHOD(read, 6, 1, (const char *path, char *buf, size_t len, off_t offset, struct fuse_file_info *info), {
l->path = path; l->path = path;
l->buf = buf; l->buf = buf;
l->len = len; l->len = len;
@ -379,14 +376,14 @@ FUSE_METHOD(read, 6, 2, (const char *path, char *buf, size_t len, off_t offset,
}, { }, {
napi_create_string_utf8(env, l->path, NAPI_AUTO_LENGTH, &(argv[2])); napi_create_string_utf8(env, l->path, NAPI_AUTO_LENGTH, &(argv[2]));
napi_create_uint32(env, l->info->fh, &(argv[3])); napi_create_uint32(env, l->info->fh, &(argv[3]));
napi_create_external_buffer(env, l->len, (char *) l->buf, NULL, NULL, &(argv[4])); napi_create_external_buffer(env, l->len, (char *) l->buf, &fin, NULL, &(argv[4]));
napi_create_uint32(env, l->len, &(argv[5])); napi_create_uint32(env, l->len, &(argv[5]));
FUSE_UINT64_TO_INTS_ARGV(l->offset, 6) FUSE_UINT64_TO_INTS_ARGV(l->offset, 6)
}, { }, {
if (IS_ARRAY_BUFFER_DETACH_SUPPORTED == 1) assert(napi_detach_arraybuffer(env, argv[3]) == napi_ok); // TODO: handle bytes processed?
}) })
FUSE_METHOD(write, 6, 2, (const char *path, const char *buf, size_t len, off_t offset, struct fuse_file_info *info), { FUSE_METHOD(write, 6, 1, (const char *path, const char *buf, size_t len, off_t offset, struct fuse_file_info *info), {
l->path = path; l->path = path;
l->buf = buf; l->buf = buf;
l->len = len; l->len = len;
@ -395,11 +392,11 @@ FUSE_METHOD(write, 6, 2, (const char *path, const char *buf, size_t len, off_t o
}, { }, {
napi_create_string_utf8(env, l->path, NAPI_AUTO_LENGTH, &(argv[2])); napi_create_string_utf8(env, l->path, NAPI_AUTO_LENGTH, &(argv[2]));
napi_create_uint32(env, l->info->fh, &(argv[3])); napi_create_uint32(env, l->info->fh, &(argv[3]));
napi_create_external_buffer(env, l->len, (char *) l->buf, NULL, NULL, &(argv[4])); napi_create_external_buffer(env, l->len, (char *) l->buf, &fin, NULL, &(argv[4]));
napi_create_uint32(env, l->len, &(argv[5])); napi_create_uint32(env, l->len, &(argv[5]));
FUSE_UINT64_TO_INTS_ARGV(l->offset, 6) FUSE_UINT64_TO_INTS_ARGV(l->offset, 6)
}, { }, {
if (IS_ARRAY_BUFFER_DETACH_SUPPORTED == 1) assert(napi_detach_arraybuffer(env, argv[3]) == napi_ok); // TODO: handle bytes processed?
}) })
FUSE_METHOD(readdir, 1, 2, (const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *info), { FUSE_METHOD(readdir, 1, 2, (const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *info), {
@ -449,7 +446,7 @@ FUSE_METHOD(readdir, 1, 2, (const char *path, void *buf, fuse_fill_dir_t filler,
#ifdef __APPLE__ #ifdef __APPLE__
FUSE_METHOD(setxattr, 5, 1, (const char *path, const char *name, const char *value, size_t size, int flags, uint32_t position), { FUSE_METHOD_VOID(setxattr, 5, 0, (const char *path, const char *name, const char *value, size_t size, int flags, uint32_t position), {
l->path = path; l->path = path;
l->name = name; l->name = name;
l->value = value; l->value = value;
@ -459,14 +456,12 @@ FUSE_METHOD(setxattr, 5, 1, (const char *path, const char *name, const char *val
}, { }, {
napi_create_string_utf8(env, l->path, NAPI_AUTO_LENGTH, &(argv[2])); napi_create_string_utf8(env, l->path, NAPI_AUTO_LENGTH, &(argv[2]));
napi_create_string_utf8(env, l->name, NAPI_AUTO_LENGTH, &(argv[3])); napi_create_string_utf8(env, l->name, NAPI_AUTO_LENGTH, &(argv[3]));
napi_create_external_buffer(env, l->size, (char *) l->value, NULL, NULL, &(argv[4])); napi_create_external_buffer(env, l->size, (char *) l->value, &fin, NULL, &(argv[4]));
napi_create_uint32(env, l->position, &(argv[5])); napi_create_uint32(env, l->position, &(argv[5]));
napi_create_uint32(env, l->flags, &(argv[6])); napi_create_uint32(env, l->flags, &(argv[6]));
}, {
if (IS_ARRAY_BUFFER_DETACH_SUPPORTED == 1) assert(napi_detach_arraybuffer(env, argv[2]) == napi_ok);
}) })
FUSE_METHOD(getxattr, 4, 1, (const char *path, const char *name, char *value, size_t size, uint32_t position), { FUSE_METHOD_VOID(getxattr, 4, 0, (const char *path, const char *name, char *value, size_t size, uint32_t position), {
l->path = path; l->path = path;
l->name = name; l->name = name;
l->value = value; l->value = value;
@ -475,15 +470,13 @@ FUSE_METHOD(getxattr, 4, 1, (const char *path, const char *name, char *value, si
}, { }, {
napi_create_string_utf8(env, l->path, NAPI_AUTO_LENGTH, &(argv[2])); napi_create_string_utf8(env, l->path, NAPI_AUTO_LENGTH, &(argv[2]));
napi_create_string_utf8(env, l->name, NAPI_AUTO_LENGTH, &(argv[3])); napi_create_string_utf8(env, l->name, NAPI_AUTO_LENGTH, &(argv[3]));
napi_create_external_buffer(env, l->size, (char *) l->value, NULL, NULL, &(argv[4])); napi_create_external_buffer(env, l->size, (char *) l->value, &fin, NULL, &(argv[4]));
napi_create_uint32(env, l->position, &(argv[5])); napi_create_uint32(env, l->position, &(argv[5]));
}, {
if (IS_ARRAY_BUFFER_DETACH_SUPPORTED == 1) assert(napi_detach_arraybuffer(env, argv[2]) == napi_ok);
}) })
#else #else
FUSE_METHOD(setxattr, 5, 1, (const char *path, const char *name, const char *value, size_t size, int flags), { FUSE_METHOD_VOID(setxattr, 5, 0, (const char *path, const char *name, const char *value, size_t size, int flags), {
l->path = path; l->path = path;
l->name = name; l->name = name;
l->value = value; l->value = value;
@ -492,14 +485,12 @@ FUSE_METHOD(setxattr, 5, 1, (const char *path, const char *name, const char *val
}, { }, {
napi_create_string_utf8(env, l->path, NAPI_AUTO_LENGTH, &(argv[2])); napi_create_string_utf8(env, l->path, NAPI_AUTO_LENGTH, &(argv[2]));
napi_create_string_utf8(env, l->name, NAPI_AUTO_LENGTH, &(argv[3])); napi_create_string_utf8(env, l->name, NAPI_AUTO_LENGTH, &(argv[3]));
napi_create_external_buffer(env, l->size, (char *) l->value, NULL, NULL, &(argv[4])); napi_create_external_buffer(env, l->size, (char *) l->value, &fin, NULL, &(argv[4]));
napi_create_uint32(env, 0, &(argv[5])); // normalize apis between mac and linux napi_create_uint32(env, 0, &(argv[5])); // normalize apis between mac and linux
napi_create_uint32(env, l->flags, &(argv[6])); napi_create_uint32(env, l->flags, &(argv[6]));
}, {
if (IS_ARRAY_BUFFER_DETACH_SUPPORTED == 1) assert(napi_detach_arraybuffer(env, argv[2]) == napi_ok);
}) })
FUSE_METHOD(getxattr, 4, 1, (const char *path, const char *name, char *value, size_t size), { FUSE_METHOD_VOID(getxattr, 4, 0, (const char *path, const char *name, char *value, size_t size), {
l->path = path; l->path = path;
l->name = name; l->name = name;
l->value = value; l->value = value;
@ -507,23 +498,19 @@ FUSE_METHOD(getxattr, 4, 1, (const char *path, const char *name, char *value, si
}, { }, {
napi_create_string_utf8(env, l->path, NAPI_AUTO_LENGTH, &(argv[2])); napi_create_string_utf8(env, l->path, NAPI_AUTO_LENGTH, &(argv[2]));
napi_create_string_utf8(env, l->name, NAPI_AUTO_LENGTH, &(argv[3])); napi_create_string_utf8(env, l->name, NAPI_AUTO_LENGTH, &(argv[3]));
napi_create_external_buffer(env, l->size, (char *) l->value, NULL, NULL, &(argv[4])); napi_create_external_buffer(env, l->size, (char *) l->value, &fin, NULL, &(argv[4]));
napi_create_uint32(env, 0, &(argv[5])); napi_create_uint32(env, 0, &(argv[5]));
}, {
if (IS_ARRAY_BUFFER_DETACH_SUPPORTED == 1) assert(napi_detach_arraybuffer(env, argv[2]) == napi_ok);
}) })
#endif #endif
FUSE_METHOD(listxattr, 2, 1, (const char *path, char *list, size_t size), { FUSE_METHOD_VOID(listxattr, 2, 0, (const char *path, char *list, size_t size), {
l->path = path; l->path = path;
l->list = list; l->list = list;
l->size = size; l->size = size;
}, { }, {
napi_create_string_utf8(env, l->path, NAPI_AUTO_LENGTH, &(argv[2])); napi_create_string_utf8(env, l->path, NAPI_AUTO_LENGTH, &(argv[2]));
napi_create_external_buffer(env, l->size, l->list, NULL, NULL, &(argv[3])); napi_create_external_buffer(env, l->size, l->list, &fin, NULL, &(argv[3]));
}, {
if (IS_ARRAY_BUFFER_DETACH_SUPPORTED == 1) assert(napi_detach_arraybuffer(env, argv[2]) == napi_ok);
}) })
FUSE_METHOD_VOID(removexattr, 2, 0, (const char *path, const char *name), { FUSE_METHOD_VOID(removexattr, 2, 0, (const char *path, const char *name), {
@ -683,10 +670,8 @@ FUSE_METHOD_VOID(rmdir, 1, 0, (const char *path), {
static void fuse_native_dispatch_init (uv_async_t* handle, fuse_thread_locals_t* l, fuse_thread_t* ft) {\ static void fuse_native_dispatch_init (uv_async_t* handle, fuse_thread_locals_t* l, fuse_thread_t* ft) {\
FUSE_NATIVE_CALLBACK(ft->handlers[op_init], { FUSE_NATIVE_CALLBACK(ft->handlers[op_init], {
napi_value argv[2]; napi_value argv[2];
napi_create_external_buffer(env, sizeof(fuse_thread_locals_t), l, &fin, NULL, &(argv[0]));
napi_get_reference_value(env, l->self, &(argv[0]));
napi_create_uint32(env, l->op, &(argv[1])); napi_create_uint32(env, l->op, &(argv[1]));
NAPI_MAKE_CALLBACK(env, NULL, ctx, callback, 2, argv, NULL); NAPI_MAKE_CALLBACK(env, NULL, ctx, callback, 2, argv, NULL);
}) })
} }
@ -702,13 +687,10 @@ NAPI_METHOD(fuse_native_signal_init) {
static void * fuse_native_init (struct fuse_conn_info *conn) { static void * fuse_native_init (struct fuse_conn_info *conn) {
fuse_thread_locals_t *l = get_thread_locals(); fuse_thread_locals_t *l = get_thread_locals();
l->op = op_init; l->op = op_init;
l->op_fn = fuse_native_dispatch_init; l->op_fn = fuse_native_dispatch_init;
uv_async_send(&(l->async)); uv_async_send(&(l->async));
uv_sem_wait(&(l->sem)); uv_sem_wait(&(l->sem));
return l->fuse; return l->fuse;
} }
@ -718,27 +700,12 @@ static void fuse_native_dispatch (uv_async_t* handle) {
fuse_thread_locals_t *l = (fuse_thread_locals_t *) handle->data; fuse_thread_locals_t *l = (fuse_thread_locals_t *) handle->data;
fuse_thread_t *ft = l->fuse; fuse_thread_t *ft = l->fuse;
void (*fn)(uv_async_t *, fuse_thread_locals_t *, fuse_thread_t *) = l->op_fn; void (*fn)(uv_async_t *, fuse_thread_locals_t *, fuse_thread_t *) = l->op_fn;
fn(handle, l, ft); fn(handle, l, ft);
} }
static void fuse_native_async_init (uv_async_t* handle) { static void fuse_native_async_init (uv_async_t* handle) {
fuse_thread_t *ft = (fuse_thread_t *) handle->data; fuse_thread_locals_t *l = (fuse_thread_locals_t *) handle->data;
fuse_thread_locals_t *l; fuse_thread_t *ft = l->fuse;
FUSE_NATIVE_CALLBACK(ft->malloc, {
napi_value argv[1];
napi_create_uint32(ft->env, (uint32_t) sizeof(fuse_thread_locals_t), &(argv[0]));
napi_value buf;
NAPI_MAKE_CALLBACK(ft->env, NULL, ctx, callback, 1, argv, &buf);
size_t l_len;
napi_get_buffer_info(env, buf, (void **) &l, &l_len);
napi_create_reference(env, buf, 1, &(l->self));
})
int err = uv_async_init(uv_default_loop(), &(l->async), (uv_async_cb) fuse_native_dispatch); int err = uv_async_init(uv_default_loop(), &(l->async), (uv_async_cb) fuse_native_dispatch);
assert(err >= 0); assert(err >= 0);
@ -747,8 +714,6 @@ static void fuse_native_async_init (uv_async_t* handle) {
uv_sem_init(&(l->sem), 0); uv_sem_init(&(l->sem), 0);
l->async.data = l; l->async.data = l;
ft->async.data = l;
l->fuse = ft;
uv_sem_post(&(ft->sem)); uv_sem_post(&(ft->sem));
} }
@ -760,18 +725,21 @@ static fuse_thread_locals_t* get_thread_locals () {
void *data = pthread_getspecific(thread_locals_key); void *data = pthread_getspecific(thread_locals_key);
if (data != NULL) { if (data != NULL) {
return (fuse_thread_locals_t *) data; return (fuse_thread_locals_t *)data;
} }
fuse_thread_locals_t* l = (fuse_thread_locals_t *) malloc(sizeof(fuse_thread_locals_t));
l->fuse = ft;
// Need to lock the mutation of l->async. // Need to lock the mutation of l->async.
uv_mutex_lock(&(ft->mut)); uv_mutex_lock(&(ft->mut));
ft->async.data = ft; ft->async.data = l;
// Notify the main thread to uv_async_init l->async. // Notify the main thread to uv_async_init l->async.
uv_async_send(&(ft->async)); uv_async_send(&(ft->async));
uv_sem_wait(&(ft->sem)); uv_sem_wait(&(ft->sem));
fuse_thread_locals_t *l = (fuse_thread_locals_t*) ft->async.data; l->async.data = l;
pthread_setspecific(thread_locals_key, (void *) l); pthread_setspecific(thread_locals_key, (void *) l);
uv_mutex_unlock(&(ft->mut)); uv_mutex_unlock(&(ft->mut));
@ -791,15 +759,14 @@ static void* start_fuse_thread (void *data) {
} }
NAPI_METHOD(fuse_native_mount) { NAPI_METHOD(fuse_native_mount) {
NAPI_ARGV(7) NAPI_ARGV(6)
NAPI_ARGV_UTF8(mnt, 1024, 0); NAPI_ARGV_UTF8(mnt, 1024, 0);
NAPI_ARGV_UTF8(mntopts, 1024, 1); NAPI_ARGV_UTF8(mntopts, 1024, 1);
NAPI_ARGV_BUFFER_CAST(fuse_thread_t *, ft, 2); NAPI_ARGV_BUFFER_CAST(fuse_thread_t *, ft, 2);
napi_create_reference(env, argv[3], 1, &(ft->ctx)); napi_create_reference(env, argv[3], 1, &(ft->ctx));
napi_create_reference(env, argv[4], 1, &(ft->malloc)); napi_value handlers = argv[4];
napi_value handlers = argv[5]; NAPI_ARGV_BUFFER_CAST(uint32_t *, implemented, 5)
NAPI_ARGV_BUFFER_CAST(uint32_t *, implemented, 6)
for (int i = 0; i < 35; i++) { for (int i = 0; i < 35; i++) {
ft->handlers[i] = NULL; ft->handlers[i] = NULL;
@ -902,13 +869,6 @@ NAPI_METHOD(fuse_native_unmount) {
} }
NAPI_INIT() { NAPI_INIT() {
const napi_node_version* version;
assert(napi_get_node_version(env, &version) == napi_ok);
if (version->major > 12 || (version->major == 12 && version->minor >= 16)) {
IS_ARRAY_BUFFER_DETACH_SUPPORTED = 1;
}
pthread_key_create(&(thread_locals_key), NULL); // TODO: add destructor pthread_key_create(&(thread_locals_key), NULL); // TODO: add destructor
NAPI_EXPORT_SIZEOF(fuse_thread_t) NAPI_EXPORT_SIZEOF(fuse_thread_t)

105
index.js
View File

@ -12,7 +12,8 @@ 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 DEFAULT_TIMEOUT = 15 * 1000 const DEFAULT_TIMEOUT = 15 * 1000
const TIMEOUT_ERRNO = IS_OSX ? -60 : -110 const TIMEOUT_ERRNO = IS_OSX ? -110 : -60
const ENOTCONN = IS_OSX ? -57 : -107 const ENOTCONN = IS_OSX ? -57 : -107
const OpcodesAndDefaults = new Map([ const OpcodesAndDefaults = new Map([
@ -138,13 +139,11 @@ class Fuse extends Nanoresource {
this.opts = opts this.opts = opts
this.mnt = path.resolve(mnt) this.mnt = path.resolve(mnt)
this.ops = ops this.ops = ops
this.timeout = opts.timeout === false ? 0 : (opts.timeout || DEFAULT_TIMEOUT) this.timeout = opts.timeout || DEFAULT_TIMEOUT
this._force = !!opts.force this._force = !!opts.force
this._mkdir = !!opts.mkdir
this._thread = null this._thread = null
this._handlers = this._makeHandlerArray() this._handlers = this._makeHandlerArray()
this._threads = new Set()
const implemented = [binding.op_init, binding.op_error, binding.op_getattr] const implemented = [binding.op_init, binding.op_error, binding.op_getattr]
if (ops) { if (ops) {
@ -201,15 +200,10 @@ class Fuse extends Nanoresource {
return options.length ? '-o' + options.join(',') : '' return options.length ? '-o' + options.join(',') : ''
} }
_malloc (size) {
const buf = Buffer.alloc(size)
this._threads.add(buf)
return buf
}
_makeHandlerArray () { _makeHandlerArray () {
const self = this const self = this
const handlers = new Array(OpcodesAndDefaults.size) const handlers = new Array(OpcodesAndDefaults.size)
const to = this.timeout || DEFAULT_TIMEOUT
for (const [name, { op, defaults }] of OpcodesAndDefaults) { for (const [name, { op, defaults }] of OpcodesAndDefaults) {
const nativeSignal = binding[`fuse_native_signal_${name}`] const nativeSignal = binding[`fuse_native_signal_${name}`]
@ -221,17 +215,8 @@ class Fuse extends Nanoresource {
return handlers return handlers
function makeHandler (name, op, defaults, nativeSignal) { function makeHandler (name, op, defaults, nativeSignal) {
let to = self.timeout
if (typeof to === 'object' && to) {
const defaultTimeout = to.default || DEFAULT_TIMEOUT
to = to[name]
if (!to && to !== false) to = defaultTimeout
}
return function (nativeHandler, opCode, ...args) { return function (nativeHandler, opCode, ...args) {
const sig = signal.bind(null, nativeHandler) const boundSignal = autoTimeout(signal.bind(null, nativeHandler))
const input = [...args]
const boundSignal = to ? autoTimeout(sig, input) : sig
const funcName = `_op_${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, ...args]) return self[funcName].apply(self, [boundSignal, ...args])
@ -239,42 +224,24 @@ class Fuse extends Nanoresource {
function signal (nativeHandler, err, ...args) { function signal (nativeHandler, err, ...args) {
var arr = [nativeHandler, err, ...args] var arr = [nativeHandler, err, ...args]
if (defaults) { if (defaults) {
while (arr.length > 2 && arr[arr.length - 1] === undefined) arr.pop() while (arr.length > 2 && arr[arr.length - 1] === undefined) arr.pop()
if (arr.length === 2) arr = arr.concat(defaults) if (arr.length === 2) arr = arr.concat(defaults)
} }
return process.nextTick(nativeSignal, ...arr) return process.nextTick(nativeSignal, ...arr)
} }
}
function autoTimeout (cb, input) { function autoTimeout (cb) {
let called = false let called = false
const timeout = setTimeout(timeoutWrap, to, TIMEOUT_ERRNO) const timeout = setTimeout(timeoutWrap, to, TIMEOUT_ERRNO)
return timeoutWrap return timeoutWrap
function timeoutWrap (err, ...args) { function timeoutWrap (...args) {
if (called) return if (called) return
called = true called = true
clearTimeout(timeout) clearTimeout(timeout)
cb(...args)
if (err === TIMEOUT_ERRNO) {
switch (name) {
case 'write':
case 'read':
return cb(TIMEOUT_ERRNO, 0, input[2].buffer)
case 'setxattr':
return cb(TIMEOUT_ERRNO, input[2].buffer)
case 'getxattr':
return cb(TIMEOUT_ERRNO, input[2].buffer)
case 'listxattr':
return cb(TIMEOUT_ERRNO, input[1].buffer)
}
}
cb(err, ...args)
}
} }
} }
} }
@ -283,7 +250,7 @@ class Fuse extends Nanoresource {
static unmount (mnt, cb) { static unmount (mnt, cb) {
mnt = JSON.stringify(mnt) mnt = JSON.stringify(mnt)
const cmd = IS_OSX ? `diskutil unmount force ${mnt}` : `fusermount -uz ${mnt}` const cmd = IS_OSX ? `diskutil umount ${mnt}` : `fusermount -uz ${mnt}`
exec(cmd, err => { exec(cmd, err => {
if (err) return cb(err) if (err) return cb(err)
return cb(null) return cb(null)
@ -314,43 +281,26 @@ class Fuse extends Nanoresource {
const implemented = self._getImplementedArray() const implemented = self._getImplementedArray()
return fs.stat(self.mnt, (err, stat) => { return fs.stat(self.mnt, (err, stat) => {
if (err && err.errno !== -2) return cb(err) if (err) return cb(new Error('Mountpoint does not exist'))
if (err) {
if (!self._mkdir) return cb(new Error('Mountpoint does not exist'))
return fs.mkdir(self.mnt, { recursive: true }, err => {
if (err) return cb(err)
fs.stat(self.mnt, (err, stat) => {
if (err) return cb(err)
return onexists(stat)
})
})
}
if (!stat.isDirectory()) return cb(new Error('Mountpoint is not a directory')) if (!stat.isDirectory()) return cb(new Error('Mountpoint is not a directory'))
return onexists(stat) return fs.stat(path.join(self.mnt, '..'), (_, parent) => {
})
function onexists (stat) {
fs.stat(path.join(self.mnt, '..'), (_, parent) => {
if (parent && parent.dev !== stat.dev) return cb(new Error('Mountpoint in use')) if (parent && parent.dev !== stat.dev) return cb(new Error('Mountpoint in use'))
try { try {
// TODO: asyncify // TODO: asyncify
binding.fuse_native_mount(self.mnt, opts, self._thread, self, self._malloc, self._handlers, implemented) binding.fuse_native_mount(self.mnt, opts, self._thread, self, self._handlers, implemented)
} catch (err) { } catch (err) {
return cb(err) return cb(err)
} }
}) })
} })
} }
} }
_close (cb) { _close (cb) {
const self = this const self = this
Fuse.unmount(this.mnt, err => { Fuse.unmount(this.mnt, () => {
if (err) { // Even if the unmount command fails, do the native unmount.
err.unmountFailure = true
return cb(err)
}
nativeUnmount() nativeUnmount()
}) })
@ -407,7 +357,6 @@ class Fuse extends Nanoresource {
} }
return return
} }
this.ops.getattr(path, (err, stat) => { this.ops.getattr(path, (err, stat) => {
if (err) return signal(err, getStatArray()) if (err) return signal(err, getStatArray())
return signal(0, getStatArray(stat)) return signal(0, getStatArray(stat))
@ -475,13 +424,13 @@ class Fuse extends Nanoresource {
_op_read (signal, path, fd, buf, len, offsetLow, offsetHigh) { _op_read (signal, path, fd, buf, len, offsetLow, offsetHigh) {
this.ops.read(path, fd, buf, len, getDoubleArg(offsetLow, offsetHigh), (err, bytesRead) => { this.ops.read(path, fd, buf, len, getDoubleArg(offsetLow, offsetHigh), (err, bytesRead) => {
return signal(err, bytesRead || 0, buf.buffer) return signal(err, bytesRead)
}) })
} }
_op_write (signal, path, fd, buf, len, offsetLow, offsetHigh) { _op_write (signal, path, fd, buf, len, offsetLow, offsetHigh) {
this.ops.write(path, fd, buf, len, getDoubleArg(offsetLow, offsetHigh), (err, bytesWritten) => { this.ops.write(path, fd, buf, len, getDoubleArg(offsetLow, offsetHigh), (err, bytesWritten) => {
return signal(err, bytesWritten || 0, buf.buffer) return signal(err, bytesWritten)
}) })
} }
@ -495,18 +444,18 @@ class Fuse extends Nanoresource {
_op_setxattr (signal, path, name, value, position, flags) { _op_setxattr (signal, path, name, value, position, flags) {
this.ops.setxattr(path, name, value, position, flags, err => { this.ops.setxattr(path, name, value, position, flags, err => {
return signal(err, value.buffer) return signal(err)
}) })
} }
_op_getxattr (signal, path, name, valueBuf, position) { _op_getxattr (signal, path, name, valueBuf, position) {
this.ops.getxattr(path, name, position, (err, value) => { this.ops.getxattr(path, name, position, (err, value) => {
if (!err) { if (!err) {
if (!value) return signal(IS_OSX ? -93 : -61, valueBuf.buffer) if (!value) return signal(IS_OSX ? -93 : -61)
value.copy(valueBuf) value.copy(valueBuf)
return signal(value.length, valueBuf.buffer) return signal(value.length)
} }
return signal(err, valueBuf.buffer) return signal(err)
}) })
} }
@ -517,7 +466,7 @@ class Fuse extends Nanoresource {
let size = 0 let size = 0
for (const name of list) size += Buffer.byteLength(name) + 1 for (const name of list) size += Buffer.byteLength(name) + 1
size += 128 // fuse yells if we do not signal room for some mac stuff also size += 128 // fuse yells if we do not signal room for some mac stuff also
return signal(size, listBuf.buffer) return signal(size)
} }
let ptr = 0 let ptr = 0
@ -527,9 +476,9 @@ class Fuse extends Nanoresource {
listBuf[ptr++] = 0 listBuf[ptr++] = 0
} }
return signal(ptr, listBuf.buffer) return signal(ptr)
} }
return signal(err, listBuf.buffer) return signal(err)
}) })
} }
@ -539,8 +488,8 @@ class Fuse extends Nanoresource {
}) })
} }
_op_flush (signal, path, fd) { _op_flush (signal, path, datasync, fd) {
this.ops.flush(path, fd, err => { this.ops.flush(path, datasync, fd, err => {
return signal(err) return signal(err)
}) })
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "fuse-native", "name": "fuse-native",
"version": "2.2.6", "version": "2.1.0",
"description": "Fully maintained fuse bindings for Node that aims to cover the entire FUSE api", "description": "Fully maintained fuse bindings for Node that aims to cover the entire FUSE api",
"main": "index.js", "main": "index.js",
"bin": { "bin": {

View File

@ -4,12 +4,10 @@ const path = require('path')
const concat = require('concat-stream') const concat = require('concat-stream')
const Fuse = require('../') const Fuse = require('../')
const createMountpoint = require('./fixtures/mnt') const mnt = require('./fixtures/mnt')
const stat = require('./fixtures/stat') const stat = require('./fixtures/stat')
const { unmount } = require('./helpers') const { unmount } = require('./helpers')
const mnt = createMountpoint()
tape('read and write big file', function (t) { tape('read and write big file', function (t) {
let size = 0 let size = 0
const reads = [0, 4 * 1024 * 1024 * 1024, 6 * 1024 * 1024 * 1024] const reads = [0, 4 * 1024 * 1024 * 1024, 6 * 1024 * 1024 * 1024]

14
test/fixtures/mnt.js vendored
View File

@ -2,18 +2,12 @@ var os = require('os')
var path = require('path') var path = require('path')
var fs = require('fs') var fs = require('fs')
function create (opts = {}) { var mnt = path.join(os.tmpdir(), 'fuse-bindings-' + process.pid + '-' + Date.now())
var mnt = path.join(os.tmpdir(), 'fuse-bindings-' + process.pid + '-' + Date.now())
if (!opts.doNotCreate) { try {
try {
fs.mkdirSync(mnt) fs.mkdirSync(mnt)
} catch (err) { } catch (err) {
// do nothing // do nothing
}
}
return mnt
} }
module.exports = create module.exports = mnt

View File

@ -1,34 +0,0 @@
const stat = require('./stat')
const Fuse = require('../../')
module.exports = function (tests = {}) {
return {
readdir: function (path, cb) {
if (tests.readdir) tests.readdir(path)
if (path === '/') return process.nextTick(cb, null, ['test'])
return process.nextTick(cb, Fuse.ENOENT)
},
getattr: function (path, cb) {
if (tests.getattr) tests.getattr(path)
if (path === '/') return process.nextTick(cb, null, stat({ mode: 'dir', size: 4096 }))
if (path === '/test') return process.nextTick(cb, null, stat({ mode: 'file', size: 11 }))
return process.nextTick(cb, Fuse.ENOENT)
},
open: function (path, flags, cb) {
if (tests.open) tests.open(path, flags)
return process.nextTick(cb, 0, 42)
},
release: function (path, fd, cb) {
if (tests.release) tests.release(path, fd)
return process.nextTick(cb, 0)
},
read: function (path, fd, buf, len, pos, cb) {
if (tests.read) tests.read(path, fd, buf, len, pos)
var str = 'hello world'.slice(pos, pos + len)
if (!str) return process.nextTick(cb, 0)
buf.write(str)
return process.nextTick(cb, str.length)
}
}
}

View File

@ -4,11 +4,9 @@ const path = require('path')
const { unmount } = require('./helpers') const { unmount } = require('./helpers')
const Fuse = require('../') const Fuse = require('../')
const createMountpoint = require('./fixtures/mnt') const mnt = require('./fixtures/mnt')
const stat = require('./fixtures/stat') const stat = require('./fixtures/stat')
const mnt = createMountpoint()
tape('readlink', function (t) { tape('readlink', function (t) {
var ops = { var ops = {
force: true, force: true,

View File

@ -1,15 +1,9 @@
const os = require('os') const mnt = require('./fixtures/mnt')
const fs = require('fs')
const tape = require('tape') const tape = require('tape')
const { spawnSync, exec } = require('child_process') const { spawnSync } = require('child_process')
const createMountpoint = require('./fixtures/mnt')
const Fuse = require('../') const Fuse = require('../')
const { unmount } = require('./helpers') const { unmount } = require('./helpers')
const simpleFS = require('./fixtures/simple-fs')
const mnt = createMountpoint()
tape('mount', function (t) { tape('mount', function (t) {
const fuse = new Fuse(mnt, {}, { force: true }) const fuse = new Fuse(mnt, {}, { force: true })
@ -117,78 +111,6 @@ tape('mounting over a broken mountpoint with force succeeds', function (t) {
}) })
}) })
tape('mounting without mkdir option and a nonexistent mountpoint fails', function (t) {
const nonexistentMnt = createMountpoint({ doNotCreate: true })
const fuse = new Fuse(nonexistentMnt, {}, { debug: false })
fuse.mount(function (err) {
t.true(err, 'could not mount')
t.end()
})
})
tape('mounting with mkdir option and a nonexistent mountpoint succeeds', function (t) {
const nonexistentMnt = createMountpoint({ doNotCreate: true })
const fuse = new Fuse(nonexistentMnt, {}, { debug: false, mkdir: true })
fuse.mount(function (err) {
t.error(err, 'no error')
unmount(fuse, function (err) {
t.end()
})
})
})
tape('(osx only) unmount with Finder open succeeds', function (t) {
if (os.platform() !== 'darwin') return t.end()
const fuse = new Fuse(mnt, simpleFS(), { force: true, debug: false })
fuse.mount(function (err) {
t.error(err, 'no error')
exec(`open ${mnt}`, err => {
t.error(err, 'no error')
setTimeout(() => {
fs.readdir(mnt, (err, list) => {
t.error(err, 'no error')
t.same(list, ['test'])
unmount(fuse, err => {
t.error(err, 'no error')
fs.readdir(mnt, (err, list) => {
t.error(err, 'no error')
t.same(list, [])
t.end()
})
})
})
}, 1000)
})
})
})
tape('(osx only) unmount with Terminal open succeeds', function (t) {
if (os.platform() !== 'darwin') return t.end()
const fuse = new Fuse(mnt, simpleFS(), { force: true, debug: false })
fuse.mount(function (err) {
t.error(err, 'no error')
exec(`open -a Terminal ${mnt}`, err => {
t.error(err, 'no error')
setTimeout(() => {
fs.readdir(mnt, (err, list) => {
t.error(err, 'no error')
t.same(list, ['test'])
unmount(fuse, err => {
t.error(err, 'no error')
fs.readdir(mnt, (err, list) => {
t.error(err, 'no error')
t.same(list, [])
t.end()
})
})
})
}, 1000)
})
})
})
tape('static unmounting', function (t) { tape('static unmounting', function (t) {
t.end() t.end()
}) })

View File

@ -4,20 +4,38 @@ const path = require('path')
const concat = require('concat-stream') const concat = require('concat-stream')
const Fuse = require('../') const Fuse = require('../')
const createMountpoint = require('./fixtures/mnt') const mnt = require('./fixtures/mnt')
const stat = require('./fixtures/stat') const stat = require('./fixtures/stat')
const simpleFS = require('./fixtures/simple-fs')
const { unmount } = require('./helpers') const { unmount } = require('./helpers')
const mnt = createMountpoint()
tape('read', function (t) { tape('read', function (t) {
const testFS = simpleFS({ var ops = {
release: function (path, fd) { force: true,
readdir: function (path, cb) {
if (path === '/') return process.nextTick(cb, null, ['test'])
return process.nextTick(cb, Fuse.ENOENT)
},
getattr: function (path, cb) {
if (path === '/') return process.nextTick(cb, null, stat({ mode: 'dir', size: 4096 }))
if (path === '/test') return process.nextTick(cb, null, stat({ mode: 'file', size: 11 }))
return process.nextTick(cb, Fuse.ENOENT)
},
open: function (path, flags, cb) {
return process.nextTick(cb, 0, 42)
},
release: function (path, fd, cb) {
t.same(fd, 42, 'fd was passed to release') t.same(fd, 42, 'fd was passed to release')
return process.nextTick(cb, 0)
},
read: function (path, fd, buf, len, pos, cb) {
var str = 'hello world'.slice(pos, pos + len)
if (!str) return process.nextTick(cb, 0)
buf.write(str)
return process.nextTick(cb, str.length)
} }
}) }
const fuse = new Fuse(mnt, testFS, { debug: true })
const fuse = new Fuse(mnt, ops, { debug: true })
fuse.mount(function (err) { fuse.mount(function (err) {
t.error(err, 'no error') t.error(err, 'no error')

View File

@ -3,11 +3,9 @@ const { unmount } = require('./helpers')
const tape = require('tape') const tape = require('tape')
const Fuse = require('../') const Fuse = require('../')
const createMountpoint = require('./fixtures/mnt') const mnt = require('./fixtures/mnt')
const stat = require('./fixtures/stat') const stat = require('./fixtures/stat')
const mnt = createMountpoint()
tape('statfs', function (t) { tape('statfs', function (t) {
const ops = { const ops = {
force: true, force: true,

View File

@ -3,12 +3,10 @@ const fs = require('fs')
const path = require('path') const path = require('path')
const Fuse = require('../') const Fuse = require('../')
const createMountpoint = require('./fixtures/mnt') const mnt = require('./fixtures/mnt')
const stat = require('./fixtures/stat') const stat = require('./fixtures/stat')
const { unmount } = require('./helpers') const { unmount } = require('./helpers')
const mnt = createMountpoint()
tape('write', function (t) { tape('write', function (t) {
var created = false var created = false
var data = Buffer.alloc(1024) var data = Buffer.alloc(1024)