Files
markbase/markbase-core/src/vfs/mod.rs
Warren f016525687 Implement VFS snapshot support (ZFS-style)
- Add VfsSnapshotInfo struct
- Add snapshot methods to VfsBackend trait:
  - create_snapshot: copy-on-write with metadata
  - list_snapshots: enumerate snapshots
  - delete_snapshot: remove snapshot and metadata
  - restore_snapshot: restore from snapshot
  - snapshot_info: get snapshot metadata
- Implement LocalFs snapshot support:
  - Uses .snapshots directory for storage
  - JSON metadata files (*.meta)
  - Recursive directory copy
  - Size calculation

This enables SMB 'Previous versions' feature foundation.

All 229 tests pass.
2026-06-20 22:13:17 +08:00

212 lines
6.1 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
pub mod local_fs;
pub mod open_flags;
pub mod s3_fs;
pub mod smb_fs;
#[cfg(feature = "smb-server")]
pub mod smb_server_backend;
pub mod util;
use std::path::{Path, PathBuf};
use std::time::SystemTime;
/// VFS 错误类型
#[derive(Debug, Clone)]
pub enum VfsError {
NotFound(String),
PermissionDenied(String),
AlreadyExists(String),
NotEmpty(String),
NotADirectory(String),
IsADirectory(String),
Unsupported(String),
Io(String),
UnexpectedEof,
}
impl std::fmt::Display for VfsError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
VfsError::NotFound(p) => write!(f, "No such file or directory: {}", p),
VfsError::PermissionDenied(p) => write!(f, "Permission denied: {}", p),
VfsError::AlreadyExists(p) => write!(f, "File already exists: {}", p),
VfsError::NotEmpty(p) => write!(f, "Directory not empty: {}", p),
VfsError::NotADirectory(p) => write!(f, "Not a directory: {}", p),
VfsError::IsADirectory(p) => write!(f, "Is a directory: {}", p),
VfsError::Unsupported(msg) => write!(f, "Unsupported: {}", msg),
VfsError::Io(msg) => write!(f, "IO error: {}", msg),
VfsError::UnexpectedEof => write!(f, "Unexpected end of file"),
}
}
}
impl std::error::Error for VfsError {}
/// 文件统计信息(类似 libc::stat
#[derive(Debug, Clone)]
pub struct VfsStat {
pub size: u64,
pub mode: u32,
pub uid: u32,
pub gid: u32,
pub atime: SystemTime,
pub mtime: SystemTime,
pub is_dir: bool,
pub is_symlink: bool,
}
impl VfsStat {
pub fn new() -> Self {
Self {
size: 0,
mode: 0,
uid: 0,
gid: 0,
atime: SystemTime::UNIX_EPOCH,
mtime: SystemTime::UNIX_EPOCH,
is_dir: false,
is_symlink: false,
}
}
}
impl Default for VfsStat {
fn default() -> Self {
Self::new()
}
}
/// 目录条目
#[derive(Debug, Clone)]
pub struct VfsDirEntry {
pub name: String,
pub long_name: String,
pub stat: VfsStat,
}
/// 打开文件的抽象
pub trait VfsFile: Send {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, VfsError>;
fn write(&mut self, buf: &[u8]) -> Result<usize, VfsError>;
fn seek(&mut self, pos: std::io::SeekFrom) -> Result<u64, VfsError>;
fn flush(&mut self) -> Result<(), VfsError>;
fn stat(&mut self) -> Result<VfsStat, VfsError>;
fn set_len(&mut self, size: u64) -> Result<(), VfsError>;
/// Write all bytes (convenience, default loops write() until done)
fn write_all(&mut self, mut buf: &[u8]) -> Result<(), VfsError> {
while !buf.is_empty() {
let n = self.write(buf)?;
if n == 0 {
return Err(VfsError::Io("write returned 0".to_string()));
}
buf = &buf[n..];
}
Ok(())
}
/// Read exactly `buf.len()` bytes (convenience, loops read() until done)
fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<(), VfsError> {
while !buf.is_empty() {
let n = self.read(buf)?;
if n == 0 {
return Err(VfsError::UnexpectedEof);
}
buf = &mut buf[n..];
}
Ok(())
}
}
/// VFS 后端 trait所有文件系统操作
pub trait VfsBackend: Send + Sync {
/// Clone boxed
fn clone_boxed(&self) -> Box<dyn VfsBackend>;
/// 读取目录内容
fn read_dir(&self, path: &Path) -> Result<Vec<VfsDirEntry>, VfsError>;
/// 打开文件(读/写)
fn open_file(
&self,
path: &Path,
flags: &open_flags::OpenFlags,
) -> Result<Box<dyn VfsFile>, VfsError>;
/// 获取文件/目录元数据
fn stat(&self, path: &Path) -> Result<VfsStat, VfsError>;
fn lstat(&self, path: &Path) -> Result<VfsStat, VfsError>;
/// 创建目录
fn create_dir(&self, path: &Path, mode: u32) -> Result<(), VfsError>;
/// 递归创建目录
fn create_dir_all(&self, path: &Path, mode: u32) -> Result<(), VfsError>;
/// 删除空目录
fn remove_dir(&self, path: &Path) -> Result<(), VfsError>;
/// 删除文件
fn remove_file(&self, path: &Path) -> Result<(), VfsError>;
/// 重命名
fn rename(&self, from: &Path, to: &Path) -> Result<(), VfsError>;
/// 设置文件属性
fn set_stat(&self, path: &Path, stat: &VfsStat) -> Result<(), VfsError>;
/// 读取符号链接目标
fn read_link(&self, path: &Path) -> Result<PathBuf, VfsError>;
/// 创建符号链接
fn create_symlink(&self, target: &Path, link: &Path) -> Result<(), VfsError>;
/// 规范化路径
fn real_path(&self, path: &Path) -> Result<PathBuf, VfsError>;
/// 检查路径是否存在
fn exists(&self, path: &Path) -> bool;
/// 创建硬链接
fn hard_link(&self, original: &Path, link: &Path) -> Result<(), VfsError>;
// ===== Snapshot support (ZFS-style) =====
/// 创建快照
fn create_snapshot(&self, _path: &Path, _name: &str) -> Result<(), VfsError> {
Err(VfsError::Unsupported("create_snapshot".to_string()))
}
/// 列出快照
fn list_snapshots(&self, _path: &Path) -> Result<Vec<String>, VfsError> {
Err(VfsError::Unsupported("list_snapshots".to_string()))
}
/// 删除快照
fn delete_snapshot(&self, _path: &Path, _name: &str) -> Result<(), VfsError> {
Err(VfsError::Unsupported("delete_snapshot".to_string()))
}
/// 从快照恢复
fn restore_snapshot(&self, _path: &Path, _name: &str) -> Result<(), VfsError> {
Err(VfsError::Unsupported("restore_snapshot".to_string()))
}
/// 获取快照信息
fn snapshot_info(&self, _path: &Path, _name: &str) -> Result<VfsSnapshotInfo, VfsError> {
Err(VfsError::Unsupported("snapshot_info".to_string()))
}
}
/// 快照信息
#[derive(Debug, Clone)]
pub struct VfsSnapshotInfo {
/// 快照名称
pub name: String,
/// 创建时间
pub created: SystemTime,
/// 快照大小(字节)
pub size: u64,
/// 是否只读
pub read_only: bool,
}