fix
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 @@
 /**
  * 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 @@
                                               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 @@
 	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 @@
 	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 @@
 		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 @@
 		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 @@
 	.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 @@
 
 	/** 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 @@
 	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 @@
 		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 @@
     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 @@
     }
 
     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 @@
     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 @@
     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_setup_compat25;
 		fuse_unmount;
 		fuse_unmount_compat22;
+		fuse_chan_recv;
 
 	local:
 		 *;