X-Git-Url: http://andersk.mit.edu/gitweb/openssh.git/blobdiff_plain/d0ac0d65c8565baeb8c559cece0de911a177d37d..HEAD:/scp.c diff --git a/scp.c b/scp.c index 1765a44e..09efb82a 100644 --- a/scp.c +++ b/scp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: scp.c,v 1.160 2007/08/06 19:16:06 sobrado Exp $ */ +/* $OpenBSD: scp.c,v 1.165 2009/12/20 07:28:36 guenther Exp $ */ /* * scp - secure remote copy. This is basically patched BSD rcp which * uses ssh to do the data transfer (instead of using rcmd). @@ -78,6 +78,13 @@ #ifdef HAVE_SYS_STAT_H # include #endif +#ifdef HAVE_POLL_H +#include +#else +# ifdef HAVE_SYS_POLL_H +# include +# endif +#endif #ifdef HAVE_SYS_TIME_H # include #endif @@ -109,6 +116,8 @@ extern char *__progname; +#define COPY_BUFLEN 16384 + int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout); void bwlimit(int); @@ -235,8 +244,11 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) close(pout[1]); replacearg(&args, 0, "%s", ssh_program); - if (remuser != NULL) - addargs(&args, "-l%s", remuser); + if (remuser != NULL) { + addargs(&args, "-l"); + addargs(&args, "%s", remuser); + } + addargs(&args, "--"); addargs(&args, "%s", host); addargs(&args, "%s", cmd); @@ -282,6 +294,7 @@ void sink(int, char *[]); void source(int, char *[]); void tolocal(int, char *[]); void toremote(char *, int, char *[]); +size_t scpio(ssize_t (*)(int, void *, size_t), int, void *, size_t, off_t *); void usage(void); int @@ -327,10 +340,12 @@ main(int argc, char **argv) case 'c': case 'i': case 'F': - addargs(&args, "-%c%s", ch, optarg); + addargs(&args, "-%c", ch); + addargs(&args, "%s", optarg); break; case 'P': - addargs(&args, "-p%s", optarg); + addargs(&args, "-p"); + addargs(&args, "%s", optarg); break; case 'B': addargs(&args, "-oBatchmode yes"); @@ -424,7 +439,7 @@ main(int argc, char **argv) } /* * Finally check the exit status of the ssh process, if one was forked - * and no error has occured yet + * and no error has occurred yet */ if (do_cmd_pid != -1 && errs == 0) { if (remin != -1) @@ -441,6 +456,43 @@ main(int argc, char **argv) exit(errs != 0); } +/* + * atomicio-like wrapper that also applies bandwidth limits and updates + * the progressmeter counter. + */ +size_t +scpio(ssize_t (*f)(int, void *, size_t), int fd, void *_p, size_t l, off_t *c) +{ + u_char *p = (u_char *)_p; + size_t offset; + ssize_t r; + struct pollfd pfd; + + pfd.fd = fd; + pfd.events = f == read ? POLLIN : POLLOUT; + for (offset = 0; offset < l;) { + r = f(fd, p + offset, l - offset); + if (r == 0) { + errno = EPIPE; + return offset; + } + if (r < 0) { + if (errno == EINTR) + continue; + if (errno == EAGAIN || errno == EWOULDBLOCK) { + (void)poll(&pfd, 1, -1); /* Ignore errors */ + continue; + } + return offset; + } + offset += (size_t)r; + *c += (off_t)r; + if (limit_rate) + bwlimit(r); + } + return offset; +} + void toremote(char *targ, int argc, char **argv) { @@ -501,6 +553,7 @@ toremote(char *targ, int argc, char **argv) } else { host = cleanhostname(argv[i]); } + addargs(&alist, "--"); addargs(&alist, "%s", host); addargs(&alist, "%s", cmd); addargs(&alist, "%s", src); @@ -511,7 +564,7 @@ toremote(char *targ, int argc, char **argv) errs = 1; } else { /* local to remote */ if (remin == -1) { - xasprintf(&bp, "%s -t %s", cmd, targ); + xasprintf(&bp, "%s -t -- %s", cmd, targ); host = cleanhostname(thost); if (do_cmd(host, tuser, bp, &remin, &remout) < 0) @@ -544,6 +597,7 @@ tolocal(int argc, char **argv) addargs(&alist, "-r"); if (pflag) addargs(&alist, "-p"); + addargs(&alist, "--"); addargs(&alist, "%s", argv[i]); addargs(&alist, "%s", argv[argc-1]); if (do_local_cmd(&alist)) @@ -563,7 +617,7 @@ tolocal(int argc, char **argv) suser = pwd->pw_name; } host = cleanhostname(host); - xasprintf(&bp, "%s -f %s", cmd, src); + xasprintf(&bp, "%s -f -- %s", cmd, src); if (do_cmd(host, suser, bp, &remin, &remout) < 0) { (void) xfree(bp); ++errs; @@ -582,8 +636,8 @@ source(int argc, char **argv) struct stat stb; static BUF buffer; BUF *bp; - off_t i, amt, statbytes; - size_t result; + off_t i, statbytes; + size_t amt; int fd = -1, haderr, indx; char *last, *name, buf[2048], encname[MAXPATHLEN]; int len; @@ -604,6 +658,10 @@ source(int argc, char **argv) syserr: run_err("%s: %s", name, strerror(errno)); goto next; } + if (stb.st_size < 0) { + run_err("%s: %s", name, "Negative file size"); + goto next; + } unset_nonblock(fd); switch (stb.st_mode & S_IFMT) { case S_IFREG: @@ -629,8 +687,14 @@ syserr: run_err("%s: %s", name, strerror(errno)); * versions expecting microseconds. */ (void) snprintf(buf, sizeof buf, "T%lu 0 %lu 0\n", - (u_long) stb.st_mtime, - (u_long) stb.st_atime); + (u_long) (stb.st_mtime < 0 ? 0 : stb.st_mtime), + (u_long) (stb.st_atime < 0 ? 0 : stb.st_atime)); + if (verbose_mode) { + fprintf(stderr, "File mtime %ld atime %ld\n", + (long)stb.st_mtime, (long)stb.st_atime); + fprintf(stderr, "Sending file timestamps: %s", + buf); + } (void) atomicio(vwrite, remout, buf, strlen(buf)); if (response() < 0) goto next; @@ -645,7 +709,7 @@ syserr: run_err("%s: %s", name, strerror(errno)); (void) atomicio(vwrite, remout, buf, strlen(buf)); if (response() < 0) goto next; - if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) { + if ((bp = allocbuf(&buffer, fd, COPY_BUFLEN)) == NULL) { next: if (fd != -1) { (void) close(fd); fd = -1; @@ -654,27 +718,25 @@ next: if (fd != -1) { } if (showprogress) start_progress_meter(curfile, stb.st_size, &statbytes); - /* Keep writing after an error so that we stay sync'd up. */ + set_nonblock(remout); for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { amt = bp->cnt; - if (i + amt > stb.st_size) + if (i + (off_t)amt > stb.st_size) amt = stb.st_size - i; if (!haderr) { - result = atomicio(read, fd, bp->buf, amt); - if (result != amt) + if (atomicio(read, fd, bp->buf, amt) != amt) haderr = errno; } - if (haderr) - (void) atomicio(vwrite, remout, bp->buf, amt); - else { - result = atomicio(vwrite, remout, bp->buf, amt); - if (result != amt) - haderr = errno; - statbytes += result; + /* Keep writing after error to retain sync */ + if (haderr) { + (void)atomicio(vwrite, remout, bp->buf, amt); + continue; } - if (limit_rate) - bwlimit(amt); + if (scpio(vwrite, remout, bp->buf, amt, + &statbytes) != amt) + haderr = errno; } + unset_nonblock(remout); if (showprogress) stop_progress_meter(); @@ -780,10 +842,10 @@ bwlimit(int amount) thresh /= 2; if (thresh < 2048) thresh = 2048; - } else if (bwend.tv_usec < 100) { + } else if (bwend.tv_usec < 10000) { thresh *= 2; - if (thresh > 32768) - thresh = 32768; + if (thresh > COPY_BUFLEN * 4) + thresh = COPY_BUFLEN * 4; } TIMEVAL_TO_TIMESPEC(&bwend, &ts); @@ -974,7 +1036,7 @@ bad: run_err("%s: %s", np, strerror(errno)); continue; } (void) atomicio(vwrite, remout, "", 1); - if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) { + if ((bp = allocbuf(&buffer, ofd, COPY_BUFLEN)) == NULL) { (void) close(ofd); continue; } @@ -984,26 +1046,24 @@ bad: run_err("%s: %s", np, strerror(errno)); statbytes = 0; if (showprogress) start_progress_meter(curfile, size, &statbytes); - for (count = i = 0; i < size; i += 4096) { - amt = 4096; + set_nonblock(remin); + for (count = i = 0; i < size; i += bp->cnt) { + amt = bp->cnt; if (i + amt > size) amt = size - i; count += amt; do { - j = atomicio(read, remin, cp, amt); + j = scpio(read, remin, cp, amt, &statbytes); if (j == 0) { - run_err("%s", j ? strerror(errno) : + run_err("%s", j != EPIPE ? + strerror(errno) : "dropped connection"); exit(1); } amt -= j; cp += j; - statbytes += j; } while (amt > 0); - if (limit_rate) - bwlimit(4096); - if (count == bp->cnt) { /* Keep reading so we stay sync'd up. */ if (wrerr == NO) { @@ -1017,6 +1077,7 @@ bad: run_err("%s: %s", np, strerror(errno)); cp = bp->buf; } } + unset_nonblock(remin); if (showprogress) stop_progress_meter(); if (count != 0 && wrerr == NO &&