The current libfuse signal handling assumes that the file system will always be 
unmounted by the child. One obvious case where this is not true is if the file 
system is busy. To replicate:

1. mount a fuse file system
2. cd anywhere on the file system
3. pkill -INT <fuse exe>

The result is a zombie child process and no more response to signals even if 
the file system is no longer busy.

This patch ensures that the child always exits and that an error is printed to 
stdout if the file system cannot be unmounted. Tested with fuse-exfat and 
ntfs_3g. Suggestions for improvement are welcome.

Index: fuse.c
===================================================================
RCS file: /cvs/src/lib/libfuse/fuse.c,v
retrieving revision 1.34
diff -u -p -r1.34 fuse.c
--- fuse.c      4 Nov 2017 13:17:18 -0000       1.34
+++ fuse.c      9 Nov 2017 08:41:11 -0000
@@ -303,26 +303,40 @@ ifuse_get_signal(unused int num)
 {
        struct fuse *f;
        pid_t child;
-       int status;
+       int status, err;
+
+       if (num == SIGCHLD) {
+               /* child fuse_unmount() completed, check error */
+               while (waitpid(WAIT_ANY, &status, WNOHANG) == -1) {
+                       if (errno != EINTR)
+                               fprintf(stderr, "fuse: %s\n", strerror(errno));
+               }
+
+               if (WIFEXITED(status)) {
+                       err = WEXITSTATUS(status);
+                       if (err)
+                               fprintf(stderr, "fuse: %s\n", strerror(err));
+               }
+
+               signal(SIGCHLD, SIG_DFL);
+               return;
+       }
 
        if (sigse != NULL) {
+               signal(SIGCHLD, ifuse_get_signal);
                child = fork();
 
                if (child < 0)
-                       return;
+                       return ;
 
-               f = sigse->args;
                if (child == 0) {
+                       /* try to unmount, file system may be busy */
+                       f = sigse->args;
+                       errno = 0;
                        fuse_unmount(f->fc->dir, f->fc);
-                       sigse = NULL;
-                       exit(0);
+                       _exit(errno);
                }
 
-               fuse_loop(f);
-               while (waitpid(child, &status, 0) == -1) {
-                       if (errno != EINTR)
-                               break;
-               }
        }
 }
 

Reply via email to