use std::cmp::Ordering;
use async_trait::async_trait;
use crate::protocol::xdr::nfs3;
pub mod permissions;
#[derive(Default, Debug)]
pub struct DirEntrySimple {
pub fileid: nfs3::fileid3,
pub name: nfs3::filename3,
}
#[derive(Default, Debug)]
pub struct ReadDirSimpleResult {
pub entries: Vec<DirEntrySimple>,
pub end: bool,
}
#[derive(Default, Debug)]
pub struct DirEntry {
pub fileid: nfs3::fileid3,
pub name: nfs3::filename3,
pub attr: nfs3::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 }
}
}
pub enum Capabilities {
ReadOnly,
ReadWrite,
}
#[async_trait]
pub trait NFSFileSystem: Sync {
fn generation(&self) -> u64;
fn capabilities(&self) -> Capabilities;
fn root_dir(&self) -> nfs3::fileid3;
async fn lookup(
&self,
dirid: nfs3::fileid3,
filename: &nfs3::filename3,
) -> Result<nfs3::fileid3, nfs3::nfsstat3>;
async fn getattr(&self, id: nfs3::fileid3) -> Result<nfs3::fattr3, nfs3::nfsstat3>;
async fn setattr(
&self,
id: nfs3::fileid3,
setattr: nfs3::sattr3,
) -> Result<nfs3::fattr3, nfs3::nfsstat3>;
async fn read(
&self,
id: nfs3::fileid3,
offset: u64,
count: u32,
) -> Result<(Vec<u8>, bool), nfs3::nfsstat3>;
async fn write(
&self,
id: nfs3::fileid3,
offset: u64,
data: &[u8],
stable: nfs3::file::stable_how,
) -> Result<(nfs3::fattr3, nfs3::file::stable_how, nfs3::count3), nfs3::nfsstat3>;
async fn create(
&self,
dirid: nfs3::fileid3,
filename: &nfs3::filename3,
attr: nfs3::sattr3,
) -> Result<(nfs3::fileid3, nfs3::fattr3), nfs3::nfsstat3>;
async fn create_exclusive(
&self,
dirid: nfs3::fileid3,
filename: &nfs3::filename3,
verifier: nfs3::createverf3,
) -> Result<nfs3::fileid3, nfs3::nfsstat3>;
async fn mkdir(
&self,
dirid: nfs3::fileid3,
dirname: &nfs3::filename3,
) -> Result<(nfs3::fileid3, nfs3::fattr3), nfs3::nfsstat3>;
async fn remove(
&self,
dirid: nfs3::fileid3,
filename: &nfs3::filename3,
) -> Result<(), nfs3::nfsstat3>;
async fn rename(
&self,
from_dirid: nfs3::fileid3,
from_filename: &nfs3::filename3,
to_dirid: nfs3::fileid3,
to_filename: &nfs3::filename3,
) -> Result<(), nfs3::nfsstat3>;
async fn readdir(
&self,
dirid: nfs3::fileid3,
start_after: nfs3::fileid3,
max_entries: usize,
) -> Result<ReadDirResult, nfs3::nfsstat3>;
async fn readdir_index(
&self,
dirid: nfs3::fileid3,
start_index: usize,
max_entries: usize,
) -> Result<ReadDirResult, nfs3::nfsstat3> {
let request_count = start_index.saturating_add(max_entries);
let mut result = self.readdir(dirid, 0, request_count).await?;
if start_index > result.entries.len()
|| (start_index == result.entries.len() && !result.end)
{
return Err(nfs3::nfsstat3::NFS3ERR_BAD_COOKIE);
}
if start_index == 0 {
return Ok(result);
}
let entries = result.entries.split_off(start_index);
Ok(ReadDirResult { entries, end: result.end })
}
async fn readdir_simple(
&self,
dirid: nfs3::fileid3,
start_after: nfs3::fileid3,
count: usize,
) -> Result<ReadDirSimpleResult, nfs3::nfsstat3> {
Ok(ReadDirSimpleResult::from_readdir_result(
&self.readdir(dirid, start_after, count).await?,
))
}
async fn readdir_simple_index(
&self,
dirid: nfs3::fileid3,
start_index: usize,
count: usize,
) -> Result<ReadDirSimpleResult, nfs3::nfsstat3> {
Ok(ReadDirSimpleResult::from_readdir_result(
&self.readdir_index(dirid, start_index, count).await?,
))
}
async fn symlink(
&self,
dirid: nfs3::fileid3,
linkname: &nfs3::filename3,
symlink: &nfs3::nfspath3,
attr: &nfs3::sattr3,
) -> Result<(nfs3::fileid3, nfs3::fattr3), nfs3::nfsstat3>;
async fn readlink(&self, id: nfs3::fileid3) -> Result<nfs3::nfspath3, nfs3::nfsstat3>;
async fn link(
&self,
file_id: nfs3::fileid3,
link_dir_id: nfs3::fileid3,
link_name: &nfs3::filename3,
) -> Result<nfs3::fattr3, nfs3::nfsstat3>;
async fn mknod(
&self,
dir_id: nfs3::fileid3,
name: &nfs3::filename3,
ftype: nfs3::ftype3,
specdata: nfs3::specdata3,
attrs: &nfs3::sattr3,
) -> Result<(nfs3::fileid3, nfs3::fattr3), nfs3::nfsstat3>;
async fn commit(
&self,
file_id: nfs3::fileid3,
offset: u64,
count: u32,
) -> Result<nfs3::fattr3, nfs3::nfsstat3>;
async fn check_access(
&self,
id: nfs3::fileid3,
auth: &crate::protocol::xdr::rpc::auth_unix,
access: u32,
) -> Result<u32, nfs3::nfsstat3> {
let attr = self.getattr(id).await?;
Ok(permissions::access_mask(&attr, auth, self.capabilities(), access))
}
fn fsinfo_rtmax(&self) -> u32 {
1024 * 1024
}
fn fsinfo_rtpref(&self) -> u32 {
1024 * 124
}
fn fsinfo_rtmult(&self) -> u32 {
1024 * 1024
}
fn fsinfo_wtmax(&self) -> u32 {
1024 * 1024
}
fn fsinfo_wtpref(&self) -> u32 {
1024 * 1024
}
fn fsinfo_wtmult(&self) -> u32 {
1024 * 1024
}
fn fsinfo_dtpref(&self) -> u32 {
1024 * 1024
}
fn fsinfo_maxfilesize(&self) -> nfs3::size3 {
128 * 1024 * 1024 * 1024
}
fn fsinfo_time_delta(&self) -> nfs3::nfstime3 {
nfs3::nfstime3 { seconds: 0, nseconds: 1_000_000 }
}
fn fsinfo_properties(&self) -> u32 {
nfs3::fs::FSF_SYMLINK | nfs3::fs::FSF_HOMOGENEOUS | nfs3::fs::FSF_CANSETTIME
}
fn pathconf_linkmax(&self) -> u32 {
0
}
fn pathconf_name_max(&self) -> u32 {
32768
}
fn pathconf_no_trunc(&self) -> bool {
true
}
fn pathconf_chown_restricted(&self) -> bool {
true
}
fn pathconf_case_insensitive(&self) -> bool {
false
}
fn pathconf_case_preserving(&self) -> bool {
true
}
async fn fsinfo(
&self,
root_fileid: nfs3::fileid3,
) -> Result<nfs3::fs::fsinfo3, nfs3::nfsstat3> {
let dir_attr: nfs3::post_op_attr = self.getattr(root_fileid).await.ok();
let res = nfs3::fs::fsinfo3 {
obj_attributes: dir_attr,
rtmax: self.fsinfo_rtmax(),
rtpref: self.fsinfo_rtpref(),
rtmult: self.fsinfo_rtmult(),
wtmax: self.fsinfo_wtmax(),
wtpref: self.fsinfo_wtpref(),
wtmult: self.fsinfo_wtmult(),
dtpref: self.fsinfo_dtpref(),
maxfilesize: self.fsinfo_maxfilesize(),
time_delta: self.fsinfo_time_delta(),
properties: self.fsinfo_properties(),
};
Ok(res)
}
async fn fsstat(
&self,
root_fileid: nfs3::fileid3,
) -> Result<nfs3::fs::FSSTAT3resok, nfs3::nfsstat3> {
let dir_attr: nfs3::post_op_attr = self.getattr(root_fileid).await.ok();
Ok(nfs3::fs::FSSTAT3resok {
obj_attributes: dir_attr,
tbytes: 0,
fbytes: 0,
abytes: 0,
tfiles: 0,
ffiles: 0,
afiles: 0,
invarsec: 0,
})
}
fn id_to_fh(&self, id: nfs3::fileid3) -> nfs3::nfs_fh3 {
let gennum = self.generation();
let mut ret: Vec<u8> = Vec::new();
ret.extend_from_slice(&gennum.to_le_bytes());
ret.extend_from_slice(&id.to_le_bytes());
nfs3::nfs_fh3 { data: ret }
}
fn fh_to_id(&self, id: &nfs3::nfs_fh3) -> Result<nfs3::fileid3, nfs3::nfsstat3> {
if id.data.len() != 16 {
return Err(nfs3::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 = self.generation();
match gen.cmp(&gennum) {
Ordering::Less => Err(nfs3::nfsstat3::NFS3ERR_STALE),
Ordering::Greater => Err(nfs3::nfsstat3::NFS3ERR_BADHANDLE),
Ordering::Equal => Ok(id),
}
}
async fn path_to_id(&self, path: &[u8]) -> Result<nfs3::fileid3, nfs3::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 server_id(&self) -> nfs3::cookieverf3 {
self.generation().to_le_bytes()
}
}