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