libfuse: add readdirplus support in fuse_lowlevel_ops
authorFeng Shuo <steve.shuo.feng@gmail.com>
Fri, 4 Jan 2013 08:23:31 +0000 (16:23 +0800)
committerMiklos Szeredi <mszeredi@suse.cz>
Thu, 7 Feb 2013 13:58:50 +0000 (14:58 +0100)
This patch implements readdirplus support in FUSE usersapce. It adds
a new fuse lowlevel operations fuse_lowleve_ops::readdir_plus,
corespoding mount options and helper functions to maintain buffer.

[From: Eric Wong <normalperson@yhbt.net>]

This makes our terminology consistent with NFS and
our kernel module, as well as reducing user/developer
confusion in the command-line.

Note: I'm keeping "fuse_add_direntry_plus" since that is
less standardized in its use than "readdirplus" for now.

Signed-off-by: Feng Shuo <steve.shuo.feng@gmail.com>
ChangeLog
include/fuse_common.h
include/fuse_kernel.h
include/fuse_lowlevel.h
lib/fuse_i.h
lib/fuse_lowlevel.c
lib/fuse_versionscript
test/stracedecode.c

index 7c9a2e200abfefbb5c177bf9d27ccc3029a75e07..d3e629c9c7c61379e4c0c9121f470a8819aa4f21 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2013-02-07  Miklos Szeredi <miklos@szeredi.hu>
+
+       * libfuse: add readdirplus support in fuse_lowlevel_ops.  Patch by
+       Feng Shuo
+
 2013-02-06  Miklos Szeredi <miklos@szeredi.hu>
 
        * libfuse: set close-on-exec flag on pipe file descriptors.  Patch
