Flush NTFS directory publication after replacing a saved file. Report a failed barrier as uncertain durability instead of successful Save. Flush directory-bundle objects before the manifest, including on same-writer retries, and stop before garbage collection when publication fails. Add regression tests for failed barriers, retained old objects, cached object retries, and directory handles. Keep existing Windows metadata preservation and the separate Unix flush-error policy. Validation: 130 format tests, 657 I/O corpus tests, clippy, layers, wasm, 12 acknowledged-save power cuts, six publication/cleanup cuts, and 1,368 native fault cases with exact archive comparisons. Existing preview-only failure ordering remains tracked by #7548/#7549. Fixes #7858.
4.5 KiB
Windows native-save durability
An acknowledged overwrite must reopen as the complete new document after a Windows/NTFS power loss. Issue #7858 demonstrated that flushing a temporary file before replacement did not establish this guarantee: a fresh Windows boot recovered the previous ZIP or directory bundle after Save returned success.
Publication barrier
The shared atomic writer still writes and flushes a sibling temporary file, preserves the
destination's metadata, and replaces the destination. It now flushes the parent directory
on Windows before acknowledging success. RealFs::sync_dir opens a directory using
FILE_FLAG_BACKUP_SEMANTICS and FILE_ADD_FILE, then calls File::sync_all, which reaches
FlushFileBuffers. This uses ordinary directory permissions and does not open or flush a
volume or require administrative privileges. No unsupported REPLACEFILE_WRITE_THROUGH
flag is used.
Windows references:
- Directory handles
- File access rights
- Flushing cached data and directory structure
- Filesystem-specific behavior: the directory-structure guarantee applies to NTFS.
- ReplaceFileW flags
A publication-flush error occurs after replacement. It preserves the underlying error kind and says that the new data was written but may not survive a crash. It does not claim the old file is unchanged, attempt an unsafe rollback, or return success. Existing callers then retain the document's dirty state and recovery data.
Directory bundles and retries
Every published bundle file receives a Windows directory flush. Before replacing the
manifest, the writer also flushes tiles/ and blobs/, including when it reuses cached
objects. A previous attempt can leave valid objects in place after a failed publication
flush; validating their bytes alone does not establish their durability. These barriers
make a retry using the same writer safe without recompressing every unchanged object.
The manifest's publication barrier completes before obsolete objects are collected. An error stops the save before that cleanup, preserving the objects referenced by the old manifest. Garbage collection remains best effort after a successful commit.
The private save_dir_with seam exercises this complete ordering with injected tile,
blob and manifest publication failures. The tests check the reported error, loaded
document, preservation of old objects, barriers on reused objects, and successful retry.
Verification
Run the format tests and repository checks:
cargo test -p photocraft-format
cargo clippy -p photocraft-format --all-targets -- -D warnings
cargo xtask layers
cargo xtask wasm
cargo xtask test-corpus -p io
Unit tests cannot prove power-loss durability. The VM test uses a disposable NTFS disk and synthetic old/new documents at 8-, 16- and 32-bit depth, in ZIP and directory form. Flush the old baseline during setup, call the production save API, wait for its successful response, then abruptly power off the guest. Preserve and hash the crashed disk before recovery. Boot Windows fresh against those exact bytes and compare the complete loaded document and every expected bundle entry with the new revision. Do not restore a RAM snapshot for recovery. Repeat each combination and retain failing unchanged-build controls.
Keep volume flushes and clean unmounts in setup or diagnostic controls only; they are not part of the production save implementation. Archive comparisons must account for the build's generator-version stamp while still comparing all document payloads exactly.
This change addresses Windows/NTFS publication. The existing Unix flush-error policy is handled separately by #5420. Preview replacement/deletion ordering on failed directory saves remains tracked by #7548 and #7549. VM tests do not establish physical-controller or firmware-cache behavior, or guarantees on other filesystems.