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 break;
1046 }
1047
1048 let target_dirent = buf[buf_offset].as_mut_ptr().cast::<Dirent64>();
1050 unsafe {
1051 target_dirent.write(Dirent64 {
1052 d_ino: dirent.ino,
1053 d_off: 0,
1054 d_reclen: (dirent_len.align_up(align_of::<Dirent64>()))
1055 .try_into()
1056 .unwrap(),
1057 d_type: (dirent.type_ as u8).try_into().unwrap(),
1058 d_name: PhantomData {},
1059 });
1060 let nameptr = ptr::from_mut(&mut (*(target_dirent)).d_name).cast::<u8>();
1061 nameptr.copy_from_nonoverlapping(
1062 dirent.name.as_ptr().cast::<u8>(),
1063 dirent.namelen as usize,
1064 );
1065 nameptr.add(dirent.namelen as usize).write(0); }
1067
1068 *rsp_offset += size_of::<fuse_dirent>() + dirent.namelen as usize;
1069 *rsp_offset = ((*rsp_offset) + U64_SIZE - 1) & (!(U64_SIZE - 1));
1071 buf_offset = next_dirent;
1072 ret = buf_offset;
1073 }
1074
1075 let (cmd, rsp_payload_len) = ops::Release::create(fuse_nid, fuse_fh);
1076 get_filesystem_driver()
1077 .unwrap()
1078 .lock()
1079 .send_command(cmd, rsp_payload_len)?;
1080
1081 Ok(ret)
1082 }
1083
1084 async fn lseek(&self, offset: isize, whence: SeekWhence) -> io::Result<isize> {
1089 if whence != SeekWhence::Set && offset != 0 {
1090 error!("Invalid offset for directory lseek ({offset})");
1091 return Err(Errno::Inval);
1092 }
1093 *self.read_position.lock().await = offset as usize;
1094 Ok(offset)
1095 }
1096}
1097
1098#[derive(Debug)]
1099pub(crate) struct VirtioFsDirectory {
1100 prefix: String,
1105 attr: FileAttr,
1106}
1107
1108impl VirtioFsDirectory {
1109 pub fn new(prefix: String) -> Self {
1110 let microseconds = arch::kernel::systemtime::now_micros();
1111 let t = timespec::from_usec(microseconds as i64);
1112
1113 VirtioFsDirectory {
1114 prefix,
1115 attr: FileAttr {
1116 st_mode: AccessPermission::from_bits(0o777).unwrap() | AccessPermission::S_IFDIR,
1117 st_atim: t,
1118 st_mtim: t,
1119 st_ctim: t,
1120 ..Default::default()
1121 },
1122 }
1123 }
1124
1125 fn traversal_path(&self, path: &str) -> CString {
1126 let prefix = self.prefix.as_str();
1127 let prefix = prefix.strip_suffix("/").unwrap_or(prefix);
1128 let path = [prefix, path].join("/");
1129 CString::new(path).unwrap()
1130 }
1131}
1132
1133impl VfsNode for VirtioFsDirectory {
1134 fn get_kind(&self) -> NodeKind {
1136 NodeKind::Directory
1137 }
1138
1139 fn get_file_attributes(&self) -> io::Result<FileAttr> {
1140 Ok(self.attr)
1141 }
1142
1143 fn get_object(&self) -> io::Result<Arc<async_lock::RwLock<Fd>>> {
1144 Ok(Arc::new(async_lock::RwLock::new(
1145 VirtioFsDirectoryHandle::new(self.prefix.clone()).into(),
1146 )))
1147 }
1148
1149 fn traverse_readdir(&self, path: &str) -> io::Result<Vec<DirectoryEntry>> {
1150 let path = self.traversal_path(path);
1151
1152 debug!("virtio-fs opendir: {path:#?}");
1153
1154 let fuse_nid = lookup(path.clone()).ok_or(Errno::Noent)?;
1155
1156 let (mut cmd, rsp_payload_len) = ops::Open::create(fuse_nid, 0x10000);
1159 cmd.headers.in_header.opcode = fuse_opcode::FUSE_OPENDIR as u32;
1160 let rsp = get_filesystem_driver()
1161 .ok_or(Errno::Nosys)?
1162 .lock()
1163 .send_command(cmd, rsp_payload_len)?;
1164 let fuse_fh = rsp.headers.op_header.fh;
1165
1166 debug!("virtio-fs readdir: {path:#?}");
1167
1168 let len = MAX_READ_LEN as u32;
1170 let mut offset: usize = 0;
1171
1172 let (mut cmd, rsp_payload_len) = ops::Read::create(fuse_nid, fuse_fh, len, 0);
1174 cmd.headers.in_header.opcode = fuse_opcode::FUSE_READDIR as u32;
1175 let rsp = get_filesystem_driver()
1176 .ok_or(Errno::Nosys)?
1177 .lock()
1178 .send_command(cmd, rsp_payload_len)?;
1179
1180 let len: usize = if rsp.headers.out_header.len as usize - size_of::<fuse_out_header>()
1181 >= usize::try_from(len).unwrap()
1182 {
1183 len.try_into().unwrap()
1184 } else {
1185 (rsp.headers.out_header.len as usize) - size_of::<fuse_out_header>()
1186 };
1187
1188 if len <= size_of::<fuse_dirent>() {
1189 debug!("virtio-fs no new dirs");
1190 return Err(Errno::Noent);
1191 }
1192
1193 let mut entries: Vec<DirectoryEntry> = Vec::new();
1194 while (rsp.headers.out_header.len as usize) - offset > size_of::<fuse_dirent>() {
1195 let dirent = unsafe {
1196 &*rsp
1197 .payload
1198 .as_ref()
1199 .unwrap()
1200 .as_ptr()
1201 .byte_add(offset)
1202 .cast::<fuse_dirent>()
1203 };
1204
1205 offset += size_of::<fuse_dirent>() + dirent.namelen as usize;
1206 offset = ((offset) + U64_SIZE - 1) & (!(U64_SIZE - 1));
1208
1209 let name: &'static [u8] = unsafe {
1210 slice::from_raw_parts(
1211 dirent.name.as_ptr().cast(),
1212 dirent.namelen.try_into().unwrap(),
1213 )
1214 };
1215 entries.push(DirectoryEntry::new(unsafe {
1216 core::str::from_utf8_unchecked(name).to_owned()
1217 }));
1218 }
1219
1220 let (cmd, rsp_payload_len) = ops::Release::create(fuse_nid, fuse_fh);
1221 get_filesystem_driver()
1222 .unwrap()
1223 .lock()
1224 .send_command(cmd, rsp_payload_len)?;
1225
1226 Ok(entries)
1227 }
1228
1229 fn traverse_stat(&self, path: &str) -> io::Result<FileAttr> {
1230 let path = self.traversal_path(path);
1231
1232 debug!("virtio-fs stat: {path:#?}");
1233
1234 let (cmd, rsp_payload_len) = ops::Lookup::create(path);
1236 let rsp = get_filesystem_driver()
1237 .unwrap()
1238 .lock()
1239 .send_command(cmd, rsp_payload_len)?;
1240
1241 if rsp.headers.out_header.error != 0 {
1242 return Err(Errno::try_from(-rsp.headers.out_header.error).unwrap());
1243 }
1244
1245 let entry_out = rsp.headers.op_header;
1246 let attr = entry_out.attr;
1247
1248 if attr.mode & S_IFMT != S_IFLNK {
1249 return Ok(FileAttr::from(attr));
1250 }
1251
1252 let path = readlink(entry_out.nodeid)?;
1253 self.traverse_stat(&path)
1254 }
1255
1256 fn traverse_lstat(&self, path: &str) -> io::Result<FileAttr> {
1257 let path = self.traversal_path(path);
1258
1259 debug!("virtio-fs lstat: {path:#?}");
1260
1261 let (cmd, rsp_payload_len) = ops::Lookup::create(path);
1262 let rsp = get_filesystem_driver()
1263 .unwrap()
1264 .lock()
1265 .send_command(cmd, rsp_payload_len)?;
1266 Ok(FileAttr::from(rsp.headers.op_header.attr))
1267 }
1268
1269 fn traverse_open(
1270 &self,
1271 path: &str,
1272 opt: OpenOption,
1273 mode: AccessPermission,
1274 ) -> io::Result<Arc<async_lock::RwLock<Fd>>> {
1275 let path = self.traversal_path(path);
1276
1277 debug!("virtio-fs open: {path:#?}, {opt:?} {mode:?}");
1278
1279 if opt.contains(OpenOption::O_DIRECTORY) {
1280 if opt.contains(OpenOption::O_CREAT) {
1281 warn!("O_DIRECTORY and O_CREAT are together invalid as open options.");
1283 return Err(Errno::Inval);
1284 }
1285
1286 let (cmd, rsp_payload_len) = ops::Lookup::create(path.clone());
1287 let rsp = get_filesystem_driver()
1288 .unwrap()
1289 .lock()
1290 .send_command(cmd, rsp_payload_len)?;
1291
1292 let attr = FileAttr::from(rsp.headers.op_header.attr);
1293 if !attr.st_mode.contains(AccessPermission::S_IFDIR) {
1294 return Err(Errno::Notdir);
1295 }
1296
1297 let mut path = path.into_string().unwrap();
1298 path.remove(0);
1299 return Ok(Arc::new(async_lock::RwLock::new(
1300 VirtioFsDirectoryHandle::new(path).into(),
1301 )));
1302 }
1303
1304 let file = VirtioFsFileHandle::new();
1305
1306 let mut file_guard = block_on(async { Ok(file.0.lock().await) }, None)?;
1309
1310 if opt.contains(OpenOption::O_CREAT) {
1312 let (cmd, rsp_payload_len) =
1314 ops::Create::create(path, opt.bits().try_into().unwrap(), mode.bits());
1315 let rsp = get_filesystem_driver()
1316 .ok_or(Errno::Nosys)?
1317 .lock()
1318 .send_command(cmd, rsp_payload_len)?;
1319
1320 let inner = rsp.headers.op_header;
1321 file_guard.fuse_nid = Some(inner.entry.nodeid);
1322 file_guard.fuse_fh = Some(inner.open.fh);
1323 } else {
1324 file_guard.fuse_nid = lookup(path);
1326
1327 if file_guard.fuse_nid.is_none() {
1328 warn!("virtio-fs lookup seems to have failed!");
1329 return Err(Errno::Noent);
1330 }
1331
1332 let (cmd, rsp_payload_len) =
1334 ops::Open::create(file_guard.fuse_nid.unwrap(), opt.bits().try_into().unwrap());
1335 let rsp = get_filesystem_driver()
1336 .ok_or(Errno::Nosys)?
1337 .lock()
1338 .send_command(cmd, rsp_payload_len)?;
1339 file_guard.fuse_fh = Some(rsp.headers.op_header.fh);
1340 }
1341
1342 drop(file_guard);
1343
1344 Ok(Arc::new(async_lock::RwLock::new(file.into())))
1345 }
1346
1347 fn traverse_unlink(&self, path: &str) -> io::Result<()> {
1348 let path = self.traversal_path(path);
1349
1350 let (cmd, rsp_payload_len) = ops::Unlink::create(path);
1351 let rsp = get_filesystem_driver()
1352 .ok_or(Errno::Nosys)?
1353 .lock()
1354 .send_command(cmd, rsp_payload_len)?;
1355 trace!("unlink answer {rsp:?}");
1356
1357 Ok(())
1358 }
1359
1360 fn traverse_rmdir(&self, path: &str) -> io::Result<()> {
1361 let path = self.traversal_path(path);
1362
1363 let (cmd, rsp_payload_len) = ops::Rmdir::create(path);
1364 let rsp = get_filesystem_driver()
1365 .ok_or(Errno::Nosys)?
1366 .lock()
1367 .send_command(cmd, rsp_payload_len)?;
1368 trace!("rmdir answer {rsp:?}");
1369
1370 Ok(())
1371 }
1372
1373 fn traverse_mkdir(&self, path: &str, mode: AccessPermission) -> io::Result<()> {
1374 let path = self.traversal_path(path);
1375 let (cmd, rsp_payload_len) = ops::Mkdir::create(path, mode.bits());
1376
1377 let rsp = get_filesystem_driver()
1378 .ok_or(Errno::Nosys)?
1379 .lock()
1380 .send_command(cmd, rsp_payload_len)?;
1381 if rsp.headers.out_header.error != 0 {
1382 return Err(Errno::try_from(-rsp.headers.out_header.error).unwrap());
1383 }
1384
1385 Ok(())
1386 }
1387}
1388
1389pub(crate) fn init() {
1390 debug!("Try to initialize virtio-fs filesystem");
1391
1392 let Some(driver) = get_filesystem_driver() else {
1393 return;
1394 };
1395
1396 let (cmd, rsp_payload_len) = ops::Init::create();
1397 let rsp = driver.lock().send_command(cmd, rsp_payload_len).unwrap();
1398 trace!("virtio-fs init answer: {rsp:?}");
1399
1400 let mount_point = driver.lock().get_mount_point();
1401 if mount_point != "/" {
1402 let mount_point = if mount_point.starts_with('/') {
1403 mount_point
1404 } else {
1405 ["/", &mount_point].join("")
1406 };
1407
1408 info!("Mounting virtio-fs at {mount_point}");
1409 fs::FILESYSTEM
1410 .get()
1411 .unwrap()
1412 .mount(
1413 mount_point.as_str(),
1414 Box::new(VirtioFsDirectory::new("/".to_owned())),
1415 )
1416 .expect("Mount failed. Invalid mount_point?");
1417 return;
1418 }
1419
1420 let fuse_nid = lookup(c"/".to_owned()).unwrap();
1421 let (mut cmd, rsp_payload_len) = ops::Open::create(fuse_nid, 0x10000);
1424 cmd.headers.in_header.opcode = fuse_opcode::FUSE_OPENDIR as u32;
1425 let rsp = get_filesystem_driver()
1426 .unwrap()
1427 .lock()
1428 .send_command(cmd, rsp_payload_len)
1429 .unwrap();
1430 let fuse_fh = rsp.headers.op_header.fh;
1431
1432 let len = MAX_READ_LEN as u32;
1434 let mut offset: usize = 0;
1435
1436 let (mut cmd, rsp_payload_len) = ops::Read::create(fuse_nid, fuse_fh, len, 0);
1438 cmd.headers.in_header.opcode = fuse_opcode::FUSE_READDIR as u32;
1439 let rsp = get_filesystem_driver()
1440 .unwrap()
1441 .lock()
1442 .send_command(cmd, rsp_payload_len)
1443 .unwrap();
1444
1445 let len: usize = if rsp.headers.out_header.len as usize - size_of::<fuse_out_header>()
1446 >= usize::try_from(len).unwrap()
1447 {
1448 len.try_into().unwrap()
1449 } else {
1450 (rsp.headers.out_header.len as usize) - size_of::<fuse_out_header>()
1451 };
1452
1453 assert!(len > size_of::<fuse_dirent>(), "virtio-fs no new dirs");
1454
1455 let mut entries: Vec<String> = Vec::new();
1456 while (rsp.headers.out_header.len as usize) - offset > size_of::<fuse_dirent>() {
1457 let dirent = unsafe {
1458 &*rsp
1459 .payload
1460 .as_ref()
1461 .unwrap()
1462 .as_ptr()
1463 .byte_add(offset)
1464 .cast::<fuse_dirent>()
1465 };
1466
1467 offset += size_of::<fuse_dirent>() + dirent.namelen as usize;
1468 offset = ((offset) + U64_SIZE - 1) & (!(U64_SIZE - 1));
1470
1471 let name: &'static [u8] = unsafe {
1472 slice::from_raw_parts(
1473 dirent.name.as_ptr().cast(),
1474 dirent.namelen.try_into().unwrap(),
1475 )
1476 };
1477 entries.push(unsafe { core::str::from_utf8_unchecked(name).to_owned() });
1478 }
1479
1480 let (cmd, rsp_payload_len) = ops::Release::create(fuse_nid, fuse_fh);
1481 get_filesystem_driver()
1482 .unwrap()
1483 .lock()
1484 .send_command(cmd, rsp_payload_len)
1485 .unwrap();
1486
1487 entries.retain(|x| x != ".");
1489 entries.retain(|x| x != "..");
1490 entries.retain(|x| x != "tmp");
1491 entries.retain(|x| x != "proc");
1492 warn!(
1493 "virtio-fs don't mount the host directories 'tmp' and 'proc' into the guest file system!"
1494 );
1495
1496 for entry in entries {
1497 let i_cstr = CString::new(entry.as_str()).unwrap();
1498 let (cmd, rsp_payload_len) = ops::Lookup::create(i_cstr);
1499 let rsp = get_filesystem_driver()
1500 .unwrap()
1501 .lock()
1502 .send_command(cmd, rsp_payload_len)
1503 .unwrap();
1504
1505 let attr = FileAttr::from(rsp.headers.op_header.attr);
1506 if attr.st_mode.contains(AccessPermission::S_IFDIR) {
1507 let path = ["/", &entry].join("");
1508 info!("virtio-fs mount {entry} to {path}");
1509 fs::FILESYSTEM
1510 .get()
1511 .unwrap()
1512 .mount(&path, Box::new(VirtioFsDirectory::new(path.clone())))
1513 .expect("Mount failed. Invalid mount_point?");
1514 } else {
1515 warn!("virtio-fs don't mount {entry}. It isn't a directory!");
1516 }
1517 }
1518}