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