changeset 287:b72f98803e46

Update to scp from OpenSSH portable 4.3p2
author Matt Johnston <matt@ucc.asn.au>
date Wed, 08 Mar 2006 14:20:24 +0000
parents 92cc938b59a2
children a933abec6d06
files options.h progressmeter.c scp.c scpmisc.c scpmisc.h
diffstat 5 files changed, 351 insertions(+), 184 deletions(-) [+]
line wrap: on
line diff
--- a/options.h	Wed Mar 08 13:48:05 2006 +0000
+++ b/options.h	Wed Mar 08 14:20:24 2006 +0000
@@ -252,6 +252,8 @@
 
 #define _PATH_TTY "/dev/tty"
 
+#define _PATH_CP "/bin/cp"
+
 /* Timeouts in seconds */
 #define SELECT_TIMEOUT 20
 
--- a/progressmeter.c	Wed Mar 08 13:48:05 2006 +0000
+++ b/progressmeter.c	Wed Mar 08 14:20:24 2006 +0000
@@ -24,7 +24,7 @@
  */
 
 #include "includes.h"
-/*RCSID("OpenBSD: progressmeter.c,v 1.15 2003/08/31 12:14:22 markus Exp ");*/
+/*RCSID("$OpenBSD: progressmeter.c,v 1.24 2005/06/07 13:25:23 jaredy Exp $");*/
 
 #include "progressmeter.h"
 #include "atomicio.h"
@@ -43,21 +43,26 @@
 static void format_size(char *, int, off_t);
 static void format_rate(char *, int, off_t);
 
+/* window resizing */
+static void sig_winch(int);
+static void setscreensize(void);
+
 /* updates the progressmeter to reflect the current state of the transfer */
 void refresh_progress_meter(void);
 
 /* signal handler for updating the progress meter */
 static void update_progress_meter(int);
 
-static time_t start; 		/* start progress */
-static time_t last_update; 	/* last progress update */
-static char *file; 		/* name of the file being transferred */
-static off_t end_pos; 		/* ending position of transfer */
-static off_t cur_pos; 		/* transfer position as of last refresh */
+static time_t start;		/* start progress */
+static time_t last_update;	/* last progress update */
+static char *file;		/* name of the file being transferred */
+static off_t end_pos;		/* ending position of transfer */
+static off_t cur_pos;		/* transfer position as of last refresh */
 static volatile off_t *counter;	/* progress counter */
-static long stalled; 		/* how long we have been stalled */
-static int bytes_per_second; 	/* current speed in bytes per second */
-static int win_size; 		/* terminal window size */
+static long stalled;		/* how long we have been stalled */
+static int bytes_per_second;	/* current speed in bytes per second */
+static int win_size;		/* terminal window size */
+static volatile sig_atomic_t win_resized; /* for window resizing */
 
 /* units for format_size */
 static const char unit[] = " KMGT";
@@ -81,8 +86,8 @@
 		bytes = (bytes + 512) / 1024;
 	}
 	snprintf(buf, size, "%3lld.%1lld%c%s",
-	    (int64_t) bytes / 100,
-	    (int64_t) (bytes + 5) / 10 % 10,
+	    (long long) (bytes + 5) / 100,
+	    (long long) (bytes + 5) / 10 % 10,
 	    unit[i],
 	    i ? "B" : " ");
 }
@@ -95,7 +100,7 @@
 	for (i = 0; bytes >= 10000 && unit[i] != 'T'; i++)
 		bytes = (bytes + 512) / 1024;
 	snprintf(buf, size, "%4lld%c%s",
-	    (int64_t) bytes,
+	    (long long) bytes,
 	    unit[i],
 	    i ? "B" : " ");
 }
@@ -108,7 +113,7 @@
 	off_t transferred;
 	double elapsed;
 	int percent;
-	int bytes_left;
+	off_t bytes_left;
 	int cur_speed;
 	int hours, minutes, seconds;
 	int i, len;
@@ -121,14 +126,18 @@
 
 	if (bytes_left > 0)
 		elapsed = now - last_update;
-	else
+	else {
 		elapsed = now - start;
+		/* Calculate true total speed when done */
+		transferred = end_pos;
+		bytes_per_second = 0;
+	}
 
 	/* calculate speed */
 	if (elapsed != 0)
 		cur_speed = (transferred / elapsed);
 	else
