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, align_of, offset_of, size_of};
9use core::sync::atomic::{AtomicU64, Ordering};
10use core::task::Poll;
11use core::{future, mem};
12
13use align_address::Align;
14use async_lock::Mutex;
15use async_trait::async_trait;
16use fuse_abi::linux::*;
17
18use crate::alloc::string::ToString;
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::{
28 self, AccessPermission, DirectoryEntry, FileAttr, NodeKind, ObjectInterface, OpenOption,
29 SeekWhence, VfsNode,
30};
31use crate::mm::device_alloc::DeviceAlloc;
32use crate::syscalls::Dirent64;
33use crate::time::{time_t, timespec};
34use crate::{arch, io};
35
36const MAX_READ_LEN: usize = 1024 * 64;
41const MAX_WRITE_LEN: usize = 1024 * 64;
42
43const U64_SIZE: usize = mem::size_of::<u64>();
44
45const S_IFLNK: u32 = 0o120_000;
46const S_IFMT: u32 = 0o170_000;
47
48pub(crate) trait FuseInterface {
49 fn send_command<O: ops::Op + 'static>(
50 &mut self,
51 cmd: Cmd<O>,
52 rsp_payload_len: u32,
53 ) -> Result<Rsp<O>, FuseError>
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
66 use fuse_abi::linux::*;
67
68 use super::Cmd;
69 use crate::fd::PollEvent;
70 use crate::fs::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: core::fmt::Debug;
83 type InPayload: ?Sized;
84 type OutStruct: core::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 Readlink;
275
276 impl Op for Readlink {
277 const OP_CODE: fuse_opcode = fuse_opcode::FUSE_READLINK;
278 type InStruct = ();
279 type InPayload = ();
280 type OutStruct = ();
281 type OutPayload = [u8];
282 }
283
284 impl Readlink {
285 pub(crate) fn create(nid: u64, size: u32) -> (Cmd<Self>, u32) {
286 let cmd = Cmd::new(nid, ());
287 (cmd, size)
288 }
289 }
290
291 #[derive(Debug)]
292 pub(crate) struct Release;
293
294 impl Op for Release {
295 const OP_CODE: fuse_opcode = fuse_opcode::FUSE_RELEASE;
296 type InStruct = fuse_release_in;
297 type InPayload = ();
298 type OutStruct = ();
299 type OutPayload = ();
300 }
301
302 impl Release {
303 pub(crate) fn create(nid: u64, fh: u64) -> (Cmd<Self>, u32) {
304 let cmd = Cmd::new(
305 nid,
306 fuse_release_in {
307 fh,
308 ..Default::default()
309 },
310 );
311 (cmd, 0)
312 }
313 }
314
315 #[derive(Debug)]
316 pub(crate) struct Poll;
317
318 impl Op for Poll {
319 const OP_CODE: fuse_opcode = fuse_opcode::FUSE_POLL;
320 type InStruct = fuse_poll_in;
321 type InPayload = ();
322 type OutStruct = fuse_poll_out;
323 type OutPayload = ();
324 }
325
326 impl Poll {
327 pub(crate) fn create(nid: u64, fh: u64, kh: u64, event: PollEvent) -> (Cmd<Self>, u32) {
328 let cmd = Cmd::new(
329 nid,
330 fuse_poll_in {
331 fh,
332 kh,
333 events: event.bits() as u32,
334 ..Default::default()
335 },
336 );
337 (cmd, 0)
338 }
339 }
340
341 #[derive(Debug)]
342 pub(crate) struct Mkdir;
343
344 impl Op for Mkdir {
345 const OP_CODE: fuse_opcode = fuse_opcode::FUSE_MKDIR;
346 type InStruct = fuse_mkdir_in;
347 type InPayload = CString;
348 type OutStruct = fuse_entry_out;
349 type OutPayload = ();
350 }
351
352 impl Mkdir {
353 pub(crate) fn create(path: CString, mode: u32) -> (Cmd<Self>, u32) {
354 let cmd = Cmd::with_cstring(
355 FUSE_ROOT_ID,
356 fuse_mkdir_in {
357 mode,
358 ..Default::default()
359 },
360 path,
361 );
362 (cmd, 0)
363 }
364 }
365
366 #[derive(Debug)]
367 pub(crate) struct Unlink;
368
369 impl Op for Unlink {
370 const OP_CODE: fuse_opcode = fuse_opcode::FUSE_UNLINK;
371 type InStruct = ();
372 type InPayload = CString;
373 type OutStruct = ();
374 type OutPayload = ();
375 }
376
377 impl Unlink {
378 pub(crate) fn create(name: CString) -> (Cmd<Self>, u32) {
379 let cmd = Cmd::with_cstring(FUSE_ROOT_ID, (), name);
380 (cmd, 0)
381 }
382 }
383
384 #[derive(Debug)]
385 pub(crate) struct Rmdir;
386
387 impl Op for Rmdir {
388 const OP_CODE: fuse_opcode = fuse_opcode::FUSE_RMDIR;
389 type InStruct = ();
390 type InPayload = CString;
391 type OutStruct = ();
392 type OutPayload = ();
393 }
394
395 impl Rmdir {
396 pub(crate) fn create(name: CString) -> (Cmd<Self>, u32) {
397 let cmd = Cmd::with_cstring(FUSE_ROOT_ID, (), name);
398 (cmd, 0)
399 }
400 }
401
402 #[derive(Debug)]
403 pub(crate) struct Lookup;
404
405 impl Op for Lookup {
406 const OP_CODE: fuse_opcode = fuse_opcode::FUSE_LOOKUP;
407 type InStruct = ();
408 type InPayload = CString;
409 type OutStruct = fuse_entry_out;
410 type OutPayload = ();
411 }
412
413 impl Lookup {
414 pub(crate) fn create(name: CString) -> (Cmd<Self>, u32) {
415 let cmd = Cmd::with_cstring(FUSE_ROOT_ID, (), name);
416 (cmd, 0)
417 }
418 }
419}
420
421impl From<fuse_attr> for FileAttr {
422 fn from(attr: fuse_attr) -> FileAttr {
423 FileAttr {
424 st_ino: attr.ino,
425 st_nlink: attr.nlink.into(),
426 st_mode: AccessPermission::from_bits_retain(attr.mode),
427 st_uid: attr.uid,
428 st_gid: attr.gid,
429 st_rdev: attr.rdev.into(),
430 st_size: attr.size.try_into().unwrap(),
431 st_blksize: attr.blksize.into(),
432 st_blocks: attr.blocks.try_into().unwrap(),
433 st_atim: timespec {
434 tv_sec: attr.atime as time_t,
435 tv_nsec: attr.atimensec as i32,
436 },
437 st_mtim: timespec {
438 tv_sec: attr.mtime as time_t,
439 tv_nsec: attr.mtimensec as i32,
440 },
441 st_ctim: timespec {
442 tv_sec: attr.ctime as time_t,
443 tv_nsec: attr.ctimensec as i32,
444 },
445 ..Default::default()
446 }
447 }
448}
449
450#[repr(C)]
451#[derive(Debug)]
452pub(crate) struct CmdHeader<O: ops::Op> {
453 pub in_header: fuse_in_header,
454 op_header: O::InStruct,
455}
456
457impl<O: ops::Op> CmdHeader<O>
458where
459 O: ops::Op<InPayload = ()>,
460{
461 fn new(nodeid: u64, op_header: O::InStruct) -> Self {
462 Self::with_payload_size(nodeid, op_header, 0)
463 }
464}
465
466impl<O: ops::Op> CmdHeader<O> {
467 fn with_payload_size(nodeid: u64, op_header: O::InStruct, len: usize) -> CmdHeader<O> {
468 CmdHeader {
469 in_header: fuse_in_header {
470 len: (core::mem::size_of::<fuse_in_header>()
472 + core::mem::size_of::<O::InStruct>()
473 + len)
474 .try_into()
475 .expect("The command is too large"),
476 opcode: O::OP_CODE.into(),
477 nodeid,
478 unique: 1,
479 ..Default::default()
480 },
481 op_header,
482 }
483 }
484}
485
486pub(crate) struct Cmd<O: ops::Op> {
487 pub headers: Box<CmdHeader<O>, DeviceAlloc>,
488 pub payload: Option<Vec<u8, DeviceAlloc>>,
489}
490
491impl<O: ops::Op> Cmd<O>
492where
493 O: ops::Op<InPayload = ()>,
494{
495 fn new(nodeid: u64, op_header: O::InStruct) -> Self {
496 Self {
497 headers: Box::new_in(CmdHeader::new(nodeid, op_header), DeviceAlloc),
498 payload: None,
499 }
500 }
501}
502
503impl<O: ops::Op> Cmd<O>
504where
505 O: ops::Op<InPayload = CString>,
506{
507 fn with_cstring(nodeid: u64, op_header: O::InStruct, cstring: CString) -> Self {
508 let cstring_bytes = cstring.into_bytes_with_nul().to_vec_in(DeviceAlloc);
509 Self {
510 headers: Box::new_in(
511 CmdHeader::with_payload_size(nodeid, op_header, cstring_bytes.len()),
512 DeviceAlloc,
513 ),
514 payload: Some(cstring_bytes),
515 }
516 }
517}
518
519impl<O: ops::Op> Cmd<O>
520where
521 O: ops::Op<InPayload = [u8]>,
522{
523 fn with_boxed_slice(nodeid: u64, op_header: O::InStruct, slice: Box<[u8]>) -> Self {
524 let mut device_slice = Vec::with_capacity_in(slice.len(), DeviceAlloc);
525 device_slice.extend_from_slice(&slice);
526 Self {
527 headers: Box::new_in(
528 CmdHeader::with_payload_size(nodeid, op_header, slice.len()),
529 DeviceAlloc,
530 ),
531 payload: Some(device_slice),
532 }
533 }
534}
535
536#[repr(C)]
537#[derive(Debug)]
538pub(crate) struct RspHeader<O: ops::Op, H = <O as ops::Op>::OutStruct> {
542 pub out_header: fuse_out_header,
543 op_header: H,
544 _phantom: PhantomData<O::OutStruct>,
545}
546
547#[derive(Debug)]
548pub(crate) struct Rsp<O: ops::Op> {
549 pub headers: Box<RspHeader<O>, DeviceAlloc>,
550 pub payload: Option<Vec<u8, DeviceAlloc>>,
551}
552
553#[derive(Debug)]
554pub(crate) enum FuseError {
555 VirtqError(VirtqError),
556 IOError(Errno),
557}
558
559impl From<VirtqError> for FuseError {
560 fn from(value: VirtqError) -> Self {
561 Self::VirtqError(value)
562 }
563}
564
565impl From<FuseError> for Errno {
566 fn from(value: FuseError) -> Self {
567 match value {
568 FuseError::VirtqError(virtq_error) => virtq_error.into(),
569 FuseError::IOError(io_error) => io_error,
570 }
571 }
572}
573
574fn lookup(name: CString) -> Option<u64> {
575 let (cmd, rsp_payload_len) = ops::Lookup::create(name);
576 let rsp = get_filesystem_driver()
577 .unwrap()
578 .lock()
579 .send_command(cmd, rsp_payload_len)
580 .ok()?;
581 Some(rsp.headers.op_header.nodeid)
582}
583
584fn readlink(nid: u64) -> io::Result<String> {
585 let len = MAX_READ_LEN as u32;
586 let (cmd, rsp_payload_len) = ops::Readlink::create(nid, len);
587 let rsp = get_filesystem_driver()
588 .unwrap()
589 .lock()
590 .send_command(cmd, rsp_payload_len)?;
591 let len: usize = if rsp.headers.out_header.len as usize - mem::size_of::<fuse_out_header>()
592 >= usize::try_from(len).unwrap()
593 {
594 len.try_into().unwrap()
595 } else {
596 (rsp.headers.out_header.len as usize) - mem::size_of::<fuse_out_header>()
597 };
598
599 Ok(String::from_utf8(rsp.payload.unwrap()[..len].to_vec()).unwrap())
600}
601
602#[derive(Debug)]
603struct FuseFileHandleInner {
604 fuse_nid: Option<u64>,
605 fuse_fh: Option<u64>,
606 offset: usize,
607}
608
609impl FuseFileHandleInner {
610 pub fn new() -> Self {
611 Self {
612 fuse_nid: None,
613 fuse_fh: None,
614 offset: 0,
615 }
616 }
617
618 async fn poll(&self, events: PollEvent) -> io::Result<PollEvent> {
619 static KH: AtomicU64 = AtomicU64::new(0);
620 let kh = KH.fetch_add(1, Ordering::SeqCst);
621
622 future::poll_fn(|cx| {
623 if let (Some(nid), Some(fh)) = (self.fuse_nid, self.fuse_fh) {
624 let (cmd, rsp_payload_len) = ops::Poll::create(nid, fh, kh, events);
625 let rsp = get_filesystem_driver()
626 .ok_or(Errno::Nosys)?
627 .lock()
628 .send_command(cmd, rsp_payload_len)?;
629
630 if rsp.headers.out_header.error < 0 {
631 Poll::Ready(Err(Errno::Io))
632 } else {
633 let revents =
634 PollEvent::from_bits(i16::try_from(rsp.headers.op_header.revents).unwrap())
635 .unwrap();
636 if !revents.intersects(events)
637 && !revents.intersects(
638 PollEvent::POLLERR | PollEvent::POLLNVAL | PollEvent::POLLHUP,
639 ) {
640 cx.waker().wake_by_ref();
643 }
644 Poll::Ready(Ok(revents))
645 }
646 } else {
647 Poll::Ready(Ok(PollEvent::POLLERR))
648 }
649 })
650 .await
651 }
652
653 fn read(&mut self, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
654 let mut len = buf.len();
655 if len > MAX_READ_LEN {
656 debug!("Reading longer than max_read_len: {len}");
657 len = MAX_READ_LEN;
658 }
659 if let (Some(nid), Some(fh)) = (self.fuse_nid, self.fuse_fh) {
660 let (cmd, rsp_payload_len) =
661 ops::Read::create(nid, fh, len.try_into().unwrap(), self.offset as u64);
662 let rsp = get_filesystem_driver()
663 .ok_or(Errno::Nosys)?
664 .lock()
665 .send_command(cmd, rsp_payload_len)?;
666 let len: usize =
667 if (rsp.headers.out_header.len as usize) - mem::size_of::<fuse_out_header>() >= len
668 {
669 len
670 } else {
671 (rsp.headers.out_header.len as usize) - mem::size_of::<fuse_out_header>()
672 };
673 self.offset += len;
674
675 buf[..len].write_copy_of_slice(&rsp.payload.unwrap()[..len]);
676
677 Ok(len)
678 } else {
679 debug!("File not open, cannot read!");
680 Err(Errno::Noent)
681 }
682 }
683
684 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
685 debug!("FUSE write!");
686 let mut truncated_len = buf.len();
687 if truncated_len > MAX_WRITE_LEN {
688 debug!(
689 "Writing longer than max_write_len: {} > {}",
690 buf.len(),
691 MAX_WRITE_LEN
692 );
693 truncated_len = MAX_WRITE_LEN;
694 }
695 if let (Some(nid), Some(fh)) = (self.fuse_nid, self.fuse_fh) {
696 let truncated_buf = Box::<[u8]>::from(&buf[..truncated_len]);
697 let (cmd, rsp_payload_len) =
698 ops::Write::create(nid, fh, truncated_buf, self.offset as u64);
699 let rsp = get_filesystem_driver()
700 .ok_or(Errno::Nosys)?
701 .lock()
702 .send_command(cmd, rsp_payload_len)?;
703
704 if rsp.headers.out_header.error < 0 {
705 return Err(Errno::Io);
706 }
707
708 let rsp_size = rsp.headers.op_header.size;
709 let rsp_len: usize = if rsp_size > u32::try_from(truncated_len).unwrap() {
710 truncated_len
711 } else {
712 rsp_size.try_into().unwrap()
713 };
714 self.offset += rsp_len;
715 Ok(rsp_len)
716 } else {
717 warn!("File not open, cannot read!");
718 Err(Errno::Noent)
719 }
720 }
721
722 fn lseek(&mut self, offset: isize, whence: SeekWhence) -> io::Result<isize> {
723 debug!("FUSE lseek");
724
725 if let (Some(nid), Some(fh)) = (self.fuse_nid, self.fuse_fh) {
726 let (cmd, rsp_payload_len) = ops::Lseek::create(nid, fh, offset, whence);
727 let rsp = get_filesystem_driver()
728 .ok_or(Errno::Nosys)?
729 .lock()
730 .send_command(cmd, rsp_payload_len)?;
731
732 if rsp.headers.out_header.error < 0 {
733 return Err(Errno::Io);
734 }
735
736 let rsp_offset = rsp.headers.op_header.offset;
737 self.offset = rsp.headers.op_header.offset.try_into().unwrap();
738
739 Ok(rsp_offset.try_into().unwrap())
740 } else {
741 Err(Errno::Io)
742 }
743 }
744
745 fn fstat(&mut self) -> io::Result<FileAttr> {
746 debug!("FUSE getattr");
747 if let (Some(nid), Some(fh)) = (self.fuse_nid, self.fuse_fh) {
748 let (cmd, rsp_payload_len) = ops::Getattr::create(nid, fh, FUSE_GETATTR_FH);
749 let rsp = get_filesystem_driver()
750 .ok_or(Errno::Nosys)?
751 .lock()
752 .send_command(cmd, rsp_payload_len)?;
753 if rsp.headers.out_header.error < 0 {
754 return Err(Errno::Io);
755 }
756 Ok(rsp.headers.op_header.attr.into())
757 } else {
758 Err(Errno::Io)
759 }
760 }
761}
762
763impl Drop for FuseFileHandleInner {
764 fn drop(&mut self) {
765 if self.fuse_nid.is_some() && self.fuse_fh.is_some() {
766 let (cmd, rsp_payload_len) =
767 ops::Release::create(self.fuse_nid.unwrap(), self.fuse_fh.unwrap());
768 get_filesystem_driver()
769 .unwrap()
770 .lock()
771 .send_command(cmd, rsp_payload_len)
772 .unwrap();
773 }
774 }
775}
776
777#[derive(Debug)]
778struct FuseFileHandle(pub Arc<Mutex<FuseFileHandleInner>>);
779
780impl FuseFileHandle {
781 pub fn new() -> Self {
782 Self(Arc::new(Mutex::new(FuseFileHandleInner::new())))
783 }
784}
785
786#[async_trait]
787impl ObjectInterface for FuseFileHandle {
788 async fn poll(&self, event: PollEvent) -> io::Result<PollEvent> {
789 self.0.lock().await.poll(event).await
790 }
791
792 async fn read(&self, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
793 self.0.lock().await.read(buf)
794 }
795
796 async fn write(&self, buf: &[u8]) -> io::Result<usize> {
797 self.0.lock().await.write(buf)
798 }
799
800 async fn lseek(&self, offset: isize, whence: SeekWhence) -> io::Result<isize> {
801 self.0.lock().await.lseek(offset, whence)
802 }
803
804 async fn fstat(&self) -> io::Result<FileAttr> {
805 self.0.lock().await.fstat()
806 }
807}
808
809impl Clone for FuseFileHandle {
810 fn clone(&self) -> Self {
811 warn!("FuseFileHandle: clone not tested");
812 Self(self.0.clone())
813 }
814}
815
816#[derive(Debug)]
817pub struct FuseDirectoryHandle {
818 name: Option<String>,
819 read_position: Mutex<usize>,
820}
821
822impl FuseDirectoryHandle {
823 pub fn new(name: Option<String>) -> Self {
824 Self {
825 name,
826 read_position: Mutex::new(0),
827 }
828 }
829}
830
831#[async_trait]
832impl ObjectInterface for FuseDirectoryHandle {
833 async fn getdents(&self, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
834 let path: CString = if let Some(name) = &self.name {
835 CString::new("/".to_string() + name).unwrap()
836 } else {
837 CString::new("/".to_string()).unwrap()
838 };
839
840 debug!("FUSE opendir: {path:#?}");
841
842 let fuse_nid = lookup(path.clone()).ok_or(Errno::Noent)?;
843
844 let (mut cmd, rsp_payload_len) = ops::Open::create(fuse_nid, 0x10000);
847 cmd.headers.in_header.opcode = fuse_opcode::FUSE_OPENDIR as u32;
848 let rsp = get_filesystem_driver()
849 .ok_or(Errno::Nosys)?
850 .lock()
851 .send_command(cmd, rsp_payload_len)?;
852 let fuse_fh = rsp.headers.op_header.fh;
853
854 debug!("FUSE readdir: {path:#?}");
855
856 let len = MAX_READ_LEN as u32;
858 let rsp_offset: &mut usize = &mut *self.read_position.lock().await;
859 let mut buf_offset: usize = 0;
860
861 let (mut cmd, rsp_payload_len) = ops::Read::create(fuse_nid, fuse_fh, len, 0);
863 cmd.headers.in_header.opcode = fuse_opcode::FUSE_READDIR as u32;
864 let rsp = get_filesystem_driver()
865 .ok_or(Errno::Nosys)?
866 .lock()
867 .send_command(cmd, rsp_payload_len)?;
868
869 let len = usize::min(
870 MAX_READ_LEN,
871 rsp.headers.out_header.len as usize - mem::size_of::<fuse_out_header>(),
872 );
873
874 if len <= core::mem::size_of::<fuse_dirent>() {
875 debug!("FUSE no new dirs");
876 return Err(Errno::Noent);
877 }
878
879 let mut ret = 0;
880
881 while (rsp.headers.out_header.len as usize) - *rsp_offset > size_of::<fuse_dirent>() {
882 let dirent = unsafe {
883 &*rsp
884 .payload
885 .as_ref()
886 .unwrap()
887 .as_ptr()
888 .byte_add(*rsp_offset)
889 .cast::<fuse_dirent>()
890 };
891
892 let dirent_len = offset_of!(Dirent64, d_name) + dirent.namelen as usize + 1;
893 let next_dirent = (buf_offset + dirent_len).align_up(align_of::<Dirent64>());
894
895 if next_dirent > buf.len() {
896 break;
898 }
899
900 let target_dirent = buf[buf_offset].as_mut_ptr().cast::<Dirent64>();
902 unsafe {
903 target_dirent.write(Dirent64 {
904 d_ino: dirent.ino,
905 d_off: 0,
906 d_reclen: (dirent_len.align_up(align_of::<Dirent64>()))
907 .try_into()
908 .unwrap(),
909 d_type: (dirent.type_ as u8).try_into().unwrap(),
910 d_name: PhantomData {},
911 });
912 let nameptr = core::ptr::from_mut(&mut (*(target_dirent)).d_name).cast::<u8>();
913 core::ptr::copy_nonoverlapping(
914 dirent.name.as_ptr().cast::<u8>(),
915 nameptr,
916 dirent.namelen as usize,
917 );
918 nameptr.add(dirent.namelen as usize).write(0); }
920
921 *rsp_offset += core::mem::size_of::<fuse_dirent>() + dirent.namelen as usize;
922 *rsp_offset = ((*rsp_offset) + U64_SIZE - 1) & (!(U64_SIZE - 1));
924 buf_offset = next_dirent;
925 ret = buf_offset;
926 }
927
928 let (cmd, rsp_payload_len) = ops::Release::create(fuse_nid, fuse_fh);
929 get_filesystem_driver()
930 .unwrap()
931 .lock()
932 .send_command(cmd, rsp_payload_len)?;
933
934 Ok(ret)
935 }
936
937 async fn lseek(&self, offset: isize, whence: SeekWhence) -> io::Result<isize> {
942 if whence != SeekWhence::Set && offset != 0 {
943 error!("Invalid offset for directory lseek ({offset})");
944 return Err(Errno::Inval);
945 }
946 *self.read_position.lock().await = offset as usize;
947 Ok(offset)
948 }
949}
950
951#[derive(Debug)]
952pub(crate) struct FuseDirectory {
953 prefix: Option<String>,
954 attr: FileAttr,
955}
956
957impl FuseDirectory {
958 pub fn new(prefix: Option<String>) -> Self {
959 let microseconds = arch::kernel::systemtime::now_micros();
960 let t = timespec::from_usec(microseconds as i64);
961
962 FuseDirectory {
963 prefix,
964 attr: FileAttr {
965 st_mode: AccessPermission::from_bits(0o777).unwrap() | AccessPermission::S_IFDIR,
966 st_atim: t,
967 st_mtim: t,
968 st_ctim: t,
969 ..Default::default()
970 },
971 }
972 }
973
974 fn traversal_path(&self, components: &[&str]) -> CString {
975 let prefix_deref = self.prefix.as_deref();
976 let components_with_prefix = prefix_deref.iter().chain(components.iter().rev());
977 let path: String = components_with_prefix
978 .flat_map(|component| ["/", component])
979 .collect();
980 if path.is_empty() {
981 CString::new("/").unwrap()
982 } else {
983 CString::new(path).unwrap()
984 }
985 }
986}
987
988impl VfsNode for FuseDirectory {
989 fn get_kind(&self) -> NodeKind {
991 NodeKind::Directory
992 }
993
994 fn get_file_attributes(&self) -> io::Result<FileAttr> {
995 Ok(self.attr)
996 }
997
998 fn get_object(&self) -> io::Result<Arc<dyn ObjectInterface>> {
999 Ok(Arc::new(FuseDirectoryHandle::new(self.prefix.clone())))
1000 }
1001
1002 fn traverse_readdir(&self, components: &mut Vec<&str>) -> io::Result<Vec<DirectoryEntry>> {
1003 let path = self.traversal_path(components);
1004
1005 debug!("FUSE opendir: {path:#?}");
1006
1007 let fuse_nid = lookup(path.clone()).ok_or(Errno::Noent)?;
1008
1009 let (mut cmd, rsp_payload_len) = ops::Open::create(fuse_nid, 0x10000);
1012 cmd.headers.in_header.opcode = fuse_opcode::FUSE_OPENDIR as u32;
1013 let rsp = get_filesystem_driver()
1014 .ok_or(Errno::Nosys)?
1015 .lock()
1016 .send_command(cmd, rsp_payload_len)?;
1017 let fuse_fh = rsp.headers.op_header.fh;
1018
1019 debug!("FUSE readdir: {path:#?}");
1020
1021 let len = MAX_READ_LEN as u32;
1023 let mut offset: usize = 0;
1024
1025 let (mut cmd, rsp_payload_len) = ops::Read::create(fuse_nid, fuse_fh, len, 0);
1027 cmd.headers.in_header.opcode = fuse_opcode::FUSE_READDIR as u32;
1028 let rsp = get_filesystem_driver()
1029 .ok_or(Errno::Nosys)?
1030 .lock()
1031 .send_command(cmd, rsp_payload_len)?;
1032
1033 let len: usize = if rsp.headers.out_header.len as usize - mem::size_of::<fuse_out_header>()
1034 >= usize::try_from(len).unwrap()
1035 {
1036 len.try_into().unwrap()
1037 } else {
1038 (rsp.headers.out_header.len as usize) - mem::size_of::<fuse_out_header>()
1039 };
1040
1041 if len <= core::mem::size_of::<fuse_dirent>() {
1042 debug!("FUSE no new dirs");
1043 return Err(Errno::Noent);
1044 }
1045
1046 let mut entries: Vec<DirectoryEntry> = Vec::new();
1047 while (rsp.headers.out_header.len as usize) - offset > core::mem::size_of::<fuse_dirent>() {
1048 let dirent = unsafe {
1049 &*rsp
1050 .payload
1051 .as_ref()
1052 .unwrap()
1053 .as_ptr()
1054 .byte_add(offset)
1055 .cast::<fuse_dirent>()
1056 };
1057
1058 offset += core::mem::size_of::<fuse_dirent>() + dirent.namelen as usize;
1059 offset = ((offset) + U64_SIZE - 1) & (!(U64_SIZE - 1));
1061
1062 let name: &'static [u8] = unsafe {
1063 core::slice::from_raw_parts(
1064 dirent.name.as_ptr().cast(),
1065 dirent.namelen.try_into().unwrap(),
1066 )
1067 };
1068 entries.push(DirectoryEntry::new(unsafe {
1069 core::str::from_utf8_unchecked(name).to_string()
1070 }));
1071 }
1072
1073 let (cmd, rsp_payload_len) = ops::Release::create(fuse_nid, fuse_fh);
1074 get_filesystem_driver()
1075 .unwrap()
1076 .lock()
1077 .send_command(cmd, rsp_payload_len)?;
1078
1079 Ok(entries)
1080 }
1081
1082 fn traverse_stat(&self, components: &mut Vec<&str>) -> io::Result<FileAttr> {
1083 let path = self.traversal_path(components);
1084
1085 debug!("FUSE stat: {path:#?}");
1086
1087 let (cmd, rsp_payload_len) = ops::Lookup::create(path);
1089 let rsp = get_filesystem_driver()
1090 .unwrap()
1091 .lock()
1092 .send_command(cmd, rsp_payload_len)?;
1093
1094 if rsp.headers.out_header.error != 0 {
1095 return Err(Errno::try_from(-rsp.headers.out_header.error).unwrap());
1096 }
1097
1098 let entry_out = rsp.headers.op_header;
1099 let attr = entry_out.attr;
1100
1101 if attr.mode & S_IFMT != S_IFLNK {
1102 return Ok(FileAttr::from(attr));
1103 }
1104
1105 let path = readlink(entry_out.nodeid)?;
1106 let mut components: Vec<&str> = path.split('/').collect();
1107 self.traverse_stat(&mut components)
1108 }
1109
1110 fn traverse_lstat(&self, components: &mut Vec<&str>) -> io::Result<FileAttr> {
1111 let path = self.traversal_path(components);
1112
1113 debug!("FUSE lstat: {path:#?}");
1114
1115 let (cmd, rsp_payload_len) = ops::Lookup::create(path);
1116 let rsp = get_filesystem_driver()
1117 .unwrap()
1118 .lock()
1119 .send_command(cmd, rsp_payload_len)?;
1120 Ok(FileAttr::from(rsp.headers.op_header.attr))
1121 }
1122
1123 fn traverse_open(
1124 &self,
1125 components: &mut Vec<&str>,
1126 opt: OpenOption,
1127 mode: AccessPermission,
1128 ) -> io::Result<Arc<dyn ObjectInterface>> {
1129 let path = self.traversal_path(components);
1130
1131 debug!("FUSE open: {path:#?}, {opt:?} {mode:?}");
1132
1133 if opt.contains(OpenOption::O_DIRECTORY) {
1134 if opt.contains(OpenOption::O_CREAT) {
1135 warn!("O_DIRECTORY and O_CREAT are together invalid as open options.");
1137 return Err(Errno::Inval);
1138 }
1139
1140 let (cmd, rsp_payload_len) = ops::Lookup::create(path.clone());
1141 let rsp = get_filesystem_driver()
1142 .unwrap()
1143 .lock()
1144 .send_command(cmd, rsp_payload_len)?;
1145
1146 let attr = FileAttr::from(rsp.headers.op_header.attr);
1147 if attr.st_mode.contains(AccessPermission::S_IFDIR) {
1148 let mut path = path.into_string().unwrap();
1149 path.remove(0);
1150 Ok(Arc::new(FuseDirectoryHandle::new(Some(path))))
1151 } else {
1152 Err(Errno::Notdir)
1153 }
1154 } else {
1155 let file = FuseFileHandle::new();
1156
1157 let mut file_guard = block_on(async { Ok(file.0.lock().await) }, None)?;
1160
1161 if opt.contains(OpenOption::O_CREAT) {
1163 let (cmd, rsp_payload_len) =
1165 ops::Create::create(path, opt.bits().try_into().unwrap(), mode.bits());
1166 let rsp = get_filesystem_driver()
1167 .ok_or(Errno::Nosys)?
1168 .lock()
1169 .send_command(cmd, rsp_payload_len)?;
1170
1171 let inner = rsp.headers.op_header;
1172 file_guard.fuse_nid = Some(inner.entry.nodeid);
1173 file_guard.fuse_fh = Some(inner.open.fh);
1174 } else {
1175 file_guard.fuse_nid = lookup(path);
1177
1178 if file_guard.fuse_nid.is_none() {
1179 warn!("Fuse lookup seems to have failed!");
1180 return Err(Errno::Noent);
1181 }
1182
1183 let (cmd, rsp_payload_len) =
1185 ops::Open::create(file_guard.fuse_nid.unwrap(), opt.bits().try_into().unwrap());
1186 let rsp = get_filesystem_driver()
1187 .ok_or(Errno::Nosys)?
1188 .lock()
1189 .send_command(cmd, rsp_payload_len)?;
1190 file_guard.fuse_fh = Some(rsp.headers.op_header.fh);
1191 }
1192
1193 drop(file_guard);
1194
1195 Ok(Arc::new(file))
1196 }
1197 }
1198
1199 fn traverse_unlink(&self, components: &mut Vec<&str>) -> io::Result<()> {
1200 let path = self.traversal_path(components);
1201
1202 let (cmd, rsp_payload_len) = ops::Unlink::create(path);
1203 let rsp = get_filesystem_driver()
1204 .ok_or(Errno::Nosys)?
1205 .lock()
1206 .send_command(cmd, rsp_payload_len)?;
1207 trace!("unlink answer {rsp:?}");
1208
1209 Ok(())
1210 }
1211
1212 fn traverse_rmdir(&self, components: &mut Vec<&str>) -> io::Result<()> {
1213 let path = self.traversal_path(components);
1214
1215 let (cmd, rsp_payload_len) = ops::Rmdir::create(path);
1216 let rsp = get_filesystem_driver()
1217 .ok_or(Errno::Nosys)?
1218 .lock()
1219 .send_command(cmd, rsp_payload_len)?;
1220 trace!("rmdir answer {rsp:?}");
1221
1222 Ok(())
1223 }
1224
1225 fn traverse_mkdir(&self, components: &mut Vec<&str>, mode: AccessPermission) -> io::Result<()> {
1226 let path = self.traversal_path(components);
1227 let (cmd, rsp_payload_len) = ops::Mkdir::create(path, mode.bits());
1228
1229 let rsp = get_filesystem_driver()
1230 .ok_or(Errno::Nosys)?
1231 .lock()
1232 .send_command(cmd, rsp_payload_len)?;
1233 if rsp.headers.out_header.error == 0 {
1234 Ok(())
1235 } else {
1236 Err(Errno::try_from(-rsp.headers.out_header.error).unwrap())
1237 }
1238 }
1239}
1240
1241pub(crate) fn init() {
1242 debug!("Try to initialize fuse filesystem");
1243
1244 if let Some(driver) = get_filesystem_driver() {
1245 let (cmd, rsp_payload_len) = ops::Init::create();
1246 let rsp = driver.lock().send_command(cmd, rsp_payload_len).unwrap();
1247 trace!("fuse init answer: {rsp:?}");
1248
1249 let mount_point = driver.lock().get_mount_point();
1250 if mount_point == "/" {
1251 let fuse_nid = lookup(c"/".to_owned()).unwrap();
1252 let (mut cmd, rsp_payload_len) = ops::Open::create(fuse_nid, 0x10000);
1255 cmd.headers.in_header.opcode = fuse_opcode::FUSE_OPENDIR as u32;
1256 let rsp = get_filesystem_driver()
1257 .unwrap()
1258 .lock()
1259 .send_command(cmd, rsp_payload_len)
1260 .unwrap();
1261 let fuse_fh = rsp.headers.op_header.fh;
1262
1263 let len = MAX_READ_LEN as u32;
1265 let mut offset: usize = 0;
1266
1267 let (mut cmd, rsp_payload_len) = ops::Read::create(fuse_nid, fuse_fh, len, 0);
1269 cmd.headers.in_header.opcode = fuse_opcode::FUSE_READDIR as u32;
1270 let rsp = get_filesystem_driver()
1271 .unwrap()
1272 .lock()
1273 .send_command(cmd, rsp_payload_len)
1274 .unwrap();
1275
1276 let len: usize = if rsp.headers.out_header.len as usize
1277 - mem::size_of::<fuse_out_header>()
1278 >= usize::try_from(len).unwrap()
1279 {
1280 len.try_into().unwrap()
1281 } else {
1282 (rsp.headers.out_header.len as usize) - mem::size_of::<fuse_out_header>()
1283 };
1284
1285 assert!(
1286 len > core::mem::size_of::<fuse_dirent>(),
1287 "FUSE no new dirs"
1288 );
1289
1290 let mut entries: Vec<String> = Vec::new();
1291 while (rsp.headers.out_header.len as usize) - offset
1292 > core::mem::size_of::<fuse_dirent>()
1293 {
1294 let dirent = unsafe {
1295 &*rsp
1296 .payload
1297 .as_ref()
1298 .unwrap()
1299 .as_ptr()
1300 .byte_add(offset)
1301 .cast::<fuse_dirent>()
1302 };
1303
1304 offset += core::mem::size_of::<fuse_dirent>() + dirent.namelen as usize;
1305 offset = ((offset) + U64_SIZE - 1) & (!(U64_SIZE - 1));
1307
1308 let name: &'static [u8] = unsafe {
1309 core::slice::from_raw_parts(
1310 dirent.name.as_ptr().cast(),
1311 dirent.namelen.try_into().unwrap(),
1312 )
1313 };
1314 entries.push(unsafe { core::str::from_utf8_unchecked(name).to_string() });
1315 }
1316
1317 let (cmd, rsp_payload_len) = ops::Release::create(fuse_nid, fuse_fh);
1318 get_filesystem_driver()
1319 .unwrap()
1320 .lock()
1321 .send_command(cmd, rsp_payload_len)
1322 .unwrap();
1323
1324 entries.retain(|x| x != ".");
1326 entries.retain(|x| x != "..");
1327 entries.retain(|x| x != "tmp");
1328 entries.retain(|x| x != "proc");
1329 warn!(
1330 "Fuse don't mount the host directories 'tmp' and 'proc' into the guest file system!"
1331 );
1332
1333 for i in entries {
1334 let i_cstr = CString::new(i.clone()).unwrap();
1335 let (cmd, rsp_payload_len) = ops::Lookup::create(i_cstr);
1336 let rsp = get_filesystem_driver()
1337 .unwrap()
1338 .lock()
1339 .send_command(cmd, rsp_payload_len)
1340 .unwrap();
1341
1342 let attr = FileAttr::from(rsp.headers.op_header.attr);
1343 if attr.st_mode.contains(AccessPermission::S_IFDIR) {
1344 info!("Fuse mount {i} to /{i}");
1345 fs::FILESYSTEM
1346 .get()
1347 .unwrap()
1348 .mount(
1349 &("/".to_owned() + i.as_str()),
1350 Box::new(FuseDirectory::new(Some(i))),
1351 )
1352 .expect("Mount failed. Invalid mount_point?");
1353 } else {
1354 warn!("Fuse don't mount {i}. It isn't a directory!");
1355 }
1356 }
1357 } else {
1358 let mount_point = if mount_point.starts_with('/') {
1359 mount_point
1360 } else {
1361 "/".to_owned() + &mount_point
1362 };
1363
1364 info!("Mounting virtio-fs at {mount_point}");
1365 fs::FILESYSTEM
1366 .get()
1367 .unwrap()
1368 .mount(mount_point.as_str(), Box::new(FuseDirectory::new(None)))
1369 .expect("Mount failed. Invalid mount_point?");
1370 }
1371 }
1372}