use super::{VfsBackend, VfsDirEntry, VfsError, VfsFile, VfsStat, VfsRaidConfig, VfsRaidLevel}; use std::path::{Path, PathBuf}; 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())); } if failed_disk_index >= self.backends.len() { return Err(VfsError::Io(format!("Invalid disk index {}", failed_disk_index))); } let source_index = if self.backends.len() > 1 { // Use backends[0] as source if failed_disk_index != 0, else use backends[1] if failed_disk_index != 0 { 0 } else { 1 } } else { return Err(VfsError::Io("Not enough disks for rebuild".to_string())); }; let target_backend = &self.backends[failed_disk_index]; let source_backend = &self.backends[source_index]; target_backend.create_dir_all(&PathBuf::from("/"), 0o755)?; self.rebuild_recursive(source_backend, target_backend, &PathBuf::from("/"))?; Ok(()) } fn rebuild_recursive( &self, source: &Box, target: &Box, path: &Path, ) -> Result<(), VfsError> { let entries = source.read_dir(path)?; for entry in &entries { let entry_path = path.join(&entry.name); if entry.stat.is_dir { target.create_dir_all(&entry_path, entry.stat.mode)?; self.rebuild_recursive(source, target, &entry_path)?; } else { let mut src_file = source.open_file(&entry_path, &super::open_flags::OpenFlags::new().read())?; let data = src_file.read_all()?; let mut dst_file = target.open_file( &entry_path, &super::open_flags::OpenFlags::new().write().create().truncate(), )?; dst_file.write_all(&data)?; if let Ok(stat) = source.stat(&entry_path) { target.set_stat(&entry_path, &stat)?; } } } 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(()) } }