-		cur_speed = 0;
+		cur_speed = transferred;
 
 #define AGE_FACTOR 0.9
 	if (bytes_per_second != 0) {
@@ -144,6 +153,8 @@
 		len = snprintf(buf, file_len + 1, "\r%s", file);
 		if (len < 0)
 			len = 0;
+		if (len >= file_len + 1)
+			len = file_len;
 		for (i = len;  i < file_len; i++ )
 			buf[i] = ' ';
 		buf[file_len] = '\0';
@@ -164,7 +175,7 @@
 
 	/* bandwidth usage */
 	format_rate(buf + strlen(buf), win_size - strlen(buf),
-	    bytes_per_second);
+	    (off_t)bytes_per_second);
 	strlcat(buf, "/s ", win_size);
 
 	/* ETA */
@@ -201,7 +212,7 @@
 			strlcat(buf, "    ", win_size);
 	}
 
-	atomicio(vwrite, STDOUT_FILENO, buf, win_size);
+	atomicio(vwrite, STDOUT_FILENO, buf, win_size - 1);
 	last_update = now;
 }
 
@@ -212,6 +223,10 @@
 
 	save_errno = errno;
 
+	if (win_resized) {
+		setscreensize();
+		win_resized = 0;
+	}
 	if (can_output())
 		refresh_progress_meter();
 
@@ -221,32 +236,22 @@
 }
 
 void
-start_progress_meter(char *f, off_t filesize, off_t *stat)
+start_progress_meter(char *f, off_t filesize, off_t *ctr)
 {
-	struct winsize winsize;
-
 	start = last_update = time(NULL);
 	file = f;
 	end_pos = filesize;
 	cur_pos = 0;
-	counter = stat;
+	counter = ctr;
 	stalled = 0;
 	bytes_per_second = 0;
 
-	if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize) != -1 &&
-	    winsize.ws_col != 0) {
-		if (winsize.ws_col > MAX_WINSIZE)
-			win_size = MAX_WINSIZE;
-		else
-			win_size = winsize.ws_col;
-	} else
-		win_size = DEFAULT_WINSIZE;
-	win_size += 1;					/* trailing \0 */
-
+	setscreensize();
 	if (can_output())
 		refresh_progress_meter();
 
 	signal(SIGALRM, update_progress_meter);
+	signal(SIGWINCH, sig_winch);
 	alarm(UPDATE_INTERVAL);
 }
 
@@ -264,4 +269,26 @@
 
 	atomicio(vwrite, STDOUT_FILENO, "\n", 1);
 }
+
+static void
+sig_winch(int sig)
+{
+	win_resized = 1;
+}
+
+static void
+setscreensize(void)
+{
+	struct winsize winsize;
+
+	if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize) != -1 &&
+	    winsize.ws_col != 0) {
+		if (winsize.ws_col > MAX_WINSIZE)
+			win_size = MAX_WINSIZE;
+		else
+			win_size = winsize.ws_col;
+	} else
+		win_size = DEFAULT_WINSIZE;
+	win_size += 1;					/* trailing \0 */
+}
 #endif /* PROGRESS_METER */
--- a/scp.c	Wed Mar 08 13:48:05 2006 +0000
+++ b/scp.c	Wed Mar 08 14:20:24 2006 +0000
@@ -71,40 +71,20 @@
  */
 
 #include "includes.h"
+/*RCSID("$OpenBSD: scp.c,v 1.130 2006/01/31 10:35:43 djm Exp $");*/
+
 #include "atomicio.h"
 #include "compat.h"
 #include "scpmisc.h"
 #include "progressmeter.h"
 
