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