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