init flow works, getattr fails

reimpl
Andrew Osheroff 5 years ago
parent bd9d36fb2f
commit e4732baf83

@ -69,16 +69,3 @@ process.on('SIGINT', function () {
} }
}) })
}) })
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
}
}

@ -39,29 +39,36 @@
fuse_native_semaphore_wait(&(l->sem)); \ fuse_native_semaphore_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)\
static void fuse_native_dispatch_##name (uv_async_t* handle, int status, fuse_thread_locals_t* l, fuse_thread_t* ft) { \ static void fuse_native_dispatch_##name (uv_async_t* handle, int status, fuse_thread_locals_t* l, fuse_thread_t* ft) {\
uint32_t op = op_##name; \ printf("at beginning of fuse_native_dispatch_%s\n", #name);\
FUSE_NATIVE_CALLBACK(ft->handlers[op], { \ uint32_t op = op_##name;\
napi_value argv[callbackArgs + 2]; \ printf("op here: %i\n", op);\
napi_create_external_buffer(env, sizeof(fuse_thread_locals_t), l, &fin, NULL, &(argv[0])); \ printf("handler here: %zu\n", ft->handlers[op]);\
napi_create_uint32(env, l->op, &(argv[1])); \ printf("in fuse_native_dispatch, op: %i, handler: %zu\n", op, ft->handlers[op]);\
callbackBlk \ FUSE_NATIVE_CALLBACK(ft->handlers[op], {\
NAPI_MAKE_CALLBACK(env, NULL, ctx, callback, callbackArgs + 2, argv, NULL) \ napi_value argv[callbackArgs + 2];\
}) \ napi_create_external_buffer(env, sizeof(fuse_thread_locals_t), l, &fin, NULL, &(argv[0]));\
} \ napi_create_uint32(env, l->op, &(argv[1]));\
NAPI_METHOD(fuse_native_signal_##name) { \ callbackBlk\
NAPI_ARGV(signalArgs + 2) \ printf("in fuse_native_callback, calling callback for %s\n", #name);\
NAPI_ARGV_BUFFER_CAST(fuse_thread_locals_t *, l, 0); \ NAPI_MAKE_CALLBACK(env, NULL, ctx, callback, callbackArgs + 2, argv, NULL)\
NAPI_ARGV_INT32(res, 1); \ })\
signalBlk \ }\
l->res = res; \ NAPI_METHOD(fuse_native_signal_##name) {\
fuse_native_semaphore_signal(&(l->sem)); \ NAPI_ARGV(signalArgs + 2)\
return NULL; \ NAPI_ARGV_BUFFER_CAST(fuse_thread_locals_t *, l, 0);\
} \ NAPI_ARGV_INT32(res, 1);\
static int fuse_native_##name signature { \ signalBlk\
FUSE_NATIVE_HANDLER(name, callBlk) \ l->res = res;\
} \ printf("in _fuse_native_signal_%s, signalling semaphore\n", #name);\
fuse_native_semaphore_signal(&(l->sem));\
return NULL;\
}\
static int fuse_native_##name signature {\
printf("in fuse_native_%s\n", #name);\
FUSE_NATIVE_HANDLER(name, callBlk)\
}
// Opcodes // Opcodes
@ -110,7 +117,7 @@ typedef struct {
napi_ref ctx; napi_ref ctx;
// Operation handlers // Operation handlers
napi_ref handlers[34]; napi_ref handlers[35];
struct fuse *fuse; struct fuse *fuse;
struct fuse_chan *ch; struct fuse_chan *ch;
@ -537,7 +544,13 @@ FUSE_METHOD(removexattr, 2, 0, (const char *path, const char *name), {
}, },
{}) {})
FUSE_METHOD(init, 0, 0, (struct fuse_conn_info *conn, struct fuse_config *cfg), {}, {}, {}) FUSE_METHOD(init, 0, 0, (struct fuse_conn_info *conn, struct fuse_config *cfg), {
printf("in fuse_native_init\n");
}, {
printf("in fuse_native_init_callback\n");
}, {
printf("in fuse_native_init_signal\n");
})
FUSE_METHOD(error, 0, 0, (), {}, {}, {}) FUSE_METHOD(error, 0, 0, (), {}, {}, {})
@ -713,6 +726,8 @@ static void fuse_native_dispatch (uv_async_t* handle, int status) {
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;
printf("dispatching %i\n", l->op);
// TODO: Either use a function pointer (like ft->handlers[op]) or generate with a macro. // TODO: Either use a function pointer (like ft->handlers[op]) or generate with a macro.
switch (l->op) { switch (l->op) {
case (op_init): return fuse_native_dispatch_init(handle, status, l, ft); case (op_init): return fuse_native_dispatch_init(handle, status, l, ft);
@ -799,10 +814,9 @@ NAPI_METHOD(fuse_native_mount) {
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_ref handlers; napi_value handlers = argv[4];
napi_create_reference(env, argv[4], 1, &handlers);
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]);
} }
@ -845,7 +859,7 @@ NAPI_METHOD(fuse_native_mount) {
.destroy = fuse_native_destroy .destroy = fuse_native_destroy
}; };
int _argc = 2; int _argc = (strcmp(mntopts, "-o") <= 0) ? 1 : 2;
char *_argv[] = { char *_argv[] = {
(char *) "fuse_bindings_dummy", (char *) "fuse_bindings_dummy",
(char *) mntopts (char *) mntopts

@ -9,115 +9,118 @@ const HAS_FOLDER_ICON = IS_OSX && fs.existsSync(OSX_FOLDER_ICON)
const binding = require('node-gyp-build')(__dirname) const binding = require('node-gyp-build')(__dirname)
const OpcodesAndDefaults = new Map([ const OpcodesAndDefaults = new Map([
[ 'init', { ['init', {
op: 0 op: 0
} ], }],
[ 'error', { ['error', {
op: 1 op: 1
} ], }],
[ 'access', { ['access', {
op: 2, op: 2,
defaults: [ 0 ] defaults: [0]
} ], }],
[ 'statfs', { ['statfs', {
op: 3, op: 3,
defaults: [ getStatfsArray() ] defaults: [getStatfsArray()]
} ], }],
[ 'fgetattr', { ['fgetattr', {
op: 4, op: 4,
defaults: [ getStatArray() ] defaults: [getStatArray()]
} ], }],
[ 'getattr', { ['getattr', {
op: 5, op: 5,
defaults: [ getStatArray() ] defaults: [getStatArray()]
} ], }],
[ 'flush', { ['flush', {
op: 6, op: 6
} ], }],
[ 'fsync', { ['fsync', {
op: 7 op: 7
} ], }],
[ 'fsyncdir', { ['fsyncdir', {
op: 8 op: 8
} ], }],
[ 'readdir', { ['readdir', {
op: 9 op: 9
} ], }],
[ 'truncate', { ['truncate', {
op: 10 op: 10
} ], }],
[ 'ftruncate', { ['ftruncate', {
op: 11 op: 11
} ], }],
[ 'utimens', { ['utimens', {
op: 12 op: 12
} ], }],
[ 'readlink', { ['readlink', {
op: 13 op: 13,
} ], defaults: ['']
[ 'chown', { }],
op: 14 ['chown', {
} ], op: 14
[ 'chmod', { }],
op: 15 ['chmod', {
} ], op: 15
[ 'mknod', { }],
op: 16 ['mknod', {
} ], op: 16
[ 'setxattr', { }],
op: 17 ['setxattr', {
} ], op: 17
[ 'getxattr', { }],
op: 18 ['getxattr', {
} ], op: 18
[ 'listxattr', { }],
op: 19 ['listxattr', {
} ], op: 19
[ 'removexattr', { }],
op: 20 ['removexattr', {
} ], op: 20
[ 'open', { }],
op: 21 ['open', {
} ], op: 21
[ 'opendir', { }],
op: 22 ['opendir', {
} ], op: 22
[ 'read', { }],
op: 23 ['read', {
} ], op: 23,
[ 'write', { defaults: [0]
op: 24 }],
} ], ['write', {
[ 'release', { op: 24,
op: 25 defaults: [0]
} ], }],
[ 'releasedir', { ['release', {
op: 26 op: 25
} ], }],
[ 'create', { ['releasedir', {
op: 27 op: 26
} ], }],
[ 'unlink', { ['create', {
op: 28 op: 27
} ], }],
[ 'rename', { ['unlink', {
op: 29 op: 28
} ], }],
[ 'link', { ['rename', {
op: 30 op: 29
} ], }],
[ 'symlink', { ['link', {
op: 31 op: 30
} ], }],
[ 'mkdir', { ['symlink', {
op: 32 op: 31
} ], }],
[ 'rmdir', { ['mkdir', {
op: 33 op: 32
} ], }],
[ 'destroy', { ['rmdir', {
op: 34 op: 33
} ] }],
['destroy', {
op: 34
}]
]) ])
class Fuse { class Fuse {
@ -133,8 +136,8 @@ class Fuse {
const implemented = [0, 1, 5] const implemented = [0, 1, 5]
if (ops) { if (ops) {
for (const [name, code] of Opcodes) { for (const [name, { op }] of OpcodesAndDefaults) {
if (ops[name]) implemented.push(code) if (ops[name]) implemented.push(op)
} }
} }
this._implemented = new Set(implemented) this._implemented = new Set(implemented)
@ -189,7 +192,9 @@ class Fuse {
// Handlers // Handlers
_makeHandlerArray () { _makeHandlerArray () {
const self = this
const handlers = new Array(OpcodesAndDefaults.size) const handlers = new Array(OpcodesAndDefaults.size)
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}`]
if (!nativeSignal) continue if (!nativeSignal) continue
@ -200,22 +205,25 @@ class Fuse {
return handlers return handlers
function makeHandler (name, op, defaults, nativeSignal) { function makeHandler (name, op, defaults, nativeSignal) {
return () => { return function () {
const boundSignal = signal.bind(arguments[0]) const boundSignal = signal.bind(null, arguments[0])
const funcName = `_$name` const funcName = `_${name}`
if (!this[funcName] || !this._implemented.has(op)) return boundSignal(-1, defaults) if (!self[funcName] || !self._implemented.has(op)) return boundSignal(-1, defaults)
return this[funcName].apply(null, [boundSignal, [...arguments].slice(1) ]) return self[funcName].apply(self, [boundSignal, ...[...arguments].slice(1)])
} }
function signal (nativeHandler, err, args) { function signal (nativeHandler, err, ...args) {
const args = [nativeHandler, err] console.log('nativeHanlder:', nativeHandler, 'err:', err, 'args:', args)
if (defaults) args.concat(defaults) const arr = [nativeHandler, err, ...args]
return nativeSignal(args) if (defaults && (!args || !args.length)) arr.concat(defaults)
console.log('signalling with arr:', arr)
return nativeSignal(...arr)
} }
} }
} }
_init (signal) { _init (signal) {
console.log('IN JS INIT')
if (!this.ops.init) { if (!this.ops.init) {
signal(0) signal(0)
return return
@ -461,7 +469,8 @@ class Fuse {
mount (cb) { mount (cb) {
const opts = this._fuseOptions() const opts = this._fuseOptions()
console.log('mounting at %s with opts: %s', this.mnt, opts)
console.log('handlers:', this._handlers)
fs.stat(this.mnt, (err, stat) => { fs.stat(this.mnt, (err, stat) => {
if (err) return cb(new Error('Mountpoint does not exist')) if (err) return cb(new Error('Mountpoint does not exist'))
if (!stat.isDirectory()) return cb(new Error('Mountpoint is not a directory')) if (!stat.isDirectory()) return cb(new Error('Mountpoint is not a directory'))
@ -646,7 +655,7 @@ function setDoubleInt (arr, idx, num) {
arr[idx + 1] = (num - arr[idx]) / 4294967296 arr[idx + 1] = (num - arr[idx]) / 4294967296
} }
function getDoubleInt(arr, idx) { function getDoubleInt (arr, idx) {
arr = new Uint32Array(arr) arr = new Uint32Array(arr)
var num = arr[idx + 1] * 4294967296 var num = arr[idx + 1] * 4294967296
num += arr[idx] num += arr[idx]
@ -658,20 +667,17 @@ function getStatArray (stat) {
ints[0] = (stat && stat.mode) || 0 ints[0] = (stat && stat.mode) || 0
ints[1] = (stat && stat.uid) || 0 ints[1] = (stat && stat.uid) || 0
ints[2] = (stat && stat.gid) || 0 ints[2] = (stat && stat.gid) || 0
ints[3] = (stat && stat.size) || 0 ints[3] = (stat && stat.size) || 0
ints[4] = (stat && stat.dev) || 0 ints[4] = (stat && stat.dev) || 0
ints[5] = (stat && stat.nlink) || 1 ints[5] = (stat && stat.nlink) || 1
ints[6] = (stat && stat.ino) || 0 ints[6] = (stat && stat.ino) || 0
ints[7] = (stat && stat.rdev) || 0 ints[7] = (stat && stat.rdev) || 0
ints[8] = (stat && stat.blksize) || 0 ints[8] = (stat && stat.blksize) || 0
ints[9] = (stat && stat.blocks) || 0 ints[9] = (stat && stat.blocks) || 0
setDoubleInt(ints, 10, (stat && stat.atim) || Date.now()) setDoubleInt(ints, 10, (stat && stat.atim) || Date.now())
setDoubleInt(ints, 12, (stat && stat.atim) || Date.now()) setDoubleInt(ints, 12, (stat && stat.atim) || Date.now())
setDoubleInt(ints, 14, (stat && stat.atim) || Date.now()) setDoubleInt(ints, 14, (stat && stat.atim) || Date.now())
return ints return ints
} }
function noop () {}
function call (cb) { cb() }

@ -5,6 +5,7 @@
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"install": "node-gyp-build", "install": "node-gyp-build",
"test": "standard && test/*.js",
"prebuild": "prebuildify -a --strip", "prebuild": "prebuildify -a --strip",
"prebuild-ia32": "prebuildify -a --strip --arch=ia32" "prebuild-ia32": "prebuildify -a --strip --arch=ia32"
}, },
@ -14,7 +15,11 @@
"napi-macros": "^2.0.0", "napi-macros": "^2.0.0",
"node-gyp-build": "^3.2.2" "node-gyp-build": "^3.2.2"
}, },
"devDependencies": {}, "devDependencies": {
"concat-stream": "^2.0.0",
"standard": "^13.1.0",
"tape": "^4.11.0"
},
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/fuse-friends/fuse-native.git" "url": "https://github.com/fuse-friends/fuse-native.git"

@ -0,0 +1,13 @@
var os = require('os')
var path = require('path')
var fs = require('fs')
var mnt = path.join(os.tmpdir(), 'fuse-bindings-' + process.pid + '-' + Date.now())
try {
fs.mkdirSync(mnt)
} catch (err) {
// do nothing
}
module.exports = mnt

@ -0,0 +1,11 @@
module.exports = function (st) {
return {
mtime: st.mtime || new Date(),
atime: st.atime || new Date(),
ctime: st.ctime || new Date(),
size: st.size !== undefined ? st.size : 0,
mode: st.mode === 'dir' ? 16877 : (st.mode === 'file' ? 33188 : (st.mode === 'link' ? 41453 : st.mode)),
uid: st.uid !== undefined ? st.uid : process.getuid(),
gid: st.gid !== undefined ? st.gid : process.getgid()
}
}

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

@ -0,0 +1,43 @@
var mnt = require('./fixtures/mnt')
var fuse = require('../')
var tape = require('tape')
tape('mount', function (t) {
fuse.mount(mnt, { force: true }, function (err) {
t.error(err, 'no error')
t.ok(true, 'works')
fuse.unmount(mnt, function () {
t.end()
})
})
})
tape('mount + unmount + mount', function (t) {
fuse.mount(mnt, { force: true }, function (err) {
t.error(err, 'no error')
t.ok(true, 'works')
fuse.unmount(mnt, function () {
fuse.mount(mnt, { force: true }, function (err) {
t.error(err, 'no error')
t.ok(true, 'works')
fuse.unmount(mnt, function () {
t.end()
})
})
})
})
})
tape('mnt point must exist', function (t) {
fuse.mount(mnt + '.does-not-exist', {}, function (err) {
t.ok(err, 'had error')
t.end()
})
})
tape('mnt point must be directory', function (t) {
fuse.mount(__filename, {}, function (err) {
t.ok(err, 'had error')
t.end()
})
})

@ -0,0 +1,64 @@
const tape = require('tape')
const fs = require('fs')
const path = require('path')
const concat = require('concat-stream')
const Fuse = require('../')
const mnt = require('./fixtures/mnt')
const stat = require('./fixtures/stat')
tape('read', function (t) {
var ops = {
force: true,
readdir: function (path, cb) {
if (path === '/') return cb(null, ['test'])
return cb(fuse.ENOENT)
},
getattr: function (path, cb) {
if (path === '/') return cb(null, stat({ mode: 'dir', size: 4096 }))
if (path === '/test') return cb(null, stat({ mode: 'file', size: 11 }))
return cb(fuse.ENOENT)
},
open: function (path, flags, cb) {
cb(0, 42)
},
release: function (path, fd, cb) {
t.same(fd, 42, 'fd was passed to release')
cb(0)
},
read: function (path, fd, buf, len, pos, cb) {
var str = 'hello world'.slice(pos, pos + len)
if (!str) return cb(0)
buf.write(str)
return cb(str.length)
}
}
const fuse = new Fuse(mnt, ops, { debug: true })
fuse.mount(function (err) {
t.error(err, 'no error')
fs.readFile(path.join(mnt, 'test'), function (err, buf) {
t.error(err, 'no error')
t.same(buf, new Buffer('hello world'), 'read file')
fs.readFile(path.join(mnt, 'test'), function (err, buf) {
t.error(err, 'no error')
t.same(buf, new Buffer('hello world'), 'read file again')
fs.createReadStream(path.join(mnt, 'test'), { start: 0, end: 4 }).pipe(concat(function (buf) {
t.same(buf, new Buffer('hello'), 'partial read file')
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')
fuse.unmount(mnt, function () {
t.end()
})
}))
}))
})
})
})
})

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