On 09/11/17(Thu) 09:02, Helg Bredow wrote:
> 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.
Nice to see that you're fixing a bug. However I'd suggest you to go
much further in your improvement.
Signal handlers are hard and instead of doing work inside the signal
handler you should toggle a global variable/flag and do this work
inside the main loop (fuse_loop()?).
For example your code below calls fprintf(3) in the signal handler. This
is incorrect, this functions is not signal handler safe. What about
fuse_unmount()? Are you sure it can be called from a signal handler?
I'd recommend you to read signal(3) and some signal handlers like the ones
from dhclient(8) and ntpd(8).
> 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;
> - }
> }
> }
>
>