From 5d9ce36da4688ba2c14f658ed40b5b4ad971879b Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Mar 2006 12:10:13 +0000 Subject: [PATCH] fix --- ChangeLog | 8 ++++++++ include/fuse_lowlevel.h | 6 ++++-- include/fuse_lowlevel_compat.h | 2 ++ kernel/dev.c | 25 ++++++++++++++++++++++++- kernel/fuse_i.h | 3 +++ kernel/inode.c | 3 +++ lib/fuse.c | 4 ++-- lib/fuse_kern_chan.c | 29 ++++++++++++++++++----------- lib/fuse_loop.c | 10 +++++----- lib/fuse_loop_mt.c | 12 +++++++----- lib/fuse_session.c | 9 ++++++++- lib/fuse_versionscript | 1 + 12 files changed, 85 insertions(+), 27 deletions(-) diff --git a/ChangeLog b/ChangeLog index c08d4c9..155cb0d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2006-03-01 Miklos Szeredi + + * Add O_ASYNC and O_NONBLOCK support to FUSE device. Patch by + Jeff Dike + + * Renamed fuse_chan_receive() to fuse_chan_recv() and changed + interface to return -errno in case of error. + 2006-03-01 Csaba Henk * libfuse: pass device file descriptor to fuse_unmount(), rewrite diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h index bdcbfed..f042dfb 100644 --- a/include/fuse_lowlevel.h +++ b/include/fuse_lowlevel.h @@ -1177,12 +1177,14 @@ struct fuse_session *fuse_chan_session(struct fuse_chan *ch); /** * Receive a raw request * + * A return value of -ENODEV means, that the filesystem was unmounted + * * @param ch the channel * @param buf the buffer to store the request in * @param size the size of the buffer - * @return the actual size of the raw request, or -1 on error + * @return the actual size of the raw request, or -errno on error */ -int fuse_chan_receive(struct fuse_chan *ch, char *buf, size_t size); +int fuse_chan_recv(struct fuse_chan *ch, char *buf, size_t size); /** * Send a raw reply diff --git a/include/fuse_lowlevel_compat.h b/include/fuse_lowlevel_compat.h index f6e116c..be4d4d8 100644 --- a/include/fuse_lowlevel_compat.h +++ b/include/fuse_lowlevel_compat.h @@ -88,4 +88,6 @@ struct fuse_session *fuse_lowlevel_new_compat(const char *opts, const struct fuse_lowlevel_ops *op, size_t op_size, void *userdata); +int fuse_chan_receive(struct fuse_chan *ch, char *buf, size_t size); + #endif /* __FreeBSD__ */ diff --git a/kernel/dev.c b/kernel/dev.c index d7c45d1..d9ffd43 100644 --- a/kernel/dev.c +++ b/kernel/dev.c @@ -357,6 +357,7 @@ static void queue_request(struct fuse_conn *fc, struct fuse_req *req) list_add_tail(&req->list, &fc->pending); req->state = FUSE_REQ_PENDING; wake_up(&fc->waitq); + kill_fasync(&fc->fasync, SIGIO, POLL_IN); } /* @@ -669,6 +670,12 @@ static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov, err = -EPERM; if (!fc) goto err_unlock; + + err = -EAGAIN; + if((file->f_flags & O_NONBLOCK) && fc->connected && + list_empty(&fc->pending)) + goto err_unlock; + request_wait(fc); err = -ENODEV; if (!fc->connected) @@ -951,6 +958,7 @@ void fuse_abort_conn(struct fuse_conn *fc) end_requests(fc, &fc->pending); end_requests(fc, &fc->processing); wake_up_all(&fc->waitq); + kill_fasync(&fc->fasync, SIGIO, POLL_IN); } spin_unlock(&fuse_lock); } @@ -967,12 +975,26 @@ static int fuse_dev_release(struct inode *inode, struct file *file) end_requests(fc, &fc->processing); } spin_unlock(&fuse_lock); - if (fc) + if (fc) { kobject_put(&fc->kobj); + fasync_helper(-1, file, 0, &fc->fasync); + fc->fasync = NULL; + } return 0; } +static int fuse_dev_fasync(int fd, struct file *file, int on) +{ + struct fuse_conn *fc = fuse_get_conn(file); + + if (!fc) + return -ENODEV; + + /* No locking - fasync_helper does its own locking */ + return fasync_helper(fd, file, on, &fc->fasync); +} + struct file_operations fuse_dev_operations = { .owner = THIS_MODULE, .llseek = no_llseek, @@ -982,6 +1004,7 @@ struct file_operations fuse_dev_operations = { .writev = fuse_dev_writev, .poll = fuse_dev_poll, .release = fuse_dev_release, + .fasync = fuse_dev_fasync, }; static struct miscdevice fuse_miscdevice = { diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h index b37b6aa..11f556d 100644 --- a/kernel/fuse_i.h +++ b/kernel/fuse_i.h @@ -417,6 +417,9 @@ struct fuse_conn { /** kobject */ struct kobject kobj; + + /** O_ASYNC requests */ + struct fasync_struct *fasync; }; static inline struct fuse_conn *get_fuse_conn_super(struct super_block *sb) diff --git a/kernel/inode.c b/kernel/inode.c index 572951d..f65dd5e 100644 --- a/kernel/inode.c +++ b/kernel/inode.c @@ -21,6 +21,7 @@ #else #include "compat/parser.h" #endif +#include MODULE_AUTHOR("Miklos Szeredi "); MODULE_DESCRIPTION("Filesystem in Userspace"); @@ -290,6 +291,7 @@ static void fuse_put_super(struct super_block *sb) spin_unlock(&fuse_lock); up_write(&fc->sbput_sem); /* Flush all readers on this fs */ + kill_fasync(&fc->fasync, SIGIO, POLL_IN); wake_up_all(&fc->waitq); #ifdef KERNEL_2_6 kobject_del(&fc->kobj); @@ -518,6 +520,7 @@ static struct fuse_conn *new_conn(void) fc->bdi.unplug_io_fn = default_unplug_io_fn; #endif fc->reqctr = 0; + fc->fasync = NULL; } return fc; } diff --git a/lib/fuse.c b/lib/fuse.c index 0b31233..ec5ace9 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -1880,10 +1880,10 @@ struct fuse_cmd *fuse_read_cmd(struct fuse *f) size_t bufsize = fuse_chan_bufsize(ch); struct fuse_cmd *cmd = fuse_alloc_cmd(bufsize); if (cmd != NULL) { - int res = fuse_chan_receive(ch, cmd->buf, bufsize); + int res = fuse_chan_recv(ch, cmd->buf, bufsize); if (res <= 0) { free_cmd(cmd); - if (res == -1) + if (res < 0 && res != -EINTR && res != -EAGAIN) fuse_exit(f); return NULL; } diff --git a/lib/fuse_kern_chan.c b/lib/fuse_kern_chan.c index d58c8b2..fe7d252 100644 --- a/lib/fuse_kern_chan.c +++ b/lib/fuse_kern_chan.c @@ -16,26 +16,33 @@ static int fuse_kern_chan_receive(struct fuse_chan *ch, char *buf, size_t size) { - ssize_t res = read(fuse_chan_fd(ch), buf, size); - int err = errno; + int err; + ssize_t res; struct fuse_session *se = fuse_chan_session(ch); - assert(se != NULL); + + restart: + res = read(fuse_chan_fd(ch), buf, size); + err = errno; + if (fuse_session_exited(se)) return 0; if (res == -1) { - /* EINTR means, the read() was interrupted, ENOENT means the - operation was interrupted */ - if (err == EINTR || err == ENOENT) - return 0; - /* ENODEV means we got unmounted, so we silently return failure */ - if (err != ENODEV) + /* ENOENT means the operation was interrupted, it's safe + to restart */ + if (err == ENOENT) + goto restart; + + /* Errors occuring during normal operation: EINTR (read + interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem + umounted) */ + if (err != EINTR && err != EAGAIN && err != ENODEV) perror("fuse: reading device"); - return -1; + return -err; } if ((size_t) res < sizeof(struct fuse_in_header)) { fprintf(stderr, "short read on fuse device\n"); - return -1; + return -EIO; } return res; } diff --git a/lib/fuse_loop.c b/lib/fuse_loop.c index 1609bfc..6a6edaa 100644 --- a/lib/fuse_loop.c +++ b/lib/fuse_loop.c @@ -10,6 +10,7 @@ #include #include +#include int fuse_session_loop(struct fuse_session *se) { @@ -23,16 +24,15 @@ int fuse_session_loop(struct fuse_session *se) } while (!fuse_session_exited(se)) { - res = fuse_chan_receive(ch, buf, bufsize); - if (!res) + res = fuse_chan_recv(ch, buf, bufsize); + if (res == -EINTR) continue; - if (res == -1) + if (res <= 0) break; fuse_session_process(se, buf, res, ch); - res = 0; } free(buf); fuse_session_reset(se); - return res; + return res < 0 ? -1 : 0; } diff --git a/lib/fuse_loop_mt.c b/lib/fuse_loop_mt.c index 540607b..16be149 100644 --- a/lib/fuse_loop_mt.c +++ b/lib/fuse_loop_mt.c @@ -74,12 +74,14 @@ static void *do_work(void *data) pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); while (!fuse_session_exited(w->se)) { - int res = fuse_chan_receive(w->prevch, buf, bufsize); - if (!res) + int res = fuse_chan_recv(w->prevch, buf, bufsize); + if (res == -EINTR) continue; - if (res == -1) { - fuse_session_exit(w->se); - w->error = -1; + if (res <= 0) { + if (res < 0) { + fuse_session_exit(w->se); + w->error = -1; + } break; } diff --git a/lib/fuse_session.c b/lib/fuse_session.c index 4ea792a..8943204 100644 --- a/lib/fuse_session.c +++ b/lib/fuse_session.c @@ -12,6 +12,7 @@ #include #include #include +#include struct fuse_session { struct fuse_session_ops op; @@ -143,11 +144,17 @@ struct fuse_session *fuse_chan_session(struct fuse_chan *ch) return ch->se; } -int fuse_chan_receive(struct fuse_chan *ch, char *buf, size_t size) +int fuse_chan_recv(struct fuse_chan *ch, char *buf, size_t size) { return ch->op.receive(ch, buf, size); } +int fuse_chan_receive(struct fuse_chan *ch, char *buf, size_t size) +{ + int res = fuse_chan_recv(ch, buf, size); + return res >= 0 ? res : (res != -EINTR && res != -EAGAIN) ? -1 : 0; +} + int fuse_chan_send(struct fuse_chan *ch, const struct iovec iov[], size_t count) { return ch->op.send(ch, iov, count); diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript index 02ac176..2a8067a 100644 --- a/lib/fuse_versionscript +++ b/lib/fuse_versionscript @@ -96,6 +96,7 @@ FUSE_2.6 { fuse_setup_compat25; fuse_unmount; fuse_unmount_compat22; + fuse_chan_recv; local: *; -- 2.30.2