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