use std::io::{Read, Write};
use tracing::{debug, error, warn};
use crate::protocol::rpc;
use crate::protocol::xdr::{self, deserialize, nfs3, Serialize};
use crate::vfs;
pub async fn nfsproc3_rename(
xid: u32,
input: &mut impl Read,
output: &mut impl Write,
context: &rpc::Context,
) -> Result<(), anyhow::Error> {
if !matches!(context.vfs.capabilities(), vfs::Capabilities::ReadWrite) {
warn!("No write capabilities.");
xdr::rpc::make_success_reply(xid).serialize(output)?;
nfs3::nfsstat3::NFS3ERR_ROFS.serialize(output)?;
nfs3::wcc_data::default().serialize(output)?;
return Ok(());
}
let fromdirops = deserialize::<nfs3::diropargs3>(input)?;
let todirops = deserialize::<nfs3::diropargs3>(input)?;
debug!("nfsproc3_rename({:?}, {:?}, {:?}) ", xid, fromdirops, todirops);
let from_dirid = context.vfs.fh_to_id(&fromdirops.dir);
if let Err(stat) = from_dirid {
xdr::rpc::make_success_reply(xid).serialize(output)?;
stat.serialize(output)?;
nfs3::wcc_data::default().serialize(output)?;
nfs3::wcc_data::default().serialize(output)?;
error!("Directory does not exist");
return Ok(());
}
let to_dirid = context.vfs.fh_to_id(&todirops.dir);
if let Err(stat) = to_dirid {
xdr::rpc::make_success_reply(xid).serialize(output)?;
stat.serialize(output)?;
nfs3::wcc_data::default().serialize(output)?;
nfs3::wcc_data::default().serialize(output)?;
error!("Directory does not exist");
return Ok(());
}
let from_dirid = from_dirid.unwrap();
let to_dirid = to_dirid.unwrap();
let from_dir_attr = match context.vfs.getattr(from_dirid).await {
Ok(v) => v,
Err(stat) => {
error!("Cannot stat directory");
xdr::rpc::make_success_reply(xid).serialize(output)?;
stat.serialize(output)?;
nfs3::wcc_data::default().serialize(output)?;
nfs3::wcc_data::default().serialize(output)?;
return Ok(());
}
};
let pre_from_dir_attr = nfs3::pre_op_attr::Some(nfs3::wcc_attr {
size: from_dir_attr.size,
mtime: from_dir_attr.mtime,
ctime: from_dir_attr.ctime,
});
let to_dir_attr = match context.vfs.getattr(to_dirid).await {
Ok(v) => v,
Err(stat) => {
error!("Cannot stat directory");
xdr::rpc::make_success_reply(xid).serialize(output)?;
stat.serialize(output)?;
nfs3::wcc_data::default().serialize(output)?;
nfs3::wcc_data::default().serialize(output)?;
return Ok(());
}
};
let pre_to_dir_attr = nfs3::pre_op_attr::Some(nfs3::wcc_attr {
size: to_dir_attr.size,
mtime: to_dir_attr.mtime,
ctime: to_dir_attr.ctime,
});
let from_access =
context.vfs.check_access(from_dirid, &context.auth, nfs3::ACCESS3_MODIFY).await;
let to_access = context.vfs.check_access(to_dirid, &context.auth, nfs3::ACCESS3_MODIFY).await;
match (from_access, to_access) {
(Ok(from_granted), Ok(to_granted))
if from_granted & nfs3::ACCESS3_MODIFY != 0
&& to_granted & nfs3::ACCESS3_MODIFY != 0 => {}
(Ok(_), Ok(_)) => {
xdr::rpc::make_success_reply(xid).serialize(output)?;
nfs3::nfsstat3::NFS3ERR_ACCES.serialize(output)?;
nfs3::wcc_data {
before: pre_from_dir_attr,
after: nfs3::post_op_attr::Some(from_dir_attr),
}
.serialize(output)?;
nfs3::wcc_data {
before: pre_to_dir_attr,
after: nfs3::post_op_attr::Some(to_dir_attr),
}
.serialize(output)?;
return Ok(());
}
(Err(stat), _) | (_, Err(stat)) => {
xdr::rpc::make_success_reply(xid).serialize(output)?;
stat.serialize(output)?;
nfs3::wcc_data {
before: pre_from_dir_attr,
after: nfs3::post_op_attr::Some(from_dir_attr),
}
.serialize(output)?;
nfs3::wcc_data {
before: pre_to_dir_attr,
after: nfs3::post_op_attr::Some(to_dir_attr),
}
.serialize(output)?;
return Ok(());
}
}
let res = context.vfs.rename(from_dirid, &fromdirops.name, to_dirid, &todirops.name).await;
let post_from_dir_attr = context.vfs.getattr(from_dirid).await.ok();
let post_to_dir_attr = context.vfs.getattr(to_dirid).await.ok();
let from_wcc_res = nfs3::wcc_data { before: pre_from_dir_attr, after: post_from_dir_attr };
let to_wcc_res = nfs3::wcc_data { before: pre_to_dir_attr, after: post_to_dir_attr };
match res {
Ok(()) => {
debug!("rename success");
xdr::rpc::make_success_reply(xid).serialize(output)?;
nfs3::nfsstat3::NFS3_OK.serialize(output)?;
from_wcc_res.serialize(output)?;
to_wcc_res.serialize(output)?;
}
Err(e) => {
error!("rename error {:?} --> {:?}", xid, e);
xdr::rpc::make_success_reply(xid).serialize(output)?;
e.serialize(output)?;
from_wcc_res.serialize(output)?;
to_wcc_res.serialize(output)?;
}
}
Ok(())
}