aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog3
-rw-r--r--include/fuse_kernel.h13
-rw-r--r--include/fuse_lowlevel.h25
-rw-r--r--lib/fuse_lowlevel.c204
-rw-r--r--lib/fuse_versionscript1
5 files changed, 162 insertions, 84 deletions
diff --git a/ChangeLog b/ChangeLog
index 58649b3..9d669de 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -61,6 +61,9 @@
* The fuse_reply_fd() interface is converted to using buffers.
+ * libfuse: add store request. Request data to be stored in the
+ kernel buffers for a given inode.
+
2010-06-23 Miklos Szeredi <miklos@szeredi.hu>
* Make the number of max background requests and congestion
diff --git a/include/fuse_kernel.h b/include/fuse_kernel.h
index 384c9f3..25d3555 100644
--- a/include/fuse_kernel.h
+++ b/include/fuse_kernel.h
@@ -63,6 +63,9 @@
*
* 7.14
* - add splice support to fuse device
+ *
+ * 7.15
+ * - add store notify
*/
#ifndef _LINUX_FUSE_H
@@ -99,7 +102,7 @@
#define FUSE_KERNEL_VERSION 7
/** Minor version number of this interface */
-#define FUSE_KERNEL_MINOR_VERSION 14
+#define FUSE_KERNEL_MINOR_VERSION 15
/** The node ID of the root inode */
#define FUSE_ROOT_ID 1
@@ -291,6 +294,7 @@ enum fuse_notify_code {
FUSE_NOTIFY_POLL = 1,
FUSE_NOTIFY_INVAL_INODE = 2,
FUSE_NOTIFY_INVAL_ENTRY = 3,
+ FUSE_NOTIFY_STORE = 4,
FUSE_NOTIFY_CODE_MAX,
};
@@ -599,4 +603,11 @@ struct fuse_notify_inval_entry_out {
__u32 padding;
};
+struct fuse_notify_store_out {
+ __u64 nodeid;
+ __u64 offset;
+ __u32 size;
+ __u32 padding;
+};
+
#endif /* _LINUX_FUSE_H */
diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h
index 9132846..0a8fdf1 100644
--- a/include/fuse_lowlevel.h
+++ b/include/fuse_lowlevel.h
@@ -1224,6 +1224,31 @@ int fuse_lowlevel_notify_inval_inode(struct fuse_chan *ch, fuse_ino_t ino,
int fuse_lowlevel_notify_inval_entry(struct fuse_chan *ch, fuse_ino_t parent,
const char *name, size_t namelen);
+/**
+ * Store data to the kernel buffers
+ *
+ * Synchronously store data in the kernel buffers belonging to the
+ * given inode. The stored data is marked up-to-date (no read will be
+ * performed against it, unless it's invalidated or evicted from the
+ * cache).
+ *
+ * If the stored data overflows the current file size, then the size
+ * is extended, similarly to a write(2) on the filesystem.
+ *
+ * If this function returns an error, then the store wasn't fully
+ * completed, but it may have been partially completed.
+ *
+ * @param ch the channel through which to send the invalidation
+ * @param ino the inode number
+ * @param offset the starting offset into the file to store to
+ * @param bufv buffer vector
+ * @param flags flags controlling the copy
+ * @return zero for success, -errno for failure
+ */
+int fuse_lowlevel_notify_store(struct fuse_chan *ch, fuse_ino_t ino,
+ off_t offset, struct fuse_bufvec *bufv,
+ enum fuse_buf_copy_flags flags);
+
/* ----------------------------------------------------------- *
* Utility functions *
* ----------------------------------------------------------- */
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index 9d3fa98..c84a9d3 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -133,6 +133,31 @@ void fuse_free_req(fuse_req_t req)
destroy_req(req);
}
+static int fuse_send_msg(struct fuse_ll *f, struct fuse_chan *ch,
+ struct iovec *iov, int count)
+{
+ struct fuse_out_header *out = iov[0].iov_base;
+
+ out->len = iov_length(iov, count);
+ if (f->debug) {
+ if (out->unique == 0) {
+ fprintf(stderr, "NOTIFY: code=%d length=%u\n",
+ out->error, out->len);
+ } else if (out->error) {
+ fprintf(stderr,
+ " unique: %llu, error: %i (%s), outsize: %i\n",
+ (unsigned long long) out->unique, out->error,
+ strerror(-out->error), out->len);
+ } else {
+ fprintf(stderr,
+ " unique: %llu, success, outsize: %i\n",
+ (unsigned long long) out->unique, out->len);
+ }
+ }
+
+ return fuse_chan_send(ch, iov, count);
+}
+
int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
int count)
{
@@ -145,24 +170,11 @@ int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
out.unique = req->unique;
out.error = error;
+
iov[0].iov_base = &out;
iov[0].iov_len = sizeof(struct fuse_out_header);
- out.len = iov_length(iov, count);
-
- if (req->f->debug) {
- if (out.error) {
- fprintf(stderr,
- " unique: %llu, error: %i (%s), outsize: %i\n",
- (unsigned long long) out.unique, out.error,
- strerror(-out.error), out.len);
- } else {
- fprintf(stderr,
- " unique: %llu, success, outsize: %i\n",
- (unsigned long long) out.unique, out.len);
- }
- }
- return fuse_chan_send(req->ch, iov, count);
+ return fuse_send_msg(req->f, req->ch, iov, count);
}
static int send_reply_iov(fuse_req_t req, int error, struct iovec *iov,
@@ -451,27 +463,6 @@ static void fuse_ll_clear_pipe(struct fuse_ll *f)
}
}
-static int send_reply_iov_buf(fuse_req_t req, const struct iovec *iov,
- int count, const char *buf, size_t len)
-{
- int res;
- struct iovec *new_iov;
-
- new_iov = malloc((count + 1) * sizeof(struct iovec));
- if (new_iov == NULL)
- return fuse_reply_err(req, ENOMEM);
-
- memcpy(new_iov, iov, count * sizeof(struct iovec));
- new_iov[count].iov_base = (void *) buf;
- new_iov[count].iov_len = len;
- count++;
-
- res = send_reply_iov(req, 0, new_iov, count);
- free(new_iov);
-
- return res;
-}
-
static int read_back(int fd, char *buf, size_t len)
{
int res;
@@ -488,12 +479,13 @@ static int read_back(int fd, char *buf, size_t len)
return 0;
}
-static int fuse_reply_data_iov(fuse_req_t req, struct iovec *iov, int iov_count,
+static int fuse_send_data_iov(struct fuse_ll *f, struct fuse_chan *ch,
+ struct iovec *iov, int iov_count,
struct fuse_bufvec *buf, unsigned int flags)
{
int res;
size_t len = fuse_buf_size(buf);
- struct fuse_out_header out;
+ struct fuse_out_header *out = iov[0].iov_base;
struct fuse_ll_pipe *llp;
int splice_flags;
size_t pipesize;
@@ -508,7 +500,10 @@ static int fuse_reply_data_iov(fuse_req_t req, struct iovec *iov, int iov_count,
.count = 1,
};
- if (req->f->broken_splice_nonblock)
+ if (f->broken_splice_nonblock)
+ goto fallback;
+
+ if (flags & FUSE_BUF_NO_SPLICE)
goto fallback;
total_fd_size = 0;
@@ -522,28 +517,24 @@ static int fuse_reply_data_iov(fuse_req_t req, struct iovec *iov, int iov_count,
if (total_fd_size < 2 * pagesize)
goto fallback;
- if (req->f->conn.proto_minor < 14 ||
- !(req->f->conn.want & FUSE_CAP_SPLICE_WRITE))
+ if (f->conn.proto_minor < 14 ||
+ !(f->conn.want & FUSE_CAP_SPLICE_WRITE))
goto fallback;
- llp = fuse_ll_get_pipe(req->f);
+ llp = fuse_ll_get_pipe(f);
if (llp == NULL)
goto fallback;
- iov[0].iov_base = &out;
- iov[0].iov_len = sizeof(struct fuse_out_header);
headerlen = iov_length(iov, iov_count);
- out.unique = req->unique;
- out.error = 0;
- out.len = headerlen + len;
+ out->len = headerlen + len;
/*
* Heuristic for the required pipe size, does not work if the
* source contains less than page size fragments
*/
- pipesize = pagesize * (iov_count + buf->count + 1) + out.len;
+ pipesize = pagesize * (iov_count + buf->count + 1) + out->len;
if (llp->size < pipesize) {
if (llp->can_grow) {
@@ -560,15 +551,13 @@ static int fuse_reply_data_iov(fuse_req_t req, struct iovec *iov, int iov_count,
res = vmsplice(llp->pipe[1], iov, iov_count, SPLICE_F_NONBLOCK);
- if (res == -1) {
- res = -errno;
- perror("fuse: vmsplice to pipe");
- return res;
- }
- if (res != sizeof(struct fuse_out_header)) {
+ if (res == -1)
+ goto fallback;
+
+ if (res != headerlen) {
res = -EIO;
fprintf(stderr, "fuse: short vmsplice to pipe: %u/%zu\n", res,
- sizeof(struct fuse_out_header));
+ headerlen);
goto clear_pipe;
}
@@ -590,13 +579,13 @@ static int fuse_reply_data_iov(fuse_req_t req, struct iovec *iov, int iov_count,
* this combination of input and output.
*/
if (res == -EAGAIN)
- req->f->broken_splice_nonblock = 1;
+ f->broken_splice_nonblock = 1;
- pthread_setspecific(req->f->pipe_key, NULL);
+ pthread_setspecific(f->pipe_key, NULL);
fuse_ll_pipe_free(llp);
goto fallback;
}
- res = fuse_reply_err(req, errno);
+ res = -res;
goto clear_pipe;
}
@@ -619,10 +608,8 @@ static int fuse_reply_data_iov(fuse_req_t req, struct iovec *iov, int iov_count,
*/
res = posix_memalign(&mbuf.mem, pagesize, len);
- if (res != 0) {
- res = fuse_reply_err(req, res);
+ if (res != 0)
goto clear_pipe;
- }
mem_buf.off = now_len;
res = fuse_buf_copy(&mem_buf, buf, 0);
@@ -637,7 +624,7 @@ static int fuse_reply_data_iov(fuse_req_t req, struct iovec *iov, int iov_count,
tmpbuf = malloc(headerlen);
if (tmpbuf == NULL) {
free(mbuf.mem);
- res = fuse_reply_err(req, ENOMEM);
+ res = ENOMEM;
goto clear_pipe;
}
res = read_back(llp->pipe[0], tmpbuf, headerlen);
@@ -652,8 +639,10 @@ static int fuse_reply_data_iov(fuse_req_t req, struct iovec *iov, int iov_count,
goto clear_pipe;
}
len = now_len + extra_len;
- res = send_reply_iov_buf(req, iov, iov_count,
- mbuf.mem, len);
+ iov[iov_count].iov_base = mbuf.mem;
+ iov[iov_count].iov_len = len;
+ iov_count++;
+ res = fuse_send_msg(f, ch, iov, iov_count);
free(mbuf.mem);
return res;
}
@@ -661,36 +650,36 @@ static int fuse_reply_data_iov(fuse_req_t req, struct iovec *iov, int iov_count,
res = now_len;
}
len = res;
- out.len = len + sizeof(struct fuse_out_header);
+ out->len = headerlen + len;
- if (req->f->debug) {
+ if (f->debug) {
fprintf(stderr,
" unique: %llu, success, outsize: %i (splice)\n",
- (unsigned long long) out.unique, out.len);
+ (unsigned long long) out->unique, out->len);
}
splice_flags = 0;
if ((flags & FUSE_BUF_SPLICE_MOVE) &&
- (req->f->conn.want & FUSE_CAP_SPLICE_MOVE))
+ (f->conn.want & FUSE_CAP_SPLICE_MOVE))
splice_flags |= SPLICE_F_MOVE;
res = splice(llp->pipe[0], NULL,
- fuse_chan_fd(req->ch), NULL, out.len, splice_flags);
+ fuse_chan_fd(ch), NULL, out->len, splice_flags);
if (res == -1) {
res = -errno;
perror("fuse: splice from pipe");
goto clear_pipe;
}
- if (res != out.len) {
+ if (res != out->len) {
res = -EIO;
fprintf(stderr, "fuse: short splice from pipe: %u/%u\n",
- res, out.len);
+ res, out->len);
goto clear_pipe;
}
return 0;
clear_pipe:
- fuse_ll_clear_pipe(req->f);
+ fuse_ll_clear_pipe(f);
return res;
fallback:
@@ -705,15 +694,19 @@ fallback:
res = posix_memalign(&mbuf.mem, pagesize, len);
if (res != 0)
- return fuse_reply_err(req, res);
+ return res;
res = fuse_buf_copy(&mem_buf, buf, 0);
if (res < 0) {
free(mbuf.mem);
- return fuse_reply_err(req, -res);
+ return -res;
}
len = res;
- res = send_reply_iov_buf(req, iov, iov_count, mbuf.mem, len);
+
+ iov[iov_count].iov_base = mbuf.mem;
+ iov[iov_count].iov_len = len;
+ iov_count++;
+ res = fuse_send_msg(f, ch, iov, iov_count);
free(mbuf.mem);
return res;
@@ -723,8 +716,21 @@ fallback:
int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv,
enum fuse_buf_copy_flags flags)
{
- struct iovec iov[1];
- return fuse_reply_data_iov(req, iov, 1, bufv, flags);
+ struct iovec iov[2];
+ struct fuse_out_header out;
+ int res;
+
+ iov[0].iov_base = &out;
+ iov[0].iov_len = sizeof(struct fuse_out_header);
+
+ out.unique = req->unique;
+ out.error = 0;
+
+ res = fuse_send_data_iov(req->f, req->ch, iov, 1, bufv, flags);
+ if (res <= 0)
+ return res;
+ else
+ return fuse_reply_err(req, res);
}
int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
@@ -1695,13 +1701,8 @@ static int send_notify_iov(struct fuse_ll *f, struct fuse_chan *ch,
out.error = notify_code;
iov[0].iov_base = &out;
iov[0].iov_len = sizeof(struct fuse_out_header);
- out.len = iov_length(iov, count);
-
- if (f->debug)
- fprintf(stderr, "NOTIFY: code=%d count=%d length=%u\n",
- notify_code, count, out.len);
- return fuse_chan_send(ch, iov, count);
+ return fuse_send_msg(f, ch, iov, count);
}
int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph)
@@ -1771,6 +1772,43 @@ int fuse_lowlevel_notify_inval_entry(struct fuse_chan *ch, fuse_ino_t parent,
return send_notify_iov(f, ch, FUSE_NOTIFY_INVAL_ENTRY, iov, 3);
}
+int fuse_lowlevel_notify_store(struct fuse_chan *ch, fuse_ino_t ino,
+ off_t offset, struct fuse_bufvec *bufv,
+ enum fuse_buf_copy_flags flags)
+{
+ struct fuse_out_header out;
+ struct fuse_notify_store_out outarg;
+ struct fuse_ll *f;
+ struct iovec iov[3];
+ size_t size = fuse_buf_size(bufv);
+ int res;
+
+ if (!ch)
+ return -EINVAL;
+
+ f = (struct fuse_ll *)fuse_session_data(fuse_chan_session(ch));
+ if (!f)
+ return -ENODEV;
+
+ out.unique = 0;
+ out.error = FUSE_NOTIFY_STORE;
+
+ outarg.nodeid = ino;
+ outarg.offset = offset;
+ outarg.size = size;
+
+ iov[0].iov_base = &out;
+ iov[0].iov_len = sizeof(out);
+ iov[1].iov_base = &outarg;
+ iov[1].iov_len = sizeof(outarg);
+
+ res = fuse_send_data_iov(f, ch, iov, 2, bufv, flags);
+ if (res > 0)
+ res = -res;
+
+ return res;
+}
+
void *fuse_req_userdata(fuse_req_t req)
{
return req->f->userdata;
diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript
index 2f531e1..f271861 100644
--- a/lib/fuse_versionscript
+++ b/lib/fuse_versionscript
@@ -184,6 +184,7 @@ FUSE_2.9 {
global:
fuse_buf_copy;
fuse_buf_size;
+ fuse_lowlevel_notify_store;
fuse_reply_data;
fuse_session_process_buf;
fuse_session_receive_buf;