aboutsummaryrefslogtreecommitdiffstats
path: root/lib/fuse_lowlevel.c
diff options
context:
space:
mode:
authorMiklos Szeredi <miklos@szeredi.hu>2010-11-08 21:13:32 +0100
committerMiklos Szeredi <mszeredi@suse.cz>2010-11-08 21:13:32 +0100
commit3f41e8f2c3d700930e1e604b2e9116f9f827283f (patch)
tree151ce225bc4a3379a09a98af78acee7388f473a4 /lib/fuse_lowlevel.c
parentd04e0ea8ff63ce39127672b4dac3b52a9cc653b2 (diff)
downloadandroid_external_fuse-3f41e8f2c3d700930e1e604b2e9116f9f827283f.tar.gz
android_external_fuse-3f41e8f2c3d700930e1e604b2e9116f9f827283f.tar.bz2
android_external_fuse-3f41e8f2c3d700930e1e604b2e9116f9f827283f.zip
libfuse: add retrieve request
Retrieve data stored in the kernel buffers for a given inode.
Diffstat (limited to 'lib/fuse_lowlevel.c')
-rw-r--r--lib/fuse_lowlevel.c155
1 files changed, 152 insertions, 3 deletions
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index 2a5e8d2..02de394 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -35,6 +35,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;
@@ -1612,7 +1616,8 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
f->conn.want |= FUSE_CAP_SPLICE_WRITE;
if (!f->no_splice_move)
f->conn.want |= FUSE_CAP_SPLICE_MOVE;
- if (f->op.write_buf && !f->no_splice_read)
+ if (!f->no_splice_read &&
+ (f->op.write_buf || f->op.retrieve_reply))
f->conn.want |= FUSE_CAP_SPLICE_READ;
}
@@ -1694,11 +1699,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, const struct fuse_buf *buf)
+{
+ 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, buf);
+}
+
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;
@@ -1811,6 +1864,95 @@ 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,
+ const struct fuse_buf *ibuf)
+{
+ struct fuse_ll *f = req->f;
+ struct fuse_retrieve_req *rreq =
+ container_of(nreq, struct fuse_retrieve_req, nreq);
+ const struct fuse_notify_retrieve_in *arg = inarg;
+ struct fuse_buf buf = *ibuf;
+ struct fuse_bufvec bufv = {
+ .buf = &buf,
+ .count = 1,
+ };
+
+ if (!(buf.flags & FUSE_BUF_IS_FD))
+ buf.mem = PARAM(arg);
+
+ buf.size -= sizeof(struct fuse_in_header) +
+ sizeof(struct fuse_notify_retrieve_in);
+
+ if (buf.size < arg->size) {
+ fprintf(stderr, "fuse: retrieve reply: buffer size too small\n");
+ fuse_reply_none(req);
+ goto out;
+ }
+ buf.size = arg->size;
+
+ if (req->f->op.retrieve_reply)
+ req->f->op.retrieve_reply(rreq->cookie, ino, arg->offset, &bufv);
+ fuse_reply_none(req);
+ free(rreq);
+
+out:
+ if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count)
+ fuse_ll_clear_pipe(f);
+}
+
+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;
@@ -1902,6 +2044,7 @@ static struct {
[FUSE_IOCTL] = { do_ioctl, "IOCTL" },
[FUSE_POLL] = { do_poll, "POLL" },
[FUSE_DESTROY] = { do_destroy, "DESTROY" },
+ [FUSE_NOTIFY_REPLY] = { (void *) 1, "NOTIFY_REPLY" },
[CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" },
};
@@ -2007,7 +2150,8 @@ static void fuse_ll_process_buf(void *data, const struct fuse_buf *buf,
in->opcode != FUSE_INIT && in->opcode != FUSE_READ &&
in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC &&
in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR &&
- in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR)
+ in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR &&
+ in->opcode != FUSE_NOTIFY_REPLY)
goto reply_err;
err = ENOSYS;
@@ -2024,7 +2168,8 @@ static void fuse_ll_process_buf(void *data, const struct fuse_buf *buf,
}
if ((buf->flags & FUSE_BUF_IS_FD) && write_header_size < buf->size &&
- (in->opcode != FUSE_WRITE || !f->op.write_buf)) {
+ (in->opcode != FUSE_WRITE || !f->op.write_buf) &&
+ in->opcode != FUSE_NOTIFY_REPLY) {
void *newmbuf;
err = ENOMEM;
@@ -2048,6 +2193,8 @@ static void fuse_ll_process_buf(void *data, const struct fuse_buf *buf,
inarg = (void *) &in[1];
if (in->opcode == FUSE_WRITE && f->op.write_buf)
do_write_buf(req, in->nodeid, inarg, buf);
+ else if (in->opcode == FUSE_NOTIFY_REPLY)
+ do_notify_reply(req, in->nodeid, inarg, buf);
else
fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg);
@@ -2310,6 +2457,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);