From: Joshua Rogers Date: Wed, 8 Oct 2025 22:06:40 +0000 (+0800) Subject: libssh2/sftp: fix resume corruption by avoiding O_APPEND with rresume X-Git-Tag: rc-8_17_0-1~48 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=dae19dd94a77dfc4568989cd84159b9502484b8b;p=thirdparty%2Fcurl.git libssh2/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/libssh2.c b/lib/vssh/libssh2.c index c73ecec949..65ccd29923 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -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) {