fix: file I/O safety for UFO save and binary import#117
Merged
Conversation
This was referenced Jul 2, 2026
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.
Fixes the file-safety blockers from the 2026-07 codebase review, plus follow-ups from adversarial review of the first pass.
User-file safety
FormatBackendError::Binarywith glyph/table context; hmtx lookup hoisted out of the per-glyph loop.UfoRounddeleted; points, component offsets, anchors, guidelines, and advances write raw f64. fontc export is unaffected (it rounds at glyf generation). Exact round-trip test added.Name::new().unwrap()sites replaced withFormatBackendError::UfoNamenaming the offending glyph/anchor/group. No silent sanitizing.remove_dir_all'd the destination before a non-atomic write; any failure destroyed the original. Now: stage the complete UFO in a temp sibling dir, swap, restore on failure (with the preserved path in the error if even the restore fails).features.feawas written before norad's save, which internally deletes the destination — feature source was silently destroyed on every UFO save. Fixed (norad serializes features itself) with a round-trip test.Durability follow-ups
File::open(dir)fails on Windows, so every UFO and .shift save would have errored after succeeding. Unix-only now; NTFS journals metadata.renamex_np(RENAME_SWAP)on macOS,renameat2(RENAME_EXCHANGE)on Linux, eliminating the crash window where no UFO existed at the target path. Runtime fallback to the two-rename dance on filesystems without swap support (ENOTSUP/EINVAL/ENOSYS) and on other platforms; the fallback path keeps direct test coverage.Verification
cargo test --workspace: 299 passed, 0 failed (+11 new tests: fractional round-trip, malformed/truncated fonts, failed-save-preserves-original, invalid names, feature round-trip, nested-layer fsync walk, exchange + fallback swap).Follow-ups tracked separately: versioned backup retention (#116), designspace multi-UFO writer and interop safety (upcoming).
🤖 Generated with Claude Code