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