From 7b1cbcb7599d9f6a3bbad79d412604aa1203b5ee Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Sat, 7 Aug 2021 00:12:09 +0000 Subject: upstream: make scp(1) in SFTP mode follow symlinks like traditional scp(1) ok markus@ OpenBSD-Commit-ID: 97255e55be37e8e26605e4ba1e69f9781765d231 --- scp.c | 8 ++++---- sftp-client.c | 58 +++++++++++++++++++++++++++++++++++++++------------------- sftp-client.h | 9 +++++---- sftp.c | 8 +++++--- 4 files changed, 53 insertions(+), 30 deletions(-) diff --git a/scp.c b/scp.c index 51925455..3eb0212b 100644 --- a/scp.c +++ b/scp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: scp.c,v 1.220 2021/08/07 00:08:52 djm Exp $ */ +/* $OpenBSD: scp.c,v 1.221 2021/08/07 00:12:09 djm Exp $ */ /* * scp - secure remote copy. This is basically patched BSD rcp which * uses ssh to do the data transfer (instead of using rcmd). @@ -1290,7 +1290,7 @@ source_sftp(int argc, char *src, char *targ, if (local_is_dir(src) && iamrecursive) { if (upload_dir(conn, src, abs_dst, pflag, - SFTP_PROGRESS_ONLY, 0, 0) != 0) { + SFTP_PROGRESS_ONLY, 0, 0, 1) != 0) { fatal("failed to upload directory %s to %s", src, abs_dst); } @@ -1522,7 +1522,7 @@ sink_sftp(int argc, char *dst, const char *src, struct sftp_conn *conn) debug("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); if (globpath_is_dir(g.gl_pathv[i]) && iamrecursive) { if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL, - pflag, SFTP_PROGRESS_ONLY, 0, 0) == -1) + pflag, SFTP_PROGRESS_ONLY, 0, 0, 1) == -1) err = -1; } else { if (do_download(conn, g.gl_pathv[i], abs_dst, NULL, @@ -1935,7 +1935,7 @@ throughlocal_sftp(struct sftp_conn *from, struct sftp_conn *to, 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) + NULL, pflag, SFTP_PROGRESS_ONLY, 1) == -1) err = -1; } else { if (do_crossload(from, to, g.gl_pathv[i], abs_dst, NULL, diff --git a/sftp-client.c b/sftp-client.c index d4ddc94f..40fae328 100644 --- a/sftp-client.c +++ b/sftp-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp-client.c,v 1.149 2021/08/07 00:10:49 djm Exp $ */ +/* $OpenBSD: sftp-client.c,v 1.150 2021/08/07 00:12:09 djm Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * @@ -1615,7 +1615,7 @@ do_download(struct sftp_conn *conn, const char *remote_path, static int download_dir_internal(struct sftp_conn *conn, const char *src, const char *dst, int depth, Attrib *dirattrib, int preserve_flag, int print_flag, - int resume_flag, int fsync_flag) + int resume_flag, int fsync_flag, int follow_link_flag) { int i, ret = 0; SFTP_DIRENT **dir_entries; @@ -1671,12 +1671,20 @@ download_dir_internal(struct sftp_conn *conn, const char *src, const char *dst, continue; if (download_dir_internal(conn, new_src, new_dst, depth + 1, &(dir_entries[i]->a), preserve_flag, - print_flag, resume_flag, fsync_flag) == -1) + print_flag, resume_flag, + fsync_flag, follow_link_flag) == -1) ret = -1; - } else if (S_ISREG(dir_entries[i]->a.perm) ) { + } else if (S_ISREG(dir_entries[i]->a.perm) || + (follow_link_flag && S_ISLNK(dir_entries[i]->a.perm))) { + /* + * 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, - &(dir_entries[i]->a), preserve_flag, - resume_flag, fsync_flag) == -1) { + S_ISLNK(dir_entries[i]->a.perm) ? NULL : + &(dir_entries[i]->a), + preserve_flag, resume_flag, fsync_flag) == -1) { error("Download of file %s to %s failed", new_src, new_dst); ret = -1; @@ -1714,7 +1722,7 @@ download_dir_internal(struct sftp_conn *conn, const char *src, const char *dst, int download_dir(struct sftp_conn *conn, const char *src, const char *dst, Attrib *dirattrib, int preserve_flag, int print_flag, int resume_flag, - int fsync_flag) + int fsync_flag, int follow_link_flag) { char *src_canon; int ret; @@ -1725,7 +1733,8 @@ download_dir(struct sftp_conn *conn, const char *src, const char *dst, } ret = download_dir_internal(conn, src_canon, dst, 0, - dirattrib, preserve_flag, print_flag, resume_flag, fsync_flag); + dirattrib, preserve_flag, print_flag, resume_flag, fsync_flag, + follow_link_flag); free(src_canon); return ret; } @@ -1936,7 +1945,8 @@ do_upload(struct sftp_conn *conn, const char *local_path, static int upload_dir_internal(struct sftp_conn *conn, const char *src, const char *dst, - int depth, int preserve_flag, int print_flag, int resume, int fsync_flag) + int depth, int preserve_flag, int print_flag, int resume, int fsync_flag, + int follow_link_flag) { int ret = 0; DIR *dirp; @@ -2014,9 +2024,10 @@ upload_dir_internal(struct sftp_conn *conn, const char *src, const char *dst, if (upload_dir_internal(conn, new_src, new_dst, depth + 1, preserve_flag, print_flag, resume, - fsync_flag) == -1) + fsync_flag, follow_link_flag) == -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, preserve_flag, resume, fsync_flag) == -1) { error("Uploading of file %s to %s failed!", @@ -2037,7 +2048,8 @@ upload_dir_internal(struct sftp_conn *conn, const char *src, const char *dst, int upload_dir(struct sftp_conn *conn, const char *src, const char *dst, - int preserve_flag, int print_flag, int resume, int fsync_flag) + int preserve_flag, int print_flag, int resume, int fsync_flag, + int follow_link_flag) { char *dst_canon; int ret; @@ -2048,7 +2060,7 @@ upload_dir(struct sftp_conn *conn, const char *src, const char *dst, } ret = upload_dir_internal(conn, src, dst_canon, 0, preserve_flag, - print_flag, resume, fsync_flag); + print_flag, resume, fsync_flag, follow_link_flag); free(dst_canon); return ret; @@ -2372,7 +2384,8 @@ do_crossload(struct sftp_conn *from, struct sftp_conn *to, static int crossload_dir_internal(struct sftp_conn *from, struct sftp_conn *to, const char *from_path, const char *to_path, - int depth, Attrib *dirattrib, int preserve_flag, int print_flag) + int depth, Attrib *dirattrib, int preserve_flag, int print_flag, + int follow_link_flag) { int i, ret = 0; SFTP_DIRENT **dir_entries; @@ -2394,7 +2407,7 @@ crossload_dir_internal(struct sftp_conn *from, struct sftp_conn *to, error("\"%s\" is not a directory", from_path); return -1; } - if (print_flag) + if (print_flag && print_flag != SFTP_PROGRESS_ONLY) mprintf("Retrieving %s\n", from_path); curdir = *dirattrib; /* dirattrib will be clobbered */ @@ -2446,10 +2459,17 @@ crossload_dir_internal(struct sftp_conn *from, struct sftp_conn *to, if (crossload_dir_internal(from, to, new_from_path, new_to_path, depth + 1, &(dir_entries[i]->a), preserve_flag, - print_flag) == -1) + print_flag, follow_link_flag) == -1) ret = -1; - } else if (S_ISREG(dir_entries[i]->a.perm) ) { + } else if (S_ISREG(dir_entries[i]->a.perm) || + (follow_link_flag && S_ISLNK(dir_entries[i]->a.perm))) { + /* + * 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_crossload(from, to, new_from_path, new_to_path, + S_ISLNK(dir_entries[i]->a.perm) ? NULL : &(dir_entries[i]->a), preserve_flag) == -1) { error("Transfer of file %s to %s failed", new_from_path, new_to_path); @@ -2472,7 +2492,7 @@ crossload_dir_internal(struct sftp_conn *from, struct sftp_conn *to, int crossload_dir(struct sftp_conn *from, struct sftp_conn *to, const char *from_path, const char *to_path, - Attrib *dirattrib, int preserve_flag, int print_flag) + Attrib *dirattrib, int preserve_flag, int print_flag, int follow_link_flag) { char *from_path_canon; int ret; @@ -2483,7 +2503,7 @@ crossload_dir(struct sftp_conn *from, struct sftp_conn *to, } ret = crossload_dir_internal(from, to, from_path_canon, to_path, 0, - dirattrib, preserve_flag, print_flag); + dirattrib, preserve_flag, print_flag, follow_link_flag); free(from_path_canon); return ret; } diff --git a/sftp-client.h b/sftp-client.h index 1de6ba8f..00707f7c 100644 --- a/sftp-client.h +++ b/sftp-client.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp-client.h,v 1.32 2021/08/07 00:08:52 djm Exp $ */ +/* $OpenBSD: sftp-client.h,v 1.33 2021/08/07 00:12:09 djm Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller @@ -140,7 +140,7 @@ int do_download(struct sftp_conn *, const char *, const char *, * times if 'pflag' is set */ int download_dir(struct sftp_conn *, const char *, const char *, - Attrib *, int, int, int, int); + Attrib *, int, int, int, int, int); /* * Upload 'local_path' to 'remote_path'. Preserve permissions and times @@ -153,7 +153,7 @@ int do_upload(struct sftp_conn *, const char *, const char *, int, int, int); * times if 'pflag' is set */ int upload_dir(struct sftp_conn *, const char *, const char *, int, int, int, - int); + int, int); /* * Download a 'from_path' from the 'from' connection and upload it to @@ -170,7 +170,8 @@ do_crossload(struct sftp_conn *from, struct sftp_conn *to, */ int crossload_dir(struct sftp_conn *from, struct sftp_conn *to, const char *from_path, const char *to_path, - Attrib *dirattrib, int preserve_flag, int print_flag); + Attrib *dirattrib, int preserve_flag, int print_flag, + int follow_link_flag); /* Concatenate paths, taking care of slashes. Caller must free result. */ char *path_append(const char *, const char *); diff --git a/sftp.c b/sftp.c index 3f46c553..95b2e0b7 100644 --- a/sftp.c +++ b/sftp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp.c,v 1.209 2021/04/03 06:58:30 djm Exp $ */ +/* $OpenBSD: sftp.c,v 1.210 2021/08/07 00:12:09 djm Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * @@ -655,10 +655,11 @@ process_get(struct sftp_conn *conn, const char *src, const char *dst, else if (!quiet && !resume) mprintf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); + /* XXX follow link flag */ if (globpath_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL, pflag || global_pflag, 1, resume, - fflag || global_fflag) == -1) + fflag || global_fflag, 0) == -1) err = -1; } else { if (do_download(conn, g.gl_pathv[i], abs_dst, NULL, @@ -748,10 +749,11 @@ process_put(struct sftp_conn *conn, const char *src, const char *dst, else if (!quiet && !resume) mprintf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst); + /* XXX follow_link_flag */ if (globpath_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { if (upload_dir(conn, g.gl_pathv[i], abs_dst, pflag || global_pflag, 1, resume, - fflag || global_fflag) == -1) + fflag || global_fflag, 0) == -1) err = -1; } else { if (do_upload(conn, g.gl_pathv[i], abs_dst, -- cgit v1.2.3