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
37const 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 SetAttrValidFields::FATTR_LOCKOWNER,
318 )
319 .bits(),
320 padding: 0,
321 fh,
322
323 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, },
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 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)]
610pub(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 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 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 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 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 break;
1013 }
1014
1015 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); }
1035
1036 *rsp_offset += core::mem::size_of::<fuse_dirent>() + dirent.namelen as usize;
1037 *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 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 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 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 let len = MAX_READ_LEN as u32;
1138 let mut offset: usize = 0;
1139
1140 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 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 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 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 let mut file_guard = block_on(async { Ok(file.0.lock().await) }, None)?;
1275
1276 if opt.contains(OpenOption::O_CREAT) {
1278 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 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 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 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 let len = MAX_READ_LEN as u32;
1380 let mut offset: usize = 0;
1381
1382 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 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 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}