Fix Windows device size truncation and backup boot sector mismatch - #1679
Merged
tdewey-rpi merged 2 commits intoAug 5, 2026
Merged
Conversation
GetSize() falls back to IOCTL_DISK_GET_DRIVE_GEOMETRY for devices and multiplies out the reported CHS geometry. That geometry is synthetic and its product is rounded down to a whole cylinder, so the result under-reports the device. Measured on a 28.7 GB USB stick: the device is 60125184 sectors, but geometry reports 3742 cylinders * 255 heads * 63 sectors = 60115230 sectors -- 9954 sectors, or 4.86 MiB, short. Anything positioned relative to the end of the device is then placed inside that gap instead of at the true end, and callers cannot tell because the size is returned as a success. IOCTL_DISK_GET_LENGTH_INFO returns the exact byte length. Geometry is kept as a fallback and now logs when it is used.
WriteBootSector() derived hidden_sectors from offset_sectors, which is where
the copy is being written rather than where the volume starts. The backup copy
at partition_start + 6 therefore recorded a value 6 higher than the primary.
fsck.fat reports this on every check:
There are differences between boot sector and its backup.
Differences: (offset:original/backup)
28:00/06
Offset 28 is BPB_HiddSec, and 00/06 is the low byte of 8192 vs 8198 for a
partition starting at sector 8192.
Pass the partition start separately from the write offset so both copies agree.
Collaborator
|
An excellent first contribution, @elibosley:
I'd be happy to review any further patches you care to submit. |
Contributor
Author
|
Thanks @tdewey-rpi! Really appreciate the feedback. I will have a couple more for you here shortly, we've been using this tool for a long time for Unraid (github.com/unraid/usb-creator-next/) and I'm finally getting around to upstreaming our work :) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two independent bugs in the format path, both found while investigating why a USB stick carrying residue from another OS could still be seen by Windows as GPT after a fresh MBR had been written. They are unrelated to each other and can be split into separate PRs if you would prefer — happy to do that.
1.
GetSize()under-reports devices on WindowsWindowsFileOperations::GetSize()falls back toIOCTL_DISK_GET_DRIVE_GEOMETRYfor devices and multiplies out the reported CHS geometry. That geometry is synthetic, and its product is rounded down to a whole cylinder, so the size comes back short.Measured on a 28.7 GB SanDisk USB stick:
The reported figure is exactly
3742 cylinders × 255 heads × 63 sectors, i.e. the true size truncated to the last whole cylinder.The failure mode is quiet: the size is returned as a success, so callers have no way to know it is wrong. Anything positioned relative to the end of the device lands ~5 MiB early, inside the gap, rather than at the true end — which is what led me here, since a structure in the device's final sector was never being overwritten.
IOCTL_DISK_GET_LENGTH_INFOreturns the exact byte length and is the documented call for this. This PR tries it first and keeps the geometry path as a fallback, which now logs when it is used.Worth flagging for review: this changes the device size used for MBR and FAT32 sizing on every Windows write, not only for end-relative work. On affected devices the partition will now extend to the true end rather than stopping at a cylinder boundary. I believe that is the intended behaviour, but it is a real change to the format path and you may want to weigh it.
2. Backup boot sector records a different
BPB_HiddSecthan the primaryWriteBootSector()deriveshidden_sectorsfromoffset_sectors, which is where that copy is being written rather than where the volume starts. The backup copy atpartition_start + 6therefore records a value 6 higher than the primary.fsck.fatreports it on every check of a card formatted by the tool:Offset 28 is
BPB_HiddSec;00/06is the low byte of 8192 vs 8198 for a partition starting at sector 8192.It is cosmetic —
fsckrepairs it in place — but it prompts the user on each boot of a system that fsck's its boot partition. Fixed by passing the partition start separately from the write offset so both copies agree.Testing
Both changes compile and the resulting builds run. Fix 2 was observed directly: the
fsck.fatoutput above came from booting a stick written by the tool, and the arithmetic matches the two values exactly. Fix 1 was diagnosed from instrumented logging on a real device, with the numbers in the table above.To be straightforward about the limits of my testing: I verified that the bugs are real and precisely measured, but I have not demonstrated a user-visible symptom being cured end-to-end by fix 1 — the drive I measured it on wrote successfully despite the truncated size. So I would treat the size correction as fixing a demonstrated defect rather than as a confirmed fix for any particular report.