iceboundrock / iceboundrock/AutoForge

quarantine_state_file: fall back when hard links are unavailable; document the link+unlink crash window

Open
#30 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
0
Forks
0
Avg merge
8h 56m
Merged PRs (30d)
40

Description

Follow-up from the round-4 review Observations on #28 (issue #11). Neither point loses data today; both concern the availability and tidiness of the run --force recovery path.

1. Hard-link availability

quarantine_state_file() reserves state.json.corrupt-<timestamp>[.n] with os.link(src, dst, follow_symlinks=False) because link fails atomically with EEXIST and never replaces an existing archive. Some filesystems do not support hard links (exFAT/vfat, several FUSE and SMB/CIFS mounts, some container overlays). There run --force currently fails closed with StateError("cannot move corrupted state file ... aside: ...") and the original stays put, so nothing is lost, but the documented recovery path is unavailable and the user has to move the file by hand.

Proposed: on EPERM / ENOTSUP / EOPNOTSUPP from os.link, fall back to an equally no-replace reservation, e.g. os.open(dst, O_CREAT | O_EXCL | O_WRONLY) + copy bytes + fsync + unlink source (for a symlink entry: os.symlink(os.readlink(src), dst), which is also atomic no-replace). Keep the bounded numeric-suffix retry. Add tests that force the fallback by monkeypatching os.link.

2. link + unlink is two steps

Reservation and removal of the source are separate syscalls. A crash between them leaves both state.json (still corrupt) and state.json.corrupt-<ts> with identical content. The next run --force archives the leftover again under a new name, so the outcome is a duplicate copy, never a lost one.

Options: use renameat2(RENAME_NOREPLACE) on Linux (not exposed by the stdlib; would need ctypes and a fallback), or simply document the duplicate-copy possibility in state.py and the README. Documenting is probably enough.

Priority: low. No behaviour change is needed for the #11 contract.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in state.py at quarantine_state_file() and inspect the existing run --force recovery tests. Add coverage that monkeypatches os.link for the listed unsupported errors and verifies the bounded no-replace fallback, including symlinks. Update state.py and the README to describe the link-and-unlink crash window; done means recovery works without hard links and the duplicate-copy possibility is documented.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
cli
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
65/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.