Annotation of src/usr.bin/ssh/scp.c, Revision 1.1
1.1 ! deraadt 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: * $Id: scp.c,v 1.6 1999/05/04 11:59:09 bg Exp $
! 46: */
! 47:
! 48: #include "includes.h"
! 49: RCSID("$Id: scp.c,v 1.6 1999/05/04 11:59:09 bg Exp $");
! 50:
! 51: #include "ssh.h"
! 52: #include "xmalloc.h"
! 53: #ifdef HAVE_UTIME_H
! 54: #include <utime.h>
! 55: #ifdef _NEXT_SOURCE
! 56: struct utimbuf {
! 57: time_t actime;
! 58: time_t modtime;
! 59: };
! 60: #endif /* _NEXT_SOURCE */
! 61: #else
! 62: struct utimbuf
! 63: {
! 64: long actime;
! 65: long modtime;
! 66: };
! 67: #endif
! 68:
! 69: #define _PATH_CP "cp"
! 70:
! 71: #ifndef STDIN_FILENO
! 72: #define STDIN_FILENO 0
! 73: #endif
! 74: #ifndef STDOUT_FILENO
! 75: #define STDOUT_FILENO 1
! 76: #endif
! 77: #ifndef STDERR_FILENO
! 78: #define STDERR_FILENO 2
! 79: #endif
! 80:
! 81: #if defined(KERBEROS_TGT_PASSING) || defined(AFS)
! 82: /* This is set to non-zero to disable authentication forwarding. */
! 83: int nofwd = 0;
! 84: #endif
! 85:
! 86: /* This is set to non-zero to enable verbose mode. */
! 87: int verbose = 0;
! 88:
! 89: /* This is set to non-zero if compression is desired. */
! 90: int compress = 0;
! 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: /* This function executes the given command as the specified user on the given
! 107: host. This returns < 0 if execution fails, and >= 0 otherwise.
! 108: This assigns the input and output file descriptors on success. */
! 109:
! 110: int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout)
! 111: {
! 112: int pin[2], pout[2], reserved[2];
! 113:
! 114: if (verbose)
! 115: fprintf(stderr, "Executing: host %s, user %s, command %s\n",
! 116: host, remuser ? remuser : "(unspecified)", cmd);
! 117:
! 118: /* Reserve two descriptors so that the real pipes won't get descriptors
! 119: 0 and 1 because that will screw up dup2 below. */
! 120: pipe(reserved);
! 121:
! 122: /* Create a socket pair for communicating with ssh. */
! 123: if (pipe(pin) < 0)
! 124: fatal("pipe: %s", strerror(errno));
! 125: if (pipe(pout) < 0)
! 126: fatal("pipe: %s", strerror(errno));
! 127:
! 128: /* Free the reserved descriptors. */
! 129: close(reserved[0]);
! 130: close(reserved[1]);
! 131:
! 132: /* For a child to execute the command on the remote host using ssh. */
! 133: if (fork() == 0)
! 134: {
! 135: char *args[100];
! 136: unsigned int i;
! 137:
! 138: /* Child. */
! 139: close(pin[1]);
! 140: close(pout[0]);
! 141: dup2(pin[0], 0);
! 142: dup2(pout[1], 1);
! 143: close(pin[0]);
! 144: close(pout[1]);
! 145:
! 146: i = 0;
! 147: args[i++] = SSH_PROGRAM;
! 148: args[i++] = "-x";
! 149: args[i++] = "-oFallBackToRsh no";
! 150: if (verbose)
! 151: args[i++] = "-v";
! 152: if (compress)
! 153: args[i++] = "-C";
! 154: if (batchmode)
! 155: args[i++] = "-oBatchMode yes";
! 156: #if defined(KERBEROS_TGT_PASSING) || defined(AFS)
! 157: if (nofwd)
! 158: args[i++] = "-k";
! 159: #endif
! 160: if (cipher != NULL)
! 161: {
! 162: args[i++] = "-c";
! 163: args[i++] = cipher;
! 164: }
! 165: if (identity != NULL)
! 166: {
! 167: args[i++] = "-i";
! 168: args[i++] = identity;
! 169: }
! 170: if (port != NULL)
! 171: {
! 172: args[i++] = "-p";
! 173: args[i++] = port;
! 174: }
! 175: if (remuser != NULL)
! 176: {
! 177: args[i++] = "-l";
! 178: args[i++] = remuser;
! 179: }
! 180: args[i++] = host;
! 181: args[i++] = cmd;
! 182: args[i++] = NULL;
! 183:
! 184: execvp(SSH_PROGRAM, args);
! 185: perror(SSH_PROGRAM);
! 186: exit(1);
! 187: }
! 188: /* Parent. Close the other side, and return the local side. */
! 189: close(pin[0]);
! 190: *fdout = pin[1];
! 191: close(pout[1]);
! 192: *fdin = pout[0];
! 193: return 0;
! 194: }
! 195:
! 196: void fatal(const char *fmt, ...)
! 197: {
! 198: va_list ap;
! 199: char buf[1024];
! 200:
! 201: va_start(ap, fmt);
! 202: vsnprintf(buf, sizeof(buf), fmt, ap);
! 203: va_end(ap);
! 204: fprintf(stderr, "%s\n", buf);
! 205: exit(255);
! 206: }
! 207:
! 208: /* This stuff used to be in BSD rcp extern.h. */
! 209:
! 210: typedef struct {
! 211: int cnt;
! 212: char *buf;
! 213: } BUF;
! 214:
! 215: extern int iamremote;
! 216:
! 217: BUF *allocbuf(BUF *, int, int);
! 218: char *colon(char *);
! 219: void lostconn(int);
! 220: void nospace(void);
! 221: int okname(char *);
! 222: void run_err(const char *, ...);
! 223: void verifydir(char *);
! 224:
! 225: /* Stuff from BSD rcp.c continues. */
! 226:
! 227: struct passwd *pwd;
! 228: uid_t userid;
! 229: int errs, remin, remout;
! 230: int pflag, iamremote, iamrecursive, targetshouldbedirectory;
! 231:
! 232: #define CMDNEEDS 64
! 233: char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */
! 234:
! 235: int response(void);
! 236: void rsource(char *, struct stat *);
! 237: void sink(int, char *[]);
! 238: void source(int, char *[]);
! 239: void tolocal(int, char *[]);
! 240: void toremote(char *, int, char *[]);
! 241: void usage(void);
! 242:
! 243: int
! 244: main(argc, argv)
! 245: int argc;
! 246: char *argv[];
! 247: {
! 248: int ch, fflag, tflag;
! 249: char *targ;
! 250: extern char *optarg;
! 251: extern int optind;
! 252:
! 253: fflag = tflag = 0;
! 254: #if defined(KERBEROS_TGT_PASSING) || defined(AFS)
! 255: while ((ch = getopt(argc, argv, "kdfprtvBCc:i:P:")) != EOF)
! 256: #else
! 257: while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:")) != EOF)
! 258: #endif
! 259: switch(ch) { /* User-visible flags. */
! 260: case 'p':
! 261: pflag = 1;
! 262: break;
! 263: case 'P':
! 264: port = optarg;
! 265: break;
! 266: case 'r':
! 267: iamrecursive = 1;
! 268: break;
! 269: /* Server options. */
! 270: #if defined(KERBEROS_TGT_PASSING) || defined(AFS)
! 271: case 'k':
! 272: nofwd = 1;
! 273: break;
! 274: #endif
! 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 = 1;
! 294: break;
! 295: case 'B':
! 296: batchmode = 1;
! 297: break;
! 298: case 'C':
! 299: compress = 1;
! 300: break;
! 301: case '?':
! 302: default:
! 303: usage();
! 304: }
! 305: argc -= optind;
! 306: argv += optind;
! 307:
! 308: if ((pwd = getpwuid(userid = getuid())) == NULL)
! 309: fatal("unknown user %d", (int)userid);
! 310:
! 311: remin = STDIN_FILENO;
! 312: remout = STDOUT_FILENO;
! 313:
! 314: if (fflag) { /* Follow "protocol", send data. */
! 315: (void)response();
! 316: source(argc, argv);
! 317: exit(errs != 0);
! 318: }
! 319:
! 320: if (tflag) { /* Receive data. */
! 321: sink(argc, argv);
! 322: exit(errs != 0);
! 323: }
! 324:
! 325: if (argc < 2)
! 326: usage();
! 327: if (argc > 2)
! 328: targetshouldbedirectory = 1;
! 329:
! 330: remin = remout = -1;
! 331: /* Command to be executed on remote system using "ssh". */
! 332: (void)sprintf(cmd, "scp%s%s%s%s", verbose ? " -v" : "",
! 333: iamrecursive ? " -r" : "", pflag ? " -p" : "",
! 334: targetshouldbedirectory ? " -d" : "");
! 335:
! 336: (void)signal(SIGPIPE, lostconn);
! 337:
! 338: if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */
! 339: toremote(targ, argc, argv);
! 340: else {
! 341: tolocal(argc, argv); /* Dest is local host. */
! 342: if (targetshouldbedirectory)
! 343: verifydir(argv[argc - 1]);
! 344: }
! 345: exit(errs != 0);
! 346: }
! 347:
! 348: void
! 349: toremote(targ, argc, argv)
! 350: char *targ, *argv[];
! 351: int argc;
! 352: {
! 353: int i, len;
! 354: char *bp, *host, *src, *suser, *thost, *tuser;
! 355:
! 356: *targ++ = 0;
! 357: if (*targ == 0)
! 358: targ = ".";
! 359:
! 360: if ((thost = strchr(argv[argc - 1], '@'))) {
! 361: /* user@host */
! 362: *thost++ = 0;
! 363: tuser = argv[argc - 1];
! 364: if (*tuser == '\0')
! 365: tuser = NULL;
! 366: else if (!okname(tuser))
! 367: exit(1);
! 368: } else {
! 369: thost = argv[argc - 1];
! 370: tuser = NULL;
! 371: }
! 372:
! 373: for (i = 0; i < argc - 1; i++) {
! 374: src = colon(argv[i]);
! 375: if (src) { /* remote to remote */
! 376: *src++ = 0;
! 377: if (*src == 0)
! 378: src = ".";
! 379: host = strchr(argv[i], '@');
! 380: len = strlen(SSH_PROGRAM) + strlen(argv[i]) +
! 381: strlen(src) + (tuser ? strlen(tuser) : 0) +
! 382: strlen(thost) + strlen(targ) + CMDNEEDS + 32;
! 383: bp = xmalloc(len);
! 384: if (host) {
! 385: *host++ = 0;
! 386: suser = argv[i];
! 387: if (*suser == '\0')
! 388: suser = pwd->pw_name;
! 389: else if (!okname(suser))
! 390: continue;
! 391: (void)sprintf(bp,
! 392: "%s%s -x -o'FallBackToRsh no' -n -l %s %s %s %s '%s%s%s:%s'",
! 393: SSH_PROGRAM, verbose ? " -v" : "",
! 394: suser, host, cmd, src,
! 395: tuser ? tuser : "", tuser ? "@" : "",
! 396: thost, targ);
! 397: } else
! 398: (void)sprintf(bp,
! 399: "exec %s%s -x -o'FallBackToRsh no' -n %s %s %s '%s%s%s:%s'",
! 400: SSH_PROGRAM, verbose ? " -v" : "",
! 401: argv[i], cmd, src,
! 402: tuser ? tuser : "", tuser ? "@" : "",
! 403: thost, targ);
! 404: if (verbose)
! 405: fprintf(stderr, "Executing: %s\n", bp);
! 406: (void)system(bp);
! 407: (void)xfree(bp);
! 408: } else { /* local to remote */
! 409: if (remin == -1) {
! 410: len = strlen(targ) + CMDNEEDS + 20;
! 411: bp = xmalloc(len);
! 412: (void)sprintf(bp, "%s -t %s", cmd, targ);
! 413: host = thost;
! 414: if (do_cmd(host, tuser,
! 415: bp, &remin, &remout) < 0)
! 416: exit(1);
! 417: if (response() < 0)
! 418: exit(1);
! 419: (void)xfree(bp);
! 420: }
! 421: source(1, argv+i);
! 422: }
! 423: }
! 424: }
! 425:
! 426: void
! 427: tolocal(argc, argv)
! 428: int argc;
! 429: char *argv[];
! 430: {
! 431: int i, len;
! 432: char *bp, *host, *src, *suser;
! 433:
! 434: for (i = 0; i < argc - 1; i++) {
! 435: if (!(src = colon(argv[i]))) { /* Local to local. */
! 436: len = strlen(_PATH_CP) + strlen(argv[i]) +
! 437: strlen(argv[argc - 1]) + 20;
! 438: bp = xmalloc(len);
! 439: (void)sprintf(bp, "exec %s%s%s %s %s", _PATH_CP,
! 440: iamrecursive ? " -r" : "", pflag ? " -p" : "",
! 441: argv[i], argv[argc - 1]);
! 442: if (verbose)
! 443: fprintf(stderr, "Executing: %s\n", bp);
! 444: if (system(bp))
! 445: ++errs;
! 446: (void)xfree(bp);
! 447: continue;
! 448: }
! 449: *src++ = 0;
! 450: if (*src == 0)
! 451: src = ".";
! 452: if ((host = strchr(argv[i], '@')) == NULL) {
! 453: host = argv[i];
! 454: suser = NULL;
! 455: } else {
! 456: *host++ = 0;
! 457: suser = argv[i];
! 458: if (*suser == '\0')
! 459: suser = pwd->pw_name;
! 460: else if (!okname(suser))
! 461: continue;
! 462: }
! 463: len = strlen(src) + CMDNEEDS + 20;
! 464: bp = xmalloc(len);
! 465: (void)sprintf(bp, "%s -f %s", cmd, src);
! 466: if (do_cmd(host, suser, bp, &remin, &remout) < 0) {
! 467: (void)xfree(bp);
! 468: ++errs;
! 469: continue;
! 470: }
! 471: xfree(bp);
! 472: sink(1, argv + argc - 1);
! 473: (void)close(remin);
! 474: remin = remout = -1;
! 475: }
! 476: }
! 477:
! 478: void
! 479: source(argc, argv)
! 480: int argc;
! 481: char *argv[];
! 482: {
! 483: struct stat stb;
! 484: static BUF buffer;
! 485: BUF *bp;
! 486: off_t i;
! 487: int amt, fd, haderr, indx, result;
! 488: char *last, *name, buf[2048];
! 489:
! 490: for (indx = 0; indx < argc; ++indx) {
! 491: name = argv[indx];
! 492: if ((fd = open(name, O_RDONLY, 0)) < 0)
! 493: goto syserr;
! 494: if (fstat(fd, &stb) < 0) {
! 495: syserr: run_err("%s: %s", name, strerror(errno));
! 496: goto next;
! 497: }
! 498: switch (stb.st_mode & S_IFMT) {
! 499: case S_IFREG:
! 500: break;
! 501: case S_IFDIR:
! 502: if (iamrecursive) {
! 503: rsource(name, &stb);
! 504: goto next;
! 505: }
! 506: /* FALLTHROUGH */
! 507: default:
! 508: run_err("%s: not a regular file", name);
! 509: goto next;
! 510: }
! 511: if ((last = strrchr(name, '/')) == NULL)
! 512: last = name;
! 513: else
! 514: ++last;
! 515: if (pflag) {
! 516: /*
! 517: * Make it compatible with possible future
! 518: * versions expecting microseconds.
! 519: */
! 520: (void)sprintf(buf, "T%lu 0 %lu 0\n",
! 521: (unsigned long)stb.st_mtime,
! 522: (unsigned long)stb.st_atime);
! 523: (void)write(remout, buf, strlen(buf));
! 524: if (response() < 0)
! 525: goto next;
! 526: }
! 527: #define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
! 528: (void)sprintf(buf, "C%04o %lu %s\n",
! 529: (unsigned int)(stb.st_mode & FILEMODEMASK),
! 530: (unsigned long)stb.st_size,
! 531: last);
! 532: if (verbose)
! 533: {
! 534: fprintf(stderr, "Sending file modes: %s", buf);
! 535: fflush(stderr);
! 536: }
! 537: (void)write(remout, buf, strlen(buf));
! 538: if (response() < 0)
! 539: goto next;
! 540: if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) {
! 541: next: (void)close(fd);
! 542: continue;
! 543: }
! 544:
! 545: /* Keep writing after an error so that we stay sync'd up. */
! 546: for (haderr = i = 0; i < stb.st_size; i += bp->cnt) {
! 547: amt = bp->cnt;
! 548: if (i + amt > stb.st_size)
! 549: amt = stb.st_size - i;
! 550: if (!haderr) {
! 551: result = read(fd, bp->buf, amt);
! 552: if (result != amt)
! 553: haderr = result >= 0 ? EIO : errno;
! 554: }
! 555: if (haderr)
! 556: (void)write(remout, bp->buf, amt);
! 557: else {
! 558: result = write(remout, bp->buf, amt);
! 559: if (result != amt)
! 560: haderr = result >= 0 ? EIO : errno;
! 561: }
! 562: }
! 563: if (close(fd) < 0 && !haderr)
! 564: haderr = errno;
! 565: if (!haderr)
! 566: (void)write(remout, "", 1);
! 567: else
! 568: run_err("%s: %s", name, strerror(haderr));
! 569: (void)response();
! 570: }
! 571: }
! 572:
! 573: void
! 574: rsource(name, statp)
! 575: char *name;
! 576: struct stat *statp;
! 577: {
! 578: DIR *dirp;
! 579: struct dirent *dp;
! 580: char *last, *vect[1], path[1100];
! 581:
! 582: if (!(dirp = opendir(name))) {
! 583: run_err("%s: %s", name, strerror(errno));
! 584: return;
! 585: }
! 586: last = strrchr(name, '/');
! 587: if (last == 0)
! 588: last = name;
! 589: else
! 590: last++;
! 591: if (pflag) {
! 592: (void)sprintf(path, "T%lu 0 %lu 0\n",
! 593: (unsigned long)statp->st_mtime,
! 594: (unsigned long)statp->st_atime);
! 595: (void)write(remout, path, strlen(path));
! 596: if (response() < 0) {
! 597: closedir(dirp);
! 598: return;
! 599: }
! 600: }
! 601: (void)sprintf(path,
! 602: "D%04o %d %.1024s\n", (unsigned int)(statp->st_mode & FILEMODEMASK),
! 603: 0, last);
! 604: if (verbose)
! 605: fprintf(stderr, "Entering directory: %s", path);
! 606: (void)write(remout, path, strlen(path));
! 607: if (response() < 0) {
! 608: closedir(dirp);
! 609: return;
! 610: }
! 611: while ((dp = readdir(dirp))) {
! 612: if (dp->d_ino == 0)
! 613: continue;
! 614: if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
! 615: continue;
! 616: if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path) - 1) {
! 617: run_err("%s/%s: name too long", name, dp->d_name);
! 618: continue;
! 619: }
! 620: (void)sprintf(path, "%s/%s", name, dp->d_name);
! 621: vect[0] = path;
! 622: source(1, vect);
! 623: }
! 624: (void)closedir(dirp);
! 625: (void)write(remout, "E\n", 2);
! 626: (void)response();
! 627: }
! 628:
! 629: void
! 630: sink(argc, argv)
! 631: int argc;
! 632: char *argv[];
! 633: {
! 634: static BUF buffer;
! 635: struct stat stb;
! 636: enum { YES, NO, DISPLAYED } wrerr;
! 637: BUF *bp;
! 638: off_t i, j;
! 639: int amt, count, exists, first, mask, mode, ofd, omode;
! 640: int setimes, size, targisdir, wrerrno = 0;
! 641: char ch, *cp, *np, *targ, *why, *vect[1], buf[2048];
! 642: struct utimbuf ut;
! 643: int dummy_usec;
! 644:
! 645: #define SCREWUP(str) { why = str; goto screwup; }
! 646:
! 647: setimes = targisdir = 0;
! 648: mask = umask(0);
! 649: if (!pflag)
! 650: (void)umask(mask);
! 651: if (argc != 1) {
! 652: run_err("ambiguous target");
! 653: exit(1);
! 654: }
! 655: targ = *argv;
! 656: if (targetshouldbedirectory)
! 657: verifydir(targ);
! 658:
! 659: (void)write(remout, "", 1);
! 660: if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode))
! 661: targisdir = 1;
! 662: for (first = 1;; first = 0) {
! 663: cp = buf;
! 664: if (read(remin, cp, 1) <= 0)
! 665: return;
! 666: if (*cp++ == '\n')
! 667: SCREWUP("unexpected <newline>");
! 668: do {
! 669: if (read(remin, &ch, sizeof(ch)) != sizeof(ch))
! 670: SCREWUP("lost connection");
! 671: *cp++ = ch;
! 672: } while (cp < &buf[sizeof(buf) - 1] && ch != '\n');
! 673: *cp = 0;
! 674:
! 675: if (buf[0] == '\01' || buf[0] == '\02') {
! 676: if (iamremote == 0)
! 677: (void)write(STDERR_FILENO,
! 678: buf + 1, strlen(buf + 1));
! 679: if (buf[0] == '\02')
! 680: exit(1);
! 681: ++errs;
! 682: continue;
! 683: }
! 684: if (buf[0] == 'E') {
! 685: (void)write(remout, "", 1);
! 686: return;
! 687: }
! 688:
! 689: if (ch == '\n')
! 690: *--cp = 0;
! 691:
! 692: #define getnum(t) (t) = 0; \
! 693: while (*cp >= '0' && *cp <= '9') (t) = (t) * 10 + (*cp++ - '0');
! 694: cp = buf;
! 695: if (*cp == 'T') {
! 696: setimes++;
! 697: cp++;
! 698: getnum(ut.modtime);
! 699: if (*cp++ != ' ')
! 700: SCREWUP("mtime.sec not delimited");
! 701: getnum(dummy_usec);
! 702: if (*cp++ != ' ')
! 703: SCREWUP("mtime.usec not delimited");
! 704: getnum(ut.actime);
! 705: if (*cp++ != ' ')
! 706: SCREWUP("atime.sec not delimited");
! 707: getnum(dummy_usec);
! 708: if (*cp++ != '\0')
! 709: SCREWUP("atime.usec not delimited");
! 710: (void)write(remout, "", 1);
! 711: continue;
! 712: }
! 713: if (*cp != 'C' && *cp != 'D') {
! 714: /*
! 715: * Check for the case "rcp remote:foo\* local:bar".
! 716: * In this case, the line "No match." can be returned
! 717: * by the shell before the rcp command on the remote is
! 718: * executed so the ^Aerror_message convention isn't
! 719: * followed.
! 720: */
! 721: if (first) {
! 722: run_err("%s", cp);
! 723: exit(1);
! 724: }
! 725: SCREWUP("expected control record");
! 726: }
! 727: mode = 0;
! 728: for (++cp; cp < buf + 5; cp++) {
! 729: if (*cp < '0' || *cp > '7')
! 730: SCREWUP("bad mode");
! 731: mode = (mode << 3) | (*cp - '0');
! 732: }
! 733: if (*cp++ != ' ')
! 734: SCREWUP("mode not delimited");
! 735:
! 736: for (size = 0; *cp >= '0' && *cp <= '9';)
! 737: size = size * 10 + (*cp++ - '0');
! 738: if (*cp++ != ' ')
! 739: SCREWUP("size not delimited");
! 740: if (targisdir) {
! 741: static char *namebuf;
! 742: static int cursize;
! 743: size_t need;
! 744:
! 745: need = strlen(targ) + strlen(cp) + 250;
! 746: if (need > cursize)
! 747: namebuf = xmalloc(need);
! 748: (void)sprintf(namebuf, "%s%s%s", targ,
! 749: *targ ? "/" : "", cp);
! 750: np = namebuf;
! 751: } else
! 752: np = targ;
! 753: exists = stat(np, &stb) == 0;
! 754: if (buf[0] == 'D') {
! 755: int mod_flag = pflag;
! 756: if (exists) {
! 757: if (!S_ISDIR(stb.st_mode)) {
! 758: errno = ENOTDIR;
! 759: goto bad;
! 760: }
! 761: if (pflag)
! 762: (void)chmod(np, mode);
! 763: } else {
! 764: /* Handle copying from a read-only directory */
! 765: mod_flag = 1;
! 766: if (mkdir(np, mode | S_IRWXU) < 0)
! 767: goto bad;
! 768: }
! 769: vect[0] = np;
! 770: sink(1, vect);
! 771: if (setimes) {
! 772: setimes = 0;
! 773: if (utime(np, &ut) < 0)
! 774: run_err("%s: set times: %s",
! 775: np, strerror(errno));
! 776: }
! 777: if (mod_flag)
! 778: (void)chmod(np, mode);
! 779: continue;
! 780: }
! 781: omode = mode;
! 782: mode |= S_IWRITE;
! 783: if ((ofd = open(np, O_WRONLY|O_CREAT|O_TRUNC, mode)) < 0) {
! 784: bad: run_err("%s: %s", np, strerror(errno));
! 785: continue;
! 786: }
! 787: (void)write(remout, "", 1);
! 788: if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) {
! 789: (void)close(ofd);
! 790: continue;
! 791: }
! 792: cp = bp->buf;
! 793: wrerr = NO;
! 794: for (count = i = 0; i < size; i += 4096) {
! 795: amt = 4096;
! 796: if (i + amt > size)
! 797: amt = size - i;
! 798: count += amt;
! 799: do {
! 800: j = read(remin, cp, amt);
! 801: if (j <= 0) {
! 802: run_err("%s", j ? strerror(errno) :
! 803: "dropped connection");
! 804: exit(1);
! 805: }
! 806: amt -= j;
! 807: cp += j;
! 808: } while (amt > 0);
! 809: if (count == bp->cnt) {
! 810: /* Keep reading so we stay sync'd up. */
! 811: if (wrerr == NO) {
! 812: j = write(ofd, bp->buf, count);
! 813: if (j != count) {
! 814: wrerr = YES;
! 815: wrerrno = j >= 0 ? EIO : errno;
! 816: }
! 817: }
! 818: count = 0;
! 819: cp = bp->buf;
! 820: }
! 821: }
! 822: if (count != 0 && wrerr == NO &&
! 823: (j = write(ofd, bp->buf, count)) != count) {
! 824: wrerr = YES;
! 825: wrerrno = j >= 0 ? EIO : errno;
! 826: }
! 827: #if 0
! 828: if (ftruncate(ofd, size)) {
! 829: run_err("%s: truncate: %s", np, strerror(errno));
! 830: wrerr = DISPLAYED;
! 831: }
! 832: #endif
! 833: if (pflag) {
! 834: if (exists || omode != mode)
! 835: #ifdef HAVE_FCHMOD
! 836: if (fchmod(ofd, omode))
! 837: #else /* HAVE_FCHMOD */
! 838: if (chmod(np, omode))
! 839: #endif /* HAVE_FCHMOD */
! 840: run_err("%s: set mode: %s",
! 841: np, strerror(errno));
! 842: } else {
! 843: if (!exists && omode != mode)
! 844: #ifdef HAVE_FCHMOD
! 845: if (fchmod(ofd, omode & ~mask))
! 846: #else /* HAVE_FCHMOD */
! 847: if (chmod(np, omode & ~mask))
! 848: #endif /* HAVE_FCHMOD */
! 849: run_err("%s: set mode: %s",
! 850: np, strerror(errno));
! 851: }
! 852: (void)close(ofd);
! 853: (void)response();
! 854: if (setimes && wrerr == NO) {
! 855: setimes = 0;
! 856: if (utime(np, &ut) < 0) {
! 857: run_err("%s: set times: %s",
! 858: np, strerror(errno));
! 859: wrerr = DISPLAYED;
! 860: }
! 861: }
! 862: switch(wrerr) {
! 863: case YES:
! 864: run_err("%s: %s", np, strerror(wrerrno));
! 865: break;
! 866: case NO:
! 867: (void)write(remout, "", 1);
! 868: break;
! 869: case DISPLAYED:
! 870: break;
! 871: }
! 872: }
! 873: screwup:
! 874: run_err("protocol error: %s", why);
! 875: exit(1);
! 876: }
! 877:
! 878: int
! 879: response()
! 880: {
! 881: char ch, *cp, resp, rbuf[2048];
! 882:
! 883: if (read(remin, &resp, sizeof(resp)) != sizeof(resp))
! 884: lostconn(0);
! 885:
! 886: cp = rbuf;
! 887: switch(resp) {
! 888: case 0: /* ok */
! 889: return (0);
! 890: default:
! 891: *cp++ = resp;
! 892: /* FALLTHROUGH */
! 893: case 1: /* error, followed by error msg */
! 894: case 2: /* fatal error, "" */
! 895: do {
! 896: if (read(remin, &ch, sizeof(ch)) != sizeof(ch))
! 897: lostconn(0);
! 898: *cp++ = ch;
! 899: } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n');
! 900:
! 901: if (!iamremote)
! 902: (void)write(STDERR_FILENO, rbuf, cp - rbuf);
! 903: ++errs;
! 904: if (resp == 1)
! 905: return (-1);
! 906: exit(1);
! 907: }
! 908: /* NOTREACHED */
! 909: }
! 910:
! 911: void
! 912: usage()
! 913: {
! 914: (void)fprintf(stderr,
! 915: "usage: scp [-p] f1 f2; or: scp [-pr] f1 ... fn directory\n");
! 916: exit(1);
! 917: }
! 918:
! 919: void
! 920: run_err(const char *fmt, ...)
! 921: {
! 922: static FILE *fp;
! 923: va_list ap;
! 924: va_start(ap, fmt);
! 925:
! 926: ++errs;
! 927: if (fp == NULL && !(fp = fdopen(remout, "w")))
! 928: return;
! 929: (void)fprintf(fp, "%c", 0x01);
! 930: (void)fprintf(fp, "scp: ");
! 931: (void)vfprintf(fp, fmt, ap);
! 932: (void)fprintf(fp, "\n");
! 933: (void)fflush(fp);
! 934:
! 935: if (!iamremote)
! 936: {
! 937: vfprintf(stderr, fmt, ap);
! 938: fprintf(stderr, "\n");
! 939: }
! 940:
! 941: va_end(ap);
! 942: }
! 943:
! 944: /* Stuff below is from BSD rcp util.c. */
! 945:
! 946: /*-
! 947: * Copyright (c) 1992, 1993
! 948: * The Regents of the University of California. All rights reserved.
! 949: *
! 950: * Redistribution and use in source and binary forms, with or without
! 951: * modification, are permitted provided that the following conditions
! 952: * are met:
! 953: * 1. Redistributions of source code must retain the above copyright
! 954: * notice, this list of conditions and the following disclaimer.
! 955: * 2. Redistributions in binary form must reproduce the above copyright
! 956: * notice, this list of conditions and the following disclaimer in the
! 957: * documentation and/or other materials provided with the distribution.
! 958: * 3. All advertising materials mentioning features or use of this software
! 959: * must display the following acknowledgement:
! 960: * This product includes software developed by the University of
! 961: * California, Berkeley and its contributors.
! 962: * 4. Neither the name of the University nor the names of its contributors
! 963: * may be used to endorse or promote products derived from this software
! 964: * without specific prior written permission.
! 965: *
! 966: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
! 967: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 968: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 969: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
! 970: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 971: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 972: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 973: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 974: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 975: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 976: * SUCH DAMAGE.
! 977: *
! 978: * $Id: scp.c,v 1.6 1999/05/04 11:59:09 bg Exp $
! 979: */
! 980:
! 981: char *
! 982: colon(cp)
! 983: char *cp;
! 984: {
! 985: if (*cp == ':') /* Leading colon is part of file name. */
! 986: return (0);
! 987:
! 988: for (; *cp; ++cp) {
! 989: if (*cp == ':')
! 990: return (cp);
! 991: if (*cp == '/')
! 992: return (0);
! 993: }
! 994: return (0);
! 995: }
! 996:
! 997: void
! 998: verifydir(cp)
! 999: char *cp;
! 1000: {
! 1001: struct stat stb;
! 1002:
! 1003: if (!stat(cp, &stb)) {
! 1004: if (S_ISDIR(stb.st_mode))
! 1005: return;
! 1006: errno = ENOTDIR;
! 1007: }
! 1008: run_err("%s: %s", cp, strerror(errno));
! 1009: exit(1);
! 1010: }
! 1011:
! 1012: int
! 1013: okname(cp0)
! 1014: char *cp0;
! 1015: {
! 1016: int c;
! 1017: char *cp;
! 1018:
! 1019: cp = cp0;
! 1020: do {
! 1021: c = *cp;
! 1022: if (c & 0200)
! 1023: goto bad;
! 1024: if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
! 1025: goto bad;
! 1026: } while (*++cp);
! 1027: return (1);
! 1028:
! 1029: bad: fprintf(stderr, "%s: invalid user name", cp0);
! 1030: return (0);
! 1031: }
! 1032:
! 1033: BUF *
! 1034: allocbuf(bp, fd, blksize)
! 1035: BUF *bp;
! 1036: int fd, blksize;
! 1037: {
! 1038: size_t size;
! 1039: #ifdef HAVE_ST_BLKSIZE
! 1040: struct stat stb;
! 1041:
! 1042: if (fstat(fd, &stb) < 0) {
! 1043: run_err("fstat: %s", strerror(errno));
! 1044: return (0);
! 1045: }
! 1046: if (stb.st_blksize == 0)
! 1047: size = blksize;
! 1048: else
! 1049: size = blksize + (stb.st_blksize - blksize % stb.st_blksize) %
! 1050: stb.st_blksize;
! 1051: #else /* HAVE_ST_BLKSIZE */
! 1052: size = blksize;
! 1053: #endif /* HAVE_ST_BLKSIZE */
! 1054: if (bp->cnt >= size)
! 1055: return (bp);
! 1056: if (bp->buf == NULL)
! 1057: bp->buf = xmalloc(size);
! 1058: else
! 1059: bp->buf = xrealloc(bp->buf, size);
! 1060: bp->cnt = size;
! 1061: return (bp);
! 1062: }
! 1063:
! 1064: void
! 1065: lostconn(signo)
! 1066: int signo;
! 1067: {
! 1068: if (!iamremote)
! 1069: fprintf(stderr, "lost connection\n");
! 1070: exit(1);
! 1071: }