add read_buf method to high level API
authorMiklos Szeredi <miklos@szeredi.hu>
Wed, 10 Nov 2010 10:41:21 +0000 (11:41 +0100)
committerMiklos Szeredi <mszeredi@suse.cz>
Wed, 10 Nov 2010 10:41:21 +0000 (11:41 +0100)
Add a new read_buf() method to the highlevel API.  This allows
returning a generic buffer from the read method, which in turn allows
zero copy reads.

ChangeLog
include/fuse.h
lib/fuse.c
lib/fuse_versionscript
lib/modules/iconv.c
lib/modules/subdir.c

index 9057c9ae8ab01f670086878424e533014b09832c..36bda9e403e1d831d06428ea45a13101a4b50dc3 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -4,6 +4,10 @@
        the lowlevel write_buf() method, this allows implementing zero
        copy writes.
 
+       * Add a new read_buf() method to the highlevel API.  This allows
+       returning a generic buffer from the read method, which in turn
+       allows zero copy reads.
+
 2010-11-08  Miklos Szeredi <miklos@szeredi.hu>
 
        * Fix check for read-only fs in mtab update
index c2708cfb9fb55dba20840398190acac8fb2c8557..ff8c253e3855c507803813634d1ab3c0f8e8cc1b 100644 (file)
@@ -524,6 +524,25 @@ struct fuse_operations {
         */
        int (*write_buf) (const char *, struct fuse_bufvec *buf, off_t off,
                          struct fuse_file_info *);
+
+       /** Store data from an open file in a buffer
+        *
+        * Similar to the read() method, but data is stored and
+        * returned in a generic buffer.
+        *
+        * No actual copying of data has to take place, the source
+        * file descriptor may simply be stored in the buffer for
+        * later data transfer.
+        *
+        * The buffer must be allocated dynamically and stored at the
+        * location pointed to by bufp.  If the buffer contains memory
+        * regions, they too must be allocated using malloc().  The
+        * allocated memory will be freed by the caller.
+        *
+        * Introduced in version 2.9
+        */
+       int (*read_buf) (const char *, struct fuse_bufvec **bufp,
+                        size_t size, off_t off, struct fuse_file_info *);
 };
 
 /** Extra context that may be needed by some filesystems
@@ -735,6 +754,9 @@ int fuse_fs_open(struct fuse_fs *fs, const char *path,
                 struct fuse_file_info *fi);
 int fuse_fs_read(struct fuse_fs *fs, const char *path, char *buf, size_t size,
                 off_t off, struct fuse_file_info *fi);
+int fuse_fs_read_buf(struct fuse_fs *fs, const char *path,
+                    struct fuse_bufvec **bufp, size_t size, off_t off,
+                    struct fuse_file_info *fi);
 int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *buf,
                  size_t size, off_t off, struct fuse_file_info *fi);
 int fuse_fs_write_buf(struct fuse_fs *fs, const char *path,
index 9e5c49b3243a75dbc78d0529a1e79cd09e26a1e3..4d24aea15b1eb3b6126352527acf8b0fbe7a723e 100644 (file)
@@ -1244,35 +1244,90 @@ int fuse_fs_open(struct fuse_fs *fs, const char *path,
        }
 }
 
-int fuse_fs_read(struct fuse_fs *fs, const char *path, char *buf, size_t size,
-                off_t off, struct fuse_file_info *fi)
+static void fuse_free_buf(struct fuse_bufvec *buf)
+{
+       if (buf != NULL) {
+               size_t i;
+
+               for (i = 0; i < buf->count; i++)
+                       free(buf->buf[i].mem);
+               free(buf);
+       }
+}
+
+int fuse_fs_read_buf(struct fuse_fs *fs, const char *path,
+                    struct fuse_bufvec **bufp, size_t size, off_t off,
+                    struct fuse_file_info *fi)
 {
        fuse_get_context()->private_data = fs->user_data;
-       if (fs->op.read) {
+       if (fs->op.read || fs->op.read_buf) {
                int res;
 
                if (fs->debug)
                        fprintf(stderr,
-                               "read[%llu] %lu bytes from %llu flags: 0x%x\n",
+                               "read[%llu] %zu bytes from %llu flags: 0x%x\n",
                                (unsigned long long) fi->fh,
-                               (unsigned long) size, (unsigned long long) off,
-                               fi->flags);
+                               size, (unsigned long long) off, fi->flags);
+
+               if (fs->op.read_buf) {
+                       res = fs->op.read_buf(path, bufp, size, off, fi);
+               } else {
+                       struct fuse_bufvec *buf;
+                       void *mem;
 
-               res = fs->op.read(path, buf, size, off, fi);
+                       buf = malloc(sizeof(struct fuse_bufvec));
+                       if (buf == NULL)
+                               return -ENOMEM;
+
+                       mem = malloc(size);
+                       if (mem == NULL) {
+                               free(buf);
+                               return -ENOMEM;
+                       }
+                       *buf = FUSE_BUFVEC_INIT(size);
+                       buf->buf[0].mem = mem;
+                       *bufp = buf;
+
+                       res = fs->op.read(path, mem, size, off, fi);
+                       if (res >= 0)
+                               buf->buf[0].size = res;
+               }
 
                if (fs->debug && res >= 0)
-                       fprintf(stderr, "   read[%llu] %u bytes from %llu\n",
-                               (unsigned long long) fi->fh, res,
+                       fprintf(stderr, "   read[%llu] %zu bytes from %llu\n",
+                               (unsigned long long) fi->fh,
+                               fuse_buf_size(*bufp),
                                (unsigned long long) off);
-               if (res > (int) size)
+               if (res >= 0 && fuse_buf_size(*bufp) > (int) size)
                        fprintf(stderr, "fuse: read too many bytes\n");
 
-               return res;
+               if (res < 0)
+                       return res;
+
+               return 0;
        } else {
                return -ENOSYS;
        }
 }
 
+int fuse_fs_read(struct fuse_fs *fs, const char *path, char *mem, size_t size,
+                off_t off, struct fuse_file_info *fi)
+{
+       int res;
+       struct fuse_bufvec *buf = NULL;
+
+       res = fuse_fs_read_buf(fs, path, &buf, size, off, fi);
+       if (res == 0) {
+               struct fuse_bufvec dst = FUSE_BUFVEC_INIT(size);
+
+               dst.buf[0].mem = mem;
+               res = fuse_buf_copy(&dst, buf, 0);
+       }
+       fuse_free_buf(buf);
+
+       return res;
+}
+
 int fuse_fs_write_buf(struct fuse_fs *fs, const char *path,
                      struct fuse_bufvec *buf, off_t off,
                      struct fuse_file_info *fi)
@@ -1282,7 +1337,7 @@ int fuse_fs_write_buf(struct fuse_fs *fs, const char *path,
                int res;
                size_t size = fuse_buf_size(buf);
 
-               assert(buf->idx = 0 && buf->off == 0);
+               assert(buf->idx == 0 && buf->off == 0);
                if (fs->debug)
                        fprintf(stderr,
                                "write%s[%llu] %zu bytes to %llu flags: 0x%x\n",
@@ -1303,6 +1358,7 @@ int fuse_fs_write_buf(struct fuse_fs *fs, const char *path,
                            !(buf->buf[0].flags & FUSE_BUF_IS_FD)) {
                                flatbuf = &buf->buf[0];
                        } else {
+                               res = -ENOMEM;
                                mem = malloc(size);
                                if (mem == NULL)
                                        goto out;
@@ -2604,32 +2660,26 @@ static void fuse_lib_read(fuse_req_t req, fuse_ino_t ino, size_t size,
                          off_t off, struct fuse_file_info *fi)
 {
        struct fuse *f = req_fuse_prepare(req);
+       struct fuse_bufvec *buf = NULL;
        char *path;
-       char *buf;
        int res;
 
-       buf = (char *) malloc(size);
-       if (buf == NULL) {
-               reply_err(req, -ENOMEM);
-               return;
-       }
-
        res = get_path_nullok(f, ino, &path);
        if (res == 0) {
                struct fuse_intr_data d;
 
                fuse_prepare_interrupt(f, req, &d);
-               res = fuse_fs_read(f->fs, path, buf, size, off, fi);
+               res = fuse_fs_read_buf(f->fs, path, &buf, size, off, fi);
                fuse_finish_interrupt(f, req, &d);
                free_path(f, ino, path);
        }
 
-       if (res >= 0)
-               fuse_reply_buf(req, buf, res);
+       if (res == 0)
+               fuse_reply_data(req, buf, FUSE_BUF_SPLICE_MOVE);
        else
                reply_err(req, res);
 
-       free(buf);
+       fuse_free_buf(buf);
 }
 
 static void fuse_lib_write_buf(fuse_req_t req, fuse_ino_t ino,
index 7c754e97ca4e004aac0f778084654bd41a252682..46945759c75edc9d40f9a05ca8ef2b4f95b950d1 100644 (file)
@@ -184,6 +184,7 @@ FUSE_2.9 {
        global:
                fuse_buf_copy;
                fuse_buf_size;
+               fuse_fs_read_buf;
                fuse_fs_write_buf;
                fuse_lowlevel_notify_retrieve;
                fuse_lowlevel_notify_store;
index fa777a7cc9e170d8e3df0c24a660062f315fbd4f..ca62c82cf0951fcef2dc75ef37581fbdd5f0dcd0 100644 (file)
@@ -397,14 +397,14 @@ static int iconv_open_file(const char *path, struct fuse_file_info *fi)
        return err;
 }
 
-static int iconv_read(const char *path, char *buf, size_t size, off_t offset,
-                     struct fuse_file_info *fi)
+static int iconv_read_buf(const char *path, struct fuse_bufvec **bufp,
+                         size_t size, off_t offset, struct fuse_file_info *fi)
 {
        struct iconv *ic = iconv_get();
        char *newpath;
        int err = iconv_convpath(ic, path, &newpath, 0);
        if (!err) {
-               err = fuse_fs_read(ic->next, newpath, buf, size, offset, fi);
+               err = fuse_fs_read_buf(ic->next, newpath, bufp, size, offset, fi);
                free(newpath);
        }
        return err;
@@ -604,7 +604,7 @@ static struct fuse_operations iconv_oper = {
        .utimens        = iconv_utimens,
        .create         = iconv_create,
        .open           = iconv_open_file,
-       .read           = iconv_read,
+       .read_buf       = iconv_read_buf,
        .write_buf      = iconv_write_buf,
        .statfs         = iconv_statfs,
        .flush          = iconv_flush,
index 788765c602551a880feab133b61d5fad581b6086..6d9ac89f83118cce2deedee8fb1aa87a4191e8fd 100644 (file)
@@ -384,14 +384,14 @@ static int subdir_open(const char *path, struct fuse_file_info *fi)
        return err;
 }
 
-static int subdir_read(const char *path, char *buf, size_t size, off_t offset,
-                      struct fuse_file_info *fi)
+static int subdir_read_buf(const char *path, struct fuse_bufvec **bufp,
+                          size_t size, off_t offset, struct fuse_file_info *fi)
 {
        struct subdir *d = subdir_get();
        char *newpath;
        int err = subdir_addpath(d, path, &newpath);
        if (!err) {
-               err = fuse_fs_read(d->next, newpath, buf, size, offset, fi);
+               err = fuse_fs_read_buf(d->next, newpath, bufp, size, offset, fi);
                free(newpath);
        }
        return err;
@@ -587,7 +587,7 @@ static struct fuse_operations subdir_oper = {
        .utimens        = subdir_utimens,
        .create         = subdir_create,
        .open           = subdir_open,
-       .read           = subdir_read,
+       .read_buf       = subdir_read_buf,
        .write_buf      = subdir_write_buf,
        .statfs         = subdir_statfs,
        .flush          = subdir_flush,