Files
markbase/markbase-core/src/vfs/compression.rs
Warren 57fd6a475f 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)
2026-06-24 00:46:33 +08:00

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),
}
}