Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 18 Jan 2012 15:13:22 +0000 (UTC)
From:      Dag-Erling Smorgrav <des@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r230307 - in head: lib/libfetch usr.bin/fetch
Message-ID:  <201201181513.q0IFDMb1045392@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: des
Date: Wed Jan 18 15:13:21 2012
New Revision: 230307
URL: http://svn.freebsd.org/changeset/base/230307

Log:
  Fix two issues related to the use of SIGINFO in fetch(1) to display
  progress information.  The first is that fetch_read() (used in the HTTP
  code but not the FTP code) can enter an infinite loop if it has previously
  been interrupted by a signal.  The second is that when it is interrupted,
  fetch_read() will discard any data it may have read up to that point.
  Luckily, both bugs are extremely timing-sensitive and therefore difficult
  to trigger.
  
  PR:		bin/153240
  Submitted by:	Mark <markjdb@gmail.com>
  MFC after:	3 weeks

Modified:
  head/lib/libfetch/common.c
  head/lib/libfetch/common.h
  head/lib/libfetch/http.c
  head/usr.bin/fetch/fetch.c

Modified: head/lib/libfetch/common.c
==============================================================================
--- head/lib/libfetch/common.c	Wed Jan 18 14:41:26 2012	(r230306)
+++ head/lib/libfetch/common.c	Wed Jan 18 15:13:21 2012	(r230307)
@@ -404,6 +404,34 @@ fetch_ssl_read(SSL *ssl, char *buf, size
 }
 #endif
 
