use crate::nfs::*;
use crate::nfs;
use async_trait::async_trait;
use std::cmp::Ordering;
use std::sync::Once;
use std::time::SystemTime;
#[derive(Default, Debug)]
pub struct DirEntrySimple {
pub fileid: fileid3,
pub name: filename3,
}
#[derive(Default, Debug)]
pub struct ReadDirSimpleResult {
pub entries: Vec<DirEntrySimple>,
pub end: bool,
}
#[derive(Default, Debug)]
pub struct DirEntry {
pub fileid: fileid3,
pub name: filename3,
pub attr: fattr3,
}
#[derive(Default, Debug)]
pub struct ReadDirResult {
pub entries: Vec<DirEntry>,
pub end: bool,
}
impl ReadDirSimpleResult {
fn from_readdir_result(result: &ReadDirResult) -> ReadDirSimpleResult {
let entries: Vec<DirEntrySimple> = result
.entries
.iter()
.map(|e| DirEntrySimple {
fileid: e.fileid,
name: e.name.clone(),
})
.collect();
ReadDirSimpleResult {
entries,
end: result.end,
}
}
}
static mut GENERATION_NUMBER: u64 = 0;
static GENERATION_NUMBER_INIT: Once = Once::new();
fn get_generation_number() -> u64 {
unsafe {
GENERATION_NUMBER_INIT.call_once(|| {
GENERATION_NUMBER = SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis() as u64;
});
GENERATION_NUMBER
}
}
pub enum VFSCapabilities {
ReadOnly,
ReadWrite,
}
#[async_trait]
pub trait NFSFileSystem: Sync {
fn capabilities(&self) -> VFSCapabilities;
fn root_dir(&self) -> fileid3;
async fn lookup(&self, dirid: fileid3, filename: &filename3) -> Result<fileid3, nfsstat3>;
async fn getattr(&self, id: fileid3) -> Result<fattr3, nfsstat3>;
async fn setattr(&self, id: fileid3, setattr: sattr3) -> Result<fattr3, nfsstat3>;
async fn read(&self, id: fileid3, offset: u64, count: u32)
-> Result<(Vec<u8>, bool), nfsstat3>;
async fn write(&self, id: fileid3, offset: u64, data: &[u8]) -> Result<fattr3, nfsstat3>;
async fn create(
&self,
dirid: fileid3,
filename: &filename3,
attr: sattr3,
) -> Result<(fileid3, fattr3), nfsstat3>;
async fn create_exclusive(
&self,
dirid: fileid3,
filename: &filename3,
) -> Result<fileid3, nfsstat3>;
async fn mkdir(
&self,
dirid: fileid3,
dirname: &filename3,
) -> Result<(fileid3, fattr3), nfsstat3>;
async fn remove(&self, dirid: fileid3, filename: &filename3) -> Result<(), nfsstat3>;
async fn rename(
&self,
from_dirid: fileid3,
from_filename: &filename3,
to_dirid: fileid3,
to_filename: &filename3,
) -> Result<(), nfsstat3>;
async fn readdir(
&self,
dirid: fileid3,
start_after: fileid3,
max_entries: usize,
) -> Result<ReadDirResult, nfsstat3>;
async fn readdir_simple(
&self,
dirid: fileid3,
count: usize,
) -> Result<ReadDirSimpleResult, nfsstat3> {
Ok(ReadDirSimpleResult::from_readdir_result(
&self.readdir(dirid, 0, count).await?,
))
}
async fn symlink(
&self,
dirid: fileid3,
linkname: &filename3,
symlink: &nfspath3,
attr: &sattr3,
) -> Result<(fileid3, fattr3), nfsstat3>;
async fn readlink(&self, id: fileid3) -> Result<nfspath3, nfsstat3>;
async fn fsinfo(
&self,
root_fileid: fileid3,
) -> Result<fsinfo3, nfsstat3> {
let dir_attr: nfs::post_op_attr = match self.getattr(root_fileid).await {
Ok(v) => nfs::post_op_attr::attributes(v),
Err(_) => nfs::post_op_attr::Void,
};
let res = fsinfo3 {
obj_attributes: dir_attr,
rtmax: 1024 * 1024,
rtpref: 1024 * 124,
rtmult: 1024 * 1024,
wtmax: 1024 * 1024,
wtpref: 1024 * 1024,
wtmult: 1024 * 1024,
dtpref: 1024 * 1024,
maxfilesize: 128 * 1024 * 1024 * 1024,
time_delta: nfs::nfstime3 {
seconds: 0,
nseconds: 1000000,
},
properties: nfs::FSF_SYMLINK | nfs::FSF_HOMOGENEOUS | nfs::FSF_CANSETTIME,
};
Ok(res)
}
fn id_to_fh(&self, id: fileid3) -> nfs_fh3 {
let gennum = get_generation_number();
let mut ret: Vec<u8> = Vec::new();
ret.extend_from_slice(&gennum.to_le_bytes());
ret.extend_from_slice(&id.to_le_bytes());
nfs_fh3 { data: ret }
}
fn fh_to_id(&self, id: &nfs_fh3) -> Result<fileid3, nfsstat3> {
if id.data.len() != 16 {
return Err(nfsstat3::NFS3ERR_BADHANDLE);
}
let gen = u64::from_le_bytes(id.data[0..8].try_into().unwrap());
let id = u64::from_le_bytes(id.data[8..16].try_into().unwrap());
let gennum = get_generation_number();
match gen.cmp(&gennum) {
Ordering::Less => Err(nfsstat3::NFS3ERR_STALE),
Ordering::Greater => Err(nfsstat3::NFS3ERR_BADHANDLE),
Ordering::Equal => Ok(id),
}
}
async fn path_to_id(&self, path: &[u8]) -> Result<fileid3, nfsstat3> {
let splits = path.split(|&r| r == b'/');
let mut fid = self.root_dir();
for component in splits {
if component.is_empty() {
continue;
}
fid = self.lookup(fid, &component.into()).await?;
}
Ok(fid)
}
fn serverid(&self) -> cookieverf3 {
let gennum = get_generation_number();
gennum.to_le_bytes()
}
}