Files
libfreemkv/CLAUDE.md
T

64 lines
4.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# libfreemkv — Rules
## No English in library code
The library contains ZERO user-facing English text. All errors use numeric codes from `error.rs`. Applications (CLI, GUI, server) handle i18n.
- `io::Error::new(kind, "english string")` — NEVER. Use `Error::VariantName.into()`.
- If you need a new error, add a variant to `error.rs` with a code, not a string.
- Acceptable strings: debug/trace logging, test assertions, comments, data format strings (paths, codec IDs).
- `Error` implements `From<Error> for io::Error` — use `?` or `.into()` anywhere an `io::Error` is expected.
## Architecture
- **Streams are PES.** Every stream reads its format → PES frames out, or PES frames in → writes its format. One type per format.
- **Disc::copy() for sector dumps.** disc→ISO is NOT a stream. It's `Disc::copy()`.
- **DiscStream = any disc.** Physical drive or ISO file. Same type, different SectorReader.
- **No IOStream.** Deleted. No byte-level Read/Write on streams.
- **Streams don't know their size.** Progress/file_size is a CLI concern.
- **One method per action.** No `foo_with_X` variants. Use `Option<T>` params.
- **Streams impl Read only (conceptually).** No Seek, no File backing.
- **Functions return errors, only main() exits.** No `process::exit` in library code.
## Device rules
- Always use `/dev/sg*` not `/dev/sr*` for SCSI.
- `--raw` only skips decryption. Init/probe/speed still run.
- Each function does one thing. One runner orchestrates the sequence.
## macOS IOKit transport
The macOS SCSI transport uses exclusive IOKit access, not hybrid MMC+pread.
- **C shim** (`src/scsi/macos_shim.c`): `diskutil unmountDisk force` → find IOBDServices via `IOServiceMatching` → MMCDeviceInterface → SCSITaskDeviceInterface → `ObtainExclusiveAccess` → raw CDB dispatch via `CreateSCSITask` + `ExecuteTaskSync`.
- **Build** (`build.rs`): compiles shim via `cc` into static lib, linked by Cargo. NOT the `cc` crate (produces object code that breaks IOKit exclusive access).
- **Rust** (`src/scsi/macos.rs`): three FFI calls (`shim_open_exclusive`, `shim_close`, `shim_execute`). All CDBs go through single path — 1:1 with Linux SG_IO.
- **Key**: must find IOBDServices directly (not walk up from IOMedia). Must unmount before exclusive access. Must release service immediately after creating plugin.
- **IOBDServices parent chain**: IOMedia → IOBDBlockStorageDriver → IOBDServices → IOSCSIPeripheralDeviceType05. The block storage driver holds exclusive unless unmounted.
- **Test disc**: DUNE_PART_TWO UHD, `/dev/disk6`, 41288704 sectors.
## Bad-sector handling (BU40N + Initio INIC-1618L)
Three failure modes on this USB bridge:
1. **NOT READY** (sense_key=2, ASC=0x04, ASCQ=0x3E) — most common on BU40N for bad sectors. Pause 3s, retry up to 3x, then mark NonTrimmed.
2. **Transport failure** (status=0xFF) — bridge crash, auto-recovers ~15s. Aborts copy.
3. **INCOMPATIBLE FORMAT** (ASC=0x30) wedge — ALL sectors fail, requires power cycle.
### Damage-jump algorithm (Pass 1 sweep)
When `skip_on_error=true` (multipass mode):
- Read each ECC block sequentially. Track a sliding window of the last 50 ECC block results.
- On error: zero-fill, mark NonTrimmed, push `false` to window.
- On success: write data, mark Finished, push `true` to window. Track consecutive good count.
- When ≥25% of the 50-block window are failures → **jump** ahead by `256×batch×multiplier` sectors (8 MB base). Zero-fill the gap as NonTrimmed. Double the multiplier (8→16→32→64 MB...).
- When 50 consecutive good reads → reset multiplier to 1.
- Only transport failures (bridge crash) abort the pass.
Two tuning knobs: `DAMAGE_WINDOW` (50 ECC blocks) and `DAMAGE_THRESHOLD_PCT` (25%). If the jump is too aggressive or too timid, adjust these.
## Public repo rules
- **No internal docs.** Audit reports, test plans, roadmaps, TODOs go in freemkv-private, never here.
- **No Co-Authored-By** in commit messages. One contributor: MattJackson.
- **No private references.** No Gitea URLs, no /data/code paths, no internal IPs in code.