summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiklos Szeredi <miklos@szeredi.hu>2010-07-12 17:17:25 +0200
committerMiklos Szeredi <mszeredi@suse.cz>2010-07-12 17:17:25 +0200
commit0065e6db7c86b20d24d76dce395dcd3f65b8771d (patch)
tree56ab8d15f16a2efc925c96452141b2060d392f13
parent33b719f7d96dc8e0f9408f2725c9a81d0ae84c27 (diff)
downloadfuse-store-retrieve.tar.gz
libfuse: add retrieve requeststore-retrieve
Retrieve data stored in the kernel buffers for a given inode.
-rw-r--r--ChangeLog3
-rw-r--r--include/fuse_kernel.h21
-rw-r--r--include/fuse_lowlevel.h39
-rw-r--r--lib/fuse_i.h10
-rw-r--r--lib/fuse_lowlevel.c127
-rw-r--r--lib/fuse_versionscript1
6 files changed, 201 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog
index 899f457..837ad5b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -9,6 +9,9 @@
* libfuse: add store request. Request data to be stored in the
kernel buffers for a given inode.
+ * libfuse: add retrieve request. Retrieve data 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 25d3555..c7c99a5 100644
--- a/include/fuse_kernel.h
+++ b/include/fuse_kernel.h
@@ -66,6 +66,7 @@
*
* 7.15
* - add store notify
+ * - add retrieve notify
*/
#ifndef _LINUX_FUSE_H
@@ -285,6 +286,7 @@ enum fuse_opcode {
FUSE_DESTROY = 38,
FUSE_IOCTL = 39,
FUSE_POLL = 40,
+ FUSE_NOTIFY_REPLY = 41,
/* CUSE specific operations */
CUSE_INIT = 4096,
@@ -295,6 +297,7 @@ enum fuse_notify_code {
FUSE_NOTIFY_INVAL_INODE = 2,
FUSE_NOTIFY_INVAL_ENTRY = 3,
FUSE_NOTIFY_STORE = 4,
+ FUSE_NOTIFY_RETRIEVE = 5,
FUSE_NOTIFY_CODE_MAX,
};
@@ -610,4 +613,22 @@ struct fuse_notify_store_out {
__u32 padding;
};
+struct fuse_notify_retrieve_out {
+ __u64 notify_unique;
+ __u64 nodeid;
+ __u64 offset;
+ __u32 size;
+ __u32 padding;
+};
+
+/* Matches the size of fuse_write_in */
+struct fuse_notify_retrieve_in {
+ __u64 dummy1;
+ __u64 offset;
+ __u32 size;
+ __u32 dummy2;
+ __u64 dummy3;
+ __u64 dummy4;
+};
+
#endif /* _LINUX_FUSE_H */
diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h
index e0e0364..7bf4df4 100644
--- a/include/fuse_lowlevel.h
+++ b/include/fuse_lowlevel.h
@@ -877,6 +877,17 @@ struct fuse_lowlevel_ops {
*/
void (*poll) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi,
struct fuse_pollhandle *ph);
+
+ /**
+ * Callback function for the retrieve request
+ *
+ * @param cookie user data supplied to fuse_lowlevel_notify_retrieve()
+ * @param ino the inode number supplied to fuse_lowlevel_notify_retrieve()
+ * @param offset the offset supplied to fuse_lowlevel_notify_retrieve()
+ * @param bufv the buffer containing the returned data
+ */
+ void (*retrieve_reply) (void *cookie, fuse_ino_t ino, off_t offset,
+ struct fuse_bufvec *bufv);
};
/**
@@ -1223,6 +1234,34 @@ int fuse_lowlevel_notify_inval_entry(struct fuse_chan *ch, fuse_ino_t parent,
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);
+/**
+ * Retrieve data from the kernel buffers
+ *
+ * Retrieve data in the kernel buffers belonging to the given inode.
+ * If successful then the retrieve_reply() method will be called with
+ * the returned data.
+ *
+ * Only present pages are returned in the retrieve reply. Retrieving
+ * stops when it finds a non-present page and only data prior to that is
+ * returned.
+ *
+ * If this function returns an error, then the retrieve will not be
+ * completed and no reply will be sent.
+ *
+ * This function doesn't change the dirty state of pages in the kernel
+ * buffer. For dirty pages the write() method will be called
+ * regardless of having been retrieved previously.
+ *
+ * @param ch the channel through which to send the invalidation
+ * @param ino the inode number
+ * @param size the number of bytes to retrieve
+ * @param offset the starting offset into the file to retrieve from
+ * @param cookie user data to supply to the reply callback
+ * @return zero for success, -errno for failure
+ */
+int fuse_lowlevel_notify_retrieve(struct fuse_chan *ch, fuse_ino_t ino,
+ size_t size, off_t offset, void *cookie);
+
/* ----------------------------------------------------------- *
* Utility functions *
diff --git a/lib/fuse_i.h b/lib/fuse_i.h
index 0206336..66d6ecb 100644
--- a/lib/fuse_i.h
+++ b/lib/fuse_i.h
@@ -43,6 +43,14 @@ struct fuse_req {
struct fuse_req *prev;
};
+struct fuse_notify_req {
+ uint64_t unique;
+ void (*reply)(struct fuse_notify_req *, fuse_req_t, fuse_ino_t,
+ const void *);
+ struct fuse_notify_req *next;
+ struct fuse_notify_req *prev;
+};
+
struct fuse_ll {
int debug;
int allow_root;
@@ -63,6 +71,8 @@ struct fuse_ll {
int got_destroy;
pthread_key_t pipe_key;
int broken_splice_nonblock;
+ uint64_t notify_ctr;
+ struct fuse_notify_req notify_list;
};
struct fuse_cmd {
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index e6171cd..9a52765 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -34,6 +34,10 @@
#define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg)))
#define OFFSET_MAX 0x7fffffffffffffffLL
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
struct fuse_pollhandle {
uint64_t kh;
struct fuse_chan *ch;
@@ -1633,11 +1637,59 @@ static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
send_reply_ok(req, NULL, 0);
}
+static void list_del_nreq(struct fuse_notify_req *nreq)
+{
+ struct fuse_notify_req *prev = nreq->prev;
+ struct fuse_notify_req *next = nreq->next;
+ prev->next = next;
+ next->prev = prev;
+}
+
+static void list_add_nreq(struct fuse_notify_req *nreq,
+ struct fuse_notify_req *next)
+{
+ struct fuse_notify_req *prev = next->prev;
+ nreq->next = next;
+ nreq->prev = prev;
+ prev->next = nreq;
+ next->prev = nreq;
+}
+
+static void list_init_nreq(struct fuse_notify_req *nreq)
+{
+ nreq->next = nreq;
+ nreq->prev = nreq;
+}
+
+static void do_notify_reply(fuse_req_t req, fuse_ino_t nodeid,
+ const void *inarg)
+{
+ struct fuse_ll *f = req->f;
+ struct fuse_notify_req *nreq;
+ struct fuse_notify_req *head;
+
+ pthread_mutex_lock(&f->lock);
+ head = &f->notify_list;
+ for (nreq = head->next; nreq != head; nreq = nreq->next) {
+ if (nreq->unique == req->unique) {
+ list_del_nreq(nreq);
+ break;
+ }
+ }
+ pthread_mutex_unlock(&f->lock);
+
+ if (nreq != head)
+ nreq->reply(nreq, req, nodeid, inarg);
+}
+
static int send_notify_iov(struct fuse_ll *f, struct fuse_chan *ch,
int notify_code, struct iovec *iov, int count)
{
struct fuse_out_header out;
+ if (!f->got_init)
+ return -ENOTCONN;
+
out.unique = 0;
out.error = notify_code;
iov[0].iov_base = &out;
@@ -1750,6 +1802,78 @@ int fuse_lowlevel_notify_store(struct fuse_chan *ch, fuse_ino_t ino,
return res;
}
+struct fuse_retrieve_req {
+ struct fuse_notify_req nreq;
+ void *cookie;
+};
+
+static void fuse_ll_retrieve_reply(struct fuse_notify_req *nreq,
+ fuse_req_t req, fuse_ino_t ino,
+ const void *inarg)
+{
+ struct fuse_retrieve_req *rreq = container_of(nreq, struct fuse_retrieve_req, nreq);
+ struct fuse_notify_retrieve_in *arg = (struct fuse_notify_retrieve_in *) inarg;
+ struct fuse_buf buf = {
+ .size = arg->size,
+ .mem = PARAM(arg),
+ };
+ struct fuse_bufvec bufv = {
+ .buf = &buf,
+ .count = 1,
+ };
+
+ if (req->f->op.retrieve_reply)
+ req->f->op.retrieve_reply(rreq->cookie, ino, arg->offset, &bufv);
+ fuse_reply_none(req);
+ free(rreq);
+}
+
+int fuse_lowlevel_notify_retrieve(struct fuse_chan *ch, fuse_ino_t ino,
+ size_t size, off_t offset, void *cookie)
+{
+ struct fuse_notify_retrieve_out outarg;
+ struct fuse_ll *f;
+ struct iovec iov[2];
+ struct fuse_retrieve_req *rreq;
+ int err;
+
+ if (!ch)
+ return -EINVAL;
+
+ f = (struct fuse_ll *)fuse_session_data(fuse_chan_session(ch));
+ if (!f)
+ return -ENODEV;
+
+ rreq = malloc(sizeof(*rreq));
+ if (rreq == NULL)
+ return -ENOMEM;
+
+ pthread_mutex_lock(&f->lock);
+ rreq->cookie = cookie;
+ rreq->nreq.unique = f->notify_ctr++;
+ rreq->nreq.reply = fuse_ll_retrieve_reply;
+ list_add_nreq(&rreq->nreq, &f->notify_list);
+ pthread_mutex_unlock(&f->lock);
+
+ outarg.notify_unique = rreq->nreq.unique;
+ outarg.nodeid = ino;
+ outarg.offset = offset;
+ outarg.size = size;
+
+ iov[1].iov_base = &outarg;
+ iov[1].iov_len = sizeof(outarg);
+
+ err = send_notify_iov(f, ch, FUSE_NOTIFY_RETRIEVE, iov, 2);
+ if (err) {
+ pthread_mutex_lock(&f->lock);
+ list_del_nreq(&rreq->nreq);
+ pthread_mutex_unlock(&f->lock);
+ free(rreq);
+ }
+
+ return err;
+}
+
void *fuse_req_userdata(fuse_req_t req)
{
return req->f->userdata;
@@ -1839,6 +1963,7 @@ static struct {
[FUSE_IOCTL] = { do_ioctl, "IOCTL" },
[FUSE_POLL] = { do_poll, "POLL" },
[FUSE_DESTROY] = { do_destroy, "DESTROY" },
+ [FUSE_NOTIFY_REPLY] = { do_notify_reply, "NOTIFY_REPLY" },
[CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" },
};
@@ -2057,6 +2182,8 @@ struct fuse_session *fuse_lowlevel_new_common(struct fuse_args *args,
f->atomic_o_trunc = 0;
list_init_req(&f->list);
list_init_req(&f->interrupts);
+ list_init_nreq(&f->notify_list);
+ f->notify_ctr = 1;
fuse_mutex_init(&f->lock);
err = pthread_key_create(&f->pipe_key, fuse_ll_pipe_destructor);
diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript
index 258081a..bdc67e9 100644
--- a/lib/fuse_versionscript
+++ b/lib/fuse_versionscript
@@ -185,6 +185,7 @@ FUSE_2.9 {
global:
fuse_buf_copy;
fuse_buf_size;
+ fuse_lowlevel_notify_retrieve;
fuse_lowlevel_notify_store;
fuse_reply_data;