tar et al: die if bb_copyfd_size copies less than asked for.
(we have bb_copyfd_exact_size now for that kind of usage)
diff --git a/archival/libunarchive/data_extract_all.c b/archival/libunarchive/data_extract_all.c
index 67f8f35..25bf028 100644
--- a/archival/libunarchive/data_extract_all.c
+++ b/archival/libunarchive/data_extract_all.c
@@ -67,10 +67,10 @@
 			/* Regular file */
 			dst_fd = xopen3(file_header->name, O_WRONLY | O_CREAT | O_EXCL,
 							file_header->mode);
-			bb_copyfd_size(archive_handle->src_fd, dst_fd, file_header->size);
+			bb_copyfd_exact_size(archive_handle->src_fd, dst_fd, file_header->size);
 			close(dst_fd);
 			break;
-			}
+		}
 		case S_IFDIR:
 			res = mkdir(file_header->name, file_header->mode);
 			if ((errno != EISDIR) && (res == -1)
diff --git a/archival/libunarchive/data_extract_to_buffer.c b/archival/libunarchive/data_extract_to_buffer.c
index 95cb8f5..d8fcdf3 100644
--- a/archival/libunarchive/data_extract_to_buffer.c
+++ b/archival/libunarchive/data_extract_to_buffer.c
@@ -10,9 +10,8 @@
 
 void data_extract_to_buffer(archive_handle_t *archive_handle)
 {
-	const unsigned int size = archive_handle->file_header->size;
+	unsigned int size = archive_handle->file_header->size;
 
 	archive_handle->buffer = xzalloc(size + 1);
-
 	xread(archive_handle->src_fd, archive_handle->buffer, size);
 }
diff --git a/archival/libunarchive/data_extract_to_stdout.c b/archival/libunarchive/data_extract_to_stdout.c
index 788246c..2e266c0 100644
--- a/archival/libunarchive/data_extract_to_stdout.c
+++ b/archival/libunarchive/data_extract_to_stdout.c
@@ -4,9 +4,11 @@
  */
 
 #include "unarchive.h"
-#include <unistd.h>
+//#include <unistd.h>
 
 void data_extract_to_stdout(archive_handle_t *archive_handle)
 {
-	bb_copyfd_size(archive_handle->src_fd, STDOUT_FILENO, archive_handle->file_header->size);
+	bb_copyfd_exact_size(archive_handle->src_fd,
+			STDOUT_FILENO,
+			archive_handle->file_header->size);
 }
diff --git a/archival/libunarchive/get_header_ar.c b/archival/libunarchive/get_header_ar.c
index 7f8c81c..6638c65 100644
--- a/archival/libunarchive/get_header_ar.c
+++ b/archival/libunarchive/get_header_ar.c
@@ -96,7 +96,8 @@
 	if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
 		archive_handle->action_header(typed);
 		if (archive_handle->sub_archive) {
-			while (archive_handle->action_data_subarchive(archive_handle->sub_archive) == EXIT_SUCCESS);
+			while (archive_handle->action_data_subarchive(archive_handle->sub_archive) == EXIT_SUCCESS)
+				/* repeat */;
 		} else {
 			archive_handle->action_data(archive_handle);
 		}
diff --git a/archival/libunarchive/get_header_tar.c b/archival/libunarchive/get_header_tar.c
index 66c3314..beb8687 100644
--- a/archival/libunarchive/get_header_tar.c
+++ b/archival/libunarchive/get_header_tar.c
@@ -251,7 +251,7 @@
 	}
 
 	/* Strip trailing '/' in directories */
-	/* Must be done after mode is set as '/' is used to check if its a directory */
+	/* Must be done after mode is set as '/' is used to check if it's a directory */
 	cp = last_char_is(file_header->name, '/');
 
 	if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
diff --git a/archival/libunarchive/seek_by_read.c b/archival/libunarchive/seek_by_read.c
index d56f94b..e46af48 100644
--- a/archival/libunarchive/seek_by_read.c
+++ b/archival/libunarchive/seek_by_read.c
@@ -13,7 +13,6 @@
  */
 void seek_by_read(const archive_handle_t *archive_handle, const unsigned int jump_size)
 {
-	if (jump_size) {
-		bb_copyfd_size(archive_handle->src_fd, -1, jump_size);
-	}
+	if (jump_size)
+		bb_copyfd_exact_size(archive_handle->src_fd, -1, jump_size);
 }
