[openssh-commits] [openssh] 01/02: upstream: add support for the "corp-data" protocol extension to

git+noreply at mindrot.org git+noreply at mindrot.org
Thu Mar 31 14:09:48 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 7988bfc4b701c4b3fe9b36c8561a3d1c5d4c9a74
Author: djm at openbsd.org <djm at openbsd.org>
Date:   Thu Mar 31 03:05:49 2022 +0000

    upstream: add support for the "corp-data" protocol extension to
    
    allow server-side copies to be performed without having to go via the client.
    Patch by Mike Frysinger, ok dtucker@
    
    OpenBSD-Commit-ID: 00aa510940fedd66dab1843b58682de4eb7156d5
---
 PROTOCOL      | 41 ++++++++++++++++++++++++--
 sftp-server.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 132 insertions(+), 3 deletions(-)

diff --git a/PROTOCOL b/PROTOCOL
index e6a7d60e..2d50b5cb 100644
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -492,7 +492,7 @@ This request asks the server to call fsync(2) on an open file handle.
 	string		"fsync at openssh.com"
 	string		handle
 
-One receiving this request, a server will call fsync(handle_fd) and will
+On receiving this request, a server will call fsync(handle_fd) and will
 respond with a SSH_FXP_STATUS message.
 
 This extension is advertised in the SSH_FXP_VERSION hello with version
@@ -576,6 +576,43 @@ Its reply is the same format as that of SSH2_FXP_REALPATH.
 This extension is advertised in the SSH_FXP_VERSION hello with version
 "1".
 
+4.10. sftp: Extension request "copy-data"
+
+This request asks the server to copy data from one open file handle and
+write it to a different open file handle.  This avoids needing to transfer
+the data across the network twice (a download followed by an upload).
+
+	byte		SSH_FXP_EXTENDED
+	uint32		id
+	string		"copy-data"
+	string		read-from-handle
+	uint64		read-from-offset
+	uint64		read-data-length
+	string		write-to-handle
+	uint64		write-to-offset
+
+The server will copy read-data-length bytes starting from
+read-from-offset from the read-from-handle and write them to
+write-to-handle starting from write-to-offset, and then respond with a
+SSH_FXP_STATUS message.
+
+It's equivalent to issuing a series of SSH_FXP_READ requests on
+read-from-handle and a series of requests of SSH_FXP_WRITE on
+write-to-handle.
+
+If read-from-handle and write-to-handle are the same, the server will
+fail the request and respond with a SSH_FX_INVALID_PARAMETER message.
+
+If read-data-length is 0, then the server will read data from the
+read-from-handle until EOF is reached.
+
+This extension is advertised in the SSH_FXP_VERSION hello with version
+"1".
+
+This request is identical to the "copy-data" request documented in:
+
+https://tools.ietf.org/html/draft-ietf-secsh-filexfer-extensions-00#section-7
+
 5. Miscellaneous changes
 
 5.1 Public key format
@@ -612,4 +649,4 @@ master instance and later clients.
 OpenSSH extends the usual agent protocol. These changes are documented
 in the PROTOCOL.agent file.
 
