fuse2rs/
lib.rs

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	// OPTIONAL
51
52	// TODO: KernelConfig
53	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}