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