]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
libssh2/sftp: fix resume corruption by avoiding O_APPEND with rresume
authorJoshua Rogers <MegaManSec@users.noreply.github.com>
Wed, 8 Oct 2025 22:06:40 +0000 (06:06 +0800)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 9 Oct 2025 06:30:23 +0000 (08:30 +0200)
Opening the remote file with O_APPEND while attempting to resume causes
all writes to be forced to EOF on servers/implementations where O_APPEND
semantics override a prior seek(). As a result, sftp_seek64() is ignored
and the resumed data is appended, duplicating/corrupting the file.

Fix by:
- Using O_WRONLY (without O_APPEND) when resume_from > 0.
- Skipping the seek entirely if remote_append mode is requested.

Closes #18952

lib/vssh/libssh2.c

index c73ecec94960c335bff426591b8838cb76549629..65ccd29923e569da0f6bb889d576780f84caa973 100644 (file)
@@ -1027,15 +1027,21 @@ sftp_upload_init(struct Curl_easy *data,
     }
   }
 
-  if(data->set.remote_append)
-    /* Try to open for append, but create if nonexisting */
-    flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_APPEND;
-  else if(data->state.resume_from > 0)
-    /* If we have restart position then open for append */
-    flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_APPEND;
-  else
+  if(data->set.remote_append) {
+    /* True append mode: create if nonexisting */
+    flags = LIBSSH2_FXF_WRITE | LIBSSH2_FXF_CREAT | LIBSSH2_FXF_APPEND;
+  }
+  else if(data->state.resume_from > 0) {
+    /*
+     * Resume MUST NOT use APPEND; some servers force writes to EOF when
+     * APPEND is set, ignoring a prior seek().
+     */
+    flags = LIBSSH2_FXF_WRITE;
+  }
+  else {
     /* Clear file before writing (normal behavior) */
-    flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC;
+    flags = LIBSSH2_FXF_WRITE | LIBSSH2_FXF_CREAT | LIBSSH2_FXF_TRUNC;
+  }
 
   sshc->sftp_handle =
     libssh2_sftp_open_ex(sshc->sftp_session, sshp->path,
@@ -1093,8 +1099,8 @@ sftp_upload_init(struct Curl_easy *data,
   }
 
   /* If we have a restart point then we need to seek to the correct
-     position. */
-  if(data->state.resume_from > 0) {
+     Skip if in explicit remote append mode. */
+  if(data->state.resume_from > 0 && !data->set.remote_append) {
     int seekerr = CURL_SEEKFUNC_OK;
     /* Let's read off the proper amount of bytes from the input. */
     if(data->set.seek_func) {