83b3dbf601
have been this way since at least 4.4. This will still dump core if malloc fails on the first trip through, instead of on any malloc failure, but should otherwise behave much more reasonably.
806 lines
18 KiB
C
806 lines
18 KiB
C
/* $NetBSD: rcp.c,v 1.48 2009/08/31 07:11:16 dholland Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 1983, 1990, 1992, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
#ifndef lint
|
|
__COPYRIGHT("@(#) Copyright (c) 1983, 1990, 1992, 1993\
|
|
The Regents of the University of California. All rights reserved.");
|
|
#endif /* not lint */
|
|
|
|
#ifndef lint
|
|
#if 0
|
|
static char sccsid[] = "@(#)rcp.c 8.2 (Berkeley) 4/2/94";
|
|
#else
|
|
__RCSID("$NetBSD: rcp.c,v 1.48 2009/08/31 07:11:16 dholland Exp $");
|
|
#endif
|
|
#endif /* not lint */
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/in_systm.h>
|
|
#include <netinet/ip.h>
|
|
|
|
#include <ctype.h>
|
|
#include <dirent.h>
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <locale.h>
|
|
#include <netdb.h>
|
|
#include <pwd.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "pathnames.h"
|
|
#include "extern.h"
|
|
|
|
#define OPTIONS "46dfprt"
|
|
|
|
struct passwd *pwd;
|
|
char *pwname;
|
|
u_short port;
|
|
uid_t userid;
|
|
int errs, rem;
|
|
int pflag, iamremote, iamrecursive, targetshouldbedirectory;
|
|
int family = AF_UNSPEC;
|
|
static char dot[] = ".";
|
|
|
|
#define CMDNEEDS 64
|
|
char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */
|
|
|
|
int response(void);
|
|
void rsource(char *, struct stat *);
|
|
void sink(int, char *[]);
|
|
void source(int, char *[]);
|
|
void tolocal(int, char *[]);
|
|
void toremote(char *, int, char *[]);
|
|
void usage(void);
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
struct servent *sp;
|
|
int ch, fflag, tflag;
|
|
char *targ;
|
|
const char *shell;
|
|
|
|
setprogname(argv[0]);
|
|
(void)setlocale(LC_ALL, "");
|
|
|
|
fflag = tflag = 0;
|
|
while ((ch = getopt(argc, argv, OPTIONS)) != -1)
|
|
switch(ch) { /* User-visible flags. */
|
|
case '4':
|
|
family = AF_INET;
|
|
break;
|
|
case '6':
|
|
family = AF_INET6;
|
|
break;
|
|
case 'K':
|
|
break;
|
|
case 'p':
|
|
pflag = 1;
|
|
break;
|
|
case 'r':
|
|
iamrecursive = 1;
|
|
break;
|
|
/* Server options. */
|
|
case 'd':
|
|
targetshouldbedirectory = 1;
|
|
break;
|
|
case 'f': /* "from" */
|
|
iamremote = 1;
|
|
fflag = 1;
|
|
break;
|
|
case 't': /* "to" */
|
|
iamremote = 1;
|
|
tflag = 1;
|
|
break;
|
|
case '?':
|
|
default:
|
|
usage();
|
|
}
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
sp = getservbyname(shell = "shell", "tcp");
|
|
if (sp == NULL)
|
|
errx(1, "%s/tcp: unknown service", shell);
|
|
port = sp->s_port;
|
|
|
|
if ((pwd = getpwuid(userid = getuid())) == NULL)
|
|
errx(1, "unknown user %d", (int)userid);
|
|
|
|
if ((pwname = strdup(pwd->pw_name)) == NULL)
|
|
err(1, NULL);
|
|
|
|
rem = STDIN_FILENO; /* XXX */
|
|
|
|
if (fflag) { /* Follow "protocol", send data. */
|
|
(void)response();
|
|
source(argc, argv);
|
|
exit(errs);
|
|
}
|
|
|
|
if (tflag) { /* Receive data. */
|
|
sink(argc, argv);
|
|
exit(errs);
|
|
}
|
|
|
|
if (argc < 2)
|
|
usage();
|
|
if (argc > 2)
|
|
targetshouldbedirectory = 1;
|
|
|
|
rem = -1;
|
|
/* Command to be executed on remote system using "rsh". */
|
|
(void)snprintf(cmd, sizeof(cmd), "rcp%s%s%s",
|
|
iamrecursive ? " -r" : "", pflag ? " -p" : "",
|
|
targetshouldbedirectory ? " -d" : "");
|
|
|
|
(void)signal(SIGPIPE, lostconn);
|
|
|
|
if ((targ = colon(argv[argc - 1])) != NULL)/* Dest is remote host. */
|
|
toremote(targ, argc, argv);
|
|
else {
|
|
tolocal(argc, argv); /* Dest is local host. */
|
|
if (targetshouldbedirectory)
|
|
verifydir(argv[argc - 1]);
|
|
}
|
|
exit(errs);
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
void
|
|
toremote(char *targ, int argc, char *argv[])
|
|
{
|
|
int i;
|
|
size_t len;
|
|
char *bp, *host, *src, *suser, *thost, *tuser;
|
|
|
|
*targ++ = 0;
|
|
if (*targ == 0)
|
|
targ = dot;
|
|
|
|
if ((thost = strchr(argv[argc - 1], '@')) != NULL) {
|
|
/* user@host */
|
|
*thost++ = 0;
|
|
tuser = argv[argc - 1];
|
|
if (*tuser == '\0')
|
|
tuser = NULL;
|
|
else if (!okname(tuser))
|
|
exit(1);
|
|
} else {
|
|
thost = argv[argc - 1];
|
|
tuser = NULL;
|
|
}
|
|
thost = unbracket(thost);
|
|
|
|
for (i = 0; i < argc - 1; i++) {
|
|
src = colon(argv[i]);
|
|
if (src) { /* remote to remote */
|
|
*src++ = 0;
|
|
if (*src == 0)
|
|
src = dot;
|
|
host = strchr(argv[i], '@');
|
|
len = strlen(_PATH_RSH) + strlen(argv[i]) +
|
|
strlen(src) + (tuser ? strlen(tuser) : 0) +
|
|
strlen(thost) + strlen(targ) + CMDNEEDS + 20;
|
|
if (!(bp = malloc(len)))
|
|
err(1, NULL);
|
|
if (host) {
|
|
*host++ = 0;
|
|
host = unbracket(host);
|
|
suser = argv[i];
|
|
if (*suser == '\0')
|
|
suser = pwname;
|
|
else if (!okname(suser)) {
|
|
(void)free(bp);
|
|
continue;
|
|
}
|
|
(void)snprintf(bp, len,
|
|
"%s %s -l %s -n %s %s '%s%s%s:%s'",
|
|
_PATH_RSH, host, suser, cmd, src,
|
|
tuser ? tuser : "", tuser ? "@" : "",
|
|
thost, targ);
|
|
} else {
|
|
host = unbracket(argv[i]);
|
|
(void)snprintf(bp, len,
|
|
"exec %s %s -n %s %s '%s%s%s:%s'",
|
|
_PATH_RSH, argv[i], cmd, src,
|
|
tuser ? tuser : "", tuser ? "@" : "",
|
|
thost, targ);
|
|
}
|
|
(void)susystem(bp);
|
|
(void)free(bp);
|
|
} else { /* local to remote */
|
|
if (rem == -1) {
|
|
len = strlen(targ) + CMDNEEDS + 20;
|
|
if (!(bp = malloc(len)))
|
|
err(1, NULL);
|
|
(void)snprintf(bp, len, "%s -t %s", cmd, targ);
|
|
host = thost;
|
|
rem = rcmd_af(&host, port, pwname,
|
|
tuser ? tuser : pwname,
|
|
bp, NULL, family);
|
|
if (rem < 0)
|
|
exit(1);
|
|
if (response() < 0)
|
|
exit(1);
|
|
(void)free(bp);
|
|
}
|
|
source(1, argv+i);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
tolocal(int argc, char *argv[])
|
|
{
|
|
int i;
|
|
size_t len;
|
|
char *bp, *host, *src, *suser;
|
|
|
|
for (i = 0; i < argc - 1; i++) {
|
|
if (!(src = colon(argv[i]))) { /* Local to local. */
|
|
len = strlen(_PATH_CP) + strlen(argv[i]) +
|
|
strlen(argv[argc - 1]) + 20;
|
|
if (!(bp = malloc(len)))
|
|
err(1, NULL);
|
|
(void)snprintf(bp, len, "exec %s%s%s %s %s", _PATH_CP,
|
|
iamrecursive ? " -r" : "", pflag ? " -p" : "",
|
|
argv[i], argv[argc - 1]);
|
|
if (susystem(bp))
|
|
++errs;
|
|
(void)free(bp);
|
|
continue;
|
|
}
|
|
*src++ = 0;
|
|
if (*src == 0)
|
|
src = dot;
|
|
if ((host = strchr(argv[i], '@')) == NULL) {
|
|
host = argv[i];
|
|
suser = pwname;
|
|
} else {
|
|
*host++ = 0;
|
|
suser = argv[i];
|
|
if (*suser == '\0')
|
|
suser = pwname;
|
|
else if (!okname(suser))
|
|
continue;
|
|
}
|
|
host = unbracket(host);
|
|
len = strlen(src) + CMDNEEDS + 20;
|
|
if ((bp = malloc(len)) == NULL)
|
|
err(1, NULL);
|
|
(void)snprintf(bp, len, "%s -f %s", cmd, src);
|
|
rem =
|
|
rcmd_af(&host, port, pwname, suser, bp, NULL, family);
|
|
(void)free(bp);
|
|
if (rem < 0) {
|
|
++errs;
|
|
continue;
|
|
}
|
|
sink(1, argv + argc - 1);
|
|
(void)close(rem);
|
|
rem = -1;
|
|
}
|
|
}
|
|
|
|
void
|
|
source(int argc, char *argv[])
|
|
{
|
|
struct stat stb;
|
|
static BUF buffer;
|
|
BUF *bp;
|
|
off_t i;
|
|
off_t amt;
|
|
int fd, haderr, indx, result;
|
|
char *last, *name, buf[BUFSIZ];
|
|
|
|
for (indx = 0; indx < argc; ++indx) {
|
|
name = argv[indx];
|
|
if ((fd = open(name, O_RDONLY, 0)) < 0)
|
|
goto syserr;
|
|
if (fstat(fd, &stb)) {
|
|
syserr: run_err("%s: %s", name, strerror(errno));
|
|
goto next;
|
|
}
|
|
switch (stb.st_mode & S_IFMT) {
|
|
case S_IFREG:
|
|
break;
|
|
case S_IFDIR:
|
|
if (iamrecursive) {
|
|
rsource(name, &stb);
|
|
goto next;
|
|
}
|
|
/* FALLTHROUGH */
|
|
default:
|
|
run_err("%s: not a regular file", name);
|
|
goto next;
|
|
}
|
|
if ((last = strrchr(name, '/')) == NULL)
|
|
last = name;
|
|
else
|
|
++last;
|
|
if (pflag) {
|
|
/*
|
|
* Make it compatible with possible future
|
|
* versions expecting microseconds.
|
|
*/
|
|
(void)snprintf(buf, sizeof(buf), "T%lld %ld %lld %ld\n",
|
|
(long long)stb.st_mtimespec.tv_sec,
|
|
(long)stb.st_mtimespec.tv_nsec / 1000,
|
|
(long long)stb.st_atimespec.tv_sec,
|
|
(long)stb.st_atimespec.tv_nsec / 1000);
|
|
(void)write(rem, buf, strlen(buf));
|
|
if (response() < 0)
|
|
goto next;
|
|
}
|
|
#define RCPMODEMASK (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO)
|
|
(void)snprintf(buf, sizeof(buf), "C%04o %lld %s\n",
|
|
stb.st_mode & RCPMODEMASK, (long long)stb.st_size, last);
|
|
(void)write(rem, buf, strlen(buf));
|
|
if (response() < 0)
|
|
goto next;
|
|
if ((bp = allocbuf(&buffer, fd, BUFSIZ)) == NULL) {
|
|
next: (void)close(fd);
|
|
continue;
|
|
}
|
|
|
|
/* Keep writing after an error so that we stay sync'd up. */
|
|
haderr = 0;
|
|
for (i = 0; i < stb.st_size; i += bp->cnt) {
|
|
amt = bp->cnt;
|
|
if (i + amt > stb.st_size)
|
|
amt = stb.st_size - i;
|
|
if (!haderr) {
|
|
result = read(fd, bp->buf, (size_t)amt);
|
|
if (result != amt)
|
|
haderr = result >= 0 ? EIO : errno;
|
|
}
|
|
if (haderr)
|
|
(void)write(rem, bp->buf, (size_t)amt);
|
|
else {
|
|
result = write(rem, bp->buf, (size_t)amt);
|
|
if (result != amt)
|
|
haderr = result >= 0 ? EIO : errno;
|
|
}
|
|
}
|
|
if (close(fd) && !haderr)
|
|
haderr = errno;
|
|
if (!haderr)
|
|
(void)write(rem, "", 1);
|
|
else
|
|
run_err("%s: %s", name, strerror(haderr));
|
|
(void)response();
|
|
}
|
|
}
|
|
|
|
void
|
|
rsource(char *name, struct stat *statp)
|
|
{
|
|
DIR *dirp;
|
|
struct dirent *dp;
|
|
char *last, *vect[1], path[MAXPATHLEN];
|
|
|
|
if (!(dirp = opendir(name))) {
|
|
run_err("%s: %s", name, strerror(errno));
|
|
return;
|
|
}
|
|
last = strrchr(name, '/');
|
|
if (last == 0)
|
|
last = name;
|
|
else
|
|
last++;
|
|
if (pflag) {
|
|
(void)snprintf(path, sizeof(path), "T%lld %ld %lld %ld\n",
|
|
(long long)statp->st_mtimespec.tv_sec,
|
|
(long)statp->st_mtimespec.tv_nsec / 1000,
|
|
(long long)statp->st_atimespec.tv_sec,
|
|
(long)statp->st_atimespec.tv_nsec / 1000);
|
|
(void)write(rem, path, strlen(path));
|
|
if (response() < 0) {
|
|
(void)closedir(dirp);
|
|
return;
|
|
}
|
|
}
|
|
(void)snprintf(path, sizeof(path),
|
|
"D%04o %d %s\n", statp->st_mode & RCPMODEMASK, 0, last);
|
|
(void)write(rem, path, strlen(path));
|
|
if (response() < 0) {
|
|
(void)closedir(dirp);
|
|
return;
|
|
}
|
|
while ((dp = readdir(dirp)) != NULL) {
|
|
if (dp->d_ino == 0)
|
|
continue;
|
|
if (!strcmp(dp->d_name, dot) || !strcmp(dp->d_name, ".."))
|
|
continue;
|
|
if (strlen(name) + 1 + strlen(dp->d_name) >= MAXPATHLEN - 1) {
|
|
run_err("%s/%s: name too long", name, dp->d_name);
|
|
continue;
|
|
}
|
|
(void)snprintf(path, sizeof(path), "%s/%s", name, dp->d_name);
|
|
vect[0] = path;
|
|
source(1, vect);
|
|
}
|
|
(void)closedir(dirp);
|
|
(void)write(rem, "E\n", 2);
|
|
(void)response();
|
|
}
|
|
|
|
void
|
|
sink(int argc, char *argv[])
|
|
{
|
|
static BUF buffer;
|
|
struct stat stb;
|
|
struct timeval tv[2];
|
|
enum { YES, NO, DISPLAYED } wrerr;
|
|
BUF *bp;
|
|
ssize_t j;
|
|
off_t i;
|
|
off_t amt;
|
|
off_t count;
|
|
int exists, first, ofd;
|
|
mode_t mask;
|
|
mode_t mode;
|
|
mode_t omode;
|
|
int setimes, targisdir;
|
|
int wrerrno = 0; /* pacify gcc */
|
|
char ch, *cp, *np, *targ, *vect[1], buf[BUFSIZ];
|
|
const char *why;
|
|
off_t size;
|
|
|
|
#define atime tv[0]
|
|
#define mtime tv[1]
|
|
#define SCREWUP(str) { why = str; goto screwup; }
|
|
|
|
setimes = targisdir = 0;
|
|
mask = umask(0);
|
|
if (!pflag)
|
|
(void)umask(mask);
|
|
if (argc != 1) {
|
|
run_err("ambiguous target");
|
|
exit(1);
|
|
}
|
|
targ = *argv;
|
|
if (targetshouldbedirectory)
|
|
verifydir(targ);
|
|
(void)write(rem, "", 1);
|
|
if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode))
|
|
targisdir = 1;
|
|
for (first = 1;; first = 0) {
|
|
cp = buf;
|
|
if (read(rem, cp, 1) <= 0)
|
|
return;
|
|
if (*cp++ == '\n')
|
|
SCREWUP("unexpected <newline>");
|
|
do {
|
|
if (read(rem, &ch, sizeof(ch)) != sizeof(ch))
|
|
SCREWUP("lost connection");
|
|
*cp++ = ch;
|
|
} while (cp < &buf[BUFSIZ - 1] && ch != '\n');
|
|
*cp = 0;
|
|
|
|
if (buf[0] == '\01' || buf[0] == '\02') {
|
|
if (iamremote == 0)
|
|
(void)write(STDERR_FILENO,
|
|
buf + 1, strlen(buf + 1));
|
|
if (buf[0] == '\02')
|
|
exit(1);
|
|
++errs;
|
|
continue;
|
|
}
|
|
if (buf[0] == 'E') {
|
|
(void)write(rem, "", 1);
|
|
return;
|
|
}
|
|
|
|
if (ch == '\n')
|
|
*--cp = 0;
|
|
|
|
#define getnum(t) (t) = 0; while (isdigit((unsigned char)*cp)) (t) = (t) * 10 + (*cp++ - '0');
|
|
cp = buf;
|
|
if (*cp == 'T') {
|
|
setimes++;
|
|
cp++;
|
|
getnum(mtime.tv_sec);
|
|
if (*cp++ != ' ')
|
|
SCREWUP("mtime.sec not delimited");
|
|
getnum(mtime.tv_usec);
|
|
if (*cp++ != ' ')
|
|
SCREWUP("mtime.usec not delimited");
|
|
getnum(atime.tv_sec);
|
|
if (*cp++ != ' ')
|
|
SCREWUP("atime.sec not delimited");
|
|
getnum(atime.tv_usec);
|
|
if (*cp++ != '\0')
|
|
SCREWUP("atime.usec not delimited");
|
|
(void)write(rem, "", 1);
|
|
continue;
|
|
}
|
|
if (*cp != 'C' && *cp != 'D') {
|
|
/*
|
|
* Check for the case "rcp remote:foo\* local:bar".
|
|
* In this case, the line "No match." can be returned
|
|
* by the shell before the rcp command on the remote is
|
|
* executed so the ^Aerror_message convention isn't
|
|
* followed.
|
|
*/
|
|
if (first) {
|
|
run_err("%s", cp);
|
|
exit(1);
|
|
}
|
|
SCREWUP("expected control record");
|
|
}
|
|
mode = 0;
|
|
for (++cp; cp < buf + 5; cp++) {
|
|
if (*cp < '0' || *cp > '7')
|
|
SCREWUP("bad mode");
|
|
mode = (mode << 3) | (*cp - '0');
|
|
}
|
|
if (*cp++ != ' ')
|
|
SCREWUP("mode not delimited");
|
|
|
|
for (size = 0; isdigit((unsigned char)*cp);)
|
|
size = size * 10 + (*cp++ - '0');
|
|
if (*cp++ != ' ')
|
|
SCREWUP("size not delimited");
|
|
if (targisdir) {
|
|
static char *namebuf;
|
|
static size_t cursize;
|
|
char *newnamebuf;
|
|
size_t need;
|
|
|
|
need = strlen(targ) + strlen(cp) + 250;
|
|
if (need > cursize) {
|
|
newnamebuf = realloc(namebuf, need);
|
|
if (newnamebuf != NULL) {
|
|
namebuf = newnamebuf;
|
|
cursize = need;
|
|
} else {
|
|
/* note: run_err is not fatal */
|
|
run_err("%s", strerror(errno));
|
|
}
|
|
}
|
|
(void)snprintf(namebuf, cursize, "%s%s%s", targ,
|
|
*targ ? "/" : "", cp);
|
|
np = namebuf;
|
|
} else
|
|
np = targ;
|
|
exists = stat(np, &stb) == 0;
|
|
if (buf[0] == 'D') {
|
|
int mod_flag = pflag;
|
|
if (exists) {
|
|
if (!S_ISDIR(stb.st_mode)) {
|
|
errno = ENOTDIR;
|
|
goto bad;
|
|
}
|
|
if (pflag)
|
|
(void)chmod(np, mode);
|
|
} else {
|
|
/* Handle copying from a read-only directory */
|
|
mod_flag = 1;
|
|
if (mkdir(np, mode | S_IRWXU) < 0)
|
|
goto bad;
|
|
}
|
|
vect[0] = np;
|
|
sink(1, vect);
|
|
if (setimes) {
|
|
setimes = 0;
|
|
if (utimes(np, tv) < 0)
|
|
run_err("%s: set times: %s",
|
|
np, strerror(errno));
|
|
}
|
|
if (mod_flag)
|
|
(void)chmod(np, mode);
|
|
continue;
|
|
}
|
|
omode = mode;
|
|
mode |= S_IWRITE;
|
|
if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) {
|
|
bad: run_err("%s: %s", np, strerror(errno));
|
|
continue;
|
|
}
|
|
(void)write(rem, "", 1);
|
|
if ((bp = allocbuf(&buffer, ofd, BUFSIZ)) == NULL) {
|
|
(void)close(ofd);
|
|
continue;
|
|
}
|
|
cp = bp->buf;
|
|
wrerr = NO;
|
|
count = 0;
|
|
for (i = 0; i < size; i += BUFSIZ) {
|
|
amt = BUFSIZ;
|
|
if (i + amt > size)
|
|
amt = size - i;
|
|
count += amt;
|
|
do {
|
|
j = read(rem, cp, (size_t)amt);
|
|
if (j == -1) {
|
|
run_err("%s", j ? strerror(errno) :
|
|
"dropped connection");
|
|
exit(1);
|
|
}
|
|
amt -= j;
|
|
cp += j;
|
|
} while (amt > 0);
|
|
if (count == bp->cnt) {
|
|
/* Keep reading so we stay sync'd up. */
|
|
if (wrerr == NO) {
|
|
j = write(ofd, bp->buf, (size_t)count);
|
|
if (j != count) {
|
|
wrerr = YES;
|
|
wrerrno = j >= 0 ? EIO : errno;
|
|
}
|
|
}
|
|
count = 0;
|
|
cp = bp->buf;
|
|
}
|
|
}
|
|
if (count != 0 && wrerr == NO &&
|
|
(j = write(ofd, bp->buf, (size_t)count)) != count) {
|
|
wrerr = YES;
|
|
wrerrno = j >= 0 ? EIO : errno;
|
|
}
|
|
if (ftruncate(ofd, size)) {
|
|
run_err("%s: truncate: %s", np, strerror(errno));
|
|
wrerr = DISPLAYED;
|
|
}
|
|
if (pflag) {
|
|
if (exists || omode != mode)
|
|
if (fchmod(ofd, omode))
|
|
run_err("%s: set mode: %s",
|
|
np, strerror(errno));
|
|
} else {
|
|
if (!exists && omode != mode)
|
|
if (fchmod(ofd, omode & ~mask))
|
|
run_err("%s: set mode: %s",
|
|
np, strerror(errno));
|
|
}
|
|
#ifndef __SVR4
|
|
if (setimes && wrerr == NO) {
|
|
setimes = 0;
|
|
if (futimes(ofd, tv) < 0) {
|
|
run_err("%s: set times: %s",
|
|
np, strerror(errno));
|
|
wrerr = DISPLAYED;
|
|
}
|
|
}
|
|
#endif
|
|
(void)close(ofd);
|
|
#ifdef __SVR4
|
|
if (setimes && wrerr == NO) {
|
|
setimes = 0;
|
|
if (utimes(np, tv) < 0) {
|
|
run_err("%s: set times: %s",
|
|
np, strerror(errno));
|
|
wrerr = DISPLAYED;
|
|
}
|
|
}
|
|
#endif
|
|
(void)response();
|
|
switch(wrerr) {
|
|
case YES:
|
|
run_err("%s: write: %s", np, strerror(wrerrno));
|
|
break;
|
|
case NO:
|
|
(void)write(rem, "", 1);
|
|
break;
|
|
case DISPLAYED:
|
|
break;
|
|
}
|
|
}
|
|
screwup:
|
|
run_err("protocol error: %s", why);
|
|
exit(1);
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
|
|
int
|
|
response(void)
|
|
{
|
|
char ch, *cp, resp, rbuf[BUFSIZ];
|
|
|
|
if (read(rem, &resp, sizeof(resp)) != sizeof(resp))
|
|
lostconn(0);
|
|
|
|
cp = rbuf;
|
|
switch(resp) {
|
|
case 0: /* ok */
|
|
return (0);
|
|
default:
|
|
*cp++ = resp;
|
|
/* FALLTHROUGH */
|
|
case 1: /* error, followed by error msg */
|
|
case 2: /* fatal error, "" */
|
|
do {
|
|
if (read(rem, &ch, sizeof(ch)) != sizeof(ch))
|
|
lostconn(0);
|
|
*cp++ = ch;
|
|
} while (cp < &rbuf[BUFSIZ] && ch != '\n');
|
|
|
|
if (!iamremote)
|
|
(void)write(STDERR_FILENO, rbuf, (size_t)(cp - rbuf));
|
|
++errs;
|
|
if (resp == 1)
|
|
return (-1);
|
|
exit(1);
|
|
}
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
void
|
|
usage(void)
|
|
{
|
|
(void)fprintf(stderr,
|
|
"usage: rcp [-46p] f1 f2; or: rcp [-46pr] f1 ... fn directory\n");
|
|
exit(1);
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
|
void
|
|
run_err(const char *fmt, ...)
|
|
{
|
|
static FILE *fp;
|
|
va_list ap;
|
|
|
|
++errs;
|
|
if (fp == NULL && !(fp = fdopen(rem, "w")))
|
|
return;
|
|
|
|
va_start(ap, fmt);
|
|
|
|
(void)fprintf(fp, "%c", 0x01);
|
|
(void)fprintf(fp, "rcp: ");
|
|
(void)vfprintf(fp, fmt, ap);
|
|
(void)fprintf(fp, "\n");
|
|
(void)fflush(fp);
|
|
va_end(ap);
|
|
|
|
if (!iamremote) {
|
|
va_start(ap, fmt);
|
|
vwarnx(fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
}
|