1use std::{
2 ffi::CString,
3 io::{Error, Result},
4 path::Path,
5 time::SystemTime,
6};
7
8use libc::{gid_t, mode_t, uid_t};
9
10mod ll;
11
12pub use crate::ll::DirFiller;
13
14pub struct Request {
15 pub uid: uid_t,
16 pub gid: gid_t,
17 pub umask: mode_t,
18}
19
20pub struct FileInfo {
21 pub flags: i32,
22 pub fh: u64,
23 pub direct_io: bool,
24 pub keep_cache: bool,
25 pub flush: bool,
26 pub nonseekable: bool,
27}
28
29pub trait Filesystem {
30 fn getattr(&mut self, _req: &Request, path: &Path) -> Result<FileAttr>;
31
32 fn readdir(
33 &mut self,
34 _req: &Request,
35 path: &Path,
36 off: u64,
37 filler: &mut DirFiller,
38 _info: &FileInfo,
39 ) -> Result<()>;
40
41 fn read(
42 &mut self,
43 _req: &Request,
44 path: &Path,
45 off: u64,
46 buf: &mut [u8],
47 _info: &FileInfo,
48 ) -> Result<usize>;
49
50 fn init(&mut self, _req: &Request) {}
54 fn destroy(&mut self) {}
55
56 fn open(&mut self, _req: &Request, path: &Path, _info: &mut FileInfo) -> Result<()> {
57 let _ = path;
58 Ok(())
59 }
60
61 fn opendir(&mut self, _req: &Request, path: &Path, _info: &mut FileInfo) -> Result<()> {
62 let _ = path;
63 Ok(())
64 }
65
66 fn release(&mut self, _req: &Request, path: &Path, _info: &FileInfo) -> Result<()> {
67 let _ = path;
68 Ok(())
69 }
70
71 fn flush(&mut self, _req: &Request, path: &Path, _info: &FileInfo) -> Result<()> {
72 let _ = path;
73 Ok(())
74 }
75
76 fn releasedir(&mut self, _req: &Request, path: &Path, _info: &FileInfo) -> Result<()> {
77 let _ = path;
78 Ok(())
79 }
80
81 fn statfs(&mut self, _req: &Request, path: &Path) -> Result<Statfs> {
82 let _ = path;
83 Ok(Statfs::default())
84 }
85
86 fn readlink(&mut self, _req: &Request, path: &Path, buf: &mut [u8]) -> Result<()> {
87 let _ = (path, buf);
88 Err(Error::from_raw_os_error(libc::ENOSYS))
89 }
90
91 fn unlink(&mut self, _req: &Request, path: &Path) -> Result<()> {
92 let _ = path;
93 Err(Error::from_raw_os_error(libc::ENOSYS))
94 }
95
96 fn rmdir(&mut self, _req: &Request, path: &Path) -> Result<()> {
97 let _ = path;
98 Err(Error::from_raw_os_error(libc::ENOSYS))
99 }
100
101 fn mkdir(&mut self, _req: &Request, path: &Path, mode: u32) -> Result<()> {
102 let _ = (path, mode);
103 Err(Error::from_raw_os_error(libc::ENOSYS))
104 }
105
106 fn mknod(&mut self, _req: &Request, path: &Path, mode: u32, dev: u32) -> Result<()> {
107 let _ = (path, mode, dev);
108 Err(Error::from_raw_os_error(libc::ENOSYS))
109 }
110
111 fn create(&mut self, _req: &Request, path: &Path, mode: u32, info: &FileInfo) -> Result<()> {
112 let _ = (path, mode, info);
113 Err(Error::from_raw_os_error(libc::ENOSYS))
114 }
115
116 fn chown(&mut self, _req: &Request, path: &Path, uid: Option<u32>, gid: Option<u32>) -> Result<()> {
117 let _ = (path, uid, gid);
118 Err(Error::from_raw_os_error(libc::ENOSYS))
119 }
120
121 fn chmod(&mut self, _req: &Request, path: &Path, mode: u32) -> Result<()> {
122 let _ = (path, mode);
123 Err(Error::from_raw_os_error(libc::ENOSYS))
124 }
125
126 fn utime(&mut self, _req: &Request, path: &Path, atime: SystemTime, mtime: SystemTime) -> Result<()> {
127 let _ = (path, atime, mtime);
128 Err(Error::from_raw_os_error(libc::ENOSYS))
129 }
130
131 fn write(
132 &mut self,
133 _req: &Request,
134 path: &Path,
135 off: u64,
136 buf: &[u8],
137 _info: &FileInfo,
138 ) -> Result<usize> {
139 let _ = (path, off, buf);
140 Err(Error::from_raw_os_error(libc::ENOSYS))
141 }
142
143 fn link(&mut self, _req: &Request, name1: &Path, name2: &Path) -> Result<()> {
144 let _ = (name1, name2);
145 Err(Error::from_raw_os_error(libc::ENOSYS))
146 }
147
148 fn symlink(&mut self, _req: &Request, name1: &Path, name2: &Path) -> Result<()> {
149 let _ = (name1, name2);
150 Err(Error::from_raw_os_error(libc::ENOSYS))
151 }
152
153 fn rename(&mut self, _req: &Request, from: &Path, to: &Path) -> Result<()> {
154 let _ = (from, to);
155 Err(Error::from_raw_os_error(libc::ENOSYS))
156 }
157
158 fn truncate(&mut self, _req: &Request, path: &Path, size: u64) -> Result<()> {
159 let _ = (path, size);
160 Err(Error::from_raw_os_error(libc::ENOSYS))
161 }
162}
163
164#[derive(Debug, Default, Clone, Copy)]
165pub enum FileType {
166 #[default]
167 RegularFile,
168 Directory,
169 NamedPipe,
170 Socket,
171 CharDevice,
172 BlockDevice,
173 Symlink,
174}
175
176#[derive(Debug, Default, Clone)]
177pub struct Statfs {
178 pub bsize: u32,
179 pub frsize: u32,
180 pub blocks: u64,
181 pub bfree: u64,
182 pub bavail: u64,
183 pub files: u64,
184 pub ffree: u64,
185 pub favail: u64,
186}
187
188#[derive(Debug, Clone)]
189pub struct FileAttr {
190 pub ino: u64,
191 pub size: u64,
192 pub blocks: u64,
193 pub atime: SystemTime,
194 pub mtime: SystemTime,
195 pub ctime: SystemTime,
196 pub btime: SystemTime,
197 pub kind: FileType,
198 pub perm: u16,
199 pub uid: u32,
200 pub gid: u32,
201 pub rdev: u32,
202 pub blksize: u32,
203 pub flags: u32,
204 pub nlink: u32,
205}
206
207impl Default for FileAttr {
208 fn default() -> Self {
209 Self {
210 ino: 0,
211 size: 0,
212 blocks: 0,
213 atime: SystemTime::UNIX_EPOCH,
214 mtime: SystemTime::UNIX_EPOCH,
215 ctime: SystemTime::UNIX_EPOCH,
216 btime: SystemTime::UNIX_EPOCH,
217 kind: FileType::default(),
218 perm: 0,
219 uid: 0,
220 gid: 0,
221 rdev: 0,
222 blksize: 512,
223 flags: 0,
224 nlink: 1,
225 }
226 }
227}
228
229#[derive(Debug, Clone)]
230pub enum MountOption {
231 Foreground,
232 Debug,
233 AllowOther,
234 DefaultPermissions,
235 KernelCache,
236 Ro,
237 Atime,
238 NoAtime,
239 Dev,
240 NoDev,
241 Suid,
242 NoSuid,
243 Exec,
244 NoExec,
245 Sync,
246 Async,
247 UseIno,
248 ReaddirIno,
249 HardRemove,
250 Uid(u32),
251 Gid(u32),
252 Umask(u16),
253 Custom(CString),
254}
255
256impl MountOption {
257 fn into_cstring(self) -> CString {
258 match self {
259 Self::Foreground => c"-f".into(),
260 Self::Debug => c"-d".into(),
261 Self::AllowOther => c"-oallow_other".into(),
262 Self::DefaultPermissions => c"-odefault_permissions".into(),
263 Self::KernelCache => c"-okernel_cache".into(),
264 Self::Ro => c"-oro".into(),
265 Self::Atime => c"-oatime".into(),
266 Self::NoAtime => c"-onoatime".into(),
267 Self::Dev => c"-odev".into(),
268 Self::NoDev => c"-onodev".into(),
269 Self::Exec => c"-oexec".into(),
270 Self::NoExec => c"-onoexec".into(),
271 Self::Suid => c"-osuid".into(),
272 Self::NoSuid => c"-onosuid".into(),
273 Self::Sync => c"-osync".into(),
274 Self::Async => c"-oasync".into(),
275 Self::UseIno => c"-ouse_ino".into(),
276 Self::ReaddirIno => c"-oreaddir_ino".into(),
277 Self::HardRemove => c"-ohard_remove".into(),
278 Self::Uid(uid) => CString::new(format!("-ouid={uid}")).unwrap(),
279 Self::Gid(gid) => CString::new(format!("-ogid={gid}")).unwrap(),
280 Self::Umask(mask) => CString::new(format!("-oumask={mask:o}")).unwrap(),
281 Self::Custom(c) => c,
282 }
283 }
284}
285
286pub fn mount(mp: &Path, fs: impl Filesystem + 'static, opts: Vec<MountOption>) -> Result<()> {
287 let opts = opts.into_iter().map(|opt| opt.into_cstring()).collect();
288 crate::ll::xmount(mp, Box::new(fs), opts)
289}