lawn_fs/
backend.rs

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/// The kind of file.
13#[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    /// Compute the kind of file from a `FileType`.
28    #[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    /// Compute the kind of file from a `stat` call.
43    #[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/// A unique identifier for a file that persists over the life of file on disk.
65#[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    /// Create this unique identifier from a kind of file, a device, and an inode number.
84    pub fn new(kind: QIDKind, dev: u64, ino: u64) -> QID {
85        Self { kind, dev, ino }
86    }
87
88    /// Return the kind of file represented by this `QID`.
89    pub fn kind(&self) -> QIDKind {
90        self.kind
91    }
92
93    /// Return the device number.
94    pub fn dev(&self) -> u64 {
95        self.dev
96    }
97
98    /// Return the inode number.
99    pub fn ino(&self) -> u64 {
100        self.ino
101    }
102}
103
104/// A unique identifier for an file or request, chosen by the server (which in some cases comes
105/// from the client).
106///
107/// Note that this can represent both an open file and a closed file.  Initially, the file is
108/// closed, and it is opened by an explicit request.
109#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
110pub struct FID {
111    id: u64,
112}
113
114impl FID {
115    /// Create a new file identifier.
116    pub const fn new(id: u64) -> FID {
117        Self { id }
118    }
119
120    /// Return the 64-bit integer representing this file number.
121    pub const fn to_u64(&self) -> u64 {
122        self.id
123    }
124}
125
126/// A request tag.
127///
128/// This is generally used by 9P-based protocols and ignored by SFTP-based protocols.
129#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
130pub struct Tag {
131    id: u64,
132}
133
134impl Tag {
135    /// Create a new tag.
136    pub const fn new(id: u64) -> Tag {
137        Self { id }
138    }
139
140    /// Return the 64-bit integer representing this tag.
141    pub const fn to_u64(&self) -> u64 {
142        self.id
143    }
144}
145
146/// The kind of 9P protocol in use.
147#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
148#[non_exhaustive]
149pub enum Plan9Type {
150    /// This is the original 9P2000 protocol.
151    Original,
152    /// This is the Unix-based 9P2000.u protocol.
153    Unix,
154    /// This is the Linux-based 9P2000.L protocol.
155    Linux,
156}
157
158/// The kind of SFTP protocol in use.
159#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
160#[non_exhaustive]
161pub enum SFTPType {
162    /// This is version 3 of the SFTP protocol.
163    V3,
164}
165
166/// The kind of SFTP extensions in use.
167#[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/// The kind of protocol in use.
177#[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/// Metadata about a request.
185#[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    /// Optimize for efficiency.
228    ///
229    /// This flag controls whether the request needs any valid QID data.
230    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        // We don't use these, but they need to exist so from_bits works properly.
268        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
510/// The interface to store and access files.
511///
512/// This is based around the joint needs of 9P and SFTP, and so the interface reflects that.
513/// Users which are interested in a more Unix-like interface will probably find most of the
514/// 9P2000.L-like interfaces more useful, along with some of the SFTP-based interfaces.
515///
516/// Implementations of this trait are free to perform optimizations depending on the protocol.  For
517/// example, SFTP does not require the `kind` field of the QID field, so it will generally be set
518/// to `QIDKind::Unknown`.
519pub trait Backend {
520    /// Create a handle to authenticate to this mount point.
521    ///
522    /// `afid` represents an `FID` to use to authenticate.  `uname` is the username and `nuname` is
523    /// the user ID, if any.  `aname` is the resource to access.  Returns the `QID` for `afid`.
524    ///
525    /// The FID used to authenticate can be passed to `read` and `write` to perform an
526    /// authenticator-specific authentication protocol.
527    fn auth(
528        &self,
529        meta: &Metadata,
530        afid: FID,
531        uname: &[u8],
532        aname: &[u8],
533        nuname: Option<u32>,
534    ) -> Result<QID>;
535    /// Attach a mount point to `fid`.
536    ///
537    /// `afid` represents an `FID` to use to authenticate if it is `Some`, which should have been
538    /// previously passed to `auth`.  If it is `None`, authentication is anonymous or controlled at
539    /// a higher-level protocol.
540    ///
541    /// `uname` is the username and `nuname` is the user ID, if any.  `aname` is the resource to
542    /// access.  Returns the `QID` for `fid`.
543    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    /// Invalidates `fid`.
553    ///
554    /// This indicates that the given `FID` is no longer needed and that the server should free
555    /// resources with it.
556    fn clunk(&self, meta: &Metadata, fid: FID) -> Result<()>;
557    /// Invalidates all outstanding `FID` values.
558    ///
559    /// This indicates that the server should free resources associated with all file identifiers.
560    fn clunk_all(&self, meta: &Metadata) -> Result<()>;
561    /// Abort the operation associated with `Tag`.
562    ///
563    /// This requests that the backend abort any in-progress operations with the given tag.  If
564    /// the request has already completed, returns `Err(Error::ESRCH)`.  If this functionality is
565    /// not implemented, returns `Err(Error::ENOSYS)`.
566    fn flush(&self, meta: &Metadata, tag: Tag) -> Result<()>;
567    /// Open a file associated with a `fid` using `mode` as permissions.
568    ///
569    /// `fid` should point to a file, which may or may not be open.
570    ///
571    /// On success, this function returns the `QID` for the file, and an I/O unit value.  If the
572    /// I/O unit value is `Some`, it indicates the maximum number of bytes that are guaranteed to
573    /// be read or written from the file without breaking it into multiple messages.  If it is
574    /// `None`, no guarantees are provided.
575    fn open(&self, meta: &Metadata, fid: FID, mode: OpenMode) -> Result<(QID, Option<u32>)>;
576    /// Determine whether the file `fid` is open.
577    ///
578    /// If this `fid` is valid and is open, returns `Ok(true)`.  If it is valid and closed, returns
579    /// `Ok(false)`.  If it is not valid, returns `Err(Error::EBADF)`.
580    fn is_open(&self, meta: &Metadata, fid: FID) -> Result<bool>;
581    /// Create a regular file with `name` under the directory `fid`, opening it, and saving the
582    /// resulting file into `newfid`.
583    ///
584    /// `flags` represents the open mode flags, and `mode` is the set of permissions for the file
585    /// to have.  `gid` is the group ID for this file.
586    ///
587    /// On success, this function returns the `QID` for the file, and an I/O unit value.  If the
588    /// I/O unit value is `Some`, it indicates the maximum number of bytes that are guaranteed to
589    /// be read or written from the file without breaking it into multiple messages.  If it is
590    /// `None`, no guarantees are provided.
591    #[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    /// Read data from `fid`, which must be open, at the given offset, into `data`.
603    ///
604    /// On EOF, this returns 0.
605    fn read(&self, meta: &Metadata, fid: FID, offset: u64, data: &mut [u8]) -> Result<u32>;
606    /// Write data to `fid`, which must be open, at the given offset, from `data`.
607    fn write(&self, meta: &Metadata, fid: FID, offset: u64, data: &[u8]) -> Result<u32>;
608    /// Remove the file specified by `fid`.
609    ///
610    /// Removes the specified file.  The `clunk` operation is issued separately by the server.
611    fn remove(&self, meta: &Metadata, fid: FID) -> Result<()>;
612    fn fsync(&self, meta: &Metadata, fid: FID);
613    /// Walk a path starting at `fid`, using the components in `name`, and leave the result in
614    /// `newfid`.
615    ///
616    /// This is one of the only two ways to resolve paths, which must be relative to an existing
617    /// directory.  Directory separators are not permitted in `name` and will be rejected with
618    /// `Err(Error::EINVAL)`.  However, `.` and `..` components are permitted, and if a user would
619    /// escape the root, the path simply resolves to the root instead.  Returns a list of the `QID`
620    /// for each component.
621    fn walk(&self, meta: &Metadata, fid: FID, newfid: FID, name: &[&[u8]]) -> Result<Vec<QID>>;
622    /// Walk a path starting at `fid`, using the components in `name`, and leave the result in
623    /// `newfid`.
624    ///
625    /// The behaviour here is exactly the same as for `walk`, except that QIDs are not computed and
626    /// attempts to escape the root may return `Err(Error::EACCES)`.  This is substantially more
627    /// efficient than `walk` when possible.
628    fn resolve(&self, meta: &Metadata, fid: FID, newfid: FID, name: &[&[u8]]) -> Result<()>;
629    /// Create a symlink in the directory `fid` with name `name`, pointing to `target`.
630    ///
631    /// The gid of the requesting user is `gid`, if known.
632    fn symlink(
633        &self,
634        meta: &Metadata,
635        fid: FID,
636        name: &[u8],
637        target: &[u8],
638        gid: Option<u32>,
639    ) -> Result<QID>;
640    /// Create a device in the directory `fid` with name `name` and mode `mode``.
641    ///
642    /// The device major and minor must be specified.
643    ///
644    /// The gid of the requesting user is `gid`, if known.
645    #[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    /// Rename the file at `fid` to `name`, which is under the directory `dfid`.
657    fn rename(&self, meta: &Metadata, fid: FID, dfid: FID, name: &[u8]) -> Result<()>;
658    /// Read the value of the symlink at `fid`.
659    fn readlink(&self, meta: &Metadata, fid: FID) -> Result<Vec<u8>>;
660    /// Canonicalize the path at `fid`.
661    ///
662    /// The resolved path is returned as a `Vec` of path segments.  If the operation would resolve
663    /// to a path outside of the root, returns `Err(Error::EACCES)`.
664    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    /// Set the attributes for the given FID.
668    ///
669    /// Set the mode, UID, GID, or size if they are `Some`.  If the corresponding set option is set
670    /// for a given time, set that time, either to the given time, or to the current time if it is
671    /// `None`.  Note that the `set_atime` and `set_mtime` do not correspond to those bits in the
672    /// protocol mask.
673    #[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    /// Read one or more directory entries from `fid`.
697    ///
698    /// The `offset` field must be 0 the first time this request has been made, and otherwise an
699    /// offset returned from the last item in the immediately previous iteration of this function
700    /// (the second item in the returned tuple).  `offsetf` returns the increment in offset for
701    /// each directory entry that is processed.  `count` is the maximum number of bytes to
702    /// serialize, and `lenf` provides the number of bytes to serialize each directory entry.
703    ///
704    /// On success, returns a `Vec` of entries; when iteration has completed, the `Vec` will be
705    /// empty.
706    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}