From 63322038855660cc106c8f87a2ae42bcac37a4af Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 10 Nov 2010 11:41:21 +0100 Subject: [PATCH] add read_buf method to high level API 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 | 4 ++ include/fuse.h | 22 ++++++++++ lib/fuse.c | 96 ++++++++++++++++++++++++++++++++---------- lib/fuse_versionscript | 1 + lib/modules/iconv.c | 8 ++-- lib/modules/subdir.c | 8 ++-- 6 files changed, 108 insertions(+), 31 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9057c9a..36bda9e 100644 --- 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 * Fix check for read-only fs in mtab update diff --git a/include/fuse.h b/include/fuse.h index c2708cf..ff8c253 100644 --- a/include/fuse.h +++ b/include/fuse.h @@ -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, diff --git a/lib/fuse.c b/lib/fuse.c index 9e5c49b..4d24aea 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -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, diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript index 7c754e9..4694575 100644 --- a/lib/fuse_versionscript +++ b/lib/fuse_versionscript @@ -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; diff --git a/lib/modules/iconv.c b/lib/modules/iconv.c index fa777a7..ca62c82 100644 --- a/lib/modules/iconv.c +++ b/lib/modules/iconv.c @@ -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, diff --git a/lib/modules/subdir.c b/lib/modules/subdir.c index 788765c..6d9ac89 100644 --- a/lib/modules/subdir.c +++ b/lib/modules/subdir.c @@ -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, -- 2.30.2