Refactor: drive discovery into platform files, no inline cfg

- drive/unix.rs: find_drives() + resolve_device() for Linux/macOS
- drive/windows.rs: find_drives() + resolve_device() + normalize_path() for Windows
- drive/mod.rs: clean delegation, no cfg branches
- scsi/windows.rs: SPTI transport only, no drive discovery
This commit is contained in:
MattJackson
2026-04-11 16:12:53 +00:00
parent 2d67b11a2c
commit 8999db68ea
5 changed files with 146 additions and 137 deletions
+13 -55
View File
@@ -6,6 +6,11 @@
//! 3. `init()` — activate custom firmware. Removes riplock.
//! 4. `probe_disc()` — probe disc surface. Drive learns optimal speeds.
#[cfg(unix)]
mod unix;
#[cfg(windows)]
mod windows;
use std::path::Path;
use crate::error::{Error, Result};
use crate::sector::SectorReader;
@@ -168,25 +173,10 @@ impl SectorReader for DriveSession {
}
pub fn find_drives() -> Vec<(String, DriveId)> {
#[cfg(target_os = "windows")]
{ crate::scsi::windows::find_drives() }
#[cfg(not(target_os = "windows"))]
{
let mut drives = Vec::new();
for i in 0..16 {
let path = format!("/dev/sg{}", i);
if !std::path::Path::new(&path).exists() { continue; }
if let Ok(mut transport) = crate::scsi::open(std::path::Path::new(&path)) {
if let Ok(id) = DriveId::from_drive(transport.as_mut()) {
if id.raw_inquiry.len() > 0 && (id.raw_inquiry[0] & 0x1F) == 0x05 {
drives.push((path, id));
}
}
}
}
drives
}
#[cfg(unix)]
{ unix::find_drives() }
#[cfg(windows)]
{ windows::find_drives() }
}
pub fn find_drive() -> Option<String> {
@@ -194,42 +184,10 @@ pub fn find_drive() -> Option<String> {
}
pub fn resolve_device(path: &str) -> Result<(String, Option<String>)> {
// Windows: drive letters, CdRom paths, UNC paths — pass through directly
#[cfg(target_os = "windows")]
{
return Ok((crate::scsi::windows::normalize_device_path(path), None));
}
#[cfg(not(target_os = "windows"))]
if path.contains("/sg") {
if !std::path::Path::new(path).exists() {
return Err(Error::DeviceNotFound { path: path.to_string() });
}
return Ok((path.to_string(), None));
}
if path.contains("/sr") {
let mut sr_transport = crate::scsi::open(std::path::Path::new(path))?;
let sr_id = DriveId::from_drive(sr_transport.as_mut())?;
drop(sr_transport);
for (sg_path, sg_id) in find_drives() {
if sg_id.vendor_id == sr_id.vendor_id
&& sg_id.product_id == sr_id.product_id
&& sg_id.serial_number == sr_id.serial_number
{
let warning = format!(
"{} is a block device (sr) — using {} (sg) for raw access", path, sg_path
);
return Ok((sg_path, Some(warning)));
}
}
return Ok((path.to_string(), Some(format!(
"{} is a block device (sr) — no matching sg device found", path
))));
}
if !std::path::Path::new(path).exists() {
return Err(Error::DeviceNotFound { path: path.to_string() });
}
Ok((path.to_string(), None))
#[cfg(unix)]
{ unix::resolve_device(path) }
#[cfg(windows)]
{ windows::resolve_device(path) }
}
fn create_driver(platform: profile::Platform, profile: &DriveProfile) -> Result<Box<dyn PlatformDriver>> {
+52
View File
@@ -0,0 +1,52 @@
//! Unix (Linux/macOS) drive discovery and device resolution.
use crate::error::{Error, Result};
use crate::identity::DriveId;
pub fn find_drives() -> Vec<(String, DriveId)> {
let mut drives = Vec::new();
for i in 0..16 {
let path = format!("/dev/sg{}", i);
if !std::path::Path::new(&path).exists() { continue; }
if let Ok(mut transport) = crate::scsi::open(std::path::Path::new(&path)) {
if let Ok(id) = DriveId::from_drive(transport.as_mut()) {
if !id.raw_inquiry.is_empty() && (id.raw_inquiry[0] & 0x1F) == 0x05 {
drives.push((path, id));
}
}
}
}
drives
}
pub fn resolve_device(path: &str) -> Result<(String, Option<String>)> {
if path.contains("/sg") {
if !std::path::Path::new(path).exists() {
return Err(Error::DeviceNotFound { path: path.to_string() });
}
return Ok((path.to_string(), None));
}
if path.contains("/sr") {
let mut sr_transport = crate::scsi::open(std::path::Path::new(path))?;
let sr_id = DriveId::from_drive(sr_transport.as_mut())?;
drop(sr_transport);
for (sg_path, sg_id) in find_drives() {
if sg_id.vendor_id == sr_id.vendor_id
&& sg_id.product_id == sr_id.product_id
&& sg_id.serial_number == sr_id.serial_number
{
let warning = format!(
"{} is a block device (sr) — using {} (sg) for raw access", path, sg_path
);
return Ok((sg_path, Some(warning)));
}
}
return Ok((path.to_string(), Some(format!(
"{} is a block device (sr) — no matching sg device found", path
))));
}
if !std::path::Path::new(path).exists() {
return Err(Error::DeviceNotFound { path: path.to_string() });
}
Ok((path.to_string(), None))
}
+80
View File
@@ -0,0 +1,80 @@
//! Windows drive discovery and device resolution.
use crate::error::Result;
use crate::identity::DriveId;
use std::path::Path;
pub fn find_drives() -> Vec<(String, DriveId)> {
let mut drives = Vec::new();
// Try CdRom0..CdRom15
for i in 0..16 {
let path = format!("\\\\.\\CdRom{}", i);
if let Ok(mut transport) = crate::scsi::open(Path::new(&path)) {
if let Ok(id) = DriveId::from_drive(transport.as_mut()) {
if !id.raw_inquiry.is_empty() && (id.raw_inquiry[0] & 0x1F) == 0x05 {
drives.push((path, id));
}
}
}
}
// Also try drive letters if CdRom didn't find anything
if drives.is_empty() {
for letter in b'D'..=b'Z' {
let path = format!("{}:", letter as char);
if let Ok(mut transport) = crate::scsi::open(Path::new(&path)) {
if let Ok(id) = DriveId::from_drive(transport.as_mut()) {
if !id.raw_inquiry.is_empty() && (id.raw_inquiry[0] & 0x1F) == 0x05 {
drives.push((path, id));
}
}
}
}
}
drives
}
pub fn resolve_device(path: &str) -> Result<(String, Option<String>)> {
Ok((normalize_path(path), None))
}
/// Normalize a device path to Windows \\.\X: format.
///
/// Accepts: "D:", "D:\\", "\\.\D:", "\\.\CdRom0"
fn normalize_path(path: &str) -> String {
if path.starts_with("\\\\.\\") {
return path.to_string();
}
let trimmed = path.trim_end_matches('\\');
if trimmed.len() == 2 && trimmed.as_bytes()[1] == b':' {
return format!("\\\\.\\{}", trimmed);
}
if path.to_lowercase().starts_with("cdrom") {
return format!("\\\\.\\{}", path);
}
format!("\\\\.\\{}", path)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn normalize_drive_letter() {
assert_eq!(normalize_path("D:"), "\\\\.\\D:");
assert_eq!(normalize_path("E:\\"), "\\\\.\\E:");
}
#[test]
fn normalize_already_prefixed() {
assert_eq!(normalize_path("\\\\.\\D:"), "\\\\.\\D:");
assert_eq!(normalize_path("\\\\.\\CdRom0"), "\\\\.\\CdRom0");
}
#[test]
fn normalize_cdrom() {
assert_eq!(normalize_path("CdRom0"), "\\\\.\\CdRom0");
}
}
+1 -1
View File
@@ -10,7 +10,7 @@ mod linux;
#[cfg(target_os = "macos")]
mod macos;
#[cfg(target_os = "windows")]
pub(crate) mod windows;
mod windows;
#[allow(unused_imports)]
use crate::error::{Error, Result};
-81
View File
@@ -194,84 +194,3 @@ impl ScsiTransport for SptiTransport {
}
}
// ── Device path helpers ────────────────────────────────────────────────────
/// Normalize a device path to Windows \\.\X: format.
///
/// Accepts: "D:", "D:\\", "\\.\D:", "\\.\CdRom0"
pub(crate) fn normalize_device_path(path: &str) -> String {
// Already in \\.\X format
if path.starts_with("\\\\.\\") {
return path.to_string();
}
// Single drive letter: "D:" or "D:\"
let trimmed = path.trim_end_matches('\\');
if trimmed.len() == 2 && trimmed.as_bytes()[1] == b':' {
return format!("\\\\.\\{}", trimmed);
}
// CdRomN format
if path.to_lowercase().starts_with("cdrom") {
return format!("\\\\.\\{}", path);
}
// Fallback: wrap in \\.\
format!("\\\\.\\{}", path)
}
/// Find all optical drives on Windows.
/// Scans drive letters A-Z and CdRom0-15.
pub fn find_drives() -> Vec<(String, crate::identity::DriveId)> {
let mut drives = Vec::new();
// Try CdRom0..CdRom15
for i in 0..16 {
let path = format!("\\\\.\\CdRom{}", i);
if let Ok(mut transport) = SptiTransport::open(Path::new(&path)) {
if let Ok(id) = crate::identity::DriveId::from_drive(&mut transport) {
if !id.raw_inquiry.is_empty() && (id.raw_inquiry[0] & 0x1F) == 0x05 {
drives.push((path, id));
}
}
}
}
// Also try drive letters if CdRom didn't find anything
if drives.is_empty() {
for letter in b'D'..=b'Z' {
let path = format!("{}:", letter as char);
if let Ok(mut transport) = SptiTransport::open(Path::new(&path)) {
if let Ok(id) = crate::identity::DriveId::from_drive(&mut transport) {
if !id.raw_inquiry.is_empty() && (id.raw_inquiry[0] & 0x1F) == 0x05 {
drives.push((path, id));
}
}
}
}
}
drives
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn normalize_drive_letter() {
assert_eq!(normalize_device_path("D:"), "\\\\.\\D:");
assert_eq!(normalize_device_path("E:\\"), "\\\\.\\E:");
}
#[test]
fn normalize_already_prefixed() {
assert_eq!(normalize_device_path("\\\\.\\D:"), "\\\\.\\D:");
assert_eq!(normalize_device_path("\\\\.\\CdRom0"), "\\\\.\\CdRom0");
}
#[test]
fn normalize_cdrom() {
assert_eq!(normalize_device_path("CdRom0"), "\\\\.\\CdRom0");
}
}