From: Eric Wong Date: Wed, 5 Mar 2014 13:45:44 +0000 (+0100) Subject: libfuse: implement readdirplus for high-level API X-Git-Tag: fuse-3.0.0pre0~90 X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=6bf2e6f07c133f7b145a4726c5d962f14c650ca7;p=qemu-gpiodev%2Flibfuse.git libfuse: implement readdirplus for high-level API Reuse the old "readdir" callback, but add a flags argument, that has FUSE_READDIR_PLUS in case this is a "plus" version. Filesystems can safely ignore this flag, but if they want they can add optimizations based on it: i.e. only retrieve the full attributes in PLUS mode. The filler function is also given a flags argument and the filesystem can set FUSE_FILL_DIR_PLUS if all the attributes in "stat" are valid. --- diff --git a/ChangeLog b/ChangeLog index 69e5fc9..9932a7c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2014-03-05 Miklos Szeredi + + * libfuse: implement readdirplus for high-level API. Reuse the + old "readdir" callback, but add a flags argument, that has + FUSE_READDIR_PLUS in case this is a "plus" version. Filesystems + can safely ignore this flag, but if they want they can add + optimizations based on it: i.e. only retrieve the full attributes + in PLUS mode. The filler function is also given a flags argument + and the filesystem can set FUSE_FILL_DIR_PLUS if all the + attributes in "stat" are valid. Original patch by Eric Wong + 2014-02-21 Miklos Szeredi * libfuse: added fuse_lo-plus.c to the examples diff --git a/configure.ac b/configure.ac index 04d7a6d..64b2af6 100644 --- a/configure.ac +++ b/configure.ac @@ -54,7 +54,7 @@ if test "$enable_mtab" = "no"; then fi AC_CHECK_FUNCS([fork setxattr fdatasync splice vmsplice utimensat pipe2]) -AC_CHECK_FUNCS([posix_fallocate]) +AC_CHECK_FUNCS([posix_fallocate fstatat]) AC_CHECK_MEMBERS([struct stat.st_atim]) AC_CHECK_MEMBERS([struct stat.st_atimespec]) diff --git a/example/fioc.c b/example/fioc.c index 2117ac8..368f807 100755 --- a/example/fioc.c +++ b/example/fioc.c @@ -169,17 +169,19 @@ static int fioc_truncate(const char *path, off_t size) } static int fioc_readdir(const char *path, void *buf, fuse_fill_dir_t filler, - off_t offset, struct fuse_file_info *fi) + off_t offset, struct fuse_file_info *fi, + enum fuse_readdir_flags flags) { (void) fi; (void) offset; + (void) flags; if (fioc_file_type(path) != FIOC_ROOT) return -ENOENT; - filler(buf, ".", NULL, 0); - filler(buf, "..", NULL, 0); - filler(buf, FIOC_NAME, NULL, 0); + filler(buf, ".", NULL, 0, 0); + filler(buf, "..", NULL, 0, 0); + filler(buf, FIOC_NAME, NULL, 0, 0); return 0; } diff --git a/example/fsel.c b/example/fsel.c index 69202ee..b496c9a 100755 --- a/example/fsel.c +++ b/example/fsel.c @@ -87,20 +87,22 @@ static int fsel_getattr(const char *path, struct stat *stbuf) } static int fsel_readdir(const char *path, void *buf, fuse_fill_dir_t filler, - off_t offset, struct fuse_file_info *fi) + off_t offset, struct fuse_file_info *fi, + enum fuse_readdir_flags flags) { char name[2] = { }; int i; (void) offset; (void) fi; + (void) flags; if (strcmp(path, "/") != 0) return -ENOENT; for (i = 0; i < FSEL_FILES; i++) { name[0] = fsel_hex_map[i]; - filler(buf, name, NULL, 0); + filler(buf, name, NULL, 0, 0); } return 0; diff --git a/example/fusexmp.c b/example/fusexmp.c index 6f63ae9..fe20f57 100755 --- a/example/fusexmp.c +++ b/example/fusexmp.c @@ -81,13 +81,15 @@ static int xmp_readlink(const char *path, char *buf, size_t size) static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler, - off_t offset, struct fuse_file_info *fi) + off_t offset, struct fuse_file_info *fi, + enum fuse_readdir_flags flags) { DIR *dp; struct dirent *de; (void) offset; (void) fi; + (void) flags; dp = opendir(path); if (dp == NULL) @@ -98,7 +100,7 @@ static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler, memset(&st, 0, sizeof(st)); st.st_ino = de->d_ino; st.st_mode = de->d_type << 12; - if (filler(buf, de->d_name, &st, 0)) + if (filler(buf, de->d_name, &st, 0, 0)) break; } diff --git a/example/fusexmp_fh.c b/example/fusexmp_fh.c index ba6789b..531438f 100755 --- a/example/fusexmp_fh.c +++ b/example/fusexmp_fh.c @@ -128,7 +128,8 @@ static inline struct xmp_dirp *get_dirp(struct fuse_file_info *fi) } static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler, - off_t offset, struct fuse_file_info *fi) + off_t offset, struct fuse_file_info *fi, + enum fuse_readdir_flags flags) { struct xmp_dirp *d = get_dirp(fi); @@ -141,18 +142,30 @@ static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler, while (1) { struct stat st; off_t nextoff; + enum fuse_fill_dir_flags fill_flags = 0; if (!d->entry) { d->entry = readdir(d->dp); if (!d->entry) break; } - - memset(&st, 0, sizeof(st)); - st.st_ino = d->entry->d_ino; - st.st_mode = d->entry->d_type << 12; +#ifdef HAVE_FSTATAT + if (flags & FUSE_READDIR_PLUS) { + int res; + + res = fstatat(dirfd(d->dp), d->entry->d_name, &st, + AT_SYMLINK_NOFOLLOW); + if (res != -1) + fill_flags |= FUSE_FILL_DIR_PLUS; + } +#endif + if (!(fill_flags & FUSE_FILL_DIR_PLUS)) { + memset(&st, 0, sizeof(st)); + st.st_ino = d->entry->d_ino; + st.st_mode = d->entry->d_type << 12; + } nextoff = telldir(d->dp); - if (filler(buf, d->entry->d_name, &st, nextoff)) + if (filler(buf, d->entry->d_name, &st, nextoff, fill_flags)) break; d->entry = NULL; diff --git a/example/hello.c b/example/hello.c index d26d826..3c24c8b 100755 --- a/example/hello.c +++ b/example/hello.c @@ -65,17 +65,19 @@ static int hello_getattr(const char *path, struct stat *stbuf) } static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler, - off_t offset, struct fuse_file_info *fi) + off_t offset, struct fuse_file_info *fi, + enum fuse_readdir_flags flags) { (void) offset; (void) fi; + (void) flags; if (strcmp(path, "/") != 0) return -ENOENT; - filler(buf, ".", NULL, 0); - filler(buf, "..", NULL, 0); - filler(buf, hello_path + 1, NULL, 0); + filler(buf, ".", NULL, 0, 0); + filler(buf, "..", NULL, 0, 0); + filler(buf, hello_path + 1, NULL, 0, 0); return 0; } diff --git a/include/fuse.h b/include/fuse.h index a3a047e..edece77 100644 --- a/include/fuse.h +++ b/include/fuse.h @@ -36,16 +36,46 @@ extern "C" { /** Handle for a FUSE filesystem */ struct fuse; +/** + * Readdir flags, passed to ->readdir() + */ +enum fuse_readdir_flags { + /** + * "Plus" mode. + * + * The kernel wants to prefill the inode cache during readdir. The + * filesystem may honour this by filling in the attributes and setting + * FUSE_FILL_DIR_FLAGS for the filler function. The filesystem may also + * just ignore this flag completely. + */ + FUSE_READDIR_PLUS = (1 << 0), +}; + +enum fuse_fill_dir_flags { + /** + * "Plus" mode: all file attributes are valid + * + * The attributes are used by the kernel to prefill the inode cache + * during a readdir. + * + * It is okay to set FUSE_FILL_DIR_PLUS if FUSE_READDIR_PLUS is not set + * and vice versa. + */ + FUSE_FILL_DIR_PLUS = (1 << 1), +}; + /** Function to add an entry in a readdir() operation * * @param buf the buffer passed to the readdir() operation * @param name the file name of the directory entry * @param stat file attributes, can be NULL * @param off offset of the next entry or zero + * @param flags fill flags * @return 1 if buffer is full, zero otherwise */ typedef int (*fuse_fill_dir_t) (void *buf, const char *name, - const struct stat *stbuf, off_t off); + const struct stat *stbuf, off_t off, + enum fuse_fill_dir_flags flags); /** * The file system operations: @@ -289,9 +319,10 @@ struct fuse_operations { * '1'. * * Introduced in version 2.3 + * The "flags" argument added in version 3.0 */ int (*readdir) (const char *, void *, fuse_fill_dir_t, off_t, - struct fuse_file_info *); + struct fuse_file_info *, enum fuse_readdir_flags); /** Release directory * @@ -806,7 +837,7 @@ int fuse_fs_opendir(struct fuse_fs *fs, const char *path, struct fuse_file_info *fi); int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf, fuse_fill_dir_t filler, off_t off, - struct fuse_file_info *fi); + struct fuse_file_info *fi, enum fuse_readdir_flags flags); int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync, struct fuse_file_info *fi); int fuse_fs_releasedir(struct fuse_fs *fs, const char *path, diff --git a/lib/fuse.c b/lib/fuse.c index 3f8e601..c9c36ad 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -188,11 +188,19 @@ struct node_lru { struct timespec forget_time; }; +struct fuse_direntry { + struct stat stat; + char *name; + struct fuse_direntry *next; +}; + struct fuse_dh { pthread_mutex_t lock; struct fuse *fuse; fuse_req_t req; char *contents; + struct fuse_direntry *first; + struct fuse_direntry **last; int allocated; unsigned len; unsigned size; @@ -1887,16 +1895,19 @@ int fuse_fs_releasedir(struct fuse_fs *fs, const char *path, int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf, fuse_fill_dir_t filler, off_t off, - struct fuse_file_info *fi) + struct fuse_file_info *fi, + enum fuse_readdir_flags flags) { fuse_get_context()->private_data = fs->user_data; if (fs->op.readdir) { - if (fs->debug) - fprintf(stderr, "readdir[%llu] from %llu\n", + if (fs->debug) { + fprintf(stderr, "readdir%s[%llu] from %llu\n", + (flags & FUSE_READDIR_PLUS) ? "plus" : "", (unsigned long long) fi->fh, (unsigned long long) off); + } - return fs->op.readdir(path, buf, filler, off, fi); + return fs->op.readdir(path, buf, filler, off, fi, flags); } else { return -ENOSYS; } @@ -2329,6 +2340,28 @@ static void update_stat(struct node *node, const struct stat *stbuf) curr_time(&node->stat_updated); } +static int do_lookup(struct fuse *f, fuse_ino_t nodeid, const char *name, + struct fuse_entry_param *e) +{ + struct node *node; + + node = find_node(f, nodeid, name); + if (node == NULL) + return -ENOMEM; + + e->ino = node->nodeid; + e->generation = node->generation; + e->entry_timeout = f->conf.entry_timeout; + e->attr_timeout = f->conf.attr_timeout; + if (f->conf.auto_cache) { + pthread_mutex_lock(&f->lock); + update_stat(node, &e->attr); + pthread_mutex_unlock(&f->lock); + } + set_stat(f, e->ino, &e->attr); + return 0; +} + static int lookup_path(struct fuse *f, fuse_ino_t nodeid, const char *name, const char *path, struct fuse_entry_param *e, struct fuse_file_info *fi) @@ -2341,25 +2374,10 @@ static int lookup_path(struct fuse *f, fuse_ino_t nodeid, else res = fuse_fs_getattr(f->fs, path, &e->attr); if (res == 0) { - struct node *node; - - node = find_node(f, nodeid, name); - if (node == NULL) - res = -ENOMEM; - else { - e->ino = node->nodeid; - e->generation = node->generation; - e->entry_timeout = f->conf.entry_timeout; - e->attr_timeout = f->conf.attr_timeout; - if (f->conf.auto_cache) { - pthread_mutex_lock(&f->lock); - update_stat(node, &e->attr); - pthread_mutex_unlock(&f->lock); - } - set_stat(f, e->ino, &e->attr); - if (f->conf.debug) - fprintf(stderr, " NODEID: %llu\n", - (unsigned long long) e->ino); + res = do_lookup(f, nodeid, name, e); + if (res == 0 && f->conf.debug) { + fprintf(stderr, " NODEID: %llu\n", + (unsigned long long) e->ino); } } return res; @@ -3199,6 +3217,7 @@ static void fuse_lib_opendir(fuse_req_t req, fuse_ino_t ino, memset(dh, 0, sizeof(struct fuse_dh)); dh->fuse = f; dh->contents = NULL; + dh->first = NULL; dh->len = 0; dh->filled = 0; dh->nodeid = ino; @@ -3257,12 +3276,56 @@ static int extend_contents(struct fuse_dh *dh, unsigned minsize) return 0; } +static int fuse_add_direntry_to_dh(struct fuse_dh *dh, const char *name, + struct stat *st) +{ + struct fuse_direntry *de; + + de = malloc(sizeof(struct fuse_direntry)); + if (!de) { + dh->error = -ENOMEM; + return -1; + } + de->name = strdup(name); + if (!de->name) { + dh->error = -ENOMEM; + free(de); + return -1; + } + de->stat = *st; + de->next = NULL; + + *dh->last = de; + dh->last = &de->next; + + return 0; +} + +static fuse_ino_t lookup_nodeid(struct fuse *f, fuse_ino_t parent, + const char *name) +{ + struct node *node; + fuse_ino_t res = FUSE_UNKNOWN_INO; + + pthread_mutex_lock(&f->lock); + node = lookup_node(f, parent, name); + if (node) + res = node->nodeid; + pthread_mutex_unlock(&f->lock); + + return res; +} + static int fill_dir(void *dh_, const char *name, const struct stat *statp, - off_t off) + off_t off, enum fuse_fill_dir_flags flags) { struct fuse_dh *dh = (struct fuse_dh *) dh_; struct stat stbuf; - size_t newlen; + + if ((flags & ~FUSE_FILL_DIR_PLUS) != 0) { + dh->error = -EIO; + return 1; + } if (statp) stbuf = *statp; @@ -3274,16 +3337,19 @@ static int fill_dir(void *dh_, const char *name, const struct stat *statp, if (!dh->fuse->conf.use_ino) { stbuf.st_ino = FUSE_UNKNOWN_INO; if (dh->fuse->conf.readdir_ino) { - struct node *node; - pthread_mutex_lock(&dh->fuse->lock); - node = lookup_node(dh->fuse, dh->nodeid, name); - if (node) - stbuf.st_ino = (ino_t) node->nodeid; - pthread_mutex_unlock(&dh->fuse->lock); + stbuf.st_ino = (ino_t) + lookup_nodeid(dh->fuse, dh->nodeid, name); } } if (off) { + size_t newlen; + + if (dh->first) { + dh->error = -EIO; + return 1; + } + if (extend_contents(dh, dh->needlen) == -1) return 1; @@ -3294,22 +3360,95 @@ static int fill_dir(void *dh_, const char *name, const struct stat *statp, &stbuf, off); if (newlen > dh->needlen) return 1; + + dh->len = newlen; } else { - newlen = dh->len + - fuse_add_direntry(dh->req, NULL, 0, name, NULL, 0); - if (extend_contents(dh, newlen) == -1) + if (!dh->filled) { + dh->error = -EIO; return 1; + } + if (fuse_add_direntry_to_dh(dh, name, &stbuf) == -1) + return 1; + } + return 0; +} - fuse_add_direntry(dh->req, dh->contents + dh->len, - dh->size - dh->len, name, &stbuf, newlen); +static int fill_dir_plus(void *dh_, const char *name, const struct stat *statp, + off_t off, enum fuse_fill_dir_flags flags) +{ + struct fuse_dh *dh = (struct fuse_dh *) dh_; + struct fuse_entry_param e = { + /* ino=0 tells the kernel to ignore readdirplus stat info */ + .ino = 0, + }; + struct fuse *f = dh->fuse; + int res; + + if ((flags & ~FUSE_FILL_DIR_PLUS) != 0) { + dh->error = -EIO; + return 1; } - dh->len = newlen; + + if (off && statp && (flags & FUSE_FILL_DIR_PLUS)) { + e.attr = *statp; + + res = do_lookup(f, dh->nodeid, name, &e); + if (res) { + dh->error = res; + return 1; + } + } else { + e.attr.st_ino = FUSE_UNKNOWN_INO; + if (!f->conf.use_ino && f->conf.readdir_ino) { + e.attr.st_ino = (ino_t) + lookup_nodeid(f, dh->nodeid, name); + } + } + + if (off) { + size_t newlen; + + if (dh->first) { + dh->error = -EIO; + return 1; + } + if (extend_contents(dh, dh->needlen) == -1) + return 1; + + dh->filled = 0; + newlen = dh->len + + fuse_add_direntry_plus(dh->req, dh->contents + dh->len, + dh->needlen - dh->len, name, + &e, off); + if (newlen > dh->needlen) + return 1; + dh->len = newlen; + } else { + if (!dh->filled) { + dh->error = -EIO; + return 1; + } + if (fuse_add_direntry_to_dh(dh, name, &e.attr) == -1) + return 1; + } + return 0; } +static void free_direntries(struct fuse_direntry *de) +{ + while (de) { + struct fuse_direntry *next = de->next; + free(de->name); + free(de); + de = next; + } +} + static int readdir_fill(struct fuse *f, fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_dh *dh, - struct fuse_file_info *fi) + struct fuse_file_info *fi, + enum fuse_readdir_flags flags) { char *path; int err; @@ -3320,14 +3459,21 @@ static int readdir_fill(struct fuse *f, fuse_req_t req, fuse_ino_t ino, err = get_path(f, ino, &path); if (!err) { struct fuse_intr_data d; + fuse_fill_dir_t filler = fill_dir; + if (flags & FUSE_READDIR_PLUS) + filler = fill_dir_plus; + + free_direntries(dh->first); + dh->first = NULL; + dh->last = &dh->first; dh->len = 0; dh->error = 0; dh->needlen = size; dh->filled = 1; dh->req = req; fuse_prepare_interrupt(f, req, &d); - err = fuse_fs_readdir(f->fs, path, dh, fill_dir, off, fi); + err = fuse_fs_readdir(f->fs, path, dh, filler, off, fi, flags); fuse_finish_interrupt(f, req, &d); dh->req = NULL; if (!err) @@ -3339,12 +3485,58 @@ static int readdir_fill(struct fuse *f, fuse_req_t req, fuse_ino_t ino, return err; } -static void fuse_lib_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, - off_t off, struct fuse_file_info *llfi) +static int readdir_fill_from_list(fuse_req_t req, struct fuse_dh *dh, + off_t off, enum fuse_readdir_flags flags) +{ + off_t pos; + struct fuse_direntry *de = dh->first; + + dh->len = 0; + + if (extend_contents(dh, dh->needlen) == -1) + return dh->error; + + for (pos = 0; pos < off; pos++) { + if (!de) + break; + + de = de->next; + } + while (de) { + char *p = dh->contents + dh->len; + unsigned rem = dh->needlen - dh->len; + unsigned thislen; + unsigned newlen; + pos++; + + if (flags & FUSE_READDIR_PLUS) { + struct fuse_entry_param e = { + .ino = 0, + .attr = de->stat, + }; + thislen = fuse_add_direntry_plus(req, p, rem, + de->name, &e, pos); + } else { + thislen = fuse_add_direntry(req, p, rem, + de->name, &de->stat, pos); + } + newlen = dh->len + thislen; + if (newlen > dh->needlen) + break; + dh->len = newlen; + de = de->next; + } + return 0; +} + +static void fuse_readdir_common(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t off, struct fuse_file_info *llfi, + enum fuse_readdir_flags flags) { struct fuse *f = req_fuse_prepare(req); struct fuse_file_info fi; struct fuse_dh *dh = get_dirhandle(llfi, &fi); + int err; pthread_mutex_lock(&dh->lock); /* According to SUS, directory contents need to be refreshed on @@ -3353,27 +3545,37 @@ static void fuse_lib_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, dh->filled = 0; if (!dh->filled) { - int err = readdir_fill(f, req, ino, size, off, dh, &fi); + err = readdir_fill(f, req, ino, size, off, dh, &fi, flags); if (err) { reply_err(req, err); goto out; } } if (dh->filled) { - if (off < dh->len) { - if (off + size > dh->len) - size = dh->len - off; - } else - size = 0; - } else { - size = dh->len; - off = 0; + dh->needlen = size; + err = readdir_fill_from_list(req, dh, off, flags); + if (err) { + reply_err(req, err); + goto out; + } } - fuse_reply_buf(req, dh->contents + off, size); + fuse_reply_buf(req, dh->contents, dh->len); out: pthread_mutex_unlock(&dh->lock); } +static void fuse_lib_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t off, struct fuse_file_info *llfi) +{ + fuse_readdir_common(req, ino, size, off, llfi, 0); +} + +static void fuse_lib_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t off, struct fuse_file_info *llfi) +{ + fuse_readdir_common(req, ino, size, off, llfi, FUSE_READDIR_PLUS); +} + static void fuse_lib_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *llfi) { @@ -3393,6 +3595,7 @@ static void fuse_lib_releasedir(fuse_req_t req, fuse_ino_t ino, pthread_mutex_lock(&dh->lock); pthread_mutex_unlock(&dh->lock); pthread_mutex_destroy(&dh->lock); + free_direntries(dh->first); free(dh->contents); free(dh); reply_err(req, 0); @@ -4023,6 +4226,7 @@ static struct fuse_lowlevel_ops fuse_path_ops = { .fsync = fuse_lib_fsync, .opendir = fuse_lib_opendir, .readdir = fuse_lib_readdir, + .readdirplus = fuse_lib_readdirplus, .releasedir = fuse_lib_releasedir, .fsyncdir = fuse_lib_fsyncdir, .statfs = fuse_lib_statfs, diff --git a/lib/modules/iconv.c b/lib/modules/iconv.c index 7438ecb..7d96c17 100644 --- a/lib/modules/iconv.c +++ b/lib/modules/iconv.c @@ -172,20 +172,22 @@ static int iconv_opendir(const char *path, struct fuse_file_info *fi) } static int iconv_dir_fill(void *buf, const char *name, - const struct stat *stbuf, off_t off) + const struct stat *stbuf, off_t off, + enum fuse_fill_dir_flags flags) { struct iconv_dh *dh = buf; char *newname; int res = 0; if (iconv_convpath(dh->ic, name, &newname, 1) == 0) { - res = dh->prev_filler(dh->prev_buf, newname, stbuf, off); + res = dh->prev_filler(dh->prev_buf, newname, stbuf, off, flags); free(newname); } return res; } static int iconv_readdir(const char *path, void *buf, fuse_fill_dir_t filler, - off_t offset, struct fuse_file_info *fi) + off_t offset, struct fuse_file_info *fi, + enum fuse_readdir_flags flags) { struct iconv *ic = iconv_get(); char *newpath; @@ -196,7 +198,7 @@ static int iconv_readdir(const char *path, void *buf, fuse_fill_dir_t filler, dh.prev_buf = buf; dh.prev_filler = filler; err = fuse_fs_readdir(ic->next, newpath, &dh, iconv_dir_fill, - offset, fi); + offset, fi, flags); free(newpath); } return err; diff --git a/lib/modules/subdir.c b/lib/modules/subdir.c index eb56d36..05eccdf 100644 --- a/lib/modules/subdir.c +++ b/lib/modules/subdir.c @@ -181,14 +181,15 @@ static int subdir_opendir(const char *path, struct fuse_file_info *fi) static int subdir_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, - struct fuse_file_info *fi) + struct fuse_file_info *fi, + enum fuse_readdir_flags flags) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_readdir(d->next, newpath, buf, filler, offset, - fi); + fi, flags); free(newpath); } return err;