diff options
author | Miklos Szeredi <miklos@szeredi.hu> | 2010-11-08 21:13:32 +0100 |
---|---|---|
committer | Miklos Szeredi <mszeredi@suse.cz> | 2010-11-08 21:13:32 +0100 |
commit | 3f41e8f2c3d700930e1e604b2e9116f9f827283f (patch) | |
tree | 151ce225bc4a3379a09a98af78acee7388f473a4 /lib/fuse_lowlevel.c | |
parent | d04e0ea8ff63ce39127672b4dac3b52a9cc653b2 (diff) | |
download | android_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.c | 155 |
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); |