From: Joshua Rogers Date: Wed, 8 Oct 2025 22:03:08 +0000 (+0800) Subject: libssh/sftp: fix resume corruption by avoiding O_APPEND with rresume X-Git-Tag: rc-8_17_0-1~49 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=391e3fbeeccb7311562f60fbd9db964bc5bf3ec7;p=thirdparty%2Fcurl.git libssh/sftp: fix resume corruption by avoiding O_APPEND with rresume 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 --- diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index 78d7986a9e..579eaeaa0d 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -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) {