From a4e9650823896675ddb6c11a88f6362912810874 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Mon, 1 Mar 2021 16:01:20 +0000 Subject: [PATCH] Fix STOR of zero length files to ProFTPd with TLS Before this change, uploading a zero length file via TLS meant that the tls.Handshake function was never called. ProFTPd quite properly refuses to accept an empty TCP connection with no TLS handshake as a zero length upload and gives the error 425 Unable to build data connection: Operation not See: https://forum.rclone.org/t/rclone-ftps-explicit-rclone-touch-empty-files-proftpd-unable-to-build-data-connection-operation-not-permitted/22522 --- ftp.go | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/ftp.go b/ftp.go index 6de8f27..c2d53c7 100644 --- a/ftp.go +++ b/ftp.go @@ -681,9 +681,30 @@ func (c *ServerConn) StorFrom(path string, r io.Reader, offset uint64) error { // the response and we cannot use the connection to send other commands. // So we don't check io.Copy error and we return the error from // ReadResponse so the user can see the real error - _, err = io.Copy(conn, r) - conn.Close() + var n int64 + n, err = io.Copy(conn, r) + // If we wrote no bytes but got no error, make sure we call + // tls.Handshake on the connection as it won't get called + // unless Write() is called. + // + // ProFTP doesn't like this and returns "Unable to build data + // connection: Operation not permitted" when trying to upload + // an empty file without this. + if n == 0 && err == nil { + if do, ok := conn.(interface{ Handshake() error }); ok { + err = do.Handshake() + } + } + + // Use io.Copy or Handshake error in preference to this one + closeErr := conn.Close() + if err == nil { + err = closeErr + } + + // Read the response and use this error in preference to + // previous errors _, _, respErr := c.conn.ReadResponse(StatusClosingDataConnection) if respErr != nil { err = respErr