diff --git a/.travis.yml b/.travis.yml index ab5857c..6ddf85a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,5 +33,18 @@ before_deploy: - ARCHIVE_NAME="${TRAVIS_TAG:-latest}-$TRAVIS_OS_NAME.tar" - npm run prebuild - 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 .. + +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' diff --git a/binding.gyp b/binding.gyp index 2ff2ef7..b4377b7 100644 --- a/binding.gyp +++ b/binding.gyp @@ -11,7 +11,18 @@ "sources": [ "fuse-native.c" ], - "cflags": ["-rdynamic"] + 'xcode_settings': { + 'OTHER_CFLAGS': [ + '-g', + '-O3', + '-Wall' + ] + }, + 'cflags': [ + '-g', + '-O3', + '-Wall' + ], }, { "target_name": "postinstall", "type": "none", diff --git a/fuse-native.c b/fuse-native.c index 044620f..4b842da 100644 --- a/fuse-native.c +++ b/fuse-native.c @@ -18,6 +18,10 @@ #include #include +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)\ napi_env env = ft->env;\ napi_handle_scope scope;\ @@ -43,7 +47,7 @@ uint32_t op = op_##name;\ FUSE_NATIVE_CALLBACK(ft->handlers[op], {\ napi_value argv[callbackArgs + 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]));\ callbackBlk\ NAPI_MAKE_CALLBACK(env, NULL, ctx, callback, callbackArgs + 2, argv, NULL)\ @@ -116,6 +120,7 @@ typedef struct { pthread_t thread; pthread_attr_t attr; napi_ref ctx; + napi_ref malloc; // Operation handlers napi_ref handlers[35]; @@ -132,6 +137,8 @@ typedef struct { } fuse_thread_t; typedef struct { + napi_ref self; + // Opcode uint32_t op; void *op_fn; @@ -175,15 +182,11 @@ typedef struct { } fuse_thread_locals_t; static pthread_key_t thread_locals_key; -static fuse_thread_locals_t* get_thread_locals (); +static fuse_thread_locals_t* get_thread_locals(); // Helpers // 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) { uint64_t low = *((*ints)++); uint64_t high = *((*ints)++); @@ -367,7 +370,7 @@ FUSE_METHOD_VOID(releasedir, 2, 0, (const char *path, struct fuse_file_info *inf } }) -FUSE_METHOD(read, 6, 1, (const char *path, char *buf, size_t len, off_t offset, struct fuse_file_info *info), { +FUSE_METHOD(read, 6, 2, (const char *path, char *buf, size_t len, off_t offset, struct fuse_file_info *info), { l->path = path; l->buf = buf; l->len = len; @@ -376,14 +379,14 @@ FUSE_METHOD(read, 6, 1, (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_uint32(env, l->info->fh, &(argv[3])); - napi_create_external_buffer(env, l->len, (char *) l->buf, &fin, NULL, &(argv[4])); + napi_create_external_buffer(env, l->len, (char *) l->buf, NULL, NULL, &(argv[4])); napi_create_uint32(env, l->len, &(argv[5])); FUSE_UINT64_TO_INTS_ARGV(l->offset, 6) }, { - // TODO: handle bytes processed? + if (IS_ARRAY_BUFFER_DETACH_SUPPORTED == 1) assert(napi_detach_arraybuffer(env, argv[3]) == napi_ok); }) -FUSE_METHOD(write, 6, 1, (const char *path, const char *buf, size_t len, off_t offset, struct fuse_file_info *info), { +FUSE_METHOD(write, 6, 2, (const char *path, const char *buf, size_t len, off_t offset, struct fuse_file_info *info), { l->path = path; l->buf = buf; l->len = len; @@ -392,11 +395,11 @@ FUSE_METHOD(write, 6, 1, (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_uint32(env, l->info->fh, &(argv[3])); - napi_create_external_buffer(env, l->len, (char *) l->buf, &fin, NULL, &(argv[4])); + napi_create_external_buffer(env, l->len, (char *) l->buf, NULL, NULL, &(argv[4])); napi_create_uint32(env, l->len, &(argv[5])); FUSE_UINT64_TO_INTS_ARGV(l->offset, 6) }, { - // TODO: handle bytes processed? + if (IS_ARRAY_BUFFER_DETACH_SUPPORTED == 1) assert(napi_detach_arraybuffer(env, argv[3]) == napi_ok); }) FUSE_METHOD(readdir, 1, 2, (const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *info), { @@ -446,7 +449,7 @@ FUSE_METHOD(readdir, 1, 2, (const char *path, void *buf, fuse_fill_dir_t filler, #ifdef __APPLE__ -FUSE_METHOD_VOID(setxattr, 5, 0, (const char *path, const char *name, const char *value, size_t size, int flags, uint32_t position), { +FUSE_METHOD(setxattr, 5, 1, (const char *path, const char *name, const char *value, size_t size, int flags, uint32_t position), { l->path = path; l->name = name; l->value = value; @@ -456,12 +459,14 @@ FUSE_METHOD_VOID(setxattr, 5, 0, (const char *path, const char *name, const char }, { 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_external_buffer(env, l->size, (char *) l->value, &fin, NULL, &(argv[4])); + napi_create_external_buffer(env, l->size, (char *) l->value, NULL, NULL, &(argv[4])); napi_create_uint32(env, l->position, &(argv[5])); 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_VOID(getxattr, 4, 0, (const char *path, const char *name, char *value, size_t size, uint32_t position), { +FUSE_METHOD(getxattr, 4, 1, (const char *path, const char *name, char *value, size_t size, uint32_t position), { l->path = path; l->name = name; l->value = value; @@ -470,13 +475,15 @@ FUSE_METHOD_VOID(getxattr, 4, 0, (const char *path, const char *name, char *valu }, { 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_external_buffer(env, l->size, (char *) l->value, &fin, NULL, &(argv[4])); + napi_create_external_buffer(env, l->size, (char *) l->value, NULL, NULL, &(argv[4])); 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 -FUSE_METHOD_VOID(setxattr, 5, 0, (const char *path, const char *name, const char *value, size_t size, int flags), { +FUSE_METHOD(setxattr, 5, 1, (const char *path, const char *name, const char *value, size_t size, int flags), { l->path = path; l->name = name; l->value = value; @@ -485,12 +492,14 @@ FUSE_METHOD_VOID(setxattr, 5, 0, (const char *path, const char *name, const char }, { 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_external_buffer(env, l->size, (char *) l->value, &fin, NULL, &(argv[4])); + napi_create_external_buffer(env, l->size, (char *) l->value, NULL, NULL, &(argv[4])); napi_create_uint32(env, 0, &(argv[5])); // normalize apis between mac and linux 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_VOID(getxattr, 4, 0, (const char *path, const char *name, char *value, size_t size), { +FUSE_METHOD(getxattr, 4, 1, (const char *path, const char *name, char *value, size_t size), { l->path = path; l->name = name; l->value = value; @@ -498,19 +507,23 @@ FUSE_METHOD_VOID(getxattr, 4, 0, (const char *path, const char *name, char *valu }, { 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_external_buffer(env, l->size, (char *) l->value, &fin, NULL, &(argv[4])); + napi_create_external_buffer(env, l->size, (char *) l->value, NULL, NULL, &(argv[4])); napi_create_uint32(env, 0, &(argv[5])); +}, { + if (IS_ARRAY_BUFFER_DETACH_SUPPORTED == 1) assert(napi_detach_arraybuffer(env, argv[2]) == napi_ok); }) #endif -FUSE_METHOD_VOID(listxattr, 2, 0, (const char *path, char *list, size_t size), { +FUSE_METHOD(listxattr, 2, 1, (const char *path, char *list, size_t size), { l->path = path; l->list = list; l->size = size; }, { napi_create_string_utf8(env, l->path, NAPI_AUTO_LENGTH, &(argv[2])); - napi_create_external_buffer(env, l->size, l->list, &fin, NULL, &(argv[3])); + napi_create_external_buffer(env, l->size, l->list, NULL, 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), { @@ -670,8 +683,10 @@ 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) {\ FUSE_NATIVE_CALLBACK(ft->handlers[op_init], { 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_MAKE_CALLBACK(env, NULL, ctx, callback, 2, argv, NULL); }) } @@ -687,10 +702,13 @@ NAPI_METHOD(fuse_native_signal_init) { static void * fuse_native_init (struct fuse_conn_info *conn) { fuse_thread_locals_t *l = get_thread_locals(); + l->op = op_init; l->op_fn = fuse_native_dispatch_init; + uv_async_send(&(l->async)); uv_sem_wait(&(l->sem)); + return l->fuse; } @@ -700,12 +718,27 @@ static void fuse_native_dispatch (uv_async_t* handle) { fuse_thread_locals_t *l = (fuse_thread_locals_t *) handle->data; fuse_thread_t *ft = l->fuse; void (*fn)(uv_async_t *, fuse_thread_locals_t *, fuse_thread_t *) = l->op_fn; + fn(handle, l, ft); } static void fuse_native_async_init (uv_async_t* handle) { - fuse_thread_locals_t *l = (fuse_thread_locals_t *) handle->data; - fuse_thread_t *ft = l->fuse; + fuse_thread_t *ft = (fuse_thread_t *) handle->data; + fuse_thread_locals_t *l; + + 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); assert(err >= 0); @@ -714,6 +747,8 @@ static void fuse_native_async_init (uv_async_t* handle) { uv_sem_init(&(l->sem), 0); l->async.data = l; + ft->async.data = l; + l->fuse = ft; uv_sem_post(&(ft->sem)); } @@ -725,21 +760,18 @@ static fuse_thread_locals_t* get_thread_locals () { void *data = pthread_getspecific(thread_locals_key); 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. uv_mutex_lock(&(ft->mut)); - ft->async.data = l; + ft->async.data = ft; // Notify the main thread to uv_async_init l->async. uv_async_send(&(ft->async)); uv_sem_wait(&(ft->sem)); - l->async.data = l; + fuse_thread_locals_t *l = (fuse_thread_locals_t*) ft->async.data; pthread_setspecific(thread_locals_key, (void *) l); uv_mutex_unlock(&(ft->mut)); @@ -759,14 +791,15 @@ static void* start_fuse_thread (void *data) { } NAPI_METHOD(fuse_native_mount) { - NAPI_ARGV(6) + NAPI_ARGV(7) NAPI_ARGV_UTF8(mnt, 1024, 0); NAPI_ARGV_UTF8(mntopts, 1024, 1); NAPI_ARGV_BUFFER_CAST(fuse_thread_t *, ft, 2); napi_create_reference(env, argv[3], 1, &(ft->ctx)); - napi_value handlers = argv[4]; - NAPI_ARGV_BUFFER_CAST(uint32_t *, implemented, 5) + napi_create_reference(env, argv[4], 1, &(ft->malloc)); + napi_value handlers = argv[5]; + NAPI_ARGV_BUFFER_CAST(uint32_t *, implemented, 6) for (int i = 0; i < 35; i++) { ft->handlers[i] = NULL; @@ -869,6 +902,13 @@ NAPI_METHOD(fuse_native_unmount) { } 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 NAPI_EXPORT_SIZEOF(fuse_thread_t) diff --git a/index.js b/index.js index 1b7b961..3a11fd7 100644 --- a/index.js +++ b/index.js @@ -144,6 +144,7 @@ class Fuse extends Nanoresource { this._mkdir = !!opts.mkdir this._thread = null this._handlers = this._makeHandlerArray() + this._threads = new Set() const implemented = [binding.op_init, binding.op_error, binding.op_getattr] if (ops) { @@ -200,6 +201,12 @@ class Fuse extends Nanoresource { return options.length ? '-o' + options.join(',') : '' } + _malloc (size) { + const buf = Buffer.alloc(size) + this._threads.add(buf) + return buf + } + _makeHandlerArray () { const self = this const handlers = new Array(OpcodesAndDefaults.size) @@ -231,10 +238,12 @@ class Fuse extends Nanoresource { function signal (nativeHandler, err, ...args) { var arr = [nativeHandler, err, ...args] + if (defaults) { while (arr.length > 2 && arr[arr.length - 1] === undefined) arr.pop() if (arr.length === 2) arr = arr.concat(defaults) } + return process.nextTick(nativeSignal, ...arr) } @@ -308,7 +317,7 @@ class Fuse extends Nanoresource { if (parent && parent.dev !== stat.dev) return cb(new Error('Mountpoint in use')) try { // TODO: asyncify - binding.fuse_native_mount(self.mnt, opts, self._thread, self, self._handlers, implemented) + binding.fuse_native_mount(self.mnt, opts, self._thread, self, self._malloc, self._handlers, implemented) } catch (err) { return cb(err) } @@ -381,6 +390,7 @@ class Fuse extends Nanoresource { } return } + this.ops.getattr(path, (err, stat) => { if (err) return signal(err, getStatArray()) return signal(0, getStatArray(stat)) @@ -448,13 +458,13 @@ class Fuse extends Nanoresource { _op_read (signal, path, fd, buf, len, offsetLow, offsetHigh) { this.ops.read(path, fd, buf, len, getDoubleArg(offsetLow, offsetHigh), (err, bytesRead) => { - return signal(err, bytesRead) + return signal(err, bytesRead, buf.buffer) }) } _op_write (signal, path, fd, buf, len, offsetLow, offsetHigh) { this.ops.write(path, fd, buf, len, getDoubleArg(offsetLow, offsetHigh), (err, bytesWritten) => { - return signal(err, bytesWritten) + return signal(err, bytesWritten, buf.buffer) }) } @@ -468,18 +478,18 @@ class Fuse extends Nanoresource { _op_setxattr (signal, path, name, value, position, flags) { this.ops.setxattr(path, name, value, position, flags, err => { - return signal(err) + return signal(err, value.buffer) }) } _op_getxattr (signal, path, name, valueBuf, position) { this.ops.getxattr(path, name, position, (err, value) => { if (!err) { - if (!value) return signal(IS_OSX ? -93 : -61) + if (!value) return signal(IS_OSX ? -93 : -61, valueBuf.buffer) value.copy(valueBuf) - return signal(value.length) + return signal(value.length, valueBuf.buffer) } - return signal(err) + return signal(err, valueBuf.buffer) }) } @@ -490,7 +500,7 @@ class Fuse extends Nanoresource { let size = 0 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 - return signal(size) + return signal(size, listBuf.buffer) } let ptr = 0 @@ -500,9 +510,9 @@ class Fuse extends Nanoresource { listBuf[ptr++] = 0 } - return signal(ptr) + return signal(ptr, listBuf.buffer) } - return signal(err) + return signal(err, listBuf.buffer) }) }