mirror of git://anongit.mindrot.org/openssh.git
upstream: fix scp in SFTP mode recursive upload and download of
directories that contain symlinks to other directories. In scp mode, the links would be followed, but in SFTP mode they were not. bz3611, ok dtucker@ OpenBSD-Commit-ID: 9760fda668eaa94a992250d7670dfbc62a45197c
This commit is contained in:
parent
0e1f4401c4
commit
249d8bd047
|
@ -1,4 +1,4 @@
|
||||||
/* $OpenBSD: sftp-client.c,v 1.171 2023/04/30 22:54:22 djm Exp $ */
|
/* $OpenBSD: sftp-client.c,v 1.172 2023/09/08 05:50:12 djm Exp $ */
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
|
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
|
||||||
*
|
*
|
||||||
|
@ -1890,6 +1890,7 @@ download_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
|
||||||
SFTP_DIRENT **dir_entries;
|
SFTP_DIRENT **dir_entries;
|
||||||
char *filename, *new_src = NULL, *new_dst = NULL;
|
char *filename, *new_src = NULL, *new_dst = NULL;
|
||||||
mode_t mode = 0777, tmpmode = mode;
|
mode_t mode = 0777, tmpmode = mode;
|
||||||
|
Attrib *a, ldirattrib, lsym;
|
||||||
|
|
||||||
if (depth >= MAX_DIR_DEPTH) {
|
if (depth >= MAX_DIR_DEPTH) {
|
||||||
error("Maximum directory depth exceeded: %d levels", depth);
|
error("Maximum directory depth exceeded: %d levels", depth);
|
||||||
|
@ -1898,10 +1899,14 @@ download_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
|
||||||
|
|
||||||
debug2_f("download dir remote \"%s\" to local \"%s\"", src, dst);
|
debug2_f("download dir remote \"%s\" to local \"%s\"", src, dst);
|
||||||
|
|
||||||
if (dirattrib == NULL &&
|
if (dirattrib == NULL) {
|
||||||
(dirattrib = do_stat(conn, src, 1)) == NULL) {
|
if ((a = do_stat(conn, src, 1)) == NULL) {
|
||||||
error("stat remote \"%s\" directory failed", src);
|
error("stat remote \"%s\" directory failed", src);
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
/* Don't let this be clobbered by later do_stat calls */
|
||||||
|
ldirattrib = *a;
|
||||||
|
dirattrib = &ldirattrib;
|
||||||
}
|
}
|
||||||
if (!S_ISDIR(dirattrib->perm)) {
|
if (!S_ISDIR(dirattrib->perm)) {
|
||||||
error("\"%s\" is not a directory", src);
|
error("\"%s\" is not a directory", src);
|
||||||
|
@ -1936,25 +1941,35 @@ download_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
|
||||||
new_dst = path_append(dst, filename);
|
new_dst = path_append(dst, filename);
|
||||||
new_src = path_append(src, filename);
|
new_src = path_append(src, filename);
|
||||||
|
|
||||||
if (S_ISDIR(dir_entries[i]->a.perm)) {
|
a = &dir_entries[i]->a;
|
||||||
|
if (S_ISLNK(a->perm)) {
|
||||||
|
if (!follow_link_flag) {
|
||||||
|
logit("download \"%s\": not a regular file",
|
||||||
|
new_src);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* Replace the stat contents with the symlink target */
|
||||||
|
if ((a = do_stat(conn, new_src, 1)) == NULL) {
|
||||||
|
logit("remote stat \"%s\" failed", new_src);
|
||||||
|
ret = -1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* Don't let this be clobbered by later do_stat calls */
|
||||||
|
lsym = *a;
|
||||||
|
a = &lsym;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (S_ISDIR(a->perm)) {
|
||||||
if (strcmp(filename, ".") == 0 ||
|
if (strcmp(filename, ".") == 0 ||
|
||||||
strcmp(filename, "..") == 0)
|
strcmp(filename, "..") == 0)
|
||||||
continue;
|
continue;
|
||||||
if (download_dir_internal(conn, new_src, new_dst,
|
if (download_dir_internal(conn, new_src, new_dst,
|
||||||
depth + 1, &(dir_entries[i]->a), preserve_flag,
|
depth + 1, a, preserve_flag,
|
||||||
print_flag, resume_flag,
|
print_flag, resume_flag,
|
||||||
fsync_flag, follow_link_flag, inplace_flag) == -1)
|
fsync_flag, follow_link_flag, inplace_flag) == -1)
|
||||||
ret = -1;
|
ret = -1;
|
||||||
} else if (S_ISREG(dir_entries[i]->a.perm) ||
|
} else if (S_ISREG(a->perm)) {
|
||||||
(follow_link_flag && S_ISLNK(dir_entries[i]->a.perm))) {
|
if (do_download(conn, new_src, new_dst, a,
|
||||||
/*
|
|
||||||
* If this is a symlink then don't send the link's
|
|
||||||
* Attrib. do_download() will do a FXP_STAT operation
|
|
||||||
* and get the link target's attributes.
|
|
||||||
*/
|
|
||||||
if (do_download(conn, new_src, new_dst,
|
|
||||||
S_ISLNK(dir_entries[i]->a.perm) ? NULL :
|
|
||||||
&(dir_entries[i]->a),
|
|
||||||
preserve_flag, resume_flag, fsync_flag,
|
preserve_flag, resume_flag, fsync_flag,
|
||||||
inplace_flag) == -1) {
|
inplace_flag) == -1) {
|
||||||
error("Download of file %s to %s failed",
|
error("Download of file %s to %s failed",
|
||||||
|
@ -2299,21 +2314,33 @@ upload_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
|
||||||
new_dst = path_append(dst, filename);
|
new_dst = path_append(dst, filename);
|
||||||
new_src = path_append(src, filename);
|
new_src = path_append(src, filename);
|
||||||
|
|
||||||
|
if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0)
|
||||||
|
continue;
|
||||||
if (lstat(new_src, &sb) == -1) {
|
if (lstat(new_src, &sb) == -1) {
|
||||||
logit("local lstat \"%s\": %s", filename,
|
logit("local lstat \"%s\": %s", filename,
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
ret = -1;
|
ret = -1;
|
||||||
} else if (S_ISDIR(sb.st_mode)) {
|
continue;
|
||||||
if (strcmp(filename, ".") == 0 ||
|
}
|
||||||
strcmp(filename, "..") == 0)
|
if (S_ISLNK(sb.st_mode)) {
|
||||||
|
if (!follow_link_flag) {
|
||||||
|
logit("%s: not a regular file", filename);
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
/* Replace the stat contents with the symlink target */
|
||||||
|
if (stat(new_src, &sb) == -1) {
|
||||||
|
logit("local stat \"%s\": %s", filename,
|
||||||
|
strerror(errno));
|
||||||
|
ret = -1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (S_ISDIR(sb.st_mode)) {
|
||||||
if (upload_dir_internal(conn, new_src, new_dst,
|
if (upload_dir_internal(conn, new_src, new_dst,
|
||||||
depth + 1, preserve_flag, print_flag, resume,
|
depth + 1, preserve_flag, print_flag, resume,
|
||||||
fsync_flag, follow_link_flag, inplace_flag) == -1)
|
fsync_flag, follow_link_flag, inplace_flag) == -1)
|
||||||
ret = -1;
|
ret = -1;
|
||||||
} else if (S_ISREG(sb.st_mode) ||
|
} else if (S_ISREG(sb.st_mode)) {
|
||||||
(follow_link_flag && S_ISLNK(sb.st_mode))) {
|
|
||||||
if (do_upload(conn, new_src, new_dst,
|
if (do_upload(conn, new_src, new_dst,
|
||||||
preserve_flag, resume, fsync_flag,
|
preserve_flag, resume, fsync_flag,
|
||||||
inplace_flag) == -1) {
|
inplace_flag) == -1) {
|
||||||
|
|
Loading…
Reference in New Issue