From 89c55e972f8061d2e405e13908c4aae8a60363ea Mon Sep 17 00:00:00 2001 From: MattJackson <1085847+MattJackson@users.noreply.github.com> Date: Tue, 7 Apr 2026 12:34:10 -0700 Subject: [PATCH] =?UTF-8?q?Remove=20standalone=20binaries=20=E2=80=94=20fu?= =?UTF-8?q?nctionality=20lives=20in=20freemkv=20CLI=20and=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Deleted: - freemkv_info.rs — duplicate of freemkv drive-info CLI command - freemkv_test.rs — duplicate of bdemu capture-disc - aacs_test.rs — covered by inline #[test] functions (31 tests) libfreemkv is a library. CLI tools belong in the freemkv repo. Dev/debug tools belong in freemkv-private. --- Cargo.toml | 11 --- src/bin/aacs_test.rs | 105 -------------------------- src/bin/freemkv_info.rs | 159 ---------------------------------------- src/bin/freemkv_test.rs | 99 ------------------------- 4 files changed, 374 deletions(-) delete mode 100644 src/bin/aacs_test.rs delete mode 100644 src/bin/freemkv_info.rs delete mode 100644 src/bin/freemkv_test.rs diff --git a/Cargo.toml b/Cargo.toml index 8e3545e..e9b0b64 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,14 +25,3 @@ zip = { version = "2", default-features = false, features = ["deflate"] } [target.'cfg(target_os = "linux")'.dependencies] libc = "0.2" -[[bin]] -name = "freemkv-info" -path = "src/bin/freemkv_info.rs" - -[[bin]] -name = "freemkv-test" -path = "src/bin/freemkv_test.rs" - -[[bin]] -name = "aacs-test" -path = "src/bin/aacs_test.rs" diff --git a/src/bin/aacs_test.rs b/src/bin/aacs_test.rs deleted file mode 100644 index df63423..0000000 --- a/src/bin/aacs_test.rs +++ /dev/null @@ -1,105 +0,0 @@ -//! aacs-test — Test AACS handshake against a real drive. -//! -//! Usage: aacs-test /dev/sr0 /path/to/keydb.cfg - -use std::env; -use std::path::Path; - -fn main() { - let args: Vec = env::args().collect(); - if args.len() < 3 { - eprintln!("Usage: aacs-test "); - std::process::exit(1); - } - - let device = Path::new(&args[1]); - let keydb_path = Path::new(&args[2]); - - println!("aacs-test v{}", env!("CARGO_PKG_VERSION")); - println!(); - - // Open drive WITHOUT unlock — AACS auth must happen before raw mode - print!("Opening {} (no unlock)... ", device.display()); - let mut session = match libfreemkv::DriveSession::open_no_unlock(device) { - Ok(s) => { println!("OK"); s } - Err(e) => { println!("FAILED: {}", e); std::process::exit(1); } - }; - println!(" Drive: {} {}", session.profile.drive_id.trim(), session.profile.chipset.name()); - - // Load KEYDB - print!("Loading KEYDB... "); - let keydb = match libfreemkv::aacs::KeyDb::load(keydb_path) { - Ok(db) => { - println!("OK ({} disc entries, {} DK, {} PK)", - db.disc_entries.len(), db.device_keys.len(), db.processing_keys.len()); - db - } - Err(e) => { println!("FAILED: {}", e); std::process::exit(1); } - }; - - let host_cert = match &keydb.host_cert { - Some(hc) => { - println!(" Host cert: {} bytes, priv_key[0]=0x{:02x}", - hc.certificate.len(), hc.private_key[0]); - hc - } - None => { println!(" No host cert in KEYDB"); std::process::exit(1); } - }; - - // AACS handshake - println!(); - print!("AACS authenticate... "); - let mut auth = match libfreemkv::aacs::handshake::aacs_authenticate( - &mut session, - &host_cert.private_key, - &host_cert.certificate, - ) { - Ok(a) => { - println!("OK"); - println!(" Bus key: {:02x?}", &a.bus_key); - println!(" AGID: {}", a.agid); - println!(" Drive cert type: 0x{:02x}", a.drive_cert[0]); - a - } - Err(e) => { - println!("FAILED: {}", e); - std::process::exit(1); - } - }; - - // Read Volume ID - print!("Reading Volume ID... "); - match libfreemkv::aacs::handshake::read_volume_id(&mut session, &mut auth) { - Ok(vid) => { - println!("OK"); - println!(" VID: {:02x?}", vid); - - // Try to find matching disc in KEYDB - let matched = keydb.disc_entries.values() - .find(|e| e.disc_id == Some(vid)); - if let Some(entry) = matched { - println!(" KEYDB match: {} (hash {})", entry.title, entry.disc_hash); - if let Some(vuk) = entry.vuk { - println!(" VUK: {:02x?}", vuk); - } - } else { - println!(" No exact VID match in KEYDB"); - } - } - Err(e) => println!("FAILED: {}", e), - } - - // Read data keys (AACS 2.0) - print!("Reading data keys... "); - match libfreemkv::aacs::handshake::read_data_keys(&mut session, &mut auth) { - Ok((rdk, wdk)) => { - println!("OK (AACS 2.0 bus encryption)"); - println!(" Read data key: {:02x?}", rdk); - println!(" Write data key: {:02x?}", wdk); - } - Err(e) => println!("not available: {} (likely AACS 1.0)", e), - } - - println!(); - println!("Done."); -} diff --git a/src/bin/freemkv_info.rs b/src/bin/freemkv_info.rs deleted file mode 100644 index ed32b99..0000000 --- a/src/bin/freemkv_info.rs +++ /dev/null @@ -1,159 +0,0 @@ -//! freemkv-info — Drive identification and compatibility checker. -//! -//! Sends standard SCSI INQUIRY and GET CONFIGURATION commands to an optical drive, -//! displays drive identity and compatibility status, and optionally outputs raw -//! response data for profile contribution. -//! -//! Usage: -//! freemkv-info /dev/sr0 -//! freemkv-info /dev/sr0 --raw -//! freemkv-info /dev/sr0 --json - -use std::env; -use std::path::Path; -use std::process; - -fn main() { - let args: Vec = env::args().collect(); - - if args.len() < 2 { - eprintln!("freemkv-info — Drive identification and compatibility checker"); - eprintln!(); - eprintln!("Usage: freemkv-info [options]"); - eprintln!(); - eprintln!(" Optical drive device (e.g. /dev/sr0)"); - eprintln!(" --raw Output raw SCSI response hex (for profile contribution)"); - eprintln!(" --json Output machine-readable JSON"); - eprintln!(" --profiles Path to profiles directory (default: ./profiles)"); - eprintln!(); - eprintln!("Examples:"); - eprintln!(" freemkv-info /dev/sr0"); - eprintln!(" freemkv-info /dev/sr0 --raw > my_drive.txt"); - process::exit(1); - } - - let device = Path::new(&args[1]); - let raw_mode = args.iter().any(|a| a == "--raw"); - let json_mode = args.iter().any(|a| a == "--json"); - let profiles_dir = args.iter() - .position(|a| a == "--profiles") - .and_then(|i| args.get(i + 1)) - .map(|s| s.as_str()) - .unwrap_or("profiles"); - - // Open SCSI transport - let mut transport = match libfreemkv::scsi::open(device) { - Ok(t) => t, - Err(e) => { - eprintln!("Error: Cannot open {}: {}", device.display(), e); - process::exit(1); - } - }; - - // INQUIRY - let inquiry = match libfreemkv::scsi::inquiry(transport.as_mut()) { - Ok(i) => i, - Err(e) => { - eprintln!("Error: INQUIRY failed: {}", e); - process::exit(1); - } - }; - - // GET CONFIGURATION feature 0x010C - let gc_010c = libfreemkv::scsi::get_config_010c(transport.as_mut()).ok(); - - if json_mode { - print_json(&inquiry, &gc_010c); - } else if raw_mode { - print_raw(&inquiry, &gc_010c); - } else { - print_human(&inquiry, &gc_010c, profiles_dir); - } -} - -fn print_human( - inquiry: &libfreemkv::scsi::InquiryResult, - gc_010c: &Option>, - profiles_dir: &str, -) { - println!("freemkv-info v{}", env!("CARGO_PKG_VERSION")); - println!(); - println!("Drive: {} {} {}", inquiry.vendor_id, inquiry.model, inquiry.firmware); - println!("INQUIRY: additional_length=0x{:02X} ({})", - inquiry.raw.get(4).unwrap_or(&0), - inquiry.raw.get(4).unwrap_or(&0)); - - if let Some(gc) = gc_010c { - let data_hex: String = gc.iter().map(|b| format!("{:02x}", b)).collect(); - println!("Feature 0x010C: {}", data_hex); - } else { - println!("Feature 0x010C: not available"); - } - - // Try to match profile - if let Ok(profiles) = libfreemkv::profile::load_all(Path::new(profiles_dir)) { - let matched = profiles.iter().find(|p| { - p.drive_id.contains(&inquiry.vendor_id) - && p.drive_id.contains(&inquiry.model) - }); - - println!(); - match matched { - Some(p) => { - println!("Profile: FOUND ({})", p.chipset.name()); - println!("Raw Read: Supported"); - } - None => { - println!("Profile: NOT FOUND"); - println!("Raw Read: Unknown — run with --raw and submit a profile request"); - } - } - } else { - println!(); - println!("Profile: No profiles directory found at '{}'", profiles_dir); - } -} - -fn print_raw( - inquiry: &libfreemkv::scsi::InquiryResult, - gc_010c: &Option>, -) { - println!("# freemkv-info raw output"); - println!("# Submit this file to https://github.com/freemkv/libfreemkv/issues"); - println!(); - println!("vendor: {}", inquiry.vendor_id); - println!("model: {}", inquiry.model); - println!("firmware: {}", inquiry.firmware); - println!(); - - // Full INQUIRY hex - println!("inquiry_hex: {}", hex_encode(&inquiry.raw)); - println!("inquiry_length: {}", inquiry.raw.len()); - - // GET CONFIG 0x010C - if let Some(gc) = gc_010c { - println!("get_config_010c_hex: {}", hex_encode(gc)); - println!("get_config_010c_length: {}", gc.len()); - } else { - println!("get_config_010c_hex: ERROR"); - } -} - -fn print_json( - inquiry: &libfreemkv::scsi::InquiryResult, - gc_010c: &Option>, -) { - let json = serde_json::json!({ - "vendor": inquiry.vendor_id, - "model": inquiry.model, - "firmware": inquiry.firmware, - "inquiry_hex": hex_encode(&inquiry.raw), - "inquiry_length": inquiry.raw.len(), - "get_config_010c_hex": gc_010c.as_ref().map(|g| hex_encode(g)), - }); - println!("{}", serde_json::to_string_pretty(&json).unwrap()); -} - -fn hex_encode(data: &[u8]) -> String { - data.iter().map(|b| format!("{:02x}", b)).collect() -} diff --git a/src/bin/freemkv_test.rs b/src/bin/freemkv_test.rs deleted file mode 100644 index 34cc7c7..0000000 --- a/src/bin/freemkv_test.rs +++ /dev/null @@ -1,99 +0,0 @@ -//! freemkv-test — Quick verification that raw disc access works. -//! -//! Enables raw read mode, calibrates speed, reads a few test sectors. -//! Use this to verify your drive and profile are working correctly. -//! -//! Usage: -//! freemkv-test /dev/sr0 -//! freemkv-test /dev/sr0 --profiles ./profiles - -use std::env; -use std::path::Path; -use std::process; - -fn main() { - let args: Vec = env::args().collect(); - - if args.len() < 2 { - eprintln!("freemkv-test — Verify raw disc access works"); - eprintln!(); - eprintln!("Usage: freemkv-test [--profiles ]"); - process::exit(1); - } - - let device = Path::new(&args[1]); - - println!("freemkv-test v{}", env!("CARGO_PKG_VERSION")); - println!(); - - // Open drive session (uses bundled profiles) - print!("Opening {}... ", device.display()); - let mut session = match libfreemkv::DriveSession::open(device) { - Ok(s) => { println!("OK"); s } - Err(e) => { println!("FAILED: {}", e); process::exit(1); } - }; - - println!(" Drive ID: {}", session.profile.drive_id); - println!(" Chipset: {}", session.profile.chipset.name()); - println!(); - - // Enable raw read mode - print!("Unlocking drive... "); - match session.unlock() { - Ok(()) => println!("OK"), - Err(e) => { println!("FAILED: {}", e); process::exit(1); } - } - - // Check status - print!("Checking status... "); - match session.status() { - Ok(status) => { - if status.unlocked { - println!("OK (active)"); - } else { - println!("WARNING: drive reported as locked"); - } - } - Err(e) => println!("SKIP ({})", e), - } - - // Calibrate speed - print!("Calibrating speed... "); - match session.calibrate() { - Ok(()) => println!("OK"), - Err(e) => println!("SKIP ({})", e), - } - - // Read test sectors - let test_lbas: &[u32] = &[0, 100, 1000, 10000]; - let mut buf = vec![0u8; 2048]; - let mut pass = 0; - let mut fail = 0; - - for &lba in test_lbas { - print!("Reading sector {}... ", lba); - match session.read_sectors(lba, 1, &mut buf) { - Ok(n) if n == 2048 => { - let nonzero = buf.iter().filter(|&&b| b != 0).count(); - println!("OK ({} bytes, {} non-zero)", n, nonzero); - pass += 1; - } - Ok(n) => { - println!("PARTIAL ({} bytes)", n); - fail += 1; - } - Err(e) => { - println!("FAILED: {}", e); - fail += 1; - } - } - } - - println!(); - if fail == 0 { - println!("All {} checks passed. Drive is fully functional.", pass); - } else { - println!("{} passed, {} failed.", pass, fail); - process::exit(1); - } -}