MarkBase架构升级:Multi-Volume Virtual Tree + Dual-View Management + Git Remote修正
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled

核心功能:
-  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:
Warren
2026-06-12 12:59:54 +08:00
parent 4cb7e80568
commit 1300a4e223
4559 changed files with 195840 additions and 4244 deletions

View 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);
}
}