Repository navigation
Conversation
On exFAT/FAT volumes macOS stores every file's extended attributes (each file Cap writes gets com.apple.provenance) in a visible ._<name> AppleDouble sibling, and creates the destination's sibling the moment a copy creates <name>. Finalization copies the recording into a staging workspace with create_dir/create_new, so the source's ._<name> always collided with the one the kernel had just made: "IO error: File exists (os error 17)" on every finalize and every recovery retry. Skip ._ entries in the recovery copy, the integrity snapshot, the publication stamp, and the preparing projection's inventories. Cap never writes ._ names, so they are filesystem bookkeeping rather than recording data, and the kernel recreates them for the copies. The preparing inventories previously declined every exFAT recording and fell back to ordinary finalization. Fixes CapSoftware#2386
| if !is_apple_double(&entry.file_name()) { | ||
| visit(root, &entry.path(), entries, version)?; | ||
| } |
There was a problem hiding this comment.
This filter changes the saved publication hash for both existing receipt versions. If an older build leaves an interrupted publication containing AppleDouble files—for example, a recording unpacked onto APFS—the new build computes different hashes and fails with “Conflicting recovery publication state; all files retained.” RecoveryLock::acquire then blocks recovery on every retry.
Add a new receipt version for the filtered hash. Preserve the old hash rules when reading versions 1 and 2.
Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/recording/src/recovery.rs
Line: 3150-3152
Comment:
**Older recoveries get stuck**
This filter changes the saved publication hash for both existing receipt versions. If an older build leaves an interrupted publication containing AppleDouble files—for example, a recording unpacked onto APFS—the new build computes different hashes and fails with “Conflicting recovery publication state; all files retained.” `RecoveryLock::acquire` then blocks recovery on every retry.
Add a new receipt version for the filtered hash. Preserve the old hash rules when reading versions 1 and 2.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.There was a problem hiding this comment.
Fixed in d17544b. Publication stamps now carry a V3 version, and only V3 leaves out ._ entries. Receipts that older builds wrote as V1 or V2 are still checked with their original rules, so an interrupted publication from an older build reconciles as before. Covered by only_v3_publication_stamps_leave_out_apple_double_entries and publication_receipts_reconcile_with_apple_double_entries_present (receipt versions 1, 2 and 3).
| assert!(!staged.join("segments/segment-0/._display.mp4").exists()); | ||
| assert!(!staged.join("segments/._segment-0").exists()); |
There was a problem hiding this comment.
These assertions require the destination to have no AppleDouble files, but macOS can create its own companions on exFAT. If the test’s temporary directory is on that volume, a correctly copied file can fail the test.
Check that the source companions were not copied rather than requiring destination companions to be absent. Allow filesystem-created companions and compare the filtered snapshots.
Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/recording/src/recovery.rs
Line: 6256-6257
Comment:
**Correct copies fail the test**
These assertions require the destination to have no AppleDouble files, but macOS can create its own companions on exFAT. If the test’s temporary directory is on that volume, a correctly copied file can fail the test.
Check that the source companions were not copied rather than requiring destination companions to be absent. Allow filesystem-created companions and compare the filtered snapshots.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
There was a problem hiding this comment.
Fixed in d17544b. The test now only checks that the source's AppleDouble bytes weren't copied to the destination, so companions macOS creates itself on an exFAT temp directory don't fail it.
Skipping ._ entries in the publication segments stamp changed the hash for receipts already written as version 1 or 2. An interrupted publication left by an older build on a tree containing AppleDouble files would then fail reconciliation as a conflict and block every later recovery attempt. Write new receipts as version 3, the only stamp version that skips ._ entries, and keep hashing them for versions 1 and 2. Original-media retirement now validates against the receipt's own stamp version instead of assuming version 2. The copy test also no longer requires the destination to have no ._ files: on exFAT the kernel creates its own companions for the copies, so it now checks that the source's companions were not copied.
…Windows File::open on a directory fails with Access denied on Windows, which broke only_v3_publication_stamps_leave_out_apple_double_entries in the Windows sync-tests job. Open it with FILE_FLAG_BACKUP_SEMANTICS and FILE_WRITE_ATTRIBUTES so set_times can restore its mtime.
Fixes #2386
On macOS, a Studio recording saved to an exFAT drive (an SD card or most USB sticks) could not be finalized or recovered. Every attempt failed with "IO error: File exists (os error 17)", and retrying never helped.
Cause
exFAT can't store extended attributes, so macOS keeps them in a visible
._<name>AppleDouble file next to each file. Every file Cap writes has at least one attribute (com.apple.provenance), so every file gets a._companion.Finalization copies the recording into a staging workspace with
create_dir/create_new:<name>in the workspace.._<name>for it.._<name>and fails with EEXIST.The same happens on every attempt. Here is the sequence on an exFAT disk image (macOS 27.0.1), copying with
O_CREAT|O_EXCLinreaddirorder:Fix
Skip
._entries in these places:Cap never writes
._names itself, so these entries are filesystem bookkeeping, not recording data. The kernel recreates them for the copies. Before this change, the preparing inventories also rejected every exFAT recording as having "unrepresented" files and fell back to the slower ordinary finalization.Testing
New tests:
recovery_copies_and_snapshots_skip_apple_double_siblingsandpreparing_projection_ignores_apple_double_siblings. Both fail whenis_apple_doublealways returns false.only_v3_publication_stamps_leave_out_apple_double_entriesfails if every stamp version skips._entries.publication_receipts_reconcile_with_apple_double_entries_presentcovers receipt versions 1–3.APFS:
cargo test -p cap-recording --libpasses 566 tests andcargo test -p cap-recording --test recoverypasses 50.cargo clippy -p cap-recording -- -D warningsis clean.exFAT: a 2 GB disk image, with
TMPDIRpointed at it so the test fixtures live there.--lib recovery: 38 failures onmain, 8 with this branch. EveryFile existsand every "Unrepresented … require ordinary finalization" is gone. The 8 remaining failures are test helpers that list or snapshot whole directories and count the sidecars, for examplefs::read_dir(project).count() == 1.--test recovery: 15 failures onmain, 11 with this branch, and again every remaining one is a helper artifact.mainand now pass includerecover_after_simulated_crash_produces_playable_mp4_with_preserved_durationandrecovery_and_finalize_retain_every_known_track_on_success.legacy_optional_failure_does_not_bypass_retained_display_validationcorruptslist_m4s_segments(..)[0], and on exFAT that entry is the sidecar._segment_000.m4srather than the fragment. Recovery now succeeds, correctly, on media that was never corrupted. Onmainthe test only passed because recovery hit EEXIST first.Hand-tested on macOS 27.0.1 (Apple silicon), recording to the exFAT disk image through
CAP_GPUI_RECORDINGS_DIR:studio finalize: IO error: File exists (os error 17), and the recording was left atNeedsRemux. Cap 0.6.0 then failed to recover that same recording three times, with the same error.GPUI preparing sources available).Successfully recovered recording). Its status changed fromNeedsRemuxtoComplete, and the editor opened it at 6.9 s.Not covered
On the macOS 27 exFAT driver (FSKit), a directory created with mode 0700 reports 0700, so publication's private-ownership checks (
recovery_publication_workspace,read_recovery_publication) pass. I couldn't check older macOS releases. If an older exFAT driver reports 0777 regardless, publication would fail next with "not privately owned".Preserve older publication hashes before merging so interrupted recordings remain recoverable after an upgrade.
Findings
Fix with agent prompt
Summary
The PR skips AppleDouble files when copying recordings, checking snapshots, stamping publication state, and building preparing inventories.
Reviews (1) · Last reviewed commit: "fix(recording): finalize and recover rec..." · Reviewed by Greptile