use ninep_proto::{Rstatfs, Stat};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ConnectOptions {
pub uid: Option<u32>,
pub gid: Option<u32>,
pub uname: Option<String>,
pub aname: String,
pub msize: u32,
pub connect_timeout_ms: Option<u32>,
}
impl Default for ConnectOptions {
fn default() -> Self {
Self {
uid: None,
gid: None,
uname: None,
aname: String::new(),
msize: 1024 * 1024,
connect_timeout_ms: Some(30_000),
}
}
}
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Capabilities {
pub extensions_v1: bool,
pub extensions_v2: bool,
pub msize: u32,
pub max_read_chunk: u32,
pub max_write_chunk: u32,
}
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct OpenOptions {
pub read: bool,
pub write: bool,
pub create: bool,
pub create_new: bool,
pub truncate: bool,
pub mode: u32,
}
impl Default for OpenOptions {
fn default() -> Self {
Self {
read: false,
write: false,
create: false,
create_new: false,
truncate: false,
mode: 0o644,
}
}
}
impl OpenOptions {
pub fn read_only() -> Self {
Self {
read: true,
..Self::default()
}
}
pub fn write_only() -> Self {
Self {
write: true,
..Self::default()
}
}
pub fn read_write() -> Self {
Self {
read: true,
write: true,
..Self::default()
}
}
pub fn create(mut self, yes: bool) -> Self {
self.create = yes;
self
}
pub fn create_new(mut self, yes: bool) -> Self {
self.create_new = yes;
self
}
pub fn truncate(mut self, yes: bool) -> Self {
self.truncate = yes;
self
}
pub fn mode(mut self, mode: u32) -> Self {
self.mode = mode;
self
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum FileType {
File,
Dir,
Symlink,
Fifo,
Socket,
CharDevice,
BlockDevice,
Unknown,
}
impl FileType {
pub(crate) fn from_mode(mode: u32) -> Self {
match mode & libc::S_IFMT as u32 {
x if x == libc::S_IFREG as u32 => Self::File,
x if x == libc::S_IFDIR as u32 => Self::Dir,
x if x == libc::S_IFLNK as u32 => Self::Symlink,
x if x == libc::S_IFIFO as u32 => Self::Fifo,
x if x == libc::S_IFSOCK as u32 => Self::Socket,
x if x == libc::S_IFCHR as u32 => Self::CharDevice,
x if x == libc::S_IFBLK as u32 => Self::BlockDevice,
_ => Self::Unknown,
}
}
pub(crate) fn from_dt(dt: u8) -> Self {
match dt {
libc::DT_REG => Self::File,
libc::DT_DIR => Self::Dir,
libc::DT_LNK => Self::Symlink,
libc::DT_FIFO => Self::Fifo,
libc::DT_SOCK => Self::Socket,
libc::DT_CHR => Self::CharDevice,
libc::DT_BLK => Self::BlockDevice,
_ => Self::Unknown,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Timestamp {
pub secs: i64,
pub nanos: u32,
}
impl From<SystemTime> for Timestamp {
fn from(t: SystemTime) -> Self {
match t.duration_since(UNIX_EPOCH) {
Ok(d) => Timestamp {
secs: d.as_secs() as i64,
nanos: d.subsec_nanos(),
},
Err(e) => {
let d = e.duration();
let (secs, nanos) = (d.as_secs() as i64, d.subsec_nanos());
if nanos == 0 {
Timestamp {
secs: -secs,
nanos: 0,
}
} else {
Timestamp {
secs: -secs - 1,
nanos: 1_000_000_000 - nanos,
}
}
}
}
}
}
impl From<Timestamp> for SystemTime {
fn from(t: Timestamp) -> Self {
systime(t)
}
}
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum SetTime {
Now,
At {
time: Timestamp,
},
}
impl From<SystemTime> for SetTime {
fn from(t: SystemTime) -> Self {
SetTime::At { time: t.into() }
}
}
#[derive(Clone, Copy, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SetAttrs {
pub mode: Option<u32>,
pub uid: Option<u32>,
pub gid: Option<u32>,
pub size: Option<u64>,
pub atime: Option<SetTime>,
pub mtime: Option<SetTime>,
}
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum NodeKind {
Fifo,
Socket,
BlockDevice {
major: u32,
minor: u32,
},
CharDevice {
major: u32,
minor: u32,
},
}
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Metadata {
pub ino: u64,
pub file_type: FileType,
pub mode: u32,
pub nlink: u64,
pub uid: u32,
pub gid: u32,
pub size: u64,
pub block_size: u64,
pub blocks: u64,
pub rdev: u64,
pub atime: Timestamp,
pub mtime: Timestamp,
pub ctime: Timestamp,
pub btime: Timestamp,
pub data_version: u64,
}
impl Metadata {
pub(crate) fn from_stat(st: &Stat) -> Self {
let ts = |secs: u64, nanos: u64| Timestamp {
secs: secs as i64,
nanos: nanos as u32,
};
Self {
ino: st.qid.path,
file_type: FileType::from_mode(st.mode),
mode: st.mode,
nlink: st.nlink,
uid: st.uid,
gid: st.gid,
size: st.size,
block_size: st.blksize,
blocks: st.blocks,
rdev: st.rdev,
atime: ts(st.atime_sec, st.atime_nsec),
mtime: ts(st.mtime_sec, st.mtime_nsec),
ctime: ts(st.ctime_sec, st.ctime_nsec),
btime: ts(st.btime_sec, st.btime_nsec),
data_version: st.data_version,
}
}
pub fn is_file(&self) -> bool {
self.file_type == FileType::File
}
pub fn is_dir(&self) -> bool {
self.file_type == FileType::Dir
}
pub fn is_symlink(&self) -> bool {
self.file_type == FileType::Symlink
}
pub fn permissions(&self) -> u32 {
self.mode & 0o7777
}
pub fn modified(&self) -> SystemTime {
systime(self.mtime)
}
pub fn accessed(&self) -> SystemTime {
systime(self.atime)
}
}
fn systime(t: Timestamp) -> SystemTime {
let nanos = t.nanos.min(999_999_999);
if t.secs >= 0 {
UNIX_EPOCH
.checked_add(Duration::new(t.secs as u64, nanos))
.unwrap_or(UNIX_EPOCH)
} else {
let base = UNIX_EPOCH
.checked_sub(Duration::new(t.secs.unsigned_abs(), 0))
.unwrap_or(UNIX_EPOCH);
base.checked_add(Duration::new(0, nanos)).unwrap_or(base)
}
}
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct StatFs {
pub block_size: u32,
pub blocks: u64,
pub blocks_free: u64,
pub blocks_available: u64,
pub files: u64,
pub files_free: u64,
pub filesystem_id: u64,
pub max_name_len: u32,
}
impl StatFs {
pub(crate) fn from_wire(r: &Rstatfs) -> Self {
Self {
block_size: r.bsize,
blocks: r.blocks,
blocks_free: r.bfree,
blocks_available: r.bavail,
files: r.files,
files_free: r.ffree,
filesystem_id: r.fsid,
max_name_len: r.namelen,
}
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DirEntry {
pub name: String,
pub name_bytes: Vec<u8>,
pub name_is_utf8: bool,
pub file_type: FileType,
pub ino: u64,
pub metadata: Option<Metadata>,
}
impl DirEntry {
pub(crate) fn from_plus(e: &ninep_proto::DirEntryPlus) -> Self {
let name_bytes = e.name.data.clone();
Self {
name: String::from_utf8_lossy(&name_bytes).into_owned(),
name_is_utf8: std::str::from_utf8(&name_bytes).is_ok(),
file_type: FileType::from_mode(e.stat.mode),
ino: e.qid.path,
metadata: Some(Metadata::from_stat(&e.stat)),
name_bytes,
}
}
pub(crate) fn from_plain(e: &ninep_proto::DirEntry) -> Self {
let name_bytes = e.name.data.clone();
Self {
name: String::from_utf8_lossy(&name_bytes).into_owned(),
name_is_utf8: std::str::from_utf8(&name_bytes).is_ok(),
file_type: FileType::from_dt(e.type_),
ino: e.qid.path,
metadata: None,
name_bytes,
}
}
}