use std::fs::Metadata;
use std::fs::Permissions;
#[cfg(unix)]
use std::os::unix::fs::{MetadataExt, PermissionsExt};
use std::path::Path;
use tokio::fs::OpenOptions;
use tracing::debug;
use crate::protocol::xdr::nfs3;
#[cfg(any(target_os = "linux", target_os = "macos"))]
pub fn metadata_differ(lhs: &Metadata, rhs: &Metadata) -> bool {
lhs.ino() != rhs.ino()
|| lhs.mtime() != rhs.mtime()
|| lhs.len() != rhs.len()
|| lhs.file_type() != rhs.file_type()
}
pub fn fattr3_differ(lhs: &nfs3::fattr3, rhs: &nfs3::fattr3) -> bool {
lhs.fileid != rhs.fileid
|| lhs.mtime.seconds != rhs.mtime.seconds
|| lhs.mtime.nseconds != rhs.mtime.nseconds
|| lhs.ctime.seconds != rhs.ctime.seconds
|| lhs.ctime.nseconds != rhs.ctime.nseconds
|| lhs.size != rhs.size
|| lhs.nlink != rhs.nlink
|| lhs.ftype as u32 != rhs.ftype as u32
}
pub fn exists_no_traverse(path: &Path) -> bool {
path.symlink_metadata().is_ok()
}
fn mode_unmask(mode: u32) -> u32 {
let mode = Permissions::from_mode(mode);
mode.mode() & 0x1FF
}
pub fn metadata_to_fattr3(fid: nfs3::fileid3, meta: &Metadata) -> nfs3::fattr3 {
let size = meta.size();
let file_mode = mode_unmask(meta.mode());
if meta.is_file() {
nfs3::fattr3 {
ftype: nfs3::ftype3::NF3REG,
mode: file_mode,
nlink: meta.nlink() as u32,
uid: meta.uid(),
gid: meta.gid(),
size,
used: size,
rdev: nfs3::specdata3::default(),
fsid: 0,
fileid: fid,
atime: nfs3::nfstime3 {
seconds: meta.atime() as u32,
nseconds: meta.atime_nsec() as u32,
},
mtime: nfs3::nfstime3 {
seconds: meta.mtime() as u32,
nseconds: meta.mtime_nsec() as u32,
},
ctime: nfs3::nfstime3 {
seconds: meta.ctime() as u32,
nseconds: meta.ctime_nsec() as u32,
},
}
} else if meta.is_symlink() {
nfs3::fattr3 {
ftype: nfs3::ftype3::NF3LNK,
mode: file_mode,
nlink: meta.nlink() as u32,
uid: meta.uid(),
gid: meta.gid(),
size,
used: size,
rdev: nfs3::specdata3::default(),
fsid: 0,
fileid: fid,
atime: nfs3::nfstime3 {
seconds: meta.atime() as u32,
nseconds: meta.atime_nsec() as u32,
},
mtime: nfs3::nfstime3 {
seconds: meta.mtime() as u32,
nseconds: meta.mtime_nsec() as u32,
},
ctime: nfs3::nfstime3 {
seconds: meta.ctime() as u32,
nseconds: meta.ctime_nsec() as u32,
},
}
} else {
nfs3::fattr3 {
ftype: nfs3::ftype3::NF3DIR,
mode: file_mode,
nlink: meta.nlink() as u32,
uid: meta.uid(),
gid: meta.gid(),
size,
used: size,
rdev: nfs3::specdata3::default(),
fsid: 0,
fileid: fid,
atime: nfs3::nfstime3 {
seconds: meta.atime() as u32,
nseconds: meta.atime_nsec() as u32,
},
mtime: nfs3::nfstime3 {
seconds: meta.mtime() as u32,
nseconds: meta.mtime_nsec() as u32,
},
ctime: nfs3::nfstime3 {
seconds: meta.ctime() as u32,
nseconds: meta.ctime_nsec() as u32,
},
}
}
}
pub async fn path_setattr(path: &Path, setattr: &nfs3::sattr3) -> Result<(), nfs3::nfsstat3> {
match setattr.atime {
nfs3::set_atime::SET_TO_SERVER_TIME => {
let _ = filetime::set_file_atime(path, filetime::FileTime::now());
}
nfs3::set_atime::SET_TO_CLIENT_TIME(time) => {
let _ = filetime::set_file_atime(path, time.into());
}
_ => {}
}
match setattr.mtime {
nfs3::set_mtime::SET_TO_SERVER_TIME => {
let _ = filetime::set_file_mtime(path, filetime::FileTime::now());
}
nfs3::set_mtime::SET_TO_CLIENT_TIME(time) => {
let _ = filetime::set_file_mtime(path, time.into());
}
_ => {}
}
if let nfs3::set_mode3::Some(mode) = setattr.mode {
debug!(" -- set permissions {:?} {:?}", path, mode);
let mode = mode_unmask(mode);
let _ = std::fs::set_permissions(path, Permissions::from_mode(mode));
}
if setattr.uid.is_some() {
debug!("Set uid not implemented");
}
if setattr.gid.is_some() {
debug!("Set gid not implemented");
}
if let nfs3::set_size3::Some(size3) = setattr.size {
let file = OpenOptions::new()
.read(true)
.write(true)
.truncate(false)
.open(path)
.await
.or(Err(nfs3::nfsstat3::NFS3ERR_IO))?;
debug!(" -- set size {:?} {:?}", path, size3);
file.set_len(size3).await.or(Err(nfs3::nfsstat3::NFS3ERR_IO))?;
}
Ok(())
}
pub async fn file_setattr(
file: &std::fs::File,
setattr: &nfs3::sattr3,
) -> Result<(), nfs3::nfsstat3> {
if let nfs3::set_mode3::Some(mode) = setattr.mode {
debug!(" -- set permissions {:?}", mode);
let mode = mode_unmask(mode);
let _ = file.set_permissions(Permissions::from_mode(mode));
}
if let nfs3::set_size3::Some(size3) = setattr.size {
debug!(" -- set size {:?}", size3);
file.set_len(size3).or(Err(nfs3::nfsstat3::NFS3ERR_IO))?;
}
Ok(())
}