lawn_9p/
backend.rs

1use crate::server::{
2    DirEntry, FileType, LinuxOpenMode, LinuxStat, LinuxStatValidity, Lock, LockCommand, LockKind,
3    LockStatus, Metadata, SimpleOpenMode, Stat, Tag, FID, QID,
4};
5use lawn_constants::Error;
6use lawn_fs::backend as fsbackend;
7use std::collections::BTreeMap;
8use std::fs;
9use std::sync::atomic::{AtomicU32, Ordering};
10use std::sync::Mutex;
11use std::time::SystemTime;
12
13type Result<T> = std::result::Result<T, Error>;
14
15#[cfg(feature = "unix")]
16pub mod libc;
17
18/// The interface to store and access files.
19pub trait Backend {
20    fn version(&self, meta: &Metadata, max_size: u32, version: &[u8]) -> Result<(u32, Vec<u8>)>;
21    fn auth(
22        &self,
23        meta: &Metadata,
24        afid: FID,
25        uname: &[u8],
26        aname: &[u8],
27        nuname: Option<u32>,
28    ) -> Result<QID>;
29    fn attach(
30        &self,
31        meta: &Metadata,
32        fid: FID,
33        afid: FID,
34        uname: &[u8],
35        aname: &[u8],
36        nuname: Option<u32>,
37    ) -> Result<QID>;
38    fn clunk(&self, meta: &Metadata, fid: FID) -> Result<()>;
39    fn clunk_all(&self, meta: &Metadata) -> Result<()>;
40    fn flush(&self, meta: &Metadata, tag: Tag) -> Result<()>;
41    fn open(&self, meta: &Metadata, fid: FID, mode: SimpleOpenMode) -> Result<(QID, u32)>;
42    fn lopen(&self, meta: &Metadata, fid: FID, flags: LinuxOpenMode) -> Result<(QID, u32)>;
43    fn create(
44        &self,
45        meta: &Metadata,
46        fid: FID,
47        name: &[u8],
48        perm: FileType,
49        mode: SimpleOpenMode,
50        extension: Option<&[u8]>,
51    ) -> Result<(QID, u32)>;
52    #[allow(clippy::too_many_arguments)]
53    fn lcreate(
54        &self,
55        meta: &Metadata,
56        fid: FID,
57        name: &[u8],
58        flags: u32,
59        mode: u32,
60        gid: u32,
61    ) -> Result<(QID, u32)>;
62    fn read(&self, meta: &Metadata, fid: FID, offset: u64, data: &mut [u8]) -> Result<u32>;
63    fn write(&self, meta: &Metadata, fid: FID, offset: u64, data: &[u8]) -> Result<u32>;
64    /// Remove the file specified by `fid`.
65    ///
66    /// Removes the specified file.  The `clunk` operation is issued separately by the server.
67    fn remove(&self, meta: &Metadata, fid: FID) -> Result<()>;
68    fn fsync(&self, meta: &Metadata, fid: FID);
69    fn stat(&self, meta: &Metadata, fid: FID) -> Result<Box<dyn Stat>>;
70    fn wstat(&self, meta: &Metadata, fid: FID, stat: &dyn Stat) -> Result<()>;
71    fn walk(&self, meta: &Metadata, fid: FID, newfid: FID, name: &[&[u8]]) -> Result<Vec<QID>>;
72    fn symlink(
73        &self,
74        meta: &Metadata,
75        fid: FID,
76        name: &[u8],
77        target: &[u8],
78        gid: u32,
79    ) -> Result<QID>;
80    #[allow(clippy::too_many_arguments)]
81    fn mknod(
82        &self,
83        meta: &Metadata,
84        fid: FID,
85        name: &[u8],
86        mode: u32,
87        major: u32,
88        minor: u32,
89        gid: u32,
90    ) -> Result<QID>;
91    fn rename(&self, meta: &Metadata, fid: FID, dfid: FID, name: &[u8]) -> Result<()>;
92    fn readlink(&self, meta: &Metadata, fid: FID) -> Result<Vec<u8>>;
93    fn getattr(&self, meta: &Metadata, fid: FID, mask: LinuxStatValidity) -> Result<LinuxStat>;
94    /// Set the attributes for the given FID.
95    ///
96    /// Set the mode, UID, GID, or size if they are `Some`.  If the corresponding set option is set
97    /// for a given time, set that time, either to the given time, or to the current time if it is
98    /// `None`.  Note that the `set_atime` and `set_mtime` do not correspond to those bits in the
99    /// protocol mask.
100    ///
101    /// This is only implemented in the 9P2000.L protocol.
102    #[allow(clippy::too_many_arguments)]
103    fn setattr(
104        &self,
105        meta: &Metadata,
106        fid: FID,
107        mode: Option<u32>,
108        uid: Option<u32>,
109        gid: Option<u32>,
110        size: Option<u64>,
111        atime: Option<SystemTime>,
112        mtime: Option<SystemTime>,
113        set_atime: bool,
114        set_mtime: bool,
115    ) -> Result<()>;
116    fn xattrwalk(&self, meta: &Metadata, fid: FID, newfid: FID, name: &[u8]) -> Result<u64>;
117    fn xattrcreate(
118        &self,
119        meta: &Metadata,
120        fid: FID,
121        name: &[u8],
122        size: u64,
123        flags: u32,
124    ) -> Result<()>;
125    fn readdir(&self, meta: &Metadata, fid: FID, offset: u64, count: u32) -> Result<Vec<DirEntry>>;
126    #[allow(clippy::too_many_arguments)]
127    fn lock(
128        &self,
129        meta: &Metadata,
130        fid: FID,
131        kind: LockCommand,
132        flags: u32,
133        start: u64,
134        length: u64,
135        proc_id: u32,
136        client_id: &[u8],
137    ) -> Result<LockStatus>;
138    #[allow(clippy::too_many_arguments)]
139    fn getlock(
140        &self,
141        meta: &Metadata,
142        fid: FID,
143        kind: LockKind,
144        start: u64,
145        length: u64,
146        proc_id: u32,
147        client_id: &[u8],
148    ) -> Result<Lock>;
149    fn link(&self, meta: &Metadata, dfid: FID, fid: FID, nane: &[u8]) -> Result<()>;
150    fn mkdir(&self, meta: &Metadata, dfid: FID, name: &[u8], mode: u32, gid: u32) -> Result<QID>;
151    fn renameat(
152        &self,
153        meta: &Metadata,
154        olddirfid: FID,
155        oldname: &[u8],
156        newdirfid: FID,
157        newname: &[u8],
158    ) -> Result<()>;
159    fn unlinkat(&self, meta: &Metadata, dirfd: FID, name: &[u8], flags: u32) -> Result<()>;
160}
161
162pub trait ToIdentifier: Ord {
163    fn to_identifier(&self) -> Vec<u8>;
164}
165
166impl<T: Ord + AsRef<[u8]>> ToIdentifier for T {
167    fn to_identifier(&self) -> Vec<u8> {
168        self.as_ref().into()
169    }
170}
171
172#[derive(Eq, PartialEq, Ord, PartialOrd)]
173pub enum FIDKind<AH: ToIdentifier, FH: ToIdentifier, DH: ToIdentifier, SH: ToIdentifier> {
174    Auth(AH),
175    File(FH),
176    Dir(DH),
177    Symlink(SH),
178    Special(SH),
179}
180
181#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
182pub enum FileKind {
183    Auth = 0x08,
184    File = 0x00,
185    Dir = 0x80,
186    Symlink = 0x02,
187    Special = 0xff,
188}
189
190impl From<fsbackend::QIDKind> for FileKind {
191    fn from(k: fsbackend::QIDKind) -> FileKind {
192        match k {
193            fsbackend::QIDKind::Directory => FileKind::Dir,
194            fsbackend::QIDKind::Regular => FileKind::File,
195            fsbackend::QIDKind::FIFO => FileKind::Special,
196            fsbackend::QIDKind::Symlink => FileKind::Symlink,
197            fsbackend::QIDKind::BlockDevice => FileKind::Special,
198            fsbackend::QIDKind::CharacterDevice => FileKind::Special,
199            fsbackend::QIDKind::Socket => FileKind::Special,
200            fsbackend::QIDKind::Authentication => FileKind::Auth,
201            fsbackend::QIDKind::Unknown => FileKind::Special,
202        }
203    }
204}
205
206impl FileKind {
207    #[cfg(feature = "unix")]
208    pub fn from_metadata(metadata: &fs::Metadata) -> Self {
209        use std::os::unix::fs::FileTypeExt;
210
211        let ft = metadata.file_type();
212        if ft.is_fifo() || ft.is_socket() || ft.is_block_device() || ft.is_char_device() {
213            Self::Special
214        } else if ft.is_dir() {
215            Self::Dir
216        } else if ft.is_symlink() {
217            Self::Symlink
218        } else {
219            Self::File
220        }
221    }
222}
223
224#[derive(Default)]
225pub struct QIDMapper {
226    next: AtomicU32,
227    tree: Mutex<BTreeMap<u64, u32>>,
228}
229
230impl QIDMapper {
231    pub fn new() -> QIDMapper {
232        Self {
233            next: AtomicU32::new(0),
234            tree: Mutex::new(BTreeMap::new()),
235        }
236    }
237    pub fn qid(&self, q: fsbackend::QID) -> QID {
238        let id = {
239            let mut g = self.tree.lock().unwrap();
240            *g.entry(q.dev())
241                .or_insert_with(|| self.next.fetch_add(1, Ordering::AcqRel))
242        };
243        let mut data = [0u8; 13];
244        data[0] = FileKind::from(q.kind()) as u8;
245        data[1..5].copy_from_slice(&id.to_le_bytes());
246        data[5..].copy_from_slice(&q.ino().to_le_bytes());
247        QID(data)
248    }
249}