hermit/fs/
fuse.rs

1use alloc::borrow::ToOwned;
2use alloc::boxed::Box;
3use alloc::ffi::CString;
4use alloc::string::String;
5use alloc::sync::Arc;
6use alloc::vec::Vec;
7use core::marker::PhantomData;
8use core::mem::{MaybeUninit, align_of, offset_of, size_of};
9use core::sync::atomic::{AtomicU64, Ordering};
10use core::task::Poll;
11use core::{future, mem};
12
13use align_address::Align;
14use async_lock::Mutex;
15use async_trait::async_trait;
16use embedded_io::{ErrorType, Read, Write};
17use fuse_abi::linux::*;
18
19use crate::alloc::string::ToString;
20#[cfg(not(feature = "pci"))]
21use crate::arch::kernel::mmio::get_filesystem_driver;
22#[cfg(feature = "pci")]
23use crate::drivers::pci::get_filesystem_driver;
24use crate::drivers::virtio::virtqueue::error::VirtqError;
25use crate::errno::Errno;
26use crate::executor::block_on;
27use crate::fd::PollEvent;
28use crate::fs::fuse::ops::SetAttrValidFields;
29use crate::fs::{
30	self, AccessPermission, DirectoryEntry, FileAttr, NodeKind, ObjectInterface, OpenOption,
31	SeekWhence, VfsNode,
32};
33use crate::mm::device_alloc::DeviceAlloc;
34use crate::syscalls::Dirent64;
35use crate::time::{time_t, timespec};
36use crate::{arch, io};
37
38// response out layout eg @ https://github.com/zargony/fuse-rs/blob/bf6d1cf03f3277e35b580f3c7b9999255d72ecf3/src/ll/request.rs#L44
39// op in/out sizes/layout: https://github.com/hanwen/go-fuse/blob/204b45dba899dfa147235c255908236d5fde2d32/fuse/opcode.go#L439
40// possible responses for command: qemu/tools/virtiofsd/fuse_lowlevel.h
41
42const MAX_READ_LEN: usize = 1024 * 64;
43const MAX_WRITE_LEN: usize = 1024 * 64;
44
45const U64_SIZE: usize = mem::size_of::<u64>();
46
47const S_IFLNK: u32 = 0o120_000;
48const S_IFMT: u32 = 0o170_000;
49
50pub(crate) trait FuseInterface {
51	fn send_command<O: ops::Op + 'static>(
52		&mut self,
53		cmd: Cmd<O>,
54		rsp_payload_len: u32,
55	) -> Result<Rsp<O>, FuseError>
56	where
57		<O as ops::Op>::InStruct: Send,
58		<O as ops::Op>::OutStruct: Send;
59
60	fn get_mount_point(&self) -> String;
61}
62
63pub(crate) mod ops {
64	#![allow(clippy::type_complexity)]
65	use alloc::boxed::Box;
66	use alloc::ffi::CString;
67
68	use fuse_abi::linux;
69	use fuse_abi::linux::*;
70
71	use super::Cmd;
72	use crate::fd::PollEvent;
73	use crate::fs::{FileAttr, SeekWhence};
74
75	#[repr(C)]
76	#[derive(Debug, Default, Copy, Clone, Hash, PartialEq, Eq)]
77	pub(crate) struct CreateOut {
78		pub entry: fuse_entry_out,
79		pub open: fuse_open_out,
80	}
81
82	pub(crate) trait Op {
83		const OP_CODE: fuse_opcode;
84
85		type InStruct: core::fmt::Debug;
86		type InPayload: ?Sized;
87		type OutStruct: core::fmt::Debug;
88		type OutPayload: ?Sized;
89	}
90
91	#[derive(Debug)]
92	pub(crate) struct Init;
93
94	impl Op for Init {
95		const OP_CODE: fuse_opcode = fuse_opcode::FUSE_INIT;
96		type InStruct = fuse_init_in;
97		type InPayload = ();
98		type OutStruct = fuse_init_out;
99		type OutPayload = ();
100	}
101
102	impl Init {
103		pub(crate) fn create() -> (Cmd<Self>, u32) {
104			let cmd = Cmd::new(
105				FUSE_ROOT_ID,
106				fuse_init_in {
107					major: 7,
108					minor: 31,
109					..Default::default()
110				},
111			);
112			(cmd, 0)
113		}
114	}
115
116	#[derive(Debug)]
117	pub(crate) struct Create;
118
119	impl Op for Create {
120		const OP_CODE: fuse_opcode = fuse_opcode::FUSE_CREATE;
121		type InStruct = fuse_create_in;
122		type InPayload = CString;
123		type OutStruct = CreateOut;
124		type OutPayload = ();
125	}
126
127	impl Create {
128		#[allow(clippy::self_named_constructors)]
129		pub(crate) fn create(path: CString, flags: u32, mode: u32) -> (Cmd<Self>, u32) {
130			let cmd = Cmd::with_cstring(
131				FUSE_ROOT_ID,
132				fuse_create_in {
133					flags,
134					mode,
135					..Default::default()
136				},
137				path,
138			);
139			(cmd, 0)
140		}
141	}
142
143	#[derive(Debug)]
144	pub(crate) struct Open;
145
146	impl Op for Open {
147		const OP_CODE: fuse_opcode = fuse_opcode::FUSE_OPEN;
148		type InStruct = fuse_open_in;
149		type InPayload = ();
150		type OutStruct = fuse_open_out;
151		type OutPayload = ();
152	}
153
154	impl Open {
155		pub(crate) fn create(nid: u64, flags: u32) -> (Cmd<Self>, u32) {
156			let cmd = Cmd::new(
157				nid,
158				fuse_open_in {
159					flags,
160					..Default::default()
161				},
162			);
163			(cmd, 0)
164		}
165	}
166
167	#[derive(Debug)]
168	pub(crate) struct Write;
169
170	impl Op for Write {
171		const OP_CODE: fuse_opcode = fuse_opcode::FUSE_WRITE;
172		type InStruct = fuse_write_in;
173		type InPayload = [u8];
174		type OutStruct = fuse_write_out;
175		type OutPayload = ();
176	}
177
178	impl Write {
179		pub(crate) fn create(nid: u64, fh: u64, buf: Box<[u8]>, offset: u64) -> (Cmd<Self>, u32) {
180			let cmd = Cmd::with_boxed_slice(
181				nid,
182				fuse_write_in {
183					fh,
184					offset,
185					size: buf.len().try_into().unwrap(),
186					..Default::default()
187				},
188				buf,
189			);
190			(cmd, 0)
191		}
192	}
193
194	#[derive(Debug)]
195	pub(crate) struct Read;
196
197	impl Op for Read {
198		const OP_CODE: fuse_opcode = fuse_opcode::FUSE_READ;
199		type InStruct = fuse_read_in;
200		type InPayload = ();
201		type OutStruct = ();
202		type OutPayload = [u8];
203	}
204
205	impl Read {
206		pub(crate) fn create(nid: u64, fh: u64, size: u32, offset: u64) -> (Cmd<Self>, u32) {
207			let cmd = Cmd::new(
208				nid,
209				fuse_read_in {
210					fh,
211					offset,
212					size,
213					..Default::default()
214				},
215			);
216			(cmd, size)
217		}
218	}
219
220	#[derive(Debug)]
221	pub(crate) struct Lseek;
222
223	impl Op for Lseek {
224		const OP_CODE: fuse_opcode = fuse_opcode::FUSE_LSEEK;
225		type InStruct = fuse_lseek_in;
226		type InPayload = ();
227		type OutStruct = fuse_lseek_out;
228		type OutPayload = ();
229	}
230
231	impl Lseek {
232		pub(crate) fn create(
233			nid: u64,
234			fh: u64,
235			offset: isize,
236			whence: SeekWhence,
237		) -> (Cmd<Self>, u32) {
238			let cmd = Cmd::new(
239				nid,
240				fuse_lseek_in {
241					fh,
242					offset: i64::try_from(offset).unwrap() as u64,
243					whence: u8::from(whence).into(),
244					..Default::default()
245				},
246			);
247			(cmd, 0)
248		}
249	}
250
251	#[derive(Debug)]
252	pub(crate) struct Getattr;
253
254	impl Op for Getattr {
255		const OP_CODE: fuse_opcode = fuse_opcode::FUSE_GETATTR;
256		type InStruct = fuse_getattr_in;
257		type InPayload = ();
258		type OutStruct = fuse_attr_out;
259		type OutPayload = ();
260	}
261
262	impl Getattr {
263		pub(crate) fn create(nid: u64, fh: u64, getattr_flags: u32) -> (Cmd<Self>, u32) {
264			let cmd = Cmd::new(
265				nid,
266				fuse_getattr_in {
267					getattr_flags,
268					fh,
269					..Default::default()
270				},
271			);
272			(cmd, 0)
273		}
274	}
275
276	#[derive(Debug)]
277	pub(crate) struct Setattr;
278
279	impl Op for Setattr {
280		const OP_CODE: fuse_opcode = fuse_opcode::FUSE_SETATTR;
281		type InStruct = fuse_setattr_in;
282		type InPayload = ();
283		type OutStruct = fuse_attr_out;
284		type OutPayload = ();
285	}
286
287	bitflags! {
288		#[derive(Debug, Copy, Clone, Default)]
289		pub struct SetAttrValidFields: u32 {
290			const FATTR_MODE = linux::FATTR_MODE;
291			const FATTR_UID = linux::FATTR_UID;
292			const FATTR_GID = linux::FATTR_GID;
293			const FATTR_SIZE = linux::FATTR_SIZE;
294			const FATTR_ATIME = linux::FATTR_ATIME;
295			const FATTR_MTIME = linux::FATTR_MTIME;
296			const FATTR_FH = linux::FATTR_FH;
297			const FATTR_ATIME_NOW = linux::FATTR_ATIME_NOW;
298			const FATTR_MTIME_NOW = linux::FATTR_MTIME_NOW;
299			const FATTR_LOCKOWNER = linux::FATTR_LOCKOWNER;
300			const FATTR_CTIME = linux::FATTR_CTIME;
301			const FATTR_KILL_SUIDGID = linux::FATTR_KILL_SUIDGID;
302		}
303	}
304
305	impl Setattr {
306		pub(crate) fn create(
307			nid: u64,
308			fh: u64,
309			attr: FileAttr,
310			valid_attr: SetAttrValidFields,
311		) -> (Cmd<Self>, u32) {
312			let cmd = Cmd::new(
313				nid,
314				fuse_setattr_in {
315					valid: valid_attr
316						.difference(
317							// Remove unsupported attributes
318							SetAttrValidFields::FATTR_LOCKOWNER,
319						)
320						.bits(),
321					padding: 0,
322					fh,
323
324					// Fuse attributes mapping: https://github.com/libfuse/libfuse/blob/fc1c8da0cf8a18d222cb1feed0057ba44ea4d18f/lib/fuse_lowlevel.c#L105
325					size: attr.st_size as u64,
326					atime: attr.st_atim.tv_sec as u64,
327					atimensec: attr.st_atim.tv_nsec as u32,
328					mtime: attr.st_ctim.tv_sec as u64,
329					mtimensec: attr.st_ctim.tv_nsec as u32,
330					ctime: attr.st_ctim.tv_sec as u64,
331					ctimensec: attr.st_ctim.tv_nsec as u32,
332					mode: attr.st_mode.bits(),
333					unused4: 0,
334					uid: attr.st_uid,
335					gid: attr.st_gid,
336					unused5: 0,
337
338					lock_owner: 0, // unsupported
339				},
340			);
341
342			(cmd, 0)
343		}
344	}
345
346	#[derive(Debug)]
347	pub(crate) struct Readlink;
348
349	impl Op for Readlink {
350		const OP_CODE: fuse_opcode = fuse_opcode::FUSE_READLINK;
351		type InStruct = ();
352		type InPayload = ();
353		type OutStruct = ();
354		type OutPayload = [u8];
355	}
356
357	impl Readlink {
358		pub(crate) fn create(nid: u64, size: u32) -> (Cmd<Self>, u32) {
359			let cmd = Cmd::new(nid, ());
360			(cmd, size)
361		}
362	}
363
364	#[derive(Debug)]
365	pub(crate) struct Release;
366
367	impl Op for Release {
368		const OP_CODE: fuse_opcode = fuse_opcode::FUSE_RELEASE;
369		type InStruct = fuse_release_in;
370		type InPayload = ();
371		type OutStruct = ();
372		type OutPayload = ();
373	}
374
375	impl Release {
376		pub(crate) fn create(nid: u64, fh: u64) -> (Cmd<Self>, u32) {
377			let cmd = Cmd::new(
378				nid,
379				fuse_release_in {
380					fh,
381					..Default::default()
382				},
383			);
384			(cmd, 0)
385		}
386	}
387
388	#[derive(Debug)]
389	pub(crate) struct Poll;
390
391	impl Op for Poll {
392		const OP_CODE: fuse_opcode = fuse_opcode::FUSE_POLL;
393		type InStruct = fuse_poll_in;
394		type InPayload = ();
395		type OutStruct = fuse_poll_out;
396		type OutPayload = ();
397	}
398
399	impl Poll {
400		pub(crate) fn create(nid: u64, fh: u64, kh: u64, event: PollEvent) -> (Cmd<Self>, u32) {
401			let cmd = Cmd::new(
402				nid,
403				fuse_poll_in {
404					fh,
405					kh,
406					events: event.bits() as u32,
407					..Default::default()
408				},
409			);
410			(cmd, 0)
411		}
412	}
413
414	#[derive(Debug)]
415	pub(crate) struct Mkdir;
416
417	impl Op for Mkdir {
418		const OP_CODE: fuse_opcode = fuse_opcode::FUSE_MKDIR;
419		type InStruct = fuse_mkdir_in;
420		type InPayload = CString;
421		type OutStruct = fuse_entry_out;
422		type OutPayload = ();
423	}
424
425	impl Mkdir {
426		pub(crate) fn create(path: CString, mode: u32) -> (Cmd<Self>, u32) {
427			let cmd = Cmd::with_cstring(
428				FUSE_ROOT_ID,
429				fuse_mkdir_in {
430					mode,
431					..Default::default()
432				},
433				path,
434			);
435			(cmd, 0)
436		}
437	}
438
439	#[derive(Debug)]
440	pub(crate) struct Unlink;
441
442	impl Op for Unlink {
443		const OP_CODE: fuse_opcode = fuse_opcode::FUSE_UNLINK;
444		type InStruct = ();
445		type InPayload = CString;
446		type OutStruct = ();
447		type OutPayload = ();
448	}
449
450	impl Unlink {
451		pub(crate) fn create(name: CString) -> (Cmd<Self>, u32) {
452			let cmd = Cmd::with_cstring(FUSE_ROOT_ID, (), name);
453			(cmd, 0)
454		}
455	}
456
457	#[derive(Debug)]
458	pub(crate) struct Rmdir;
459
460	impl Op for Rmdir {
461		const OP_CODE: fuse_opcode = fuse_opcode::FUSE_RMDIR;
462		type InStruct = ();
463		type InPayload = CString;
464		type OutStruct = ();
465		type OutPayload = ();
466	}
467
468	impl Rmdir {
469		pub(crate) fn create(name: CString) -> (Cmd<Self>, u32) {
470			let cmd = Cmd::with_cstring(FUSE_ROOT_ID, (), name);
471			(cmd, 0)
472		}
473	}
474
475	#[derive(Debug)]
476	pub(crate) struct Lookup;
477
478	impl Op for Lookup {
479		const OP_CODE: fuse_opcode = fuse_opcode::FUSE_LOOKUP;
480		type InStruct = ();
481		type InPayload = CString;
482		type OutStruct = fuse_entry_out;
483		type OutPayload = ();
484	}
485
486	impl Lookup {
487		pub(crate) fn create(name: CString) -> (Cmd<Self>, u32) {
488			let cmd = Cmd::with_cstring(FUSE_ROOT_ID, (), name);
489			(cmd, 0)
490		}
491	}
492}
493
494impl From<fuse_attr> for FileAttr {
495	fn from(attr: fuse_attr) -> FileAttr {
496		FileAttr {
497			st_ino: attr.ino,
498			st_nlink: attr.nlink.into(),
499			st_mode: AccessPermission::from_bits_retain(attr.mode),
500			st_uid: attr.uid,
501			st_gid: attr.gid,
502			st_rdev: attr.rdev.into(),
503			st_size: attr.size.try_into().unwrap(),
504			st_blksize: attr.blksize.into(),
505			st_blocks: attr.blocks.try_into().unwrap(),
506			st_atim: timespec {
507				tv_sec: attr.atime as time_t,
508				tv_nsec: attr.atimensec as i32,
509			},
510			st_mtim: timespec {
511				tv_sec: attr.mtime as time_t,
512				tv_nsec: attr.mtimensec as i32,
513			},
514			st_ctim: timespec {
515				tv_sec: attr.ctime as time_t,
516				tv_nsec: attr.ctimensec as i32,
517			},
518			..Default::default()
519		}
520	}
521}
522
523#[repr(C)]
524#[derive(Debug)]
525pub(crate) struct CmdHeader<O: ops::Op> {
526	pub in_header: fuse_in_header,
527	op_header: O::InStruct,
528}
529
530impl<O: ops::Op> CmdHeader<O>
531where
532	O: ops::Op<InPayload = ()>,
533{
534	fn new(nodeid: u64, op_header: O::InStruct) -> Self {
535		Self::with_payload_size(nodeid, op_header, 0)
536	}
537}
538
539impl<O: ops::Op> CmdHeader<O> {
540	fn with_payload_size(nodeid: u64, op_header: O::InStruct, len: usize) -> CmdHeader<O> {
541		CmdHeader {
542			in_header: fuse_in_header {
543				// The length we need the provide in the header is not the same as the size of the struct because of padding, so we need to calculate it manually.
544				len: (core::mem::size_of::<fuse_in_header>()
545					+ core::mem::size_of::<O::InStruct>()
546					+ len)
547					.try_into()
548					.expect("The command is too large"),
549				opcode: O::OP_CODE.into(),
550				nodeid,
551				unique: 1,
552				..Default::default()
553			},
554			op_header,
555		}
556	}
557}
558
559pub(crate) struct Cmd<O: ops::Op> {
560	pub headers: Box<CmdHeader<O>, DeviceAlloc>,
561	pub payload: Option<Vec<u8, DeviceAlloc>>,
562}
563
564impl<O: ops::Op> Cmd<O>
565where
566	O: ops::Op<InPayload = ()>,
567{
568	fn new(nodeid: u64, op_header: O::InStruct) -> Self {
569		Self {
570			headers: Box::new_in(CmdHeader::new(nodeid, op_header), DeviceAlloc),
571			payload: None,
572		}
573	}
574}
575
576impl<O: ops::Op> Cmd<O>
577where
578	O: ops::Op<InPayload = CString>,
579{
580	fn with_cstring(nodeid: u64, op_header: O::InStruct, cstring: CString) -> Self {
581		let cstring_bytes = cstring.into_bytes_with_nul().to_vec_in(DeviceAlloc);
582		Self {
583			headers: Box::new_in(
584				CmdHeader::with_payload_size(nodeid, op_header, cstring_bytes.len()),
585				DeviceAlloc,
586			),
587			payload: Some(cstring_bytes),
588		}
589	}
590}
591
592impl<O: ops::Op> Cmd<O>
593where
594	O: ops::Op<InPayload = [u8]>,
595{
596	fn with_boxed_slice(nodeid: u64, op_header: O::InStruct, slice: Box<[u8]>) -> Self {
597		let mut device_slice = Vec::with_capacity_in(slice.len(), DeviceAlloc);
598		device_slice.extend_from_slice(&slice);
599		Self {
600			headers: Box::new_in(
601				CmdHeader::with_payload_size(nodeid, op_header, slice.len()),
602				DeviceAlloc,
603			),
604			payload: Some(device_slice),
605		}
606	}
607}
608
609#[repr(C)]
610#[derive(Debug)]
611// The generic H parameter allows us to handle RspHeaders with their op_header
612// potenitally uninitialized. After checking for the error code in the out_header,
613// the object can be transmuted to one with an initialized op_header.
614pub(crate) struct RspHeader<O: ops::Op, H = <O as ops::Op>::OutStruct> {
615	pub out_header: fuse_out_header,
616	op_header: H,
617	_phantom: PhantomData<O::OutStruct>,
618}
619
620#[derive(Debug)]
621pub(crate) struct Rsp<O: ops::Op> {
622	pub headers: Box<RspHeader<O>, DeviceAlloc>,
623	pub payload: Option<Vec<u8, DeviceAlloc>>,
624}
625
626#[derive(Debug)]
627pub(crate) enum FuseError {
628	VirtqError(VirtqError),
629	IOError(Errno),
630}
631
632impl From<VirtqError> for FuseError {
633	fn from(value: VirtqError) -> Self {
634		Self::VirtqError(value)
635	}
636}
637
638impl From<FuseError> for Errno {
639	fn from(value: FuseError) -> Self {
640		match value {
641			FuseError::VirtqError(virtq_error) => virtq_error.into(),
642			FuseError::IOError(io_error) => io_error,
643		}
644	}
645}
646
647fn lookup(name: CString) -> Option<u64> {
648	let (cmd, rsp_payload_len) = ops::Lookup::create(name);
649	let rsp = get_filesystem_driver()
650		.unwrap()
651		.lock()
652		.send_command(cmd, rsp_payload_len)
653		.ok()?;
654	Some(rsp.headers.op_header.nodeid)
655}
656
657fn readlink(nid: u64) -> io::Result<String> {
658	let len = MAX_READ_LEN as u32;
659	let (cmd, rsp_payload_len) = ops::Readlink::create(nid, len);
660	let rsp = get_filesystem_driver()
661		.unwrap()
662		.lock()
663		.send_command(cmd, rsp_payload_len)?;
664	let len: usize = if rsp.headers.out_header.len as usize - mem::size_of::<fuse_out_header>()
665		>= usize::try_from(len).unwrap()
666	{
667		len.try_into().unwrap()
668	} else {
669		(rsp.headers.out_header.len as usize) - mem::size_of::<fuse_out_header>()
670	};
671
672	Ok(String::from_utf8(rsp.payload.unwrap()[..len].to_vec()).unwrap())
673}
674
675#[derive(Debug)]
676struct FuseFileHandleInner {
677	fuse_nid: Option<u64>,
678	fuse_fh: Option<u64>,
679	offset: usize,
680}
681
682impl FuseFileHandleInner {
683	pub fn new() -> Self {
684		Self {
685			fuse_nid: None,
686			fuse_fh: None,
687			offset: 0,
688		}
689	}
690
691	async fn poll(&self, events: PollEvent) -> io::Result<PollEvent> {
692		static KH: AtomicU64 = AtomicU64::new(0);
693		let kh = KH.fetch_add(1, Ordering::SeqCst);
694
695		future::poll_fn(|cx| {
696			if let (Some(nid), Some(fh)) = (self.fuse_nid, self.fuse_fh) {
697				let (cmd, rsp_payload_len) = ops::Poll::create(nid, fh, kh, events);
698				let rsp = get_filesystem_driver()
699					.ok_or(Errno::Nosys)?
700					.lock()
701					.send_command(cmd, rsp_payload_len)?;
702
703				if rsp.headers.out_header.error < 0 {
704					Poll::Ready(Err(Errno::Io))
705				} else {
706					let revents =
707						PollEvent::from_bits(i16::try_from(rsp.headers.op_header.revents).unwrap())
708							.unwrap();
709					if !revents.intersects(events)
710						&& !revents.intersects(
711							PollEvent::POLLERR | PollEvent::POLLNVAL | PollEvent::POLLHUP,
712						) {
713						// the current implementation use polling to wait for an event
714						// consequently, we have to wakeup the waker, if the the event doesn't arrive
715						cx.waker().wake_by_ref();
716					}
717					Poll::Ready(Ok(revents))
718				}
719			} else {
720				Poll::Ready(Ok(PollEvent::POLLERR))
721			}
722		})
723		.await
724	}
725
726	fn lseek(&mut self, offset: isize, whence: SeekWhence) -> io::Result<isize> {
727		debug!("FUSE lseek");
728
729		if let (Some(nid), Some(fh)) = (self.fuse_nid, self.fuse_fh) {
730			let (cmd, rsp_payload_len) = ops::Lseek::create(nid, fh, offset, whence);
731			let rsp = get_filesystem_driver()
732				.ok_or(Errno::Nosys)?
733				.lock()
734				.send_command(cmd, rsp_payload_len)?;
735
736			if rsp.headers.out_header.error < 0 {
737				return Err(Errno::Io);
738			}
739
740			let rsp_offset = rsp.headers.op_header.offset;
741			self.offset = rsp.headers.op_header.offset.try_into().unwrap();
742
743			Ok(rsp_offset.try_into().unwrap())
744		} else {
745			Err(Errno::Io)
746		}
747	}
748
749	fn fstat(&mut self) -> io::Result<FileAttr> {
750		debug!("FUSE getattr");
751		if let (Some(nid), Some(fh)) = (self.fuse_nid, self.fuse_fh) {
752			let (cmd, rsp_payload_len) = ops::Getattr::create(nid, fh, FUSE_GETATTR_FH);
753			let rsp = get_filesystem_driver()
754				.ok_or(Errno::Nosys)?
755				.lock()
756				.send_command(cmd, rsp_payload_len)?;
757			if rsp.headers.out_header.error < 0 {
758				return Err(Errno::Io);
759			}
760			Ok(rsp.headers.op_header.attr.into())
761		} else {
762			Err(Errno::Io)
763		}
764	}
765
766	fn set_attr(&mut self, attr: FileAttr, valid: SetAttrValidFields) -> io::Result<FileAttr> {
767		debug!("FUSE setattr");
768		if let (Some(nid), Some(fh)) = (self.fuse_nid, self.fuse_fh) {
769			let (cmd, rsp_payload_len) = ops::Setattr::create(nid, fh, attr, valid);
770			let rsp = get_filesystem_driver()
771				.ok_or(Errno::Nosys)?
772				.lock()
773				.send_command(cmd, rsp_payload_len)?;
774			if rsp.headers.out_header.error < 0 {
775				return Err(Errno::Io);
776			}
777			Ok(rsp.headers.op_header.attr.into())
778		} else {
779			Err(Errno::Io)
780		}
781	}
782}
783
784impl ErrorType for FuseFileHandleInner {
785	type Error = Errno;
786}
787
788impl Read for FuseFileHandleInner {
789	fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
790		let mut len = buf.len();
791		if len > MAX_READ_LEN {
792			debug!("Reading longer than max_read_len: {len}");
793			len = MAX_READ_LEN;
794		}
795		if let (Some(nid), Some(fh)) = (self.fuse_nid, self.fuse_fh) {
796			let (cmd, rsp_payload_len) =
797				ops::Read::create(nid, fh, len.try_into().unwrap(), self.offset as u64);
798			let rsp = get_filesystem_driver()
799				.ok_or(Errno::Nosys)?
800				.lock()
801				.send_command(cmd, rsp_payload_len)?;
802			let len: usize =
803				if (rsp.headers.out_header.len as usize) - mem::size_of::<fuse_out_header>() >= len
804				{
805					len
806				} else {
807					(rsp.headers.out_header.len as usize) - mem::size_of::<fuse_out_header>()
808				};
809			self.offset += len;
810
811			buf[..len].copy_from_slice(&rsp.payload.unwrap()[..len]);
812
813			Ok(len)
814		} else {
815			debug!("File not open, cannot read!");
816			Err(Errno::Noent)
817		}
818	}
819}
820
821impl Write for FuseFileHandleInner {
822	fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
823		debug!("FUSE write!");
824		let mut truncated_len = buf.len();
825		if truncated_len > MAX_WRITE_LEN {
826			debug!(
827				"Writing longer than max_write_len: {} > {}",
828				buf.len(),
829				MAX_WRITE_LEN
830			);
831			truncated_len = MAX_WRITE_LEN;
832		}
833		if let (Some(nid), Some(fh)) = (self.fuse_nid, self.fuse_fh) {
834			let truncated_buf = Box::<[u8]>::from(&buf[..truncated_len]);
835			let (cmd, rsp_payload_len) =
836				ops::Write::create(nid, fh, truncated_buf, self.offset as u64);
837			let rsp = get_filesystem_driver()
838				.ok_or(Errno::Nosys)?
839				.lock()
840				.send_command(cmd, rsp_payload_len)?;
841
842			if rsp.headers.out_header.error < 0 {
843				return Err(Errno::Io);
844			}
845
846			let rsp_size = rsp.headers.op_header.size;
847			let rsp_len: usize = if rsp_size > u32::try_from(truncated_len).unwrap() {
848				truncated_len
849			} else {
850				rsp_size.try_into().unwrap()
851			};
852			self.offset += rsp_len;
853			Ok(rsp_len)
854		} else {
855			warn!("File not open, cannot read!");
856			Err(Errno::Noent)
857		}
858	}
859
860	fn flush(&mut self) -> Result<(), Self::Error> {
861		Ok(())
862	}
863}
864
865impl Drop for FuseFileHandleInner {
866	fn drop(&mut self) {
867		if self.fuse_nid.is_some() && self.fuse_fh.is_some() {
868			let (cmd, rsp_payload_len) =
869				ops::Release::create(self.fuse_nid.unwrap(), self.fuse_fh.unwrap());
870			get_filesystem_driver()
871				.unwrap()
872				.lock()
873				.send_command(cmd, rsp_payload_len)
874				.unwrap();
875		}
876	}
877}
878
879#[derive(Debug)]
880struct FuseFileHandle(pub Arc<Mutex<FuseFileHandleInner>>);
881
882impl FuseFileHandle {
883	pub fn new() -> Self {
884		Self(Arc::new(Mutex::new(FuseFileHandleInner::new())))
885	}
886}
887
888#[async_trait]
889impl ObjectInterface for FuseFileHandle {
890	async fn poll(&self, event: PollEvent) -> io::Result<PollEvent> {
891		self.0.lock().await.poll(event).await
892	}
893
894	async fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
895		self.0.lock().await.read(buf)
896	}
897
898	async fn write(&self, buf: &[u8]) -> io::Result<usize> {
899		self.0.lock().await.write(buf)
900	}
901
902	async fn lseek(&self, offset: isize, whence: SeekWhence) -> io::Result<isize> {
903		self.0.lock().await.lseek(offset, whence)
904	}
905
906	async fn fstat(&self) -> io::Result<FileAttr> {
907		self.0.lock().await.fstat()
908	}
909
910	async fn truncate(&self, size: usize) -> io::Result<()> {
911		let attr = FileAttr {
912			st_size: size.try_into().unwrap(),
913			..FileAttr::default()
914		};
915
916		self.0
917			.lock()
918			.await
919			.set_attr(attr, SetAttrValidFields::FATTR_SIZE)
920			.map(|_| ())
921	}
922
923	async fn chmod(&self, access_permission: AccessPermission) -> io::Result<()> {
924		let attr = FileAttr {
925			st_mode: access_permission,
926			..FileAttr::default()
927		};
928
929		self.0
930			.lock()
931			.await
932			.set_attr(attr, SetAttrValidFields::FATTR_MODE)
933			.map(|_| ())
934	}
935}
936
937impl Clone for FuseFileHandle {
938	fn clone(&self) -> Self {
939		warn!("FuseFileHandle: clone not tested");
940		Self(self.0.clone())
941	}
942}
943
944#[derive(Debug)]
945pub struct FuseDirectoryHandle {
946	name: Option<String>,
947	read_position: Mutex<usize>,
948}
949
950impl FuseDirectoryHandle {
951	pub fn new(name: Option<String>) -> Self {
952		Self {
953			name,
954			read_position: Mutex::new(0),
955		}
956	}
957}
958
959#[async_trait]
960impl ObjectInterface for FuseDirectoryHandle {
961	async fn getdents(&self, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
962		let path: CString = if let Some(name) = &self.name {
963			CString::new("/".to_string() + name).unwrap()
964		} else {
965			CString::new("/".to_string()).unwrap()
966		};
967
968		debug!("FUSE opendir: {path:#?}");
969
970		let fuse_nid = lookup(path.clone()).ok_or(Errno::Noent)?;
971
972		// Opendir
973		// Flag 0x10000 for O_DIRECTORY might not be necessary
974		let (mut cmd, rsp_payload_len) = ops::Open::create(fuse_nid, 0x10000);
975		cmd.headers.in_header.opcode = fuse_opcode::FUSE_OPENDIR as u32;
976		let rsp = get_filesystem_driver()
977			.ok_or(Errno::Nosys)?
978			.lock()
979			.send_command(cmd, rsp_payload_len)?;
980		let fuse_fh = rsp.headers.op_header.fh;
981
982		debug!("FUSE readdir: {path:#?}");
983
984		// Linux seems to allocate a single page to store the dirfile
985		let len = MAX_READ_LEN as u32;
986		let rsp_offset: &mut usize = &mut *self.read_position.lock().await;
987		let mut buf_offset: usize = 0;
988
989		// read content of the directory
990		let (mut cmd, rsp_payload_len) = ops::Read::create(fuse_nid, fuse_fh, len, 0);
991		cmd.headers.in_header.opcode = fuse_opcode::FUSE_READDIR as u32;
992		let rsp = get_filesystem_driver()
993			.ok_or(Errno::Nosys)?
994			.lock()
995			.send_command(cmd, rsp_payload_len)?;
996
997		let len = usize::min(
998			MAX_READ_LEN,
999			rsp.headers.out_header.len as usize - mem::size_of::<fuse_out_header>(),
1000		);
1001
1002		if len <= core::mem::size_of::<fuse_dirent>() {
1003			debug!("FUSE no new dirs");
1004			return Err(Errno::Noent);
1005		}
1006
1007		let mut ret = 0;
1008
1009		while (rsp.headers.out_header.len as usize) - *rsp_offset > size_of::<fuse_dirent>() {
1010			let dirent = unsafe {
1011				&*rsp
1012					.payload
1013					.as_ref()
1014					.unwrap()
1015					.as_ptr()
1016					.byte_add(*rsp_offset)
1017					.cast::<fuse_dirent>()
1018			};
1019
1020			let dirent_len = offset_of!(Dirent64, d_name) + dirent.namelen as usize + 1;
1021			let next_dirent = (buf_offset + dirent_len).align_up(align_of::<Dirent64>());
1022
1023			if next_dirent > buf.len() {
1024				// target buffer full -> we return the nr. of bytes written (like linux does)
1025				break;
1026			}
1027
1028			// could be replaced with slice_as_ptr once maybe_uninit_slice is stabilized.
1029			let target_dirent = buf[buf_offset].as_mut_ptr().cast::<Dirent64>();
1030			unsafe {
1031				target_dirent.write(Dirent64 {
1032					d_ino: dirent.ino,
1033					d_off: 0,
1034					d_reclen: (dirent_len.align_up(align_of::<Dirent64>()))
1035						.try_into()
1036						.unwrap(),
1037					d_type: (dirent.type_ as u8).try_into().unwrap(),
1038					d_name: PhantomData {},
1039				});
1040				let nameptr = core::ptr::from_mut(&mut (*(target_dirent)).d_name).cast::<u8>();
1041				core::ptr::copy_nonoverlapping(
1042					dirent.name.as_ptr().cast::<u8>(),
1043					nameptr,
1044					dirent.namelen as usize,
1045				);
1046				nameptr.add(dirent.namelen as usize).write(0); // zero termination
1047			}
1048
1049			*rsp_offset += core::mem::size_of::<fuse_dirent>() + dirent.namelen as usize;
1050			// Align to dirent struct
1051			*rsp_offset = ((*rsp_offset) + U64_SIZE - 1) & (!(U64_SIZE - 1));
1052			buf_offset = next_dirent;
1053			ret = buf_offset;
1054		}
1055
1056		let (cmd, rsp_payload_len) = ops::Release::create(fuse_nid, fuse_fh);
1057		get_filesystem_driver()
1058			.unwrap()
1059			.lock()
1060			.send_command(cmd, rsp_payload_len)?;
1061
1062		Ok(ret)
1063	}
1064
1065	/// lseek for a directory entry is the equivalent for seekdir on linux. But on Hermit this is
1066	/// logically the same operation, so we can just use the same fn in the backend.
1067	/// Any other offset than 0 is not supported. (Mostly because it doesn't make any sense, as
1068	/// userspace applications have no way of knowing valid offsets)
1069	async fn lseek(&self, offset: isize, whence: SeekWhence) -> io::Result<isize> {
1070		if whence != SeekWhence::Set && offset != 0 {
1071			error!("Invalid offset for directory lseek ({offset})");
1072			return Err(Errno::Inval);
1073		}
1074		*self.read_position.lock().await = offset as usize;
1075		Ok(offset)
1076	}
1077}
1078
1079#[derive(Debug)]
1080pub(crate) struct FuseDirectory {
1081	prefix: Option<String>,
1082	attr: FileAttr,
1083}
1084
1085impl FuseDirectory {
1086	pub fn new(prefix: Option<String>) -> Self {
1087		let microseconds = arch::kernel::systemtime::now_micros();
1088		let t = timespec::from_usec(microseconds as i64);
1089
1090		FuseDirectory {
1091			prefix,
1092			attr: FileAttr {
1093				st_mode: AccessPermission::from_bits(0o777).unwrap() | AccessPermission::S_IFDIR,
1094				st_atim: t,
1095				st_mtim: t,
1096				st_ctim: t,
1097				..Default::default()
1098			},
1099		}
1100	}
1101
1102	fn traversal_path(&self, components: &[&str]) -> CString {
1103		let prefix_deref = self.prefix.as_deref();
1104		let components_with_prefix = prefix_deref.iter().chain(components.iter().rev());
1105		let path: String = components_with_prefix
1106			.flat_map(|component| ["/", component])
1107			.collect();
1108		if path.is_empty() {
1109			CString::new("/").unwrap()
1110		} else {
1111			CString::new(path).unwrap()
1112		}
1113	}
1114}
1115
1116impl VfsNode for FuseDirectory {
1117	/// Returns the node type
1118	fn get_kind(&self) -> NodeKind {
1119		NodeKind::Directory
1120	}
1121
1122	fn get_file_attributes(&self) -> io::Result<FileAttr> {
1123		Ok(self.attr)
1124	}
1125
1126	fn get_object(&self) -> io::Result<Arc<async_lock::RwLock<dyn ObjectInterface>>> {
1127		Ok(Arc::new(async_lock::RwLock::new(FuseDirectoryHandle::new(
1128			self.prefix.clone(),
1129		))))
1130	}
1131
1132	fn traverse_readdir(&self, components: &mut Vec<&str>) -> io::Result<Vec<DirectoryEntry>> {
1133		let path = self.traversal_path(components);
1134
1135		debug!("FUSE opendir: {path:#?}");
1136
1137		let fuse_nid = lookup(path.clone()).ok_or(Errno::Noent)?;
1138
1139		// Opendir
1140		// Flag 0x10000 for O_DIRECTORY might not be necessary
1141		let (mut cmd, rsp_payload_len) = ops::Open::create(fuse_nid, 0x10000);
1142		cmd.headers.in_header.opcode = fuse_opcode::FUSE_OPENDIR as u32;
1143		let rsp = get_filesystem_driver()
1144			.ok_or(Errno::Nosys)?
1145			.lock()
1146			.send_command(cmd, rsp_payload_len)?;
1147		let fuse_fh = rsp.headers.op_header.fh;
1148
1149		debug!("FUSE readdir: {path:#?}");
1150
1151		// Linux seems to allocate a single page to store the dirfile
1152		let len = MAX_READ_LEN as u32;
1153		let mut offset: usize = 0;
1154
1155		// read content of the directory
1156		let (mut cmd, rsp_payload_len) = ops::Read::create(fuse_nid, fuse_fh, len, 0);
1157		cmd.headers.in_header.opcode = fuse_opcode::FUSE_READDIR as u32;
1158		let rsp = get_filesystem_driver()
1159			.ok_or(Errno::Nosys)?
1160			.lock()
1161			.send_command(cmd, rsp_payload_len)?;
1162
1163		let len: usize = if rsp.headers.out_header.len as usize - mem::size_of::<fuse_out_header>()
1164			>= usize::try_from(len).unwrap()
1165		{
1166			len.try_into().unwrap()
1167		} else {
1168			(rsp.headers.out_header.len as usize) - mem::size_of::<fuse_out_header>()
1169		};
1170
1171		if len <= core::mem::size_of::<fuse_dirent>() {
1172			debug!("FUSE no new dirs");
1173			return Err(Errno::Noent);
1174		}
1175
1176		let mut entries: Vec<DirectoryEntry> = Vec::new();
1177		while (rsp.headers.out_header.len as usize) - offset > core::mem::size_of::<fuse_dirent>() {
1178			let dirent = unsafe {
1179				&*rsp
1180					.payload
1181					.as_ref()
1182					.unwrap()
1183					.as_ptr()
1184					.byte_add(offset)
1185					.cast::<fuse_dirent>()
1186			};
1187
1188			offset += core::mem::size_of::<fuse_dirent>() + dirent.namelen as usize;
1189			// Align to dirent struct
1190			offset = ((offset) + U64_SIZE - 1) & (!(U64_SIZE - 1));
1191
1192			let name: &'static [u8] = unsafe {
1193				core::slice::from_raw_parts(
1194					dirent.name.as_ptr().cast(),
1195					dirent.namelen.try_into().unwrap(),
1196				)
1197			};
1198			entries.push(DirectoryEntry::new(unsafe {
1199				core::str::from_utf8_unchecked(name).to_string()
1200			}));
1201		}
1202
1203		let (cmd, rsp_payload_len) = ops::Release::create(fuse_nid, fuse_fh);
1204		get_filesystem_driver()
1205			.unwrap()
1206			.lock()
1207			.send_command(cmd, rsp_payload_len)?;
1208
1209		Ok(entries)
1210	}
1211
1212	fn traverse_stat(&self, components: &mut Vec<&str>) -> io::Result<FileAttr> {
1213		let path = self.traversal_path(components);
1214
1215		debug!("FUSE stat: {path:#?}");
1216
1217		// Is there a better way to implement this?
1218		let (cmd, rsp_payload_len) = ops::Lookup::create(path);
1219		let rsp = get_filesystem_driver()
1220			.unwrap()
1221			.lock()
1222			.send_command(cmd, rsp_payload_len)?;
1223
1224		if rsp.headers.out_header.error != 0 {
1225			return Err(Errno::try_from(-rsp.headers.out_header.error).unwrap());
1226		}
1227
1228		let entry_out = rsp.headers.op_header;
1229		let attr = entry_out.attr;
1230
1231		if attr.mode & S_IFMT != S_IFLNK {
1232			return Ok(FileAttr::from(attr));
1233		}
1234
1235		let path = readlink(entry_out.nodeid)?;
1236		let mut components: Vec<&str> = path.split('/').collect();
1237		self.traverse_stat(&mut components)
1238	}
1239
1240	fn traverse_lstat(&self, components: &mut Vec<&str>) -> io::Result<FileAttr> {
1241		let path = self.traversal_path(components);
1242
1243		debug!("FUSE lstat: {path:#?}");
1244
1245		let (cmd, rsp_payload_len) = ops::Lookup::create(path);
1246		let rsp = get_filesystem_driver()
1247			.unwrap()
1248			.lock()
1249			.send_command(cmd, rsp_payload_len)?;
1250		Ok(FileAttr::from(rsp.headers.op_header.attr))
1251	}
1252
1253	fn traverse_open(
1254		&self,
1255		components: &mut Vec<&str>,
1256		opt: OpenOption,
1257		mode: AccessPermission,
1258	) -> io::Result<Arc<async_lock::RwLock<dyn ObjectInterface>>> {
1259		let path = self.traversal_path(components);
1260
1261		debug!("FUSE open: {path:#?}, {opt:?} {mode:?}");
1262
1263		if opt.contains(OpenOption::O_DIRECTORY) {
1264			if opt.contains(OpenOption::O_CREAT) {
1265				// See https://lwn.net/Articles/926782/
1266				warn!("O_DIRECTORY and O_CREAT are together invalid as open options.");
1267				return Err(Errno::Inval);
1268			}
1269
1270			let (cmd, rsp_payload_len) = ops::Lookup::create(path.clone());
1271			let rsp = get_filesystem_driver()
1272				.unwrap()
1273				.lock()
1274				.send_command(cmd, rsp_payload_len)?;
1275
1276			let attr = FileAttr::from(rsp.headers.op_header.attr);
1277			if attr.st_mode.contains(AccessPermission::S_IFDIR) {
1278				let mut path = path.into_string().unwrap();
1279				path.remove(0);
1280				Ok(Arc::new(async_lock::RwLock::new(FuseDirectoryHandle::new(
1281					Some(path),
1282				))))
1283			} else {
1284				Err(Errno::Notdir)
1285			}
1286		} else {
1287			let file = FuseFileHandle::new();
1288
1289			// 1.FUSE_INIT to create session
1290			// Already done
1291			let mut file_guard = block_on(async { Ok(file.0.lock().await) }, None)?;
1292
1293			// Differentiate between opening and creating new file, since fuse does not support O_CREAT on open.
1294			if opt.contains(OpenOption::O_CREAT) {
1295				// Create file (opens implicitly, returns results from both lookup and open calls)
1296				let (cmd, rsp_payload_len) =
1297					ops::Create::create(path, opt.bits().try_into().unwrap(), mode.bits());
1298				let rsp = get_filesystem_driver()
1299					.ok_or(Errno::Nosys)?
1300					.lock()
1301					.send_command(cmd, rsp_payload_len)?;
1302
1303				let inner = rsp.headers.op_header;
1304				file_guard.fuse_nid = Some(inner.entry.nodeid);
1305				file_guard.fuse_fh = Some(inner.open.fh);
1306			} else {
1307				// 2.FUSE_LOOKUP(FUSE_ROOT_ID, “foo”) -> nodeid
1308				file_guard.fuse_nid = lookup(path);
1309
1310				if file_guard.fuse_nid.is_none() {
1311					warn!("Fuse lookup seems to have failed!");
1312					return Err(Errno::Noent);
1313				}
1314
1315				// 3.FUSE_OPEN(nodeid, O_RDONLY) -> fh
1316				let (cmd, rsp_payload_len) =
1317					ops::Open::create(file_guard.fuse_nid.unwrap(), opt.bits().try_into().unwrap());
1318				let rsp = get_filesystem_driver()
1319					.ok_or(Errno::Nosys)?
1320					.lock()
1321					.send_command(cmd, rsp_payload_len)?;
1322				file_guard.fuse_fh = Some(rsp.headers.op_header.fh);
1323			}
1324
1325			drop(file_guard);
1326
1327			Ok(Arc::new(async_lock::RwLock::new(file)))
1328		}
1329	}
1330
1331	fn traverse_unlink(&self, components: &mut Vec<&str>) -> io::Result<()> {
1332		let path = self.traversal_path(components);
1333
1334		let (cmd, rsp_payload_len) = ops::Unlink::create(path);
1335		let rsp = get_filesystem_driver()
1336			.ok_or(Errno::Nosys)?
1337			.lock()
1338			.send_command(cmd, rsp_payload_len)?;
1339		trace!("unlink answer {rsp:?}");
1340
1341		Ok(())
1342	}
1343
1344	fn traverse_rmdir(&self, components: &mut Vec<&str>) -> io::Result<()> {
1345		let path = self.traversal_path(components);
1346
1347		let (cmd, rsp_payload_len) = ops::Rmdir::create(path);
1348		let rsp = get_filesystem_driver()
1349			.ok_or(Errno::Nosys)?
1350			.lock()
1351			.send_command(cmd, rsp_payload_len)?;
1352		trace!("rmdir answer {rsp:?}");
1353
1354		Ok(())
1355	}
1356
1357	fn traverse_mkdir(&self, components: &mut Vec<&str>, mode: AccessPermission) -> io::Result<()> {
1358		let path = self.traversal_path(components);
1359		let (cmd, rsp_payload_len) = ops::Mkdir::create(path, mode.bits());
1360
1361		let rsp = get_filesystem_driver()
1362			.ok_or(Errno::Nosys)?
1363			.lock()
1364			.send_command(cmd, rsp_payload_len)?;
1365		if rsp.headers.out_header.error == 0 {
1366			Ok(())
1367		} else {
1368			Err(Errno::try_from(-rsp.headers.out_header.error).unwrap())
1369		}
1370	}
1371}
1372
1373pub(crate) fn init() {
1374	debug!("Try to initialize fuse filesystem");
1375
1376	if let Some(driver) = get_filesystem_driver() {
1377		let (cmd, rsp_payload_len) = ops::Init::create();
1378		let rsp = driver.lock().send_command(cmd, rsp_payload_len).unwrap();
1379		trace!("fuse init answer: {rsp:?}");
1380
1381		let mount_point = driver.lock().get_mount_point();
1382		if mount_point == "/" {
1383			let fuse_nid = lookup(c"/".to_owned()).unwrap();
1384			// Opendir
1385			// Flag 0x10000 for O_DIRECTORY might not be necessary
1386			let (mut cmd, rsp_payload_len) = ops::Open::create(fuse_nid, 0x10000);
1387			cmd.headers.in_header.opcode = fuse_opcode::FUSE_OPENDIR as u32;
1388			let rsp = get_filesystem_driver()
1389				.unwrap()
1390				.lock()
1391				.send_command(cmd, rsp_payload_len)
1392				.unwrap();
1393			let fuse_fh = rsp.headers.op_header.fh;
1394
1395			// Linux seems to allocate a single page to store the dirfile
1396			let len = MAX_READ_LEN as u32;
1397			let mut offset: usize = 0;
1398
1399			// read content of the directory
1400			let (mut cmd, rsp_payload_len) = ops::Read::create(fuse_nid, fuse_fh, len, 0);
1401			cmd.headers.in_header.opcode = fuse_opcode::FUSE_READDIR as u32;
1402			let rsp = get_filesystem_driver()
1403				.unwrap()
1404				.lock()
1405				.send_command(cmd, rsp_payload_len)
1406				.unwrap();
1407
1408			let len: usize = if rsp.headers.out_header.len as usize
1409				- mem::size_of::<fuse_out_header>()
1410				>= usize::try_from(len).unwrap()
1411			{
1412				len.try_into().unwrap()
1413			} else {
1414				(rsp.headers.out_header.len as usize) - mem::size_of::<fuse_out_header>()
1415			};
1416
1417			assert!(
1418				len > core::mem::size_of::<fuse_dirent>(),
1419				"FUSE no new dirs"
1420			);
1421
1422			let mut entries: Vec<String> = Vec::new();
1423			while (rsp.headers.out_header.len as usize) - offset
1424				> core::mem::size_of::<fuse_dirent>()
1425			{
1426				let dirent = unsafe {
1427					&*rsp
1428						.payload
1429						.as_ref()
1430						.unwrap()
1431						.as_ptr()
1432						.byte_add(offset)
1433						.cast::<fuse_dirent>()
1434				};
1435
1436				offset += core::mem::size_of::<fuse_dirent>() + dirent.namelen as usize;
1437				// Align to dirent struct
1438				offset = ((offset) + U64_SIZE - 1) & (!(U64_SIZE - 1));
1439
1440				let name: &'static [u8] = unsafe {
1441					core::slice::from_raw_parts(
1442						dirent.name.as_ptr().cast(),
1443						dirent.namelen.try_into().unwrap(),
1444					)
1445				};
1446				entries.push(unsafe { core::str::from_utf8_unchecked(name).to_string() });
1447			}
1448
1449			let (cmd, rsp_payload_len) = ops::Release::create(fuse_nid, fuse_fh);
1450			get_filesystem_driver()
1451				.unwrap()
1452				.lock()
1453				.send_command(cmd, rsp_payload_len)
1454				.unwrap();
1455
1456			// remove predefined directories
1457			entries.retain(|x| x != ".");
1458			entries.retain(|x| x != "..");
1459			entries.retain(|x| x != "tmp");
1460			entries.retain(|x| x != "proc");
1461			warn!(
1462				"Fuse don't mount the host directories 'tmp' and 'proc' into the guest file system!"
1463			);
1464
1465			for i in entries {
1466				let i_cstr = CString::new(i.clone()).unwrap();
1467				let (cmd, rsp_payload_len) = ops::Lookup::create(i_cstr);
1468				let rsp = get_filesystem_driver()
1469					.unwrap()
1470					.lock()
1471					.send_command(cmd, rsp_payload_len)
1472					.unwrap();
1473
1474				let attr = FileAttr::from(rsp.headers.op_header.attr);
1475				if attr.st_mode.contains(AccessPermission::S_IFDIR) {
1476					info!("Fuse mount {i} to /{i}");
1477					fs::FILESYSTEM
1478						.get()
1479						.unwrap()
1480						.mount(
1481							&("/".to_owned() + i.as_str()),
1482							Box::new(FuseDirectory::new(Some(i))),
1483						)
1484						.expect("Mount failed. Invalid mount_point?");
1485				} else {
1486					warn!("Fuse don't mount {i}. It isn't a directory!");
1487				}
1488			}
1489		} else {
1490			let mount_point = if mount_point.starts_with('/') {
1491				mount_point
1492			} else {
1493				"/".to_owned() + &mount_point
1494			};
1495
1496			info!("Mounting virtio-fs at {mount_point}");
1497			fs::FILESYSTEM
1498				.get()
1499				.unwrap()
1500				.mount(mount_point.as_str(), Box::new(FuseDirectory::new(None)))
1501				.expect("Mount failed. Invalid mount_point?");
1502		}
1503	}
1504}