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;
9use core::sync::atomic::{AtomicU64, Ordering};
10use core::task::Poll;
11use core::{future, mem, ptr, slice};
12
13use align_address::Align;
14use async_lock::Mutex;
15use embedded_io::{ErrorType, Read, Write};
16use fuse_abi::linux::*;
17
18#[cfg(not(feature = "pci"))]
19use crate::arch::kernel::mmio::get_filesystem_driver;
20#[cfg(feature = "pci")]
21use crate::drivers::pci::get_filesystem_driver;
22use crate::drivers::virtio::virtqueue::error::VirtqError;
23use crate::errno::Errno;
24use crate::executor::block_on;
25use crate::fd::{Fd, PollEvent};
26use crate::fs::virtio_fs::ops::SetAttrValidFields;
27use crate::fs::{
28 self, AccessPermission, DirectoryEntry, FileAttr, NodeKind, ObjectInterface, OpenOption,
29 SeekWhence, VfsNode,
30};
31use crate::mm::device_alloc::DeviceAlloc;
32use crate::syscalls::Dirent64;
33use crate::time::{time_t, timespec};
34use crate::{arch, io};
35
36const MAX_READ_LEN: usize = 1024 * 64;
41const MAX_WRITE_LEN: usize = 1024 * 64;
42
43const U64_SIZE: usize = mem::size_of::<u64>();
44
45const S_IFLNK: u32 = 0o120_000;
46const S_IFMT: u32 = 0o170_000;
47
48pub(crate) trait VirtioFsInterface {
49 fn send_command<O: ops::Op + 'static>(
50 &mut self,
51 cmd: Cmd<O>,
52 rsp_payload_len: u32,
53 ) -> Result<Rsp<O>, VirtioFsError>
54 where
55 <O as ops::Op>::InStruct: Send,
56 <O as ops::Op>::OutStruct: Send;
57
58 fn get_mount_point(&self) -> String;
59}
60
61pub(crate) mod ops {
62 #![allow(clippy::type_complexity)]
63 use alloc::boxed::Box;
64 use alloc::ffi::CString;
65 use core::fmt;
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: fmt::Debug;
85 type InPayload: ?Sized;
86 type OutStruct: 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: (mem::size_of::<fuse_in_header>() + mem::size_of::<O::InStruct>() + len)
544 .try_into()
545 .expect("The command is too large"),
546 opcode: O::OP_CODE.into(),
547 nodeid,
548 unique: 1,
549 ..Default::default()
550 },
551 op_header,
552 }
553 }
554}
555
556pub(crate) struct Cmd<O: ops::Op> {
557 pub headers: Box<CmdHeader<O>, DeviceAlloc>,
558 pub payload: Option<Vec<u8, DeviceAlloc>>,
559}
560
561impl<O: ops::Op> Cmd<O>
562where
563 O: ops::Op<InPayload = ()>,
564{
565 fn new(nodeid: u64, op_header: O::InStruct) -> Self {
566 Self {
567 headers: Box::new_in(CmdHeader::new(nodeid, op_header), DeviceAlloc),
568 payload: None,
569 }
570 }
571}
572
573impl<O: ops::Op> Cmd<O>
574where
575 O: ops::Op<InPayload = CString>,
576{
577 fn with_cstring(nodeid: u64, op_header: O::InStruct, cstring: CString) -> Self {
578 let cstring_bytes = cstring.into_bytes_with_nul().to_vec_in(DeviceAlloc);
579 Self {
580 headers: Box::new_in(
581 CmdHeader::with_payload_size(nodeid, op_header, cstring_bytes.len()),
582 DeviceAlloc,
583 ),
584 payload: Some(cstring_bytes),
585 }
586 }
587}
588
589impl<O: ops::Op> Cmd<O>
590where
591 O: ops::Op<InPayload = [u8]>,
592{
593 fn with_boxed_slice(nodeid: u64, op_header: O::InStruct, slice: Box<[u8]>) -> Self {
594 let mut device_slice = Vec::with_capacity_in(slice.len(), DeviceAlloc);
595 device_slice.extend_from_slice(&slice);
596 Self {
597 headers: Box::new_in(
598 CmdHeader::with_payload_size(nodeid, op_header, slice.len()),
599 DeviceAlloc,
600 ),
601 payload: Some(device_slice),
602 }
603 }
604}
605
606#[repr(C)]
607#[derive(Debug)]
608pub(crate) struct RspHeader<O: ops::Op, H = <O as ops::Op>::OutStruct> {
612 pub out_header: fuse_out_header,
613 op_header: H,
614 _phantom: PhantomData<O::OutStruct>,
615}
616
617#[derive(Debug)]
618pub(crate) struct Rsp<O: ops::Op> {
619 pub headers: Box<RspHeader<O>, DeviceAlloc>,
620 pub payload: Option<Vec<u8, DeviceAlloc>>,
621}
622
623#[derive(Debug)]
624pub(crate) enum VirtioFsError {
625 VirtqError(VirtqError),
626 IOError(Errno),
627}
628
629impl From<VirtqError> for VirtioFsError {
630 fn from(value: VirtqError) -> Self {
631 Self::VirtqError(value)
632 }
633}
634
635impl From<VirtioFsError> for Errno {
636 fn from(value: VirtioFsError) -> Self {
637 match value {
638 VirtioFsError::VirtqError(virtq_error) => virtq_error.into(),
639 VirtioFsError::IOError(io_error) => io_error,
640 }
641 }
642}
643
644fn lookup(name: CString) -> Option<u64> {
645 let (cmd, rsp_payload_len) = ops::Lookup::create(name);
646 let rsp = get_filesystem_driver()
647 .unwrap()
648 .lock()
649 .send_command(cmd, rsp_payload_len)
650 .ok()?;
651 Some(rsp.headers.op_header.nodeid)
652}
653
654fn readlink(nid: u64) -> io::Result<String> {
655 let len = MAX_READ_LEN as u32;
656 let (cmd, rsp_payload_len) = ops::Readlink::create(nid, len);
657 let rsp = get_filesystem_driver()
658 .unwrap()
659 .lock()
660 .send_command(cmd, rsp_payload_len)?;
661 let len: usize = if rsp.headers.out_header.len as usize - mem::size_of::<fuse_out_header>()
662 >= usize::try_from(len).unwrap()
663 {
664 len.try_into().unwrap()
665 } else {
666 (rsp.headers.out_header.len as usize) - mem::size_of::<fuse_out_header>()
667 };
668
669 Ok(String::from_utf8(rsp.payload.unwrap()[..len].to_vec()).unwrap())
670}
671
672#[derive(Debug)]
673struct VirtioFsFileHandleInner {
674 fuse_nid: Option<u64>,
675 fuse_fh: Option<u64>,
676 offset: usize,
677}
678
679impl VirtioFsFileHandleInner {
680 pub fn new() -> Self {
681 Self {
682 fuse_nid: None,
683 fuse_fh: None,
684 offset: 0,
685 }
686 }
687
688 async fn poll(&self, events: PollEvent) -> io::Result<PollEvent> {
689 static KH: AtomicU64 = AtomicU64::new(0);
690 let kh = KH.fetch_add(1, Ordering::SeqCst);
691
692 future::poll_fn(|cx| {
693 let Some(nid) = self.fuse_nid else {
694 return Poll::Ready(Ok(PollEvent::POLLERR));
695 };
696
697 let Some(fh) = self.fuse_fh else {
698 return Poll::Ready(Ok(PollEvent::POLLERR));
699 };
700
701 let (cmd, rsp_payload_len) = ops::Poll::create(nid, fh, kh, events);
702 let rsp = get_filesystem_driver()
703 .ok_or(Errno::Nosys)?
704 .lock()
705 .send_command(cmd, rsp_payload_len)?;
706
707 if rsp.headers.out_header.error < 0 {
708 return Poll::Ready(Err(Errno::Io));
709 }
710
711 let revents =
712 PollEvent::from_bits(i16::try_from(rsp.headers.op_header.revents).unwrap())
713 .unwrap();
714 if !revents.intersects(events)
715 && !revents
716 .intersects(PollEvent::POLLERR | PollEvent::POLLNVAL | PollEvent::POLLHUP)
717 {
718 cx.waker().wake_by_ref();
721 }
722 Poll::Ready(Ok(revents))
723 })
724 .await
725 }
726
727 fn lseek(&mut self, offset: isize, whence: SeekWhence) -> io::Result<isize> {
728 debug!("virtio-fs lseek: offset: {offset}, whence: {whence:?}");
729
730 match whence {
736 SeekWhence::End | SeekWhence::Data | SeekWhence::Hole => {
737 let nid = self.fuse_nid.ok_or(Errno::Io)?;
738 let fh = self.fuse_fh.ok_or(Errno::Io)?;
739
740 let (cmd, rsp_payload_len) = ops::Lseek::create(nid, fh, offset, whence);
741 let rsp = get_filesystem_driver()
742 .ok_or(Errno::Nosys)?
743 .lock()
744 .send_command(cmd, rsp_payload_len)?;
745
746 if rsp.headers.out_header.error < 0 {
747 return Err(Errno::Io);
748 }
749
750 let rsp_offset = rsp.headers.op_header.offset;
751 self.offset = rsp.headers.op_header.offset.try_into().unwrap();
752
753 Ok(rsp_offset.try_into().unwrap())
754 }
755 SeekWhence::Set => {
756 self.offset = offset.try_into().map_err(|_e| Errno::Inval)?;
757 Ok(self.offset as isize)
758 }
759 SeekWhence::Cur => {
760 self.offset = (self.offset as isize + offset)
761 .try_into()
762 .map_err(|_e| Errno::Inval)?;
763 Ok(self.offset as isize)
764 }
765 }
766 }
767
768 fn fstat(&mut self) -> io::Result<FileAttr> {
769 debug!("virtio-fs getattr");
770
771 let nid = self.fuse_nid.ok_or(Errno::Io)?;
772 let fh = self.fuse_fh.ok_or(Errno::Io)?;
773
774 let (cmd, rsp_payload_len) = ops::Getattr::create(nid, fh, FUSE_GETATTR_FH);
775 let rsp = get_filesystem_driver()
776 .ok_or(Errno::Nosys)?
777 .lock()
778 .send_command(cmd, rsp_payload_len)?;
779
780 if rsp.headers.out_header.error < 0 {
781 return Err(Errno::Io);
782 }
783
784 Ok(rsp.headers.op_header.attr.into())
785 }
786
787 fn set_attr(&mut self, attr: FileAttr, valid: SetAttrValidFields) -> io::Result<FileAttr> {
788 debug!("virtio-fs setattr");
789
790 let nid = self.fuse_nid.ok_or(Errno::Io)?;
791 let fh = self.fuse_fh.ok_or(Errno::Io)?;
792
793 let (cmd, rsp_payload_len) = ops::Setattr::create(nid, fh, attr, valid);
794 let rsp = get_filesystem_driver()
795 .ok_or(Errno::Nosys)?
796 .lock()
797 .send_command(cmd, rsp_payload_len)?;
798
799 if rsp.headers.out_header.error < 0 {
800 return Err(Errno::Io);
801 }
802
803 Ok(rsp.headers.op_header.attr.into())
804 }
805}
806
807impl ErrorType for VirtioFsFileHandleInner {
808 type Error = Errno;
809}
810
811impl Read for VirtioFsFileHandleInner {
812 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
813 let mut len = buf.len();
814 if len > MAX_READ_LEN {
815 debug!("Reading longer than max_read_len: {len}");
816 len = MAX_READ_LEN;
817 }
818
819 let nid = self.fuse_nid.ok_or(Errno::Io)?;
820 let fh = self.fuse_fh.ok_or(Errno::Io)?;
821
822 let (cmd, rsp_payload_len) =
823 ops::Read::create(nid, fh, len.try_into().unwrap(), self.offset as u64);
824 let rsp = get_filesystem_driver()
825 .ok_or(Errno::Nosys)?
826 .lock()
827 .send_command(cmd, rsp_payload_len)?;
828 let len: usize =
829 if (rsp.headers.out_header.len as usize) - mem::size_of::<fuse_out_header>() >= len {
830 len
831 } else {
832 (rsp.headers.out_header.len as usize) - mem::size_of::<fuse_out_header>()
833 };
834 self.offset += len;
835
836 buf[..len].copy_from_slice(&rsp.payload.unwrap()[..len]);
837
838 Ok(len)
839 }
840}
841
842impl Write for VirtioFsFileHandleInner {
843 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
844 debug!("virtio-fs write!");
845 let mut truncated_len = buf.len();
846 if truncated_len > MAX_WRITE_LEN {
847 debug!(
848 "Writing longer than max_write_len: {} > {}",
849 buf.len(),
850 MAX_WRITE_LEN
851 );
852 truncated_len = MAX_WRITE_LEN;
853 }
854
855 let nid = self.fuse_nid.ok_or(Errno::Io)?;
856 let fh = self.fuse_fh.ok_or(Errno::Io)?;
857
858 let truncated_buf = Box::<[u8]>::from(&buf[..truncated_len]);
859 let (cmd, rsp_payload_len) = ops::Write::create(nid, fh, truncated_buf, self.offset as u64);
860 let rsp = get_filesystem_driver()
861 .ok_or(Errno::Nosys)?
862 .lock()
863 .send_command(cmd, rsp_payload_len)?;
864
865 if rsp.headers.out_header.error < 0 {
866 return Err(Errno::Io);
867 }
868
869 let rsp_size = rsp.headers.op_header.size;
870 let rsp_len: usize = if rsp_size > u32::try_from(truncated_len).unwrap() {
871 truncated_len
872 } else {
873 rsp_size.try_into().unwrap()
874 };
875 self.offset += rsp_len;
876 Ok(rsp_len)
877 }
878
879 fn flush(&mut self) -> Result<(), Self::Error> {
880 Ok(())
881 }
882}
883
884impl Drop for VirtioFsFileHandleInner {
885 fn drop(&mut self) {
886 let Some(fuse_nid) = self.fuse_nid else {
887 return;
888 };
889
890 let Some(fuse_fh) = self.fuse_fh else {
891 return;
892 };
893
894 let (cmd, rsp_payload_len) = ops::Release::create(fuse_nid, fuse_fh);
895 get_filesystem_driver()
896 .unwrap()
897 .lock()
898 .send_command(cmd, rsp_payload_len)
899 .unwrap();
900 }
901}
902
903pub struct VirtioFsFileHandle(Arc<Mutex<VirtioFsFileHandleInner>>);
904
905impl VirtioFsFileHandle {
906 pub fn new() -> Self {
907 Self(Arc::new(Mutex::new(VirtioFsFileHandleInner::new())))
908 }
909}
910
911impl ObjectInterface for VirtioFsFileHandle {
912 async fn poll(&self, event: PollEvent) -> io::Result<PollEvent> {
913 self.0.lock().await.poll(event).await
914 }
915
916 async fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
917 self.0.lock().await.read(buf)
918 }
919
920 async fn write(&self, buf: &[u8]) -> io::Result<usize> {
921 self.0.lock().await.write(buf)
922 }
923
924 async fn lseek(&self, offset: isize, whence: SeekWhence) -> io::Result<isize> {
925 self.0.lock().await.lseek(offset, whence)
926 }
927
928 async fn fstat(&self) -> io::Result<FileAttr> {
929 self.0.lock().await.fstat()
930 }
931
932 async fn truncate(&self, size: usize) -> io::Result<()> {
933 let attr = FileAttr {
934 st_size: size.try_into().unwrap(),
935 ..FileAttr::default()
936 };
937
938 self.0
939 .lock()
940 .await
941 .set_attr(attr, SetAttrValidFields::FATTR_SIZE)
942 .map(|_| ())
943 }
944
945 async fn chmod(&self, access_permission: AccessPermission) -> io::Result<()> {
946 let attr = FileAttr {
947 st_mode: access_permission,
948 ..FileAttr::default()
949 };
950
951 self.0
952 .lock()
953 .await
954 .set_attr(attr, SetAttrValidFields::FATTR_MODE)
955 .map(|_| ())
956 }
957}
958
959impl Clone for VirtioFsFileHandle {
960 fn clone(&self) -> Self {
961 warn!("VirtioFsFileHandle: clone not tested");
962 Self(self.0.clone())
963 }
964}
965
966pub struct VirtioFsDirectoryHandle {
967 name: String,
968 read_position: Mutex<usize>,
969}
970
971impl VirtioFsDirectoryHandle {
972 pub fn new(name: String) -> Self {
973 Self {
974 name,
975 read_position: Mutex::new(0),
976 }
977 }
978}
979
980impl ObjectInterface for VirtioFsDirectoryHandle {
981 async fn getdents(&self, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
982 let path = if self.name.is_empty() {
983 CString::new("/").unwrap()
984 } else {
985 let path = ["/", &self.name].join("");
986 CString::new(path).unwrap()
987 };
988
989 debug!("virtio-fs opendir: {path:#?}");
990
991 let fuse_nid = lookup(path.clone()).ok_or(Errno::Noent)?;
992
993 let (mut cmd, rsp_payload_len) = ops::Open::create(fuse_nid, 0x10000);
996 cmd.headers.in_header.opcode = fuse_opcode::FUSE_OPENDIR as u32;
997 let rsp = get_filesystem_driver()
998 .ok_or(Errno::Nosys)?
999 .lock()
1000 .send_command(cmd, rsp_payload_len)?;
1001 let fuse_fh = rsp.headers.op_header.fh;
1002
1003 debug!("virtio-fs readdir: {path:#?}");
1004
1005 let len = MAX_READ_LEN as u32;
1007 let rsp_offset: &mut usize = &mut *self.read_position.lock().await;
1008 let mut buf_offset: usize = 0;
1009
1010 let (mut cmd, rsp_payload_len) = ops::Read::create(fuse_nid, fuse_fh, len, 0);
1012 cmd.headers.in_header.opcode = fuse_opcode::FUSE_READDIR as u32;
1013 let rsp = get_filesystem_driver()
1014 .ok_or(Errno::Nosys)?
1015 .lock()
1016 .send_command(cmd, rsp_payload_len)?;
1017
1018 let len = usize::min(
1019 MAX_READ_LEN,
1020 rsp.headers.out_header.len as usize - mem::size_of::<fuse_out_header>(),
1021 );
1022
1023 if len <= mem::size_of::<fuse_dirent>() {
1024 debug!("virtio-fs no new dirs");
1025 return Err(Errno::Noent);
1026 }
1027
1028 let mut ret = 0;
1029
1030 while (rsp.headers.out_header.len as usize) - *rsp_offset > mem::size_of::<fuse_dirent>() {
1031 let dirent = unsafe {
1032 &*rsp
1033 .payload
1034 .as_ref()
1035 .unwrap()
1036 .as_ptr()
1037 .byte_add(*rsp_offset)
1038 .cast::<fuse_dirent>()
1039 };
1040
1041 let dirent_len = mem::offset_of!(Dirent64, d_name) + dirent.namelen as usize + 1;
1042 let next_dirent = (buf_offset + dirent_len).align_up(mem::align_of::<Dirent64>());
1043
1044 if next_dirent > buf.len() {
1045 break;
1047 }
1048
1049 let target_dirent = buf[buf_offset].as_mut_ptr().cast::<Dirent64>();
1051 unsafe {
1052 target_dirent.write(Dirent64 {
1053 d_ino: dirent.ino,
1054 d_off: 0,
1055 d_reclen: (dirent_len.align_up(mem::align_of::<Dirent64>()))
1056 .try_into()
1057 .unwrap(),
1058 d_type: (dirent.type_ as u8).try_into().unwrap(),
1059 d_name: PhantomData {},
1060 });
1061 let nameptr = ptr::from_mut(&mut (*(target_dirent)).d_name).cast::<u8>();
1062 nameptr.copy_from_nonoverlapping(
1063 dirent.name.as_ptr().cast::<u8>(),
1064 dirent.namelen as usize,
1065 );
1066 nameptr.add(dirent.namelen as usize).write(0); }
1068
1069 *rsp_offset += mem::size_of::<fuse_dirent>() + dirent.namelen as usize;
1070 *rsp_offset = ((*rsp_offset) + U64_SIZE - 1) & (!(U64_SIZE - 1));
1072 buf_offset = next_dirent;
1073 ret = buf_offset;
1074 }
1075
1076 let (cmd, rsp_payload_len) = ops::Release::create(fuse_nid, fuse_fh);
1077 get_filesystem_driver()
1078 .unwrap()
1079 .lock()
1080 .send_command(cmd, rsp_payload_len)?;
1081
1082 Ok(ret)
1083 }
1084
1085 async fn lseek(&self, offset: isize, whence: SeekWhence) -> io::Result<isize> {
1090 if whence != SeekWhence::Set && offset != 0 {
1091 error!("Invalid offset for directory lseek ({offset})");
1092 return Err(Errno::Inval);
1093 }
1094 *self.read_position.lock().await = offset as usize;
1095 Ok(offset)
1096 }
1097}
1098
1099#[derive(Debug)]
1100pub(crate) struct VirtioFsDirectory {
1101 prefix: String,
1106 attr: FileAttr,
1107}
1108
1109impl VirtioFsDirectory {
1110 pub fn new(prefix: String) -> Self {
1111 let microseconds = arch::kernel::systemtime::now_micros();
1112 let t = timespec::from_usec(microseconds as i64);
1113
1114 VirtioFsDirectory {
1115 prefix,
1116 attr: FileAttr {
1117 st_mode: AccessPermission::from_bits(0o777).unwrap() | AccessPermission::S_IFDIR,
1118 st_atim: t,
1119 st_mtim: t,
1120 st_ctim: t,
1121 ..Default::default()
1122 },
1123 }
1124 }
1125
1126 fn traversal_path(&self, path: &str) -> CString {
1127 let prefix = self.prefix.as_str();
1128 let prefix = prefix.strip_suffix("/").unwrap_or(prefix);
1129 let path = [prefix, path].join("/");
1130 CString::new(path).unwrap()
1131 }
1132}
1133
1134impl VfsNode for VirtioFsDirectory {
1135 fn get_kind(&self) -> NodeKind {
1137 NodeKind::Directory
1138 }
1139
1140 fn get_file_attributes(&self) -> io::Result<FileAttr> {
1141 Ok(self.attr)
1142 }
1143
1144 fn get_object(&self) -> io::Result<Arc<async_lock::RwLock<Fd>>> {
1145 Ok(Arc::new(async_lock::RwLock::new(
1146 VirtioFsDirectoryHandle::new(self.prefix.clone()).into(),
1147 )))
1148 }
1149
1150 fn traverse_readdir(&self, path: &str) -> io::Result<Vec<DirectoryEntry>> {
1151 let path = self.traversal_path(path);
1152
1153 debug!("virtio-fs opendir: {path:#?}");
1154
1155 let fuse_nid = lookup(path.clone()).ok_or(Errno::Noent)?;
1156
1157 let (mut cmd, rsp_payload_len) = ops::Open::create(fuse_nid, 0x10000);
1160 cmd.headers.in_header.opcode = fuse_opcode::FUSE_OPENDIR as u32;
1161 let rsp = get_filesystem_driver()
1162 .ok_or(Errno::Nosys)?
1163 .lock()
1164 .send_command(cmd, rsp_payload_len)?;
1165 let fuse_fh = rsp.headers.op_header.fh;
1166
1167 debug!("virtio-fs readdir: {path:#?}");
1168
1169 let len = MAX_READ_LEN as u32;
1171 let mut offset: usize = 0;
1172
1173 let (mut cmd, rsp_payload_len) = ops::Read::create(fuse_nid, fuse_fh, len, 0);
1175 cmd.headers.in_header.opcode = fuse_opcode::FUSE_READDIR as u32;
1176 let rsp = get_filesystem_driver()
1177 .ok_or(Errno::Nosys)?
1178 .lock()
1179 .send_command(cmd, rsp_payload_len)?;
1180
1181 let len: usize = if rsp.headers.out_header.len as usize - mem::size_of::<fuse_out_header>()
1182 >= usize::try_from(len).unwrap()
1183 {
1184 len.try_into().unwrap()
1185 } else {
1186 (rsp.headers.out_header.len as usize) - mem::size_of::<fuse_out_header>()
1187 };
1188
1189 if len <= mem::size_of::<fuse_dirent>() {
1190 debug!("virtio-fs no new dirs");
1191 return Err(Errno::Noent);
1192 }
1193
1194 let mut entries: Vec<DirectoryEntry> = Vec::new();
1195 while (rsp.headers.out_header.len as usize) - offset > mem::size_of::<fuse_dirent>() {
1196 let dirent = unsafe {
1197 &*rsp
1198 .payload
1199 .as_ref()
1200 .unwrap()
1201 .as_ptr()
1202 .byte_add(offset)
1203 .cast::<fuse_dirent>()
1204 };
1205
1206 offset += mem::size_of::<fuse_dirent>() + dirent.namelen as usize;
1207 offset = ((offset) + U64_SIZE - 1) & (!(U64_SIZE - 1));
1209
1210 let name: &'static [u8] = unsafe {
1211 slice::from_raw_parts(
1212 dirent.name.as_ptr().cast(),
1213 dirent.namelen.try_into().unwrap(),
1214 )
1215 };
1216 entries.push(DirectoryEntry::new(unsafe {
1217 core::str::from_utf8_unchecked(name).to_owned()
1218 }));
1219 }
1220
1221 let (cmd, rsp_payload_len) = ops::Release::create(fuse_nid, fuse_fh);
1222 get_filesystem_driver()
1223 .unwrap()
1224 .lock()
1225 .send_command(cmd, rsp_payload_len)?;
1226
1227 Ok(entries)
1228 }
1229
1230 fn traverse_stat(&self, path: &str) -> io::Result<FileAttr> {
1231 let path = self.traversal_path(path);
1232
1233 debug!("virtio-fs stat: {path:#?}");
1234
1235 let (cmd, rsp_payload_len) = ops::Lookup::create(path);
1237 let rsp = get_filesystem_driver()
1238 .unwrap()
1239 .lock()
1240 .send_command(cmd, rsp_payload_len)?;
1241
1242 if rsp.headers.out_header.error != 0 {
1243 return Err(Errno::try_from(-rsp.headers.out_header.error).unwrap());
1244 }
1245
1246 let entry_out = rsp.headers.op_header;
1247 let attr = entry_out.attr;
1248
1249 if attr.mode & S_IFMT != S_IFLNK {
1250 return Ok(FileAttr::from(attr));
1251 }
1252
1253 let path = readlink(entry_out.nodeid)?;
1254 self.traverse_stat(&path)
1255 }
1256
1257 fn traverse_lstat(&self, path: &str) -> io::Result<FileAttr> {
1258 let path = self.traversal_path(path);
1259
1260 debug!("virtio-fs lstat: {path:#?}");
1261
1262 let (cmd, rsp_payload_len) = ops::Lookup::create(path);
1263 let rsp = get_filesystem_driver()
1264 .unwrap()
1265 .lock()
1266 .send_command(cmd, rsp_payload_len)?;
1267 Ok(FileAttr::from(rsp.headers.op_header.attr))
1268 }
1269
1270 fn traverse_open(
1271 &self,
1272 path: &str,
1273 opt: OpenOption,
1274 mode: AccessPermission,
1275 ) -> io::Result<Arc<async_lock::RwLock<Fd>>> {
1276 let path = self.traversal_path(path);
1277
1278 debug!("virtio-fs open: {path:#?}, {opt:?} {mode:?}");
1279
1280 if opt.contains(OpenOption::O_DIRECTORY) {
1281 if opt.contains(OpenOption::O_CREAT) {
1282 warn!("O_DIRECTORY and O_CREAT are together invalid as open options.");
1284 return Err(Errno::Inval);
1285 }
1286
1287 let (cmd, rsp_payload_len) = ops::Lookup::create(path.clone());
1288 let rsp = get_filesystem_driver()
1289 .unwrap()
1290 .lock()
1291 .send_command(cmd, rsp_payload_len)?;
1292
1293 let attr = FileAttr::from(rsp.headers.op_header.attr);
1294 if !attr.st_mode.contains(AccessPermission::S_IFDIR) {
1295 return Err(Errno::Notdir);
1296 }
1297
1298 let mut path = path.into_string().unwrap();
1299 path.remove(0);
1300 return Ok(Arc::new(async_lock::RwLock::new(
1301 VirtioFsDirectoryHandle::new(path).into(),
1302 )));
1303 }
1304
1305 let file = VirtioFsFileHandle::new();
1306
1307 let mut file_guard = block_on(async { Ok(file.0.lock().await) }, None)?;
1310
1311 if opt.contains(OpenOption::O_CREAT) {
1313 let (cmd, rsp_payload_len) =
1315 ops::Create::create(path, opt.bits().try_into().unwrap(), mode.bits());
1316 let rsp = get_filesystem_driver()
1317 .ok_or(Errno::Nosys)?
1318 .lock()
1319 .send_command(cmd, rsp_payload_len)?;
1320
1321 let inner = rsp.headers.op_header;
1322 file_guard.fuse_nid = Some(inner.entry.nodeid);
1323 file_guard.fuse_fh = Some(inner.open.fh);
1324 } else {
1325 file_guard.fuse_nid = lookup(path);
1327
1328 if file_guard.fuse_nid.is_none() {
1329 warn!("virtio-fs lookup seems to have failed!");
1330 return Err(Errno::Noent);
1331 }
1332
1333 let (cmd, rsp_payload_len) =
1335 ops::Open::create(file_guard.fuse_nid.unwrap(), opt.bits().try_into().unwrap());
1336 let rsp = get_filesystem_driver()
1337 .ok_or(Errno::Nosys)?
1338 .lock()
1339 .send_command(cmd, rsp_payload_len)?;
1340 file_guard.fuse_fh = Some(rsp.headers.op_header.fh);
1341 }
1342
1343 drop(file_guard);
1344
1345 Ok(Arc::new(async_lock::RwLock::new(file.into())))
1346 }
1347
1348 fn traverse_unlink(&self, path: &str) -> io::Result<()> {
1349 let path = self.traversal_path(path);
1350
1351 let (cmd, rsp_payload_len) = ops::Unlink::create(path);
1352 let rsp = get_filesystem_driver()
1353 .ok_or(Errno::Nosys)?
1354 .lock()
1355 .send_command(cmd, rsp_payload_len)?;
1356 trace!("unlink answer {rsp:?}");
1357
1358 Ok(())
1359 }
1360
1361 fn traverse_rmdir(&self, path: &str) -> io::Result<()> {
1362 let path = self.traversal_path(path);
1363
1364 let (cmd, rsp_payload_len) = ops::Rmdir::create(path);
1365 let rsp = get_filesystem_driver()
1366 .ok_or(Errno::Nosys)?
1367 .lock()
1368 .send_command(cmd, rsp_payload_len)?;
1369 trace!("rmdir answer {rsp:?}");
1370
1371 Ok(())
1372 }
1373
1374 fn traverse_mkdir(&self, path: &str, mode: AccessPermission) -> io::Result<()> {
1375 let path = self.traversal_path(path);
1376 let (cmd, rsp_payload_len) = ops::Mkdir::create(path, mode.bits());
1377
1378 let rsp = get_filesystem_driver()
1379 .ok_or(Errno::Nosys)?
1380 .lock()
1381 .send_command(cmd, rsp_payload_len)?;
1382 if rsp.headers.out_header.error != 0 {
1383 return Err(Errno::try_from(-rsp.headers.out_header.error).unwrap());
1384 }
1385
1386 Ok(())
1387 }
1388}
1389
1390pub(crate) fn init() {
1391 debug!("Try to initialize virtio-fs filesystem");
1392
1393 let Some(driver) = get_filesystem_driver() else {
1394 return;
1395 };
1396
1397 let (cmd, rsp_payload_len) = ops::Init::create();
1398 let rsp = driver.lock().send_command(cmd, rsp_payload_len).unwrap();
1399 trace!("virtio-fs init answer: {rsp:?}");
1400
1401 let mount_point = driver.lock().get_mount_point();
1402 if mount_point != "/" {
1403 let mount_point = if mount_point.starts_with('/') {
1404 mount_point
1405 } else {
1406 ["/", &mount_point].join("")
1407 };
1408
1409 info!("Mounting virtio-fs at {mount_point}");
1410 fs::FILESYSTEM
1411 .get()
1412 .unwrap()
1413 .mount(
1414 mount_point.as_str(),
1415 Box::new(VirtioFsDirectory::new("/".to_owned())),
1416 )
1417 .expect("Mount failed. Invalid mount_point?");
1418 return;
1419 }
1420
1421 let fuse_nid = lookup(c"/".to_owned()).unwrap();
1422 let (mut cmd, rsp_payload_len) = ops::Open::create(fuse_nid, 0x10000);
1425 cmd.headers.in_header.opcode = fuse_opcode::FUSE_OPENDIR as u32;
1426 let rsp = get_filesystem_driver()
1427 .unwrap()
1428 .lock()
1429 .send_command(cmd, rsp_payload_len)
1430 .unwrap();
1431 let fuse_fh = rsp.headers.op_header.fh;
1432
1433 let len = MAX_READ_LEN as u32;
1435 let mut offset: usize = 0;
1436
1437 let (mut cmd, rsp_payload_len) = ops::Read::create(fuse_nid, fuse_fh, len, 0);
1439 cmd.headers.in_header.opcode = fuse_opcode::FUSE_READDIR as u32;
1440 let rsp = get_filesystem_driver()
1441 .unwrap()
1442 .lock()
1443 .send_command(cmd, rsp_payload_len)
1444 .unwrap();
1445
1446 let len: usize = if rsp.headers.out_header.len as usize - mem::size_of::<fuse_out_header>()
1447 >= usize::try_from(len).unwrap()
1448 {
1449 len.try_into().unwrap()
1450 } else {
1451 (rsp.headers.out_header.len as usize) - mem::size_of::<fuse_out_header>()
1452 };
1453
1454 assert!(len > mem::size_of::<fuse_dirent>(), "virtio-fs no new dirs");
1455
1456 let mut entries: Vec<String> = Vec::new();
1457 while (rsp.headers.out_header.len as usize) - offset > mem::size_of::<fuse_dirent>() {
1458 let dirent = unsafe {
1459 &*rsp
1460 .payload
1461 .as_ref()
1462 .unwrap()
1463 .as_ptr()
1464 .byte_add(offset)
1465 .cast::<fuse_dirent>()
1466 };
1467
1468 offset += mem::size_of::<fuse_dirent>() + dirent.namelen as usize;
1469 offset = ((offset) + U64_SIZE - 1) & (!(U64_SIZE - 1));
1471
1472 let name: &'static [u8] = unsafe {
1473 slice::from_raw_parts(
1474 dirent.name.as_ptr().cast(),
1475 dirent.namelen.try_into().unwrap(),
1476 )
1477 };
1478 entries.push(unsafe { core::str::from_utf8_unchecked(name).to_owned() });
1479 }
1480
1481 let (cmd, rsp_payload_len) = ops::Release::create(fuse_nid, fuse_fh);
1482 get_filesystem_driver()
1483 .unwrap()
1484 .lock()
1485 .send_command(cmd, rsp_payload_len)
1486 .unwrap();
1487
1488 entries.retain(|x| x != ".");
1490 entries.retain(|x| x != "..");
1491 entries.retain(|x| x != "tmp");
1492 entries.retain(|x| x != "proc");
1493 warn!(
1494 "virtio-fs don't mount the host directories 'tmp' and 'proc' into the guest file system!"
1495 );
1496
1497 for entry in entries {
1498 let i_cstr = CString::new(entry.as_str()).unwrap();
1499 let (cmd, rsp_payload_len) = ops::Lookup::create(i_cstr);
1500 let rsp = get_filesystem_driver()
1501 .unwrap()
1502 .lock()
1503 .send_command(cmd, rsp_payload_len)
1504 .unwrap();
1505
1506 let attr = FileAttr::from(rsp.headers.op_header.attr);
1507 if attr.st_mode.contains(AccessPermission::S_IFDIR) {
1508 let path = ["/", &entry].join("");
1509 info!("virtio-fs mount {entry} to {path}");
1510 fs::FILESYSTEM
1511 .get()
1512 .unwrap()
1513 .mount(&path, Box::new(VirtioFsDirectory::new(path.clone())))
1514 .expect("Mount failed. Invalid mount_point?");
1515 } else {
1516 warn!("virtio-fs don't mount {entry}. It isn't a directory!");
1517 }
1518 }
1519}