Remove standalone binaries — functionality lives in freemkv CLI and tests
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.
This commit is contained in:
-11
@@ -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"
|
||||
|
||||
@@ -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<String> = env::args().collect();
|
||||
if args.len() < 3 {
|
||||
eprintln!("Usage: aacs-test <device> <keydb_path>");
|
||||
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.");
|
||||
}
|
||||
@@ -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<String> = env::args().collect();
|
||||
|
||||
if args.len() < 2 {
|
||||
eprintln!("freemkv-info — Drive identification and compatibility checker");
|
||||
eprintln!();
|
||||
eprintln!("Usage: freemkv-info <device> [options]");
|
||||
eprintln!();
|
||||
eprintln!(" <device> 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<Vec<u8>>,
|
||||
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<Vec<u8>>,
|
||||
) {
|
||||
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<Vec<u8>>,
|
||||
) {
|
||||
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()
|
||||
}
|
||||
@@ -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<String> = env::args().collect();
|
||||
|
||||
if args.len() < 2 {
|
||||
eprintln!("freemkv-test — Verify raw disc access works");
|
||||
eprintln!();
|
||||
eprintln!("Usage: freemkv-test <device> [--profiles <dir>]");
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user