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_setattr(
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 args = deserialize::<nfs3::SETATTR3args>(input)?;
debug!("nfsproc3_setattr({:?},{:?}) ", xid, args);
let id = context.vfs.fh_to_id(&args.object);
if let Err(stat) = id {
xdr::rpc::make_success_reply(xid).serialize(output)?;
stat.serialize(output)?;
nfs3::wcc_data::default().serialize(output)?;
return Ok(());
}
let id = id.unwrap();
let current_attr = match context.vfs.getattr(id).await {
Ok(v) => v,
Err(stat) => {
xdr::rpc::make_success_reply(xid).serialize(output)?;
stat.serialize(output)?;
nfs3::wcc_data::default().serialize(output)?;
return Ok(());
}
};
let pre_op_attr = nfs3::pre_op_attr::Some(nfs3::wcc_attr {
size: current_attr.size,
mtime: current_attr.mtime,
ctime: current_attr.ctime,
});
let ctime = current_attr.ctime;
let requires_modify = args.new_attribute.size.is_some();
if requires_modify {
match context.vfs.check_access(id, &context.auth, nfs3::ACCESS3_MODIFY).await {
Ok(granted) if granted & nfs3::ACCESS3_MODIFY != 0 => {}
Ok(_) => {
xdr::rpc::make_success_reply(xid).serialize(output)?;
nfs3::nfsstat3::NFS3ERR_ACCES.serialize(output)?;
nfs3::wcc_data {
before: pre_op_attr,
after: nfs3::post_op_attr::Some(current_attr),
}
.serialize(output)?;
return Ok(());
}
Err(stat) => {
xdr::rpc::make_success_reply(xid).serialize(output)?;
stat.serialize(output)?;
nfs3::wcc_data {
before: pre_op_attr,
after: nfs3::post_op_attr::Some(current_attr),
}
.serialize(output)?;
return Ok(());
}
}
}
if let nfs3::sattrguard3::Some(c) = args.guard {
if c.seconds != ctime.seconds || c.nseconds != ctime.nseconds {
xdr::rpc::make_success_reply(xid).serialize(output)?;
nfs3::nfsstat3::NFS3ERR_NOT_SYNC.serialize(output)?;
nfs3::wcc_data::default().serialize(output)?;
return Ok(());
}
}
match context.vfs.setattr(id, args.new_attribute).await {
Ok(post_op_attr) => {
debug!(" setattr success {:?} --> {:?}", xid, post_op_attr);
let wcc_res = nfs3::wcc_data {
before: pre_op_attr,
after: nfs3::post_op_attr::Some(post_op_attr),
};
xdr::rpc::make_success_reply(xid).serialize(output)?;
nfs3::nfsstat3::NFS3_OK.serialize(output)?;
wcc_res.serialize(output)?;
}
Err(stat) => {
error!("setattr error {:?} --> {:?}", xid, stat);
xdr::rpc::make_success_reply(xid).serialize(output)?;
stat.serialize(output)?;
nfs3::wcc_data::default().serialize(output)?;
}
}
Ok(())
}