use bytes::{Buf, Bytes};
use super::mount::{Mount41, extract_stateid};
use super::state::StateId;
use crate::error::{NfsError, Result};
use crate::nfs4::attrs::encode_setattr;
impl Mount41 {
#[allow(clippy::too_many_arguments)]
pub(crate) async fn setattr(
&self,
fh: Bytes,
_guard_ctime: Option<crate::Time>,
mode: Option<u32>,
uid: Option<u32>,
gid: Option<u32>,
size: Option<u64>,
atime: Option<crate::Time>,
mtime: Option<crate::Time>,
) -> Result<()> {
let _io_guard = if size.is_some() {
let guard = self.layout_manager.write_file_io(&fh).await;
self.flush_layoutcommit(&fh).await?;
Some(guard)
} else {
None
};
let (attrmask, attr_vals) = encode_setattr(mode, uid, gid, size, atime, mtime);
let stateid = [0u8; 16]; let resp = self
.compound("setattr", |b| {
b.putfh(&fh).setattr(&stateid, &attrmask, &attr_vals)
})
.await?;
resp.op_ok(1)?; let setattr_op = resp
.results
.get(2)
.ok_or_else(|| NfsError::Xdr("SETATTR response missing op at index 2".to_string()))?;
if !matches!(setattr_op.status, crate::nfs4::fastxdr::nfsstat4::NFS4_OK) {
return Err(NfsError::Nfs4(setattr_op.status));
}
Ok(())
}
#[allow(clippy::too_many_arguments)]
pub(crate) async fn setattr_path(
&self,
path: &str,
specify_guard: bool,
mode: Option<u32>,
uid: Option<u32>,
gid: Option<u32>,
size: Option<u64>,
atime: Option<crate::Time>,
mtime: Option<crate::Time>,
) -> Result<()> {
let obj = self.lookup_path(path).await?;
let guard = if specify_guard {
let attr = self.getattr(obj.fh.clone()).await?;
Some(attr.ctime)
} else {
None
};
self.setattr(obj.fh, guard, mode, uid, gid, size, atime, mtime)
.await
}
pub(crate) async fn lock(
&self,
fh: Bytes,
lock_type: u32,
offset: u64,
length: u64,
) -> Result<Bytes> {
let sid = self.state.get_stateid(&fh).await;
if sid == StateId::anonymous() {
return Err(NfsError::InvalidInput(
"file must be opened before locking (call open() first)".to_string(),
));
}
let open_stateid = sid.raw;
let client_id = self.session_holder.get().await.client_id();
let resp = self
.compound("lock", |b| {
b.require_generation(sid.generation).putfh(&fh).lock(
lock_type,
false, offset,
length,
true, &open_stateid,
0, 0, b"nfs-rs-lock",
client_id,
)
})
.await?;
resp.op_ok(1)?; let lock_op = resp.op_ok(2)?;
let mut data = lock_op.data.clone();
let stateid_raw = extract_stateid(&mut data)?;
Ok(Bytes::copy_from_slice(&stateid_raw))
}
pub(crate) async fn locku(
&self,
fh: Bytes,
lock_stateid: Bytes,
lock_type: u32,
offset: u64,
length: u64,
) -> Result<()> {
if lock_stateid.len() < 16 {
return Err(NfsError::InvalidInput(
"lock_stateid must be 16 bytes".to_string(),
));
}
let mut sid = [0u8; 16];
sid.copy_from_slice(&lock_stateid[..16]);
let resp = self
.compound("locku", |b| {
b.putfh(&fh).locku(lock_type, 0, &sid, offset, length)
})
.await?;
resp.op_ok(1)?; resp.op_ok(2)?; Ok(())
}
pub(crate) async fn lock_test(
&self,
fh: Bytes,
lock_type: u32,
offset: u64,
length: u64,
) -> Result<()> {
if !matches!(lock_type, 1 | 2) || length == 0 {
return Err(NfsError::InvalidInput(
"LOCKT requires type 1/2 and non-zero length".to_string(),
));
}
let client_id = self.session_holder.get().await.client_id();
let resp = self
.compound("lockt", |b| {
b.putfh(&fh)
.lockt(lock_type, offset, length, b"nfs-rs-lockt", client_id)
})
.await?;
resp.op_ok(1)?;
resp.op_ok(2)?;
Ok(())
}
pub(crate) async fn commit(&self, fh: Bytes, offset: u64, count: u32) -> Result<()> {
self.commit_with_verifier(fh, offset, count)
.await
.map(|_| ())
}
pub(crate) async fn commit_with_verifier(
&self,
fh: Bytes,
offset: u64,
count: u32,
) -> Result<Option<[u8; 8]>> {
let resp = self
.compound("commit", |b| b.putfh(&fh).commit(offset, count))
.await?;
resp.op_ok(1)?; let commit_op = resp.op_ok(2)?; let mut d = commit_op.data.clone();
if d.remaining() < 8 {
return Err(NfsError::Xdr("COMMIT result too short".to_string()));
}
let mut verifier = [0u8; 8];
d.copy_to_slice(&mut verifier);
Ok(Some(verifier))
}
pub(crate) async fn commit_path(&self, path: &str, offset: u64, count: u32) -> Result<()> {
let obj = self.lookup_path(path).await?;
self.commit(obj.fh, offset, count).await
}
}