Unify handling of fuse_conn_info options

Instead of using command line options to modify struct fuse_conn_info
before and after calling the init() handler, we now give the file system
explicit control over this.
This commit is contained in:
Nikolaus Rath
2016-10-15 16:09:16 -07:00
parent 587df37041
commit d49f2e77b4
14 changed files with 226 additions and 161 deletions
+22 -12
View File
@@ -9,22 +9,32 @@ Unreleased Changes
the `splice_read` option is now enabled by default. As usual, this
can be changed in the file system's `init` handler.
* `fuse_session_new` now treats low-level options more consistently:
First, options are used to modify FUSE defaults. Second, the file
system may inspect and/or adjust the settings in its `init`
handler. Third, command line arguments take precedence over any
modifications made by the `init` handler.
* The treatment of low-level options has been made more consistent:
* Removed the `async_read` field from `struct fuse_conn_info`. To
determine if the kernel supports asynchronous reads, file systems
should check the `FUSE_CAP_ASYNC_READ` bit of the `capable`
field. To enable/disable asynchronous reads, file systems should set
the flag in the `wanted` field.
Options that can be set in the init() handler (via the
fuse_conn_info parameter) can now be set only here,
i.e. fuse_session_new() no longer accepts arguments that change the
fuse_conn_info object before or after the call do init(). As a side
effect, this removes the ambiguity where some options can be
overwritten by init(), while others overwrite the choices made by
init().
For file systems that wish to offer command line options for these
settings, the new fuse_parse_conn_info_opts() and
fuse_apply_conn_info_opts() functions are available.
Consequently, the fuse_lowlevel_help() method has been dropped.
* The `async_read` field in `struct fuse_conn_info` has been
removed. To determine if the kernel supports asynchronous reads,
file systems should check the `FUSE_CAP_ASYNC_READ` bit of the
`capable` field. To enable/disable asynchronous reads, file systems
should set the flag in the `wanted` field.
* The `fuse_parse_cmdline` function no longer prints out help when the
``--verbose`` or ``--help`` flags are given. This needs to be done
by the file system (e.g. using the `fuse_cmdline_help()`,
`fuse_lowlevel_help()` and `fuse_mount_help()` functions).
by the file system (e.g. using the `fuse_cmdline_help()` and
`fuse_mount_help()` functions).
* Added ``example/cuse_client.c`` to test ``example/cuse.c``.
-1
View File
@@ -195,7 +195,6 @@ int main(int argc, char *argv[])
if (opts.show_help) {
printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
fuse_cmdline_help();
fuse_lowlevel_help();
fuse_mount_help();
ret = 0;
goto err_out1;
-1
View File
@@ -287,7 +287,6 @@ int main(int argc, char *argv[]) {
if (opts.show_help) {
show_help(argv[0]);
fuse_cmdline_help();
fuse_lowlevel_help();
fuse_mount_help();
ret = 0;
goto err_out1;
-1
View File
@@ -310,7 +310,6 @@ int main(int argc, char *argv[]) {
if (opts.show_help) {
show_help(argv[0]);
fuse_cmdline_help();
fuse_lowlevel_help();
fuse_mount_help();
ret = 0;
goto err_out1;
-1
View File
@@ -353,7 +353,6 @@ int main(int argc, char *argv[]) {
if (opts.show_help) {
show_help(argv[0]);
fuse_cmdline_help();
fuse_lowlevel_help();
fuse_mount_help();
ret = 0;
goto err_out1;
-1
View File
@@ -469,7 +469,6 @@ int main(int argc, char *argv[])
if (opts.show_help) {
printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
fuse_cmdline_help();
fuse_lowlevel_help();
fuse_mount_help();
ret = 0;
goto err_out1;
+55
View File
@@ -205,6 +205,61 @@ struct fuse_conn_info {
struct fuse_session;
struct fuse_pollhandle;
struct fuse_conn_info_opts;
/**
* This function parses several command-line options that can be used
* to override elements of struct fuse_conn_info. The pointer returned
* by this function should be passed to the
* fuse_apply_conn_info_opts() method by the file system's init()
* handler.
*
* Before using this function, think twice if you really want these
* parameters to be adjustable from the command line. In most cases,
* they should be determined by the file system internally.
*
* The following options are recognized:
*
* -o max_write=N sets conn->max_write
* -o max_readahead=N sets conn->max_readahead
* -o max_background=N sets conn->max_background
* -o congestion_threshold=N sets conn->congestion_threshold
* -o async_read sets FUSE_CAP_ASYNC_READ in conn->want
* -o sync_read unsets FUSE_CAP_ASYNC_READ in conn->want
* -o atomic_o_trunc sets FUSE_CAP_ATOMIC_O_TRUNC in conn->want
* -o no_remote_lock Equivalent to -o no_remote_flock,no_remote_posix_lock
* -o no_remote_flock Unsets FUSE_CAP_FLOCK_LOCKS in conn->want
* -o no_remote_posix_lock Unsets FUSE_CAP_POSIX_LOCKS in conn->want
* -o [no_]splice_write (un-)sets FUSE_CAP_SPLICE_WRITE in conn->want
* -o [no_]splice_move (un-)sets FUSE_CAP_SPLICE_MOVE in conn->want
* -o [no_]splice_read (un-)sets FUSE_CAP_SPLICE_READ in conn->want
* -o [no_]auto_inval_data (un-)sets FUSE_CAP_AUTO_INVAL_DATA in conn->want
* -o readdirplus=no unsets FUSE_CAP_READDIRPLUS in conn->want
* -o readdirplus=yes sets FUSE_CAP_READDIRPLUS and unsets
* FUSE_CAP_READDIRPLUS_AUTO in conn->want
* -o readdirplus=auto sets FUSE_CAP_READDIRPLUS and
* FUSE_CAP_READDIRPLUS_AUTO in conn->want
* -o [no_]async_dio (un-)sets FUSE_CAP_ASYNC_DIO in conn->want
* -o [no_]writeback_cache (un-)sets FUSE_CAP_WRITEBACK_CACHE in conn->want
* -o time_gran=N sets conn->time_gran
*
* Known options will be removed from *args*, unknown options will be
* passed through unchanged.
*
* @param args argument vector (input+output)
* @return parsed options
**/
struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args);
/**
* This function applies the (parsed) parameters in *opts* to the
* *conn* pointer. It may modify the following fields: wants,
* max_write, max_readahead, congestion_threshold, max_background,
* time_gran. A field is only set (or unset) if the corresponding
* option has been explicitly set.
*/
void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts,
struct fuse_conn_info *conn);
/**
* Go into the background
+6 -10
View File
@@ -1627,19 +1627,12 @@ void fuse_lowlevel_version(void);
*/
void fuse_mount_version(void);
/**
* Print available low-level options to stdout.
* These options may be passed to `fuse_session_new()`
*/
void fuse_lowlevel_help(void);
/**
* Print available mount options to stdout.
* These options may be passed to `fuse_session_new()`
*/
void fuse_mount_help(void);
/**
* Print available options for `fuse_parse_cmdline()`.
*/
@@ -1684,9 +1677,12 @@ int fuse_parse_cmdline(struct fuse_args *args,
* Returns a session structure suitable for passing to
* fuse_session_mount() and fuse_session_loop().
*
* Known options are defined in `struct fuse_opt fuse_ll_opts[]` and
* `struct fuse_opt fuse_mount_opts[]`. If not all options are known,
* an error message is written to stderr and the function returns NULL.
* Known options can be listed by fuse_mount_help(). Additionally,
* this function accepts the `--debug` option (which is not explicitly
* listed by fuse_mount_help()).
*
* If not all options are known, an error message is written to stderr
* and the function returns NULL.
*
* @param args argument vector
* @param op the (low-level) filesystem operations
-1
View File
@@ -4661,7 +4661,6 @@ struct fuse *fuse_new(struct fuse_args *args,
if (f->conf.show_help) {
fuse_lib_help();
fuse_lowlevel_help();
fuse_mount_help();
/* Defer printing module help until modules
have been loaded */
-23
View File
@@ -41,34 +41,11 @@ struct fuse_notify_req {
struct fuse_notify_req *prev;
};
struct session_opts {
int atomic_o_trunc;
int no_remote_posix_lock;
int no_remote_flock;
int splice_write;
int splice_move;
int splice_read;
int no_splice_write;
int no_splice_move;
int no_splice_read;
int auto_inval_data;
int no_auto_inval_data;
int no_readdirplus;
int no_readdirplus_auto;
int async_dio;
int no_async_dio;
int writeback_cache;
int no_writeback_cache;
int async_read;
int sync_read;
};
struct fuse_session {
char *mountpoint;
volatile int exited;
int fd;
struct mount_opts *mo;
struct session_opts opts;
int debug;
int allow_root;
struct fuse_lowlevel_ops op;
-99
View File
@@ -1809,42 +1809,6 @@ static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
fuse_reply_err(req, ENOSYS);
}
static void apply_want_options(struct session_opts *opts,
struct fuse_conn_info *conn)
{
#define LL_ENABLE(cond,cap) \
if (cond) conn->want |= (cap)
#define LL_DISABLE(cond,cap) \
if (cond) conn->want &= ~(cap)
LL_ENABLE(opts->splice_read, FUSE_CAP_SPLICE_READ);
LL_DISABLE(opts->no_splice_read, FUSE_CAP_SPLICE_READ);
LL_ENABLE(opts->splice_write, FUSE_CAP_SPLICE_WRITE);
LL_DISABLE(opts->no_splice_write, FUSE_CAP_SPLICE_WRITE);
LL_ENABLE(opts->splice_move, FUSE_CAP_SPLICE_MOVE);
LL_DISABLE(opts->no_splice_move, FUSE_CAP_SPLICE_MOVE);
LL_ENABLE(opts->auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA);
LL_DISABLE(opts->no_auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA);
LL_DISABLE(opts->no_readdirplus, FUSE_CAP_READDIRPLUS);
LL_DISABLE(opts->no_readdirplus_auto, FUSE_CAP_READDIRPLUS_AUTO);
LL_ENABLE(opts->async_dio, FUSE_CAP_ASYNC_DIO);
LL_DISABLE(opts->no_async_dio, FUSE_CAP_ASYNC_DIO);
LL_ENABLE(opts->writeback_cache, FUSE_CAP_WRITEBACK_CACHE);
LL_DISABLE(opts->no_writeback_cache, FUSE_CAP_WRITEBACK_CACHE);
LL_ENABLE(opts->async_read, FUSE_CAP_ASYNC_READ);
LL_DISABLE(opts->sync_read, FUSE_CAP_ASYNC_READ);
LL_DISABLE(opts->no_remote_posix_lock, FUSE_CAP_POSIX_LOCKS);
LL_DISABLE(opts->no_remote_flock, FUSE_CAP_FLOCK_LOCKS);
}
static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
{
struct fuse_init_in *arg = (struct fuse_init_in *) inarg;
@@ -1949,19 +1913,9 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
f->conn.max_write = bufsize;
f->got_init = 1;
/* Apply command-line options (so that init() handler has
an idea about user preferences */
apply_want_options(&f->opts, &f->conn);
/* Allow file-system to overwrite defaults */
if (f->op.init)
f->op.init(f->userdata, &f->conn);
/* Now explicitly overwrite file-system's decision
with command-line options */
apply_want_options(&f->opts, &f->conn);
/* Always enable big writes, this is superseded
by the max_write option */
outarg.flags |= FUSE_BIG_WRITES;
@@ -2570,35 +2524,6 @@ static const struct fuse_opt fuse_ll_opts[] = {
LL_OPTION("debug", debug, 1),
LL_OPTION("-d", debug, 1),
LL_OPTION("allow_root", allow_root, 1),
LL_OPTION("max_write=%u", conn.max_write, 0),
LL_OPTION("max_readahead=%u", conn.max_readahead, 0),
LL_OPTION("max_background=%u", conn.max_background, 0),
LL_OPTION("congestion_threshold=%u", conn.congestion_threshold, 0),
LL_OPTION("sync_read", opts.sync_read, 1),
LL_OPTION("async_read", opts.async_read, 1),
LL_OPTION("atomic_o_trunc", opts.atomic_o_trunc, 1),
LL_OPTION("no_remote_lock", opts.no_remote_posix_lock, 1),
LL_OPTION("no_remote_lock", opts.no_remote_flock, 1),
LL_OPTION("no_remote_flock", opts.no_remote_flock, 1),
LL_OPTION("no_remote_posix_lock", opts.no_remote_posix_lock, 1),
LL_OPTION("splice_write", opts.splice_write, 1),
LL_OPTION("no_splice_write", opts.no_splice_write, 1),
LL_OPTION("splice_move", opts.splice_move, 1),
LL_OPTION("no_splice_move", opts.no_splice_move, 1),
LL_OPTION("splice_read", opts.splice_read, 1),
LL_OPTION("no_splice_read", opts.no_splice_read, 1),
LL_OPTION("auto_inval_data", opts.auto_inval_data, 1),
LL_OPTION("no_auto_inval_data", opts.no_auto_inval_data, 1),
LL_OPTION("readdirplus=no", opts.no_readdirplus, 1),
LL_OPTION("readdirplus=yes", opts.no_readdirplus, 0),
LL_OPTION("readdirplus=yes", opts.no_readdirplus_auto, 1),
LL_OPTION("readdirplus=auto", opts.no_readdirplus, 0),
LL_OPTION("readdirplus=auto", opts.no_readdirplus_auto, 0),
LL_OPTION("async_dio", opts.async_dio, 1),
LL_OPTION("no_async_dio", opts.no_async_dio, 1),
LL_OPTION("writeback_cache", opts.writeback_cache, 1),
LL_OPTION("no_writeback_cache", opts.no_writeback_cache, 1),
LL_OPTION("time_gran=%u", conn.time_gran, 0),
FUSE_OPT_END
};
@@ -2608,30 +2533,6 @@ void fuse_lowlevel_version(void)
FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION);
}
void fuse_lowlevel_help(void)
{
printf(
"Low-level options\n"
" -o max_write=N set maximum size of write requests\n"
" -o max_readahead=N set maximum readahead\n"
" -o max_background=N set number of maximum background requests\n"
" -o congestion_threshold=N set kernel's congestion threshold\n"
" -o async_read perform reads asynchronously (default)\n"
" -o sync_read perform reads synchronously\n"
" -o atomic_o_trunc enable atomic open+truncate support\n"
" -o no_remote_lock disable remote file locking\n"
" -o no_remote_flock disable remote file locking (BSD)\n"
" -o no_remote_posix_lock disable remote file locking (POSIX)\n"
" -o [no_]splice_write use splice to write to the fuse device\n"
" -o [no_]splice_move move data while splicing to the fuse device\n"
" -o [no_]splice_read use splice to read from the fuse device\n"
" -o [no_]auto_inval_data use automatic kernel cache invalidation logic\n"
" -o readdirplus=S control readdirplus use (yes|no|auto)\n"
" -o [no_]async_dio asynchronous direct I/O\n"
" -o [no_]writeback_cache asynchronous, buffered writes\n"
" -o time_gran=N time granularity in nsec\n\n");
}
void fuse_session_destroy(struct fuse_session *se)
{
struct fuse_ll_pipe *llp;
+127
View File
@@ -49,6 +49,69 @@ static const struct fuse_opt fuse_helper_opts[] = {
FUSE_OPT_END
};
struct fuse_conn_info_opts {
int atomic_o_trunc;
int no_remote_posix_lock;
int no_remote_flock;
int splice_write;
int splice_move;
int splice_read;
int no_splice_write;
int no_splice_move;
int no_splice_read;
int auto_inval_data;
int no_auto_inval_data;
int no_readdirplus;
int no_readdirplus_auto;
int async_dio;
int no_async_dio;
int writeback_cache;
int no_writeback_cache;
int async_read;
int sync_read;
unsigned max_write;
unsigned max_readahead;
unsigned max_background;
unsigned congestion_threshold;
unsigned time_gran;
};
#define CONN_OPTION(t, p, v) \
{ t, offsetof(struct fuse_conn_info_opts, p), v }
static const struct fuse_opt conn_info_opt_spec[] = {
CONN_OPTION("max_write=%u", max_write, 0),
CONN_OPTION("max_readahead=%u", max_readahead, 0),
CONN_OPTION("max_background=%u", max_background, 0),
CONN_OPTION("congestion_threshold=%u", congestion_threshold, 0),
CONN_OPTION("sync_read", sync_read, 1),
CONN_OPTION("async_read", async_read, 1),
CONN_OPTION("atomic_o_trunc", atomic_o_trunc, 1),
CONN_OPTION("no_remote_lock", no_remote_posix_lock, 1),
CONN_OPTION("no_remote_lock", no_remote_flock, 1),
CONN_OPTION("no_remote_flock", no_remote_flock, 1),
CONN_OPTION("no_remote_posix_lock", no_remote_posix_lock, 1),
CONN_OPTION("splice_write", splice_write, 1),
CONN_OPTION("no_splice_write", no_splice_write, 1),
CONN_OPTION("splice_move", splice_move, 1),
CONN_OPTION("no_splice_move", no_splice_move, 1),
CONN_OPTION("splice_read", splice_read, 1),
CONN_OPTION("no_splice_read", no_splice_read, 1),
CONN_OPTION("auto_inval_data", auto_inval_data, 1),
CONN_OPTION("no_auto_inval_data", no_auto_inval_data, 1),
CONN_OPTION("readdirplus=no", no_readdirplus, 1),
CONN_OPTION("readdirplus=yes", no_readdirplus, 0),
CONN_OPTION("readdirplus=yes", no_readdirplus_auto, 1),
CONN_OPTION("readdirplus=auto", no_readdirplus, 0),
CONN_OPTION("readdirplus=auto", no_readdirplus_auto, 0),
CONN_OPTION("async_dio", async_dio, 1),
CONN_OPTION("no_async_dio", no_async_dio, 1),
CONN_OPTION("writeback_cache", writeback_cache, 1),
CONN_OPTION("no_writeback_cache", no_writeback_cache, 1),
CONN_OPTION("time_gran=%u", time_gran, 0),
FUSE_OPT_END
};
void fuse_cmdline_help(void)
{
printf("General options:\n"
@@ -262,3 +325,67 @@ out1:
fuse_opt_free_args(&args);
return res;
}
void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts,
struct fuse_conn_info *conn)
{
if(opts->max_write)
conn->max_write = opts->max_write;
if(opts->max_background)
conn->max_background = opts->max_background;
if(opts->congestion_threshold)
conn->congestion_threshold = opts->congestion_threshold;
if(opts->time_gran)
conn->time_gran = opts->time_gran;
if(opts->max_readahead)
conn->max_readahead = opts->max_readahead;
#define LL_ENABLE(cond,cap) \
if (cond) conn->want |= (cap)
#define LL_DISABLE(cond,cap) \
if (cond) conn->want &= ~(cap)
LL_ENABLE(opts->splice_read, FUSE_CAP_SPLICE_READ);
LL_DISABLE(opts->no_splice_read, FUSE_CAP_SPLICE_READ);
LL_ENABLE(opts->splice_write, FUSE_CAP_SPLICE_WRITE);
LL_DISABLE(opts->no_splice_write, FUSE_CAP_SPLICE_WRITE);
LL_ENABLE(opts->splice_move, FUSE_CAP_SPLICE_MOVE);
LL_DISABLE(opts->no_splice_move, FUSE_CAP_SPLICE_MOVE);
LL_ENABLE(opts->auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA);
LL_DISABLE(opts->no_auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA);
LL_DISABLE(opts->no_readdirplus, FUSE_CAP_READDIRPLUS);
LL_DISABLE(opts->no_readdirplus_auto, FUSE_CAP_READDIRPLUS_AUTO);
LL_ENABLE(opts->async_dio, FUSE_CAP_ASYNC_DIO);
LL_DISABLE(opts->no_async_dio, FUSE_CAP_ASYNC_DIO);
LL_ENABLE(opts->writeback_cache, FUSE_CAP_WRITEBACK_CACHE);
LL_DISABLE(opts->no_writeback_cache, FUSE_CAP_WRITEBACK_CACHE);
LL_ENABLE(opts->async_read, FUSE_CAP_ASYNC_READ);
LL_DISABLE(opts->sync_read, FUSE_CAP_ASYNC_READ);
LL_DISABLE(opts->no_remote_posix_lock, FUSE_CAP_POSIX_LOCKS);
LL_DISABLE(opts->no_remote_flock, FUSE_CAP_FLOCK_LOCKS);
}
struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args)
{
struct fuse_conn_info_opts *opts;
opts = calloc(1, sizeof(struct fuse_conn_info_opts));
if(opts == NULL) {
fprintf(stderr, "calloc failed\n");
return NULL;
}
if(fuse_opt_parse(args, opts, conn_info_opt_spec, NULL) == -1) {
free(opts);
return NULL;
}
return opts;
}
+5 -10
View File
@@ -29,11 +29,8 @@ def name_generator(__ctr=[0]):
__ctr[0] += 1
return 'testfile_%d' % __ctr[0]
LL_OPTIONS = [ ['-o', 'splice_move,splice_write,splice_read' ],
['-o', 'clone_fd,writeback_cache' ] ]
@pytest.mark.parametrize("name", ('hello', 'hello_ll'))
@pytest.mark.parametrize("options", LL_OPTIONS)
@pytest.mark.parametrize("options", ([], ['-o', 'clone_fd']))
def test_hello(tmpdir, name, options):
mnt_dir = str(tmpdir)
cmdline = base_cmdline + \
@@ -63,14 +60,13 @@ def test_hello(tmpdir, name, options):
@pytest.mark.parametrize("name", ('passthrough', 'passthrough_fh',
'passthrough_ll'))
@pytest.mark.parametrize("options", LL_OPTIONS)
def test_passthrough(tmpdir, name, options):
def test_passthrough(tmpdir, name):
mnt_dir = str(tmpdir.mkdir('mnt'))
src_dir = str(tmpdir.mkdir('src'))
cmdline = base_cmdline + \
[ pjoin(basename, 'example', name),
'-f', mnt_dir ] + options
'-f', mnt_dir ]
if not name.endswith('_ll'):
cmdline += [ '-o', 'use_ino,readdir_ino,kernel_cache' ]
mount_process = subprocess.Popen(cmdline)
@@ -146,13 +142,12 @@ def test_poll(tmpdir):
@pytest.mark.parametrize("name",
('notify_inval_inode',
'notify_store_retrieve'))
@pytest.mark.parametrize("options", LL_OPTIONS)
@pytest.mark.parametrize("notify", (True, False))
def test_notify1(tmpdir, name, options, notify):
def test_notify1(tmpdir, name, notify):
mnt_dir = str(tmpdir)
cmdline = base_cmdline + \
[ pjoin(basename, 'example', name),
'-f', '--update-interval=1', mnt_dir ] + options
'-f', '--update-interval=1', mnt_dir ]
if not notify:
cmdline.append('--no-notify')
mount_process = subprocess.Popen(cmdline)
+11 -1
View File
@@ -39,11 +39,20 @@ struct options {
static const struct fuse_opt option_spec[] = {
OPTION("writeback_cache", writeback),
OPTION("--data-size=%d", data_size),
FUSE_OPT_KEY("writeback_cache", FUSE_OPT_KEY_KEEP),
FUSE_OPT_END
};
static int got_write;
static void tfs_init (void *userdata, struct fuse_conn_info *conn)
{
(void) userdata;
if(options.writeback) {
assert(conn->capable & FUSE_CAP_WRITEBACK_CACHE);
conn->want |= FUSE_CAP_WRITEBACK_CACHE;
}
}
static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
stbuf->st_ino = ino;
if (ino == FUSE_ROOT_ID) {
@@ -126,6 +135,7 @@ static void tfs_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
}
static struct fuse_lowlevel_ops tfs_oper = {
.init = tfs_init,
.lookup = tfs_lookup,
.getattr = tfs_getattr,
.open = tfs_open,