# PST/OST: parse the MS-PST header layout and verify its CRCs - #417
Merged
Merged
Conversation
Hawkynt
force-pushed
the
feat/pst-ost
branch
from
September 30, 2026 13:11
c512b03 to
3a7df4f
Compare
Hawkynt
force-pushed
the
feat/pst-ost
branch
2 times, most recently
from
September 30, 2026 16:11
7b52b69 to
119594d
Compare
Symptom: PST/OST were read-only and surfaced incorrect header metadata for ANSI and Unicode files. Root cause: the parser classified ANSI version 15 as Unicode, used incorrect ROOT pointer offsets, and assumed a 512-byte header for every variant. Fix: parse the specified ANSI/Unicode layouts and expose additional ROOT/header fields; add validated streaming re-emission of one existing PST/OST image so every opaque property and compression/encryption flag remains untouched. New mailboxes from loose files remain unsupported because that requires writing the NDB/LTP model.
Symptom: CI failed every Convert_*__to__Pst pair: the descriptor advertised CanCreate and then refused every input except one existing .pst/.ost file. Checked against real Outlook files, the corrected header parser also rejected every OST, and List() threw on any damaged header. Root cause: - "Creation" was a byte copy of a PST the caller already had. That is not a PST writer, and advertising CanCreate for it misstates what the library can do. - wMagicClient was required to be "SM"; an OST carries "SO". - The header CRCs were read but never checked, and a failed header parse escaped from List(). Fix: - Remove IArchiveCreatable, Create and CreateFromStreams; the descriptor is read-only again, and the README row says what it does. - Accept "SM" and "SO"; verify dwCRCPartial (471 bytes) and, for Unicode, dwCRCFull (516 bytes) with the MS-PST section 5.3 CRC. - List/OpenEntry fall back to FULL.pst plus a metadata.ini with parse_status=partial and the defect; Extract writes what it can and then throws, so an integrity test still reports the damage. - Tests pin the parser against the headers of two real Outlook files (java-libpst's dist-list.pst, wVer 23, and example-2013.ost, wVer 36): version, client version, crypt method, ibFileEof, NBT/BBT offsets and both CRCs. Damage inside each CRC range, truncation, bad version and bad sentinel list without throwing. Sourcing: rung 3, MS-PST HEADER, ROOT and CRC sections. The header bytes of the java-libpst test files (Apache-2.0, notice beside the tests) are used as reference vectors only.
Hawkynt
force-pushed
the
feat/pst-ost
branch
from
September 30, 2026 18:09
f6031b8 to
358a9cb
Compare
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.
What changed
parse_status=partial; Extract writes what it can and then throws, so an integrity test reports the damage.Verification
dotnet test Compression.Tests --filter "FullyQualifiedName~Pst|FullyQualifiedName~Readme|FullyQualifiedName~SupportMatrix"green locally.