1use ninep_proto::{Rstatfs, Stat};
2use std::time::{Duration, SystemTime, UNIX_EPOCH};
3
4#[derive(Clone, Debug)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub struct ConnectOptions {
12 pub uid: Option<u32>,
15 pub gid: Option<u32>,
18 pub uname: Option<String>,
21 pub aname: String,
23 pub msize: u32,
25 pub connect_timeout_ms: Option<u32>,
28}
29
30impl Default for ConnectOptions {
31 fn default() -> Self {
32 Self {
33 uid: None,
34 gid: None,
35 uname: None,
36 aname: String::new(),
37 msize: 1024 * 1024,
38 connect_timeout_ms: Some(30_000),
39 }
40 }
41}
42
43#[derive(Clone, Copy, Debug)]
45#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
46pub struct Capabilities {
47 pub msize: u32,
49 pub max_read_chunk: u32,
51 pub max_write_chunk: u32,
53}
54
55#[derive(Clone, Copy, Debug)]
58#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
59pub struct OpenOptions {
60 pub read: bool,
62 pub write: bool,
64 pub create: bool,
67 pub create_new: bool,
70 pub truncate: bool,
73 pub mode: u32,
75}
76
77impl Default for OpenOptions {
78 fn default() -> Self {
79 Self {
80 read: false,
81 write: false,
82 create: false,
83 create_new: false,
84 truncate: false,
85 mode: 0o644,
86 }
87 }
88}
89
90impl OpenOptions {
91 pub fn read_only() -> Self {
93 Self {
94 read: true,
95 ..Self::default()
96 }
97 }
98
99 pub fn write_only() -> Self {
101 Self {
102 write: true,
103 ..Self::default()
104 }
105 }
106
107 pub fn read_write() -> Self {
109 Self {
110 read: true,
111 write: true,
112 ..Self::default()
113 }
114 }
115
116 pub fn create(mut self, yes: bool) -> Self {
118 self.create = yes;
119 self
120 }
121
122 pub fn create_new(mut self, yes: bool) -> Self {
124 self.create_new = yes;
125 self
126 }
127
128 pub fn truncate(mut self, yes: bool) -> Self {
130 self.truncate = yes;
131 self
132 }
133
134 pub fn mode(mut self, mode: u32) -> Self {
136 self.mode = mode;
137 self
138 }
139}
140
141#[derive(Clone, Copy, Debug, PartialEq, Eq)]
143#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
144pub enum FileType {
145 File,
147 Dir,
149 Symlink,
151 Fifo,
153 Socket,
155 CharDevice,
157 BlockDevice,
159 Unknown,
161}
162
163impl FileType {
164 pub(crate) fn from_mode(mode: u32) -> Self {
165 match mode & crate::linux::S_IFMT {
166 x if x == crate::linux::S_IFREG => Self::File,
167 x if x == crate::linux::S_IFDIR => Self::Dir,
168 x if x == crate::linux::S_IFLNK => Self::Symlink,
169 x if x == crate::linux::S_IFIFO => Self::Fifo,
170 x if x == crate::linux::S_IFSOCK => Self::Socket,
171 x if x == crate::linux::S_IFCHR => Self::CharDevice,
172 x if x == crate::linux::S_IFBLK => Self::BlockDevice,
173 _ => Self::Unknown,
174 }
175 }
176}
177
178#[derive(Clone, Copy, Debug, PartialEq, Eq)]
180#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
181pub struct Timestamp {
182 pub secs: i64,
184 pub nanos: u32,
186}
187
188impl From<SystemTime> for Timestamp {
189 fn from(t: SystemTime) -> Self {
190 match t.duration_since(UNIX_EPOCH) {
191 Ok(d) => Timestamp {
192 secs: d.as_secs() as i64,
193 nanos: d.subsec_nanos(),
194 },
195 Err(e) => {
198 let d = e.duration();
199 let (secs, nanos) = (d.as_secs() as i64, d.subsec_nanos());
200 if nanos == 0 {
201 Timestamp {
202 secs: -secs,
203 nanos: 0,
204 }
205 } else {
206 Timestamp {
207 secs: -secs - 1,
208 nanos: 1_000_000_000 - nanos,
209 }
210 }
211 }
212 }
213 }
214}
215
216impl From<Timestamp> for SystemTime {
217 fn from(t: Timestamp) -> Self {
218 systime(t)
219 }
220}
221
222#[derive(Clone, Copy, Debug)]
224#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
225pub enum SetTime {
226 Now,
228 At {
230 time: Timestamp,
232 },
233}
234
235impl From<SystemTime> for SetTime {
236 fn from(t: SystemTime) -> Self {
237 SetTime::At { time: t.into() }
238 }
239}
240
241#[derive(Clone, Copy, Debug, Default)]
243#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
244pub struct SetAttrs {
245 pub mode: Option<u32>,
247 pub uid: Option<u32>,
249 pub gid: Option<u32>,
251 pub size: Option<u64>,
253 pub atime: Option<SetTime>,
255 pub mtime: Option<SetTime>,
257}
258
259#[derive(Clone, Copy, Debug)]
262#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
263pub enum NodeKind {
264 Fifo,
266 Socket,
268 BlockDevice {
270 major: u32,
272 minor: u32,
274 },
275 CharDevice {
277 major: u32,
279 minor: u32,
281 },
282}
283
284#[derive(Clone, Copy, Debug)]
286#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
287pub struct Metadata {
288 pub ino: u64,
290 pub file_type: FileType,
292 pub mode: u32,
294 pub nlink: u64,
296 pub uid: u32,
298 pub gid: u32,
300 pub size: u64,
302 pub block_size: u64,
304 pub blocks: u64,
306 pub rdev: u64,
308 pub atime: Timestamp,
310 pub mtime: Timestamp,
312 pub ctime: Timestamp,
314 pub btime: Timestamp,
316 pub data_version: u64,
319}
320
321impl Metadata {
322 pub(crate) fn from_stat(st: &Stat) -> Self {
323 let ts = |secs: u64, nanos: u64| Timestamp {
324 secs: secs as i64,
325 nanos: nanos as u32,
326 };
327 Self {
328 ino: st.qid.path,
329 file_type: FileType::from_mode(st.mode),
330 mode: st.mode,
331 nlink: st.nlink,
332 uid: st.uid,
333 gid: st.gid,
334 size: st.size,
335 block_size: st.blksize,
336 blocks: st.blocks,
337 rdev: st.rdev,
338 atime: ts(st.atime_sec, st.atime_nsec),
339 mtime: ts(st.mtime_sec, st.mtime_nsec),
340 ctime: ts(st.ctime_sec, st.ctime_nsec),
341 btime: ts(st.btime_sec, st.btime_nsec),
342 data_version: st.data_version,
343 }
344 }
345
346 pub fn is_file(&self) -> bool {
348 self.file_type == FileType::File
349 }
350
351 pub fn is_dir(&self) -> bool {
353 self.file_type == FileType::Dir
354 }
355
356 pub fn is_symlink(&self) -> bool {
358 self.file_type == FileType::Symlink
359 }
360
361 pub fn permissions(&self) -> u32 {
363 self.mode & 0o7777
364 }
365
366 pub fn modified(&self) -> SystemTime {
368 systime(self.mtime)
369 }
370
371 pub fn accessed(&self) -> SystemTime {
373 systime(self.atime)
374 }
375}
376
377fn systime(t: Timestamp) -> SystemTime {
378 let nanos = t.nanos.min(999_999_999);
381 if t.secs >= 0 {
382 UNIX_EPOCH
383 .checked_add(Duration::new(t.secs as u64, nanos))
384 .unwrap_or(UNIX_EPOCH)
385 } else {
386 let base = UNIX_EPOCH
387 .checked_sub(Duration::new(t.secs.unsigned_abs(), 0))
388 .unwrap_or(UNIX_EPOCH);
389 base.checked_add(Duration::new(0, nanos)).unwrap_or(base)
390 }
391}
392
393#[derive(Clone, Copy, Debug)]
395#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
396pub struct StatFs {
397 pub block_size: u32,
399 pub blocks: u64,
401 pub blocks_free: u64,
403 pub blocks_available: u64,
405 pub files: u64,
407 pub files_free: u64,
409 pub filesystem_id: u64,
411 pub max_name_len: u32,
413}
414
415impl StatFs {
416 pub(crate) fn from_wire(r: &Rstatfs) -> Self {
417 Self {
418 block_size: r.bsize,
419 blocks: r.blocks,
420 blocks_free: r.bfree,
421 blocks_available: r.bavail,
422 files: r.files,
423 files_free: r.ffree,
424 filesystem_id: r.fsid,
425 max_name_len: r.namelen,
426 }
427 }
428}
429
430#[derive(Clone, Debug)]
432#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
433pub struct DirEntry {
434 pub name: String,
436 pub name_bytes: Vec<u8>,
439 pub name_is_utf8: bool,
441 pub file_type: FileType,
443 pub ino: u64,
445 pub metadata: Metadata,
447}
448
449impl DirEntry {
450 pub(crate) fn from_plus(e: &ninep_proto::DirEntryPlus) -> Self {
451 let name_bytes = e.name.data.clone();
452 Self {
453 name: String::from_utf8_lossy(&name_bytes).into_owned(),
454 name_is_utf8: std::str::from_utf8(&name_bytes).is_ok(),
455 file_type: FileType::from_mode(e.stat.mode),
456 ino: e.qid.path,
457 metadata: Metadata::from_stat(&e.stat),
458 name_bytes,
459 }
460 }
461}