scp.c revision 221420
1221420Sdes/* $OpenBSD: scp.c,v 1.170 2010/12/09 14:13:33 jmc Exp $ */ 257429Smarkm/* 365668Skris * scp - secure remote copy. This is basically patched BSD rcp which 465668Skris * uses ssh to do the data transfer (instead of using rcmd). 560573Skris * 665668Skris * NOTE: This version should NOT be suid root. (This uses ssh to 765668Skris * do the transfer and ssh has the necessary privileges.) 860573Skris * 957429Smarkm * 1995 Timo Rinne <tri@iki.fi>, Tatu Ylonen <ylo@cs.hut.fi> 1060573Skris * 1165668Skris * As far as I am concerned, the code I have written for this software 1265668Skris * can be used freely for any purpose. Any derived versions of this 1365668Skris * software must be clearly marked as such, and if the derived work is 1465668Skris * incompatible with the protocol description in the RFC file, it must be 1565668Skris * called by a name other than "ssh" or "Secure Shell". 1665668Skris */ 1765668Skris/* 1876259Sgreen * Copyright (c) 1999 Theo de Raadt. All rights reserved. 1976259Sgreen * Copyright (c) 1999 Aaron Campbell. All rights reserved. 2065668Skris * 2165668Skris * Redistribution and use in source and binary forms, with or without 2265668Skris * modification, are permitted provided that the following conditions 2365668Skris * are met: 2465668Skris * 1. Redistributions of source code must retain the above copyright 2565668Skris * notice, this list of conditions and the following disclaimer. 2665668Skris * 2. Redistributions in binary form must reproduce the above copyright 2765668Skris * notice, this list of conditions and the following disclaimer in the 2865668Skris * documentation and/or other materials provided with the distribution. 2965668Skris * 3065668Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 3165668Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 3265668Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 3365668Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 3465668Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 3565668Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3665668Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3765668Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3865668Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3965668Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 4065668Skris */ 4157429Smarkm 4257429Smarkm/* 4365668Skris * Parts from: 4465668Skris * 4557429Smarkm * Copyright (c) 1983, 1990, 1992, 1993, 1995 4657429Smarkm * The Regents of the University of California. All rights reserved. 4757429Smarkm * 4857429Smarkm * Redistribution and use in source and binary forms, with or without 4957429Smarkm * modification, are permitted provided that the following conditions 5057429Smarkm * are met: 5157429Smarkm * 1. Redistributions of source code must retain the above copyright 5257429Smarkm * notice, this list of conditions and the following disclaimer. 5357429Smarkm * 2. Redistributions in binary form must reproduce the above copyright 5457429Smarkm * notice, this list of conditions and the following disclaimer in the 5557429Smarkm * documentation and/or other materials provided with the distribution. 56124211Sdes * 3. Neither the name of the University nor the names of its contributors 5757429Smarkm * may be used to endorse or promote products derived from this software 5857429Smarkm * without specific prior written permission. 5957429Smarkm * 6057429Smarkm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 6157429Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 6257429Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 6357429Smarkm * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 6457429Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 6557429Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 6657429Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 6757429Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 6857429Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 6957429Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 7057429Smarkm * SUCH DAMAGE. 7157429Smarkm * 7257429Smarkm */ 7357429Smarkm 7457429Smarkm#include "includes.h" 7557429Smarkm 76162856Sdes#include <sys/types.h> 77162856Sdes#include <sys/param.h> 78162856Sdes#ifdef HAVE_SYS_STAT_H 79162856Sdes# include <sys/stat.h> 80162856Sdes#endif 81181111Sdes#ifdef HAVE_POLL_H 82181111Sdes#include <poll.h> 83181111Sdes#else 84181111Sdes# ifdef HAVE_SYS_POLL_H 85181111Sdes# include <sys/poll.h> 86181111Sdes# endif 87181111Sdes#endif 88162856Sdes#ifdef HAVE_SYS_TIME_H 89162856Sdes# include <sys/time.h> 90162856Sdes#endif 91162856Sdes#include <sys/wait.h> 92162856Sdes#include <sys/uio.h> 93162856Sdes 94162856Sdes#include <ctype.h> 95162856Sdes#include <dirent.h> 96162856Sdes#include <errno.h> 97162856Sdes#include <fcntl.h> 98162856Sdes#include <pwd.h> 99162856Sdes#include <signal.h> 100162856Sdes#include <stdarg.h> 101162856Sdes#include <stdio.h> 102162856Sdes#include <stdlib.h> 103162856Sdes#include <string.h> 104162856Sdes#include <time.h> 105162856Sdes#include <unistd.h> 106181111Sdes#if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) 107181111Sdes#include <vis.h> 108181111Sdes#endif 109162856Sdes 11057429Smarkm#include "xmalloc.h" 11176259Sgreen#include "atomicio.h" 11276259Sgreen#include "pathnames.h" 11376259Sgreen#include "log.h" 11492555Sdes#include "misc.h" 115113911Sdes#include "progressmeter.h" 11657429Smarkm 11798937Sdesextern char *__progname; 11898937Sdes 119181111Sdes#define COPY_BUFLEN 16384 120181111Sdes 121162856Sdesint do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout); 122221420Sdesint do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout); 123162856Sdes 12492555Sdes/* Struct for addargs */ 12592555Sdesarglist args; 126221420Sdesarglist remote_remote_args; 12769587Sgreen 128113911Sdes/* Bandwidth limit */ 129221420Sdeslong long limit_kbps = 0; 130221420Sdesstruct bwlimit bwlimit; 13157429Smarkm 13257429Smarkm/* Name of current file being transferred. */ 13357429Smarkmchar *curfile; 13457429Smarkm 13557429Smarkm/* This is set to non-zero to enable verbose mode. */ 13657429Smarkmint verbose_mode = 0; 13757429Smarkm 13857429Smarkm/* This is set to zero if the progressmeter is not desired. */ 13957429Smarkmint showprogress = 1; 14057429Smarkm 141221420Sdes/* 142221420Sdes * This is set to non-zero if remote-remote copy should be piped 143221420Sdes * through this process. 144221420Sdes */ 145221420Sdesint throughlocal = 0; 146221420Sdes 14765668Skris/* This is the program to execute for the secured connection. ("ssh" or -S) */ 14876259Sgreenchar *ssh_program = _PATH_SSH_PROGRAM; 14965668Skris 150113911Sdes/* This is used to store the pid of ssh_program */ 151124211Sdespid_t do_cmd_pid = -1; 152113911Sdes 153124211Sdesstatic void 154124211Sdeskillchild(int signo) 155124211Sdes{ 156147005Sdes if (do_cmd_pid > 1) { 157149753Sdes kill(do_cmd_pid, signo ? signo : SIGTERM); 158147005Sdes waitpid(do_cmd_pid, NULL, 0); 159147005Sdes } 160124211Sdes 161149753Sdes if (signo) 162149753Sdes _exit(1); 163149753Sdes exit(1); 164124211Sdes} 165124211Sdes 166215116Sdesstatic void 167215116Sdessuspchild(int signo) 168215116Sdes{ 169215116Sdes int status; 170215116Sdes 171215116Sdes if (do_cmd_pid > 1) { 172215116Sdes kill(do_cmd_pid, signo); 173215116Sdes while (waitpid(do_cmd_pid, &status, WUNTRACED) == -1 && 174215116Sdes errno == EINTR) 175215116Sdes ; 176215116Sdes kill(getpid(), SIGSTOP); 177215116Sdes } 178215116Sdes} 179215116Sdes 180157019Sdesstatic int 181157019Sdesdo_local_cmd(arglist *a) 182157019Sdes{ 183157019Sdes u_int i; 184157019Sdes int status; 185157019Sdes pid_t pid; 186157019Sdes 187157019Sdes if (a->num == 0) 188157019Sdes fatal("do_local_cmd: no arguments"); 189157019Sdes 190157019Sdes if (verbose_mode) { 191157019Sdes fprintf(stderr, "Executing:"); 192157019Sdes for (i = 0; i < a->num; i++) 193157019Sdes fprintf(stderr, " %s", a->list[i]); 194157019Sdes fprintf(stderr, "\n"); 195157019Sdes } 196157019Sdes if ((pid = fork()) == -1) 197157019Sdes fatal("do_local_cmd: fork: %s", strerror(errno)); 198157019Sdes 199157019Sdes if (pid == 0) { 200157019Sdes execvp(a->list[0], a->list); 201157019Sdes perror(a->list[0]); 202157019Sdes exit(1); 203157019Sdes } 204157019Sdes 205157019Sdes do_cmd_pid = pid; 206157019Sdes signal(SIGTERM, killchild); 207157019Sdes signal(SIGINT, killchild); 208157019Sdes signal(SIGHUP, killchild); 209157019Sdes 210157019Sdes while (waitpid(pid, &status, 0) == -1) 211157019Sdes if (errno != EINTR) 212157019Sdes fatal("do_local_cmd: waitpid: %s", strerror(errno)); 213157019Sdes 214157019Sdes do_cmd_pid = -1; 215157019Sdes 216157019Sdes if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) 217157019Sdes return (-1); 218157019Sdes 219157019Sdes return (0); 220157019Sdes} 221157019Sdes 22257429Smarkm/* 22357429Smarkm * This function executes the given command as the specified user on the 22457429Smarkm * given host. This returns < 0 if execution fails, and >= 0 otherwise. This 22557429Smarkm * assigns the input and output file descriptors on success. 22657429Smarkm */ 22757429Smarkm 22860573Skrisint 229162856Sdesdo_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) 23057429Smarkm{ 23157429Smarkm int pin[2], pout[2], reserved[2]; 23257429Smarkm 23357429Smarkm if (verbose_mode) 23492555Sdes fprintf(stderr, 23592555Sdes "Executing: program %s host %s, user %s, command %s\n", 23692555Sdes ssh_program, host, 23792555Sdes remuser ? remuser : "(unspecified)", cmd); 23857429Smarkm 23957429Smarkm /* 24057429Smarkm * Reserve two descriptors so that the real pipes won't get 24157429Smarkm * descriptors 0 and 1 because that will screw up dup2 below. 24257429Smarkm */ 243162856Sdes if (pipe(reserved) < 0) 244162856Sdes fatal("pipe: %s", strerror(errno)); 24557429Smarkm 24657429Smarkm /* Create a socket pair for communicating with ssh. */ 24757429Smarkm if (pipe(pin) < 0) 24857429Smarkm fatal("pipe: %s", strerror(errno)); 24957429Smarkm if (pipe(pout) < 0) 25057429Smarkm fatal("pipe: %s", strerror(errno)); 25157429Smarkm 25257429Smarkm /* Free the reserved descriptors. */ 25357429Smarkm close(reserved[0]); 25457429Smarkm close(reserved[1]); 25557429Smarkm 256215116Sdes signal(SIGTSTP, suspchild); 257215116Sdes signal(SIGTTIN, suspchild); 258215116Sdes signal(SIGTTOU, suspchild); 259215116Sdes 260124211Sdes /* Fork a child to execute the command on the remote host using ssh. */ 261113911Sdes do_cmd_pid = fork(); 262113911Sdes if (do_cmd_pid == 0) { 26357429Smarkm /* Child. */ 26457429Smarkm close(pin[1]); 26557429Smarkm close(pout[0]); 26657429Smarkm dup2(pin[0], 0); 26757429Smarkm dup2(pout[1], 1); 26857429Smarkm close(pin[0]); 26957429Smarkm close(pout[1]); 27057429Smarkm 271157019Sdes replacearg(&args, 0, "%s", ssh_program); 272204917Sdes if (remuser != NULL) { 273204917Sdes addargs(&args, "-l"); 274204917Sdes addargs(&args, "%s", remuser); 275204917Sdes } 276204917Sdes addargs(&args, "--"); 27792555Sdes addargs(&args, "%s", host); 27892555Sdes addargs(&args, "%s", cmd); 27957429Smarkm 28069587Sgreen execvp(ssh_program, args.list); 28165668Skris perror(ssh_program); 28257429Smarkm exit(1); 283113911Sdes } else if (do_cmd_pid == -1) { 284113911Sdes fatal("fork: %s", strerror(errno)); 28557429Smarkm } 28657429Smarkm /* Parent. Close the other side, and return the local side. */ 28757429Smarkm close(pin[0]); 28857429Smarkm *fdout = pin[1]; 28957429Smarkm close(pout[1]); 29057429Smarkm *fdin = pout[0]; 291124211Sdes signal(SIGTERM, killchild); 292124211Sdes signal(SIGINT, killchild); 293124211Sdes signal(SIGHUP, killchild); 29457429Smarkm return 0; 29557429Smarkm} 29657429Smarkm 297221420Sdes/* 298221420Sdes * This functions executes a command simlar to do_cmd(), but expects the 299221420Sdes * input and output descriptors to be setup by a previous call to do_cmd(). 300221420Sdes * This way the input and output of two commands can be connected. 301221420Sdes */ 302221420Sdesint 303221420Sdesdo_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout) 304221420Sdes{ 305221420Sdes pid_t pid; 306221420Sdes int status; 307221420Sdes 308221420Sdes if (verbose_mode) 309221420Sdes fprintf(stderr, 310221420Sdes "Executing: 2nd program %s host %s, user %s, command %s\n", 311221420Sdes ssh_program, host, 312221420Sdes remuser ? remuser : "(unspecified)", cmd); 313221420Sdes 314221420Sdes /* Fork a child to execute the command on the remote host using ssh. */ 315221420Sdes pid = fork(); 316221420Sdes if (pid == 0) { 317221420Sdes dup2(fdin, 0); 318221420Sdes dup2(fdout, 1); 319221420Sdes 320221420Sdes replacearg(&args, 0, "%s", ssh_program); 321221420Sdes if (remuser != NULL) { 322221420Sdes addargs(&args, "-l"); 323221420Sdes addargs(&args, "%s", remuser); 324221420Sdes } 325221420Sdes addargs(&args, "--"); 326221420Sdes addargs(&args, "%s", host); 327221420Sdes addargs(&args, "%s", cmd); 328221420Sdes 329221420Sdes execvp(ssh_program, args.list); 330221420Sdes perror(ssh_program); 331221420Sdes exit(1); 332221420Sdes } else if (pid == -1) { 333221420Sdes fatal("fork: %s", strerror(errno)); 334221420Sdes } 335221420Sdes while (waitpid(pid, &status, 0) == -1) 336221420Sdes if (errno != EINTR) 337221420Sdes fatal("do_cmd2: waitpid: %s", strerror(errno)); 338221420Sdes return 0; 339221420Sdes} 340221420Sdes 34157429Smarkmtypedef struct { 342149753Sdes size_t cnt; 34357429Smarkm char *buf; 34457429Smarkm} BUF; 34557429Smarkm 34657429SmarkmBUF *allocbuf(BUF *, int, int); 34757429Smarkmvoid lostconn(int); 34857429Smarkmint okname(char *); 34957429Smarkmvoid run_err(const char *,...); 35057429Smarkmvoid verifydir(char *); 35157429Smarkm 35257429Smarkmstruct passwd *pwd; 35357429Smarkmuid_t userid; 35457429Smarkmint errs, remin, remout; 35557429Smarkmint pflag, iamremote, iamrecursive, targetshouldbedirectory; 35657429Smarkm 35757429Smarkm#define CMDNEEDS 64 35857429Smarkmchar cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ 35957429Smarkm 36057429Smarkmint response(void); 36157429Smarkmvoid rsource(char *, struct stat *); 36257429Smarkmvoid sink(int, char *[]); 36357429Smarkmvoid source(int, char *[]); 36457429Smarkmvoid tolocal(int, char *[]); 36557429Smarkmvoid toremote(char *, int, char *[]); 36657429Smarkmvoid usage(void); 36757429Smarkm 36857429Smarkmint 369124211Sdesmain(int argc, char **argv) 37057429Smarkm{ 371162856Sdes int ch, fflag, tflag, status, n; 372221420Sdes char *targ, **newargv; 373221420Sdes const char *errstr; 37457429Smarkm extern char *optarg; 37557429Smarkm extern int optind; 37657429Smarkm 377157019Sdes /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 378157019Sdes sanitise_stdfd(); 379157019Sdes 380162856Sdes /* Copy argv, because we modify it */ 381162856Sdes newargv = xcalloc(MAX(argc + 1, 1), sizeof(*newargv)); 382162856Sdes for (n = 0; n < argc; n++) 383162856Sdes newargv[n] = xstrdup(argv[n]); 384162856Sdes argv = newargv; 385162856Sdes 386124211Sdes __progname = ssh_get_progname(argv[0]); 38798937Sdes 388157019Sdes memset(&args, '\0', sizeof(args)); 389221420Sdes memset(&remote_remote_args, '\0', sizeof(remote_remote_args)); 390221420Sdes args.list = remote_remote_args.list = NULL; 391157019Sdes addargs(&args, "%s", ssh_program); 39292555Sdes addargs(&args, "-x"); 393221420Sdes addargs(&args, "-oForwardAgent=no"); 394221420Sdes addargs(&args, "-oPermitLocalCommand=no"); 395221420Sdes addargs(&args, "-oClearAllForwardings=yes"); 39669587Sgreen 39757429Smarkm fflag = tflag = 0; 398221420Sdes while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q12346S:o:F:")) != -1) 39957429Smarkm switch (ch) { 40057429Smarkm /* User-visible flags. */ 401113911Sdes case '1': 402113911Sdes case '2': 40357429Smarkm case '4': 40457429Smarkm case '6': 40569587Sgreen case 'C': 40692555Sdes addargs(&args, "-%c", ch); 407221420Sdes addargs(&remote_remote_args, "-%c", ch); 40857429Smarkm break; 409221420Sdes case '3': 410221420Sdes throughlocal = 1; 411221420Sdes break; 41269587Sgreen case 'o': 41369587Sgreen case 'c': 41469587Sgreen case 'i': 41592555Sdes case 'F': 416221420Sdes addargs(&remote_remote_args, "-%c", ch); 417221420Sdes addargs(&remote_remote_args, "%s", optarg); 418204917Sdes addargs(&args, "-%c", ch); 419204917Sdes addargs(&args, "%s", optarg); 42069587Sgreen break; 42169587Sgreen case 'P': 422221420Sdes addargs(&remote_remote_args, "-p"); 423221420Sdes addargs(&remote_remote_args, "%s", optarg); 424204917Sdes addargs(&args, "-p"); 425204917Sdes addargs(&args, "%s", optarg); 42669587Sgreen break; 42769587Sgreen case 'B': 428221420Sdes addargs(&remote_remote_args, "-oBatchmode=yes"); 429221420Sdes addargs(&args, "-oBatchmode=yes"); 43069587Sgreen break; 431113911Sdes case 'l': 432221420Sdes limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024, 433221420Sdes &errstr); 434221420Sdes if (errstr != NULL) 435113911Sdes usage(); 436221420Sdes limit_kbps *= 1024; /* kbps */ 437221420Sdes bandwidth_limit_init(&bwlimit, limit_kbps, COPY_BUFLEN); 438113911Sdes break; 43957429Smarkm case 'p': 44057429Smarkm pflag = 1; 44157429Smarkm break; 44257429Smarkm case 'r': 44357429Smarkm iamrecursive = 1; 44457429Smarkm break; 44565668Skris case 'S': 44669587Sgreen ssh_program = xstrdup(optarg); 44765668Skris break; 44869587Sgreen case 'v': 44992555Sdes addargs(&args, "-v"); 450221420Sdes addargs(&remote_remote_args, "-v"); 45169587Sgreen verbose_mode = 1; 45269587Sgreen break; 45369587Sgreen case 'q': 454126277Sdes addargs(&args, "-q"); 455221420Sdes addargs(&remote_remote_args, "-q"); 45669587Sgreen showprogress = 0; 45769587Sgreen break; 45865668Skris 45957429Smarkm /* Server options. */ 46057429Smarkm case 'd': 46157429Smarkm targetshouldbedirectory = 1; 46257429Smarkm break; 46357429Smarkm case 'f': /* "from" */ 46457429Smarkm iamremote = 1; 46557429Smarkm fflag = 1; 46657429Smarkm break; 46757429Smarkm case 't': /* "to" */ 46857429Smarkm iamremote = 1; 46957429Smarkm tflag = 1; 47098937Sdes#ifdef HAVE_CYGWIN 47198937Sdes setmode(0, O_BINARY); 47298937Sdes#endif 47357429Smarkm break; 47457429Smarkm default: 47557429Smarkm usage(); 47657429Smarkm } 47757429Smarkm argc -= optind; 47857429Smarkm argv += optind; 47957429Smarkm 48057429Smarkm if ((pwd = getpwuid(userid = getuid())) == NULL) 481124211Sdes fatal("unknown user %u", (u_int) userid); 48257429Smarkm 483181111Sdes if (!isatty(STDOUT_FILENO)) 48457429Smarkm showprogress = 0; 48557429Smarkm 48657429Smarkm remin = STDIN_FILENO; 48757429Smarkm remout = STDOUT_FILENO; 48857429Smarkm 48976259Sgreen if (fflag) { 49057429Smarkm /* Follow "protocol", send data. */ 49157429Smarkm (void) response(); 49257429Smarkm source(argc, argv); 49357429Smarkm exit(errs != 0); 49457429Smarkm } 49557429Smarkm if (tflag) { 49657429Smarkm /* Receive data. */ 49757429Smarkm sink(argc, argv); 49857429Smarkm exit(errs != 0); 49957429Smarkm } 50057429Smarkm if (argc < 2) 50157429Smarkm usage(); 50257429Smarkm if (argc > 2) 50357429Smarkm targetshouldbedirectory = 1; 50457429Smarkm 50557429Smarkm remin = remout = -1; 506113911Sdes do_cmd_pid = -1; 50757429Smarkm /* Command to be executed on remote system using "ssh". */ 50876259Sgreen (void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s", 50976259Sgreen verbose_mode ? " -v" : "", 51065668Skris iamrecursive ? " -r" : "", pflag ? " -p" : "", 51165668Skris targetshouldbedirectory ? " -d" : ""); 51257429Smarkm 51357429Smarkm (void) signal(SIGPIPE, lostconn); 51457429Smarkm 51557429Smarkm if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ 51657429Smarkm toremote(targ, argc, argv); 51757429Smarkm else { 51857429Smarkm if (targetshouldbedirectory) 51957429Smarkm verifydir(argv[argc - 1]); 520157019Sdes tolocal(argc, argv); /* Dest is local host. */ 52157429Smarkm } 522113911Sdes /* 523113911Sdes * Finally check the exit status of the ssh process, if one was forked 524192595Sdes * and no error has occurred yet 525113911Sdes */ 526113911Sdes if (do_cmd_pid != -1 && errs == 0) { 527113911Sdes if (remin != -1) 528113911Sdes (void) close(remin); 529113911Sdes if (remout != -1) 530113911Sdes (void) close(remout); 531113911Sdes if (waitpid(do_cmd_pid, &status, 0) == -1) 532113911Sdes errs = 1; 533113911Sdes else { 534113911Sdes if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) 535113911Sdes errs = 1; 536113911Sdes } 537113911Sdes } 53857429Smarkm exit(errs != 0); 53957429Smarkm} 54057429Smarkm 541221420Sdes/* Callback from atomicio6 to update progress meter and limit bandwidth */ 542221420Sdesstatic int 543221420Sdesscpio(void *_cnt, size_t s) 544181111Sdes{ 545221420Sdes off_t *cnt = (off_t *)_cnt; 546181111Sdes 547221420Sdes *cnt += s; 548221420Sdes if (limit_kbps > 0) 549221420Sdes bandwidth_limit(&bwlimit, s); 550221420Sdes return 0; 551181111Sdes} 552181111Sdes 55357429Smarkmvoid 554124211Sdestoremote(char *targ, int argc, char **argv) 55557429Smarkm{ 556147005Sdes char *bp, *host, *src, *suser, *thost, *tuser, *arg; 557157019Sdes arglist alist; 558162856Sdes int i; 559221420Sdes u_int j; 56057429Smarkm 561157019Sdes memset(&alist, '\0', sizeof(alist)); 562157019Sdes alist.list = NULL; 563157019Sdes 56457429Smarkm *targ++ = 0; 56557429Smarkm if (*targ == 0) 56657429Smarkm targ = "."; 56757429Smarkm 568147005Sdes arg = xstrdup(argv[argc - 1]); 569147005Sdes if ((thost = strrchr(arg, '@'))) { 57057429Smarkm /* user@host */ 57157429Smarkm *thost++ = 0; 572147005Sdes tuser = arg; 57357429Smarkm if (*tuser == '\0') 57457429Smarkm tuser = NULL; 57557429Smarkm } else { 576147005Sdes thost = arg; 57757429Smarkm tuser = NULL; 57857429Smarkm } 57957429Smarkm 580157019Sdes if (tuser != NULL && !okname(tuser)) { 581157019Sdes xfree(arg); 582157019Sdes return; 583157019Sdes } 584157019Sdes 58557429Smarkm for (i = 0; i < argc - 1; i++) { 58657429Smarkm src = colon(argv[i]); 587221420Sdes if (src && throughlocal) { /* extended remote to remote */ 588221420Sdes *src++ = 0; 589221420Sdes if (*src == 0) 590221420Sdes src = "."; 591221420Sdes host = strrchr(argv[i], '@'); 592221420Sdes if (host) { 593221420Sdes *host++ = 0; 594221420Sdes host = cleanhostname(host); 595221420Sdes suser = argv[i]; 596221420Sdes if (*suser == '\0') 597221420Sdes suser = pwd->pw_name; 598221420Sdes else if (!okname(suser)) 599221420Sdes continue; 600221420Sdes } else { 601221420Sdes host = cleanhostname(argv[i]); 602221420Sdes suser = NULL; 603221420Sdes } 604221420Sdes xasprintf(&bp, "%s -f -- %s", cmd, src); 605221420Sdes if (do_cmd(host, suser, bp, &remin, &remout) < 0) 606221420Sdes exit(1); 607221420Sdes (void) xfree(bp); 608221420Sdes host = cleanhostname(thost); 609221420Sdes xasprintf(&bp, "%s -t -- %s", cmd, targ); 610221420Sdes if (do_cmd2(host, tuser, bp, remin, remout) < 0) 611221420Sdes exit(1); 612221420Sdes (void) xfree(bp); 613221420Sdes (void) close(remin); 614221420Sdes (void) close(remout); 615221420Sdes remin = remout = -1; 616221420Sdes } else if (src) { /* standard remote to remote */ 617157019Sdes freeargs(&alist); 618157019Sdes addargs(&alist, "%s", ssh_program); 619157019Sdes addargs(&alist, "-x"); 620221420Sdes addargs(&alist, "-oClearAllForwardings=yes"); 621157019Sdes addargs(&alist, "-n"); 622221420Sdes for (j = 0; j < remote_remote_args.num; j++) { 623221420Sdes addargs(&alist, "%s", 624221420Sdes remote_remote_args.list[j]); 625221420Sdes } 62657429Smarkm *src++ = 0; 62757429Smarkm if (*src == 0) 62857429Smarkm src = "."; 629113911Sdes host = strrchr(argv[i], '@'); 630157019Sdes 63157429Smarkm if (host) { 63257429Smarkm *host++ = 0; 63357429Smarkm host = cleanhostname(host); 63457429Smarkm suser = argv[i]; 63557429Smarkm if (*suser == '\0') 63657429Smarkm suser = pwd->pw_name; 637157019Sdes else if (!okname(suser)) 63857429Smarkm continue; 639157019Sdes addargs(&alist, "-l"); 640157019Sdes addargs(&alist, "%s", suser); 64157429Smarkm } else { 64257429Smarkm host = cleanhostname(argv[i]); 64357429Smarkm } 644204917Sdes addargs(&alist, "--"); 645157019Sdes addargs(&alist, "%s", host); 646157019Sdes addargs(&alist, "%s", cmd); 647157019Sdes addargs(&alist, "%s", src); 648157019Sdes addargs(&alist, "%s%s%s:%s", 649157019Sdes tuser ? tuser : "", tuser ? "@" : "", 650157019Sdes thost, targ); 651157019Sdes if (do_local_cmd(&alist) != 0) 652126277Sdes errs = 1; 65357429Smarkm } else { /* local to remote */ 65457429Smarkm if (remin == -1) { 655204917Sdes xasprintf(&bp, "%s -t -- %s", cmd, targ); 65657429Smarkm host = cleanhostname(thost); 65765668Skris if (do_cmd(host, tuser, bp, &remin, 658162856Sdes &remout) < 0) 65957429Smarkm exit(1); 66057429Smarkm if (response() < 0) 66157429Smarkm exit(1); 66257429Smarkm (void) xfree(bp); 66357429Smarkm } 66457429Smarkm source(1, argv + i); 66557429Smarkm } 66657429Smarkm } 667162856Sdes xfree(arg); 66857429Smarkm} 66957429Smarkm 67057429Smarkmvoid 671124211Sdestolocal(int argc, char **argv) 67257429Smarkm{ 67357429Smarkm char *bp, *host, *src, *suser; 674157019Sdes arglist alist; 675162856Sdes int i; 67657429Smarkm 677157019Sdes memset(&alist, '\0', sizeof(alist)); 678157019Sdes alist.list = NULL; 679157019Sdes 68057429Smarkm for (i = 0; i < argc - 1; i++) { 68157429Smarkm if (!(src = colon(argv[i]))) { /* Local to local. */ 682157019Sdes freeargs(&alist); 683157019Sdes addargs(&alist, "%s", _PATH_CP); 684157019Sdes if (iamrecursive) 685157019Sdes addargs(&alist, "-r"); 686157019Sdes if (pflag) 687157019Sdes addargs(&alist, "-p"); 688204917Sdes addargs(&alist, "--"); 689157019Sdes addargs(&alist, "%s", argv[i]); 690157019Sdes addargs(&alist, "%s", argv[argc-1]); 691157019Sdes if (do_local_cmd(&alist)) 69257429Smarkm ++errs; 69357429Smarkm continue; 69457429Smarkm } 69557429Smarkm *src++ = 0; 69657429Smarkm if (*src == 0) 69757429Smarkm src = "."; 698113911Sdes if ((host = strrchr(argv[i], '@')) == NULL) { 69957429Smarkm host = argv[i]; 70057429Smarkm suser = NULL; 70157429Smarkm } else { 70257429Smarkm *host++ = 0; 70357429Smarkm suser = argv[i]; 70457429Smarkm if (*suser == '\0') 70557429Smarkm suser = pwd->pw_name; 70657429Smarkm } 70757429Smarkm host = cleanhostname(host); 708204917Sdes xasprintf(&bp, "%s -f -- %s", cmd, src); 709162856Sdes if (do_cmd(host, suser, bp, &remin, &remout) < 0) { 71057429Smarkm (void) xfree(bp); 71157429Smarkm ++errs; 71257429Smarkm continue; 71357429Smarkm } 71457429Smarkm xfree(bp); 71557429Smarkm sink(1, argv + argc - 1); 71657429Smarkm (void) close(remin); 71757429Smarkm remin = remout = -1; 71857429Smarkm } 71957429Smarkm} 72057429Smarkm 72157429Smarkmvoid 722124211Sdessource(int argc, char **argv) 72357429Smarkm{ 72457429Smarkm struct stat stb; 72557429Smarkm static BUF buffer; 72657429Smarkm BUF *bp; 727181111Sdes off_t i, statbytes; 728181111Sdes size_t amt; 729149753Sdes int fd = -1, haderr, indx; 730181111Sdes char *last, *name, buf[2048], encname[MAXPATHLEN]; 73176259Sgreen int len; 73257429Smarkm 73357429Smarkm for (indx = 0; indx < argc; ++indx) { 73457429Smarkm name = argv[indx]; 73557429Smarkm statbytes = 0; 73676259Sgreen len = strlen(name); 73776259Sgreen while (len > 1 && name[len-1] == '/') 73876259Sgreen name[--len] = '\0'; 739181111Sdes if ((fd = open(name, O_RDONLY|O_NONBLOCK, 0)) < 0) 740181111Sdes goto syserr; 74192555Sdes if (strchr(name, '\n') != NULL) { 742181111Sdes strnvis(encname, name, sizeof(encname), VIS_NL); 743181111Sdes name = encname; 74492555Sdes } 74557429Smarkm if (fstat(fd, &stb) < 0) { 74657429Smarkmsyserr: run_err("%s: %s", name, strerror(errno)); 74757429Smarkm goto next; 74857429Smarkm } 749181111Sdes if (stb.st_size < 0) { 750181111Sdes run_err("%s: %s", name, "Negative file size"); 751181111Sdes goto next; 752181111Sdes } 753181111Sdes unset_nonblock(fd); 75457429Smarkm switch (stb.st_mode & S_IFMT) { 75557429Smarkm case S_IFREG: 75657429Smarkm break; 75757429Smarkm case S_IFDIR: 75857429Smarkm if (iamrecursive) { 75957429Smarkm rsource(name, &stb); 76057429Smarkm goto next; 76157429Smarkm } 76257429Smarkm /* FALLTHROUGH */ 76357429Smarkm default: 76457429Smarkm run_err("%s: not a regular file", name); 76557429Smarkm goto next; 76657429Smarkm } 76757429Smarkm if ((last = strrchr(name, '/')) == NULL) 76857429Smarkm last = name; 76957429Smarkm else 77057429Smarkm ++last; 77157429Smarkm curfile = last; 77257429Smarkm if (pflag) { 77357429Smarkm /* 77457429Smarkm * Make it compatible with possible future 77557429Smarkm * versions expecting microseconds. 77657429Smarkm */ 77776259Sgreen (void) snprintf(buf, sizeof buf, "T%lu 0 %lu 0\n", 778181111Sdes (u_long) (stb.st_mtime < 0 ? 0 : stb.st_mtime), 779181111Sdes (u_long) (stb.st_atime < 0 ? 0 : stb.st_atime)); 780181111Sdes if (verbose_mode) { 781181111Sdes fprintf(stderr, "File mtime %ld atime %ld\n", 782181111Sdes (long)stb.st_mtime, (long)stb.st_atime); 783181111Sdes fprintf(stderr, "Sending file timestamps: %s", 784181111Sdes buf); 785181111Sdes } 786124211Sdes (void) atomicio(vwrite, remout, buf, strlen(buf)); 78757429Smarkm if (response() < 0) 78857429Smarkm goto next; 78957429Smarkm } 79057429Smarkm#define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) 79176259Sgreen snprintf(buf, sizeof buf, "C%04o %lld %s\n", 79276259Sgreen (u_int) (stb.st_mode & FILEMODEMASK), 793157019Sdes (long long)stb.st_size, last); 79457429Smarkm if (verbose_mode) { 79557429Smarkm fprintf(stderr, "Sending file modes: %s", buf); 79657429Smarkm } 797124211Sdes (void) atomicio(vwrite, remout, buf, strlen(buf)); 79857429Smarkm if (response() < 0) 79957429Smarkm goto next; 800181111Sdes if ((bp = allocbuf(&buffer, fd, COPY_BUFLEN)) == NULL) { 801157019Sdesnext: if (fd != -1) { 802157019Sdes (void) close(fd); 803157019Sdes fd = -1; 804157019Sdes } 80557429Smarkm continue; 80657429Smarkm } 807113911Sdes if (showprogress) 808113911Sdes start_progress_meter(curfile, stb.st_size, &statbytes); 809181111Sdes set_nonblock(remout); 81057429Smarkm for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { 81157429Smarkm amt = bp->cnt; 812181111Sdes if (i + (off_t)amt > stb.st_size) 81357429Smarkm amt = stb.st_size - i; 81457429Smarkm if (!haderr) { 815181111Sdes if (atomicio(read, fd, bp->buf, amt) != amt) 816149753Sdes haderr = errno; 81757429Smarkm } 818181111Sdes /* Keep writing after error to retain sync */ 819181111Sdes if (haderr) { 820181111Sdes (void)atomicio(vwrite, remout, bp->buf, amt); 821181111Sdes continue; 82257429Smarkm } 823221420Sdes if (atomicio6(vwrite, remout, bp->buf, amt, scpio, 824181111Sdes &statbytes) != amt) 825181111Sdes haderr = errno; 82657429Smarkm } 827181111Sdes unset_nonblock(remout); 82857429Smarkm if (showprogress) 829113911Sdes stop_progress_meter(); 83057429Smarkm 831157019Sdes if (fd != -1) { 832157019Sdes if (close(fd) < 0 && !haderr) 833157019Sdes haderr = errno; 834157019Sdes fd = -1; 835157019Sdes } 83657429Smarkm if (!haderr) 837124211Sdes (void) atomicio(vwrite, remout, "", 1); 83857429Smarkm else 83957429Smarkm run_err("%s: %s", name, strerror(haderr)); 84057429Smarkm (void) response(); 84157429Smarkm } 84257429Smarkm} 84357429Smarkm 84457429Smarkmvoid 845124211Sdesrsource(char *name, struct stat *statp) 84657429Smarkm{ 84757429Smarkm DIR *dirp; 84857429Smarkm struct dirent *dp; 84957429Smarkm char *last, *vect[1], path[1100]; 85057429Smarkm 85157429Smarkm if (!(dirp = opendir(name))) { 85257429Smarkm run_err("%s: %s", name, strerror(errno)); 85357429Smarkm return; 85457429Smarkm } 85557429Smarkm last = strrchr(name, '/'); 85657429Smarkm if (last == 0) 85757429Smarkm last = name; 85857429Smarkm else 85957429Smarkm last++; 86057429Smarkm if (pflag) { 86176259Sgreen (void) snprintf(path, sizeof(path), "T%lu 0 %lu 0\n", 86276259Sgreen (u_long) statp->st_mtime, 86376259Sgreen (u_long) statp->st_atime); 864124211Sdes (void) atomicio(vwrite, remout, path, strlen(path)); 86557429Smarkm if (response() < 0) { 86657429Smarkm closedir(dirp); 86757429Smarkm return; 86857429Smarkm } 86957429Smarkm } 87076259Sgreen (void) snprintf(path, sizeof path, "D%04o %d %.1024s\n", 87176259Sgreen (u_int) (statp->st_mode & FILEMODEMASK), 0, last); 87257429Smarkm if (verbose_mode) 87357429Smarkm fprintf(stderr, "Entering directory: %s", path); 874124211Sdes (void) atomicio(vwrite, remout, path, strlen(path)); 87557429Smarkm if (response() < 0) { 87657429Smarkm closedir(dirp); 87757429Smarkm return; 87857429Smarkm } 87976259Sgreen while ((dp = readdir(dirp)) != NULL) { 88057429Smarkm if (dp->d_ino == 0) 88157429Smarkm continue; 88257429Smarkm if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 88357429Smarkm continue; 88457429Smarkm if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path) - 1) { 88557429Smarkm run_err("%s/%s: name too long", name, dp->d_name); 88657429Smarkm continue; 88757429Smarkm } 88876259Sgreen (void) snprintf(path, sizeof path, "%s/%s", name, dp->d_name); 88957429Smarkm vect[0] = path; 89057429Smarkm source(1, vect); 89157429Smarkm } 89257429Smarkm (void) closedir(dirp); 893124211Sdes (void) atomicio(vwrite, remout, "E\n", 2); 89457429Smarkm (void) response(); 89557429Smarkm} 89657429Smarkm 89757429Smarkmvoid 898124211Sdessink(int argc, char **argv) 89957429Smarkm{ 90057429Smarkm static BUF buffer; 90157429Smarkm struct stat stb; 90257429Smarkm enum { 90357429Smarkm YES, NO, DISPLAYED 90457429Smarkm } wrerr; 90557429Smarkm BUF *bp; 906149753Sdes off_t i; 907149753Sdes size_t j, count; 908162856Sdes int amt, exists, first, ofd; 909162856Sdes mode_t mode, omode, mask; 910113911Sdes off_t size, statbytes; 91165668Skris int setimes, targisdir, wrerrno = 0; 91257429Smarkm char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; 91369587Sgreen struct timeval tv[2]; 91457429Smarkm 91576259Sgreen#define atime tv[0] 91676259Sgreen#define mtime tv[1] 917147005Sdes#define SCREWUP(str) { why = str; goto screwup; } 91857429Smarkm 91957429Smarkm setimes = targisdir = 0; 92057429Smarkm mask = umask(0); 92157429Smarkm if (!pflag) 92257429Smarkm (void) umask(mask); 92357429Smarkm if (argc != 1) { 92457429Smarkm run_err("ambiguous target"); 92557429Smarkm exit(1); 92657429Smarkm } 92757429Smarkm targ = *argv; 92857429Smarkm if (targetshouldbedirectory) 92957429Smarkm verifydir(targ); 93057429Smarkm 931124211Sdes (void) atomicio(vwrite, remout, "", 1); 93257429Smarkm if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) 93357429Smarkm targisdir = 1; 93457429Smarkm for (first = 1;; first = 0) { 93557429Smarkm cp = buf; 936149753Sdes if (atomicio(read, remin, cp, 1) != 1) 93757429Smarkm return; 93857429Smarkm if (*cp++ == '\n') 93957429Smarkm SCREWUP("unexpected <newline>"); 94057429Smarkm do { 94160573Skris if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) 94257429Smarkm SCREWUP("lost connection"); 94357429Smarkm *cp++ = ch; 94457429Smarkm } while (cp < &buf[sizeof(buf) - 1] && ch != '\n'); 94557429Smarkm *cp = 0; 946137019Sdes if (verbose_mode) 947137019Sdes fprintf(stderr, "Sink: %s", buf); 94857429Smarkm 94957429Smarkm if (buf[0] == '\01' || buf[0] == '\02') { 95057429Smarkm if (iamremote == 0) 951124211Sdes (void) atomicio(vwrite, STDERR_FILENO, 95276259Sgreen buf + 1, strlen(buf + 1)); 95357429Smarkm if (buf[0] == '\02') 95457429Smarkm exit(1); 95557429Smarkm ++errs; 95657429Smarkm continue; 95757429Smarkm } 95857429Smarkm if (buf[0] == 'E') { 959124211Sdes (void) atomicio(vwrite, remout, "", 1); 96057429Smarkm return; 96157429Smarkm } 96257429Smarkm if (ch == '\n') 96357429Smarkm *--cp = 0; 96457429Smarkm 96557429Smarkm cp = buf; 96657429Smarkm if (*cp == 'T') { 96757429Smarkm setimes++; 96857429Smarkm cp++; 96976259Sgreen mtime.tv_sec = strtol(cp, &cp, 10); 97076259Sgreen if (!cp || *cp++ != ' ') 97157429Smarkm SCREWUP("mtime.sec not delimited"); 97276259Sgreen mtime.tv_usec = strtol(cp, &cp, 10); 97376259Sgreen if (!cp || *cp++ != ' ') 97457429Smarkm SCREWUP("mtime.usec not delimited"); 97576259Sgreen atime.tv_sec = strtol(cp, &cp, 10); 97676259Sgreen if (!cp || *cp++ != ' ') 97757429Smarkm SCREWUP("atime.sec not delimited"); 97876259Sgreen atime.tv_usec = strtol(cp, &cp, 10); 97976259Sgreen if (!cp || *cp++ != '\0') 98057429Smarkm SCREWUP("atime.usec not delimited"); 981124211Sdes (void) atomicio(vwrite, remout, "", 1); 98257429Smarkm continue; 98357429Smarkm } 98457429Smarkm if (*cp != 'C' && *cp != 'D') { 98557429Smarkm /* 98657429Smarkm * Check for the case "rcp remote:foo\* local:bar". 98757429Smarkm * In this case, the line "No match." can be returned 98857429Smarkm * by the shell before the rcp command on the remote is 98957429Smarkm * executed so the ^Aerror_message convention isn't 99057429Smarkm * followed. 99157429Smarkm */ 99257429Smarkm if (first) { 99357429Smarkm run_err("%s", cp); 99457429Smarkm exit(1); 99557429Smarkm } 99657429Smarkm SCREWUP("expected control record"); 99757429Smarkm } 99857429Smarkm mode = 0; 99957429Smarkm for (++cp; cp < buf + 5; cp++) { 100057429Smarkm if (*cp < '0' || *cp > '7') 100157429Smarkm SCREWUP("bad mode"); 100257429Smarkm mode = (mode << 3) | (*cp - '0'); 100357429Smarkm } 100457429Smarkm if (*cp++ != ' ') 100557429Smarkm SCREWUP("mode not delimited"); 100657429Smarkm 100776259Sgreen for (size = 0; isdigit(*cp);) 100857429Smarkm size = size * 10 + (*cp++ - '0'); 100957429Smarkm if (*cp++ != ' ') 101057429Smarkm SCREWUP("size not delimited"); 1011137019Sdes if ((strchr(cp, '/') != NULL) || (strcmp(cp, "..") == 0)) { 1012137019Sdes run_err("error: unexpected filename: %s", cp); 1013137019Sdes exit(1); 1014137019Sdes } 101557429Smarkm if (targisdir) { 101657429Smarkm static char *namebuf; 1017149753Sdes static size_t cursize; 101857429Smarkm size_t need; 101957429Smarkm 102057429Smarkm need = strlen(targ) + strlen(cp) + 250; 102176259Sgreen if (need > cursize) { 102276259Sgreen if (namebuf) 102376259Sgreen xfree(namebuf); 102457429Smarkm namebuf = xmalloc(need); 102576259Sgreen cursize = need; 102676259Sgreen } 102776259Sgreen (void) snprintf(namebuf, need, "%s%s%s", targ, 102898675Sdes strcmp(targ, "/") ? "/" : "", cp); 102957429Smarkm np = namebuf; 103057429Smarkm } else 103157429Smarkm np = targ; 103257429Smarkm curfile = cp; 103357429Smarkm exists = stat(np, &stb) == 0; 103457429Smarkm if (buf[0] == 'D') { 103557429Smarkm int mod_flag = pflag; 1036137019Sdes if (!iamrecursive) 1037137019Sdes SCREWUP("received directory without -r"); 103857429Smarkm if (exists) { 103957429Smarkm if (!S_ISDIR(stb.st_mode)) { 104057429Smarkm errno = ENOTDIR; 104157429Smarkm goto bad; 104257429Smarkm } 104357429Smarkm if (pflag) 104457429Smarkm (void) chmod(np, mode); 104557429Smarkm } else { 104657429Smarkm /* Handle copying from a read-only 104757429Smarkm directory */ 104857429Smarkm mod_flag = 1; 104957429Smarkm if (mkdir(np, mode | S_IRWXU) < 0) 105057429Smarkm goto bad; 105157429Smarkm } 105276259Sgreen vect[0] = xstrdup(np); 105357429Smarkm sink(1, vect); 105457429Smarkm if (setimes) { 105557429Smarkm setimes = 0; 105676259Sgreen if (utimes(vect[0], tv) < 0) 105757429Smarkm run_err("%s: set times: %s", 105876259Sgreen vect[0], strerror(errno)); 105957429Smarkm } 106057429Smarkm if (mod_flag) 106176259Sgreen (void) chmod(vect[0], mode); 106276259Sgreen if (vect[0]) 106376259Sgreen xfree(vect[0]); 106457429Smarkm continue; 106557429Smarkm } 106657429Smarkm omode = mode; 106757429Smarkm mode |= S_IWRITE; 106892555Sdes if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { 106957429Smarkmbad: run_err("%s: %s", np, strerror(errno)); 107057429Smarkm continue; 107157429Smarkm } 1072124211Sdes (void) atomicio(vwrite, remout, "", 1); 1073181111Sdes if ((bp = allocbuf(&buffer, ofd, COPY_BUFLEN)) == NULL) { 107457429Smarkm (void) close(ofd); 107557429Smarkm continue; 107657429Smarkm } 107757429Smarkm cp = bp->buf; 107857429Smarkm wrerr = NO; 107957429Smarkm 108057429Smarkm statbytes = 0; 1081113911Sdes if (showprogress) 1082113911Sdes start_progress_meter(curfile, size, &statbytes); 1083181111Sdes set_nonblock(remin); 1084181111Sdes for (count = i = 0; i < size; i += bp->cnt) { 1085181111Sdes amt = bp->cnt; 108657429Smarkm if (i + amt > size) 108757429Smarkm amt = size - i; 108857429Smarkm count += amt; 108957429Smarkm do { 1090221420Sdes j = atomicio6(read, remin, cp, amt, 1091221420Sdes scpio, &statbytes); 1092149753Sdes if (j == 0) { 1093181111Sdes run_err("%s", j != EPIPE ? 1094181111Sdes strerror(errno) : 109576259Sgreen "dropped connection"); 109657429Smarkm exit(1); 109757429Smarkm } 109857429Smarkm amt -= j; 109957429Smarkm cp += j; 110057429Smarkm } while (amt > 0); 1101126277Sdes 110257429Smarkm if (count == bp->cnt) { 110357429Smarkm /* Keep reading so we stay sync'd up. */ 110457429Smarkm if (wrerr == NO) { 1105149753Sdes if (atomicio(vwrite, ofd, bp->buf, 1106149753Sdes count) != count) { 110757429Smarkm wrerr = YES; 1108149753Sdes wrerrno = errno; 110957429Smarkm } 111057429Smarkm } 111157429Smarkm count = 0; 111257429Smarkm cp = bp->buf; 111357429Smarkm } 111457429Smarkm } 1115181111Sdes unset_nonblock(remin); 111657429Smarkm if (showprogress) 1117113911Sdes stop_progress_meter(); 111857429Smarkm if (count != 0 && wrerr == NO && 1119149753Sdes atomicio(vwrite, ofd, bp->buf, count) != count) { 112057429Smarkm wrerr = YES; 1121149753Sdes wrerrno = errno; 112257429Smarkm } 1123181111Sdes if (wrerr == NO && (!exists || S_ISREG(stb.st_mode)) && 1124181111Sdes ftruncate(ofd, size) != 0) { 112557429Smarkm run_err("%s: truncate: %s", np, strerror(errno)); 112657429Smarkm wrerr = DISPLAYED; 112757429Smarkm } 112857429Smarkm if (pflag) { 112957429Smarkm if (exists || omode != mode) 113098937Sdes#ifdef HAVE_FCHMOD 1131137019Sdes if (fchmod(ofd, omode)) { 113298937Sdes#else /* HAVE_FCHMOD */ 1133137019Sdes if (chmod(np, omode)) { 113498937Sdes#endif /* HAVE_FCHMOD */ 113557429Smarkm run_err("%s: set mode: %s", 113676259Sgreen np, strerror(errno)); 1137137019Sdes wrerr = DISPLAYED; 1138137019Sdes } 113957429Smarkm } else { 114057429Smarkm if (!exists && omode != mode) 114198937Sdes#ifdef HAVE_FCHMOD 1142137019Sdes if (fchmod(ofd, omode & ~mask)) { 114398937Sdes#else /* HAVE_FCHMOD */ 1144137019Sdes if (chmod(np, omode & ~mask)) { 114598937Sdes#endif /* HAVE_FCHMOD */ 114657429Smarkm run_err("%s: set mode: %s", 114776259Sgreen np, strerror(errno)); 1148137019Sdes wrerr = DISPLAYED; 1149137019Sdes } 115057429Smarkm } 115165668Skris if (close(ofd) == -1) { 115265668Skris wrerr = YES; 115365668Skris wrerrno = errno; 115465668Skris } 115557429Smarkm (void) response(); 115657429Smarkm if (setimes && wrerr == NO) { 115757429Smarkm setimes = 0; 115869587Sgreen if (utimes(np, tv) < 0) { 115957429Smarkm run_err("%s: set times: %s", 116076259Sgreen np, strerror(errno)); 116157429Smarkm wrerr = DISPLAYED; 116257429Smarkm } 116357429Smarkm } 116457429Smarkm switch (wrerr) { 116557429Smarkm case YES: 116657429Smarkm run_err("%s: %s", np, strerror(wrerrno)); 116757429Smarkm break; 116857429Smarkm case NO: 1169124211Sdes (void) atomicio(vwrite, remout, "", 1); 117057429Smarkm break; 117157429Smarkm case DISPLAYED: 117257429Smarkm break; 117357429Smarkm } 117457429Smarkm } 117557429Smarkmscrewup: 117657429Smarkm run_err("protocol error: %s", why); 117757429Smarkm exit(1); 117857429Smarkm} 117957429Smarkm 118057429Smarkmint 118192555Sdesresponse(void) 118257429Smarkm{ 118357429Smarkm char ch, *cp, resp, rbuf[2048]; 118457429Smarkm 118560573Skris if (atomicio(read, remin, &resp, sizeof(resp)) != sizeof(resp)) 118657429Smarkm lostconn(0); 118757429Smarkm 118857429Smarkm cp = rbuf; 118957429Smarkm switch (resp) { 119057429Smarkm case 0: /* ok */ 119157429Smarkm return (0); 119257429Smarkm default: 119357429Smarkm *cp++ = resp; 119457429Smarkm /* FALLTHROUGH */ 119557429Smarkm case 1: /* error, followed by error msg */ 119657429Smarkm case 2: /* fatal error, "" */ 119757429Smarkm do { 119860573Skris if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) 119957429Smarkm lostconn(0); 120057429Smarkm *cp++ = ch; 120157429Smarkm } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); 120257429Smarkm 120357429Smarkm if (!iamremote) 1204124211Sdes (void) atomicio(vwrite, STDERR_FILENO, rbuf, cp - rbuf); 120557429Smarkm ++errs; 120657429Smarkm if (resp == 1) 120757429Smarkm return (-1); 120857429Smarkm exit(1); 120957429Smarkm } 121057429Smarkm /* NOTREACHED */ 121157429Smarkm} 121257429Smarkm 121357429Smarkmvoid 121492555Sdesusage(void) 121557429Smarkm{ 121692555Sdes (void) fprintf(stderr, 1217221420Sdes "usage: scp [-12346BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n" 1218126277Sdes " [-l limit] [-o ssh_option] [-P port] [-S program]\n" 1219181111Sdes " [[user@]host1:]file1 ... [[user@]host2:]file2\n"); 122057429Smarkm exit(1); 122157429Smarkm} 122257429Smarkm 122357429Smarkmvoid 122457429Smarkmrun_err(const char *fmt,...) 122557429Smarkm{ 122657429Smarkm static FILE *fp; 122757429Smarkm va_list ap; 122857429Smarkm 122957429Smarkm ++errs; 1230162856Sdes if (fp != NULL || (remout != -1 && (fp = fdopen(remout, "w")))) { 1231162856Sdes (void) fprintf(fp, "%c", 0x01); 1232162856Sdes (void) fprintf(fp, "scp: "); 1233162856Sdes va_start(ap, fmt); 1234162856Sdes (void) vfprintf(fp, fmt, ap); 1235162856Sdes va_end(ap); 1236162856Sdes (void) fprintf(fp, "\n"); 1237162856Sdes (void) fflush(fp); 1238162856Sdes } 123957429Smarkm 124057429Smarkm if (!iamremote) { 124192555Sdes va_start(ap, fmt); 124257429Smarkm vfprintf(stderr, fmt, ap); 124392555Sdes va_end(ap); 124457429Smarkm fprintf(stderr, "\n"); 124557429Smarkm } 124657429Smarkm} 124757429Smarkm 124857429Smarkmvoid 1249124211Sdesverifydir(char *cp) 125057429Smarkm{ 125157429Smarkm struct stat stb; 125257429Smarkm 125357429Smarkm if (!stat(cp, &stb)) { 125457429Smarkm if (S_ISDIR(stb.st_mode)) 125557429Smarkm return; 125657429Smarkm errno = ENOTDIR; 125757429Smarkm } 125857429Smarkm run_err("%s: %s", cp, strerror(errno)); 1259149753Sdes killchild(0); 126057429Smarkm} 126157429Smarkm 126257429Smarkmint 1263124211Sdesokname(char *cp0) 126457429Smarkm{ 126557429Smarkm int c; 126657429Smarkm char *cp; 126757429Smarkm 126857429Smarkm cp = cp0; 126957429Smarkm do { 127092555Sdes c = (int)*cp; 127157429Smarkm if (c & 0200) 127257429Smarkm goto bad; 1273113911Sdes if (!isalpha(c) && !isdigit(c)) { 1274113911Sdes switch (c) { 1275113911Sdes case '\'': 1276113911Sdes case '"': 1277113911Sdes case '`': 1278113911Sdes case ' ': 1279113911Sdes case '#': 1280113911Sdes goto bad; 1281113911Sdes default: 1282113911Sdes break; 1283113911Sdes } 1284113911Sdes } 128557429Smarkm } while (*++cp); 128657429Smarkm return (1); 128757429Smarkm 128857429Smarkmbad: fprintf(stderr, "%s: invalid user name\n", cp0); 128957429Smarkm return (0); 129057429Smarkm} 129157429Smarkm 129257429SmarkmBUF * 1293124211Sdesallocbuf(BUF *bp, int fd, int blksize) 129457429Smarkm{ 129557429Smarkm size_t size; 129698937Sdes#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE 129757429Smarkm struct stat stb; 129857429Smarkm 129957429Smarkm if (fstat(fd, &stb) < 0) { 130057429Smarkm run_err("fstat: %s", strerror(errno)); 130157429Smarkm return (0); 130257429Smarkm } 1303113911Sdes size = roundup(stb.st_blksize, blksize); 1304113911Sdes if (size == 0) 130557429Smarkm size = blksize; 130698937Sdes#else /* HAVE_STRUCT_STAT_ST_BLKSIZE */ 130798937Sdes size = blksize; 130898937Sdes#endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */ 130957429Smarkm if (bp->cnt >= size) 131057429Smarkm return (bp); 131157429Smarkm if (bp->buf == NULL) 131257429Smarkm bp->buf = xmalloc(size); 131357429Smarkm else 1314162856Sdes bp->buf = xrealloc(bp->buf, 1, size); 131592555Sdes memset(bp->buf, 0, size); 131657429Smarkm bp->cnt = size; 131757429Smarkm return (bp); 131857429Smarkm} 131957429Smarkm 132057429Smarkmvoid 1321124211Sdeslostconn(int signo) 132257429Smarkm{ 132357429Smarkm if (!iamremote) 132492555Sdes write(STDERR_FILENO, "lost connection\n", 16); 132592555Sdes if (signo) 132692555Sdes _exit(1); 132792555Sdes else 132892555Sdes exit(1); 132957429Smarkm} 1330