4.0 KiB
4.0 KiB
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. UseError::VariantName.into().- If you need a new error, add a variant to
error.rswith a code, not a string. - Acceptable strings: debug/trace logging, test assertions, comments, data format strings (paths, codec IDs).
ErrorimplementsFrom<Error> for io::Error— use?or.into()anywhere anio::Erroris 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_Xvariants. UseOption<T>params. - Streams impl Read only (conceptually). No Seek, no File backing.
- Functions return errors, only main() exits. No
process::exitin library code.
Device rules
- Always use
/dev/sg*not/dev/sr*for SCSI. --rawonly 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 viaIOServiceMatching→ MMCDeviceInterface → SCSITaskDeviceInterface →ObtainExclusiveAccess→ raw CDB dispatch viaCreateSCSITask+ExecuteTaskSync. - Build (
build.rs): compiles shim viaccinto static lib, linked by Cargo. NOT thecccrate (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:
- 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.
- Transport failure (status=0xFF) — bridge crash, auto-recovers ~15s. Aborts copy.
- 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
falseto window. - On success: write data, mark Finished, push
trueto window. Track consecutive good count. - When ≥25% of the 50-block window are failures → jump ahead by
256×batch×multipliersectors (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.