diff --git a/archival/tar.c b/archival/tar.c
index 7465e88..ee9007c 100644
--- a/archival/tar.c
+++ b/archival/tar.c
@@ -452,26 +452,28 @@
 
 	/* If it was a regular file, write out the body */
 	if (inputFileFd >= 0) {
-		off_t readSize = 0;
+		size_t readSize;
+		/* Wwrite the file to the archive. */
+		/* We record size into header first, */
+		/* and then write out file. If file shrinks in between, */
+		/* tar will be corrupted. So we don't allow for that. */
+		/* NB: GNU tar 1.16 warns and pads with zeroes */
+		/* or even seeks back and updates header */
+		bb_copyfd_exact_size(inputFileFd, tbInfo->tarFd, statbuf->st_size);
+		////off_t readSize;
+		////readSize = bb_copyfd_size(inputFileFd, tbInfo->tarFd, statbuf->st_size);
+		////if (readSize != statbuf->st_size && readSize >= 0) {
+		////	bb_error_msg_and_die("short read from %s, aborting", fileName);
+		////}
 
-		/* write the file to the archive */
-		readSize = bb_copyfd_size(inputFileFd, tbInfo->tarFd, statbuf->st_size);
-		/* readSize < 0 means that error was already reported */
-		if (readSize != statbuf->st_size && readSize >= 0) {
-			/* Deadly. We record size into header first, */
-			/* and then write out file. If file shrinks in between, */
-			/* tar will be corrupted. So bail out. */
-			/* NB: GNU tar 1.16 warns and pads with zeroes */
-			/* or even seeks back and updates header */
-			bb_error_msg_and_die("short read from %s, aborting", fileName);
-		}
 		/* Check that file did not grow in between? */
-		/* if (safe_read(inputFileFd,1) == 1) warn but continue? */
+		/* if (safe_read(inputFileFd, 1) == 1) warn but continue? */
+
 		close(inputFileFd);
 
 		/* Pad the file up to the tar block size */
 		/* (a few tricks here in the name of code size) */
-		readSize = (-(int)readSize) & (TAR_BLOCK_SIZE-1);
+		readSize = (-(int)statbuf->st_size) & (TAR_BLOCK_SIZE-1);
 		memset(bb_common_bufsiz1, 0, readSize);
 		xwrite(tbInfo->tarFd, bb_common_bufsiz1, readSize);
 	}
diff --git a/archival/unzip.c b/archival/unzip.c
index f553eef..1c03a4c 100644
--- a/archival/unzip.c
+++ b/archival/unzip.c
@@ -54,9 +54,9 @@
 static void unzip_skip(int fd, off_t skip)
 {
 	if (lseek(fd, skip, SEEK_CUR) == (off_t)-1) {
-		if ((errno != ESPIPE) || (bb_copyfd_size(fd, -1, skip) != skip)) {
+		if (errno != ESPIPE)
 			bb_error_msg_and_die("seek failure");
-		}
+		bb_copyfd_exact_size(fd, -1, skip);
 	}
 }
 
@@ -75,10 +75,8 @@
 	if (zip_header->formatted.method == 0) {
 		/* Method 0 - stored (not compressed) */
 		off_t size = zip_header->formatted.ucmpsize;
-		if (size && (bb_copyfd_size(src_fd, dst_fd, size) != size)) {
-			bb_error_msg_and_die("cannot complete extraction");
-		}
-
+		if (size)
+			bb_copyfd_exact_size(src_fd, dst_fd, size);
 	} else {
 		/* Method 8 - inflate */
 		inflate_init(zip_header->formatted.cmpsize);
@@ -249,8 +247,8 @@
 		unzip_skip(src_fd, zip_header.formatted.extra_len);
 
 		if ((verbosity == v_list) && !list_header_done){
-			printf("  Length     Date   Time    Name\n"
-			       " --------    ----   ----    ----\n");
+			puts("  Length     Date   Time    Name\n"
+			     " --------    ----   ----    ----");
 			list_header_done = 1;
 		}
 
@@ -274,10 +272,8 @@
 					   dst_fn);
 				total_entries++;
 				i = 'n';
