Skip to content

fix(mft): real overlapped re-open for snapshot devices + snapshot-correct $MFT fallback#578

Merged
githubrobbi merged 1 commit into
mainfrom
fix/mft-snapshot-reopen-paths
Jul 23, 2026
Merged

fix(mft): real overlapped re-open for snapshot devices + snapshot-correct $MFT fallback#578
githubrobbi merged 1 commit into
mainfrom
fix/mft-snapshot-reopen-paths

Conversation

@githubrobbi

Copy link
Copy Markdown
Collaborator

Context

Reported from a live VSS content-ingest run: the in-process MFT read logged IOCP inline read failed … 0x80070057, then Fallback 1 ($MFT file) … Access is denied (os error 5), succeeding only on Fallback 2 (unbuffered volume I/O, 179.9 MB/s). The resilient chain worked and the index was correct — but the first two rungs were broken by construction on VSS snapshot devices.

Bug 1: IOCP could never work first-try on a snapshot device

open_overlapped_handle answered the snapshot-device case (!is_live_letter) with a DuplicateHandle of the original handle. Overlapped-ness is a property of the file object fixed at CreateFileW time — a duplicate of a synchronous handle is never overlapped, so binding it to an IOCP fails with ERROR_INVALID_PARAMETER (0x80070057) every time. The opened_path field's own doc comment already describes this exact trap for the unbuffered path; the overlapped path just never received the same fix.

Fix: non-broker handles now get a real re-open of the exact stored original path (reopen_path, the renamed shared helper) with FILE_FLAG_OVERLAPPED. Broker-adopted handles still duplicate — the broker's handle is opened overlapped, and a non-elevated process cannot re-open the volume. Result: the IOCP fast path works first-try on snapshot devices instead of falling down the chain.

Bug 2 (latent, wrong-device): Fallback 1 targeted the live volume

open_mft_read_handle built {letter}:\$MFT from the drive letter unconditionally — on a snapshot-device handle that opens the live volume's $MFT. In the observed run it failed access-denied (harmless), but a successful open would have silently indexed the live volume while the caller believed it was reading the point-in-time snapshot — the same wrong-device class previously fixed for get_mft_extents and the unbuffered re-open.

Fix: the new mft_reopen_path helper appends $MFT to the stored snapshot device path (\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopyN\$MFT), which is both correct and actually functional on shadow copies. Live-letter and broker-adopted handles keep the classic {letter}:\$MFT form unchanged.

Fallback 2 was already path-correct via opened_path — which is why the observed run produced a valid snapshot index.

Tests

Unit tests pin the path-selection decisions without touching the filesystem: reopen_path (stored snapshot path wins; letter fallback only when no stored path) and mft_reopen_path (snapshot append, live letter, broker cases). Windows-native execution happens in the merge queue as usual.

Gates: full workspace suite (2500), lint-prod, lint-tests, xwin Windows clippy, rustdoc, doc-tests — green; full pre-push bundle passed on push.

…rect $MFT fallback

Reported from a live VSS content-ingest run: the in-process MFT read
logged 'IOCP inline read failed … 0x80070057', then 'Fallback 1 ($MFT
file) … Access is denied', succeeding only on Fallback 2 (unbuffered
volume I/O). The chain worked, but the first two rungs were broken by
construction on snapshot devices:

- open_overlapped_handle answered !is_live_letter with a
  DuplicateHandle of the original (synchronous) handle. Overlapped-ness
  is a property of the file object fixed at CreateFileW time, so the
  duplicate was never overlapped and IOCP reads on it always failed
  with ERROR_INVALID_PARAMETER — the exact trap opened_path's own doc
  comment describes for the unbuffered path. Snapshot (and live) opens
  now RE-OPEN the exact stored original path with FILE_FLAG_OVERLAPPED;
  only broker-adopted handles still duplicate (the broker's handle is
  opened overlapped, and a non-elevated process cannot re-open).
  IOCP now works first-try on VSS snapshot devices.

- open_mft_read_handle built '{letter}:\$MFT' unconditionally — on a
  snapshot-device handle that path opens the LIVE volume's $MFT. It
  happened to fail access-denied in the observed run, but a successful
  open would have silently indexed the wrong volume (the same
  wrong-device class fixed earlier for get_mft_extents and the
  unbuffered re-open). Fallback 1 now appends $MFT to the stored
  snapshot device path ('\\?\GLOBALROOT\…\$MFT'), which is both
  correct and actually functional on shadow copies.

unbuffered_reopen_path is renamed reopen_path (now shared by the
unbuffered and overlapped re-opens); the new mft_reopen_path helper is
unit-tested for the snapshot-append, live-letter, and broker cases.
@githubrobbi
githubrobbi added this pull request to the merge queue Jul 23, 2026
Merged via the queue into main with commit c156590 Jul 23, 2026
21 checks passed
@githubrobbi
githubrobbi deleted the fix/mft-snapshot-reopen-paths branch July 23, 2026 06:04
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.

1 participant