mirror of
https://github.com/PowerShell/openssh-portable.git
synced 2025-07-29 08:44:52 +02:00
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
|
* scp - secure remote copy. This is basically patched BSD rcp which
|
||||||
* uses ssh to do the data transfer (instead of using rcmd).
|
* uses ssh to do the data transfer (instead of using rcmd).
|
||||||
@ -139,7 +139,7 @@ extern char *__progname;
|
|||||||
#define COPY_BUFLEN 16384
|
#define COPY_BUFLEN 16384
|
||||||
|
|
||||||
int do_cmd(char *program, char *host, char *remuser, int port, char *cmd,
|
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 do_cmd2(char *host, char *remuser, int port, char *cmd,
|
||||||
int fdin, int fdout);
|
int fdin, int fdout);
|
||||||
|
|
||||||
@ -175,6 +175,7 @@ char *ssh_program = _PATH_SSH_PROGRAM;
|
|||||||
|
|
||||||
/* This is used to store the pid of ssh_program */
|
/* This is used to store the pid of ssh_program */
|
||||||
pid_t do_cmd_pid = -1;
|
pid_t do_cmd_pid = -1;
|
||||||
|
pid_t do_cmd_pid2 = -1;
|
||||||
|
|
||||||
/* Needed for sftp */
|
/* Needed for sftp */
|
||||||
volatile sig_atomic_t interrupted = 0;
|
volatile sig_atomic_t interrupted = 0;
|
||||||
@ -189,6 +190,10 @@ killchild(int signo)
|
|||||||
kill(do_cmd_pid, signo ? signo : SIGTERM);
|
kill(do_cmd_pid, signo ? signo : SIGTERM);
|
||||||
waitpid(do_cmd_pid, NULL, 0);
|
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)
|
if (signo)
|
||||||
_exit(1);
|
_exit(1);
|
||||||
@ -196,19 +201,26 @@ killchild(int signo)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
suspchild(int signo)
|
suspone(int pid, int signo)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
if (do_cmd_pid > 1) {
|
if (pid > 1) {
|
||||||
kill(do_cmd_pid, signo);
|
kill(pid, signo);
|
||||||
while (waitpid(do_cmd_pid, &status, WUNTRACED) == -1 &&
|
while (waitpid(pid, &status, WUNTRACED) == -1 &&
|
||||||
errno == EINTR)
|
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
|
static int
|
||||||
do_local_cmd(arglist *a)
|
do_local_cmd(arglist *a)
|
||||||
{
|
{
|
||||||
@ -259,7 +271,7 @@ do_local_cmd(arglist *a)
|
|||||||
|
|
||||||
int
|
int
|
||||||
do_cmd(char *program, char *host, char *remuser, int port, char *cmd,
|
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];
|
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);
|
ssh_signal(SIGTTOU, suspchild);
|
||||||
|
|
||||||
/* Fork a child to execute the command on the remote host using ssh. */
|
/* Fork a child to execute the command on the remote host using ssh. */
|
||||||
do_cmd_pid = fork();
|
*pid = fork();
|
||||||
if (do_cmd_pid == 0) {
|
if (*pid == 0) {
|
||||||
/* Child. */
|
/* Child. */
|
||||||
close(pin[1]);
|
close(pin[1]);
|
||||||
close(pout[0]);
|
close(pout[0]);
|
||||||
@ -320,7 +332,7 @@ do_cmd(char *program, char *host, char *remuser, int port, char *cmd,
|
|||||||
execvp(program, args.list);
|
execvp(program, args.list);
|
||||||
perror(program);
|
perror(program);
|
||||||
exit(1);
|
exit(1);
|
||||||
} else if (do_cmd_pid == -1) {
|
} else if (*pid == -1) {
|
||||||
fatal("fork: %s", strerror(errno));
|
fatal("fork: %s", strerror(errno));
|
||||||
}
|
}
|
||||||
/* Parent. Close the other side, and return the local side. */
|
/* 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.
|
* This way the input and output of two commands can be connected.
|
||||||
*/
|
*/
|
||||||
int
|
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;
|
int status;
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
if (verbose_mode)
|
if (verbose_mode)
|
||||||
fmprintf(stderr,
|
fmprintf(stderr,
|
||||||
@ -403,7 +416,7 @@ void verifydir(char *);
|
|||||||
|
|
||||||
struct passwd *pwd;
|
struct passwd *pwd;
|
||||||
uid_t userid;
|
uid_t userid;
|
||||||
int errs, remin, remout;
|
int errs, remin, remout, remin2, remout2;
|
||||||
int Tflag, pflag, iamremote, iamrecursive, targetshouldbedirectory;
|
int Tflag, pflag, iamremote, iamrecursive, targetshouldbedirectory;
|
||||||
|
|
||||||
#define CMDNEEDS 64
|
#define CMDNEEDS 64
|
||||||
@ -424,6 +437,8 @@ void usage(void);
|
|||||||
|
|
||||||
void source_sftp(int, char *, char *, struct sftp_conn *, char **);
|
void source_sftp(int, char *, char *, struct sftp_conn *, char **);
|
||||||
void sink_sftp(int, char *, const char *, struct sftp_conn *);
|
void sink_sftp(int, char *, const char *, struct sftp_conn *);
|
||||||
|
void throughlocal_sftp(struct sftp_conn *, struct sftp_conn *,
|
||||||
|
char *, char *, char **);
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, char **argv)
|
main(int argc, char **argv)
|
||||||
@ -943,22 +958,23 @@ brace_expand(const char *pattern, char ***patternsp, size_t *npatternsp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static struct sftp_conn *
|
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) {
|
if (sftp_direct == NULL) {
|
||||||
addargs(&args, "-s");
|
addargs(&args, "-s");
|
||||||
if (do_cmd(ssh_program, host, user, port, "sftp",
|
if (do_cmd(ssh_program, host, user, port, "sftp",
|
||||||
&remin, &remout) < 0)
|
reminp, remoutp, pidp) < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
args.list = NULL;
|
args.list = NULL;
|
||||||
addargs(&args, "sftp-server");
|
addargs(&args, "sftp-server");
|
||||||
if (do_cmd(sftp_direct, host, NULL, -1, "sftp",
|
if (do_cmd(sftp_direct, host, NULL, -1, "sftp",
|
||||||
&remin, &remout) < 0)
|
reminp, remoutp, pidp) < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return do_init(remin, remout, 32768, 64, limit_kbps);
|
return do_init(*reminp, *remoutp, 32768, 64, limit_kbps);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -968,9 +984,9 @@ toremote(int argc, char **argv, enum scp_mode_e mode, char *sftp_direct)
|
|||||||
char *bp, *tuser, *thost, *targ;
|
char *bp, *tuser, *thost, *targ;
|
||||||
char *remote_path = NULL;
|
char *remote_path = NULL;
|
||||||
int sport = -1, tport = -1;
|
int sport = -1, tport = -1;
|
||||||
struct sftp_conn *conn = NULL;
|
struct sftp_conn *conn = NULL, *conn2 = NULL;
|
||||||
arglist alist;
|
arglist alist;
|
||||||
int i, r;
|
int i, r, status;
|
||||||
u_int j;
|
u_int j;
|
||||||
|
|
||||||
memset(&alist, '\0', sizeof(alist));
|
memset(&alist, '\0', sizeof(alist));
|
||||||
@ -1011,21 +1027,64 @@ toremote(int argc, char **argv, enum scp_mode_e mode, char *sftp_direct)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (host && throughlocal) { /* extended remote to remote */
|
if (host && throughlocal) { /* extended remote to remote */
|
||||||
/* XXX uses scp; need to support SFTP remote-remote */
|
if (mode == MODE_SFTP) {
|
||||||
xasprintf(&bp, "%s -f %s%s", cmd,
|
if (remin == -1) {
|
||||||
*src == '-' ? "-- " : "", src);
|
/* Connect to dest now */
|
||||||
if (do_cmd(ssh_program, host, suser, sport, bp,
|
conn = do_sftp_connect(thost, tuser,
|
||||||
&remin, &remout) < 0)
|
tport, sftp_direct,
|
||||||
exit(1);
|
&remin, &remout, &do_cmd_pid);
|
||||||
free(bp);
|
if (conn == NULL) {
|
||||||
xasprintf(&bp, "%s -t %s%s", cmd,
|
fatal("Unable to open "
|
||||||
*targ == '-' ? "-- " : "", targ);
|
"destination connection");
|
||||||
if (do_cmd2(thost, tuser, tport, bp, remin, remout) < 0)
|
}
|
||||||
exit(1);
|
debug3_f("origin in %d out %d pid %ld",
|
||||||
free(bp);
|
remin, remout, (long)do_cmd_pid);
|
||||||
(void) close(remin);
|
}
|
||||||
(void) close(remout);
|
/*
|
||||||
remin = remout = -1;
|
* 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 */
|
} else if (host) { /* standard remote to remote */
|
||||||
/*
|
/*
|
||||||
* Second remote user is passed to first remote side
|
* 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) {
|
if (remin == -1) {
|
||||||
/* Connect to remote now */
|
/* Connect to remote now */
|
||||||
conn = do_sftp_connect(thost, tuser,
|
conn = do_sftp_connect(thost, tuser,
|
||||||
tport, sftp_direct);
|
tport, sftp_direct,
|
||||||
|
&remin, &remout, &do_cmd_pid);
|
||||||
if (conn == NULL) {
|
if (conn == NULL) {
|
||||||
fatal("Unable to open sftp "
|
fatal("Unable to open sftp "
|
||||||
"connection");
|
"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,
|
xasprintf(&bp, "%s -t %s%s", cmd,
|
||||||
*targ == '-' ? "-- " : "", targ);
|
*targ == '-' ? "-- " : "", targ);
|
||||||
if (do_cmd(ssh_program, thost, tuser, tport, bp,
|
if (do_cmd(ssh_program, thost, tuser, tport, bp,
|
||||||
&remin, &remout) < 0)
|
&remin, &remout, &do_cmd_pid) < 0)
|
||||||
exit(1);
|
exit(1);
|
||||||
if (response() < 0)
|
if (response() < 0)
|
||||||
exit(1);
|
exit(1);
|
||||||
@ -1156,7 +1216,8 @@ tolocal(int argc, char **argv, enum scp_mode_e mode, char *sftp_direct)
|
|||||||
}
|
}
|
||||||
/* Remote to local. */
|
/* Remote to local. */
|
||||||
if (mode == MODE_SFTP) {
|
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) {
|
if (conn == NULL) {
|
||||||
error("Couldn't make sftp connection "
|
error("Couldn't make sftp connection "
|
||||||
"to server");
|
"to server");
|
||||||
@ -1176,8 +1237,8 @@ tolocal(int argc, char **argv, enum scp_mode_e mode, char *sftp_direct)
|
|||||||
/* SCP */
|
/* SCP */
|
||||||
xasprintf(&bp, "%s -f %s%s",
|
xasprintf(&bp, "%s -f %s%s",
|
||||||
cmd, *src == '-' ? "-- " : "", src);
|
cmd, *src == '-' ? "-- " : "", src);
|
||||||
if (do_cmd(ssh_program, host, suser, sport, bp, &remin,
|
if (do_cmd(ssh_program, host, suser, sport, bp,
|
||||||
&remout) < 0) {
|
&remin, &remout, &do_cmd_pid) < 0) {
|
||||||
free(bp);
|
free(bp);
|
||||||
++errs;
|
++errs;
|
||||||
continue;
|
continue;
|
||||||
@ -1808,6 +1869,94 @@ screwup:
|
|||||||
exit(1);
|
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
|
int
|
||||||
response(void)
|
response(void)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user