-
 			} else if (dst_fd == STDOUT_FILENO) { /* Extracting to STDOUT */
 				i = -1;
-
 			} else if (last_char_is(dst_fn, '/')) { /* Extract directory */
 				if (stat(dst_fn, &stat_buf) == -1) {
 					if (errno != ENOENT) {
@@ -298,17 +294,15 @@
 				i = 'n';
 
 			} else {  /* Extract file */
-			_check_file:
+ _check_file:
 				if (stat(dst_fn, &stat_buf) == -1) { /* File does not exist */
 					if (errno != ENOENT) {
 						bb_perror_msg_and_die("cannot stat '%s'",dst_fn);
 					}
 					i = 'y';
-
 				} else { /* File already exists */
 					if (overwrite == o_never) {
 						i = 'n';
-
 					} else if (S_ISREG(stat_buf.st_mode)) { /* File is regular file */
 						if (overwrite == o_always) {
 							i = 'y';
@@ -319,7 +313,6 @@
 							}
 							i = key_buf[0];
 						}
-
 					} else { /* File is not regular file */
 						bb_error_msg_and_die("'%s' exists but is not regular file",dst_fn);
 					}
@@ -338,7 +331,7 @@
 				printf("  inflating: %s\n", dst_fn);
 			}
 			if (unzip_extract(&zip_header, src_fd, dst_fd)) {
-			    failed = 1;
+				failed = 1;
 			}
 			if (dst_fd != STDOUT_FILENO) {
 				/* closing STDOUT is potentially bad for future business */
diff --git a/coreutils/cat.c b/coreutils/cat.c
index d828b86..2b7c603 100644
--- a/coreutils/cat.c
+++ b/coreutils/cat.c
@@ -25,9 +25,8 @@
 		if (f) {
 			off_t r = bb_copyfd_eof(fileno(f), STDOUT_FILENO);
 			fclose_if_not_stdin(f);
-			if (r >= 0) {
+			if (r >= 0)
 				continue;
-			}
 		}
 		retval = EXIT_FAILURE;
 	} while (*++argv);
diff --git a/include/libbb.h b/include/libbb.h
index 2fd54e7..2bfeba4 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -203,8 +203,13 @@
 extern int device_open(const char *device, int mode);
 extern int get_console_fd(void);
 extern char *find_block_device(char *path);
-extern off_t bb_copyfd_size(int fd1, int fd2, off_t size);
+/* bb_copyfd_XX print read/write errors and return -1 if they occur */
 extern off_t bb_copyfd_eof(int fd1, int fd2);
+extern off_t bb_copyfd_size(int fd1, int fd2, off_t size);
+extern void bb_copyfd_exact_size(int fd1, int fd2, off_t size);
+/* "short" copy can be detected by return value < size */
+/* this helper yells "short read!" if param is not -1 */
+extern void complain_copyfd_and_die(off_t sz) ATTRIBUTE_NORETURN;
 extern char bb_process_escape_sequence(const char **ptr);
 extern char *bb_get_last_path_component(char *path);
 extern int ndelay_on(int fd);
diff --git a/include/unarchive.h b/include/unarchive.h
index 7de6a63..88c0088 100644
--- a/include/unarchive.h
+++ b/include/unarchive.h
@@ -24,7 +24,7 @@
 } file_header_t;
 
 typedef struct archive_handle_s {
-	/* define if the header and data component should processed */
+	/* define if the header and data component should be processed */
 	char (*filter)(struct archive_handle_s *);
 	llist_t *accept;
 	/* List of files that have been rejected */
diff --git a/libbb/copyfd.c b/libbb/copyfd.c
index c6b8866..17bf4fb 100644
--- a/libbb/copyfd.c
+++ b/libbb/copyfd.c
@@ -39,7 +39,7 @@
 
 		rd = safe_read(src_fd, buffer, size > BUFSIZ ? BUFSIZ : size);
 
-		if (!rd) { /* eof - all done. */
+		if (!rd) { /* eof - all done */
 			status = 0;
 			break;
 		}
@@ -56,22 +56,31 @@
 			}
 		}
 		total += rd;
-		if (status < 0) {
+		if (status < 0) { /* if we aren't copying till EOF... */
 			size -= rd;
 			if (!size) {
-				status = 0;
+				/* 'size' bytes copied - all done */
+				status = 0; 
 				break;
 			}
 		}
 	}
-
-out:
+ out:
 	RELEASE_CONFIG_BUFFER(buffer);
-
 	return status ? -1 : total;
 }
 
 
+#if 0
+void complain_copyfd_and_die(off_t sz)
+{
+	if (sz != -1)
+		bb_error_msg_and_die("short read");
+	/* if sz == -1, bb_copyfd_XX already complained */
+	exit(xfunc_error_retval);
+}
+#endif
+
 off_t bb_copyfd_size(int fd1, int fd2, off_t size)
 {
 	if (size) {
@@ -80,6 +89,17 @@
 	return 0;
 }
 
+void bb_copyfd_exact_size(int fd1, int fd2, off_t size)
+{
+	off_t sz = bb_copyfd_size(fd1, fd2, size);
+	if (sz == size)
+		return;
+	if (sz != -1)
+		bb_error_msg_and_die("short read");
+	/* if sz == -1, bb_copyfd_XX already complained */
+	exit(xfunc_error_retval);
+}
+
 off_t bb_copyfd_eof(int fd1, int fd2)
 {
 	return bb_full_fd_action(fd1, fd2, 0);
diff --git a/networking/ftpgetput.c b/networking/ftpgetput.c
index dff8944..9d05442 100644
--- a/networking/ftpgetput.c
+++ b/networking/ftpgetput.c
@@ -22,8 +22,8 @@
 	struct sockaddr_in *s_in;
 } ftp_host_info_t;
 
-static char verbose_flag = 0;
-static char do_continue = 0;
+static char verbose_flag;
+static char do_continue;
 
 static int ftpcmd(const char *s1, const char *s2, FILE *stream, char *buf)
 {
@@ -112,7 +112,9 @@
 		const char *local_path, char *server_path)
 {
 	char buf[512];
-	off_t filesize = 0;
+/* I think 'filesize' usage here is bogus. Let's see... */
+	//off_t filesize = -1;
+#define filesize ((off_t)-1)
 	int fd_data;
 	int fd_local = -1;
 	off_t beg_range = 0;
@@ -124,11 +126,10 @@
 	fd_data = xconnect_ftpdata(server, buf);
 
 	if (ftpcmd("SIZE ", server_path, control_stream, buf) == 213) {
-		filesize = BB_STRTOOFF(buf + 4, NULL, 10);
-		if (errno || filesize < 0)
-			bb_error_msg_and_die("SIZE error: %s", buf + 4);
+		//filesize = BB_STRTOOFF(buf + 4, NULL, 10);
+		//if (errno || filesize < 0)
+		//	bb_error_msg_and_die("SIZE error: %s", buf + 4);
 	} else {
-		filesize = -1;
 		do_continue = 0;
 	}
 
@@ -154,7 +155,8 @@
 		if (ftpcmd(buf, NULL, control_stream, buf) != 350) {
 			do_continue = 0;
 		} else {
-			filesize -= beg_range;
+			//if (filesize != -1)
+			//	filesize -= beg_range;
 		}
 	}
 
@@ -173,11 +175,11 @@
 
 	/* Copy the file */
 	if (filesize != -1) {
-		if (-1 == bb_copyfd_size(fd_data, fd_local, filesize))
-			exit(EXIT_FAILURE);
+		if (bb_copyfd_size(fd_data, fd_local, filesize) == -1)
+			return EXIT_FAILURE;
 	} else {
-		if (-1 == bb_copyfd_eof(fd_data, fd_local))
-			exit(EXIT_FAILURE);
+		if (bb_copyfd_eof(fd_data, fd_local) == -1)
+			return EXIT_FAILURE;
 	}
 
 	/* close it all down */
@@ -277,14 +279,11 @@
 	/* content-length of the file */
 	unsigned opt;
 	char *port = "ftp";
-
 	/* socket to ftp server */
 	FILE *control_stream;
 	struct sockaddr_in s_in;
-
 	/* continue a prev transfer (-c) */
 	ftp_host_info_t *server;
-
 	int (*ftp_action)(ftp_host_info_t *, FILE *, const char *, char *) = NULL;
 
 	/* Check to see if the command is ftpget or ftput */