-$OpenBSD: PROTOCOL,v 1.43 2021/12/19 22:15:42 djm Exp $
+$OpenBSD: PROTOCOL,v 1.44 2022/03/31 03:05:49 djm Exp $
diff --git a/sftp-server.c b/sftp-server.c
index d4c6a3b4..3dd19d4c 100644
--- a/sftp-server.c
+++ b/sftp-server.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sftp-server.c,v 1.139 2022/02/01 23:32:51 djm Exp $ */
+/* $OpenBSD: sftp-server.c,v 1.140 2022/03/31 03:05:49 djm Exp $ */
 /*
  * Copyright (c) 2000-2004 Markus Friedl.  All rights reserved.
  *
@@ -44,6 +44,7 @@
 #include <unistd.h>
 #include <stdarg.h>
 
+#include "atomicio.h"
 #include "xmalloc.h"
 #include "sshbuf.h"
 #include "ssherr.h"
@@ -119,6 +120,7 @@ static void process_extended_fsync(u_int32_t id);
 static void process_extended_lsetstat(u_int32_t id);
 static void process_extended_limits(u_int32_t id);
 static void process_extended_expand(u_int32_t id);
+static void process_extended_copy_data(u_int32_t id);
 static void process_extended(u_int32_t id);
 
 struct sftp_handler {
@@ -164,6 +166,7 @@ static const struct sftp_handler extended_handlers[] = {
 	{ "limits", "limits at openssh.com", 0, process_extended_limits, 0 },
 	{ "expand-path", "expand-path at openssh.com", 0,
 	    process_extended_expand, 0 },
+	{ "copy-data", "copy-data", 0, process_extended_copy_data, 1 },
 	{ NULL, NULL, 0, NULL, 0 }
 };
 
@@ -720,6 +723,7 @@ process_init(void)
 	compose_extension(msg, "lsetstat at openssh.com", "1");
 	compose_extension(msg, "limits at openssh.com", "1");
 	compose_extension(msg, "expand-path at openssh.com", "1");
+	compose_extension(msg, "copy-data", "1");
 
 	send_msg(msg);
 	sshbuf_free(msg);
@@ -1592,6 +1596,94 @@ process_extended_expand(u_int32_t id)
 	free(path);
 }
 
+static void
+process_extended_copy_data(u_int32_t id)
+{
+	u_char buf[64*1024];
+	int read_handle, read_fd, write_handle, write_fd;
+	u_int64_t len, read_off, read_len, write_off;
+	int r, copy_until_eof, status = SSH2_FX_OP_UNSUPPORTED;
+	size_t ret;
+
+	if ((r = get_handle(iqueue, &read_handle)) != 0 ||
+	    (r = sshbuf_get_u64(iqueue, &read_off)) != 0 ||
+	    (r = sshbuf_get_u64(iqueue, &read_len)) != 0 ||
+	    (r = get_handle(iqueue, &write_handle)) != 0 ||
+	    (r = sshbuf_get_u64(iqueue, &write_off)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+
+	debug("request %u: copy-data from \"%s\" (handle %d) off %llu len %llu "
+	    "to \"%s\" (handle %d) off %llu",
+	    id, handle_to_name(read_handle), read_handle,
+	    (unsigned long long)read_off, (unsigned long long)read_len,
+	    handle_to_name(write_handle), write_handle,
+	    (unsigned long long)write_off);
+
+	/* For read length of 0, we read until EOF. */
+	if (read_len == 0) {
+		read_len = (u_int64_t)-1 - read_off;
+		copy_until_eof = 1;
+	} else
+		copy_until_eof = 0;
+
+	read_fd = handle_to_fd(read_handle);
+	write_fd = handle_to_fd(write_handle);
+
+	/* Disallow reading & writing to the same handle or same path or dirs */
+	if (read_handle == write_handle || read_fd < 0 || write_fd < 0 ||
+	    !strcmp(handle_to_name(read_handle), handle_to_name(write_handle))) {
+		status = SSH2_FX_FAILURE;
+		goto out;
+	}
+
+	if (lseek(read_fd, read_off, SEEK_SET) < 0) {
+		status = errno_to_portable(errno);
+		error("%s: read_seek failed", __func__);
+		goto out;
+	}
+
+	if ((handle_to_flags(write_handle) & O_APPEND) == 0 &&
+	    lseek(write_fd, write_off, SEEK_SET) < 0) {
+		status = errno_to_portable(errno);
+		error("%s: write_seek failed", __func__);
+		goto out;
+	}
+
+	/* Process the request in chunks. */
+	while (read_len > 0 || copy_until_eof) {
+		len = MINIMUM(sizeof(buf), read_len);
+		read_len -= len;
+
+		ret = atomicio(read, read_fd, buf, len);
+		if (ret == 0 && errno == EPIPE) {
+			status = copy_until_eof ? SSH2_FX_OK : SSH2_FX_EOF;
+			break;
+		} else if (ret == 0) {
+			status = errno_to_portable(errno);
+			error("%s: read failed: %s", __func__, strerror(errno));
+			break;
+		}
+		len = ret;
+		handle_update_read(read_handle, len);
+
+		ret = atomicio(vwrite, write_fd, buf, len);
+		if (ret != len) {
+			status = errno_to_portable(errno);
+			error("%s: write failed: %llu != %llu: %s", __func__,
+			    (unsigned long long)ret, (unsigned long long)len,
+			    strerror(errno));
+			break;
+		}
+		handle_update_write(write_handle, len);
+	}
+
+	if (read_len == 0)
+		status = SSH2_FX_OK;
+
+ out:
+	send_status(id, status);
+}
+
 static void
 process_extended(u_int32_t id)
 {

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


More information about the openssh-commits mailing list