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
38const 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 SetAttrValidFields::FATTR_LOCKOWNER,
319 )
320 .bits(),
321 padding: 0,
322 fh,
323
324 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, },
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 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)]
611pub(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 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 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 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 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 break;
1026 }
1027
1028 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); }
1048
1049 *rsp_offset += core::mem::size_of::<fuse_dirent>() + dirent.namelen as usize;
1050 *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 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 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 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 let len = MAX_READ_LEN as u32;
1153 let mut offset: usize = 0;
1154
1155 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 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 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 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 let mut file_guard = block_on(async { Ok(file.0.lock().await) }, None)?;
1292
1293 if opt.contains(OpenOption::O_CREAT) {
1295 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 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 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 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 let len = MAX_READ_LEN as u32;
1397 let mut offset: usize = 0;
1398
1399 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 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 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}