use std::path::{Path, PathBuf};
use crate::error::RpcError;
pub const VF_FD_NULL: i32 = -1;
pub const VF_FD_CWD: i32 = -2;
pub const VF_FD_ABS: i32 = -3;
pub const VF_OFFSET_END: u64 = u64::MAX;
pub const VF_OFFSET_CUR: u64 = u64::MAX - 1;
pub const VF_ERR_RPC: u32 = 0xFFFF_FFFF;
pub const VF_ERR_UNSUPPORTED: u32 = 0xFFFF_FFFE;
pub const ERR_NOENT: u32 = 2;
pub const ERR_EBADF: u32 = 9;
pub const ERR_EXIST: u32 = 17;
pub const ERR_NOTDIR: u32 = 20;
pub const ERR_ISDIR: u32 = 21;
pub const ERR_INVAL: u32 = 22;
pub const NF4REG: u32 = 1;
pub const NF4DIR: u32 = 2;
pub const NF4LNK: u32 = 5;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VfError {
pub index: usize,
pub err_no: u32,
}
impl VfError {
pub fn failure(index: usize, err_no: u32) -> VfError {
VfError { index, err_no }
}
pub fn from_rpc(index: usize, e: RpcError) -> VfError {
VfError {
index,
err_no: e.status,
}
}
pub fn unsupported(index: usize) -> VfError {
VfError::failure(index, VF_ERR_UNSUPPORTED)
}
}
impl std::fmt::Display for VfError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "op {} failed: {}", self.index, self.err_no)
}
}
impl std::error::Error for VfError {}
pub type VfResult<T> = Result<T, VfError>;
pub type VfRes = VfResult<()>;
pub(crate) fn split_path(path: &str) -> VfResult<(&str, &str)> {
let trimmed = path.trim_matches('/');
if trimmed.is_empty() {
return Err(VfError::failure(0, ERR_NOENT));
}
match trimmed.rfind('/') {
Some(i) => Ok((&trimmed[..i], &trimmed[i + 1..])),
None => Ok(("", trimmed)),
}
}
pub(crate) fn join_path(dir: &str, name: &str) -> String {
if dir.is_empty() {
name.to_string()
} else {
format!("{}/{}", dir, name)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VfFileType {
Null,
Descriptor,
Path,
Handle,
Current,
Saved,
}
#[derive(Debug, Clone)]
pub struct VfFile {
pub ftype: VfFileType,
pub fd: i32,
pub path: Option<PathBuf>,
}
impl VfFile {
pub fn from_path(path: &str) -> VfFile {
let fd = if path.starts_with('/') {
VF_FD_ABS
} else {
VF_FD_CWD
};
VfFile {
ftype: VfFileType::Path,
fd,
path: Some(PathBuf::from(path)),
}
}
pub fn from_fd(fd: i32) -> VfFile {
VfFile {
ftype: VfFileType::Descriptor,
fd,
path: None,
}
}
pub fn current(relpath: Option<&str>) -> VfFile {
VfFile {
ftype: VfFileType::Current,
fd: -1,
path: relpath.map(PathBuf::from),
}
}
pub fn saved() -> VfFile {
VfFile {
ftype: VfFileType::Saved,
fd: -1,
path: None,
}
}
}
impl Default for VfFile {
fn default() -> VfFile {
VfFile {
ftype: VfFileType::Null,
fd: VF_FD_NULL,
path: None,
}
}
}
#[derive(Debug, Clone)]
pub struct VfIoVec {
pub file: VfFile,
pub offset: u64,
pub length: usize,
pub data: Vec<u8>,
pub is_creation: bool,
pub is_failure: bool,
pub is_eof: bool,
pub is_write_stable: bool,
}
impl VfIoVec {
pub fn new(file: VfFile, offset: u64, length: usize, data: Vec<u8>) -> VfIoVec {
VfIoVec {
file,
offset,
length,
data,
is_creation: false,
is_failure: false,
is_eof: false,
is_write_stable: true,
}
}
pub fn from_path(path: &str, offset: u64, length: usize, data: Vec<u8>) -> VfIoVec {
VfIoVec::new(VfFile::from_path(path), offset, length, data)
}
pub fn from_fd(fd: i32, offset: u64, length: usize, data: Vec<u8>) -> VfIoVec {
VfIoVec::new(VfFile::from_fd(fd), offset, length, data)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ExtentPair {
pub src_path: String,
pub dst_path: String,
pub src_offset: u64,
pub dst_offset: u64,
pub length: u64,
}
impl ExtentPair {
pub fn new(
src_path: &str,
src_offset: u64,
dst_path: &str,
dst_offset: u64,
length: u64,
) -> ExtentPair {
ExtentPair {
src_path: src_path.to_string(),
dst_path: dst_path.to_string(),
src_offset,
dst_offset,
length,
}
}
}
#[derive(Debug, Clone)]
pub struct Adb {
pub path: String,
pub adb_offset: u64,
pub adb_block_size: u64,
pub adb_block_count: usize,
pub adb_reloff_blocknum: u64,
pub adb_block_num: u64,
pub adb_reloff_pattern: u64,
pub adb_pattern_size: usize,
pub adb_pattern_data: Vec<u8>,
}
impl Adb {
pub fn blocknum_only(
path: &str,
offset: u64,
block_size: u64,
block_count: usize,
reloff_blocknum: u64,
first_adbn: u64,
) -> Adb {
Adb {
path: path.to_string(),
adb_offset: offset,
adb_block_size: block_size,
adb_block_count: block_count,
adb_reloff_blocknum: reloff_blocknum,
adb_block_num: first_adbn,
adb_reloff_pattern: u64::MAX,
adb_pattern_size: 0,
adb_pattern_data: Vec::new(),
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct AttrMask {
pub has_mode: bool,
pub has_size: bool,
pub has_nlink: bool,
pub has_fileid: bool,
pub has_blocks: bool,
pub has_uid: bool,
pub has_gid: bool,
pub has_rdev: bool,
pub has_atime: bool,
pub has_mtime: bool,
pub has_ctime: bool,
pub has_named_attr: bool,
}
impl AttrMask {
pub fn all() -> AttrMask {
AttrMask {
has_mode: true,
has_size: true,
has_nlink: true,
has_fileid: true,
has_blocks: true,
has_uid: true,
has_gid: true,
has_rdev: true,
has_atime: true,
has_mtime: true,
has_ctime: true,
has_named_attr: true,
}
}
}
#[derive(Debug, Clone)]
pub struct WalkEntry {
pub path: String,
pub entries: Vec<VfAttrs>,
}
#[derive(Debug, Clone, Default)]
pub struct VfAttrs {
pub file: VfFile,
pub masks: AttrMask,
pub ftype: u32,
pub mode: u32,
pub size: u64,
pub nlink: u32,
pub fileid: u64,
pub uid: u32,
pub gid: u32,
pub rdev: u64,
pub blocks: u64,
pub mtime_sec: i64,
pub mtime_nsec: u32,
pub atime_sec: i64,
pub atime_nsec: u32,
pub ctime_sec: i64,
pub ctime_nsec: u32,
pub has_named_attr: bool,
}
pub trait VecFs {
fn abs_path(&self, path: &str) -> String;
fn open_by_path(
&mut self,
dirfd: i32,
pathname: &str,
flags: i32,
mode: u32,
) -> VfResult<VfFile>;
fn close(&mut self, tcf: &VfFile) -> VfResult<()>;
fn chdir(&mut self, path: &str) -> VfResult<()>;
fn getcwd(&self) -> String;
fn readv(&mut self, reads: &mut [VfIoVec]) -> VfRes;
fn writev(&mut self, writes: &mut [VfIoVec]) -> VfRes;
fn fseek(&mut self, tcf: &mut VfFile, offset: i64, whence: i32) -> VfResult<i64>;
fn getattrsv(&mut self, attrs: &mut [VfAttrs]) -> VfRes;
fn setattrsv(&mut self, attrs: &[VfAttrs]) -> VfRes;
fn listdir(
&mut self,
dir: &str,
masks: AttrMask,
max_count: usize,
recursive: bool,
) -> VfResult<Vec<VfAttrs>>;
fn walk(
&mut self,
root: &str,
masks: AttrMask,
sort: &dyn Fn(&str, &mut Vec<VfAttrs>),
) -> VfResult<Vec<WalkEntry>> {
fn rec<F: VecFs + ?Sized>(
fs: &mut F,
dir: &str,
masks: AttrMask,
sort: &dyn Fn(&str, &mut Vec<VfAttrs>),
out: &mut Vec<WalkEntry>,
) -> VfResult<()> {
let mut entries = fs.listdir(dir, masks, 0, false)?;
sort(dir, &mut entries);
let subdirs: Vec<String> = entries
.iter()
.filter(|e| e.ftype == NF4DIR)
.filter_map(|e| {
e.file
.path
.as_ref()
.map(|p| p.to_string_lossy().into_owned())
})
.collect();
out.push(WalkEntry {
path: dir.to_string(),
entries,
});
for s in subdirs {
rec(fs, &s, masks, sort, out)?;
}
Ok(())
}
let mut out = Vec::new();
rec(self, root, masks, sort, &mut out)?;
Ok(out)
}
fn renamev(&mut self, pairs: &[(VfFile, VfFile)]) -> VfRes;
fn removev(&mut self, files: &[VfFile]) -> VfRes;
fn mkdirv(&mut self, dirs: &[VfAttrs]) -> VfRes;
fn symlinkv(&mut self, oldpaths: &[&str], newpaths: &[&str]) -> VfRes;
fn readlinkv(&mut self, paths: &[&str]) -> VfResult<Vec<Vec<u8>>>;
fn hardlinkv(&mut self, oldpaths: &[&str], newpaths: &[&str]) -> VfRes;
fn dupv(&mut self, pairs: &[ExtentPair]) -> VfRes;
fn write_adb(&mut self, patterns: &mut [Adb]) -> VfRes;
fn rm(&mut self, objs: &[&str], recursive: bool) -> VfRes;
fn cp_recursive(
&mut self,
src_dir: &str,
dst: &str,
symlinks: bool,
use_server_side_copy: bool,
) -> VfRes;
fn open(&mut self, pathname: &str, flags: i32, mode: u32) -> VfResult<VfFile> {
self.open_by_path(VF_FD_CWD, pathname, flags, mode)
}
fn openv(&mut self, paths: &[&str], flags: &[i32], modes: &[u32]) -> VfResult<Vec<VfFile>> {
if paths.len() != flags.len() || paths.len() != modes.len() {
return Err(VfError::failure(0, ERR_INVAL));
}
let mut out = Vec::with_capacity(paths.len());
for (i, p) in paths.iter().enumerate() {
out.push(self.open(p, flags[i], modes[i]).map_err(|mut e| {
e.index = i;
e
})?);
}
Ok(out)
}
fn openv_simple(&mut self, paths: &[&str], flags: i32, mode: u32) -> VfResult<Vec<VfFile>> {
let flags_v = vec![flags; paths.len()];
let modes_v = vec![mode; paths.len()];
self.openv(paths, &flags_v, &modes_v)
}
fn closev(&mut self, files: &[VfFile]) -> VfRes {
for (i, f) in files.iter().enumerate() {
self.close(f).map_err(|mut e| {
e.index = i;
e
})?;
}
Ok(())
}
fn lgetattrsv(&mut self, attrs: &mut [VfAttrs]) -> VfRes {
self.getattrsv(attrs)
}
fn lsetattrsv(&mut self, attrs: &[VfAttrs]) -> VfRes {
self.setattrsv(attrs)
}
fn stat(&mut self, path: &str) -> VfResult<VfAttrs> {
let mut a = VfAttrs {
file: VfFile::from_path(path),
masks: AttrMask {
has_mode: true,
has_size: true,
has_nlink: true,
has_fileid: true,
..AttrMask::default()
},
..VfAttrs::default()
};
self.getattrsv(std::slice::from_mut(&mut a))?;
Ok(a)
}
fn lstat(&mut self, path: &str) -> VfResult<VfAttrs> {
self.stat(path)
}
fn fstat(&mut self, tcf: &VfFile) -> VfResult<VfAttrs> {
let mut a = VfAttrs {
file: tcf.clone(),
masks: AttrMask {
has_mode: true,
has_size: true,
has_nlink: true,
has_fileid: true,
..AttrMask::default()
},
..VfAttrs::default()
};
self.getattrsv(std::slice::from_mut(&mut a))?;
Ok(a)
}
fn exists(&mut self, path: &str) -> bool {
self.lstat(path).is_ok()
}
fn file_type(&mut self, path: &str) -> VfResult<u32> {
Ok(self.stat(path)?.ftype)
}
fn listdirv<F>(
&mut self,
dirs: &[&str],
masks: AttrMask,
max_entries: usize,
recursive: bool,
cb: &mut F,
) -> VfRes
where
F: FnMut(&VfAttrs, &str) -> bool,
{
for (i, d) in dirs.iter().enumerate() {
let entries = self
.listdir(d, masks, max_entries, recursive)
.map_err(|mut e| {
e.index = i;
e
})?;
for e in &entries {
if !cb(e, d) {
return Ok(());
}
}
}
Ok(())
}
fn unlink(&mut self, pathname: &str) -> VfResult<()> {
self.removev(&[VfFile::from_path(pathname)])
}
fn unlinkv(&mut self, pathnames: &[&str]) -> VfRes {
let files: Vec<VfFile> = pathnames.iter().map(|p| VfFile::from_path(p)).collect();
self.removev(&files)
}
fn mkdir(&mut self, path: &str, mode: u32) -> VfResult<()> {
let a = VfAttrs {
file: VfFile::from_path(path),
masks: AttrMask {
has_mode: true,
..AttrMask::default()
},
mode,
..VfAttrs::default()
};
self.mkdirv(std::slice::from_ref(&a))
}
fn symlink(&mut self, oldpath: &str, newpath: &str) -> VfResult<()> {
self.symlinkv(
std::slice::from_ref(&oldpath),
std::slice::from_ref(&newpath),
)
}
fn readlink(&mut self, path: &str) -> VfResult<Vec<u8>> {
let mut v = self.readlinkv(std::slice::from_ref(&path))?;
Ok(v.remove(0))
}
fn ldupv(&mut self, pairs: &[ExtentPair]) -> VfRes {
self.dupv(pairs)
}
fn copyv(&mut self, pairs: &[ExtentPair]) -> VfRes {
self.dupv(pairs)
}
fn lcopyv(&mut self, pairs: &[ExtentPair]) -> VfRes {
self.dupv(pairs)
}
fn ensure_dir(&mut self, dir: &str, mode: u32) -> VfResult<()> {
let rel = self.abs_path(dir);
let mut so_far = String::new();
for comp in rel.split('/').filter(|c| !c.is_empty()) {
so_far = join_path(&so_far, comp);
let full = format!("/{}", so_far);
if !self.exists(&full) {
self.mkdir(&full, mode)?;
}
}
Ok(())
}
}
pub fn rm_recursive(fs: &mut impl VecFs, dir: &str) -> VfRes {
fs.rm(&[dir], true)
}
pub fn okay(res: &VfRes) -> bool {
res.is_ok()
}
pub fn file_from_path(path: &str) -> VfFile {
VfFile::from_path(path)
}
#[allow(unused)]
fn _path_ref(_p: &Path) {}