libfuse: implement readdirplus for high-level API
authorEric Wong <normalperson@yhbt.net>
Wed, 5 Mar 2014 13:45:44 +0000 (14:45 +0100)
committerMiklos Szeredi <mszeredi@suse.cz>
Wed, 5 Mar 2014 13:45:44 +0000 (14:45 +0100)
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.

ChangeLog
configure.ac
example/fioc.c
example/fsel.c
example/fusexmp.c
example/fusexmp_fh.c
example/hello.c
include/fuse.h
lib/fuse.c
lib/modules/iconv.c
lib/modules/subdir.c

index 69e5fc979fd9a9608379a398b0718a93dda26a87..9932a7c610cae6a03ef0d26de5598607945caab7 100644 (file)
--- 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
index 04d7a6d359a5680f28ac9bb7124cffa07ead880b..64b2af65cdf46d9fc5a30ccf130884f9c44a8a7e 100644 (file)
@@ -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])
 
index 2117ac8089668ca828d7c1eb7133b4ef80658b4e..368f807014619237efa49608058bf89af0542180 100755 (executable)
@@ -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;
 }
index 69202ee6af3e204b4384c9ec2983e0b76483a6fb..b496c9a8590740a982618b5f77235f08cc6cd854 100755 (executable)
@@ -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;
index 6f63ae9593d4a640bb5c589531b69d648aee7fc1..fe20f5749eded568a867705ce38086347a381fa7 100755 (executable)
@@ -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;
        }
 
index ba6789bc6ff32c362d9cd031f01aee64ac515395..531438f0701241eff57f74eb48f61ec179007949 100755 (executable)
@@ -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;
index d26d826cdb89050e2c99b0cf5eaa95344c48f88e..3c24c8be3310c8dee80dd85f1f8b554b7070cc91 100755 (executable)
@@ -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;
 }
index a3a047ec317ef7c0abfe5fc98cc17457849e104c..edece7786fafa7fb4f4e07f88dce7e430331f7ba 100644 (file)
@@ -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,
index 3f8e601a08f2abfd0366f63b03ca8111e6311bb7..c9c36ad02ee659870ec3a73c5a40c39f4160d32c 100644 (file)
@@ -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,
index 7438ecbcd50ac73d6f196a6dbf790dbc46493f23..7d96c1755a99b3d1211efe9e835d822c30175374 100644 (file)
@@ -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;
index eb56d3642414c242fe452a3f55915ee6571cdc9c..05eccdf1779aeea43f73637a5bc75376286bebc8 100644 (file)
@@ -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;