use crate::{ClientError, ClientResult, NinePClient};
use ninep_proto::{
DirEntry, DirEntryPlus, GETATTR_ALL, Qid, SETATTR_ATIME, SETATTR_ATIME_SET, SETATTR_GID,
SETATTR_MODE, SETATTR_MTIME, SETATTR_MTIME_SET, SETATTR_SIZE, SETATTR_UID, Stat, Tsetattr,
};
use std::collections::VecDeque;
#[derive(Clone, Copy, Debug)]
pub enum SetattrTime {
Now,
At { sec: u64, nsec: u64 },
}
pub struct SetattrBuilder {
ts: Tsetattr,
}
impl SetattrBuilder {
pub fn new(fid: u32) -> Self {
Self {
ts: Tsetattr {
fid,
valid: 0,
mode: 0,
uid: 0,
gid: 0,
size: 0,
atime_sec: 0,
atime_nsec: 0,
mtime_sec: 0,
mtime_nsec: 0,
},
}
}
pub fn mode(mut self, mode: Option<u32>) -> Self {
if let Some(mode) = mode {
self.ts.valid |= SETATTR_MODE;
self.ts.mode = mode;
}
self
}
pub fn uid(mut self, uid: Option<u32>) -> Self {
if let Some(uid) = uid {
self.ts.valid |= SETATTR_UID;
self.ts.uid = uid;
}
self
}
pub fn gid(mut self, gid: Option<u32>) -> Self {
if let Some(gid) = gid {
self.ts.valid |= SETATTR_GID;
self.ts.gid = gid;
}
self
}
pub fn size(mut self, size: Option<u64>) -> Self {
if let Some(size) = size {
self.ts.valid |= SETATTR_SIZE;
self.ts.size = size;
}
self
}
pub fn atime(mut self, atime: Option<SetattrTime>) -> Self {
match atime {
Some(SetattrTime::Now) => self.ts.valid |= SETATTR_ATIME,
Some(SetattrTime::At { sec, nsec }) => {
self.ts.valid |= SETATTR_ATIME | SETATTR_ATIME_SET;
self.ts.atime_sec = sec;
self.ts.atime_nsec = nsec;
}
None => {}
}
self
}
pub fn mtime(mut self, mtime: Option<SetattrTime>) -> Self {
match mtime {
Some(SetattrTime::Now) => self.ts.valid |= SETATTR_MTIME,
Some(SetattrTime::At { sec, nsec }) => {
self.ts.valid |= SETATTR_MTIME | SETATTR_MTIME_SET;
self.ts.mtime_sec = sec;
self.ts.mtime_nsec = nsec;
}
None => {}
}
self
}
pub fn build(self) -> Tsetattr {
self.ts
}
}
impl NinePClient {
pub async fn setattr_stat(&self, ts: Tsetattr) -> ClientResult<Stat> {
if self.extensions_v2_enabled() {
return self.setattr_attr(ts).await;
}
let fid = ts.fid;
self.setattr(ts).await?;
self.getattr(fid, GETATTR_ALL).await
}
pub async fn walk_stat(
&self,
from: u32,
newfid: u32,
names: &[&[u8]],
) -> ClientResult<(u32, Stat)> {
if !names.is_empty() && self.extensions_enabled() {
return self
.walk_getattr(from, newfid, names)
.await
.map(|(_, stat)| (newfid, stat));
}
match self.walk(from, newfid, names).await {
Ok(qids) if qids.len() == names.len() => {}
Ok(_) => return Err(ClientError::Errno(libc::ENOENT as u32)),
Err(e) => return Err(e),
}
match self.getattr(newfid, GETATTR_ALL).await {
Ok(stat) => Ok((newfid, stat)),
Err(e) => {
let _ = self.clunk(newfid).await;
Err(e)
}
}
}
pub async fn open_clone(
&self,
from: u32,
newfid: u32,
flags: u32,
fallback_flags: Option<u32>,
) -> ClientResult<(u32, Qid, u32)> {
if self.extensions_v2_enabled() {
let mut res = self.lopenat(from, newfid, flags).await;
if res.is_err()
&& let Some(orig) = fallback_flags
{
res = self.lopenat(from, newfid, orig).await;
}
return res.map(|(qid, iounit)| (newfid, qid, iounit));
}
self.walk(from, newfid, &[]).await?;
let mut res = self.lopen(newfid, flags).await;
if res.is_err()
&& let Some(orig) = fallback_flags
{
res = self.lopen(newfid, orig).await;
}
match res {
Ok((qid, iounit)) => Ok((newfid, qid, iounit)),
Err(e) => {
let _ = self.clunk(newfid).await;
Err(e)
}
}
}
pub async fn create_open(
&self,
dfid: u32,
newfid: u32,
name: &[u8],
flags: u32,
mode: u32,
gid: u32,
) -> ClientResult<(u32, Option<Stat>, u32)> {
self.create_open_op_id(dfid, newfid, name, flags, mode, gid, [0u8; 16])
.await
}
#[allow(clippy::too_many_arguments)]
pub async fn create_open_op_id(
&self,
dfid: u32,
newfid: u32,
name: &[u8],
flags: u32,
mode: u32,
gid: u32,
op_id: [u8; 16],
) -> ClientResult<(u32, Option<Stat>, u32)> {
if self.extensions_v2_enabled() {
return self
.lcreateattr_op_id(dfid, newfid, name, flags, mode, gid, op_id)
.await
.map(|(stat, iounit)| (newfid, Some(stat), iounit));
}
self.walk(dfid, newfid, &[]).await?;
match self
.lcreate_op_id(newfid, name, flags, mode, gid, op_id)
.await
{
Ok((_qid, iounit)) => Ok((newfid, None, iounit)),
Err(e) => {
let _ = self.clunk(newfid).await;
Err(e)
}
}
}
async fn walk_stat_surplus(
&self,
dfid: u32,
newfid: Option<u32>,
name: &[u8],
) -> ClientResult<(Option<u32>, Stat)> {
match newfid {
Some(nf) => self
.walk_stat(dfid, nf, &[name])
.await
.map(|(fid, stat)| (Some(fid), stat)),
None => {
let nf = self.alloc_fid();
match self.walk_stat(dfid, nf, &[name]).await {
Ok((fid, stat)) => Ok((Some(fid), stat)),
Err(e) => {
self.free_fid(nf);
Err(e)
}
}
}
}
}
pub async fn mkdir_stat(
&self,
dfid: u32,
newfid: Option<u32>,
name: &[u8],
mode: u32,
gid: u32,
) -> ClientResult<(Option<u32>, Stat)> {
self.mkdir_stat_op_id(dfid, newfid, name, mode, gid, [0u8; 16])
.await
}
pub async fn mkdir_stat_op_id(
&self,
dfid: u32,
newfid: Option<u32>,
name: &[u8],
mode: u32,
gid: u32,
op_id: [u8; 16],
) -> ClientResult<(Option<u32>, Stat)> {
if self.extensions_v2_enabled() {
return self
.mkdir_attr_op_id(dfid, name, mode, gid, op_id)
.await
.map(|s| (None, s));
}
self.mkdir_op_id(dfid, name, mode, gid, op_id).await?;
self.walk_stat_surplus(dfid, newfid, name).await
}
pub async fn symlink_stat(
&self,
dfid: u32,
newfid: Option<u32>,
name: &[u8],
target: &[u8],
gid: u32,
) -> ClientResult<(Option<u32>, Stat)> {
self.symlink_stat_op_id(dfid, newfid, name, target, gid, [0u8; 16])
.await
}
#[allow(clippy::too_many_arguments)]
pub async fn symlink_stat_op_id(
&self,
dfid: u32,
newfid: Option<u32>,
name: &[u8],
target: &[u8],
gid: u32,
op_id: [u8; 16],
) -> ClientResult<(Option<u32>, Stat)> {
if self.extensions_v2_enabled() {
return self
.symlink_attr_op_id(dfid, name, target, gid, op_id)
.await
.map(|s| (None, s));
}
self.symlink_op_id(dfid, name, target, gid, op_id).await?;
self.walk_stat_surplus(dfid, newfid, name).await
}
#[allow(clippy::too_many_arguments)]
pub async fn mknod_stat(
&self,
dfid: u32,
newfid: Option<u32>,
name: &[u8],
mode: u32,
major: u32,
minor: u32,
gid: u32,
) -> ClientResult<(Option<u32>, Stat)> {
self.mknod_stat_op_id(dfid, newfid, name, mode, major, minor, gid, [0u8; 16])
.await
}
#[allow(clippy::too_many_arguments)]
pub async fn mknod_stat_op_id(
&self,
dfid: u32,
newfid: Option<u32>,
name: &[u8],
mode: u32,
major: u32,
minor: u32,
gid: u32,
op_id: [u8; 16],
) -> ClientResult<(Option<u32>, Stat)> {
if self.extensions_v2_enabled() {
return self
.mknod_attr_op_id(dfid, name, mode, major, minor, gid, op_id)
.await
.map(|s| (None, s));
}
self.mknod_op_id(dfid, name, mode, major, minor, gid, op_id)
.await?;
self.walk_stat_surplus(dfid, newfid, name).await
}
pub async fn link_stat(
&self,
dfid: u32,
newfid: Option<u32>,
fid: u32,
name: &[u8],
) -> ClientResult<(Option<u32>, Stat)> {
self.link_stat_op_id(dfid, newfid, fid, name, [0u8; 16])
.await
}
pub async fn link_stat_op_id(
&self,
dfid: u32,
newfid: Option<u32>,
fid: u32,
name: &[u8],
op_id: [u8; 16],
) -> ClientResult<(Option<u32>, Stat)> {
if self.extensions_v2_enabled() {
return self
.link_attr_op_id(dfid, fid, name, op_id)
.await
.map(|s| (None, s));
}
self.link_op_id(dfid, fid, name, op_id).await?;
self.walk_stat_surplus(dfid, newfid, name).await
}
}
pub trait DirEntryCookie {
fn cookie(&self) -> u64;
}
impl DirEntryCookie for DirEntry {
fn cookie(&self) -> u64 {
self.offset
}
}
impl DirEntryCookie for DirEntryPlus {
fn cookie(&self) -> u64 {
self.offset
}
}
pub struct ReaddirState<E> {
pub buf: VecDeque<E>,
pub fetch_cookie: u64,
pub resume_offset: u64,
pub eof: bool,
}
impl<E: DirEntryCookie> ReaddirState<E> {
pub fn starting_at(offset: u64) -> Self {
Self {
buf: VecDeque::new(),
fetch_cookie: offset,
resume_offset: offset,
eof: false,
}
}
pub fn seek(&mut self, offset: u64) {
if offset != self.resume_offset {
self.buf.clear();
self.fetch_cookie = offset;
self.resume_offset = offset;
self.eof = false;
}
}
pub fn absorb(&mut self, entries: Vec<E>) {
match entries.last() {
Some(last) => self.fetch_cookie = last.cookie(),
None => self.eof = true,
}
self.buf.extend(entries);
}
}