mirror of git://anongit.mindrot.org/openssh.git
upstream: use sftp_client crossloading to implement scp -3
feedback/ok markus@ OpenBSD-Commit-ID: 7db4c0086cfc12afc9cfb71d4c2fd3c7e9416ee9
This commit is contained in:
parent
de7115b373
commit
318c06bb04
229
scp.c
229
scp.c
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: scp.c,v 1.218 2021/08/07 00:00:33 djm Exp $ */
|
||||
/* $OpenBSD: scp.c,v 1.219 2021/08/07 00:06:30 djm Exp $ */
|
||||
/*
|
||||
* scp - secure remote copy. This is basically patched BSD rcp which
|
||||
* uses ssh to do the data transfer (instead of using rcmd).
|
||||
|
@ -139,7 +139,7 @@ extern char *__progname;
|
|||
#define COPY_BUFLEN 16384
|
||||
|
||||
int do_cmd(char *program, char *host, char *remuser, int port, char *cmd,
|
||||
int *fdin, int *fdout);
|
||||
int *fdin, int *fdout, pid_t *pidp);
|
||||
int do_cmd2(char *host, char *remuser, int port, char *cmd,
|
||||
int fdin, int fdout);
|
||||
|
||||
|
@ -175,6 +175,7 @@ char *ssh_program = _PATH_SSH_PROGRAM;
|
|||
|
||||
/* This is used to store the pid of ssh_program */
|
||||
pid_t do_cmd_pid = -1;
|
||||
pid_t do_cmd_pid2 = -1;
|
||||
|
||||
/* Needed for sftp */
|
||||
volatile sig_atomic_t interrupted = 0;
|
||||
|
@ -189,6 +190,10 @@ killchild(int signo)
|
|||
kill(do_cmd_pid, signo ? signo : SIGTERM);
|
||||
waitpid(do_cmd_pid, NULL, 0);
|
||||
}
|
||||
if (do_cmd_pid2 > 1) {
|
||||
kill(do_cmd_pid2, signo ? signo : SIGTERM);
|
||||
waitpid(do_cmd_pid2, NULL, 0);
|
||||
}
|
||||
|
||||
if (signo)
|
||||
_exit(1);
|
||||
|
@ -196,19 +201,26 @@ killchild(int signo)
|
|||
}
|
||||
|
||||
static void
|
||||
suspchild(int signo)
|
||||
suspone(int pid, int signo)
|
||||
{
|
||||
int status;
|
||||
|
||||
if (do_cmd_pid > 1) {
|
||||
kill(do_cmd_pid, signo);
|
||||
while (waitpid(do_cmd_pid, &status, WUNTRACED) == -1 &&
|
||||
if (pid > 1) {
|
||||
kill(pid, signo);
|
||||
while (waitpid(pid, &status, WUNTRACED) == -1 &&
|
||||
errno == EINTR)
|
||||
;
|
||||
kill(getpid(), SIGSTOP);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
suspchild(int signo)
|
||||
{
|
||||
suspone(do_cmd_pid, signo);
|
||||
suspone(do_cmd_pid2, signo);
|
||||
kill(getpid(), SIGSTOP);
|
||||
}
|
||||
|
||||
static int
|
||||
do_local_cmd(arglist *a)
|
||||
{
|
||||
|
@ -259,7 +271,7 @@ do_local_cmd(arglist *a)
|
|||
|
||||
int
|
||||
do_cmd(char *program, char *host, char *remuser, int port, char *cmd,
|
||||
int *fdin, int *fdout)
|
||||
int *fdin, int *fdout, int *pid)
|
||||
{
|
||||
int pin[2], pout[2], reserved[2];
|
||||
|
||||
|
@ -294,8 +306,8 @@ do_cmd(char *program, char *host, char *remuser, int port, char *cmd,
|
|||
ssh_signal(SIGTTOU, suspchild);
|
||||
|
||||
/* Fork a child to execute the command on the remote host using ssh. */
|
||||
do_cmd_pid = fork();
|
||||
if (do_cmd_pid == 0) {
|
||||
*pid = fork();
|
||||
if (*pid == 0) {
|
||||
/* Child. */
|
||||
close(pin[1]);
|
||||
close(pout[0]);
|
||||
|
@ -320,7 +332,7 @@ do_cmd(char *program, char *host, char *remuser, int port, char *cmd,
|
|||
execvp(program, args.list);
|
||||
perror(program);
|
||||
exit(1);
|
||||
} else if (do_cmd_pid == -1) {
|
||||
} else if (*pid == -1) {
|
||||
fatal("fork: %s", strerror(errno));
|
||||
}
|
||||
/* Parent. Close the other side, and return the local side. */
|
||||
|
@ -340,10 +352,11 @@ do_cmd(char *program, char *host, char *remuser, int port, char *cmd,
|
|||
* This way the input and output of two commands can be connected.
|
||||
*/
|
||||
int
|
||||
do_cmd2(char *host, char *remuser, int port, char *cmd, int fdin, int fdout)
|
||||
do_cmd2(char *host, char *remuser, int port, char *cmd,
|
||||
int fdin, int fdout)
|
||||
{
|
||||
pid_t pid;
|
||||
int status;
|
||||
pid_t pid;
|
||||
|
||||
if (verbose_mode)
|
||||
fmprintf(stderr,
|
||||
|
@ -403,7 +416,7 @@ void verifydir(char *);
|
|||
|
||||
struct passwd *pwd;
|
||||
uid_t userid;
|
||||
int errs, remin, remout;
|
||||
int errs, remin, remout, remin2, remout2;
|
||||
int Tflag, pflag, iamremote, iamrecursive, targetshouldbedirectory;
|
||||
|
||||
#define CMDNEEDS 64
|
||||
|
@ -424,6 +437,8 @@ void usage(void);
|
|||
|
||||
void source_sftp(int, char *, char *, struct sftp_conn *, char **);
|
||||
void sink_sftp(int, char *, const char *, struct sftp_conn *);
|
||||
void throughlocal_sftp(struct sftp_conn *, struct sftp_conn *,
|
||||
char *, char *, char **);
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
|
@ -943,22 +958,23 @@ brace_expand(const char *pattern, char ***patternsp, size_t *npatternsp)
|
|||
}
|
||||
|
||||
static struct sftp_conn *
|
||||
do_sftp_connect(char *host, char *user, int port, char *sftp_direct)
|
||||
do_sftp_connect(char *host, char *user, int port, char *sftp_direct,
|
||||
int *reminp, int *remoutp, int *pidp)
|
||||
{
|
||||
if (sftp_direct == NULL) {
|
||||
addargs(&args, "-s");
|
||||
if (do_cmd(ssh_program, host, user, port, "sftp",
|
||||
&remin, &remout) < 0)
|
||||
reminp, remoutp, pidp) < 0)
|
||||
return NULL;
|
||||
|
||||
} else {
|
||||
args.list = NULL;
|
||||
addargs(&args, "sftp-server");
|
||||
if (do_cmd(sftp_direct, host, NULL, -1, "sftp",
|
||||
&remin, &remout) < 0)
|
||||
reminp, remoutp, pidp) < 0)
|
||||
return NULL;
|
||||
}
|
||||
return do_init(remin, remout, 32768, 64, limit_kbps);
|
||||
return do_init(*reminp, *remoutp, 32768, 64, limit_kbps);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -968,9 +984,9 @@ toremote(int argc, char **argv, enum scp_mode_e mode, char *sftp_direct)
|
|||
char *bp, *tuser, *thost, *targ;
|
||||
char *remote_path = NULL;
|
||||
int sport = -1, tport = -1;
|
||||
struct sftp_conn *conn = NULL;
|
||||
struct sftp_conn *conn = NULL, *conn2 = NULL;
|
||||
arglist alist;
|
||||
int i, r;
|
||||
int i, r, status;
|
||||
u_int j;
|
||||
|
||||
memset(&alist, '\0', sizeof(alist));
|
||||
|
@ -1011,21 +1027,64 @@ toremote(int argc, char **argv, enum scp_mode_e mode, char *sftp_direct)
|
|||
continue;
|
||||
}
|
||||
if (host && throughlocal) { /* extended remote to remote */
|
||||
/* XXX uses scp; need to support SFTP remote-remote */
|
||||
xasprintf(&bp, "%s -f %s%s", cmd,
|
||||
*src == '-' ? "-- " : "", src);
|
||||
if (do_cmd(ssh_program, host, suser, sport, bp,
|
||||
&remin, &remout) < 0)
|
||||
exit(1);
|
||||
free(bp);
|
||||
xasprintf(&bp, "%s -t %s%s", cmd,
|
||||
*targ == '-' ? "-- " : "", targ);
|
||||
if (do_cmd2(thost, tuser, tport, bp, remin, remout) < 0)
|
||||
exit(1);
|
||||
free(bp);
|
||||
(void) close(remin);
|
||||
(void) close(remout);
|
||||
remin = remout = -1;
|
||||
if (mode == MODE_SFTP) {
|
||||
if (remin == -1) {
|
||||
/* Connect to dest now */
|
||||
conn = do_sftp_connect(thost, tuser,
|
||||
tport, sftp_direct,
|
||||
&remin, &remout, &do_cmd_pid);
|
||||
if (conn == NULL) {
|
||||
fatal("Unable to open "
|
||||
"destination connection");
|
||||
}
|
||||
debug3_f("origin in %d out %d pid %ld",
|
||||
remin, remout, (long)do_cmd_pid);
|
||||
}
|
||||
/*
|
||||
* XXX remember suser/host/sport and only
|
||||
* reconnect if they change between arguments.
|
||||
* would save reconnections for cases like
|
||||
* scp -3 hosta:/foo hosta:/bar hostb:
|
||||
*/
|
||||
/* Connect to origin now */
|
||||
conn2 = do_sftp_connect(host, suser,
|
||||
sport, sftp_direct,
|
||||
&remin2, &remout2, &do_cmd_pid2);
|
||||
if (conn2 == NULL) {
|
||||
fatal("Unable to open "
|
||||
"source connection");
|
||||
}
|
||||
debug3_f("destination in %d out %d pid %ld",
|
||||
remin2, remout2, (long)do_cmd_pid2);
|
||||
throughlocal_sftp(conn2, conn, src, targ,
|
||||
&remote_path);
|
||||
(void) close(remin2);
|
||||
(void) close(remout2);
|
||||
remin2 = remout2 = -1;
|
||||
if (waitpid(do_cmd_pid2, &status, 0) == -1)
|
||||
++errs;
|
||||
else if (!WIFEXITED(status) ||
|
||||
WEXITSTATUS(status) != 0)
|
||||
++errs;
|
||||
do_cmd_pid2 = -1;
|
||||
continue;
|
||||
} else {
|
||||
xasprintf(&bp, "%s -f %s%s", cmd,
|
||||
*src == '-' ? "-- " : "", src);
|
||||
if (do_cmd(ssh_program, host, suser, sport,
|
||||
bp, &remin, &remout, &do_cmd_pid) < 0)
|
||||
exit(1);
|
||||
free(bp);
|
||||
xasprintf(&bp, "%s -t %s%s", cmd,
|
||||
*targ == '-' ? "-- " : "", targ);
|
||||
if (do_cmd2(thost, tuser, tport, bp,
|
||||
remin, remout) < 0)
|
||||
exit(1);
|
||||
free(bp);
|
||||
(void) close(remin);
|
||||
(void) close(remout);
|
||||
remin = remout = -1;
|
||||
}
|
||||
} else if (host) { /* standard remote to remote */
|
||||
/*
|
||||
* Second remote user is passed to first remote side
|
||||
|
@ -1074,7 +1133,8 @@ toremote(int argc, char **argv, enum scp_mode_e mode, char *sftp_direct)
|
|||
if (remin == -1) {
|
||||
/* Connect to remote now */
|
||||
conn = do_sftp_connect(thost, tuser,
|
||||
tport, sftp_direct);
|
||||
tport, sftp_direct,
|
||||
&remin, &remout, &do_cmd_pid);
|
||||
if (conn == NULL) {
|
||||
fatal("Unable to open sftp "
|
||||
"connection");
|
||||
|
@ -1091,7 +1151,7 @@ toremote(int argc, char **argv, enum scp_mode_e mode, char *sftp_direct)
|
|||
xasprintf(&bp, "%s -t %s%s", cmd,
|
||||
*targ == '-' ? "-- " : "", targ);
|
||||
if (do_cmd(ssh_program, thost, tuser, tport, bp,
|
||||
&remin, &remout) < 0)
|
||||
&remin, &remout, &do_cmd_pid) < 0)
|
||||
exit(1);
|
||||
if (response() < 0)
|
||||
exit(1);
|
||||
|
@ -1156,7 +1216,8 @@ tolocal(int argc, char **argv, enum scp_mode_e mode, char *sftp_direct)
|
|||
}
|
||||
/* Remote to local. */
|
||||
if (mode == MODE_SFTP) {
|
||||
conn = do_sftp_connect(host, suser, sport, sftp_direct);
|
||||
conn = do_sftp_connect(host, suser, sport,
|
||||
sftp_direct, &remin, &remout, &do_cmd_pid);
|
||||
if (conn == NULL) {
|
||||
error("Couldn't make sftp connection "
|
||||
"to server");
|
||||
|
@ -1176,8 +1237,8 @@ tolocal(int argc, char **argv, enum scp_mode_e mode, char *sftp_direct)
|
|||
/* SCP */
|
||||
xasprintf(&bp, "%s -f %s%s",
|
||||
cmd, *src == '-' ? "-- " : "", src);
|
||||
if (do_cmd(ssh_program, host, suser, sport, bp, &remin,
|
||||
&remout) < 0) {
|
||||
if (do_cmd(ssh_program, host, suser, sport, bp,
|
||||
&remin, &remout, &do_cmd_pid) < 0) {
|
||||
free(bp);
|
||||
++errs;
|
||||
continue;
|
||||
|
@ -1808,6 +1869,94 @@ screwup:
|
|||
exit(1);
|
||||
}
|
||||
|
||||
void
|
||||
throughlocal_sftp(struct sftp_conn *from, struct sftp_conn *to,
|
||||
char *src, char *targ, char **to_remote_path)
|
||||
{
|
||||
char *target = NULL, *filename = NULL, *abs_dst = NULL;
|
||||
char *abs_src = NULL, *tmp = NULL, *from_remote_path;
|
||||
glob_t g;
|
||||
int i, r, targetisdir, err = 0;
|
||||
|
||||
if (*to_remote_path == NULL) {
|
||||
*to_remote_path = do_realpath(to, ".");
|
||||
if (*to_remote_path == NULL) {
|
||||
fatal("Unable to determine destination remote "
|
||||
"working directory");
|
||||
}
|
||||
}
|
||||
|
||||
if ((from_remote_path = do_realpath(from, ".")) == NULL) {
|
||||
fatal("Unable to determine source remote "
|
||||
"working directory");
|
||||
}
|
||||
|
||||
if ((filename = basename(src)) == NULL)
|
||||
fatal("basename %s: %s", src, strerror(errno));
|
||||
|
||||
abs_src = xstrdup(src);
|
||||
abs_src = make_absolute(abs_src, from_remote_path);
|
||||
free(from_remote_path);
|
||||
target = xstrdup(targ);
|
||||
target = make_absolute(target, *to_remote_path);
|
||||
memset(&g, 0, sizeof(g));
|
||||
|
||||
targetisdir = remote_is_dir(to, target);
|
||||
if (!targetisdir && targetshouldbedirectory) {
|
||||
error("Destination path \"%s\" is not a directory", target);
|
||||
err = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
debug3_f("copying remote %s to remote %s", abs_src, target);
|
||||
if ((r = remote_glob(from, abs_src, GLOB_MARK, NULL, &g)) != 0) {
|
||||
if (r == GLOB_NOSPACE)
|
||||
error("Too many glob matches for \"%s\".", abs_src);
|
||||
else
|
||||
error("File \"%s\" not found.", abs_src);
|
||||
err = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
|
||||
tmp = xstrdup(g.gl_pathv[i]);
|
||||
if ((filename = basename(tmp)) == NULL) {
|
||||
error("basename %s: %s", tmp, strerror(errno));
|
||||
err = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (targetisdir)
|
||||
abs_dst = path_append(target, filename);
|
||||
else
|
||||
abs_dst = xstrdup(target);
|
||||
|
||||
debug("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
|
||||
if (globpath_is_dir(g.gl_pathv[i]) && iamrecursive) {
|
||||
if (crossload_dir(from, to, g.gl_pathv[i], abs_dst,
|
||||
NULL, pflag, 1) == -1)
|
||||
err = -1;
|
||||
} else {
|
||||
if (do_crossload(from, to, g.gl_pathv[i], abs_dst, NULL,
|
||||
pflag) == -1)
|
||||
err = -1;
|
||||
}
|
||||
free(abs_dst);
|
||||
abs_dst = NULL;
|
||||
free(tmp);
|
||||
tmp = NULL;
|
||||
}
|
||||
|
||||
out:
|
||||
free(abs_src);
|
||||
free(abs_dst);
|
||||
free(target);
|
||||
free(tmp);
|
||||
globfree(&g);
|
||||
if (err == -1)
|
||||
fatal("Failed to download file '%s'", src);
|
||||
}
|
||||
|
||||
int
|
||||
response(void)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue