use bytes::Bytes;
use super::mount::{Mount41, extract_stateid};
use super::state::StateId;
use crate::error::{NfsError, Result};
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 (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, super::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 commit(&self, fh: Bytes, offset: u64, count: u32) -> Result<()> {
let resp = self
.compound("commit", |b| b.putfh(&fh).commit(offset, count))
.await?;
resp.op_ok(1)?; resp.op_ok(2)?; Ok(())
}
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
}
}
pub(super) fn encode_setattr(
mode: Option<u32>,
uid: Option<u32>,
gid: Option<u32>,
size: Option<u64>,
atime: Option<crate::Time>,
mtime: Option<crate::Time>,
) -> (Vec<u32>, Vec<u8>) {
let mut word0: u32 = 0;
let mut word1: u32 = 0;
let mut vals = Vec::new();
if let Some(s) = size {
word0 |= 1 << 4;
vals.extend_from_slice(&s.to_be_bytes());
}
if let Some(m) = mode {
word1 |= 1 << 1;
vals.extend_from_slice(&m.to_be_bytes());
}
if let Some(u) = uid {
word1 |= 1 << 4;
let s = u.to_string();
let bytes = s.as_bytes();
vals.extend_from_slice(&(bytes.len() as u32).to_be_bytes());
vals.extend_from_slice(bytes);
let pad = (4 - bytes.len() % 4) % 4;
vals.extend(std::iter::repeat_n(0, pad));
}
if let Some(g) = gid {
word1 |= 1 << 5;
let s = g.to_string();
let bytes = s.as_bytes();
vals.extend_from_slice(&(bytes.len() as u32).to_be_bytes());
vals.extend_from_slice(bytes);
let pad = (4 - bytes.len() % 4) % 4;
vals.extend(std::iter::repeat_n(0, pad));
}
if let Some(t) = atime {
word1 |= 1 << 16;
vals.extend_from_slice(&1u32.to_be_bytes());
vals.extend_from_slice(&(t.seconds as i64).to_be_bytes());
vals.extend_from_slice(&t.nseconds.to_be_bytes());
}
if let Some(t) = mtime {
word1 |= 1 << 22;
vals.extend_from_slice(&1u32.to_be_bytes());
vals.extend_from_slice(&(t.seconds as i64).to_be_bytes());
vals.extend_from_slice(&t.nseconds.to_be_bytes());
}
let attrmask = if word1 != 0 {
vec![word0, word1]
} else if word0 != 0 {
vec![word0]
} else {
vec![]
};
(attrmask, vals)
}