1use lawn_constants::Error;
2use num_derive::FromPrimitive;
3use std::any::Any;
4use std::fs;
5#[cfg(feature = "unix")]
6use std::os::unix::fs::FileTypeExt;
7use std::time::SystemTime;
8
9#[cfg(feature = "unix")]
10pub mod libc;
11
12#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
14pub enum QIDKind {
15 Directory = 0o04,
16 Regular = 0o10,
17 FIFO = 0o01,
18 Symlink = 0o12,
19 BlockDevice = 0o06,
20 CharacterDevice = 0o02,
21 Socket = 0o14,
22 Authentication = 0xfe,
23 Unknown = 0xff,
24}
25
26impl QIDKind {
27 #[cfg(unix)]
29 fn from_filetype(ft: rustix::fs::FileType) -> Self {
30 match ft {
31 rustix::fs::FileType::Socket => Self::Socket,
32 rustix::fs::FileType::Symlink => Self::Symlink,
33 rustix::fs::FileType::BlockDevice => Self::BlockDevice,
34 rustix::fs::FileType::CharacterDevice => Self::CharacterDevice,
35 rustix::fs::FileType::Fifo => Self::FIFO,
36 rustix::fs::FileType::Directory => Self::Directory,
37 rustix::fs::FileType::RegularFile => Self::Regular,
38 _ => Self::Unknown,
39 }
40 }
41
42 #[cfg(unix)]
44 fn from_metadata(metadata: &std::fs::Metadata) -> Self {
45 let ft = metadata.file_type();
46 if ft.is_fifo() {
47 Self::FIFO
48 } else if ft.is_socket() {
49 Self::Socket
50 } else if ft.is_block_device() {
51 Self::BlockDevice
52 } else if ft.is_char_device() {
53 Self::CharacterDevice
54 } else if ft.is_dir() {
55 Self::Directory
56 } else if ft.is_symlink() {
57 Self::Symlink
58 } else {
59 Self::Unknown
60 }
61 }
62}
63
64#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
66pub struct QID {
67 kind: QIDKind,
68 dev: u64,
69 ino: u64,
70}
71
72impl Default for QID {
73 fn default() -> QID {
74 Self {
75 kind: QIDKind::Unknown,
76 dev: u64::MAX,
77 ino: u64::MAX,
78 }
79 }
80}
81
82impl QID {
83 pub fn new(kind: QIDKind, dev: u64, ino: u64) -> QID {
85 Self { kind, dev, ino }
86 }
87
88 pub fn kind(&self) -> QIDKind {
90 self.kind
91 }
92
93 pub fn dev(&self) -> u64 {
95 self.dev
96 }
97
98 pub fn ino(&self) -> u64 {
100 self.ino
101 }
102}
103
104#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
110pub struct FID {
111 id: u64,
112}
113
114impl FID {
115 pub const fn new(id: u64) -> FID {
117 Self { id }
118 }
119
120 pub const fn to_u64(&self) -> u64 {
122 self.id
123 }
124}
125
126#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
130pub struct Tag {
131 id: u64,
132}
133
134impl Tag {
135 pub const fn new(id: u64) -> Tag {
137 Self { id }
138 }
139
140 pub const fn to_u64(&self) -> u64 {
142 self.id
143 }
144}
145
146#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
148#[non_exhaustive]
149pub enum Plan9Type {
150 Original,
152 Unix,
154 Linux,
156}
157
158#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
160#[non_exhaustive]
161pub enum SFTPType {
162 V3,
164}
165
166#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
168#[non_exhaustive]
169pub enum SFTPExtensions {
170 OpenSSHPosixRenameV1,
171 OpenSSHReversedSymlink,
172 OpenSSHHardlinkV1,
173 OpenSSHFsyncV1,
174}
175
176#[derive(Copy, Clone, Debug)]
178pub enum ProtocolType<'a> {
179 Plan9(Plan9Type),
180 SFTP(SFTPType, &'a [SFTPExtensions]),
181 Other(Option<&'a dyn Any>),
182}
183
184#[derive(Clone, Debug)]
186pub struct Metadata<'a> {
187 tag: Tag,
188 command: u64,
189 proto: ProtocolType<'a>,
190 proto_extra: Option<u128>,
191 efficient: bool,
192}
193
194impl<'a> Metadata<'a> {
195 pub fn new(
196 tag: Tag,
197 command: u64,
198 proto: ProtocolType<'a>,
199 proto_extra: Option<u128>,
200 efficient: bool,
201 ) -> Metadata<'a> {
202 Self {
203 tag,
204 command,
205 proto,
206 proto_extra,
207 efficient,
208 }
209 }
210
211 pub fn command(&self) -> u64 {
212 self.command
213 }
214
215 pub fn tag(&self) -> Tag {
216 self.tag
217 }
218
219 pub fn protocol(&self) -> ProtocolType<'a> {
220 self.proto
221 }
222
223 pub fn protocol_extra_data(&self) -> Option<u128> {
224 self.proto_extra
225 }
226
227 pub fn efficient(&self) -> bool {
231 self.efficient
232 }
233
234 fn needs_valid_qid(&self) -> bool {
235 self.efficient
236 }
237}
238
239type Result<T> = std::result::Result<T, Error>;
240
241bitflags! {
242 pub struct OpenMode: u32 {
243 const O_RDONLY = 0x00;
244 const O_WRONLY = 0x01;
245 const O_RDWR = 0x02;
246 const O_ACCMODE = 0x03;
247 const O_CREAT = 0o100;
248 const O_EXCL = 0o200;
249 const O_NOCTTY = 0o400;
250 const O_TRUNC = 0o1000;
251 const O_APPEND = 0o2000;
252 const O_NONBLOCK = 0o4000;
253 const O_LARGEFILE = 0o100000;
254 const O_DIRECTORY = 0o200000;
255 const O_NOFOLLOW = 0o400000;
256 }
257
258 pub struct FileType: u32 {
259 const S_IFMT = 0o170000;
260 const S_IFDIR = 0o040000;
261 const S_IFCHR = 0o020000;
262 const S_IFBLK = 0o060000;
263 const S_IFREG = 0o100000;
264 const S_IFIFO = 0o010000;
265 const S_IFLNK = 0o120000;
266 const S_IFSOCK = 0o140000;
267 const S_ISUID = 0o4000;
269 const S_ISGID = 0o2000;
270 const S_ISVTX = 0o1000;
271 const S_IRUSR = 0o0400;
272 const S_IWUSR = 0o0200;
273 const S_IXUSR = 0o0100;
274 const S_IRGRP = 0o0040;
275 const S_IWGRP = 0o0020;
276 const S_IXGRP = 0o0010;
277 const S_IROTH = 0o0004;
278 const S_IWOTH = 0o0002;
279 const S_IXOTH = 0o0001;
280 }
281
282 pub struct StatValidity: u64 {
283 const MODE = 0x00000001;
284 const NLINK = 0x00000002;
285 const UID = 0x00000004;
286 const GID = 0x00000008;
287 const RDEV = 0x00000010;
288 const ATIME = 0x00000020;
289 const MTIME = 0x00000040;
290 const CTIME = 0x00000080;
291 const INODE = 0x00000100;
292 const SIZE = 0x00000200;
293 const BLOCKS = 0x00000400;
294 const BTIME = 0x00000800;
295 const GEN = 0x00001000;
296 const DATA_VERSION = 0x00002000;
297 const BASIC = 0x000007ff;
298 const ALL = 0x00003fff;
299 }
300}
301
302impl OpenMode {
303 #[cfg(feature = "unix")]
304 fn to_unix(self) -> rustix::fs::OFlags {
305 let mut val = rustix::fs::OFlags::empty();
306 if self.contains(Self::O_RDONLY) {
307 val |= rustix::fs::OFlags::RDONLY;
308 }
309 if self.contains(Self::O_WRONLY) {
310 val |= rustix::fs::OFlags::WRONLY;
311 }
312 if self.contains(Self::O_RDWR) {
313 val |= rustix::fs::OFlags::RDWR;
314 }
315 if self.contains(Self::O_ACCMODE) {
316 val |= rustix::fs::OFlags::ACCMODE;
317 }
318 if self.contains(Self::O_CREAT) {
319 val |= rustix::fs::OFlags::CREATE;
320 }
321 if self.contains(Self::O_EXCL) {
322 val |= rustix::fs::OFlags::EXCL;
323 }
324 if self.contains(Self::O_NOCTTY) {
325 val |= rustix::fs::OFlags::NOCTTY;
326 }
327 if self.contains(Self::O_TRUNC) {
328 val |= rustix::fs::OFlags::TRUNC;
329 }
330 if self.contains(Self::O_APPEND) {
331 val |= rustix::fs::OFlags::APPEND;
332 }
333 if self.contains(Self::O_NONBLOCK) {
334 val |= rustix::fs::OFlags::NONBLOCK;
335 }
336 if self.contains(Self::O_DIRECTORY) {
337 val |= rustix::fs::OFlags::DIRECTORY;
338 }
339 if self.contains(Self::O_NOFOLLOW) {
340 val |= rustix::fs::OFlags::NOFOLLOW;
341 }
342 val
343 }
344}
345
346impl FileType {
347 #[cfg(feature = "unix")]
348 fn from_unix(mode: u32) -> Self {
349 let kind = match rustix::fs::FileType::from_raw_mode(mode as rustix::fs::RawMode) {
350 rustix::fs::FileType::Socket => Self::S_IFSOCK,
351 rustix::fs::FileType::Symlink => Self::S_IFLNK,
352 rustix::fs::FileType::BlockDevice => Self::S_IFBLK,
353 rustix::fs::FileType::CharacterDevice => Self::S_IFCHR,
354 rustix::fs::FileType::Fifo => Self::S_IFIFO,
355 rustix::fs::FileType::Directory => Self::S_IFDIR,
356 rustix::fs::FileType::RegularFile => Self::S_IFREG,
357 _ => Self::from_bits(0).unwrap(),
358 };
359 let dmode = Self::from_bits(mode & 0o7777).unwrap();
360 kind | dmode
361 }
362
363 #[allow(dead_code)]
364 #[cfg(feature = "unix")]
365 fn to_unix(self) -> rustix::fs::RawMode {
366 let ft = match self & Self::S_IFMT {
367 Self::S_IFSOCK => rustix::fs::FileType::Socket,
368 Self::S_IFLNK => rustix::fs::FileType::Symlink,
369 Self::S_IFBLK => rustix::fs::FileType::BlockDevice,
370 Self::S_IFCHR => rustix::fs::FileType::CharacterDevice,
371 Self::S_IFIFO => rustix::fs::FileType::Fifo,
372 Self::S_IFDIR => rustix::fs::FileType::Directory,
373 Self::S_IFREG => rustix::fs::FileType::RegularFile,
374 _ => rustix::fs::FileType::Unknown,
375 };
376 ft.as_raw_mode() | (self.bits() & 0o7777) as rustix::fs::RawMode
377 }
378}
379
380#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
381pub struct Stat {
382 pub qid: QID,
383 pub mode: u32,
384 pub uid: u32,
385 pub gid: u32,
386 pub nlink: Option<u64>,
387 pub rdev: Option<u64>,
388 pub length: u64,
389 pub blksize: u64,
390 pub blocks: Option<u64>,
391 pub atime_sec: Option<u64>,
392 pub atime_nsec: Option<u64>,
393 pub mtime_sec: Option<u64>,
394 pub mtime_nsec: Option<u64>,
395 pub ctime_sec: Option<u64>,
396 pub ctime_nsec: Option<u64>,
397 pub btime_sec: Option<u64>,
398 pub btime_nsec: Option<u64>,
399 pub gen: Option<u64>,
400 pub data_version: Option<u64>,
401}
402
403impl Stat {
404 #[allow(clippy::unnecessary_cast)]
405 #[cfg(all(feature = "unix", not(target_os = "netbsd")))]
406 fn from_unix(meta: &rustix::fs::Stat) -> Self {
407 let mode = FileType::from_unix(meta.st_mode as u32);
408 Self {
409 qid: QID::new(QIDKind::Unknown, u64::MAX, u64::MAX),
410 mode: mode.bits(),
411 uid: meta.st_uid as u32,
412 gid: meta.st_gid as u32,
413 nlink: Some(meta.st_nlink as u64),
414 rdev: Some(meta.st_rdev as u64),
415 length: meta.st_size as u64,
416 blksize: meta.st_blksize as u64,
417 blocks: Some(meta.st_blocks as u64),
418 atime_sec: Some(meta.st_atime as u64),
419 atime_nsec: Some(meta.st_atime_nsec as u64),
420 mtime_sec: Some(meta.st_mtime as u64),
421 mtime_nsec: Some(meta.st_mtime_nsec as u64),
422 ctime_sec: Some(meta.st_ctime as u64),
423 ctime_nsec: Some(meta.st_ctime_nsec as u64),
424 btime_sec: None,
425 btime_nsec: None,
426 gen: None,
427 data_version: None,
428 }
429 }
430
431 #[allow(clippy::unnecessary_cast)]
432 #[cfg(all(feature = "unix", target_os = "netbsd"))]
433 fn from_unix(meta: &rustix::fs::Stat) -> Self {
434 let mode = FileType::from_unix(meta.st_mode as u32);
435 Self {
436 qid: QID::new(QIDKind::Unknown, u64::MAX, u64::MAX),
437 mode: mode.bits(),
438 uid: meta.st_uid as u32,
439 gid: meta.st_gid as u32,
440 nlink: Some(meta.st_nlink as u64),
441 rdev: Some(meta.st_rdev as u64),
442 length: meta.st_size as u64,
443 blksize: meta.st_blksize as u64,
444 blocks: Some(meta.st_blocks as u64),
445 atime_sec: Some(meta.st_atime as u64),
446 atime_nsec: Some(meta.st_atimensec as u64),
447 mtime_sec: Some(meta.st_mtime as u64),
448 mtime_nsec: Some(meta.st_mtimensec as u64),
449 ctime_sec: Some(meta.st_ctime as u64),
450 ctime_nsec: Some(meta.st_ctimensec as u64),
451 btime_sec: None,
452 btime_nsec: None,
453 gen: None,
454 data_version: None,
455 }
456 }
457}
458
459#[derive(FromPrimitive, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
460pub enum LockKind {
461 Read = 0,
462 Write = 1,
463}
464
465#[derive(FromPrimitive, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
466pub enum LockCommand {
467 ReadLock = 0,
468 WriteLock = 1,
469 Unlock = 2,
470}
471
472impl From<LockKind> for LockCommand {
473 fn from(kind: LockKind) -> Self {
474 match kind {
475 LockKind::Read => Self::ReadLock,
476 LockKind::Write => Self::WriteLock,
477 }
478 }
479}
480
481#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
482pub enum LockStatus {
483 Ok = 0,
484 Blocked = 1,
485 Error = 2,
486 Grace = 3,
487}
488
489#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
490pub struct Lock {
491 pub kind: LockKind,
492 pub start: u64,
493 pub length: u64,
494 pub proc_id: u32,
495 pub client_id: Vec<u8>,
496}
497
498#[derive(Clone, Debug)]
499pub struct DirEntry {
500 pub qid: QID,
501 pub offset: u64,
502 pub kind: u8,
503 pub name: Vec<u8>,
504 pub extension: Option<Vec<u8>>,
505 pub file_type: FileType,
506 pub size: u64,
507 pub metadata: fs::Metadata,
508}
509
510pub trait Backend {
520 fn auth(
528 &self,
529 meta: &Metadata,
530 afid: FID,
531 uname: &[u8],
532 aname: &[u8],
533 nuname: Option<u32>,
534 ) -> Result<QID>;
535 fn attach(
544 &self,
545 meta: &Metadata,
546 fid: FID,
547 afid: Option<FID>,
548 uname: &[u8],
549 aname: &[u8],
550 nuname: Option<u32>,
551 ) -> Result<QID>;
552 fn clunk(&self, meta: &Metadata, fid: FID) -> Result<()>;
557 fn clunk_all(&self, meta: &Metadata) -> Result<()>;
561 fn flush(&self, meta: &Metadata, tag: Tag) -> Result<()>;
567 fn open(&self, meta: &Metadata, fid: FID, mode: OpenMode) -> Result<(QID, Option<u32>)>;
576 fn is_open(&self, meta: &Metadata, fid: FID) -> Result<bool>;
581 #[allow(clippy::too_many_arguments)]
592 fn create(
593 &self,
594 meta: &Metadata,
595 fid: FID,
596 newfid: FID,
597 name: &[u8],
598 flags: OpenMode,
599 mode: FileType,
600 gid: Option<u32>,
601 ) -> Result<(QID, Option<u32>)>;
602 fn read(&self, meta: &Metadata, fid: FID, offset: u64, data: &mut [u8]) -> Result<u32>;
606 fn write(&self, meta: &Metadata, fid: FID, offset: u64, data: &[u8]) -> Result<u32>;
608 fn remove(&self, meta: &Metadata, fid: FID) -> Result<()>;
612 fn fsync(&self, meta: &Metadata, fid: FID);
613 fn walk(&self, meta: &Metadata, fid: FID, newfid: FID, name: &[&[u8]]) -> Result<Vec<QID>>;
622 fn resolve(&self, meta: &Metadata, fid: FID, newfid: FID, name: &[&[u8]]) -> Result<()>;
629 fn symlink(
633 &self,
634 meta: &Metadata,
635 fid: FID,
636 name: &[u8],
637 target: &[u8],
638 gid: Option<u32>,
639 ) -> Result<QID>;
640 #[allow(clippy::too_many_arguments)]
646 fn mknod(
647 &self,
648 meta: &Metadata,
649 fid: FID,
650 name: &[u8],
651 mode: FileType,
652 major: u32,
653 minor: u32,
654 gid: Option<u32>,
655 ) -> Result<QID>;
656 fn rename(&self, meta: &Metadata, fid: FID, dfid: FID, name: &[u8]) -> Result<()>;
658 fn readlink(&self, meta: &Metadata, fid: FID) -> Result<Vec<u8>>;
660 fn realpath(&self, meta: &Metadata, fid: FID) -> Result<Vec<Vec<u8>>>;
665 fn pathname(&self, meta: &Metadata, fid: FID) -> Result<Vec<Vec<u8>>>;
666 fn getattr(&self, meta: &Metadata, fid: FID, mask: StatValidity) -> Result<Stat>;
667 #[allow(clippy::too_many_arguments)]
674 fn setattr(
675 &self,
676 meta: &Metadata,
677 fid: FID,
678 mode: Option<u32>,
679 uid: Option<u32>,
680 gid: Option<u32>,
681 size: Option<u64>,
682 atime: Option<SystemTime>,
683 mtime: Option<SystemTime>,
684 set_atime: bool,
685 set_mtime: bool,
686 ) -> Result<()>;
687 fn xattrwalk(&self, meta: &Metadata, fid: FID, newfid: FID, name: &[u8]) -> Result<u64>;
688 fn xattrcreate(
689 &self,
690 meta: &Metadata,
691 fid: FID,
692 name: &[u8],
693 size: u64,
694 flags: u32,
695 ) -> Result<()>;
696 fn readdir(
707 &self,
708 meta: &Metadata,
709 fid: FID,
710 offset: u64,
711 count: u32,
712 offsetf: Box<dyn FnMut(&DirEntry) -> usize>,
713 lenf: Box<dyn FnMut(&DirEntry) -> usize>,
714 ) -> Result<Vec<DirEntry>>;
715 #[allow(clippy::too_many_arguments)]
716 fn lock(
717 &self,
718 meta: &Metadata,
719 fid: FID,
720 kind: LockCommand,
721 flags: u32,
722 start: u64,
723 length: u64,
724 proc_id: u32,
725 client_id: &[u8],
726 ) -> Result<LockStatus>;
727 #[allow(clippy::too_many_arguments)]
728 fn getlock(
729 &self,
730 meta: &Metadata,
731 fid: FID,
732 kind: LockKind,
733 start: u64,
734 length: u64,
735 proc_id: u32,
736 client_id: &[u8],
737 ) -> Result<Lock>;
738 fn link(&self, meta: &Metadata, dfid: FID, fid: FID, nane: &[u8]) -> Result<()>;
739 fn mkdir(
740 &self,
741 meta: &Metadata,
742 dfid: FID,
743 name: &[u8],
744 mode: FileType,
745 gid: Option<u32>,
746 ) -> Result<QID>;
747 fn renameat(
748 &self,
749 meta: &Metadata,
750 olddirfid: FID,
751 oldname: &[u8],
752 newdirfid: FID,
753 newname: &[u8],
754 ) -> Result<()>;
755 fn unlinkat(&self, meta: &Metadata, dirfd: FID, name: &[u8], flags: u32) -> Result<()>;
756}