]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
libssh/sftp: fix resume corruption by avoiding O_APPEND with rresume
authorJoshua Rogers <MegaManSec@users.noreply.github.com>
Wed, 8 Oct 2025 22:03:08 +0000 (06:03 +0800)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 9 Oct 2025 06:30:09 +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/libssh.c

index 78d7986a9ecdfb46b3dbd46f90c511f2caa6d039..579eaeaa0ded962f0c573635e44e75851e5dfe75 100644 (file)
@@ -1154,12 +1154,18 @@ static int myssh_in_UPLOAD_INIT(struct Curl_easy *data,
     }
   }
 
-  if(data->set.remote_append)
-    /* Try to open for append, but create if nonexisting */
-    flags = O_WRONLY|O_CREAT|O_APPEND;
-  else if(data->state.resume_from > 0)
-    /* If we have restart position then open for append */
-    flags = O_WRONLY|O_APPEND;
+  if(data->set.remote_append) {
+    /* True append mode: create if nonexisting */
+    flags = O_WRONLY | O_CREAT | O_APPEND;
+  }
+  else if(data->state.resume_from > 0) {
+    /*
+     * Resume MUST NOT use O_APPEND. Many SFTP servers/impls force all
+     * writes to EOF when O_APPEND is set, ignoring a prior seek().
+     * Open write-only and seek to the resume offset instead.
+     */
+    flags = O_WRONLY;
+  }
   else
     /* Clear file before writing (normal behavior) */
     flags = O_WRONLY|O_CREAT|O_TRUNC;
@@ -1189,8 +1195,8 @@ static int myssh_in_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) {
+     position. 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) {