1 /* 2 * 3 * scp - secure remote copy. This is basically patched BSD rcp which uses ssh 4 * to do the data transfer (instead of using rcmd). 5 * 6 * NOTE: This version should NOT be suid root. (This uses ssh to do the transfer 7 * and ssh has the necessary privileges.) 8 * 9 * 1995 Timo Rinne <tri@iki.fi>, Tatu Ylonen <ylo@cs.hut.fi> 10 * 11 */ 12 13 /* 14 * Copyright (c) 1983, 1990, 1992, 1993, 1995 15 * The Regents of the University of California. All rights reserved. 16 * 17 * Redistribution and use in source and binary forms, with or without 18 * modification, are permitted provided that the following conditions 19 * are met: 20 * 1. Redistributions of source code must retain the above copyright 21 * notice, this list of conditions and the following disclaimer. 22 * 2. Redistributions in binary form must reproduce the above copyright 23 * notice, this list of conditions and the following disclaimer in the 24 * documentation and/or other materials provided with the distribution. 25 * 3. All advertising materials mentioning features or use of this software 26 * must display the following acknowledgement: 27 * This product includes software developed by the University of 28 * California, Berkeley and its contributors. 29 * 4. Neither the name of the University nor the names of its contributors 30 * may be used to endorse or promote products derived from this software 31 * without specific prior written permission. 32 * 33 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 34 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 35 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 36 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 37 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 38 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 39 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 40 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 41 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 42 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 43 * SUCH DAMAGE. 44 * 45 */ 46 47 #include "includes.h" 48 RCSID("$Id: scp.c,v 1.26 2000/03/16 20:56:14 markus Exp $"); 49 50 #include "ssh.h" 51 #include "xmalloc.h" 52 #include <utime.h> 53 54 #define _PATH_CP "cp" 55 56 /* For progressmeter() -- number of seconds before xfer considered "stalled" */ 57 #define STALLTIME 5 58 59 /* Visual statistics about files as they are transferred. */ 60 void progressmeter(int); 61 62 /* Returns width of the terminal (for progress meter calculations). */ 63 int getttywidth(void); 64 65 /* Time a transfer started. */ 66 static struct timeval start; 67 68 /* Number of bytes of current file transferred so far. */ 69 volatile unsigned long statbytes; 70 71 /* Total size of current file. */ 72 off_t totalbytes = 0; 73 74 /* Name of current file being transferred. */ 75 char *curfile; 76 77 /* This is set to non-zero if IPv4 is desired. */ 78 int IPv4 = 0; 79 80 /* This is set to non-zero if IPv6 is desired. */ 81 int IPv6 = 0; 82 83 /* This is set to non-zero to enable verbose mode. */ 84 int verbose_mode = 0; 85 86 /* This is set to non-zero if compression is desired. */ 87 int compress = 0; 88 89 /* This is set to zero if the progressmeter is not desired. */ 90 int showprogress = 1; 91 92 /* This is set to non-zero if running in batch mode (that is, password 93 and passphrase queries are not allowed). */ 94 int batchmode = 0; 95 96 /* This is set to the cipher type string if given on the command line. */ 97 char *cipher = NULL; 98 99 /* This is set to the RSA authentication identity file name if given on 100 the command line. */ 101 char *identity = NULL; 102 103 /* This is the port to use in contacting the remote site (is non-NULL). */ 104 char *port = NULL; 105 106 /* 107 * This function executes the given command as the specified user on the 108 * given host. This returns < 0 if execution fails, and >= 0 otherwise. This 109 * assigns the input and output file descriptors on success. 110 */ 111 112 int 113 do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) 114 { 115 int pin[2], pout[2], reserved[2]; 116 117 if (verbose_mode) 118 fprintf(stderr, "Executing: host %s, user %s, command %s\n", 119 host, remuser ? remuser : "(unspecified)", cmd); 120 121 /* 122 * Reserve two descriptors so that the real pipes won't get 123 * descriptors 0 and 1 because that will screw up dup2 below. 124 */ 125 pipe(reserved); 126 127 /* Create a socket pair for communicating with ssh. */ 128 if (pipe(pin) < 0) 129 fatal("pipe: %s", strerror(errno)); 130 if (pipe(pout) < 0) 131 fatal("pipe: %s", strerror(errno)); 132 133 /* Free the reserved descriptors. */ 134 close(reserved[0]); 135 close(reserved[1]); 136 137 /* For a child to execute the command on the remote host using ssh. */ 138 if (fork() == 0) { 139 char *args[100]; 140 unsigned int i; 141 142 /* Child. */ 143 close(pin[1]); 144 close(pout[0]); 145 dup2(pin[0], 0); 146 dup2(pout[1], 1); 147 close(pin[0]); 148 close(pout[1]); 149 150 i = 0; 151 args[i++] = SSH_PROGRAM; 152 args[i++] = "-x"; 153 args[i++] = "-oFallBackToRsh no"; 154 if (IPv4) 155 args[i++] = "-4"; 156 if (IPv6) 157 args[i++] = "-6"; 158 args[i++] = "-oFallBackToRsh no"; 159 if (verbose_mode) 160 args[i++] = "-v"; 161 if (compress) 162 args[i++] = "-C"; 163 if (batchmode) 164 args[i++] = "-oBatchMode yes"; 165 if (cipher != NULL) { 166 args[i++] = "-c"; 167 args[i++] = cipher; 168 } 169 if (identity != NULL) { 170 args[i++] = "-i"; 171 args[i++] = identity; 172 } 173 if (port != NULL) { 174 args[i++] = "-p"; 175 args[i++] = port; 176 } 177 if (remuser != NULL) { 178 args[i++] = "-l"; 179 args[i++] = remuser; 180 } 181 args[i++] = host; 182 args[i++] = cmd; 183 args[i++] = NULL; 184 185 execvp(SSH_PROGRAM, args); 186 perror(SSH_PROGRAM); 187 exit(1); 188 } 189 /* Parent. Close the other side, and return the local side. */ 190 close(pin[0]); 191 *fdout = pin[1]; 192 close(pout[1]); 193 *fdin = pout[0]; 194 return 0; 195 } 196 197 void 198 fatal(const char *fmt,...) 199 { 200 va_list ap; 201 char buf[1024]; 202 203 va_start(ap, fmt); 204 vsnprintf(buf, sizeof(buf), fmt, ap); 205 va_end(ap); 206 fprintf(stderr, "%s\n", buf); 207 exit(255); 208 } 209 210 /* This stuff used to be in BSD rcp extern.h. */ 211 212 typedef struct { 213 int cnt; 214 char *buf; 215 } BUF; 216 217 extern int iamremote; 218 219 BUF *allocbuf(BUF *, int, int); 220 char *colon(char *); 221 void lostconn(int); 222 void nospace(void); 223 int okname(char *); 224 void run_err(const char *,...); 225 void verifydir(char *); 226 227 /* Stuff from BSD rcp.c continues. */ 228 229 struct passwd *pwd; 230 uid_t userid; 231 int errs, remin, remout; 232 int pflag, iamremote, iamrecursive, targetshouldbedirectory; 233 234 #define CMDNEEDS 64 235 char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ 236 237 int response(void); 238 void rsource(char *, struct stat *); 239 void sink(int, char *[]); 240 void source(int, char *[]); 241 void tolocal(int, char *[]); 242 void toremote(char *, int, char *[]); 243 void usage(void); 244 245 int 246 main(argc, argv) 247 int argc; 248 char *argv[]; 249 { 250 int ch, fflag, tflag; 251 char *targ; 252 extern char *optarg; 253 extern int optind; 254 255 fflag = tflag = 0; 256 while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q46")) != EOF) 257 switch (ch) { 258 /* User-visible flags. */ 259 case '4': 260 IPv4 = 1; 261 break; 262 case '6': 263 IPv6 = 1; 264 break; 265 case 'p': 266 pflag = 1; 267 break; 268 case 'P': 269 port = optarg; 270 break; 271 case 'r': 272 iamrecursive = 1; 273 break; 274 /* Server options. */ 275 case 'd': 276 targetshouldbedirectory = 1; 277 break; 278 case 'f': /* "from" */ 279 iamremote = 1; 280 fflag = 1; 281 break; 282 case 't': /* "to" */ 283 iamremote = 1; 284 tflag = 1; 285 break; 286 case 'c': 287 cipher = optarg; 288 break; 289 case 'i': 290 identity = optarg; 291 break; 292 case 'v': 293 verbose_mode = 1; 294 break; 295 case 'B': 296 batchmode = 1; 297 break; 298 case 'C': 299 compress = 1; 300 break; 301 case 'q': 302 showprogress = 0; 303 break; 304 case '?': 305 default: 306 usage(); 307 } 308 argc -= optind; 309 argv += optind; 310 311 if ((pwd = getpwuid(userid = getuid())) == NULL) 312 fatal("unknown user %d", (int) userid); 313 314 if (!isatty(STDERR_FILENO)) 315 showprogress = 0; 316 317 remin = STDIN_FILENO; 318 remout = STDOUT_FILENO; 319 320 if (fflag) { 321 /* Follow "protocol", send data. */ 322 (void) response(); 323 source(argc, argv); 324 exit(errs != 0); 325 } 326 if (tflag) { 327 /* Receive data. */ 328 sink(argc, argv); 329 exit(errs != 0); 330 } 331 if (argc < 2) 332 usage(); 333 if (argc > 2) 334 targetshouldbedirectory = 1; 335 336 remin = remout = -1; 337 /* Command to be executed on remote system using "ssh". */ 338 (void) sprintf(cmd, "scp%s%s%s%s", verbose_mode ? " -v" : "", 339 iamrecursive ? " -r" : "", pflag ? " -p" : "", 340 targetshouldbedirectory ? " -d" : ""); 341 342 (void) signal(SIGPIPE, lostconn); 343 344 if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ 345 toremote(targ, argc, argv); 346 else { 347 tolocal(argc, argv); /* Dest is local host. */ 348 if (targetshouldbedirectory) 349 verifydir(argv[argc - 1]); 350 } 351 exit(errs != 0); 352 } 353 354 char * 355 cleanhostname(host) 356 char *host; 357 { 358 if (*host == '[' && host[strlen(host) - 1] == ']') { 359 host[strlen(host) - 1] = '\0'; 360 return (host + 1); 361 } else 362 return host; 363 } 364 365 void 366 toremote(targ, argc, argv) 367 char *targ, *argv[]; 368 int argc; 369 { 370 int i, len; 371 char *bp, *host, *src, *suser, *thost, *tuser; 372 373 *targ++ = 0; 374 if (*targ == 0) 375 targ = "."; 376 377 if ((thost = strchr(argv[argc - 1], '@'))) { 378 /* user@host */ 379 *thost++ = 0; 380 tuser = argv[argc - 1]; 381 if (*tuser == '\0') 382 tuser = NULL; 383 else if (!okname(tuser)) 384 exit(1); 385 } else { 386 thost = argv[argc - 1]; 387 tuser = NULL; 388 } 389 390 for (i = 0; i < argc - 1; i++) { 391 src = colon(argv[i]); 392 if (src) { /* remote to remote */ 393 *src++ = 0; 394 if (*src == 0) 395 src = "."; 396 host = strchr(argv[i], '@'); 397 len = strlen(SSH_PROGRAM) + strlen(argv[i]) + 398 strlen(src) + (tuser ? strlen(tuser) : 0) + 399 strlen(thost) + strlen(targ) + CMDNEEDS + 32; 400 bp = xmalloc(len); 401 if (host) { 402 *host++ = 0; 403 host = cleanhostname(host); 404 suser = argv[i]; 405 if (*suser == '\0') 406 suser = pwd->pw_name; 407 else if (!okname(suser)) 408 continue; 409 (void) sprintf(bp, 410 "%s%s -x -o'FallBackToRsh no' -n -l %s %s %s %s '%s%s%s:%s'", 411 SSH_PROGRAM, verbose_mode ? " -v" : "", 412 suser, host, cmd, src, 413 tuser ? tuser : "", tuser ? "@" : "", 414 thost, targ); 415 } else { 416 host = cleanhostname(argv[i]); 417 (void) sprintf(bp, 418 "exec %s%s -x -o'FallBackToRsh no' -n %s %s %s '%s%s%s:%s'", 419 SSH_PROGRAM, verbose_mode ? " -v" : "", 420 host, cmd, src, 421 tuser ? tuser : "", tuser ? "@" : "", 422 thost, targ); 423 } 424 if (verbose_mode) 425 fprintf(stderr, "Executing: %s\n", bp); 426 (void) system(bp); 427 (void) xfree(bp); 428 } else { /* local to remote */ 429 if (remin == -1) { 430 len = strlen(targ) + CMDNEEDS + 20; 431 bp = xmalloc(len); 432 (void) sprintf(bp, "%s -t %s", cmd, targ); 433 host = cleanhostname(thost); 434 if (do_cmd(host, tuser, 435 bp, &remin, &remout) < 0) 436 exit(1); 437 if (response() < 0) 438 exit(1); 439 (void) xfree(bp); 440 } 441 source(1, argv + i); 442 } 443 } 444 } 445 446 void 447 tolocal(argc, argv) 448 int argc; 449 char *argv[]; 450 { 451 int i, len; 452 char *bp, *host, *src, *suser; 453 454 for (i = 0; i < argc - 1; i++) { 455 if (!(src = colon(argv[i]))) { /* Local to local. */ 456 len = strlen(_PATH_CP) + strlen(argv[i]) + 457 strlen(argv[argc - 1]) + 20; 458 bp = xmalloc(len); 459 (void) sprintf(bp, "exec %s%s%s %s %s", _PATH_CP, 460 iamrecursive ? " -r" : "", pflag ? " -p" : "", 461 argv[i], argv[argc - 1]); 462 if (verbose_mode) 463 fprintf(stderr, "Executing: %s\n", bp); 464 if (system(bp)) 465 ++errs; 466 (void) xfree(bp); 467 continue; 468 } 469 *src++ = 0; 470 if (*src == 0) 471 src = "."; 472 if ((host = strchr(argv[i], '@')) == NULL) { 473 host = argv[i]; 474 suser = NULL; 475 } else { 476 *host++ = 0; 477 suser = argv[i]; 478 if (*suser == '\0') 479 suser = pwd->pw_name; 480 else if (!okname(suser)) 481 continue; 482 } 483 host = cleanhostname(host); 484 len = strlen(src) + CMDNEEDS + 20; 485 bp = xmalloc(len); 486 (void) sprintf(bp, "%s -f %s", cmd, src); 487 if (do_cmd(host, suser, bp, &remin, &remout) < 0) { 488 (void) xfree(bp); 489 ++errs; 490 continue; 491 } 492 xfree(bp); 493 sink(1, argv + argc - 1); 494 (void) close(remin); 495 remin = remout = -1; 496 } 497 } 498 499 void 500 source(argc, argv) 501 int argc; 502 char *argv[]; 503 { 504 struct stat stb; 505 static BUF buffer; 506 BUF *bp; 507 off_t i; 508 int amt, fd, haderr, indx, result; 509 char *last, *name, buf[2048]; 510 511 for (indx = 0; indx < argc; ++indx) { 512 name = argv[indx]; 513 statbytes = 0; 514 if ((fd = open(name, O_RDONLY, 0)) < 0) 515 goto syserr; 516 if (fstat(fd, &stb) < 0) { 517 syserr: run_err("%s: %s", name, strerror(errno)); 518 goto next; 519 } 520 switch (stb.st_mode & S_IFMT) { 521 case S_IFREG: 522 break; 523 case S_IFDIR: 524 if (iamrecursive) { 525 rsource(name, &stb); 526 goto next; 527 } 528 /* FALLTHROUGH */ 529 default: 530 run_err("%s: not a regular file", name); 531 goto next; 532 } 533 if ((last = strrchr(name, '/')) == NULL) 534 last = name; 535 else 536 ++last; 537 curfile = last; 538 if (pflag) { 539 /* 540 * Make it compatible with possible future 541 * versions expecting microseconds. 542 */ 543 (void) sprintf(buf, "T%lu 0 %lu 0\n", 544 (unsigned long) stb.st_mtime, 545 (unsigned long) stb.st_atime); 546 (void) write(remout, buf, strlen(buf)); 547 if (response() < 0) 548 goto next; 549 } 550 #define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) 551 (void) sprintf(buf, "C%04o %lu %s\n", 552 (unsigned int) (stb.st_mode & FILEMODEMASK), 553 (unsigned long) stb.st_size, 554 last); 555 if (verbose_mode) { 556 fprintf(stderr, "Sending file modes: %s", buf); 557 fflush(stderr); 558 } 559 (void) write(remout, buf, strlen(buf)); 560 if (response() < 0) 561 goto next; 562 if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) { 563 next: (void) close(fd); 564 continue; 565 } 566 if (showprogress) { 567 totalbytes = stb.st_size; 568 progressmeter(-1); 569 } 570 /* Keep writing after an error so that we stay sync'd up. */ 571 for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { 572 amt = bp->cnt; 573 if (i + amt > stb.st_size) 574 amt = stb.st_size - i; 575 if (!haderr) { 576 result = read(fd, bp->buf, amt); 577 if (result != amt) 578 haderr = result >= 0 ? EIO : errno; 579 } 580 if (haderr) 581 (void) write(remout, bp->buf, amt); 582 else { 583 result = write(remout, bp->buf, amt); 584 if (result != amt) 585 haderr = result >= 0 ? EIO : errno; 586 statbytes += result; 587 } 588 } 589 if (showprogress) 590 progressmeter(1); 591 592 if (close(fd) < 0 && !haderr) 593 haderr = errno; 594 if (!haderr) 595 (void) write(remout, "", 1); 596 else 597 run_err("%s: %s", name, strerror(haderr)); 598 (void) response(); 599 } 600 } 601 602 void 603 rsource(name, statp) 604 char *name; 605 struct stat *statp; 606 { 607 DIR *dirp; 608 struct dirent *dp; 609 char *last, *vect[1], path[1100]; 610 611 if (!(dirp = opendir(name))) { 612 run_err("%s: %s", name, strerror(errno)); 613 return; 614 } 615 last = strrchr(name, '/'); 616 if (last == 0) 617 last = name; 618 else 619 last++; 620 if (pflag) { 621 (void) sprintf(path, "T%lu 0 %lu 0\n", 622 (unsigned long) statp->st_mtime, 623 (unsigned long) statp->st_atime); 624 (void) write(remout, path, strlen(path)); 625 if (response() < 0) { 626 closedir(dirp); 627 return; 628 } 629 } 630 (void) sprintf(path, "D%04o %d %.1024s\n", 631 (unsigned int) (statp->st_mode & FILEMODEMASK), 632 0, last); 633 if (verbose_mode) 634 fprintf(stderr, "Entering directory: %s", path); 635 (void) write(remout, path, strlen(path)); 636 if (response() < 0) { 637 closedir(dirp); 638 return; 639 } 640 while ((dp = readdir(dirp))) { 641 if (dp->d_ino == 0) 642 continue; 643 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 644 continue; 645 if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path) - 1) { 646 run_err("%s/%s: name too long", name, dp->d_name); 647 continue; 648 } 649 (void) sprintf(path, "%s/%s", name, dp->d_name); 650 vect[0] = path; 651 source(1, vect); 652 } 653 (void) closedir(dirp); 654 (void) write(remout, "E\n", 2); 655 (void) response(); 656 } 657 658 void 659 sink(argc, argv) 660 int argc; 661 char *argv[]; 662 { 663 static BUF buffer; 664 struct stat stb; 665 enum { 666 YES, NO, DISPLAYED 667 } wrerr; 668 BUF *bp; 669 off_t i, j; 670 int amt, count, exists, first, mask, mode, ofd, omode; 671 int setimes, size, targisdir, wrerrno = 0; 672 char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; 673 struct utimbuf ut; 674 int dummy_usec; 675 676 #define SCREWUP(str) { why = str; goto screwup; } 677 678 setimes = targisdir = 0; 679 mask = umask(0); 680 if (!pflag) 681 (void) umask(mask); 682 if (argc != 1) { 683 run_err("ambiguous target"); 684 exit(1); 685 } 686 targ = *argv; 687 if (targetshouldbedirectory) 688 verifydir(targ); 689 690 (void) write(remout, "", 1); 691 if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) 692 targisdir = 1; 693 for (first = 1;; first = 0) { 694 cp = buf; 695 if (read(remin, cp, 1) <= 0) 696 return; 697 if (*cp++ == '\n') 698 SCREWUP("unexpected <newline>"); 699 do { 700 if (read(remin, &ch, sizeof(ch)) != sizeof(ch)) 701 SCREWUP("lost connection"); 702 *cp++ = ch; 703 } while (cp < &buf[sizeof(buf) - 1] && ch != '\n'); 704 *cp = 0; 705 706 if (buf[0] == '\01' || buf[0] == '\02') { 707 if (iamremote == 0) 708 (void) write(STDERR_FILENO, 709 buf + 1, strlen(buf + 1)); 710 if (buf[0] == '\02') 711 exit(1); 712 ++errs; 713 continue; 714 } 715 if (buf[0] == 'E') { 716 (void) write(remout, "", 1); 717 return; 718 } 719 if (ch == '\n') 720 *--cp = 0; 721 722 #define getnum(t) (t) = 0; \ 723 while (*cp >= '0' && *cp <= '9') (t) = (t) * 10 + (*cp++ - '0'); 724 cp = buf; 725 if (*cp == 'T') { 726 setimes++; 727 cp++; 728 getnum(ut.modtime); 729 if (*cp++ != ' ') 730 SCREWUP("mtime.sec not delimited"); 731 getnum(dummy_usec); 732 if (*cp++ != ' ') 733 SCREWUP("mtime.usec not delimited"); 734 getnum(ut.actime); 735 if (*cp++ != ' ') 736 SCREWUP("atime.sec not delimited"); 737 getnum(dummy_usec); 738 if (*cp++ != '\0') 739 SCREWUP("atime.usec not delimited"); 740 (void) write(remout, "", 1); 741 continue; 742 } 743 if (*cp != 'C' && *cp != 'D') { 744 /* 745 * Check for the case "rcp remote:foo\* local:bar". 746 * In this case, the line "No match." can be returned 747 * by the shell before the rcp command on the remote is 748 * executed so the ^Aerror_message convention isn't 749 * followed. 750 */ 751 if (first) { 752 run_err("%s", cp); 753 exit(1); 754 } 755 SCREWUP("expected control record"); 756 } 757 mode = 0; 758 for (++cp; cp < buf + 5; cp++) { 759 if (*cp < '0' || *cp > '7') 760 SCREWUP("bad mode"); 761 mode = (mode << 3) | (*cp - '0'); 762 } 763 if (*cp++ != ' ') 764 SCREWUP("mode not delimited"); 765 766 for (size = 0; *cp >= '0' && *cp <= '9';) 767 size = size * 10 + (*cp++ - '0'); 768 if (*cp++ != ' ') 769 SCREWUP("size not delimited"); 770 if (targisdir) { 771 static char *namebuf; 772 static int cursize; 773 size_t need; 774 775 need = strlen(targ) + strlen(cp) + 250; 776 if (need > cursize) 777 namebuf = xmalloc(need); 778 (void) sprintf(namebuf, "%s%s%s", targ, 779 *targ ? "/" : "", cp); 780 np = namebuf; 781 } else 782 np = targ; 783 curfile = cp; 784 exists = stat(np, &stb) == 0; 785 if (buf[0] == 'D') { 786 int mod_flag = pflag; 787 if (exists) { 788 if (!S_ISDIR(stb.st_mode)) { 789 errno = ENOTDIR; 790 goto bad; 791 } 792 if (pflag) 793 (void) chmod(np, mode); 794 } else { 795 /* Handle copying from a read-only 796 directory */ 797 mod_flag = 1; 798 if (mkdir(np, mode | S_IRWXU) < 0) 799 goto bad; 800 } 801 vect[0] = np; 802 sink(1, vect); 803 if (setimes) { 804 setimes = 0; 805 if (utime(np, &ut) < 0) 806 run_err("%s: set times: %s", 807 np, strerror(errno)); 808 } 809 if (mod_flag) 810 (void) chmod(np, mode); 811 continue; 812 } 813 omode = mode; 814 mode |= S_IWRITE; 815 if ((ofd = open(np, O_WRONLY | O_CREAT | O_TRUNC, mode)) < 0) { 816 bad: run_err("%s: %s", np, strerror(errno)); 817 continue; 818 } 819 (void) write(remout, "", 1); 820 if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) { 821 (void) close(ofd); 822 continue; 823 } 824 cp = bp->buf; 825 wrerr = NO; 826 827 if (showprogress) { 828 totalbytes = size; 829 progressmeter(-1); 830 } 831 statbytes = 0; 832 for (count = i = 0; i < size; i += 4096) { 833 amt = 4096; 834 if (i + amt > size) 835 amt = size - i; 836 count += amt; 837 do { 838 j = read(remin, cp, amt); 839 if (j <= 0) { 840 run_err("%s", j ? strerror(errno) : 841 "dropped connection"); 842 exit(1); 843 } 844 amt -= j; 845 cp += j; 846 statbytes += j; 847 } while (amt > 0); 848 if (count == bp->cnt) { 849 /* Keep reading so we stay sync'd up. */ 850 if (wrerr == NO) { 851 j = write(ofd, bp->buf, count); 852 if (j != count) { 853 wrerr = YES; 854 wrerrno = j >= 0 ? EIO : errno; 855 } 856 } 857 count = 0; 858 cp = bp->buf; 859 } 860 } 861 if (showprogress) 862 progressmeter(1); 863 if (count != 0 && wrerr == NO && 864 (j = write(ofd, bp->buf, count)) != count) { 865 wrerr = YES; 866 wrerrno = j >= 0 ? EIO : errno; 867 } 868 #if 0 869 if (ftruncate(ofd, size)) { 870 run_err("%s: truncate: %s", np, strerror(errno)); 871 wrerr = DISPLAYED; 872 } 873 #endif 874 if (pflag) { 875 if (exists || omode != mode) 876 if (fchmod(ofd, omode)) 877 run_err("%s: set mode: %s", 878 np, strerror(errno)); 879 } else { 880 if (!exists && omode != mode) 881 if (fchmod(ofd, omode & ~mask)) 882 run_err("%s: set mode: %s", 883 np, strerror(errno)); 884 } 885 (void) close(ofd); 886 (void) response(); 887 if (setimes && wrerr == NO) { 888 setimes = 0; 889 if (utime(np, &ut) < 0) { 890 run_err("%s: set times: %s", 891 np, strerror(errno)); 892 wrerr = DISPLAYED; 893 } 894 } 895 switch (wrerr) { 896 case YES: 897 run_err("%s: %s", np, strerror(wrerrno)); 898 break; 899 case NO: 900 (void) write(remout, "", 1); 901 break; 902 case DISPLAYED: 903 break; 904 } 905 } 906 screwup: 907 run_err("protocol error: %s", why); 908 exit(1); 909 } 910 911 int 912 response() 913 { 914 char ch, *cp, resp, rbuf[2048]; 915 916 if (read(remin, &resp, sizeof(resp)) != sizeof(resp)) 917 lostconn(0); 918 919 cp = rbuf; 920 switch (resp) { 921 case 0: /* ok */ 922 return (0); 923 default: 924 *cp++ = resp; 925 /* FALLTHROUGH */ 926 case 1: /* error, followed by error msg */ 927 case 2: /* fatal error, "" */ 928 do { 929 if (read(remin, &ch, sizeof(ch)) != sizeof(ch)) 930 lostconn(0); 931 *cp++ = ch; 932 } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); 933 934 if (!iamremote) 935 (void) write(STDERR_FILENO, rbuf, cp - rbuf); 936 ++errs; 937 if (resp == 1) 938 return (-1); 939 exit(1); 940 } 941 /* NOTREACHED */ 942 } 943 944 void 945 usage() 946 { 947 (void) fprintf(stderr, 948 "usage: scp [-pqrvC46] [-P port] [-c cipher] [-i identity] f1 f2; or:\n scp [options] f1 ... fn directory\n"); 949 exit(1); 950 } 951 952 void 953 run_err(const char *fmt,...) 954 { 955 static FILE *fp; 956 va_list ap; 957 va_start(ap, fmt); 958 959 ++errs; 960 if (fp == NULL && !(fp = fdopen(remout, "w"))) 961 return; 962 (void) fprintf(fp, "%c", 0x01); 963 (void) fprintf(fp, "scp: "); 964 (void) vfprintf(fp, fmt, ap); 965 (void) fprintf(fp, "\n"); 966 (void) fflush(fp); 967 968 if (!iamremote) { 969 vfprintf(stderr, fmt, ap); 970 fprintf(stderr, "\n"); 971 } 972 va_end(ap); 973 } 974 975 /* Stuff below is from BSD rcp util.c. */ 976 977 /*- 978 * Copyright (c) 1992, 1993 979 * The Regents of the University of California. All rights reserved. 980 * 981 * Redistribution and use in source and binary forms, with or without 982 * modification, are permitted provided that the following conditions 983 * are met: 984 * 1. Redistributions of source code must retain the above copyright 985 * notice, this list of conditions and the following disclaimer. 986 * 2. Redistributions in binary form must reproduce the above copyright 987 * notice, this list of conditions and the following disclaimer in the 988 * documentation and/or other materials provided with the distribution. 989 * 3. All advertising materials mentioning features or use of this software 990 * must display the following acknowledgement: 991 * This product includes software developed by the University of 992 * California, Berkeley and its contributors. 993 * 4. Neither the name of the University nor the names of its contributors 994 * may be used to endorse or promote products derived from this software 995 * without specific prior written permission. 996 * 997 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 998 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 999 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1000 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 1001 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1002 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 1003 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 1004 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 1005 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 1006 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 1007 * SUCH DAMAGE. 1008 * 1009 * $Id: scp.c,v 1.26 2000/03/16 20:56:14 markus Exp $ 1010 */ 1011 1012 char * 1013 colon(cp) 1014 char *cp; 1015 { 1016 int flag = 0; 1017 1018 if (*cp == ':') /* Leading colon is part of file name. */ 1019 return (0); 1020 if (*cp == '[') 1021 flag = 1; 1022 1023 for (; *cp; ++cp) { 1024 if (*cp == '@' && *(cp+1) == '[') 1025 flag = 1; 1026 if (*cp == ']' && *(cp+1) == ':' && flag) 1027 return (cp+1); 1028 if (*cp == ':' && !flag) 1029 return (cp); 1030 if (*cp == '/') 1031 return (0); 1032 } 1033 return (0); 1034 } 1035 1036 void 1037 verifydir(cp) 1038 char *cp; 1039 { 1040 struct stat stb; 1041 1042 if (!stat(cp, &stb)) { 1043 if (S_ISDIR(stb.st_mode)) 1044 return; 1045 errno = ENOTDIR; 1046 } 1047 run_err("%s: %s", cp, strerror(errno)); 1048 exit(1); 1049 } 1050 1051 int 1052 okname(cp0) 1053 char *cp0; 1054 { 1055 int c; 1056 char *cp; 1057 1058 cp = cp0; 1059 do { 1060 c = *cp; 1061 if (c & 0200) 1062 goto bad; 1063 if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-' && c != '.') 1064 goto bad; 1065 } while (*++cp); 1066 return (1); 1067 1068 bad: fprintf(stderr, "%s: invalid user name\n", cp0); 1069 return (0); 1070 } 1071 1072 BUF * 1073 allocbuf(bp, fd, blksize) 1074 BUF *bp; 1075 int fd, blksize; 1076 { 1077 size_t size; 1078 struct stat stb; 1079 1080 if (fstat(fd, &stb) < 0) { 1081 run_err("fstat: %s", strerror(errno)); 1082 return (0); 1083 } 1084 if (stb.st_blksize == 0) 1085 size = blksize; 1086 else 1087 size = blksize + (stb.st_blksize - blksize % stb.st_blksize) % 1088 stb.st_blksize; 1089 if (bp->cnt >= size) 1090 return (bp); 1091 if (bp->buf == NULL) 1092 bp->buf = xmalloc(size); 1093 else 1094 bp->buf = xrealloc(bp->buf, size); 1095 bp->cnt = size; 1096 return (bp); 1097 } 1098 1099 void 1100 lostconn(signo) 1101 int signo; 1102 { 1103 if (!iamremote) 1104 fprintf(stderr, "lost connection\n"); 1105 exit(1); 1106 } 1107 1108 1109 void 1110 alarmtimer(int wait) 1111 { 1112 struct itimerval itv; 1113 1114 itv.it_value.tv_sec = wait; 1115 itv.it_value.tv_usec = 0; 1116 itv.it_interval = itv.it_value; 1117 setitimer(ITIMER_REAL, &itv, NULL); 1118 } 1119 1120 void 1121 updateprogressmeter(int ignore) 1122 { 1123 int save_errno = errno; 1124 1125 progressmeter(0); 1126 errno = save_errno; 1127 } 1128 1129 int 1130 foregroundproc() 1131 { 1132 static pid_t pgrp = -1; 1133 int ctty_pgrp; 1134 1135 if (pgrp == -1) 1136 pgrp = getpgrp(); 1137 1138 return ((ioctl(STDOUT_FILENO, TIOCGPGRP, &ctty_pgrp) != -1 && 1139 ctty_pgrp == pgrp)); 1140 } 1141 1142 void 1143 progressmeter(int flag) 1144 { 1145 static const char prefixes[] = " KMGTP"; 1146 static struct timeval lastupdate; 1147 static off_t lastsize; 1148 struct timeval now, td, wait; 1149 off_t cursize, abbrevsize; 1150 double elapsed; 1151 int ratio, barlength, i, remaining; 1152 char buf[256]; 1153 1154 if (flag == -1) { 1155 (void) gettimeofday(&start, (struct timezone *) 0); 1156 lastupdate = start; 1157 lastsize = 0; 1158 } 1159 if (foregroundproc() == 0) 1160 return; 1161 1162 (void) gettimeofday(&now, (struct timezone *) 0); 1163 cursize = statbytes; 1164 if (totalbytes != 0) { 1165 ratio = 100.0 * cursize / totalbytes; 1166 ratio = MAX(ratio, 0); 1167 ratio = MIN(ratio, 100); 1168 } else 1169 ratio = 100; 1170 1171 snprintf(buf, sizeof(buf), "\r%-20.20s %3d%% ", curfile, ratio); 1172 1173 barlength = getttywidth() - 51; 1174 if (barlength > 0) { 1175 i = barlength * ratio / 100; 1176 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), 1177 "|%.*s%*s|", i, 1178 "*****************************************************************************" 1179 "*****************************************************************************", 1180 barlength - i, ""); 1181 } 1182 i = 0; 1183 abbrevsize = cursize; 1184 while (abbrevsize >= 100000 && i < sizeof(prefixes)) { 1185 i++; 1186 abbrevsize >>= 10; 1187 } 1188 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %5qd %c%c ", 1189 (quad_t) abbrevsize, prefixes[i], prefixes[i] == ' ' ? ' ' : 1190 'B'); 1191 1192 timersub(&now, &lastupdate, &wait); 1193 if (cursize > lastsize) { 1194 lastupdate = now; 1195 lastsize = cursize; 1196 if (wait.tv_sec >= STALLTIME) { 1197 start.tv_sec += wait.tv_sec; 1198 start.tv_usec += wait.tv_usec; 1199 } 1200 wait.tv_sec = 0; 1201 } 1202 timersub(&now, &start, &td); 1203 elapsed = td.tv_sec + (td.tv_usec / 1000000.0); 1204 1205 if (statbytes <= 0 || elapsed <= 0.0 || cursize > totalbytes) { 1206 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), 1207 " --:-- ETA"); 1208 } else if (wait.tv_sec >= STALLTIME) { 1209 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), 1210 " - stalled -"); 1211 } else { 1212 remaining = (int) (totalbytes / (statbytes / elapsed) - elapsed); 1213 i = remaining / 3600; 1214 if (i) 1215 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), 1216 "%2d:", i); 1217 else 1218 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), 1219 " "); 1220 i = remaining % 3600; 1221 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), 1222 "%02d:%02d ETA", i / 60, i % 60); 1223 } 1224 atomicio(write, fileno(stdout), buf, strlen(buf)); 1225 1226 if (flag == -1) { 1227 signal(SIGALRM, updateprogressmeter); 1228 alarmtimer(1); 1229 } else if (flag == 1) { 1230 alarmtimer(0); 1231 write(fileno(stdout), "\n", 1); 1232 statbytes = 0; 1233 } 1234 } 1235 1236 int 1237 getttywidth(void) 1238 { 1239 struct winsize winsize; 1240 1241 if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1) 1242 return (winsize.ws_col ? winsize.ws_col : 80); 1243 else 1244 return (80); 1245 } 1246