[openssh-commits] [openssh] 02/02: upstream: add a sftp client "cp" command that supports server-side

git+noreply at mindrot.org git+noreply at mindrot.org
Thu Mar 31 14:09:49 AEDT 2022


This is an automated email from the git hooks/post-receive script.

djm pushed a commit to branch master
in repository openssh.

commit 3fa539c3ffaabd6211995512d33e29150f88c5c5
Author: djm at openbsd.org <djm at openbsd.org>
Date:   Thu Mar 31 03:07:03 2022 +0000

    upstream: add a sftp client "cp" command that supports server-side
    
    copying of files. Useful for this task and for testing the copy-data
    extension. Patch from Mike Frysinger; ok dtucker@
    
    OpenBSD-Commit-ID: 1bb1b950af0d49f0d5425b1f267e197aa1b57444
---
 sftp-client.c | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 sftp-client.h |   5 ++-
 sftp.1        |  18 +++++++--
 sftp.c        |  16 +++++++-
 4 files changed, 155 insertions(+), 6 deletions(-)

diff --git a/sftp-client.c b/sftp-client.c
index c7565755..1b8ce6d7 100644
--- a/sftp-client.c
+++ b/sftp-client.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sftp-client.c,v 1.161 2022/01/17 21:41:04 djm Exp $ */
+/* $OpenBSD: sftp-client.c,v 1.162 2022/03/31 03:07:03 djm Exp $ */
 /*
  * Copyright (c) 2001-2004 Damien Miller <djm at openbsd.org>
  *
@@ -103,6 +103,7 @@ struct sftp_conn {
 #define SFTP_EXT_LSETSTAT	0x00000020
 #define SFTP_EXT_LIMITS		0x00000040
 #define SFTP_EXT_PATH_EXPAND	0x00000080
+#define SFTP_EXT_COPY_DATA	0x00000100
 	u_int exts;
 	u_int64_t limit_kbps;
 	struct bwlimit bwlimit_in, bwlimit_out;
@@ -534,6 +535,10 @@ do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests,
 		    strcmp((char *)value, "1") == 0) {
 			ret->exts |= SFTP_EXT_PATH_EXPAND;
 			known = 1;
+		} else if (strcmp(name, "copy-data") == 0 &&
+		    strcmp((char *)value, "1") == 0) {
+			ret->exts |= SFTP_EXT_COPY_DATA;
+			known = 1;
 		}
 		if (known) {
 			debug2("Server supports extension \"%s\" revision %s",
@@ -1078,6 +1083,121 @@ do_expand_path(struct sftp_conn *conn, const char *path)
 	return do_realpath_expand(conn, path, 1);
 }
 
+int
+do_copy(struct sftp_conn *conn, const char *oldpath, const char *newpath)
+{
+	Attrib junk, *a;
+	struct sshbuf *msg;
+	u_char *old_handle, *new_handle;
+	u_int mode, status, id;
+	size_t old_handle_len, new_handle_len;
+	int r;
+
+	/* Return if the extension is not supported */
+	if ((conn->exts & SFTP_EXT_COPY_DATA) == 0) {
+		error("Server does not support copy-data extension");
+		return -1;
+	}
+
+	/* Make sure the file exists, and we can copy its perms */
+	if ((a = do_stat(conn, oldpath, 0)) == NULL)
+		return -1;
+
+	/* Do not preserve set[ug]id here, as we do not preserve ownership */
+	if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
+		mode = a->perm & 0777;
+
+		if (!S_ISREG(a->perm)) {
+			error("Cannot copy non-regular file: %s", oldpath);
+			return -1;
+		}
+	} else {
+		/* NB: The user's umask will apply to this */
+		mode = 0666;
+	}
+
+	/* Set up the new perms for the new file */
+	attrib_clear(a);
+	a->perm = mode;
+	a->flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
+
+	if ((msg = sshbuf_new()) == NULL)
+		fatal("%s: sshbuf_new failed", __func__);
+
+	attrib_clear(&junk); /* Send empty attributes */
+
+	/* Open the old file for reading */
+	id = conn->msg_id++;
+	if ((r = sshbuf_put_u8(msg, SSH2_FXP_OPEN)) != 0 ||
+	    (r = sshbuf_put_u32(msg, id)) != 0 ||
+	    (r = sshbuf_put_cstring(msg, oldpath)) != 0 ||
+	    (r = sshbuf_put_u32(msg, SSH2_FXF_READ)) != 0 ||
+	    (r = encode_attrib(msg, &junk)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+	send_msg(conn, msg);
+	debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, oldpath);
+
+	sshbuf_reset(msg);
+
+	old_handle = get_handle(conn, id, &old_handle_len,
+	    "remote open(\"%s\")", oldpath);
+	if (old_handle == NULL) {
+		sshbuf_free(msg);
+		return -1;
+	}
+
+	/* Open the new file for writing */
+	id = conn->msg_id++;
+	if ((r = sshbuf_put_u8(msg, SSH2_FXP_OPEN)) != 0 ||
+	    (r = sshbuf_put_u32(msg, id)) != 0 ||
+	    (r = sshbuf_put_cstring(msg, newpath)) != 0 ||
+	    (r = sshbuf_put_u32(msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|
+	    SSH2_FXF_TRUNC)) != 0 ||
+	    (r = encode_attrib(msg, a)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+	send_msg(conn, msg);
+	debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, newpath);
+
+	sshbuf_reset(msg);
+
+	new_handle = get_handle(conn, id, &new_handle_len,
+	    "remote open(\"%s\")", newpath);
+	if (new_handle == NULL) {
+		sshbuf_free(msg);
+		free(old_handle);
+		return -1;
+	}
+
+	/* Copy the file data */
+	id = conn->msg_id++;
+	if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
+	    (r = sshbuf_put_u32(msg, id)) != 0 ||
+	    (r = sshbuf_put_cstring(msg, "copy-data")) != 0 ||
+	    (r = sshbuf_put_string(msg, old_handle, old_handle_len)) != 0 ||
+	    (r = sshbuf_put_u64(msg, 0)) != 0 ||
+	    (r = sshbuf_put_u64(msg, 0)) != 0 ||
+	    (r = sshbuf_put_string(msg, new_handle, new_handle_len)) != 0 ||
+	    (r = sshbuf_put_u64(msg, 0)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+	send_msg(conn, msg);
+	debug3("Sent message copy-data \"%s\" 0 0 -> \"%s\" 0",
+	       oldpath, newpath);
+
+	status = get_status(conn, id);
+	if (status != SSH2_FX_OK)
+		error("Couldn't copy file \"%s\" to \"%s\": %s", oldpath,
+		    newpath, fx2txt(status));
+
+	/* Clean up everything */
+	sshbuf_free(msg);
+	do_close(conn, old_handle, old_handle_len);
+	do_close(conn, new_handle, new_handle_len);
+	free(old_handle);
+	free(new_handle);
+
+	return status == SSH2_FX_OK ? 0 : -1;
+}
+
 int
 do_rename(struct sftp_conn *conn, const char *oldpath, const char *newpath,
     int force_legacy)
diff --git a/sftp-client.h b/sftp-client.h
index 8851b23b..282a4c70 100644
--- a/sftp-client.h
+++ b/sftp-client.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: sftp-client.h,v 1.35 2022/01/01 01:55:30 jsg Exp $ */
+/* $OpenBSD: sftp-client.h,v 1.36 2022/03/31 03:07:03 djm Exp $ */
 
 /*
  * Copyright (c) 2001-2004 Damien Miller <djm at openbsd.org>
@@ -125,6 +125,9 @@ int do_statvfs(struct sftp_conn *, const char *, struct sftp_statvfs *, int);
 /* Rename 'oldpath' to 'newpath' */
 int do_rename(struct sftp_conn *, const char *, const char *, int);
 
+/* Copy 'oldpath' to 'newpath' */
+int do_copy(struct sftp_conn *, const char *, const char *);
+
 /* Link 'oldpath' to 'newpath' */
 int do_hardlink(struct sftp_conn *, const char *, const char *);
 
diff --git a/sftp.1 b/sftp.1
index 7eebeeac..766adcee 100644
--- a/sftp.1
+++ b/sftp.1
@@ -1,4 +1,4 @@
-.\" $OpenBSD: sftp.1,v 1.138 2021/07/02 05:11:21 dtucker Exp $
+.\" $OpenBSD: sftp.1,v 1.139 2022/03/31 03:07:03 djm Exp $
 .\"
 .\" Copyright (c) 2001 Damien Miller.  All rights reserved.
 .\"
@@ -22,7 +22,7 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd $Mdocdate: July 2 2021 $
+.Dd $Mdocdate: March 31 2022 $
 .Dt SFTP 1
 .Os
 .Sh NAME
@@ -144,7 +144,7 @@ will abort if any of the following
 commands fail:
 .Ic get , put , reget , reput , rename , ln ,
 .Ic rm , mkdir , chdir , ls ,
-.Ic lchdir , chmod , chown ,
+.Ic lchdir , copy , cp , chmod , chown ,
 .Ic chgrp , lpwd , df , symlink ,
 and
 .Ic lmkdir .
@@ -400,6 +400,18 @@ If the
 flag is specified, then symlinks will not be followed.
 Note that this is only supported by servers that implement
 the "lsetstat at openssh.com" extension.
+.It Ic copy Ar oldpath Ar newpath
+Copy remote file from
+.Ar oldpath
+to
+.Ar newpath .
+.Pp
+Note that this is only supported by servers that implement the "copy-data"
+extension.
+.It Ic cp Ar oldpath Ar newpath
+Alias to
+.Ic copy
+command.
 .It Xo Ic df
 .Op Fl hi
 .Op Ar path
diff --git a/sftp.c b/sftp.c
index 01e0afef..4efc025a 100644
--- a/sftp.c
+++ b/sftp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sftp.c,v 1.213 2022/03/18 02:50:21 djm Exp $ */
+/* $OpenBSD: sftp.c,v 1.214 2022/03/31 03:07:03 djm Exp $ */
 /*
  * Copyright (c) 2001-2004 Damien Miller <djm at openbsd.org>
  *
@@ -137,6 +137,7 @@ enum sftp_command {
 	I_CHGRP,
 	I_CHMOD,
 	I_CHOWN,
+	I_COPY,
 	I_DF,
 	I_GET,
 	I_HELP,
@@ -180,6 +181,8 @@ static const struct CMD cmds[] = {
 	{ "chgrp",	I_CHGRP,	REMOTE	},
 	{ "chmod",	I_CHMOD,	REMOTE	},
 	{ "chown",	I_CHOWN,	REMOTE	},
+	{ "copy",	I_COPY,		REMOTE	},
+	{ "cp",		I_COPY,		REMOTE	},
 	{ "df",		I_DF,		REMOTE	},
 	{ "dir",	I_LS,		REMOTE	},
 	{ "exit",	I_QUIT,		NOARGS	},
@@ -286,6 +289,8 @@ help(void)
 	    "chgrp [-h] grp path                Change group of file 'path' to 'grp'\n"
 	    "chmod [-h] mode path               Change permissions of file 'path' to 'mode'\n"
 	    "chown [-h] own path                Change owner of file 'path' to 'own'\n"
+	    "copy oldpath newpath               Copy remote file\n"
+	    "cp oldpath newpath                 Copy remote file\n"
 	    "df [-hi] [path]                    Display statistics for current directory or\n"
 	    "                                   filesystem containing 'path'\n"
 	    "exit                               Quit sftp\n"
@@ -1369,6 +1374,10 @@ parse_args(const char **cpp, int *ignore_errors, int *disable_echo, int *aflag,
 		if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
 			return -1;
 		goto parse_two_paths;
+	case I_COPY:
+		if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
+			return -1;
+		goto parse_two_paths;
 	case I_RENAME:
 		if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1)
 			return -1;
@@ -1536,6 +1545,11 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
 		err = process_put(conn, path1, path2, *pwd, pflag,
 		    rflag, aflag, fflag);
 		break;
+	case I_COPY:
+		path1 = make_absolute(path1, *pwd);
+		path2 = make_absolute(path2, *pwd);
+		err = do_copy(conn, path1, path2);
+		break;
 	case I_RENAME:
 		path1 = make_absolute(path1, *pwd);
 		path2 = make_absolute(path2, *pwd);

-- 
To stop receiving notification emails like this one, please contact
djm at mindrot.org.


More information about the openssh-commits mailing list