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