use std::io::{Read, Write};
use num_traits::FromPrimitive;
use tracing::{debug, error, warn};
use crate::protocol::rpc;
use crate::protocol::xdr::{self, deserialize, nfs3, Serialize};
use crate::vfs;
pub async fn nfsproc3_write(
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::file::WRITE3args>(input)?;
debug!("nfsproc3_write({:?},...) ", xid);
if args.data.len() != args.count as usize {
xdr::rpc::garbage_args_reply_message(xid).serialize(output)?;
return Ok(());
}
let id = context.vfs.fh_to_id(&args.file);
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 pre_attr = context.vfs.getattr(id).await.ok();
let pre_obj_attr =
pre_attr.map(|v| nfs3::wcc_attr { size: v.size, mtime: v.mtime, ctime: v.ctime });
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_obj_attr, after: nfs3::post_op_attr::None }
.serialize(output)?;
return Ok(());
}
Err(stat) => {
xdr::rpc::make_success_reply(xid).serialize(output)?;
stat.serialize(output)?;
nfs3::wcc_data { before: pre_obj_attr, after: nfs3::post_op_attr::None }
.serialize(output)?;
return Ok(());
}
}
let stable = match nfs3::file::stable_how::from_u32(args.stable) {
Some(stable) => stable,
None => {
xdr::rpc::garbage_args_reply_message(xid).serialize(output)?;
return Ok(());
}
};
match context.vfs.write(id, args.offset, &args.data, stable).await {
Ok((fattr, committed, count)) => {
debug!("write success {:?} --> {:?}", xid, fattr);
let res = nfs3::file::WRITE3resok {
file_wcc: nfs3::wcc_data {
before: pre_obj_attr,
after: nfs3::post_op_attr::Some(fattr),
},
count,
committed,
verf: context.vfs.server_id(),
};
xdr::rpc::make_success_reply(xid).serialize(output)?;
nfs3::nfsstat3::NFS3_OK.serialize(output)?;
res.serialize(output)?;
}
Err(stat) => {
error!("write error {:?} --> {:?}", xid, stat);
xdr::rpc::make_success_reply(xid).serialize(output)?;
stat.serialize(output)?;
nfs3::wcc_data::default().serialize(output)?;
}
}
Ok(())
}