Search code examples
pythonsftpparamiko

Implementing server-side file copy with Paramiko SFTP: Need guidance on custom extensions


I am currently working on a project that involves server-side file copy operations using Paramiko's SFTP on the server side. I am seeking guidance on how to implement server-side file copy operations with Paramiko. Specifically, I want to add a custom extension or hook to Paramiko that supports a "copy-file" operation.

Details:

  • Using Paramiko as a server
  • Paramiko Version: 3.3.0
  • Python Version: 3.9.11
  • Operating System: Windows 11
  • SSH Server: OpenSSH v9.4.0

Observations:

I have explored the Paramiko documentation and examples, but I couldn't find clear guidance on implementing server-side file copy operations. I attempted to modify the extension_pairs in paramiko/sftp.py (line 161) to ["copy-file", "md5,sha1"], but it doesn't seem to be recognized. I suspect this is definitely not be the correct approach, and I'm looking for guidance on the proper way to add a custom extension.

here's what I think the useful part of the log of client side to represent it's not working

debug2: channel_input_status_confirm: type 99 id 0
debug2: subsystem request accepted on channel 0
debug2: Remote version: 3
debug2: Unrecognised server extension "copy-data"
Connected to 127.0.0.1.
debug2: Sending SSH2_FXP_REALPATH "."
debug3: Sent message fd 6 T:16 I:1
debug3: SSH2_FXP_REALPATH . -> /
sftp> cp foo bart
Server does not support copy-data extension

Questions:

  1. How can I add a custom extension or hook to Paramiko to support server-side file copy operations?
  2. Are there existing hooks or methods that I can leverage for this purpose?

Thanks to anyone who can guide me on the right path or provide insights into implementing server-side file copy with Paramiko.

This question is also asked on github issue: https://github.com/paramiko/paramiko/issues/2336


Solution

  • Paramiko SFTPServer class does not seem to have any mechanism to implement custom extensions.

    All you can do is to override the _process method and handle the t == CMD_EXTENDED on your own. But you will have to implement all (both) existing extensions again.

    Something like:

    class MySFTPServer(SFTPServer):
        def _process(self, t, request_number, msg):
            if t == CMD_EXTENDED:
                # Copy of CMD_EXTENDED implementation from SFTPServer._process
                tag = msg.get_text()
                if tag == "check-file":
                    self._check_file(request_number, msg)
                elif tag == "[email protected]":
                    oldpath = msg.get_text()
                    newpath = msg.get_text()
                    self._send_status(
                        request_number, self.server.posix_rename(oldpath, newpath)
                    )
                elif tag == "copy-file":
                    oldpath = msg.get_text()
                    newpath = msg.get_text()
                    # Your copy-file implementation
                    # ... 
                else:
                    self._send_status(request_number, SFTP_OP_UNSUPPORTED)
            else:
                super(SFTPServer, self)._process(t, request_number, msg)
    

    (untested)

    And similarly you need to reimplement _send_server_version to announce copy-file support. What you have tried already, but with wrong syntax. It's not obvious to me, what the right syntax is though. Maybe:

    extension_pairs = [["check-file", "md5,sha1"], ["copy-data", "1"]]
    

    Though note that the OpenSSH sftp client does not support the copy-file extension. It supports the copy-data.