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