aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Eric Wong <normalperson@yhbt.net>2014-03-05 14:45:44 +0100
committerGravatar Miklos Szeredi <mszeredi@suse.cz>2014-03-05 14:45:44 +0100
commit6bf2e6f07c133f7b145a4726c5d962f14c650ca7 (patch)
tree7ab3a9941edad868c961404f0bbee23967990118
parent0096c126aa4548df66c658afeb18a5a5356a2c57 (diff)
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.
-rw-r--r--ChangeLog11
-rw-r--r--configure.ac2
-rwxr-xr-xexample/fioc.c10
-rwxr-xr-xexample/fsel.c6
-rwxr-xr-xexample/fusexmp.c6
-rwxr-xr-xexample/fusexmp_fh.c25
-rwxr-xr-xexample/hello.c10
-rw-r--r--include/fuse.h37
-rw-r--r--lib/fuse.c306
-rw-r--r--lib/modules/iconv.c10
-rw-r--r--lib/modules/subdir.c5
11 files changed, 349 insertions, 79 deletions
diff --git a/ChangeLog b/ChangeLog
index 69e5fc9..9932a7c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2014-03-05 Miklos Szeredi <miklos@szeredi.hu>
+
+ * 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 <miklos@szeredi.hu>
* 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;