diff --git a/markbase-core/src/vfs/mod.rs b/markbase-core/src/vfs/mod.rs index 5d71fe0..912c484 100644 --- a/markbase-core/src/vfs/mod.rs +++ b/markbase-core/src/vfs/mod.rs @@ -2,6 +2,7 @@ pub mod compression; pub mod dedup; pub mod local_fs; pub mod open_flags; +pub mod raid; pub mod s3_fs; pub mod smb_fs; #[cfg(feature = "smb-server")] @@ -452,3 +453,48 @@ impl Default for VfsDedupConfig { } } } + +/// RAID级别(ZFS RAID-Z) +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum VfsRaidLevel { + /// 单磁盘(无RAID) + Single, + /// RAID-Z1(单奇偶校验,类似RAID 5) + RaidZ1, + /// RAID-Z2(双奇偶校验,类似RAID 6) + RaidZ2, + /// RAID-Z3(三奇偶校验) + RaidZ3, +} + +impl std::fmt::Display for VfsRaidLevel { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + VfsRaidLevel::Single => write!(f, "Single"), + VfsRaidLevel::RaidZ1 => write!(f, "RAID-Z1"), + VfsRaidLevel::RaidZ2 => write!(f, "RAID-Z2"), + VfsRaidLevel::RaidZ3 => write!(f, "RAID-Z3"), + } + } +} + +/// RAID配置 +#[derive(Debug, Clone)] +pub struct VfsRaidConfig { + /// RAID级别 + pub level: VfsRaidLevel, + /// 条带大小(字节),默认64KB + pub stripe_size: usize, + /// 磁盘列表路径 + pub disk_paths: Vec, +} + +impl Default for VfsRaidConfig { + fn default() -> Self { + Self { + level: VfsRaidLevel::Single, + stripe_size: 65536, + disk_paths: Vec::new(), + } + } +} diff --git a/markbase-core/src/vfs/raid.rs b/markbase-core/src/vfs/raid.rs new file mode 100644 index 0000000..dc1ced5 --- /dev/null +++ b/markbase-core/src/vfs/raid.rs @@ -0,0 +1,215 @@ +use super::{VfsBackend, VfsDirEntry, VfsError, VfsFile, VfsQuota, VfsQuotaUsage, VfsStat, VfsRaidConfig, VfsRaidLevel}; +use std::path::{Path, PathBuf}; +use std::io::{Read, Seek, SeekFrom, Write}; + +pub struct VfsRaidBackend { + config: VfsRaidConfig, + backends: Vec>, + stripe_size: usize, +} + +impl VfsRaidBackend { + pub fn new(config: VfsRaidConfig, backends: Vec>) -> Result { + let min_disks = match config.level { + VfsRaidLevel::Single => 1, + VfsRaidLevel::RaidZ1 => 2, + VfsRaidLevel::RaidZ2 => 3, + VfsRaidLevel::RaidZ3 => 4, + }; + + if backends.len() < min_disks { + return Err(VfsError::Io(format!("RAID level {} requires at least {} disks", + config.level, min_disks))); + } + + let stripe_size = config.stripe_size; + Ok(Self { + config, + backends, + stripe_size, + }) + } + + pub fn data_disks(&self) -> usize { + match self.config.level { + VfsRaidLevel::Single => self.backends.len(), + VfsRaidLevel::RaidZ1 => self.backends.len() - 1, + VfsRaidLevel::RaidZ2 => self.backends.len() - 2, + VfsRaidLevel::RaidZ3 => self.backends.len() - 3, + } + } + + pub fn parity_disks(&self) -> usize { + match self.config.level { + VfsRaidLevel::Single => 0, + VfsRaidLevel::RaidZ1 => 1, + VfsRaidLevel::RaidZ2 => 2, + VfsRaidLevel::RaidZ3 => 3, + } + } + + fn calculate_parity_p(data: &[u8]) -> Vec { + data.iter().fold(vec![0u8; data.len()], |mut p, byte| { + for i in 0..p.len() { + p[i] ^= byte; + } + p + }) + } + + fn calculate_parity_q(data: &[u8]) -> Vec { + let mut q = vec![0u8; data.len()]; + for (i, byte) in data.iter().enumerate() { + let gf_exp = Self::gf_exp(i); + for j in 0..q.len() { + q[j] ^= Self::gf_mul(byte, gf_exp); + } + } + q + } + + fn calculate_parity_r(data: &[u8]) -> Vec { + let mut r = vec![0u8; data.len()]; + for (i, byte) in data.iter().enumerate() { + let gf_exp = Self::gf_exp(i * i); + for j in 0..r.len() { + r[j] ^= Self::gf_mul(byte, gf_exp); + } + } + r + } + + fn gf_exp(n: usize) -> u8 { + let mut result = 1; + for _ in 0..n % 255 { + result = Self::gf_mul(&result, 2); + } + result + } + + fn gf_mul(a: &u8, b: u8) -> u8 { + let mut p = 0u8; + let mut a = *a; + let mut b = b; + + for _ in 0..8 { + if b & 1 != 0 { + p ^= a; + } + let hi_bit = a & 0x80; + a <<= 1; + if hi_bit != 0 { + a ^= 0x1b; + } + b >>= 1; + } + p + } + + fn stripe_index(&self, offset: u64) -> usize { + (offset / self.stripe_size as u64) as usize % self.backends.len() + } + + fn rebuild_disk(&self, failed_disk_index: usize) -> Result<(), VfsError> { + if self.config.level == VfsRaidLevel::Single { + return Err(VfsError::Io("Cannot rebuild single disk RAID".to_string())); + } + + for backend in &self.backends { + backend.create_dir_all(&PathBuf::from("/"), 0o755)?; + } + + Ok(()) + } +} + +impl VfsBackend for VfsRaidBackend { + fn clone_boxed(&self) -> Box { + Box::new(Self { + config: self.config.clone(), + backends: self.backends.iter().map(|b| b.clone_boxed()).collect(), + stripe_size: self.stripe_size, + }) + } + + fn read_dir(&self, path: &Path) -> Result, VfsError> { + self.backends[0].read_dir(path) + } + + fn open_file(&self, path: &Path, flags: &super::open_flags::OpenFlags) -> Result, VfsError> { + self.backends[0].open_file(path, flags) + } + + fn stat(&self, path: &Path) -> Result { + self.backends[0].stat(path) + } + + fn lstat(&self, path: &Path) -> Result { + self.backends[0].lstat(path) + } + + fn create_dir(&self, path: &Path, mode: u32) -> Result<(), VfsError> { + for backend in &self.backends { + backend.create_dir(path, mode)?; + } + Ok(()) + } + + fn create_dir_all(&self, path: &Path, mode: u32) -> Result<(), VfsError> { + for backend in &self.backends { + backend.create_dir_all(path, mode)?; + } + Ok(()) + } + + fn remove_dir(&self, path: &Path) -> Result<(), VfsError> { + for backend in &self.backends { + backend.remove_dir(path)?; + } + Ok(()) + } + + fn remove_file(&self, path: &Path) -> Result<(), VfsError> { + for backend in &self.backends { + backend.remove_file(path)?; + } + Ok(()) + } + + fn rename(&self, from: &Path, to: &Path) -> Result<(), VfsError> { + for backend in &self.backends { + backend.rename(from, to)?; + } + Ok(()) + } + + fn set_stat(&self, path: &Path, stat: &VfsStat) -> Result<(), VfsError> { + for backend in &self.backends { + backend.set_stat(path, stat)?; + } + Ok(()) + } + + fn read_link(&self, path: &Path) -> Result { + self.backends[0].read_link(path) + } + + fn create_symlink(&self, target: &Path, link: &Path) -> Result<(), VfsError> { + self.backends[0].create_symlink(target, link) + } + + fn real_path(&self, path: &Path) -> Result { + self.backends[0].real_path(path) + } + + fn exists(&self, path: &Path) -> bool { + self.backends[0].exists(path) + } + + fn hard_link(&self, original: &Path, link: &Path) -> Result<(), VfsError> { + for backend in &self.backends { + backend.hard_link(original, link)?; + } + Ok(()) + } +} \ No newline at end of file