8000 fix(storage): refuse a sub-page file that is not a torn first write (#506) by adityamukho · Pull Request #508 · project-minigraf/minigraf · GitHub
[go: up one dir, main page]

Skip to content

fix(storage): refuse a sub-page file that is not a torn first write (#506) - #508

Merged
adityamukho merged 1 commit into
v3from
fix/506-short-file
Oct 10, 2026
Merged

adityamukho merged 1 commit into
v3from
fix/506-short-file

Conversation

@adityamukho
Copy link
Copy Markdown
Collaborator

Part of #506 (data-integrity; the issue stays open per the known-issues process).

Problem

A file of 1..4095 bytes holds no whole page, so FileBackend reported page_count == 0 and open took the new-database path. A read-write open then wrote a fresh empty database over it: a database cut below one page by an interrupted copy, or an unrelated file at the path, was silently replaced.

Fix (option 2, decided on the issue)

  • StorageBackend::partial_page() returns the bytes past the last whole page (None by default; FileBackend reads them; the mutex and fault-injection wrappers forward it).
  • select_meta with page_count == 0: a tail is accepted as a new database only if it is zeros or a prefix of MetaPage::empty(1).encode(), the same rule is_torn_initial_meta applies to a full page 0 (now shared as is_initial_meta_prefix). Anything else is the new STG-045, read-write and read-only, with the file unchanged. A zero-length file is still a new database.
  • Limitation, by design: a database checkpointed only once still has the untouched gen-1 empty meta in page 0, so if it is cut below one page it matches the prefix rule and opens as new. Every tree lies past the first page, so nothing in the file could be recovered anyway.

Tests

  • tests/integrity_test.rs::sub_page_file_is_refused_unless_a_torn_first_write: cuts to 2222 and 20 bytes and foreign bytes give STG-045 (rw and ro, file unchanged, no WAL); 8- and 40-byte meta prefixes and 300 zeros open, take a write and checkpoint.
  • FileBackend unit test covers partial_page.
  • corrupt_file fuzzer: the len < PAGE_SIZE skip is gone; a short file has to fail to open or open empty. New seeds: the A file shorter than one page opens as a new empty database (read-write re-initializes it) #506 reproducer, and a cut to an 8-byte meta prefix. Ran for 120 s locally, 5,192 runs, no findings.
  • cargo test: 1372 passed, 9 ignored. cargo clippy --all-features -- -D warnings and the wasm32 browser clippy are clean.

Docs: ERROR_REFERENCE.md (STG-045), CHANGELOG.md, TEST_COVERAGE.md, test counts in CLAUDE.md/README.md.

🤖 Generated with Claude Code

https://claude.ai/code/session_012wXxZguv7PgKQjMMH4yJ5h

…506)

A file of 1..4095 bytes held no whole page, so open treated it as a new
database and a read-write open re-initialised it. It now opens as new only
when its bytes are zeros or a prefix of init_empty's generation-1 meta (the
rule is_torn_initial_meta already applied to a full page 0); anything else
is STG-045, read-write and read-only, with the file unchanged.

StorageBackend gains partial_page() (bytes past the last whole page; None by
default, forwarded by the mutex and fault-injection wrappers). The
corrupt_file fuzzer no longer skips short files: they must fail to open or
open empty. The #506 input and a meta-prefix truncation are corpus seeds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012wXxZguv7PgKQjMMH4yJ5h
@codecov
codecov Bot commented Oct 10, 2026
Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 84.00000% with 4 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/storage/btree.rs 0.00% 3 Missing ⚠️
src/storage/backend/file.rs 90.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@adityamukho
adityamukho merged commit 2fc7d16 into v3 Oct 10, 2026
24 checks passed
@adityamukho
adityamukho deleted the fix/506-short-file branch October 10, 2026 05:40
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

0