use std::io::{Read, Write};
use tracing::{debug, error, warn};
use crate::protocol::rpc;
use crate::protocol::xdr::{self, deserialize, nfs3, Deserialize, Serialize};
use crate::vfs;
pub async fn nfsproc3_create(
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 dirops = deserialize::<nfs3::diropargs3>(input)?;
let createhow = deserialize::<nfs3::createmode3>(input)?;
debug!("nfsproc3_create({:?}, {:?}, {:?}) ", xid, dirops, createhow);
let dirid = context.vfs.fh_to_id(&dirops.dir);
if let Err(stat) = dirid {
xdr::rpc::make_success_reply(xid).serialize(output)?;
stat.serialize(output)?;
nfs3::wcc_data::default().serialize(output)?;
error!("Directory does not exist");
return Ok(());
}
let dirid = dirid.unwrap();
let dir_attr = match context.vfs.getattr(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)?;
return Ok(());
}
};
let pre_dir_attr = nfs3::pre_op_attr::Some(nfs3::wcc_attr {
size: dir_attr.size,
mtime: dir_attr.mtime,
ctime: dir_attr.ctime,
});
match context.vfs.check_access(dirid, &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_dir_attr, after: nfs3::post_op_attr::Some(dir_attr) }
.serialize(output)?;
return Ok(());
}
Err(stat) => {
xdr::rpc::make_success_reply(xid).serialize(output)?;
stat.serialize(output)?;
nfs3::wcc_data { before: pre_dir_attr, after: nfs3::post_op_attr::Some(dir_attr) }
.serialize(output)?;
return Ok(());
}
}
let mut target_attributes = nfs3::sattr3::default();
let mut create_verifier: Option<nfs3::createverf3> = None;
match createhow {
nfs3::createmode3::UNCHECKED => {
target_attributes.deserialize(input)?;
debug!("create unchecked {:?}", target_attributes);
}
nfs3::createmode3::GUARDED => {
target_attributes.deserialize(input)?;
debug!("create guarded {:?}", target_attributes);
if context.vfs.lookup(dirid, &dirops.name).await.is_ok() {
let post_dir_attr = context.vfs.getattr(dirid).await.ok();
xdr::rpc::make_success_reply(xid).serialize(output)?;
nfs3::nfsstat3::NFS3ERR_EXIST.serialize(output)?;
nfs3::wcc_data { before: pre_dir_attr, after: post_dir_attr }.serialize(output)?;
return Ok(());
}
}
nfs3::createmode3::EXCLUSIVE => {
create_verifier = Some(deserialize::<nfs3::createverf3>(input)?);
debug!("create exclusive");
}
}
let fid: Result<nfs3::fileid3, nfs3::nfsstat3>;
let postopattr: nfs3::post_op_attr;
if matches!(createhow, nfs3::createmode3::EXCLUSIVE) {
let verifier = create_verifier.unwrap_or_default();
fid = context.vfs.create_exclusive(dirid, &dirops.name, verifier).await;
postopattr = nfs3::post_op_attr::None;
} else if matches!(createhow, nfs3::createmode3::UNCHECKED) {
if let Ok(existing_id) = context.vfs.lookup(dirid, &dirops.name).await {
match context.vfs.setattr(existing_id, target_attributes).await {
Ok(attr) => {
fid = Ok(existing_id);
postopattr = nfs3::post_op_attr::Some(attr);
}
Err(stat) => {
fid = Err(stat);
postopattr = nfs3::post_op_attr::None;
}
}
} else {
let res = context.vfs.create(dirid, &dirops.name, target_attributes).await;
fid = res.map(|x| x.0);
postopattr = res.map(|(_, fattr)| fattr).ok();
}
} else {
let res = context.vfs.create(dirid, &dirops.name, target_attributes).await;
fid = res.map(|x| x.0);
postopattr = res.map(|(_, fattr)| fattr).ok();
}
let post_dir_attr = context.vfs.getattr(dirid).await.ok();
let wcc_res = nfs3::wcc_data { before: pre_dir_attr, after: post_dir_attr };
match fid {
Ok(fid) => {
debug!("create success --> {:?}, {:?}", fid, postopattr);
xdr::rpc::make_success_reply(xid).serialize(output)?;
nfs3::nfsstat3::NFS3_OK.serialize(output)?;
let fh = context.vfs.id_to_fh(fid);
nfs3::post_op_fh3::Some(fh).serialize(output)?;
postopattr.serialize(output)?;
wcc_res.serialize(output)?;
}
Err(e) => {
error!("create error --> {:?}", e);
xdr::rpc::make_success_reply(xid).serialize(output)?;
e.serialize(output)?;
wcc_res.serialize(output)?;
}
}
Ok(())
}