scp.c revision 215116
1215116Sdes/* $OpenBSD: scp.c,v 1.166 2010/07/01 13:06:59 millert 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); 122162856Sdes 123113911Sdesvoid bwlimit(int); 12457429Smarkm 12592555Sdes/* Struct for addargs */ 12692555Sdesarglist args; 12769587Sgreen 128113911Sdes/* Bandwidth limit */ 129126277Sdesoff_t limit_rate = 0; 13057429Smarkm 13157429Smarkm/* Name of current file being transferred. */ 13257429Smarkmchar *curfile; 13357429Smarkm 13457429Smarkm/* This is set to non-zero to enable verbose mode. */ 13557429Smarkmint verbose_mode = 0; 13657429Smarkm 13757429Smarkm/* This is set to zero if the progressmeter is not desired. */ 13857429Smarkmint showprogress = 1; 13957429Smarkm 14065668Skris/* This is the program to execute for the secured connection. ("ssh" or -S) */ 14176259Sgreenchar *ssh_program = _PATH_SSH_PROGRAM; 14265668Skris 143113911Sdes/* This is used to store the pid of ssh_program */ 144124211Sdespid_t do_cmd_pid = -1; 145113911Sdes 146124211Sdesstatic void 147124211Sdeskillchild(int signo) 148124211Sdes{ 149147005Sdes if (do_cmd_pid > 1) { 150149753Sdes kill(do_cmd_pid, signo ? signo : SIGTERM); 151147005Sdes waitpid(do_cmd_pid, NULL, 0); 152147005Sdes } 153124211Sdes 154149753Sdes if (signo) 155149753Sdes _exit(1); 156149753Sdes exit(1); 157124211Sdes} 158124211Sdes 159215116Sdesstatic void 160215116Sdessuspchild(int signo) 161215116Sdes{ 162215116Sdes int status; 163215116Sdes 164215116Sdes if (do_cmd_pid > 1) { 165215116Sdes kill(do_cmd_pid, signo); 166215116Sdes while (waitpid(do_cmd_pid, &status, WUNTRACED) == -1 && 167215116Sdes errno == EINTR) 168215116Sdes ; 169215116Sdes kill(getpid(), SIGSTOP); 170215116Sdes } 171215116Sdes} 172215116Sdes 173157019Sdesstatic int 174157019Sdesdo_local_cmd(arglist *a) 175157019Sdes{ 176157019Sdes u_int i; 177157019Sdes int status; 178157019Sdes pid_t pid; 179157019Sdes 180157019Sdes if (a->num == 0) 181157019Sdes fatal("do_local_cmd: no arguments"); 182157019Sdes 183157019Sdes if (verbose_mode) { 184157019Sdes fprintf(stderr, "Executing:"); 185157019Sdes for (i = 0; i < a->num; i++) 186157019Sdes fprintf(stderr, " %s", a->list[i]); 187157019Sdes fprintf(stderr, "\n"); 188157019Sdes } 189157019Sdes if ((pid = fork()) == -1) 190157019Sdes fatal("do_local_cmd: fork: %s", strerror(errno)); 191157019Sdes 192157019Sdes if (pid == 0) { 193157019Sdes execvp(a->list[0], a->list); 194157019Sdes perror(a->list[0]); 195157019Sdes exit(1); 196157019Sdes } 197157019Sdes 198157019Sdes do_cmd_pid = pid; 199157019Sdes signal(SIGTERM, killchild); 200157019Sdes signal(SIGINT, killchild); 201157019Sdes signal(SIGHUP, killchild); 202157019Sdes 203157019Sdes while (waitpid(pid, &status, 0) == -1) 204157019Sdes if (errno != EINTR) 205157019Sdes fatal("do_local_cmd: waitpid: %s", strerror(errno)); 206157019Sdes 207157019Sdes do_cmd_pid = -1; 208157019Sdes 209157019Sdes if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) 210157019Sdes return (-1); 211157019Sdes 212157019Sdes return (0); 213157019Sdes} 214157019Sdes 21557429Smarkm/* 21657429Smarkm * This function executes the given command as the specified user on the 21757429Smarkm * given host. This returns < 0 if execution fails, and >= 0 otherwise. This 21857429Smarkm * assigns the input and output file descriptors on success. 21957429Smarkm */ 22057429Smarkm 22160573Skrisint 222162856Sdesdo_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) 22357429Smarkm{ 22457429Smarkm int pin[2], pout[2], reserved[2]; 22557429Smarkm 22657429Smarkm if (verbose_mode) 22792555Sdes fprintf(stderr, 22892555Sdes "Executing: program %s host %s, user %s, command %s\n", 22992555Sdes ssh_program, host, 23092555Sdes remuser ? remuser : "(unspecified)", cmd); 23157429Smarkm 23257429Smarkm /* 23357429Smarkm * Reserve two descriptors so that the real pipes won't get 23457429Smarkm * descriptors 0 and 1 because that will screw up dup2 below. 23557429Smarkm */ 236162856Sdes if (pipe(reserved) < 0) 237162856Sdes fatal("pipe: %s", strerror(errno)); 23857429Smarkm 23957429Smarkm /* Create a socket pair for communicating with ssh. */ 24057429Smarkm if (pipe(pin) < 0) 24157429Smarkm fatal("pipe: %s", strerror(errno)); 24257429Smarkm if (pipe(pout) < 0) 24357429Smarkm fatal("pipe: %s", strerror(errno)); 24457429Smarkm 24557429Smarkm /* Free the reserved descriptors. */ 24657429Smarkm close(reserved[0]); 24757429Smarkm close(reserved[1]); 24857429Smarkm 249215116Sdes signal(SIGTSTP, suspchild); 250215116Sdes signal(SIGTTIN, suspchild); 251215116Sdes signal(SIGTTOU, suspchild); 252215116Sdes 253124211Sdes /* Fork a child to execute the command on the remote host using ssh. */ 254113911Sdes do_cmd_pid = fork(); 255113911Sdes if (do_cmd_pid == 0) { 25657429Smarkm /* Child. */ 25757429Smarkm close(pin[1]); 25857429Smarkm close(pout[0]); 25957429Smarkm dup2(pin[0], 0); 26057429Smarkm dup2(pout[1], 1); 26157429Smarkm close(pin[0]); 26257429Smarkm close(pout[1]); 26357429Smarkm 264157019Sdes replacearg(&args, 0, "%s", ssh_program); 265204917Sdes if (remuser != NULL) { 266204917Sdes addargs(&args, "-l"); 267204917Sdes addargs(&args, "%s", remuser); 268204917Sdes } 269204917Sdes addargs(&args, "--"); 27092555Sdes addargs(&args, "%s", host); 27192555Sdes addargs(&args, "%s", cmd); 27257429Smarkm 27369587Sgreen execvp(ssh_program, args.list); 27465668Skris perror(ssh_program); 27557429Smarkm exit(1); 276113911Sdes } else if (do_cmd_pid == -1) { 277113911Sdes fatal("fork: %s", strerror(errno)); 27857429Smarkm } 27957429Smarkm /* Parent. Close the other side, and return the local side. */ 28057429Smarkm close(pin[0]); 28157429Smarkm *fdout = pin[1]; 28257429Smarkm close(pout[1]); 28357429Smarkm *fdin = pout[0]; 284124211Sdes signal(SIGTERM, killchild); 285124211Sdes signal(SIGINT, killchild); 286124211Sdes signal(SIGHUP, killchild); 28757429Smarkm return 0; 28857429Smarkm} 28957429Smarkm 29057429Smarkmtypedef struct { 291149753Sdes size_t cnt; 29257429Smarkm char *buf; 29357429Smarkm} BUF; 29457429Smarkm 29557429SmarkmBUF *allocbuf(BUF *, int, int); 29657429Smarkmvoid lostconn(int); 29757429Smarkmint okname(char *); 29857429Smarkmvoid run_err(const char *,...); 29957429Smarkmvoid verifydir(char *); 30057429Smarkm 30157429Smarkmstruct passwd *pwd; 30257429Smarkmuid_t userid; 30357429Smarkmint errs, remin, remout; 30457429Smarkmint pflag, iamremote, iamrecursive, targetshouldbedirectory; 30557429Smarkm 30657429Smarkm#define CMDNEEDS 64 30757429Smarkmchar cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ 30857429Smarkm 30957429Smarkmint response(void); 31057429Smarkmvoid rsource(char *, struct stat *); 31157429Smarkmvoid sink(int, char *[]); 31257429Smarkmvoid source(int, char *[]); 31357429Smarkmvoid tolocal(int, char *[]); 31457429Smarkmvoid toremote(char *, int, char *[]); 315181111Sdessize_t scpio(ssize_t (*)(int, void *, size_t), int, void *, size_t, off_t *); 31657429Smarkmvoid usage(void); 31757429Smarkm 31857429Smarkmint 319124211Sdesmain(int argc, char **argv) 32057429Smarkm{ 321162856Sdes int ch, fflag, tflag, status, n; 322113911Sdes double speed; 323162856Sdes char *targ, *endp, **newargv; 32457429Smarkm extern char *optarg; 32557429Smarkm extern int optind; 32657429Smarkm 327157019Sdes /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 328157019Sdes sanitise_stdfd(); 329157019Sdes 330162856Sdes /* Copy argv, because we modify it */ 331162856Sdes newargv = xcalloc(MAX(argc + 1, 1), sizeof(*newargv)); 332162856Sdes for (n = 0; n < argc; n++) 333162856Sdes newargv[n] = xstrdup(argv[n]); 334162856Sdes argv = newargv; 335162856Sdes 336124211Sdes __progname = ssh_get_progname(argv[0]); 33798937Sdes 338157019Sdes memset(&args, '\0', sizeof(args)); 33969587Sgreen args.list = NULL; 340157019Sdes addargs(&args, "%s", ssh_program); 34192555Sdes addargs(&args, "-x"); 34292555Sdes addargs(&args, "-oForwardAgent no"); 343157019Sdes addargs(&args, "-oPermitLocalCommand no"); 34492555Sdes addargs(&args, "-oClearAllForwardings yes"); 34569587Sgreen 34657429Smarkm fflag = tflag = 0; 347113911Sdes while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q1246S:o:F:")) != -1) 34857429Smarkm switch (ch) { 34957429Smarkm /* User-visible flags. */ 350113911Sdes case '1': 351113911Sdes case '2': 35257429Smarkm case '4': 35357429Smarkm case '6': 35469587Sgreen case 'C': 35592555Sdes addargs(&args, "-%c", ch); 35657429Smarkm break; 35769587Sgreen case 'o': 35869587Sgreen case 'c': 35969587Sgreen case 'i': 36092555Sdes case 'F': 361204917Sdes addargs(&args, "-%c", ch); 362204917Sdes addargs(&args, "%s", optarg); 36369587Sgreen break; 36469587Sgreen case 'P': 365204917Sdes addargs(&args, "-p"); 366204917Sdes addargs(&args, "%s", optarg); 36769587Sgreen break; 36869587Sgreen case 'B': 36992555Sdes addargs(&args, "-oBatchmode yes"); 37069587Sgreen break; 371113911Sdes case 'l': 372113911Sdes speed = strtod(optarg, &endp); 373113911Sdes if (speed <= 0 || *endp != '\0') 374113911Sdes usage(); 375126277Sdes limit_rate = speed * 1024; 376113911Sdes break; 37757429Smarkm case 'p': 37857429Smarkm pflag = 1; 37957429Smarkm break; 38057429Smarkm case 'r': 38157429Smarkm iamrecursive = 1; 38257429Smarkm break; 38365668Skris case 'S': 38469587Sgreen ssh_program = xstrdup(optarg); 38565668Skris break; 38669587Sgreen case 'v': 38792555Sdes addargs(&args, "-v"); 38869587Sgreen verbose_mode = 1; 38969587Sgreen break; 39069587Sgreen case 'q': 391126277Sdes addargs(&args, "-q"); 39269587Sgreen showprogress = 0; 39369587Sgreen break; 39465668Skris 39557429Smarkm /* Server options. */ 39657429Smarkm case 'd': 39757429Smarkm targetshouldbedirectory = 1; 39857429Smarkm break; 39957429Smarkm case 'f': /* "from" */ 40057429Smarkm iamremote = 1; 40157429Smarkm fflag = 1; 40257429Smarkm break; 40357429Smarkm case 't': /* "to" */ 40457429Smarkm iamremote = 1; 40557429Smarkm tflag = 1; 40698937Sdes#ifdef HAVE_CYGWIN 40798937Sdes setmode(0, O_BINARY); 40898937Sdes#endif 40957429Smarkm break; 41057429Smarkm default: 41157429Smarkm usage(); 41257429Smarkm } 41357429Smarkm argc -= optind; 41457429Smarkm argv += optind; 41557429Smarkm 41657429Smarkm if ((pwd = getpwuid(userid = getuid())) == NULL) 417124211Sdes fatal("unknown user %u", (u_int) userid); 41857429Smarkm 419181111Sdes if (!isatty(STDOUT_FILENO)) 42057429Smarkm showprogress = 0; 42157429Smarkm 42257429Smarkm remin = STDIN_FILENO; 42357429Smarkm remout = STDOUT_FILENO; 42457429Smarkm 42576259Sgreen if (fflag) { 42657429Smarkm /* Follow "protocol", send data. */ 42757429Smarkm (void) response(); 42857429Smarkm source(argc, argv); 42957429Smarkm exit(errs != 0); 43057429Smarkm } 43157429Smarkm if (tflag) { 43257429Smarkm /* Receive data. */ 43357429Smarkm sink(argc, argv); 43457429Smarkm exit(errs != 0); 43557429Smarkm } 43657429Smarkm if (argc < 2) 43757429Smarkm usage(); 43857429Smarkm if (argc > 2) 43957429Smarkm targetshouldbedirectory = 1; 44057429Smarkm 44157429Smarkm remin = remout = -1; 442113911Sdes do_cmd_pid = -1; 44357429Smarkm /* Command to be executed on remote system using "ssh". */ 44476259Sgreen (void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s", 44576259Sgreen verbose_mode ? " -v" : "", 44665668Skris iamrecursive ? " -r" : "", pflag ? " -p" : "", 44765668Skris targetshouldbedirectory ? " -d" : ""); 44857429Smarkm 44957429Smarkm (void) signal(SIGPIPE, lostconn); 45057429Smarkm 45157429Smarkm if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ 45257429Smarkm toremote(targ, argc, argv); 45357429Smarkm else { 45457429Smarkm if (targetshouldbedirectory) 45557429Smarkm verifydir(argv[argc - 1]); 456157019Sdes tolocal(argc, argv); /* Dest is local host. */ 45757429Smarkm } 458113911Sdes /* 459113911Sdes * Finally check the exit status of the ssh process, if one was forked 460192595Sdes * and no error has occurred yet 461113911Sdes */ 462113911Sdes if (do_cmd_pid != -1 && errs == 0) { 463113911Sdes if (remin != -1) 464113911Sdes (void) close(remin); 465113911Sdes if (remout != -1) 466113911Sdes (void) close(remout); 467113911Sdes if (waitpid(do_cmd_pid, &status, 0) == -1) 468113911Sdes errs = 1; 469113911Sdes else { 470113911Sdes if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) 471113911Sdes errs = 1; 472113911Sdes } 473113911Sdes } 47457429Smarkm exit(errs != 0); 47557429Smarkm} 47657429Smarkm 477181111Sdes/* 478181111Sdes * atomicio-like wrapper that also applies bandwidth limits and updates 479181111Sdes * the progressmeter counter. 480181111Sdes */ 481181111Sdessize_t 482181111Sdesscpio(ssize_t (*f)(int, void *, size_t), int fd, void *_p, size_t l, off_t *c) 483181111Sdes{ 484181111Sdes u_char *p = (u_char *)_p; 485181111Sdes size_t offset; 486181111Sdes ssize_t r; 487181111Sdes struct pollfd pfd; 488181111Sdes 489181111Sdes pfd.fd = fd; 490181111Sdes pfd.events = f == read ? POLLIN : POLLOUT; 491181111Sdes for (offset = 0; offset < l;) { 492181111Sdes r = f(fd, p + offset, l - offset); 493181111Sdes if (r == 0) { 494181111Sdes errno = EPIPE; 495181111Sdes return offset; 496181111Sdes } 497181111Sdes if (r < 0) { 498181111Sdes if (errno == EINTR) 499181111Sdes continue; 500181111Sdes if (errno == EAGAIN || errno == EWOULDBLOCK) { 501181111Sdes (void)poll(&pfd, 1, -1); /* Ignore errors */ 502181111Sdes continue; 503181111Sdes } 504181111Sdes return offset; 505181111Sdes } 506181111Sdes offset += (size_t)r; 507181111Sdes *c += (off_t)r; 508181111Sdes if (limit_rate) 509181111Sdes bwlimit(r); 510181111Sdes } 511181111Sdes return offset; 512181111Sdes} 513181111Sdes 51457429Smarkmvoid 515124211Sdestoremote(char *targ, int argc, char **argv) 51657429Smarkm{ 517147005Sdes char *bp, *host, *src, *suser, *thost, *tuser, *arg; 518157019Sdes arglist alist; 519162856Sdes int i; 52057429Smarkm 521157019Sdes memset(&alist, '\0', sizeof(alist)); 522157019Sdes alist.list = NULL; 523157019Sdes 52457429Smarkm *targ++ = 0; 52557429Smarkm if (*targ == 0) 52657429Smarkm targ = "."; 52757429Smarkm 528147005Sdes arg = xstrdup(argv[argc - 1]); 529147005Sdes if ((thost = strrchr(arg, '@'))) { 53057429Smarkm /* user@host */ 53157429Smarkm *thost++ = 0; 532147005Sdes tuser = arg; 53357429Smarkm if (*tuser == '\0') 53457429Smarkm tuser = NULL; 53557429Smarkm } else { 536147005Sdes thost = arg; 53757429Smarkm tuser = NULL; 53857429Smarkm } 53957429Smarkm 540157019Sdes if (tuser != NULL && !okname(tuser)) { 541157019Sdes xfree(arg); 542157019Sdes return; 543157019Sdes } 544157019Sdes 54557429Smarkm for (i = 0; i < argc - 1; i++) { 54657429Smarkm src = colon(argv[i]); 54757429Smarkm if (src) { /* remote to remote */ 548157019Sdes freeargs(&alist); 549157019Sdes addargs(&alist, "%s", ssh_program); 550157019Sdes if (verbose_mode) 551157019Sdes addargs(&alist, "-v"); 552157019Sdes addargs(&alist, "-x"); 553157019Sdes addargs(&alist, "-oClearAllForwardings yes"); 554157019Sdes addargs(&alist, "-n"); 555157019Sdes 55657429Smarkm *src++ = 0; 55757429Smarkm if (*src == 0) 55857429Smarkm src = "."; 559113911Sdes host = strrchr(argv[i], '@'); 560157019Sdes 56157429Smarkm if (host) { 56257429Smarkm *host++ = 0; 56357429Smarkm host = cleanhostname(host); 56457429Smarkm suser = argv[i]; 56557429Smarkm if (*suser == '\0') 56657429Smarkm suser = pwd->pw_name; 567157019Sdes else if (!okname(suser)) 56857429Smarkm continue; 569157019Sdes addargs(&alist, "-l"); 570157019Sdes addargs(&alist, "%s", suser); 57157429Smarkm } else { 57257429Smarkm host = cleanhostname(argv[i]); 57357429Smarkm } 574204917Sdes addargs(&alist, "--"); 575157019Sdes addargs(&alist, "%s", host); 576157019Sdes addargs(&alist, "%s", cmd); 577157019Sdes addargs(&alist, "%s", src); 578157019Sdes addargs(&alist, "%s%s%s:%s", 579157019Sdes tuser ? tuser : "", tuser ? "@" : "", 580157019Sdes thost, targ); 581157019Sdes if (do_local_cmd(&alist) != 0) 582126277Sdes errs = 1; 58357429Smarkm } else { /* local to remote */ 58457429Smarkm if (remin == -1) { 585204917Sdes xasprintf(&bp, "%s -t -- %s", cmd, targ); 58657429Smarkm host = cleanhostname(thost); 58765668Skris if (do_cmd(host, tuser, bp, &remin, 588162856Sdes &remout) < 0) 58957429Smarkm exit(1); 59057429Smarkm if (response() < 0) 59157429Smarkm exit(1); 59257429Smarkm (void) xfree(bp); 59357429Smarkm } 59457429Smarkm source(1, argv + i); 59557429Smarkm } 59657429Smarkm } 597162856Sdes xfree(arg); 59857429Smarkm} 59957429Smarkm 60057429Smarkmvoid 601124211Sdestolocal(int argc, char **argv) 60257429Smarkm{ 60357429Smarkm char *bp, *host, *src, *suser; 604157019Sdes arglist alist; 605162856Sdes int i; 60657429Smarkm 607157019Sdes memset(&alist, '\0', sizeof(alist)); 608157019Sdes alist.list = NULL; 609157019Sdes 61057429Smarkm for (i = 0; i < argc - 1; i++) { 61157429Smarkm if (!(src = colon(argv[i]))) { /* Local to local. */ 612157019Sdes freeargs(&alist); 613157019Sdes addargs(&alist, "%s", _PATH_CP); 614157019Sdes if (iamrecursive) 615157019Sdes addargs(&alist, "-r"); 616157019Sdes if (pflag) 617157019Sdes addargs(&alist, "-p"); 618204917Sdes addargs(&alist, "--"); 619157019Sdes addargs(&alist, "%s", argv[i]); 620157019Sdes addargs(&alist, "%s", argv[argc-1]); 621157019Sdes if (do_local_cmd(&alist)) 62257429Smarkm ++errs; 62357429Smarkm continue; 62457429Smarkm } 62557429Smarkm *src++ = 0; 62657429Smarkm if (*src == 0) 62757429Smarkm src = "."; 628113911Sdes if ((host = strrchr(argv[i], '@')) == NULL) { 62957429Smarkm host = argv[i]; 63057429Smarkm suser = NULL; 63157429Smarkm } else { 63257429Smarkm *host++ = 0; 63357429Smarkm suser = argv[i]; 63457429Smarkm if (*suser == '\0') 63557429Smarkm suser = pwd->pw_name; 63657429Smarkm } 63757429Smarkm host = cleanhostname(host); 638204917Sdes xasprintf(&bp, "%s -f -- %s", cmd, src); 639162856Sdes if (do_cmd(host, suser, bp, &remin, &remout) < 0) { 64057429Smarkm (void) xfree(bp); 64157429Smarkm ++errs; 64257429Smarkm continue; 64357429Smarkm } 64457429Smarkm xfree(bp); 64557429Smarkm sink(1, argv + argc - 1); 64657429Smarkm (void) close(remin); 64757429Smarkm remin = remout = -1; 64857429Smarkm } 64957429Smarkm} 65057429Smarkm 65157429Smarkmvoid 652124211Sdessource(int argc, char **argv) 65357429Smarkm{ 65457429Smarkm struct stat stb; 65557429Smarkm static BUF buffer; 65657429Smarkm BUF *bp; 657181111Sdes off_t i, statbytes; 658181111Sdes size_t amt; 659149753Sdes int fd = -1, haderr, indx; 660181111Sdes char *last, *name, buf[2048], encname[MAXPATHLEN]; 66176259Sgreen int len; 66257429Smarkm 66357429Smarkm for (indx = 0; indx < argc; ++indx) { 66457429Smarkm name = argv[indx]; 66557429Smarkm statbytes = 0; 66676259Sgreen len = strlen(name); 66776259Sgreen while (len > 1 && name[len-1] == '/') 66876259Sgreen name[--len] = '\0'; 669181111Sdes if ((fd = open(name, O_RDONLY|O_NONBLOCK, 0)) < 0) 670181111Sdes goto syserr; 67192555Sdes if (strchr(name, '\n') != NULL) { 672181111Sdes strnvis(encname, name, sizeof(encname), VIS_NL); 673181111Sdes name = encname; 67492555Sdes } 67557429Smarkm if (fstat(fd, &stb) < 0) { 67657429Smarkmsyserr: run_err("%s: %s", name, strerror(errno)); 67757429Smarkm goto next; 67857429Smarkm } 679181111Sdes if (stb.st_size < 0) { 680181111Sdes run_err("%s: %s", name, "Negative file size"); 681181111Sdes goto next; 682181111Sdes } 683181111Sdes unset_nonblock(fd); 68457429Smarkm switch (stb.st_mode & S_IFMT) { 68557429Smarkm case S_IFREG: 68657429Smarkm break; 68757429Smarkm case S_IFDIR: 68857429Smarkm if (iamrecursive) { 68957429Smarkm rsource(name, &stb); 69057429Smarkm goto next; 69157429Smarkm } 69257429Smarkm /* FALLTHROUGH */ 69357429Smarkm default: 69457429Smarkm run_err("%s: not a regular file", name); 69557429Smarkm goto next; 69657429Smarkm } 69757429Smarkm if ((last = strrchr(name, '/')) == NULL) 69857429Smarkm last = name; 69957429Smarkm else 70057429Smarkm ++last; 70157429Smarkm curfile = last; 70257429Smarkm if (pflag) { 70357429Smarkm /* 70457429Smarkm * Make it compatible with possible future 70557429Smarkm * versions expecting microseconds. 70657429Smarkm */ 70776259Sgreen (void) snprintf(buf, sizeof buf, "T%lu 0 %lu 0\n", 708181111Sdes (u_long) (stb.st_mtime < 0 ? 0 : stb.st_mtime), 709181111Sdes (u_long) (stb.st_atime < 0 ? 0 : stb.st_atime)); 710181111Sdes if (verbose_mode) { 711181111Sdes fprintf(stderr, "File mtime %ld atime %ld\n", 712181111Sdes (long)stb.st_mtime, (long)stb.st_atime); 713181111Sdes fprintf(stderr, "Sending file timestamps: %s", 714181111Sdes buf); 715181111Sdes } 716124211Sdes (void) atomicio(vwrite, remout, buf, strlen(buf)); 71757429Smarkm if (response() < 0) 71857429Smarkm goto next; 71957429Smarkm } 72057429Smarkm#define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) 72176259Sgreen snprintf(buf, sizeof buf, "C%04o %lld %s\n", 72276259Sgreen (u_int) (stb.st_mode & FILEMODEMASK), 723157019Sdes (long long)stb.st_size, last); 72457429Smarkm if (verbose_mode) { 72557429Smarkm fprintf(stderr, "Sending file modes: %s", buf); 72657429Smarkm } 727124211Sdes (void) atomicio(vwrite, remout, buf, strlen(buf)); 72857429Smarkm if (response() < 0) 72957429Smarkm goto next; 730181111Sdes if ((bp = allocbuf(&buffer, fd, COPY_BUFLEN)) == NULL) { 731157019Sdesnext: if (fd != -1) { 732157019Sdes (void) close(fd); 733157019Sdes fd = -1; 734157019Sdes } 73557429Smarkm continue; 73657429Smarkm } 737113911Sdes if (showprogress) 738113911Sdes start_progress_meter(curfile, stb.st_size, &statbytes); 739181111Sdes set_nonblock(remout); 74057429Smarkm for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { 74157429Smarkm amt = bp->cnt; 742181111Sdes if (i + (off_t)amt > stb.st_size) 74357429Smarkm amt = stb.st_size - i; 74457429Smarkm if (!haderr) { 745181111Sdes if (atomicio(read, fd, bp->buf, amt) != amt) 746149753Sdes haderr = errno; 74757429Smarkm } 748181111Sdes /* Keep writing after error to retain sync */ 749181111Sdes if (haderr) { 750181111Sdes (void)atomicio(vwrite, remout, bp->buf, amt); 751181111Sdes continue; 75257429Smarkm } 753181111Sdes if (scpio(vwrite, remout, bp->buf, amt, 754181111Sdes &statbytes) != amt) 755181111Sdes haderr = errno; 75657429Smarkm } 757181111Sdes unset_nonblock(remout); 75857429Smarkm if (showprogress) 759113911Sdes stop_progress_meter(); 76057429Smarkm 761157019Sdes if (fd != -1) { 762157019Sdes if (close(fd) < 0 && !haderr) 763157019Sdes haderr = errno; 764157019Sdes fd = -1; 765157019Sdes } 76657429Smarkm if (!haderr) 767124211Sdes (void) atomicio(vwrite, remout, "", 1); 76857429Smarkm else 76957429Smarkm run_err("%s: %s", name, strerror(haderr)); 77057429Smarkm (void) response(); 77157429Smarkm } 77257429Smarkm} 77357429Smarkm 77457429Smarkmvoid 775124211Sdesrsource(char *name, struct stat *statp) 77657429Smarkm{ 77757429Smarkm DIR *dirp; 77857429Smarkm struct dirent *dp; 77957429Smarkm char *last, *vect[1], path[1100]; 78057429Smarkm 78157429Smarkm if (!(dirp = opendir(name))) { 78257429Smarkm run_err("%s: %s", name, strerror(errno)); 78357429Smarkm return; 78457429Smarkm } 78557429Smarkm last = strrchr(name, '/'); 78657429Smarkm if (last == 0) 78757429Smarkm last = name; 78857429Smarkm else 78957429Smarkm last++; 79057429Smarkm if (pflag) { 79176259Sgreen (void) snprintf(path, sizeof(path), "T%lu 0 %lu 0\n", 79276259Sgreen (u_long) statp->st_mtime, 79376259Sgreen (u_long) statp->st_atime); 794124211Sdes (void) atomicio(vwrite, remout, path, strlen(path)); 79557429Smarkm if (response() < 0) { 79657429Smarkm closedir(dirp); 79757429Smarkm return; 79857429Smarkm } 79957429Smarkm } 80076259Sgreen (void) snprintf(path, sizeof path, "D%04o %d %.1024s\n", 80176259Sgreen (u_int) (statp->st_mode & FILEMODEMASK), 0, last); 80257429Smarkm if (verbose_mode) 80357429Smarkm fprintf(stderr, "Entering directory: %s", path); 804124211Sdes (void) atomicio(vwrite, remout, path, strlen(path)); 80557429Smarkm if (response() < 0) { 80657429Smarkm closedir(dirp); 80757429Smarkm return; 80857429Smarkm } 80976259Sgreen while ((dp = readdir(dirp)) != NULL) { 81057429Smarkm if (dp->d_ino == 0) 81157429Smarkm continue; 81257429Smarkm if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 81357429Smarkm continue; 81457429Smarkm if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path) - 1) { 81557429Smarkm run_err("%s/%s: name too long", name, dp->d_name); 81657429Smarkm continue; 81757429Smarkm } 81876259Sgreen (void) snprintf(path, sizeof path, "%s/%s", name, dp->d_name); 81957429Smarkm vect[0] = path; 82057429Smarkm source(1, vect); 82157429Smarkm } 82257429Smarkm (void) closedir(dirp); 823124211Sdes (void) atomicio(vwrite, remout, "E\n", 2); 82457429Smarkm (void) response(); 82557429Smarkm} 82657429Smarkm 82757429Smarkmvoid 828113911Sdesbwlimit(int amount) 829113911Sdes{ 830113911Sdes static struct timeval bwstart, bwend; 831113911Sdes static int lamt, thresh = 16384; 832137019Sdes u_int64_t waitlen; 833113911Sdes struct timespec ts, rm; 834113911Sdes 835113911Sdes if (!timerisset(&bwstart)) { 836113911Sdes gettimeofday(&bwstart, NULL); 837113911Sdes return; 838113911Sdes } 839113911Sdes 840113911Sdes lamt += amount; 841113911Sdes if (lamt < thresh) 842113911Sdes return; 843113911Sdes 844113911Sdes gettimeofday(&bwend, NULL); 845113911Sdes timersub(&bwend, &bwstart, &bwend); 846113911Sdes if (!timerisset(&bwend)) 847113911Sdes return; 848113911Sdes 849113911Sdes lamt *= 8; 850137019Sdes waitlen = (double)1000000L * lamt / limit_rate; 851113911Sdes 852137019Sdes bwstart.tv_sec = waitlen / 1000000L; 853137019Sdes bwstart.tv_usec = waitlen % 1000000L; 854113911Sdes 855113911Sdes if (timercmp(&bwstart, &bwend, >)) { 856113911Sdes timersub(&bwstart, &bwend, &bwend); 857113911Sdes 858113911Sdes /* Adjust the wait time */ 859113911Sdes if (bwend.tv_sec) { 860113911Sdes thresh /= 2; 861113911Sdes if (thresh < 2048) 862113911Sdes thresh = 2048; 863181111Sdes } else if (bwend.tv_usec < 10000) { 864113911Sdes thresh *= 2; 865181111Sdes if (thresh > COPY_BUFLEN * 4) 866181111Sdes thresh = COPY_BUFLEN * 4; 867113911Sdes } 868113911Sdes 869113911Sdes TIMEVAL_TO_TIMESPEC(&bwend, &ts); 870113911Sdes while (nanosleep(&ts, &rm) == -1) { 871113911Sdes if (errno != EINTR) 872113911Sdes break; 873113911Sdes ts = rm; 874113911Sdes } 875113911Sdes } 876113911Sdes 877113911Sdes lamt = 0; 878113911Sdes gettimeofday(&bwstart, NULL); 879113911Sdes} 880113911Sdes 881113911Sdesvoid 882124211Sdessink(int argc, char **argv) 88357429Smarkm{ 88457429Smarkm static BUF buffer; 88557429Smarkm struct stat stb; 88657429Smarkm enum { 88757429Smarkm YES, NO, DISPLAYED 88857429Smarkm } wrerr; 88957429Smarkm BUF *bp; 890149753Sdes off_t i; 891149753Sdes size_t j, count; 892162856Sdes int amt, exists, first, ofd; 893162856Sdes mode_t mode, omode, mask; 894113911Sdes off_t size, statbytes; 89565668Skris int setimes, targisdir, wrerrno = 0; 89657429Smarkm char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; 89769587Sgreen struct timeval tv[2]; 89857429Smarkm 89976259Sgreen#define atime tv[0] 90076259Sgreen#define mtime tv[1] 901147005Sdes#define SCREWUP(str) { why = str; goto screwup; } 90257429Smarkm 90357429Smarkm setimes = targisdir = 0; 90457429Smarkm mask = umask(0); 90557429Smarkm if (!pflag) 90657429Smarkm (void) umask(mask); 90757429Smarkm if (argc != 1) { 90857429Smarkm run_err("ambiguous target"); 90957429Smarkm exit(1); 91057429Smarkm } 91157429Smarkm targ = *argv; 91257429Smarkm if (targetshouldbedirectory) 91357429Smarkm verifydir(targ); 91457429Smarkm 915124211Sdes (void) atomicio(vwrite, remout, "", 1); 91657429Smarkm if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) 91757429Smarkm targisdir = 1; 91857429Smarkm for (first = 1;; first = 0) { 91957429Smarkm cp = buf; 920149753Sdes if (atomicio(read, remin, cp, 1) != 1) 92157429Smarkm return; 92257429Smarkm if (*cp++ == '\n') 92357429Smarkm SCREWUP("unexpected <newline>"); 92457429Smarkm do { 92560573Skris if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) 92657429Smarkm SCREWUP("lost connection"); 92757429Smarkm *cp++ = ch; 92857429Smarkm } while (cp < &buf[sizeof(buf) - 1] && ch != '\n'); 92957429Smarkm *cp = 0; 930137019Sdes if (verbose_mode) 931137019Sdes fprintf(stderr, "Sink: %s", buf); 93257429Smarkm 93357429Smarkm if (buf[0] == '\01' || buf[0] == '\02') { 93457429Smarkm if (iamremote == 0) 935124211Sdes (void) atomicio(vwrite, STDERR_FILENO, 93676259Sgreen buf + 1, strlen(buf + 1)); 93757429Smarkm if (buf[0] == '\02') 93857429Smarkm exit(1); 93957429Smarkm ++errs; 94057429Smarkm continue; 94157429Smarkm } 94257429Smarkm if (buf[0] == 'E') { 943124211Sdes (void) atomicio(vwrite, remout, "", 1); 94457429Smarkm return; 94557429Smarkm } 94657429Smarkm if (ch == '\n') 94757429Smarkm *--cp = 0; 94857429Smarkm 94957429Smarkm cp = buf; 95057429Smarkm if (*cp == 'T') { 95157429Smarkm setimes++; 95257429Smarkm cp++; 95376259Sgreen mtime.tv_sec = strtol(cp, &cp, 10); 95476259Sgreen if (!cp || *cp++ != ' ') 95557429Smarkm SCREWUP("mtime.sec not delimited"); 95676259Sgreen mtime.tv_usec = strtol(cp, &cp, 10); 95776259Sgreen if (!cp || *cp++ != ' ') 95857429Smarkm SCREWUP("mtime.usec not delimited"); 95976259Sgreen atime.tv_sec = strtol(cp, &cp, 10); 96076259Sgreen if (!cp || *cp++ != ' ') 96157429Smarkm SCREWUP("atime.sec not delimited"); 96276259Sgreen atime.tv_usec = strtol(cp, &cp, 10); 96376259Sgreen if (!cp || *cp++ != '\0') 96457429Smarkm SCREWUP("atime.usec not delimited"); 965124211Sdes (void) atomicio(vwrite, remout, "", 1); 96657429Smarkm continue; 96757429Smarkm } 96857429Smarkm if (*cp != 'C' && *cp != 'D') { 96957429Smarkm /* 97057429Smarkm * Check for the case "rcp remote:foo\* local:bar". 97157429Smarkm * In this case, the line "No match." can be returned 97257429Smarkm * by the shell before the rcp command on the remote is 97357429Smarkm * executed so the ^Aerror_message convention isn't 97457429Smarkm * followed. 97557429Smarkm */ 97657429Smarkm if (first) { 97757429Smarkm run_err("%s", cp); 97857429Smarkm exit(1); 97957429Smarkm } 98057429Smarkm SCREWUP("expected control record"); 98157429Smarkm } 98257429Smarkm mode = 0; 98357429Smarkm for (++cp; cp < buf + 5; cp++) { 98457429Smarkm if (*cp < '0' || *cp > '7') 98557429Smarkm SCREWUP("bad mode"); 98657429Smarkm mode = (mode << 3) | (*cp - '0'); 98757429Smarkm } 98857429Smarkm if (*cp++ != ' ') 98957429Smarkm SCREWUP("mode not delimited"); 99057429Smarkm 99176259Sgreen for (size = 0; isdigit(*cp);) 99257429Smarkm size = size * 10 + (*cp++ - '0'); 99357429Smarkm if (*cp++ != ' ') 99457429Smarkm SCREWUP("size not delimited"); 995137019Sdes if ((strchr(cp, '/') != NULL) || (strcmp(cp, "..") == 0)) { 996137019Sdes run_err("error: unexpected filename: %s", cp); 997137019Sdes exit(1); 998137019Sdes } 99957429Smarkm if (targisdir) { 100057429Smarkm static char *namebuf; 1001149753Sdes static size_t cursize; 100257429Smarkm size_t need; 100357429Smarkm 100457429Smarkm need = strlen(targ) + strlen(cp) + 250; 100576259Sgreen if (need > cursize) { 100676259Sgreen if (namebuf) 100776259Sgreen xfree(namebuf); 100857429Smarkm namebuf = xmalloc(need); 100976259Sgreen cursize = need; 101076259Sgreen } 101176259Sgreen (void) snprintf(namebuf, need, "%s%s%s", targ, 101298675Sdes strcmp(targ, "/") ? "/" : "", cp); 101357429Smarkm np = namebuf; 101457429Smarkm } else 101557429Smarkm np = targ; 101657429Smarkm curfile = cp; 101757429Smarkm exists = stat(np, &stb) == 0; 101857429Smarkm if (buf[0] == 'D') { 101957429Smarkm int mod_flag = pflag; 1020137019Sdes if (!iamrecursive) 1021137019Sdes SCREWUP("received directory without -r"); 102257429Smarkm if (exists) { 102357429Smarkm if (!S_ISDIR(stb.st_mode)) { 102457429Smarkm errno = ENOTDIR; 102557429Smarkm goto bad; 102657429Smarkm } 102757429Smarkm if (pflag) 102857429Smarkm (void) chmod(np, mode); 102957429Smarkm } else { 103057429Smarkm /* Handle copying from a read-only 103157429Smarkm directory */ 103257429Smarkm mod_flag = 1; 103357429Smarkm if (mkdir(np, mode | S_IRWXU) < 0) 103457429Smarkm goto bad; 103557429Smarkm } 103676259Sgreen vect[0] = xstrdup(np); 103757429Smarkm sink(1, vect); 103857429Smarkm if (setimes) { 103957429Smarkm setimes = 0; 104076259Sgreen if (utimes(vect[0], tv) < 0) 104157429Smarkm run_err("%s: set times: %s", 104276259Sgreen vect[0], strerror(errno)); 104357429Smarkm } 104457429Smarkm if (mod_flag) 104576259Sgreen (void) chmod(vect[0], mode); 104676259Sgreen if (vect[0]) 104776259Sgreen xfree(vect[0]); 104857429Smarkm continue; 104957429Smarkm } 105057429Smarkm omode = mode; 105157429Smarkm mode |= S_IWRITE; 105292555Sdes if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { 105357429Smarkmbad: run_err("%s: %s", np, strerror(errno)); 105457429Smarkm continue; 105557429Smarkm } 1056124211Sdes (void) atomicio(vwrite, remout, "", 1); 1057181111Sdes if ((bp = allocbuf(&buffer, ofd, COPY_BUFLEN)) == NULL) { 105857429Smarkm (void) close(ofd); 105957429Smarkm continue; 106057429Smarkm } 106157429Smarkm cp = bp->buf; 106257429Smarkm wrerr = NO; 106357429Smarkm 106457429Smarkm statbytes = 0; 1065113911Sdes if (showprogress) 1066113911Sdes start_progress_meter(curfile, size, &statbytes); 1067181111Sdes set_nonblock(remin); 1068181111Sdes for (count = i = 0; i < size; i += bp->cnt) { 1069181111Sdes amt = bp->cnt; 107057429Smarkm if (i + amt > size) 107157429Smarkm amt = size - i; 107257429Smarkm count += amt; 107357429Smarkm do { 1074181111Sdes j = scpio(read, remin, cp, amt, &statbytes); 1075149753Sdes if (j == 0) { 1076181111Sdes run_err("%s", j != EPIPE ? 1077181111Sdes strerror(errno) : 107876259Sgreen "dropped connection"); 107957429Smarkm exit(1); 108057429Smarkm } 108157429Smarkm amt -= j; 108257429Smarkm cp += j; 108357429Smarkm } while (amt > 0); 1084126277Sdes 108557429Smarkm if (count == bp->cnt) { 108657429Smarkm /* Keep reading so we stay sync'd up. */ 108757429Smarkm if (wrerr == NO) { 1088149753Sdes if (atomicio(vwrite, ofd, bp->buf, 1089149753Sdes count) != count) { 109057429Smarkm wrerr = YES; 1091149753Sdes wrerrno = errno; 109257429Smarkm } 109357429Smarkm } 109457429Smarkm count = 0; 109557429Smarkm cp = bp->buf; 109657429Smarkm } 109757429Smarkm } 1098181111Sdes unset_nonblock(remin); 109957429Smarkm if (showprogress) 1100113911Sdes stop_progress_meter(); 110157429Smarkm if (count != 0 && wrerr == NO && 1102149753Sdes atomicio(vwrite, ofd, bp->buf, count) != count) { 110357429Smarkm wrerr = YES; 1104149753Sdes wrerrno = errno; 110557429Smarkm } 1106181111Sdes if (wrerr == NO && (!exists || S_ISREG(stb.st_mode)) && 1107181111Sdes ftruncate(ofd, size) != 0) { 110857429Smarkm run_err("%s: truncate: %s", np, strerror(errno)); 110957429Smarkm wrerr = DISPLAYED; 111057429Smarkm } 111157429Smarkm if (pflag) { 111257429Smarkm if (exists || omode != mode) 111398937Sdes#ifdef HAVE_FCHMOD 1114137019Sdes if (fchmod(ofd, omode)) { 111598937Sdes#else /* HAVE_FCHMOD */ 1116137019Sdes if (chmod(np, omode)) { 111798937Sdes#endif /* HAVE_FCHMOD */ 111857429Smarkm run_err("%s: set mode: %s", 111976259Sgreen np, strerror(errno)); 1120137019Sdes wrerr = DISPLAYED; 1121137019Sdes } 112257429Smarkm } else { 112357429Smarkm if (!exists && omode != mode) 112498937Sdes#ifdef HAVE_FCHMOD 1125137019Sdes if (fchmod(ofd, omode & ~mask)) { 112698937Sdes#else /* HAVE_FCHMOD */ 1127137019Sdes if (chmod(np, omode & ~mask)) { 112898937Sdes#endif /* HAVE_FCHMOD */ 112957429Smarkm run_err("%s: set mode: %s", 113076259Sgreen np, strerror(errno)); 1131137019Sdes wrerr = DISPLAYED; 1132137019Sdes } 113357429Smarkm } 113465668Skris if (close(ofd) == -1) { 113565668Skris wrerr = YES; 113665668Skris wrerrno = errno; 113765668Skris } 113857429Smarkm (void) response(); 113957429Smarkm if (setimes && wrerr == NO) { 114057429Smarkm setimes = 0; 114169587Sgreen if (utimes(np, tv) < 0) { 114257429Smarkm run_err("%s: set times: %s", 114376259Sgreen np, strerror(errno)); 114457429Smarkm wrerr = DISPLAYED; 114557429Smarkm } 114657429Smarkm } 114757429Smarkm switch (wrerr) { 114857429Smarkm case YES: 114957429Smarkm run_err("%s: %s", np, strerror(wrerrno)); 115057429Smarkm break; 115157429Smarkm case NO: 1152124211Sdes (void) atomicio(vwrite, remout, "", 1); 115357429Smarkm break; 115457429Smarkm case DISPLAYED: 115557429Smarkm break; 115657429Smarkm } 115757429Smarkm } 115857429Smarkmscrewup: 115957429Smarkm run_err("protocol error: %s", why); 116057429Smarkm exit(1); 116157429Smarkm} 116257429Smarkm 116357429Smarkmint 116492555Sdesresponse(void) 116557429Smarkm{ 116657429Smarkm char ch, *cp, resp, rbuf[2048]; 116757429Smarkm 116860573Skris if (atomicio(read, remin, &resp, sizeof(resp)) != sizeof(resp)) 116957429Smarkm lostconn(0); 117057429Smarkm 117157429Smarkm cp = rbuf; 117257429Smarkm switch (resp) { 117357429Smarkm case 0: /* ok */ 117457429Smarkm return (0); 117557429Smarkm default: 117657429Smarkm *cp++ = resp; 117757429Smarkm /* FALLTHROUGH */ 117857429Smarkm case 1: /* error, followed by error msg */ 117957429Smarkm case 2: /* fatal error, "" */ 118057429Smarkm do { 118160573Skris if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) 118257429Smarkm lostconn(0); 118357429Smarkm *cp++ = ch; 118457429Smarkm } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); 118557429Smarkm 118657429Smarkm if (!iamremote) 1187124211Sdes (void) atomicio(vwrite, STDERR_FILENO, rbuf, cp - rbuf); 118857429Smarkm ++errs; 118957429Smarkm if (resp == 1) 119057429Smarkm return (-1); 119157429Smarkm exit(1); 119257429Smarkm } 119357429Smarkm /* NOTREACHED */ 119457429Smarkm} 119557429Smarkm 119657429Smarkmvoid 119792555Sdesusage(void) 119857429Smarkm{ 119992555Sdes (void) fprintf(stderr, 1200126277Sdes "usage: scp [-1246BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n" 1201126277Sdes " [-l limit] [-o ssh_option] [-P port] [-S program]\n" 1202181111Sdes " [[user@]host1:]file1 ... [[user@]host2:]file2\n"); 120357429Smarkm exit(1); 120457429Smarkm} 120557429Smarkm 120657429Smarkmvoid 120757429Smarkmrun_err(const char *fmt,...) 120857429Smarkm{ 120957429Smarkm static FILE *fp; 121057429Smarkm va_list ap; 121157429Smarkm 121257429Smarkm ++errs; 1213162856Sdes if (fp != NULL || (remout != -1 && (fp = fdopen(remout, "w")))) { 1214162856Sdes (void) fprintf(fp, "%c", 0x01); 1215162856Sdes (void) fprintf(fp, "scp: "); 1216162856Sdes va_start(ap, fmt); 1217162856Sdes (void) vfprintf(fp, fmt, ap); 1218162856Sdes va_end(ap); 1219162856Sdes (void) fprintf(fp, "\n"); 1220162856Sdes (void) fflush(fp); 1221162856Sdes } 122257429Smarkm 122357429Smarkm if (!iamremote) { 122492555Sdes va_start(ap, fmt); 122557429Smarkm vfprintf(stderr, fmt, ap); 122692555Sdes va_end(ap); 122757429Smarkm fprintf(stderr, "\n"); 122857429Smarkm } 122957429Smarkm} 123057429Smarkm 123157429Smarkmvoid 1232124211Sdesverifydir(char *cp) 123357429Smarkm{ 123457429Smarkm struct stat stb; 123557429Smarkm 123657429Smarkm if (!stat(cp, &stb)) { 123757429Smarkm if (S_ISDIR(stb.st_mode)) 123857429Smarkm return; 123957429Smarkm errno = ENOTDIR; 124057429Smarkm } 124157429Smarkm run_err("%s: %s", cp, strerror(errno)); 1242149753Sdes killchild(0); 124357429Smarkm} 124457429Smarkm 124557429Smarkmint 1246124211Sdesokname(char *cp0) 124757429Smarkm{ 124857429Smarkm int c; 124957429Smarkm char *cp; 125057429Smarkm 125157429Smarkm cp = cp0; 125257429Smarkm do { 125392555Sdes c = (int)*cp; 125457429Smarkm if (c & 0200) 125557429Smarkm goto bad; 1256113911Sdes if (!isalpha(c) && !isdigit(c)) { 1257113911Sdes switch (c) { 1258113911Sdes case '\'': 1259113911Sdes case '"': 1260113911Sdes case '`': 1261113911Sdes case ' ': 1262113911Sdes case '#': 1263113911Sdes goto bad; 1264113911Sdes default: 1265113911Sdes break; 1266113911Sdes } 1267113911Sdes } 126857429Smarkm } while (*++cp); 126957429Smarkm return (1); 127057429Smarkm 127157429Smarkmbad: fprintf(stderr, "%s: invalid user name\n", cp0); 127257429Smarkm return (0); 127357429Smarkm} 127457429Smarkm 127557429SmarkmBUF * 1276124211Sdesallocbuf(BUF *bp, int fd, int blksize) 127757429Smarkm{ 127857429Smarkm size_t size; 127998937Sdes#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE 128057429Smarkm struct stat stb; 128157429Smarkm 128257429Smarkm if (fstat(fd, &stb) < 0) { 128357429Smarkm run_err("fstat: %s", strerror(errno)); 128457429Smarkm return (0); 128557429Smarkm } 1286113911Sdes size = roundup(stb.st_blksize, blksize); 1287113911Sdes if (size == 0) 128857429Smarkm size = blksize; 128998937Sdes#else /* HAVE_STRUCT_STAT_ST_BLKSIZE */ 129098937Sdes size = blksize; 129198937Sdes#endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */ 129257429Smarkm if (bp->cnt >= size) 129357429Smarkm return (bp); 129457429Smarkm if (bp->buf == NULL) 129557429Smarkm bp->buf = xmalloc(size); 129657429Smarkm else 1297162856Sdes bp->buf = xrealloc(bp->buf, 1, size); 129892555Sdes memset(bp->buf, 0, size); 129957429Smarkm bp->cnt = size; 130057429Smarkm return (bp); 130157429Smarkm} 130257429Smarkm 130357429Smarkmvoid 1304124211Sdeslostconn(int signo) 130557429Smarkm{ 130657429Smarkm if (!iamremote) 130792555Sdes write(STDERR_FILENO, "lost connection\n", 16); 130892555Sdes if (signo) 130992555Sdes _exit(1); 131092555Sdes else 131192555Sdes exit(1); 131257429Smarkm} 1313