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