fix
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:
 		 *;