-#define _PATH_CP "/bin/cp"
-
-#ifndef TIMEVAL_TO_TIMESPEC
-#define	TIMEVAL_TO_TIMESPEC(tv, ts) {					\
-	(ts)->tv_sec = (tv)->tv_sec;					\
-	(ts)->tv_nsec = (tv)->tv_usec * 1000;				\
-}
-#endif
-
-#ifndef timersub
-#define timersub(tvp, uvp, vvp)					 \
-	do {								\
-		(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec;	  \
-		(vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec;   \
-		if ((vvp)->tv_usec < 0) {			   \
-			(vvp)->tv_sec--;				\
-			(vvp)->tv_usec += 1000000;		  \
-		}						   \
-	} while (/* CONSTCOND */ 0)
-#endif /* timersub */
-
-
 void bwlimit(int);
 
 /* Struct for addargs */
 arglist args;
 
 /* Bandwidth limit */
-off_t limitbw = 0;
+off_t limit_rate = 0;
 
 /* Name of current file being transferred. */
 char *curfile;
@@ -112,10 +92,8 @@
 /* This is set to non-zero to enable verbose mode. */
 int verbose_mode = 0;
 
-#ifdef PROGRESS_METER
 /* This is set to zero if the progressmeter is not desired. */
 int showprogress = 1;
-#endif
 
 /* This is the program to execute for the secured connection. ("ssh" or -S) */
 char *ssh_program = _PATH_SSH_PROGRAM;
@@ -126,10 +104,56 @@
 static void
 killchild(int signo)
 {
-	if (do_cmd_pid > 1)
-		kill(do_cmd_pid, signo);
+	if (do_cmd_pid > 1) {
+		kill(do_cmd_pid, signo ? signo : SIGTERM);
+		waitpid(do_cmd_pid, NULL, 0);
+	}
+
+	if (signo)
+		_exit(1);
+	exit(1);
+}
+
+static int
+do_local_cmd(arglist *a)
+{
+	u_int i;
+	int status;
+	pid_t pid;
+
+	if (a->num == 0)
+		fatal("do_local_cmd: no arguments");
 
-	_exit(1);
+	if (verbose_mode) {
+		fprintf(stderr, "Executing:");
+		for (i = 0; i < a->num; i++)
+			fprintf(stderr, " %s", a->list[i]);
+		fprintf(stderr, "\n");
+	}
+	if ((pid = fork()) == -1)
+		fatal("do_local_cmd: fork: %s", strerror(errno));
+
+	if (pid == 0) {
+		execvp(a->list[0], a->list);
+		perror(a->list[0]);
+		exit(1);
+	}
+
+	do_cmd_pid = pid;
+	signal(SIGTERM, killchild);
+	signal(SIGINT, killchild);
+	signal(SIGHUP, killchild);
+
+	while (waitpid(pid, &status, 0) == -1)
+		if (errno != EINTR)
+			fatal("do_local_cmd: waitpid: %s", strerror(errno));
+
+	do_cmd_pid = -1;
+
+	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+		return (-1);
+
+	return (0);
 }
 
 /*
@@ -156,11 +180,10 @@
 	pipe(reserved);
 
 	/* Create a socket pair for communicating with ssh. */
-	if (pipe(pin) < 0 || pipe(pout) < 0)
-	{
-		fprintf(stderr, "Fatal error: pipe: %s\n", strerror(errno));
-		exit(1);
-	}
+	if (pipe(pin) < 0)
+		fatal("pipe: %s", strerror(errno));
+	if (pipe(pout) < 0)
+		fatal("pipe: %s", strerror(errno));
 
 	/* Free the reserved descriptors. */
 	close(reserved[0]);
@@ -169,19 +192,20 @@
     /* uClinux needs to build the args here before vforking,
        otherwise we do it later on. */
 #ifdef __uClinux__
-	args.list[0] = ssh_program;
-	if (remuser != NULL)
-		addargs(&args, "-l%s", remuser);
-	addargs(&args, "%s", host);
-	addargs(&args, "%s", cmd);
+		replacearg(&args, 0, "%s", ssh_program);
+		if (remuser != NULL)
+			addargs(&args, "-l%s", remuser);
+		addargs(&args, "%s", host);
+		addargs(&args, "%s", cmd);
 #endif /* __uClinux__ */
 
 	/* Fork a child to execute the command on the remote host using ssh. */
-#ifdef __uClinux__
+#ifndef __uClinux__
 	do_cmd_pid = vfork();
 #else
 	do_cmd_pid = fork();
 #endif /* __uClinux__ */
+
 	if (do_cmd_pid == 0) {
 		/* Child. */
 		close(pin[1]);
@@ -192,38 +216,39 @@
 		close(pout[1]);
 
 #ifndef __uClinux__
-		args.list[0] = ssh_program;
-		if (remuser != NULL) {
-			addargs(&args, "-l");
-			addargs(&args, "%s", remuser);
-		}
+		replacearg(&args, 0, "%s", ssh_program);
+		if (remuser != NULL)
+			addargs(&args, "-l%s", remuser);
 		addargs(&args, "%s", host);
 		addargs(&args, "%s", cmd);
-#endif
+#endif /* __uClinux__ */
 
 		execvp(ssh_program, args.list);
 		perror(ssh_program);
 		exit(1);
 	} else if (do_cmd_pid == -1) {
-		fprintf(stderr, "Fatal error: fork: %s\n", strerror(errno));
-		exit(1);
+		fatal("fork: %s", strerror(errno));
 	}
 
+
 #ifdef __uClinux__
 	/* clean up command */
 	/* pop cmd */
-	xfree(args->list[--args->num]);
-	args->list[args->num]=NULL;
+	xfree(args->list[args->num-1]);
+	args->list[args->num-1]=NULL;
+	args->num--;
 	/* pop host */
-	xfree(args->list[--args->num-1]);
-	args->list[args->num]=NULL;
+	xfree(args->list[args->num-1]);
+	args->list[args->num-1]=NULL;
+	args->num--;
 	/* pop user */
 	if (remuser != NULL) {
-		xfree(args->list[--args->num-1]);
-		args->list[args->num]=NULL;
+		xfree(args->list[args->num-1]);
+		args->list[args->num-1]=NULL;
+		args->num--;
 	}
 #endif /* __uClinux__ */
-	  
+
 	/* Parent.  Close the other side, and return the local side. */
 	close(pin[0]);
 	*fdout = pin[1];
@@ -236,7 +261,7 @@
 }
 
 typedef struct {
-	int cnt;
+	size_t cnt;
 	char *buf;
 } BUF;
 
@@ -267,6 +292,7 @@
 #if defined(DBMULTI_scp) && defined(DROPBEAR_MULTI)
 int scp_main(int argc, char **argv)
 #else
+int
 main(int argc, char **argv)
 #endif
 {
@@ -276,10 +302,15 @@
 	extern char *optarg;
 	extern int optind;
 
+	/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
+	sanitise_stdfd();
+
+	memset(&args, '\0', sizeof(args));
 	args.list = NULL;
-	addargs(&args, "ssh");		/* overwritten with ssh_program */
+	addargs(&args, "%s", ssh_program);
 	addargs(&args, "-x");
 	addargs(&args, "-oForwardAgent no");
+	addargs(&args, "-oPermitLocalCommand no");
 	addargs(&args, "-oClearAllForwardings yes");
 
 	fflag = tflag = 0;
@@ -309,7 +340,7 @@
 			speed = strtod(optarg, &endp);
 			if (speed <= 0 || *endp != '\0')
 				usage();
-			limitbw = speed * 1024;
+			limit_rate = speed * 1024;
 			break;
 		case 'p':
 			pflag = 1;
@@ -326,6 +357,7 @@
 			break;
 #ifdef PROGRESS_METER
 		case 'q':
+			addargs(&args, "-q");
 			showprogress = 0;
 			break;
 #endif
@@ -351,14 +383,11 @@
 	argc -= optind;
 	argv += optind;
 
-	if ((pwd = getpwuid(userid = getuid())) == NULL) {
-		fprintf(stderr, "unknown user %u", (u_int) userid);
-	}
+	if ((pwd = getpwuid(userid = getuid())) == NULL)
+		fatal("unknown user %u", (u_int) userid);
 
-#ifdef PROGRESS_METER
 	if (!isatty(STDERR_FILENO))
 		showprogress = 0;
-#endif
 
 	remin = STDIN_FILENO;
 	remout = STDOUT_FILENO;
@@ -392,9 +421,9 @@
 	if ((targ = colon(argv[argc - 1])))	/* Dest is remote host. */
 		toremote(targ, argc, argv);
 	else {
-		tolocal(argc, argv);	/* Dest is local host. */
 		if (targetshouldbedirectory)
 			verifydir(argv[argc - 1]);
+		tolocal(argc, argv);	/* Dest is local host. */
 	}
 	/*
 	 * Finally check the exit status of the ssh process, if one was forked
@@ -414,78 +443,76 @@
 	}
 	exit(errs != 0);
 }
-#endif /* DBMULTI stuff */
+#endif /* DBMULTI_scp stuff */
 
 void
 toremote(char *targ, int argc, char **argv)
 {
 	int i, len;
-	char *bp, *host, *src, *suser, *thost, *tuser;
+	char *bp, *host, *src, *suser, *thost, *tuser, *arg;
+	arglist alist;
+
+	memset(&alist, '\0', sizeof(alist));
+	alist.list = NULL;
 
 	*targ++ = 0;
 	if (*targ == 0)
 		targ = ".";
 
-	if ((thost = strrchr(argv[argc - 1], '@'))) {
+	arg = xstrdup(argv[argc - 1]);
+	if ((thost = strrchr(arg, '@'))) {
 		/* user@host */
 		*thost++ = 0;
-		tuser = argv[argc - 1];
+		tuser = arg;
 		if (*tuser == '\0')
 			tuser = NULL;
 	} else {
-		thost = argv[argc - 1];
+		thost = arg;
 		tuser = NULL;
 	}
 
+	if (tuser != NULL && !okname(tuser)) {
+		xfree(arg);
+		return;
+	}
+
 	for (i = 0; i < argc - 1; i++) {
 		src = colon(argv[i]);
 		if (src) {	/* remote to remote */
-			static char *ssh_options =
-			    "-x -o'ClearAllForwardings yes'";
+			freeargs(&alist);
+			addargs(&alist, "%s", ssh_program);
+			if (verbose_mode)
+				addargs(&alist, "-v");
+			addargs(&alist, "-x");
+			addargs(&alist, "-oClearAllForwardings yes");
+			addargs(&alist, "-n");
+
 			*src++ = 0;
 			if (*src == 0)
 				src = ".";
 			host = strrchr(argv[i], '@');
-			len = strlen(ssh_program) + strlen(argv[i]) +
-			    strlen(src) + (tuser ? strlen(tuser) : 0) +
-			    strlen(thost) + strlen(targ) +
-			    strlen(ssh_options) + CMDNEEDS + 20;
-			bp = xmalloc(len);
+
 			if (host) {
 				*host++ = 0;
 				host = cleanhostname(host);
 				suser = argv[i];
 				if (*suser == '\0')
 					suser = pwd->pw_name;
-				else if (!okname(suser)) {
-					xfree(bp);
-					continue;
-				}
-				if (tuser && !okname(tuser)) {
-					xfree(bp);
+				else if (!okname(suser))
 					continue;
-				}
-				snprintf(bp, len,
-				    "%s%s %s -n "
-				    "-l %s %s %s %s '%s%s%s:%s'",
-				    ssh_program, verbose_mode ? " -v" : "",
-				    ssh_options, suser, host, cmd, src,
-				    tuser ? tuser : "", tuser ? "@" : "",
-				    thost, targ);
+				addargs(&alist, "-l");
+				addargs(&alist, "%s", suser);
 			} else {
 				host = cleanhostname(argv[i]);
-				snprintf(bp, len,
-				    "exec %s%s %s -n %s "
-				    "%s %s '%s%s%s:%s'",
-				    ssh_program, verbose_mode ? " -v" : "",
-				    ssh_options, host, cmd, src,
-				    tuser ? tuser : "", tuser ? "@" : "",
-				    thost, targ);
 			}
-			if (verbose_mode)
-				fprintf(stderr, "Executing: %s\n", bp);
-			(void) system(bp);
-			(void) xfree(bp);
+			addargs(&alist, "%s", host);
+			addargs(&alist, "%s", cmd);
+			addargs(&alist, "%s", src);
+			addargs(&alist, "%s%s%s:%s",
+			    tuser ? tuser : "", tuser ? "@" : "",
+			    thost, targ);
+			if (do_local_cmd(&alist) != 0)
+				errs = 1;
 		} else {	/* local to remote */
 			if (remin == -1) {
 				len = strlen(targ) + CMDNEEDS + 20;
@@ -509,20 +536,23 @@
 {
 	int i, len;
 	char *bp, *host, *src, *suser;
+	arglist alist;
+
+	memset(&alist, '\0', sizeof(alist));
+	alist.list = NULL;
 
 	for (i = 0; i < argc - 1; i++) {
 		if (!(src = colon(argv[i]))) {	/* Local to local. */
-			len = strlen(_PATH_CP) + strlen(argv[i]) +
-			    strlen(argv[argc - 1]) + 20;
-			bp = xmalloc(len);
-			(void) snprintf(bp, len, "exec %s%s%s %s %s", _PATH_CP,
-			    iamrecursive ? " -r" : "", pflag ? " -p" : "",
-			    argv[i], argv[argc - 1]);
-			if (verbose_mode)
-				fprintf(stderr, "Executing: %s\n", bp);
-			if (system(bp))
+			freeargs(&alist);
+			addargs(&alist, "%s", _PATH_CP);
+			if (iamrecursive)
+				addargs(&alist, "-r");
+			if (pflag)
+				addargs(&alist, "-p");
+			addargs(&alist, "%s", argv[i]);
+			addargs(&alist, "%s", argv[argc-1]);
+			if (do_local_cmd(&alist))
 				++errs;
-			(void) xfree(bp);
 			continue;
 		}
 		*src++ = 0;
@@ -559,8 +589,9 @@
 	struct stat stb;
 	static BUF buffer;
 	BUF *bp;
-	off_t i, amt, result, statbytes;
-	int fd, haderr, indx;
+	off_t i, amt, statbytes;
+	size_t result;
+	int fd = -1, haderr, indx;
 	char *last, *name, buf[2048];
 	int len;
 
@@ -614,7 +645,7 @@
 #define	FILEMODEMASK	(S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
 		snprintf(buf, sizeof buf, "C%04o %lld %s\n",
 		    (u_int) (stb.st_mode & FILEMODEMASK),
-		    (int64_t)stb.st_size, last);
+		    (long long)stb.st_size, last);
 		if (verbose_mode) {
 			fprintf(stderr, "Sending file modes: %s", buf);
 		}
@@ -622,10 +653,13 @@
 		if (response() < 0)
 			goto next;
 		if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) {
-next:			(void) close(fd);
+next:			if (fd != -1) {
+				(void) close(fd);
+				fd = -1;
+			}
 			continue;
 		}
-#ifdef PROGRESS_METER
+#if PROGRESS_METER
 		if (showprogress)
 			start_progress_meter(curfile, stb.st_size, &statbytes);
 #endif
@@ -637,17 +671,17 @@
 			if (!haderr) {
 				result = atomicio(read, fd, bp->buf, amt);
 				if (result != amt)
-					haderr = result >= 0 ? EIO : errno;
+					haderr = errno;
 			}
 			if (haderr)
 				(void) atomicio(vwrite, remout, bp->buf, amt);
 			else {
 				result = atomicio(vwrite, remout, bp->buf, amt);
 				if (result != amt)
-					haderr = result >= 0 ? EIO : errno;
+					haderr = errno;
 				statbytes += result;
 			}
-			if (limitbw)
+			if (limit_rate)
 				bwlimit(amt);
 		}
 #ifdef PROGRESS_METER
@@ -655,8 +689,11 @@
 			stop_progress_meter();
 #endif
 
-		if (close(fd) < 0 && !haderr)
-			haderr = errno;
+		if (fd != -1) {
+			if (close(fd) < 0 && !haderr)
+				haderr = errno;
+			fd = -1;
+		}
 		if (!haderr)
 			(void) atomicio(vwrite, remout, "", 1);
 		else
@@ -723,7 +760,7 @@
 {
 	static struct timeval bwstart, bwend;
 	static int lamt, thresh = 16384;
-	uint64_t wait;
+	u_int64_t waitlen;
 	struct timespec ts, rm;
 
 	if (!timerisset(&bwstart)) {
@@ -741,10 +778,10 @@
 		return;
 
 	lamt *= 8;
-	wait = (double)1000000L * lamt / limitbw;
+	waitlen = (double)1000000L * lamt / limit_rate;
 
-	bwstart.tv_sec = wait / 1000000L;
-	bwstart.tv_usec = wait % 1000000L;
+	bwstart.tv_sec = waitlen / 1000000L;
+	bwstart.tv_usec = waitlen % 1000000L;
 
 	if (timercmp(&bwstart, &bwend, >)) {
 		timersub(&bwstart, &bwend, &bwend);
@@ -781,8 +818,9 @@
 		YES, NO, DISPLAYED
 	} wrerr;
 	BUF *bp;
-	off_t i, j;
-	int amt, count, exists, first, mask, mode, ofd, omode;
+	off_t i;
+	size_t j, count;
+	int amt, exists, first, mask, mode, ofd, omode;
 	off_t size, statbytes;
 	int setimes, targisdir, wrerrno = 0;
 	char ch, *cp, *np, *targ, *why, *vect[1], buf[2048];
@@ -790,7 +828,7 @@
 
 #define	atime	tv[0]
 #define	mtime	tv[1]
-#define	SCREWUP(str)	do { why = str; goto screwup; } while (0)
+#define	SCREWUP(str)	{ why = str; goto screwup; }
 
 	setimes = targisdir = 0;
 	mask = umask(0);
@@ -809,7 +847,7 @@
 		targisdir = 1;
 	for (first = 1;; first = 0) {
 		cp = buf;
-		if (atomicio(read, remin, cp, 1) <= 0)
+		if (atomicio(read, remin, cp, 1) != 1)
 			return;
 		if (*cp++ == '\n')
 			SCREWUP("unexpected <newline>");
@@ -819,6 +857,8 @@
 			*cp++ = ch;
 		} while (cp < &buf[sizeof(buf) - 1] && ch != '\n');
 		*cp = 0;
+		if (verbose_mode)
+			fprintf(stderr, "Sink: %s", buf);
 
 		if (buf[0] == '\01' || buf[0] == '\02') {
 			if (iamremote == 0)
@@ -882,9 +922,13 @@
 			size = size * 10 + (*cp++ - '0');
 		if (*cp++ != ' ')
 			SCREWUP("size not delimited");
+		if ((strchr(cp, '/') != NULL) || (strcmp(cp, "..") == 0)) {
+			run_err("error: unexpected filename: %s", cp);
+			exit(1);
+		}
 		if (targisdir) {
 			static char *namebuf;
-			static int cursize;
+			static size_t cursize;
 			size_t need;
 
 			need = strlen(targ) + strlen(cp) + 250;
@@ -903,6 +947,8 @@
 		exists = stat(np, &stb) == 0;
 		if (buf[0] == 'D') {
 			int mod_flag = pflag;
+			if (!iamrecursive)
+				SCREWUP("received directory without -r");
 			if (exists) {
 				if (!S_ISDIR(stb.st_mode)) {
 					errno = ENOTDIR;
@@ -956,11 +1002,8 @@
 				amt = size - i;
 			count += amt;
 			do {
-				j = read(remin, cp, amt);
-				if (j == -1 && (errno == EINTR ||
-				    errno == EAGAIN)) {
-					continue;
-				} else if (j <= 0) {
+				j = atomicio(read, remin, cp, amt);
+				if (j == 0) {
 					run_err("%s", j ? strerror(errno) :
 					    "dropped connection");
 					exit(1);
@@ -969,17 +1012,17 @@
 				cp += j;
 				statbytes += j;
 			} while (amt > 0);
-		
-			if (limitbw)
+
+			if (limit_rate)
 				bwlimit(4096);
 
 			if (count == bp->cnt) {
 				/* Keep reading so we stay sync'd up. */
 				if (wrerr == NO) {
-					j = atomicio(vwrite, ofd, bp->buf, count);
-					if (j != count) {
+					if (atomicio(vwrite, ofd, bp->buf,
+					    count) != count) {
 						wrerr = YES;
-						wrerrno = j >= 0 ? EIO : errno;
+						wrerrno = errno;
 					}
 				}
 				count = 0;
@@ -991,9 +1034,9 @@
 			stop_progress_meter();
 #endif
 		if (count != 0 && wrerr == NO &&
-		    (j = atomicio(vwrite, ofd, bp->buf, count)) != count) {
+		    atomicio(vwrite, ofd, bp->buf, count) != count) {
 			wrerr = YES;
-			wrerrno = j >= 0 ? EIO : errno;
+			wrerrno = errno;
 		}
 		if (wrerr == NO && ftruncate(ofd, size) != 0) {
 			run_err("%s: truncate: %s", np, strerror(errno));
@@ -1002,21 +1045,25 @@
 		if (pflag) {
 			if (exists || omode != mode)
 #ifdef HAVE_FCHMOD
-				if (fchmod(ofd, omode))
+				if (fchmod(ofd, omode)) {
 #else /* HAVE_FCHMOD */
-				if (chmod(np, omode))
+				if (chmod(np, omode)) {
 #endif /* HAVE_FCHMOD */
 					run_err("%s: set mode: %s",
 					    np, strerror(errno));
+					wrerr = DISPLAYED;
+				}
 		} else {
 			if (!exists && omode != mode)
 #ifdef HAVE_FCHMOD
-				if (fchmod(ofd, omode & ~mask))
+				if (fchmod(ofd, omode & ~mask)) {
 #else /* HAVE_FCHMOD */
-				if (chmod(np, omode & ~mask))
+				if (chmod(np, omode & ~mask)) {
 #endif /* HAVE_FCHMOD */
 					run_err("%s: set mode: %s",
 					    np, strerror(errno));
+					wrerr = DISPLAYED;
+				}
 		}
 		if (close(ofd) == -1) {
 			wrerr = YES;
@@ -1084,8 +1131,8 @@
 usage(void)
 {
 	(void) fprintf(stderr,
-	    "usage: scp [-pqrvBC1246] [-F config] [-S program] [-P port]\n"
-	    "           [-c cipher] [-i identity] [-l limit] [-o option]\n"
+	    "usage: scp [-1246BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n"
+	    "           [-l limit] [-o ssh_option] [-P port] [-S program]\n"
 	    "           [[user@]host1:]file1 [...] [[user@]host2:]file2\n");
 	exit(1);
 }
@@ -1126,7 +1173,7 @@
 		errno = ENOTDIR;
 	}
 	run_err("%s: %s", cp, strerror(errno));
-	exit(1);
+	killchild(0);
 }
 
 int
--- a/scpmisc.c	Wed Mar 08 13:48:05 2006 +0000
+++ b/scpmisc.c	Wed Mar 08 14:20:24 2006 +0000
@@ -140,12 +140,15 @@
 addargs(arglist *args, char *fmt, ...)
 {
 	va_list ap;
-	char buf[1024];
-	int nalloc;
+	char *cp;
+	u_int nalloc;
+	int r;
 
 	va_start(ap, fmt);
-	vsnprintf(buf, sizeof(buf), fmt, ap);
+	r = vasprintf(&cp, fmt, ap);
 	va_end(ap);
+	if (r == -1)
+		fatal("addargs: argument too long");
 
 	nalloc = args->nalloc;
 	if (args->list == NULL) {
@@ -156,6 +159,90 @@
 
 	args->list = xrealloc(args->list, nalloc * sizeof(char *));
 	args->nalloc = nalloc;
-	args->list[args->num++] = xstrdup(buf);
+	args->list[args->num++] = cp;
 	args->list[args->num] = NULL;
 }
+
+void
+replacearg(arglist *args, u_int which, char *fmt, ...)
+{
+	va_list ap;
+	char *cp;
+	int r;
+
+	va_start(ap, fmt);
+	r = vasprintf(&cp, fmt, ap);
+	va_end(ap);
+	if (r == -1)
+		fatal("replacearg: argument too long");
+
+	if (which >= args->num)
+		fatal("replacearg: tried to replace invalid arg %d >= %d",
+		    which, args->num);
+	xfree(args->list[which]);
+	args->list[which] = cp;
+}
+
+void
+freeargs(arglist *args)
+{
+	u_int i;
+
+	if (args->list != NULL) {
+		for (i = 0; i < args->num; i++)
+			xfree(args->list[i]);
+		xfree(args->list);
+		args->nalloc = args->num = 0;
+		args->list = NULL;
+	}
+}
+
+/*
+ * NB. duplicate __progname in case it is an alias for argv[0]
+ * Otherwise it may get clobbered by setproctitle()
+ */
+char *ssh_get_progname(char *argv0)
+{
+	char *p;
+
+	if (argv0 == NULL)
+		return ("unknown");	/* XXX */
+	p = strrchr(argv0, '/');
+	if (p == NULL)
+		p = argv0;
+	else
+		p++;
+
+	return (xstrdup(p));
+}
+
+void fatal(char* fmt,...)
+{
+	va_list args;
+	va_start(args, fmt);
+	vfprintf(stderr, fmt, args);
+	va_end(args);
+	exit(255);
+}
+
+void
+sanitise_stdfd(void)
+{
+	int nullfd, dupfd;
+
+	if ((nullfd = dupfd = open(_PATH_DEVNULL, O_RDWR)) == -1) {
+		fprintf(stderr, "Couldn't open /dev/null: %s", strerror(errno));
+		exit(1);
+	}
+	while (++dupfd <= 2) {
+		/* Only clobber closed fds */
+		if (fcntl(dupfd, F_GETFL, 0) >= 0)
+			continue;
+		if (dup2(nullfd, dupfd) == -1) {
+			fprintf(stderr, "dup2: %s", strerror(errno));
+			exit(1);
+		}
+	}
+	if (nullfd > 2)
+		close(nullfd);
+}
--- a/scpmisc.h	Wed Mar 08 13:48:05 2006 +0000
+++ b/scpmisc.h	Wed Mar 08 14:20:24 2006 +0000
@@ -34,6 +34,8 @@
 	int     nalloc;
 };
 void	 addargs(arglist *, char *, ...);
+void	 replacearg(arglist *, u_int, char *, ...);
+void	 freeargs(arglist *);
 
 /* from xmalloc.h */
 void	*xmalloc(size_t);
@@ -41,4 +43,6 @@
 void     xfree(void *);
 char	*xstrdup(const char *);
 
-
+char *ssh_get_progname(char *);
+void fatal(char* fmt,...);
+void sanitise_stdfd(void);