aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Miklos Szeredi <miklos@szeredi.hu>2010-11-10 11:41:21 +0100
committerGravatar Miklos Szeredi <mszeredi@suse.cz>2010-11-10 11:41:21 +0100
commit63322038855660cc106c8f87a2ae42bcac37a4af (patch)
tree36445b71a1cc00b5f30e3f1da4b08d7fea644caa
parentdf31f2b11efd0d024f12c7035ab0d3646ad9c7c6 (diff)
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.
-rw-r--r--ChangeLog4
-rw-r--r--include/fuse.h22
-rw-r--r--lib/fuse.c96
-rw-r--r--lib/fuse_versionscript1
-rw-r--r--lib/modules/iconv.c8
-rw-r--r--lib/modules/subdir.c8
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 <miklos@szeredi.hu>
* 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,