Skip to content

wolfsftp: keep the local file when resuming a get - #1216

Merged
ejohnstown merged 1 commit into
wolfSSL:masterfrom
yosuke-wolfssl:fix/f_12547
Sep 1, 2026
Merged

wolfsftp: keep the local file when resuming a get#1216
ejohnstown merged 1 commit into
wolfSSL:masterfrom
yosuke-wolfssl:fix/f_12547

Conversation

@yosuke-wolfssl

@yosuke-wolfssl yosuke-wolfssl commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Problem

wolfSSH_SFTP_Get() opened the local destination with CREATE_ALWAYS on every Windows transfer, including a resumed one. The file was truncated to zero before WriteFile() resumed at the saved offset, so the already-downloaded prefix came back as a zero-filled gap — a reget on Windows silently corrupted the file it was meant to finish.

Separately, neither platform checked the saved offset against the destination's real size. On POSIX "ab" appends at the file's current end regardless of gOfst, so a destination that was deleted, replaced, or shortened between the interrupt and the reget got the remainder spliced in at the wrong position. The corruption is not Windows-only: on POSIX a reget onto a missing destination writes the remainder at offset 0, producing a 768-byte file holding src[256..1024].

Fix (src/wolfsftp.c)

  • STATE_GET_LOOKUP_OFFSET stats the destination when the saved offset is nonzero and clears the offset unless the reported size matches it exactly, restarting the transfer from zero. This mirrors STATE_PUT_STAT_REMOTE, added for the upload direction in 11659, and is not #ifdef-guarded — it corrects the POSIX and Harmony append paths too.
  • STATE_GET_LOOKUP_OFFSET also clears the saved offset when the remote size already in state->attrib from STATE_GET_LSTAT is less than or equal to it, so a shortened remote file cannot leave the local copy longer than its source. This mirrors STATE_PUT_LOOKUP_OFFSET.
  • STATE_GET_OPEN_LOCAL passes OPEN_EXISTING when the write offset is nonzero and reserves CREATE_ALWAYS for offset zero.
  • FILE_APPEND_DATA is dropped from the desired access: GENERIC_WRITE already grants it and the write offset comes from the OVERLAPPED. The open now reports INVALID_HANDLE_VALUE as WS_BAD_FILE_E rather than falling through on a stale ret.

Closes f-12547.

Tests

test_wolfSSH_SFTP_GetResume() in tests/api.c, six cases over a 1024-byte source with a 256-byte offset:

Source / destination state resume Expected result
holds 256 bytes unlike the source 1 staged prefix kept, remainder appended
missing 1 full re-download
shorter than the saved offset 1 full re-download
longer than the saved offset 1 full re-download
longer than the source 0 truncated to the source
source shortened below the saved offset 1 full re-download

Staging a prefix that differs from the source is what separates a real resume from a re-download, and the comparison reads one byte past the expected size so a leftover tail fails.

Verification

  • GCC preflight sweep clean across all 6 configs; make check 7 passed / 2 skipped / 0 failed, including scripts/sftp.test and get-put.test.
  • Windows (MSVC, Debug x64): api-test and unit-test both exit 0.
  • Negative controls: without the size check the missing / short / long destination cases each fail independently on Linux; with CREATE_ALWAYS restored the resume case fails on Windows.

Not in this PR

A failed local open jumps to STATE_GET_CLEANUP without closing the remote handle. This is pre-existing on every branch — the sibling if (ret != 0) path does the same — and routing through STATE_GET_CLOSE_REMOTE would overwrite ret and surface WS_CLOSE_FILE_E instead of WS_BAD_FILE_E, so fixing both branches properly belongs in its own change.

@yosuke-wolfssl yosuke-wolfssl self-assigned this Aug 31, 2026
Copilot AI lite review requested due to automatic review settings August 31, 2026 04:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #1216

Scan targets checked: wolfssh-bugs, wolfssh-src

Findings: 1
1 finding(s) posted as inline comments (see file-level comments below)

This review was generated automatically by Fenrir. Reported findings require changes before merge.

Comment thread src/wolfsftp.c
- The Windows local open in wolfSSH_SFTP_Get() passes OPEN_EXISTING
  when the write offset is nonzero and CREATE_ALWAYS otherwise, and
  drops FILE_APPEND_DATA from the desired access. A new
  DWORD creationDisp replaces the block-scoped desiredAccess.
- That open reports INVALID_HANDLE_VALUE as WS_BAD_FILE_E and moves to
  STATE_GET_CLEANUP; the OVERLAPPED offset is set from gOfst on every
  open rather than only when resuming.
- STATE_GET_LOOKUP_OFFSET clears a saved offset when the remote size
  STATE_GET_LSTAT stored in state->attrib is no larger than it, and
  again when the local destination does not hold exactly that many
  bytes. The destination stat overwrites state->attrib.
- tests/api.c adds test_wolfSSH_SFTP_GetResume() and its
  sftpGetToCompletion() helper, six cases over the resume paths, built
  where the hosted file wrappers are available.
- test_wolfSSH_SFTP_PutResume() and test_wolfSSH_SFTP_GetResume() drop
  the WOLFSSH_ZEPHYR k_sleep() block their bodies exclude.
- .gitignore covers every wolfssh_*.tmp the api tests leave behind on
  an aborted run, replacing the known_hosts-only entry.

Issue: F-12547
Comment thread src/wolfsftp.c

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #1216

Scan targets checked: wolfssh-bugs, wolfssh-src

Fenrir result: Approved ✅

No new issues found in the changed files.

Advisory only — this automated result does not count as a GitHub approval.

@wolfSSL-Fenrir-bot
wolfSSL-Fenrir-bot dismissed their stale review August 31, 2026 05:35

Fenrir's latest completed scan found no issues; clearing the prior automated change request.

@ejohnstown
ejohnstown merged commit 5151732 into wolfSSL:master Sep 1, 2026
166 checks passed
@yosuke-wolfssl
yosuke-wolfssl deleted the fix/f_12547 branch September 1, 2026 22:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants