64 lines
4.0 KiB
Markdown
64 lines
4.0 KiB
Markdown
# 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.
|