MarkBase架构升级:Multi-Volume Virtual Tree + Dual-View Management + Git Remote修正
核心功能: - ✅ Categories/Series双视图管理(category_view.rs + import_markdown.rs) - ✅ FUSE Multi-Volume支持(tree_type参数) - ✅ SSH/SFTP/SCP/rsync协议完整实现(4042行) - ✅ NFS/SMB Module Phase 1-3完成 - ✅ Archive Module Phase 1-4完成(2916行) - ✅ Download Center API完整实现 - ✅ S3兼容API实现(560行) Git配置修正: - ✅ 删除错误origin(gitea.momentry.ddns.net) - ✅ 删除m5max128(指向机器名) - ✅ 设置origin = m5max128gitea.momentry.ddns.net/admin/markbase - ✅ 设置m4minigitea = m4minigitea.momentry.ddns.net/warren/markbase 数据清理: - ✅ 删除38个临时SQLite(保留accusys.sqlite、demo.sqlite) - ✅ 删除.bak、test_*.bin、调试脚本等临时文件 - ✅ 删除临时目录(build/、download files/、raid_test/等) - ✅ 更新.gitignore排除临时文件 架构优化: - 52个文件修改,2434行新增,4739行删除 - Workspace成员整合(16个crate) - 数据库状态:accusys.sqlite保留(主demo测试) 远程同步: - ✅ 准备推送到m5max128gitea(远程Gitea) - ✅ 准备推送到m4minigitea(本地Gitea)
This commit is contained in:
545
rust-iscsi-initiator/src/scsi/mod.rs
Normal file
545
rust-iscsi-initiator/src/scsi/mod.rs
Normal file
@@ -0,0 +1,545 @@
|
||||
use crate::Result;
|
||||
|
||||
/// SCSI Command abstraction
|
||||
pub enum ScsiCommand {
|
||||
/// Test Unit Ready
|
||||
TestUnitReady,
|
||||
|
||||
/// Inquiry
|
||||
Inquiry,
|
||||
|
||||
/// Read (6 bytes CDB)
|
||||
Read6 { lba: u32, transfer_length: u8 },
|
||||
|
||||
/// Read (10 bytes CDB)
|
||||
Read10 { lba: u32, transfer_length: u16 },
|
||||
|
||||
/// Read (16 bytes CDB)
|
||||
Read16 { lba: u64, transfer_length: u32 },
|
||||
|
||||
/// Write (6 bytes CDB)
|
||||
Write6 { lba: u32, transfer_length: u8 },
|
||||
|
||||
/// Write (10 bytes CDB)
|
||||
Write10 { lba: u32, transfer_length: u16 },
|
||||
|
||||
/// Write (16 bytes CDB)
|
||||
Write16 { lba: u64, transfer_length: u32 },
|
||||
|
||||
/// Read Capacity (10)
|
||||
ReadCapacity10,
|
||||
|
||||
/// Read Capacity (16)
|
||||
ReadCapacity16,
|
||||
|
||||
/// Mode Sense (6)
|
||||
ModeSense6 {
|
||||
page_code: u8,
|
||||
allocation_length: u8,
|
||||
},
|
||||
|
||||
/// Mode Sense (10)
|
||||
ModeSense10 {
|
||||
page_code: u8,
|
||||
allocation_length: u16,
|
||||
},
|
||||
|
||||
/// Mode Select (6)
|
||||
ModeSelect6 {
|
||||
page_code: u8,
|
||||
parameter_list: Vec<u8>,
|
||||
},
|
||||
|
||||
/// Mode Select (10)
|
||||
ModeSelect10 {
|
||||
page_code: u8,
|
||||
parameter_list: Vec<u8>,
|
||||
},
|
||||
|
||||
/// Unmap
|
||||
Unmap { lba: u64, blocks: u32 },
|
||||
|
||||
/// Write Same (10)
|
||||
WriteSame10 { lba: u32, blocks: u16 },
|
||||
|
||||
/// Write Same (16)
|
||||
WriteSame16 { lba: u64, blocks: u32 },
|
||||
|
||||
/// Persistent Reserve In
|
||||
PersistentReserveIn {
|
||||
service_action: u8,
|
||||
scope: u8,
|
||||
key: Vec<u8>,
|
||||
},
|
||||
|
||||
/// Persistent Reserve Out
|
||||
PersistentReserveOut {
|
||||
service_action: u8,
|
||||
scope: u8,
|
||||
key: Vec<u8>,
|
||||
},
|
||||
|
||||
/// Start Stop Unit
|
||||
StartStopUnit { start: bool, load_eject: bool },
|
||||
|
||||
/// Prevent Allow Medium Removal
|
||||
PreventAllowMediumRemoval { prevent: bool },
|
||||
|
||||
/// Synchronize Cache (10)
|
||||
SynchronizeCache10 { lba: u32, blocks: u16 },
|
||||
|
||||
/// Synchronize Cache (16)
|
||||
SynchronizeCache16 { lba: u64, blocks: u32 },
|
||||
|
||||
/// Verify (10)
|
||||
Verify10 { lba: u32, blocks: u16 },
|
||||
|
||||
/// Verify (16)
|
||||
Verify16 { lba: u64, blocks: u32 },
|
||||
}
|
||||
|
||||
impl ScsiCommand {
|
||||
/// Encode SCSI CDB (Command Descriptor Block)
|
||||
pub fn encode_cdb(&self) -> Vec<u8> {
|
||||
match self {
|
||||
ScsiCommand::TestUnitReady => {
|
||||
vec![0x00, 0, 0, 0, 0, 0]
|
||||
}
|
||||
|
||||
ScsiCommand::Inquiry => {
|
||||
vec![0x12, 0, 0, 0, 0x24, 0]
|
||||
}
|
||||
|
||||
ScsiCommand::Read6 {
|
||||
lba,
|
||||
transfer_length,
|
||||
} => {
|
||||
vec![
|
||||
0x08,
|
||||
((lba >> 16) & 0x1F) as u8,
|
||||
((lba >> 8) & 0xFF) as u8,
|
||||
(lba & 0xFF) as u8,
|
||||
*transfer_length,
|
||||
0,
|
||||
]
|
||||
}
|
||||
|
||||
ScsiCommand::Read10 {
|
||||
lba,
|
||||
transfer_length,
|
||||
} => {
|
||||
vec![
|
||||
0x28,
|
||||
0,
|
||||
((lba >> 24) & 0xFF) as u8,
|
||||
((lba >> 16) & 0xFF) as u8,
|
||||
((lba >> 8) & 0xFF) as u8,
|
||||
(lba & 0xFF) as u8,
|
||||
0,
|
||||
((transfer_length >> 8) & 0xFF) as u8,
|
||||
(transfer_length & 0xFF) as u8,
|
||||
0,
|
||||
]
|
||||
}
|
||||
|
||||
ScsiCommand::Read16 {
|
||||
lba,
|
||||
transfer_length,
|
||||
} => {
|
||||
let mut cdb = vec![0x88, 0];
|
||||
cdb.extend_from_slice(&lba.to_be_bytes());
|
||||
cdb.extend_from_slice(&transfer_length.to_be_bytes());
|
||||
cdb.push(0);
|
||||
cdb.push(0);
|
||||
cdb
|
||||
}
|
||||
|
||||
ScsiCommand::Write6 {
|
||||
lba,
|
||||
transfer_length,
|
||||
} => {
|
||||
vec![
|
||||
0x0A,
|
||||
((lba >> 16) & 0x1F) as u8,
|
||||
((lba >> 8) & 0xFF) as u8,
|
||||
(lba & 0xFF) as u8,
|
||||
*transfer_length,
|
||||
0,
|
||||
]
|
||||
}
|
||||
|
||||
ScsiCommand::Write10 {
|
||||
lba,
|
||||
transfer_length,
|
||||
} => {
|
||||
vec![
|
||||
0x2A,
|
||||
0,
|
||||
((lba >> 24) & 0xFF) as u8,
|
||||
((lba >> 16) & 0xFF) as u8,
|
||||
((lba >> 8) & 0xFF) as u8,
|
||||
(lba & 0xFF) as u8,
|
||||
0,
|
||||
((transfer_length >> 8) & 0xFF) as u8,
|
||||
(transfer_length & 0xFF) as u8,
|
||||
0,
|
||||
]
|
||||
}
|
||||
|
||||
ScsiCommand::Write16 {
|
||||
lba,
|
||||
transfer_length,
|
||||
} => {
|
||||
let mut cdb = vec![0x8A, 0];
|
||||
cdb.extend_from_slice(&lba.to_be_bytes());
|
||||
cdb.extend_from_slice(&transfer_length.to_be_bytes());
|
||||
cdb.push(0);
|
||||
cdb.push(0);
|
||||
cdb
|
||||
}
|
||||
|
||||
ScsiCommand::ReadCapacity10 => {
|
||||
vec![0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||
}
|
||||
|
||||
ScsiCommand::ReadCapacity16 => {
|
||||
vec![0x9E, 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x20, 0]
|
||||
}
|
||||
|
||||
ScsiCommand::ModeSense6 {
|
||||
page_code,
|
||||
allocation_length,
|
||||
} => {
|
||||
vec![0x1A, 0, *page_code, 0, *allocation_length, 0]
|
||||
}
|
||||
|
||||
ScsiCommand::ModeSense10 {
|
||||
page_code,
|
||||
allocation_length,
|
||||
} => {
|
||||
vec![
|
||||
0x5A,
|
||||
0,
|
||||
0,
|
||||
*page_code,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
((allocation_length >> 8) & 0xFF) as u8,
|
||||
(allocation_length & 0xFF) as u8,
|
||||
0,
|
||||
]
|
||||
}
|
||||
|
||||
ScsiCommand::ModeSelect6 {
|
||||
page_code,
|
||||
parameter_list,
|
||||
} => {
|
||||
let mut cdb = vec![0x15, 0, *page_code, 0, parameter_list.len() as u8, 0];
|
||||
cdb.extend(parameter_list);
|
||||
cdb
|
||||
}
|
||||
|
||||
ScsiCommand::ModeSelect10 {
|
||||
page_code,
|
||||
parameter_list,
|
||||
} => {
|
||||
let mut cdb = vec![
|
||||
0x55,
|
||||
0,
|
||||
0,
|
||||
*page_code,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
((parameter_list.len() >> 8) & 0xFF) as u8,
|
||||
(parameter_list.len() & 0xFF) as u8,
|
||||
0,
|
||||
];
|
||||
cdb.extend(parameter_list);
|
||||
cdb
|
||||
}
|
||||
|
||||
ScsiCommand::Unmap { lba, blocks } => {
|
||||
let mut cdb = vec![0x42, 0];
|
||||
cdb.extend_from_slice(&lba.to_be_bytes());
|
||||
cdb.extend_from_slice(&blocks.to_be_bytes());
|
||||
cdb.push(0);
|
||||
cdb.push(0);
|
||||
cdb.push(0);
|
||||
cdb.push(0);
|
||||
cdb.push(0);
|
||||
cdb.push(0);
|
||||
cdb.push(0);
|
||||
cdb.push(0);
|
||||
cdb.push(0);
|
||||
cdb.push(0);
|
||||
cdb
|
||||
}
|
||||
|
||||
ScsiCommand::WriteSame10 { lba, blocks } => {
|
||||
vec![
|
||||
0x41,
|
||||
0,
|
||||
((lba >> 24) & 0xFF) as u8,
|
||||
((lba >> 16) & 0xFF) as u8,
|
||||
((lba >> 8) & 0xFF) as u8,
|
||||
(lba & 0xFF) as u8,
|
||||
0,
|
||||
((blocks >> 8) & 0xFF) as u8,
|
||||
(blocks & 0xFF) as u8,
|
||||
0,
|
||||
]
|
||||
}
|
||||
|
||||
ScsiCommand::WriteSame16 { lba, blocks } => {
|
||||
let mut cdb = vec![0x93, 0];
|
||||
cdb.extend_from_slice(&lba.to_be_bytes());
|
||||
cdb.extend_from_slice(&blocks.to_be_bytes());
|
||||
cdb.push(0);
|
||||
cdb.push(0);
|
||||
cdb
|
||||
}
|
||||
|
||||
ScsiCommand::PersistentReserveIn {
|
||||
service_action,
|
||||
scope,
|
||||
key,
|
||||
} => {
|
||||
let mut cdb = vec![
|
||||
0x5E,
|
||||
(service_action & 0x1F) | ((scope << 4) & 0xF0),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
((key.len() >> 8) & 0xFF) as u8,
|
||||
(key.len() & 0xFF) as u8,
|
||||
0,
|
||||
];
|
||||
cdb.extend(key);
|
||||
cdb
|
||||
}
|
||||
|
||||
ScsiCommand::PersistentReserveOut {
|
||||
service_action,
|
||||
scope,
|
||||
key,
|
||||
} => {
|
||||
let mut cdb = vec![
|
||||
0x5F,
|
||||
(service_action & 0x1F) | ((scope << 4) & 0xF0),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
((key.len() >> 8) & 0xFF) as u8,
|
||||
(key.len() & 0xFF) as u8,
|
||||
0,
|
||||
];
|
||||
cdb.extend(key);
|
||||
cdb
|
||||
}
|
||||
|
||||
ScsiCommand::StartStopUnit { start, load_eject } => {
|
||||
vec![
|
||||
0x1B,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
if *start { 1 } else { 0 } | if *load_eject { 2 } else { 0 },
|
||||
0,
|
||||
]
|
||||
}
|
||||
|
||||
ScsiCommand::PreventAllowMediumRemoval { prevent } => {
|
||||
vec![0x1E, 0, 0, 0, if *prevent { 1 } else { 0 }, 0]
|
||||
}
|
||||
|
||||
ScsiCommand::SynchronizeCache10 { lba, blocks } => {
|
||||
vec![
|
||||
0x35,
|
||||
0,
|
||||
((lba >> 24) & 0xFF) as u8,
|
||||
((lba >> 16) & 0xFF) as u8,
|
||||
((lba >> 8) & 0xFF) as u8,
|
||||
(lba & 0xFF) as u8,
|
||||
0,
|
||||
((blocks >> 8) & 0xFF) as u8,
|
||||
(blocks & 0xFF) as u8,
|
||||
0,
|
||||
]
|
||||
}
|
||||
|
||||
ScsiCommand::SynchronizeCache16 { lba, blocks } => {
|
||||
let mut cdb = vec![0x91, 0];
|
||||
cdb.extend_from_slice(&lba.to_be_bytes());
|
||||
cdb.extend_from_slice(&blocks.to_be_bytes());
|
||||
cdb.push(0);
|
||||
cdb.push(0);
|
||||
cdb
|
||||
}
|
||||
|
||||
ScsiCommand::Verify10 { lba, blocks } => {
|
||||
vec![
|
||||
0x2F,
|
||||
0,
|
||||
((lba >> 24) & 0xFF) as u8,
|
||||
((lba >> 16) & 0xFF) as u8,
|
||||
((lba >> 8) & 0xFF) as u8,
|
||||
(lba & 0xFF) as u8,
|
||||
0,
|
||||
((blocks >> 8) & 0xFF) as u8,
|
||||
(blocks & 0xFF) as u8,
|
||||
0,
|
||||
]
|
||||
}
|
||||
|
||||
ScsiCommand::Verify16 { lba, blocks } => {
|
||||
let mut cdb = vec![0x8F, 0];
|
||||
cdb.extend_from_slice(&lba.to_be_bytes());
|
||||
cdb.extend_from_slice(&blocks.to_be_bytes());
|
||||
cdb.push(0);
|
||||
cdb.push(0);
|
||||
cdb
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get command name
|
||||
pub fn name(&self) -> &'static str {
|
||||
match self {
|
||||
ScsiCommand::TestUnitReady => "Test Unit Ready",
|
||||
ScsiCommand::Inquiry => "Inquiry",
|
||||
ScsiCommand::Read6 { .. } => "Read(6)",
|
||||
ScsiCommand::Read10 { .. } => "Read(10)",
|
||||
ScsiCommand::Read16 { .. } => "Read(16)",
|
||||
ScsiCommand::Write6 { .. } => "Write(6)",
|
||||
ScsiCommand::Write10 { .. } => "Write(10)",
|
||||
ScsiCommand::Write16 { .. } => "Write(16)",
|
||||
ScsiCommand::ReadCapacity10 => "Read Capacity(10)",
|
||||
ScsiCommand::ReadCapacity16 => "Read Capacity(16)",
|
||||
ScsiCommand::ModeSense6 { .. } => "Mode Sense(6)",
|
||||
ScsiCommand::ModeSense10 { .. } => "Mode Sense(10)",
|
||||
ScsiCommand::ModeSelect6 { .. } => "Mode Select(6)",
|
||||
ScsiCommand::ModeSelect10 { .. } => "Mode Select(10)",
|
||||
ScsiCommand::Unmap { .. } => "Unmap",
|
||||
ScsiCommand::WriteSame10 { .. } => "Write Same(10)",
|
||||
ScsiCommand::WriteSame16 { .. } => "Write Same(16)",
|
||||
ScsiCommand::PersistentReserveIn { .. } => "Persistent Reserve In",
|
||||
ScsiCommand::PersistentReserveOut { .. } => "Persistent Reserve Out",
|
||||
ScsiCommand::StartStopUnit { .. } => "Start Stop Unit",
|
||||
ScsiCommand::PreventAllowMediumRemoval { .. } => "Prevent/Allow Medium Removal",
|
||||
ScsiCommand::SynchronizeCache10 { .. } => "Synchronize Cache(10)",
|
||||
ScsiCommand::SynchronizeCache16 { .. } => "Synchronize Cache(16)",
|
||||
ScsiCommand::Verify10 { .. } => "Verify(10)",
|
||||
ScsiCommand::Verify16 { .. } => "Verify(16)",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Inquiry response structure
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct InquiryResponse {
|
||||
/// Peripheral device type
|
||||
pub peripheral_type: u8,
|
||||
|
||||
/// Vendor identification
|
||||
pub vendor_id: String,
|
||||
|
||||
/// Product identification
|
||||
pub product_id: String,
|
||||
|
||||
/// Product revision level
|
||||
pub product_rev: String,
|
||||
}
|
||||
|
||||
impl InquiryResponse {
|
||||
/// Parse inquiry response from bytes
|
||||
pub fn parse(data: &[u8]) -> Result<Self> {
|
||||
if data.len() < 36 {
|
||||
return Err(crate::Error::Scsi("Inquiry data too short".into()));
|
||||
}
|
||||
|
||||
let peripheral_type = data[0] & 0x1F;
|
||||
let vendor_id = String::from_utf8_lossy(&data[8..16]).trim().to_string();
|
||||
let product_id = String::from_utf8_lossy(&data[16..32]).trim().to_string();
|
||||
let product_rev = String::from_utf8_lossy(&data[32..36]).trim().to_string();
|
||||
|
||||
Ok(Self {
|
||||
peripheral_type,
|
||||
vendor_id,
|
||||
product_id,
|
||||
product_rev,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Read Capacity response structure
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ReadCapacityResponse {
|
||||
/// Maximum LBA
|
||||
pub max_lba: u64,
|
||||
|
||||
/// Block size
|
||||
pub block_size: u32,
|
||||
}
|
||||
|
||||
impl ReadCapacityResponse {
|
||||
/// Parse from bytes
|
||||
pub fn parse_10(data: &[u8]) -> Result<Self> {
|
||||
if data.len() < 8 {
|
||||
return Err(crate::Error::Scsi("Read Capacity data too short".into()));
|
||||
}
|
||||
|
||||
let max_lba = u32::from_be_bytes([data[0], data[1], data[2], data[3]]) as u64;
|
||||
let block_size = u32::from_be_bytes([data[4], data[5], data[6], data[7]]);
|
||||
|
||||
Ok(Self {
|
||||
max_lba,
|
||||
block_size,
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse from 16-byte response
|
||||
pub fn parse_16(data: &[u8]) -> Result<Self> {
|
||||
if data.len() < 32 {
|
||||
return Err(crate::Error::Scsi(
|
||||
"Read Capacity(16) data too short".into(),
|
||||
));
|
||||
}
|
||||
|
||||
let max_lba = u64::from_be_bytes([
|
||||
data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
|
||||
]);
|
||||
let block_size = u32::from_be_bytes([data[8], data[9], data[10], data[11]]);
|
||||
|
||||
Ok(Self {
|
||||
max_lba,
|
||||
block_size,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_cdb_encode() {
|
||||
let cmd = ScsiCommand::TestUnitReady;
|
||||
let cdb = cmd.encode_cdb();
|
||||
assert_eq!(cdb.len(), 6);
|
||||
assert_eq!(cdb[0], 0x00);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_capacity() {
|
||||
let data = [0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x02, 0x00];
|
||||
let resp = ReadCapacityResponse::parse_10(&data).unwrap();
|
||||
assert_eq!(resp.max_lba, 127);
|
||||
assert_eq!(resp.block_size, 512);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user