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