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"

Reply via email to