- 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)
110 lines
3.7 KiB
Rust
110 lines
3.7 KiB
Rust
use super::{VfsCompression, VfsCompressionConfig, VfsError};
|
|
use std::path::Path;
|
|
|
|
pub struct Compressor {
|
|
config: VfsCompressionConfig,
|
|
}
|
|
|
|
impl Compressor {
|
|
pub fn new(config: VfsCompressionConfig) -> Self {
|
|
Self { config }
|
|
}
|
|
|
|
pub fn should_compress(&self, size: u64) -> bool {
|
|
self.config.algorithm != VfsCompression::None && size >= self.config.min_size
|
|
}
|
|
|
|
pub fn compress(&self, data: &[u8]) -> Result<Vec<u8>, VfsError> {
|
|
if !self.should_compress(data.len() as u64) {
|
|
return Ok(data.to_vec());
|
|
}
|
|
|
|
match self.config.algorithm {
|
|
VfsCompression::None => Ok(data.to_vec()),
|
|
VfsCompression::Zstd => {
|
|
let level = self.config.level as i32;
|
|
zstd::encode_all(data, level)
|
|
.map_err(|e| VfsError::Io(format!("ZSTD compression failed: {}", e)))
|
|
}
|
|
VfsCompression::Lz4 => {
|
|
Ok(lz4_flex::compress_prepend_size(data))
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn decompress(&self, data: &[u8]) -> Result<Vec<u8>, VfsError> {
|
|
match self.config.algorithm {
|
|
VfsCompression::None => Ok(data.to_vec()),
|
|
VfsCompression::Zstd => {
|
|
zstd::decode_all(data)
|
|
.map_err(|e| VfsError::Io(format!("ZSTD decompression failed: {}", e)))
|
|
}
|
|
VfsCompression::Lz4 => {
|
|
lz4_flex::decompress_size_prepended(data)
|
|
.map_err(|e| VfsError::Io(format!("LZ4 decompression failed: {}", e)))
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn compress_file(&self, source: &Path, target: &Path) -> Result<(), VfsError> {
|
|
let data = std::fs::read(source)
|
|
.map_err(|e| super::util::map_io_error(source, e))?;
|
|
|
|
if !self.should_compress(data.len() as u64) {
|
|
std::fs::copy(source, target)
|
|
.map_err(|e| super::util::map_io_error(source, e))?;
|
|
return Ok(());
|
|
}
|
|
|
|
let compressed = self.compress(&data)?;
|
|
std::fs::write(target, compressed)
|
|
.map_err(|e| super::util::map_io_error(target, e))?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn decompress_file(&self, source: &Path, target: &Path) -> Result<(), VfsError> {
|
|
let data = std::fs::read(source)
|
|
.map_err(|e| super::util::map_io_error(source, e))?;
|
|
|
|
let decompressed = self.decompress(&data)?;
|
|
std::fs::write(target, decompressed)
|
|
.map_err(|e| super::util::map_io_error(target, e))?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn extension(&self) -> &'static str {
|
|
match self.config.algorithm {
|
|
VfsCompression::None => "",
|
|
VfsCompression::Zstd => ".zst",
|
|
VfsCompression::Lz4 => ".lz4",
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn detect_compression(path: &Path) -> VfsCompression {
|
|
let ext = path.extension().map(|e| e.to_string_lossy());
|
|
match ext.as_ref().map(|s| s.as_ref()) {
|
|
Some("zst") => VfsCompression::Zstd,
|
|
Some("lz4") => VfsCompression::Lz4,
|
|
_ => VfsCompression::None,
|
|
}
|
|
}
|
|
|
|
pub fn get_decompressed_size(path: &Path) -> Result<u64, VfsError> {
|
|
let data = std::fs::read(path)
|
|
.map_err(|e| super::util::map_io_error(path, e))?;
|
|
|
|
match detect_compression(path) {
|
|
VfsCompression::Zstd => {
|
|
let decompressed = zstd::decode_all(data.as_slice())
|
|
.map_err(|e| VfsError::Io(format!("ZSTD decompression failed: {}", e)))?;
|
|
Ok(decompressed.len() as u64)
|
|
}
|
|
VfsCompression::Lz4 => {
|
|
Err(VfsError::Unsupported("LZ4 size detection not implemented".to_string()))
|
|
}
|
|
VfsCompression::None => Ok(data.len() as u64),
|
|
}
|
|
} |