fix
authorMiklos Szeredi <miklos@szeredi.hu>
Wed, 1 Mar 2006 12:10:13 +0000 (12:10 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Wed, 1 Mar 2006 12:10:13 +0000 (12:10 +0000)
12 files changed:
ChangeLog
include/fuse_lowlevel.h
include/fuse_lowlevel_compat.h
kernel/dev.c
kernel/fuse_i.h
kernel/inode.c
lib/fuse.c
lib/fuse_kern_chan.c
lib/fuse_loop.c
lib/fuse_loop_mt.c
lib/fuse_session.c
lib/fuse_versionscript

index c08d4c9f62a20ebe1fb321c1e3f9d99d89421093..155cb0dd27050b938c40d1bac1ad88016521a991 100644 (file)
--- 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
index bdcbfed0dbfb83e3192557efe66b8fa442ad9a42..f042dfba6e8d16e0a4aec890edeb8f6751580afe 100644 (file)
@@ -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
index f6e116cd2a89b66d5e334a6f3ff6b7adcaf60d3b..be4d4d80cf77ca736abad6a6052cce233c7148de 100644 (file)
@@ -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__ */
index d7c45d1c3870f3c4f54159a14f1c98d56736e132..d9ffd43f8ac23027fc7842bda62bd3822e77ea64 100644 (file)
@@ -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 = {
index b37b6aa9d065b1d96750facb38f56ecc79394b34..11f556d4f76de23468a884919e2e30014ee15068 100644 (file)
@@ -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)
index 572951d40b5a50eef6392ce715b9337264f8036d..f65dd5ecf4321d4eb8e88ee73d8f220d7c80755c 100644 (file)
@@ -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;
 }
index 0b312331002b6bfcdef4f35d900262b556945416..ec5ace981172337c180a4af65b6fbf6f2c67b53b 100644 (file)
@@ -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;
         }
index d58c8b21ff2a14d28a74de8723b14b42500ce629..fe7d252730d626d04f9df9e3161c2f164dc1b713 100644 (file)
 
 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;
 }
index 1609bfc12a82c747659f01718fd4d912322ed41d..6a6edaac9c6c196a7089dd5f8cf0d2f480c0b416 100644 (file)
@@ -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;
 }
index 540607b5d21421cb638f2ec60b5a6bc7273fb50e..16be149d3de8af68448e44887e9a634d841199f8 100644 (file)
@@ -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;
         }
 
index 4ea792a5fc042c59e582c48016be506c8b8985a7..8943204e1a65da8fc9e937170d4bcc50bb1f802a 100644 (file)
@@ -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);
index 02ac1760fdd5098f41be17a34e4e6a451f30345c..2a8067aa31b82efdcdbcabc1c32ba23b62ad4107 100644 (file)
@@ -96,6 +96,7 @@ FUSE_2.6 {
                fuse_setup_compat25;
                fuse_unmount;
                fuse_unmount_compat22;
+               fuse_chan_recv;
 
        local:
                 *;