macOS Time Machine AFP monitoring: backup_time update on file modification

- Added afp_monitor.rs module to track AFP_AfpInfo backup_time
- Open struct now has 'modified' flag to track file modifications
- write.rs sets modified=true on successful write
- close.rs calls AfpMonitor::update_backup_time() on modified files
- create.rs calls AfpMonitor::init_afp_info() on new file creation
- AFP_AfpInfo stored as xattr com.apple.aapl.AfpInfo
- backup_time updated to current epoch time on modification

Also includes:
- LZ4 compression using lz4_flex crate
- Case sensitivity conditional on backend capabilities
- LDAP cfg feature gate fix
- RAID rebuild reconstruction implementation
- DOS attributes xattr persistence
- Snapshot disk persistence

Tests: 201 smb-server, 452 markbase-core (653 total)
This commit is contained in:
Warren
2026-06-24 00:46:33 +08:00
parent 5300b672cb
commit 57fd6a475f
33 changed files with 1211 additions and 253 deletions

View File

@@ -109,15 +109,57 @@ impl VfsRaidBackend {
(offset / self.stripe_size as u64) as usize % self.backends.len()
}
fn rebuild_disk(&self, _failed_disk_index: usize) -> Result<(), VfsError> {
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)?;
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<dyn VfsBackend>,
target: &Box<dyn VfsBackend>,
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(())
}
}