- S3Vfs with all 15 VfsBackend methods via rusty-s3 + ureq - S3VfsFile for buffered writes + ranged reads - AWS Signature V4 pre-signed URLs (rusty-s3) - ListObjectsV2 for directory listing (prefix + delimiter) - Path-style URL mapping (/path to bucket/key)
162 lines
4.7 KiB
Rust
162 lines
4.7 KiB
Rust
pub mod open_flags;
|
||
pub mod local_fs;
|
||
pub mod s3_fs;
|
||
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 {
|
||
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 {
|
||
/// 读取目录内容
|
||
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>;
|
||
}
|