diff options
Diffstat (limited to 'scp.c')
-rw-r--r-- | scp.c | 116 |
1 files changed, 85 insertions, 31 deletions
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: scp.c,v 1.160 2007/08/06 19:16:06 sobrado Exp $ */ | 1 | /* $OpenBSD: scp.c,v 1.163 2008/06/13 18:55:22 dtucker Exp $ */ |
2 | /* | 2 | /* |
3 | * scp - secure remote copy. This is basically patched BSD rcp which | 3 | * scp - secure remote copy. This is basically patched BSD rcp which |
4 | * uses ssh to do the data transfer (instead of using rcmd). | 4 | * uses ssh to do the data transfer (instead of using rcmd). |
@@ -78,6 +78,13 @@ | |||
78 | #ifdef HAVE_SYS_STAT_H | 78 | #ifdef HAVE_SYS_STAT_H |
79 | # include <sys/stat.h> | 79 | # include <sys/stat.h> |
80 | #endif | 80 | #endif |
81 | #ifdef HAVE_POLL_H | ||
82 | #include <poll.h> | ||
83 | #else | ||
84 | # ifdef HAVE_SYS_POLL_H | ||
85 | # include <sys/poll.h> | ||
86 | # endif | ||
87 | #endif | ||
81 | #ifdef HAVE_SYS_TIME_H | 88 | #ifdef HAVE_SYS_TIME_H |
82 | # include <sys/time.h> | 89 | # include <sys/time.h> |
83 | #endif | 90 | #endif |
@@ -109,6 +116,8 @@ | |||
109 | 116 | ||
110 | extern char *__progname; | 117 | extern char *__progname; |
111 | 118 | ||
119 | #define COPY_BUFLEN 16384 | ||
120 | |||
112 | int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout); | 121 | int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout); |
113 | 122 | ||
114 | void bwlimit(int); | 123 | void bwlimit(int); |
@@ -290,6 +299,7 @@ void sink(int, char *[]); | |||
290 | void source(int, char *[]); | 299 | void source(int, char *[]); |
291 | void tolocal(int, char *[]); | 300 | void tolocal(int, char *[]); |
292 | void toremote(char *, int, char *[]); | 301 | void toremote(char *, int, char *[]); |
302 | size_t scpio(ssize_t (*)(int, void *, size_t), int, void *, size_t, off_t *); | ||
293 | void usage(void); | 303 | void usage(void); |
294 | 304 | ||
295 | int | 305 | int |
@@ -449,6 +459,43 @@ main(int argc, char **argv) | |||
449 | exit(errs != 0); | 459 | exit(errs != 0); |
450 | } | 460 | } |
451 | 461 | ||
462 | /* | ||
463 | * atomicio-like wrapper that also applies bandwidth limits and updates | ||
464 | * the progressmeter counter. | ||
465 | */ | ||
466 | size_t | ||
467 | scpio(ssize_t (*f)(int, void *, size_t), int fd, void *_p, size_t l, off_t *c) | ||
468 | { | ||
469 | u_char *p = (u_char *)_p; | ||
470 | size_t offset; | ||
471 | ssize_t r; | ||
472 | struct pollfd pfd; | ||
473 | |||
474 | pfd.fd = fd; | ||
475 | pfd.events = f == read ? POLLIN : POLLOUT; | ||
476 | for (offset = 0; offset < l;) { | ||
477 | r = f(fd, p + offset, l - offset); | ||
478 | if (r == 0) { | ||
479 | errno = EPIPE; | ||
480 | return offset; | ||
481 | } | ||
482 | if (r < 0) { | ||
483 | if (errno == EINTR) | ||
484 | continue; | ||
485 | if (errno == EAGAIN || errno == EWOULDBLOCK) { | ||
486 | (void)poll(&pfd, 1, -1); /* Ignore errors */ | ||
487 | continue; | ||
488 | } | ||
489 | return offset; | ||
490 | } | ||
491 | offset += (size_t)r; | ||
492 | *c += (off_t)r; | ||
493 | if (limit_rate) | ||
494 | bwlimit(r); | ||
495 | } | ||
496 | return offset; | ||
497 | } | ||
498 | |||
452 | void | 499 | void |
453 | toremote(char *targ, int argc, char **argv) | 500 | toremote(char *targ, int argc, char **argv) |
454 | { | 501 | { |
@@ -590,8 +637,8 @@ source(int argc, char **argv) | |||
590 | struct stat stb; | 637 | struct stat stb; |
591 | static BUF buffer; | 638 | static BUF buffer; |
592 | BUF *bp; | 639 | BUF *bp; |
593 | off_t i, amt, statbytes; | 640 | off_t i, statbytes; |
594 | size_t result; | 641 | size_t amt; |
595 | int fd = -1, haderr, indx; | 642 | int fd = -1, haderr, indx; |
596 | char *last, *name, buf[2048], encname[MAXPATHLEN]; | 643 | char *last, *name, buf[2048], encname[MAXPATHLEN]; |
597 | int len; | 644 | int len; |
@@ -612,6 +659,10 @@ source(int argc, char **argv) | |||
612 | syserr: run_err("%s: %s", name, strerror(errno)); | 659 | syserr: run_err("%s: %s", name, strerror(errno)); |
613 | goto next; | 660 | goto next; |
614 | } | 661 | } |
662 | if (stb.st_size < 0) { | ||
663 | run_err("%s: %s", name, "Negative file size"); | ||
664 | goto next; | ||
665 | } | ||
615 | unset_nonblock(fd); | 666 | unset_nonblock(fd); |
616 | switch (stb.st_mode & S_IFMT) { | 667 | switch (stb.st_mode & S_IFMT) { |
617 | case S_IFREG: | 668 | case S_IFREG: |
@@ -637,8 +688,14 @@ syserr: run_err("%s: %s", name, strerror(errno)); | |||
637 | * versions expecting microseconds. | 688 | * versions expecting microseconds. |
638 | */ | 689 | */ |
639 | (void) snprintf(buf, sizeof buf, "T%lu 0 %lu 0\n", | 690 | (void) snprintf(buf, sizeof buf, "T%lu 0 %lu 0\n", |
640 | (u_long) stb.st_mtime, | 691 | (u_long) (stb.st_mtime < 0 ? 0 : stb.st_mtime), |
641 | (u_long) stb.st_atime); | 692 | (u_long) (stb.st_atime < 0 ? 0 : stb.st_atime)); |
693 | if (verbose_mode) { | ||
694 | fprintf(stderr, "File mtime %ld atime %ld\n", | ||
695 | (long)stb.st_mtime, (long)stb.st_atime); | ||
696 | fprintf(stderr, "Sending file timestamps: %s", | ||
697 | buf); | ||
698 | } | ||
642 | (void) atomicio(vwrite, remout, buf, strlen(buf)); | 699 | (void) atomicio(vwrite, remout, buf, strlen(buf)); |
643 | if (response() < 0) | 700 | if (response() < 0) |
644 | goto next; | 701 | goto next; |
@@ -653,7 +710,7 @@ syserr: run_err("%s: %s", name, strerror(errno)); | |||
653 | (void) atomicio(vwrite, remout, buf, strlen(buf)); | 710 | (void) atomicio(vwrite, remout, buf, strlen(buf)); |
654 | if (response() < 0) | 711 | if (response() < 0) |
655 | goto next; | 712 | goto next; |
656 | if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) { | 713 | if ((bp = allocbuf(&buffer, fd, COPY_BUFLEN)) == NULL) { |
657 | next: if (fd != -1) { | 714 | next: if (fd != -1) { |
658 | (void) close(fd); | 715 | (void) close(fd); |
659 | fd = -1; | 716 | fd = -1; |
@@ -662,27 +719,25 @@ next: if (fd != -1) { | |||
662 | } | 719 | } |
663 | if (showprogress) | 720 | if (showprogress) |
664 | start_progress_meter(curfile, stb.st_size, &statbytes); | 721 | start_progress_meter(curfile, stb.st_size, &statbytes); |
665 | /* Keep writing after an error so that we stay sync'd up. */ | 722 | set_nonblock(remout); |
666 | for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { | 723 | for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { |
667 | amt = bp->cnt; | 724 | amt = bp->cnt; |
668 | if (i + amt > stb.st_size) | 725 | if (i + (off_t)amt > stb.st_size) |
669 | amt = stb.st_size - i; | 726 | amt = stb.st_size - i; |
670 | if (!haderr) { | 727 | if (!haderr) { |
671 | result = atomicio(read, fd, bp->buf, amt); | 728 | if (atomicio(read, fd, bp->buf, amt) != amt) |
672 | if (result != amt) | ||
673 | haderr = errno; | 729 | haderr = errno; |
674 | } | 730 | } |
675 | if (haderr) | 731 | /* Keep writing after error to retain sync */ |
676 | (void) atomicio(vwrite, remout, bp->buf, amt); | 732 | if (haderr) { |
677 | else { | 733 | (void)atomicio(vwrite, remout, bp->buf, amt); |
678 | result = atomicio(vwrite, remout, bp->buf, amt); | 734 | continue; |
679 | if (result != amt) | ||
680 | haderr = errno; | ||
681 | statbytes += result; | ||
682 | } | 735 | } |
683 | if (limit_rate) | 736 | if (scpio(vwrite, remout, bp->buf, amt, |
684 | bwlimit(amt); | 737 | &statbytes) != amt) |
738 | haderr = errno; | ||
685 | } | 739 | } |
740 | unset_nonblock(remout); | ||
686 | if (showprogress) | 741 | if (showprogress) |
687 | stop_progress_meter(); | 742 | stop_progress_meter(); |
688 | 743 | ||
@@ -788,10 +843,10 @@ bwlimit(int amount) | |||
788 | thresh /= 2; | 843 | thresh /= 2; |
789 | if (thresh < 2048) | 844 | if (thresh < 2048) |
790 | thresh = 2048; | 845 | thresh = 2048; |
791 | } else if (bwend.tv_usec < 100) { | 846 | } else if (bwend.tv_usec < 10000) { |
792 | thresh *= 2; | 847 | thresh *= 2; |
793 | if (thresh > 32768) | 848 | if (thresh > COPY_BUFLEN * 4) |
794 | thresh = 32768; | 849 | thresh = COPY_BUFLEN * 4; |
795 | } | 850 | } |
796 | 851 | ||
797 | TIMEVAL_TO_TIMESPEC(&bwend, &ts); | 852 | TIMEVAL_TO_TIMESPEC(&bwend, &ts); |
@@ -982,7 +1037,7 @@ bad: run_err("%s: %s", np, strerror(errno)); | |||
982 | continue; | 1037 | continue; |
983 | } | 1038 | } |
984 | (void) atomicio(vwrite, remout, "", 1); | 1039 | (void) atomicio(vwrite, remout, "", 1); |
985 | if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) { | 1040 | if ((bp = allocbuf(&buffer, ofd, COPY_BUFLEN)) == NULL) { |
986 | (void) close(ofd); | 1041 | (void) close(ofd); |
987 | continue; | 1042 | continue; |
988 | } | 1043 | } |
@@ -992,26 +1047,24 @@ bad: run_err("%s: %s", np, strerror(errno)); | |||
992 | statbytes = 0; | 1047 | statbytes = 0; |
993 | if (showprogress) | 1048 | if (showprogress) |
994 | start_progress_meter(curfile, size, &statbytes); | 1049 | start_progress_meter(curfile, size, &statbytes); |
995 | for (count = i = 0; i < size; i += 4096) { | 1050 | set_nonblock(remin); |
996 | amt = 4096; | 1051 | for (count = i = 0; i < size; i += bp->cnt) { |
1052 | amt = bp->cnt; | ||
997 | if (i + amt > size) | 1053 | if (i + amt > size) |
998 | amt = size - i; | 1054 | amt = size - i; |
999 | count += amt; | 1055 | count += amt; |
1000 | do { | 1056 | do { |
1001 | j = atomicio(read, remin, cp, amt); | 1057 | j = scpio(read, remin, cp, amt, &statbytes); |
1002 | if (j == 0) { | 1058 | if (j == 0) { |
1003 | run_err("%s", j ? strerror(errno) : | 1059 | run_err("%s", j != EPIPE ? |
1060 | strerror(errno) : | ||
1004 | "dropped connection"); | 1061 | "dropped connection"); |
1005 | exit(1); | 1062 | exit(1); |
1006 | } | 1063 | } |
1007 | amt -= j; | 1064 | amt -= j; |
1008 | cp += j; | 1065 | cp += j; |
1009 | statbytes += j; | ||
1010 | } while (amt > 0); | 1066 | } while (amt > 0); |
1011 | 1067 | ||
1012 | if (limit_rate) | ||
1013 | bwlimit(4096); | ||
1014 | |||
1015 | if (count == bp->cnt) { | 1068 | if (count == bp->cnt) { |
1016 | /* Keep reading so we stay sync'd up. */ | 1069 | /* Keep reading so we stay sync'd up. */ |
1017 | if (wrerr == NO) { | 1070 | if (wrerr == NO) { |
@@ -1025,6 +1078,7 @@ bad: run_err("%s: %s", np, strerror(errno)); | |||
1025 | cp = bp->buf; | 1078 | cp = bp->buf; |
1026 | } | 1079 | } |
1027 | } | 1080 | } |
1081 | unset_nonblock(remin); | ||
1028 | if (showprogress) | 1082 | if (showprogress) |
1029 | stop_progress_meter(); | 1083 | stop_progress_meter(); |
1030 | if (count != 0 && wrerr == NO && | 1084 | if (count != 0 && wrerr == NO && |