Package: sshfs Version: 2.4-1 Severity: important Tags: patch
--- Please enter the report below this line. --- In the event of a network failure, sshfs can become network-starved, causing programs accessing the sshfs mount to enter uninterruptible sleep. The solution is to kill the sshfs process; in contrast to some user expectations, "fusermount -u /mountpoint" does not work, since the mountpoint is still busy. Killing sshfs is annoying at best, and impossible in some cases (e.g., if $HOME is an sshfs, all new shells will immediately enter uninterruptible sleep). This scenario may be related to [1], but that bug appears to also be associated with an issue on the kernel side. The attached patch addresses this issue by providing a new configuration option "-oread_timeout=N" which causes sshfs to die (or attempt to remount if combined with "-oremount") if its network connection becomes starved for N seconds (in reality, the delay may be as long as 2N; eliminating this factor of two would complicate this patch). This simple patch protects the critical read() call to the network socket with a select(). The call must fail only in the event that sshfs is actually expecting network input, which is determined by checking if the request tab "sshfs.reqtab" is nonempty. I have set a default value of "0" for this option, since that changes things the least (i.e., it disables the timeout). It may be best for this default to be nonzero. Best, Antonio [1] http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=571005 P.S. I have attempted to report this bug to upstream, fuse-ss...@lists.sourceforge.net, but that mailing list seems to be dead and may have rejected my email. Hopefully someone else has a better relationship with upstream and can contact them. --- System information. --- Architecture: amd64 Kernel: Linux 3.9-1-amd64 Debian Release: jessie/sid 451 unstable www.deb-multimedia.org 451 unstable ftp.us.debian.org 399 testing ftp.us.debian.org 200 experimental www.deb-multimedia.org 200 experimental ftp.us.debian.org 150 stable dl.google.com --- Package information. --- Depends (Version) | Installed =================================-+-=============== libc6 (>= 2.4) | libfuse2 (>= 2.8.1) | libglib2.0-0 (>= 2.12.0) | fuse (>= 2.7) | openssh-client (>= 1:4.6p1-1) | Package's Recommends field is empty. Package's Suggests field is empty.
--- sshfs.c.old 2012-03-08 01:34:39.000000000 -0800 +++ sshfs.c.new 2013-08-20 21:11:38.274865000 -0700 @@ -219,6 +219,7 @@ int reconnect; int delay_connect; int slave; + int read_timeout; char *host; char *base_path; GHashTable *reqtab; @@ -358,6 +359,7 @@ SSHFS_OPT("password_stdin", password_stdin, 1), SSHFS_OPT("delay_connect", delay_connect, 1), SSHFS_OPT("slave", slave, 1), + SSHFS_OPT("read_timeout=%u", read_timeout, 0), FUSE_OPT_KEY("-p ", KEY_PORT), FUSE_OPT_KEY("-C", KEY_COMPRESS), @@ -1220,7 +1222,34 @@ int res; uint8_t *p = buf->p; size_t size = buf->size; + unsigned int outstanding_requests = 0; + + struct timeval timeout; + fd_set fdset; + while (size) { + if (sshfs.read_timeout > 0) { + FD_ZERO(&fdset); + FD_SET(sshfs.rfd, &fdset); + + pthread_mutex_lock(&sshfs.lock); + outstanding_requests = (unsigned int) + g_hash_table_size(sshfs.reqtab); + pthread_mutex_unlock(&sshfs.lock); + + timeout.tv_sec = sshfs.read_timeout; + timeout.tv_usec = 0; + + if (select(FD_SETSIZE, &fdset, NULL, NULL, &timeout)<1) { + if (outstanding_requests>0) { + fprintf(stderr, "wait exceeded read_timeout\n"); + return -1; + } else { + continue; + } + } + } + res = read(sshfs.rfd, p, size); if (res == -1) { perror("read"); @@ -3190,6 +3219,7 @@ " -o sftp_server=SERV path to sftp server or subsystem (default: sftp)\n" " -o directport=PORT directly connect to PORT bypassing ssh\n" " -o slave communicate over stdin and stdout bypassing network\n" +" -o read_timeout=N unmount if a read takes longer than N seconds\n" " -o transform_symlinks transform absolute symlinks to relative\n" " -o follow_symlinks follow symlinks on the server\n" " -o no_check_root don't check for existence of 'dir' on server\n"