+/*
+ * Cache some data that was read from a socket but cannot be immediately
+ * returned because of an interrupted system call.
+ */
+static int
+fetch_cache_data(conn_t *conn, char *src, size_t nbytes)
+{
+	char *tmp;
+
+	if (conn->cache.size < nbytes) {
+		tmp = realloc(conn->cache.buf, nbytes);
+		if (tmp == NULL) {
+			errno = ENOMEM;
+			fetch_syserr();
+			return (-1);
+		}
+		conn->cache.buf = tmp;
+		conn->cache.size = nbytes;
+	}
+
+	memcpy(conn->cache.buf, src, nbytes);
+	conn->cache.len = nbytes;
+	conn->cache.pos = 0;
+
+	return (0);
+}
+
+
 static ssize_t
 fetch_socket_read(int sd, char *buf, size_t len)
 {
@@ -429,6 +457,7 @@ fetch_read(conn_t *conn, char *buf, size
 	fd_set readfds;
 	ssize_t rlen, total;
 	int r;
+	char *start;
 
 	if (fetchTimeout) {
 		FD_ZERO(&readfds);
@@ -437,6 +466,24 @@ fetch_read(conn_t *conn, char *buf, size
 	}
 
 	total = 0;
+	start = buf;
+
+	if (conn->cache.len > 0) {
+		/*
+		 * The last invocation of fetch_read was interrupted by a
+		 * signal after some data had been read from the socket. Copy
+		 * the cached data into the supplied buffer before trying to
+		 * read from the socket again.
+		 */
+		total = (conn->cache.len < len) ? conn->cache.len : len;
+		memcpy(buf, conn->cache.buf, total);
+
+		conn->cache.len -= total;
+		conn->cache.pos += total;
+		len -= total;
+		buf+= total;
+	}
+
 	while (len > 0) {
 		/*
 		 * The socket is non-blocking.  Instead of the canonical
@@ -472,6 +519,8 @@ fetch_read(conn_t *conn, char *buf, size
 			total += rlen;
 			continue;
 		} else if (rlen == FETCH_READ_ERROR) {
+			if (errno == EINTR)
+				fetch_cache_data(conn, start, total);
 			return (-1);
 		}
 		// assert(rlen == FETCH_READ_WAIT);
@@ -492,8 +541,12 @@ fetch_read(conn_t *conn, char *buf, size
 			errno = 0;
 			r = select(conn->sd + 1, &readfds, NULL, NULL, &delta);
 			if (r == -1) {
-				if (errno == EINTR && fetchRestartCalls)
-					continue;
+				if (errno == EINTR) {
+					if (fetchRestartCalls)
+						continue;
+					/* Save anything that was read. */
+					fetch_cache_data(conn, start, total);
+				}
 				fetch_syserr();
 				return (-1);
 			}
@@ -677,6 +730,7 @@ fetch_close(conn_t *conn)
 	if (--conn->ref > 0)
 		return (0);
 	ret = close(conn->sd);
+	free(conn->cache.buf);
 	free(conn->buf);
 	free(conn);
 	return (ret);

Modified: head/lib/libfetch/common.h
==============================================================================
--- head/lib/libfetch/common.h	Wed Jan 18 14:41:26 2012	(r230306)
+++ head/lib/libfetch/common.h	Wed Jan 18 15:13:21 2012	(r230307)
@@ -52,6 +52,13 @@ struct fetchconn {
 	size_t		 bufsize;	/* buffer size */
 	size_t		 buflen;	/* length of buffer contents */
 	int		 err;		/* last protocol reply code */
+	struct {			/* data cached after an interrupted
+					   read */
+		char	*buf;
+		size_t	 size;
+		size_t	 pos;
+		size_t	 len;
+	} cache;
 #ifdef WITH_SSL
 	SSL		*ssl;		/* SSL handle */
 	SSL_CTX		*ssl_ctx;	/* SSL context */

Modified: head/lib/libfetch/http.c
==============================================================================
--- head/lib/libfetch/http.c	Wed Jan 18 14:41:26 2012	(r230306)
+++ head/lib/libfetch/http.c	Wed Jan 18 15:13:21 2012	(r230307)
@@ -196,6 +196,8 @@ http_growbuf(struct httpio *io, size_t l
 static int
 http_fillbuf(struct httpio *io, size_t len)
 {
+	ssize_t nbytes;
+
 	if (io->error)
 		return (-1);
 	if (io->eof)
@@ -204,10 +206,11 @@ http_fillbuf(struct httpio *io, size_t l
 	if (io->chunked == 0) {
 		if (http_growbuf(io, len) == -1)
 			return (-1);
-		if ((io->buflen = fetch_read(io->conn, io->buf, len)) == -1) {
-			io->error = 1;
+		if ((nbytes = fetch_read(io->conn, io->buf, len)) == -1) {
+			io->error = errno;
 			return (-1);
 		}
+		io->buflen = nbytes;
 		io->bufpos = 0;
 		return (io->buflen);
 	}
@@ -227,10 +230,11 @@ http_fillbuf(struct httpio *io, size_t l
 		len = io->chunksize;
 	if (http_growbuf(io, len) == -1)
 		return (-1);
-	if ((io->buflen = fetch_read(io->conn, io->buf, len)) == -1) {
-		io->error = 1;
+	if ((nbytes = fetch_read(io->conn, io->buf, len)) == -1) {
+		io->error = errno;
 		return (-1);
 	}
+	io->buflen = nbytes;
 	io->chunksize -= io->buflen;
 
 	if (io->chunksize == 0) {
@@ -272,8 +276,11 @@ http_readfn(void *v, char *buf, int len)
 		io->bufpos += l;
 	}
 
-	if (!pos && io->error)
+	if (!pos && io->error) {
+		if (io->error == EINTR)
+			io->error = 0;
 		return (-1);
+	}
 	return (pos);
 }
 

Modified: head/usr.bin/fetch/fetch.c
==============================================================================
--- head/usr.bin/fetch/fetch.c	Wed Jan 18 14:41:26 2012	(r230306)
+++ head/usr.bin/fetch/fetch.c	Wed Jan 18 15:13:21 2012	(r230307)
@@ -317,7 +317,7 @@ fetch(char *URL, const char *path)
 	struct stat sb, nsb;
 	struct xferstat xs;
 	FILE *f, *of;
-	size_t size, wr;
+	size_t size, readcnt, wr;
 	off_t count;
 	char flags[8];
 	const char *slash;
@@ -636,21 +636,26 @@ fetch(char *URL, const char *path)
 			stat_end(&xs);
 			siginfo = 0;
 		}
-		if ((size = fread(buf, 1, size, f)) == 0) {
+
+		if (size == 0)
+			break;
+
+		if ((readcnt = fread(buf, 1, size, f)) < size) {
 			if (ferror(f) && errno == EINTR && !sigint)
 				clearerr(f);
-			else
+			else if (readcnt == 0)
 				break;
 		}
-		stat_update(&xs, count += size);
-		for (ptr = buf; size > 0; ptr += wr, size -= wr)
-			if ((wr = fwrite(ptr, 1, size, of)) < size) {
+
+		stat_update(&xs, count += readcnt);
+		for (ptr = buf; readcnt > 0; ptr += wr, readcnt -= wr)
+			if ((wr = fwrite(ptr, 1, readcnt, of)) < readcnt) {
 				if (ferror(of) && errno == EINTR && !sigint)
 					clearerr(of);
 				else
 					break;
 			}
-		if (size != 0)
+		if (readcnt != 0)
 			break;
 	}
 	if (!sigalrm)



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201201181513.q0IFDMb1045392>