index 58458ab610882e9861f6eeeff69ab1d9a10a2140..c8a2409964965eaa2a426246e8a21c039c6f7128 100644 (file)
@@ -112,6 +112,7 @@ struct fuse_file_info {
 #define FUSE_CAP_FLOCK_LOCKS           (1 << 10)
 #define FUSE_CAP_IOCTL_DIR             (1 << 11)
 #define FUSE_CAP_AUTO_INVAL_DATA       (1 << 12)
+#define FUSE_CAP_READDIRPLUS           (1 << 13)
 
 /**
  * Ioctl flags
index df8e9b9c9ef213a7690d69fb2ad05f188ce6fcfe..28fefbbf9fa3c852152824731cdc5cc442d172c3 100644 (file)
@@ -86,6 +86,9 @@
  *
  * 7.20
  *  - add FUSE_AUTO_INVAL_DATA
+ *
+ * 7.21
+ *  - add FUSE_READDIRPLUS
  */
 
 #ifndef _LINUX_FUSE_H
 #define FUSE_KERNEL_VERSION 7
 
 /** Minor version number of this interface */
-#define FUSE_KERNEL_MINOR_VERSION 20
+#define FUSE_KERNEL_MINOR_VERSION 21
 
 /** The node ID of the root inode */
 #define FUSE_ROOT_ID 1
@@ -228,6 +231,7 @@ struct fuse_file_lock {
 #define FUSE_FLOCK_LOCKS       (1 << 10)
 #define FUSE_HAS_IOCTL_DIR     (1 << 11)
 #define FUSE_AUTO_INVAL_DATA   (1 << 12)
+#define FUSE_DO_READDIRPLUS    (1 << 13)
 
 /**
  * CUSE INIT request/reply flags
@@ -334,6 +338,7 @@ enum fuse_opcode {
        FUSE_NOTIFY_REPLY  = 41,
        FUSE_BATCH_FORGET  = 42,
        FUSE_FALLOCATE     = 43,
+       FUSE_READDIRPLUS   = 44,
 
        /* CUSE specific operations */
        CUSE_INIT          = 4096,
@@ -665,6 +670,16 @@ struct fuse_dirent {
 #define FUSE_DIRENT_SIZE(d) \
        FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
 
+struct fuse_direntplus {
+       struct fuse_entry_out entry_out;
+       struct fuse_dirent dirent;
+};
+
+#define FUSE_NAME_OFFSET_DIRENTPLUS \
+       offsetof(struct fuse_direntplus, dirent.name)
+#define FUSE_DIRENTPLUS_SIZE(d) \
+       FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET_DIRENTPLUS + (d)->dirent.namelen)
+
 struct fuse_notify_inval_inode_out {
        __u64   ino;
        __s64   off;
index 9a24bcc273afad298c1f5cf28928bbca217f718d..22c2e5a18356704f7f74083e00ae3dad69589f7b 100644 (file)
@@ -1016,6 +1016,32 @@ struct fuse_lowlevel_ops {
         */
        void (*fallocate) (fuse_req_t req, fuse_ino_t ino, int mode,
                       off_t offset, off_t length, struct fuse_file_info *fi);
+
+       /**
+        * Read directory with attributes
+        *
+        * Send a buffer filled using fuse_add_direntry_plus(), with size not
+        * exceeding the requested size.  Send an empty buffer on end of
+        * stream.
+        *
+        * fi->fh will contain the value set by the opendir method, or
+        * will be undefined if the opendir method didn't set any value.
+        *
+        * Introduced in version 3.0
+        *
+        * Valid replies:
+        *   fuse_reply_buf
+        *   fuse_reply_data
+        *   fuse_reply_err
+        *
+        * @param req request handle
+        * @param ino the inode number
+        * @param size maximum number of bytes to send
+        * @param off offset to continue reading the directory stream
+        * @param fi file information
+        */
+       void (*readdirplus) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
+                        struct fuse_file_info *fi);
 };
 
 /**
@@ -1251,6 +1277,34 @@ size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
                         const char *name, const struct stat *stbuf,
                         off_t off);
 
+/**
+ * Add a directory entry to the buffer with the attributes
+ *
+ * Buffer needs to be large enough to hold the entry.  If it's not,
+ * then the entry is not filled in but the size of the entry is still
+ * returned.  The caller can check this by comparing the bufsize
+ * parameter with the returned entry size.  If the entry size is
+ * larger than the buffer size, the operation failed.
+ *
+ * From the 'stbuf' argument the st_ino field and bits 12-15 of the
+ * st_mode field are used.  The other fields are ignored.
+ *
+ * Note: offsets do not necessarily represent physical offsets, and
+ * could be any marker, that enables the implementation to find a
+ * specific point in the directory stream.
+ *
+ * @param req request handle
+ * @param buf the point where the new entry will be added to the buffer
+ * @param bufsize remaining size of the buffer
+ * @param name the name of the entry
+ * @param e the directory entry
+ * @param off the offset of the next entry
+ * @return the space needed for the entry
+ */
+size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize,
+                             const char *name,
+                             const struct fuse_entry_param *e, off_t off);
+
 /**
  * Reply to ask for data fetch and output buffer preparation.  ioctl
  * will be retried with the specified input data fetched and output
index 225ff7de606f1e6ff557fd8ae9a019614f042e95..02e7af1d36f8911ea8fe32e6b4579a35ea428232 100644 (file)
@@ -73,6 +73,7 @@ struct fuse_ll {
        int no_splice_read;
        int auto_inval_data;
        int no_auto_inval_data;
+       int no_readdirplus;
        struct fuse_lowlevel_ops op;
        int got_init;
        struct cuse_data *cuse_data;
index 568cf472f034f33810ae7a6a3bbc3a42afcd3457..feaa076b415101b20de7da57e115e41b1d256cf7 100644 (file)
@@ -339,6 +339,25 @@ static void fill_entry(struct fuse_entry_out *arg,
        convert_stat(&e->attr, &arg->attr);
 }
 
+size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize,
+                             const char *name,
+                             const struct fuse_entry_param *e, off_t off)
+{
+       struct fuse_entry_out *argp;
+       size_t entsize;
+
+       (void) req;
+       entsize = FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET_DIRENTPLUS +
+                                   fuse_dirent_size(strlen(name)));
+       if (entsize <= bufsize && buf){
+               argp = (struct fuse_entry_out *)buf;
+               memset(argp, 0, sizeof(*argp));
+               fill_entry(argp, e);
+               fuse_add_dirent(buf + sizeof(*argp), name, &(e->attr), off);
+       }
+       return entsize;
+}
+
 static void fill_open(struct fuse_open_out *arg,
                      const struct fuse_file_info *f)
 {
@@ -1406,6 +1425,21 @@ static void do_readdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
                fuse_reply_err(req, ENOSYS);
 }
 
+static void do_readdirplus(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+       struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
+       struct fuse_file_info fi;
+
+       memset(&fi, 0, sizeof(fi));
+       fi.fh = arg->fh;
+       fi.fh_old = fi.fh;
+
+       if (req->f->op.readdirplus)
+               req->f->op.readdirplus(req, nodeid, arg->size, arg->offset, &fi);
+       else
+               fuse_reply_err(req, ENOSYS);
+}
+
 static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 {
        struct fuse_release_in *arg = (struct fuse_release_in *) inarg;
@@ -1806,6 +1840,8 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
                        f->conn.capable |= FUSE_CAP_FLOCK_LOCKS;
                if (arg->flags & FUSE_AUTO_INVAL_DATA)
                        f->conn.capable |= FUSE_CAP_AUTO_INVAL_DATA;
+               if (arg->flags & FUSE_DO_READDIRPLUS)
+                       f->conn.capable |= FUSE_CAP_READDIRPLUS;
        } else {
                f->conn.async_read = 0;
                f->conn.max_readahead = 0;
@@ -1838,6 +1874,8 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
                f->conn.want |= FUSE_CAP_BIG_WRITES;
        if (f->auto_inval_data)
                f->conn.want |= FUSE_CAP_AUTO_INVAL_DATA;
+       if (f->op.readdirplus && !f->no_readdirplus)
+               f->conn.want |= FUSE_CAP_READDIRPLUS;
 
        if (bufsize < FUSE_MIN_READ_BUFFER) {
                fprintf(stderr, "fuse: warning: buffer size too small: %zu\n",
@@ -1861,6 +1899,8 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
                f->conn.want &= ~FUSE_CAP_SPLICE_MOVE;
        if (f->no_auto_inval_data)
                f->conn.want &= ~FUSE_CAP_AUTO_INVAL_DATA;
+       if (f->no_readdirplus)
+               f->conn.want &= ~FUSE_CAP_READDIRPLUS;
 
        if (f->conn.async_read || (f->conn.want & FUSE_CAP_ASYNC_READ))
                outarg.flags |= FUSE_ASYNC_READ;
@@ -1878,6 +1918,8 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
                outarg.flags |= FUSE_FLOCK_LOCKS;
        if (f->conn.want & FUSE_CAP_AUTO_INVAL_DATA)
                outarg.flags |= FUSE_AUTO_INVAL_DATA;
+       if (f->conn.want & FUSE_CAP_READDIRPLUS)
+               outarg.flags |= FUSE_DO_READDIRPLUS;
        outarg.max_readahead = f->conn.max_readahead;
        outarg.max_write = f->conn.max_write;
        if (f->conn.proto_minor >= 13) {
@@ -2294,6 +2336,7 @@ static struct {
        [FUSE_DESTROY]     = { do_destroy,     "DESTROY"     },
        [FUSE_NOTIFY_REPLY] = { (void *) 1,    "NOTIFY_REPLY" },
        [FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" },
+       [FUSE_READDIRPLUS] = { do_readdirplus,  "READDIRPLUS"},
        [CUSE_INIT]        = { cuse_lowlevel_init, "CUSE_INIT"   },
 };
 
@@ -2402,7 +2445,8 @@ static void fuse_ll_process_buf(void *data, const struct fuse_buf *buf,
                 in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC &&
                 in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR &&
                 in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR &&
-                in->opcode != FUSE_NOTIFY_REPLY)
+                in->opcode != FUSE_NOTIFY_REPLY &&
+                in->opcode != FUSE_READDIRPLUS)
                goto reply_err;
 
        err = ENOSYS;
@@ -2501,6 +2545,7 @@ static const struct fuse_opt fuse_ll_opts[] = {
        { "no_splice_read", offsetof(struct fuse_ll, no_splice_read), 1},
        { "auto_inval_data", offsetof(struct fuse_ll, auto_inval_data), 1},
        { "no_auto_inval_data", offsetof(struct fuse_ll, no_auto_inval_data), 1},
+       { "no_readdirplus", offsetof(struct fuse_ll, no_readdirplus), 1},
        FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_DISCARD),
        FUSE_OPT_KEY("-h", KEY_HELP),
        FUSE_OPT_KEY("--help", KEY_HELP),
@@ -2533,6 +2578,7 @@ static void fuse_ll_help(void)
 "    -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 [no_]readdirplus   use readdirplus if possible.\n"
 );
 }
 
index ea2a1c87c8a8106c528bbbcda937441c55f7e0cb..ad5a9409c4348a520d6f74208f8a3545c8f73820 100644 (file)
@@ -51,6 +51,7 @@ FUSE_3.0 {
                fuse_reply_statfs;
                fuse_set_signal_handlers;
                fuse_add_direntry;
+               fuse_add_direntry_plus;
                fuse_chan_new;
                fuse_chan_recv;
                fuse_daemonize;
index 27b883c9c72c72196baf569427bce9606697a7f6..940438ac32ed8833c97e2c0a7538a40cdc2f6e9d 100644 (file)
@@ -41,6 +41,7 @@ static struct {
        [FUSE_INTERRUPT]   = { "INTERRUPT"   },
        [FUSE_BMAP]        = { "BMAP"        },
        [FUSE_DESTROY]     = { "DESTROY"     },
+       [FUSE_READDIRPLUS] = { "READDIRPLUS" },
 };
 
 #define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))