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