aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiklos Szeredi <miklos@szeredi.hu>2006-03-01 12:10:13 +0000
committerMiklos Szeredi <miklos@szeredi.hu>2006-03-01 12:10:13 +0000
commit5d9ce36da4688ba2c14f658ed40b5b4ad971879b (patch)
tree379c5435a0741965848f8eb2449bef9c8b4d080c
parentee588c01dca3991807aea456a66246ff8ad0c332 (diff)
downloadlibfuse-5d9ce36da4688ba2c14f658ed40b5b4ad971879b.tar.gz
fix
-rw-r--r--ChangeLog8
-rw-r--r--include/fuse_lowlevel.h6
-rw-r--r--include/fuse_lowlevel_compat.h2
-rw-r--r--kernel/dev.c25
-rw-r--r--kernel/fuse_i.h3
-rw-r--r--kernel/inode.c3
-rw-r--r--lib/fuse.c4
-rw-r--r--lib/fuse_kern_chan.c29
-rw-r--r--lib/fuse_loop.c10
-rw-r--r--lib/fuse_loop_mt.c12
-rw-r--r--lib/fuse_session.c9
-rw-r--r--lib/fuse_versionscript1
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 <miklos@szeredi.hu>
+
+ * 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 <csaba.henk@creo.hu>
* 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 <linux/poll.h>
MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>");
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 <stdio.h>
#include <stdlib.h>
+#include <errno.h>
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 <stdlib.h>
#include <string.h>
#include <assert.h>
+#include <errno.h>
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:
*;