Files
markbase/markbase-core/src/vfs/local_fs.rs
Warren eb80c07c85
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
Implement WebDAV VFS integration: dav-server 0.11 compatible
- Add webdav.rs module: VfsDavFs, VfsDavFile, VfsDavMetaData
- Implement DavFileSystem + Clone for GuardedFileSystem blanket impl
- Add clone_boxed to VfsBackend trait (required for Sync)
- Update CLI webdav.rs to use VFS instead of SQLite
- Add bytes dependency
- All 155 tests pass
2026-06-19 08:19:16 +08:00

234 lines
7.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.
use super::open_flags::OpenFlags;
use super::util;
use super::{VfsBackend, VfsDirEntry, VfsError, VfsFile, VfsStat};
use std::fs::{self, File, OpenOptions};
use std::io::{Read, Seek, SeekFrom, Write};
use std::os::unix::fs::{MetadataExt, PermissionsExt};
use std::path::{Path, PathBuf};
/// 本地文件系统实现(直接包装 std::fs不做路径解析
/// 路径解析由上层SftpHandler负责
pub struct LocalFs;
impl Default for LocalFs {
fn default() -> Self {
Self::new()
}
}
impl LocalFs {
pub fn new() -> Self {
Self
}
}
struct LocalFile {
file: File,
}
impl VfsFile for LocalFile {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, VfsError> {
self.file.read(buf).map_err(|e| VfsError::Io(e.to_string()))
}
fn write(&mut self, buf: &[u8]) -> Result<usize, VfsError> {
self.file
.write(buf)
.map_err(|e| VfsError::Io(e.to_string()))
}
fn seek(&mut self, pos: SeekFrom) -> Result<u64, VfsError> {
self.file.seek(pos).map_err(|e| VfsError::Io(e.to_string()))
}
fn flush(&mut self) -> Result<(), VfsError> {
self.file.flush().map_err(|e| VfsError::Io(e.to_string()))
}
fn stat(&mut self) -> Result<VfsStat, VfsError> {
let meta = self
.file
.metadata()
.map_err(|e| VfsError::Io(e.to_string()))?;
Ok(util::stat_from_metadata(&meta, false))
}
fn set_len(&mut self, size: u64) -> Result<(), VfsError> {
self.file
.set_len(size)
.map_err(|e| VfsError::Io(e.to_string()))
}
}
impl VfsBackend for LocalFs {
fn clone_boxed(&self) -> Box<dyn VfsBackend> {
Box::new(Self {})
}
fn read_dir(&self, path: &Path) -> Result<Vec<VfsDirEntry>, VfsError> {
let dir = fs::read_dir(path).map_err(|e| util::map_io_error(path, e))?;
let mut entries = Vec::new();
for entry in dir {
let entry = entry.map_err(|e| util::map_io_error(path, e))?;
let name = entry.file_name().to_string_lossy().to_string();
let file_type = entry.file_type().map_err(|e| util::map_io_error(path, e))?;
let meta = entry.metadata().map_err(|e| util::map_io_error(path, e))?;
let stat = util::stat_from_metadata(&meta, file_type.is_symlink());
let long_name = util::build_long_name(&stat, &name);
entries.push(VfsDirEntry {
name,
long_name,
stat,
});
}
entries.sort_by(|a, b| a.name.cmp(&b.name));
Ok(entries)
}
fn open_file(&self, path: &Path, flags: &OpenFlags) -> Result<Box<dyn VfsFile>, VfsError> {
let mut opts = OpenOptions::new();
opts.read(flags.read);
opts.write(flags.write);
opts.append(flags.append);
opts.create(flags.create);
opts.truncate(flags.truncate);
opts.create_new(flags.exclusive);
let file = opts.open(path).map_err(|e| util::map_io_error(path, e))?;
#[cfg(unix)]
if flags.create && !flags.exclusive {
if let Ok(meta) = file.metadata() {
if flags.mode != 0 && meta.permissions().mode() != flags.mode {
fs::set_permissions(path, std::fs::Permissions::from_mode(flags.mode)).ok();
}
}
}
Ok(Box::new(LocalFile { file }))
}
fn stat(&self, path: &Path) -> Result<VfsStat, VfsError> {
let meta = fs::metadata(path).map_err(|e| util::map_io_error(path, e))?;
Ok(util::stat_from_metadata(&meta, false))
}
fn lstat(&self, path: &Path) -> Result<VfsStat, VfsError> {
let meta = fs::symlink_metadata(path).map_err(|e| util::map_io_error(path, e))?;
let is_symlink = path.is_symlink() || meta.file_type().is_symlink();
Ok(util::stat_from_metadata(&meta, is_symlink))
}
fn create_dir(&self, path: &Path, mode: u32) -> Result<(), VfsError> {
fs::create_dir(path).map_err(|e| util::map_io_error(path, e))?;
#[cfg(unix)]
{
fs::set_permissions(path, std::fs::Permissions::from_mode(mode))
.map_err(|e| util::map_io_error(path, e))?;
}
Ok(())
}
fn create_dir_all(&self, path: &Path, mode: u32) -> Result<(), VfsError> {
fs::create_dir_all(path).map_err(|e| util::map_io_error(path, e))?;
#[cfg(unix)]
{
if mode != 0 {
fs::set_permissions(path, std::fs::Permissions::from_mode(mode))
.map_err(|e| util::map_io_error(path, e))?;
}
}
Ok(())
}
fn remove_dir(&self, path: &Path) -> Result<(), VfsError> {
fs::remove_dir(path).map_err(|e| util::map_io_error(path, e))
}
fn remove_file(&self, path: &Path) -> Result<(), VfsError> {
fs::remove_file(path).map_err(|e| util::map_io_error(path, e))
}
fn rename(&self, from: &Path, to: &Path) -> Result<(), VfsError> {
fs::rename(from, to).map_err(|e| util::map_io_error(from, e))
}
fn set_stat(&self, path: &Path, stat: &VfsStat) -> Result<(), VfsError> {
#[cfg(unix)]
{
if stat.mode != 0 {
fs::set_permissions(path, std::fs::Permissions::from_mode(stat.mode))
.map_err(|e| util::map_io_error(path, e))?;
}
}
if let (Some(atime), Some(mtime)) = (
stat.atime.duration_since(std::time::UNIX_EPOCH).ok(),
stat.mtime.duration_since(std::time::UNIX_EPOCH).ok(),
) {
filetime::set_file_times(
path,
filetime::FileTime::from_unix_time(atime.as_secs() as i64, 0),
filetime::FileTime::from_unix_time(mtime.as_secs() as i64, 0),
)
.map_err(|e| util::map_io_error(path, e))?;
}
Ok(())
}
fn read_link(&self, path: &Path) -> Result<PathBuf, VfsError> {
let target = fs::read_link(path).map_err(|e| util::map_io_error(path, e))?;
Ok(target)
}
fn create_symlink(&self, target: &Path, link: &Path) -> Result<(), VfsError> {
#[cfg(unix)]
{
std::os::unix::fs::symlink(target, link).map_err(|e| util::map_io_error(link, e))?;
}
#[cfg(not(unix))]
{
std::os::windows::fs::symlink_file(target, link)
.map_err(|e| util::map_io_error(link, e))?;
}
Ok(())
}
fn real_path(&self, path: &Path) -> Result<PathBuf, VfsError> {
let canonical = path
.canonicalize()
.map_err(|e| util::map_io_error(path, e))?;
Ok(canonical)
}
fn exists(&self, path: &Path) -> bool {
path.exists()
}
fn hard_link(&self, original: &Path, link: &Path) -> Result<(), VfsError> {
#[cfg(unix)]
{
fs::hard_link(original, link).map_err(|e| util::map_io_error(original, e))?;
}
#[cfg(not(unix))]
{
return Err(VfsError::Unsupported(
"hard_link not supported on non-Unix systems".to_string(),
));
}
Ok(())
}
}