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