1use std::sync::Arc;
4
5use arcbox_virtio_core::error::{Result, VirtioError};
6use arcbox_virtio_core::queue::VirtQueue;
7use arcbox_virtio_core::{QueueConfig, VirtioDevice, VirtioDeviceId, virtio_bindings};
8
9use crate::handler::FuseRequestHandler;
10use crate::request::FuseResponse;
11use crate::session::FuseSession;
12
13#[derive(Debug, Clone)]
15pub struct FsConfig {
16 pub tag: String,
18 pub num_queues: u32,
20 pub queue_size: u16,
22 pub shared_dir: String,
24}
25
26impl Default for FsConfig {
27 fn default() -> Self {
28 Self {
29 tag: "arcbox".to_string(),
30 num_queues: 1,
31 queue_size: 1024,
32 shared_dir: String::new(),
33 }
34 }
35}
36
37pub struct VirtioFs {
42 config: FsConfig,
43 features: u64,
44 acked_features: u64,
45 session: FuseSession,
47 handler: Option<Arc<dyn FuseRequestHandler>>,
49 request_queues: Vec<VirtQueue>,
51 activated: bool,
53 last_avail_idx_q1: u16,
55}
56
57impl VirtioFs {
58 pub const FEATURE_NOTIFICATION: u64 = 1 << 0;
60 pub const FEATURE_VERSION_1: u64 = 1 << virtio_bindings::virtio_config::VIRTIO_F_VERSION_1;
62
63 pub(crate) const FUSE_INIT: u32 = 26;
65
66 const FUSE_DESTROY: u32 = 38;
68
69 #[must_use]
71 pub fn new(config: FsConfig) -> Self {
72 Self {
73 config,
74 features: Self::FEATURE_VERSION_1,
75 acked_features: 0,
76 session: FuseSession::new(),
77 handler: None,
78 request_queues: Vec::new(),
79 activated: false,
80 last_avail_idx_q1: 0,
81 }
82 }
83
84 #[must_use]
86 pub fn with_handler(config: FsConfig, handler: Arc<dyn FuseRequestHandler>) -> Self {
87 Self {
88 config,
89 features: Self::FEATURE_VERSION_1,
90 acked_features: 0,
91 session: FuseSession::new(),
92 handler: Some(handler),
93 request_queues: Vec::new(),
94 activated: false,
95 last_avail_idx_q1: 0,
96 }
97 }
98
99 pub fn set_handler(&mut self, handler: Arc<dyn FuseRequestHandler>) {
101 self.handler = Some(handler);
102 }
103
104 #[must_use]
106 pub fn handler(&self) -> Option<&Arc<dyn FuseRequestHandler>> {
107 self.handler.as_ref()
108 }
109
110 #[must_use]
112 pub const fn session(&self) -> &FuseSession {
113 &self.session
114 }
115
116 #[must_use]
118 pub const fn is_activated(&self) -> bool {
119 self.activated
120 }
121
122 #[must_use]
124 pub fn tag(&self) -> &str {
125 &self.config.tag
126 }
127
128 #[must_use]
130 pub fn shared_dir(&self) -> &str {
131 &self.config.shared_dir
132 }
133
134 #[must_use]
136 pub const fn num_queues(&self) -> u32 {
137 self.config.num_queues
138 }
139
140 #[must_use]
142 pub const fn queue_size(&self) -> u16 {
143 self.config.queue_size
144 }
145
146 pub fn process_request(&mut self, request: &[u8]) -> Result<Vec<u8>> {
162 if request.len() < 40 {
163 return Err(VirtioError::DeviceError {
164 device: "fs".to_string(),
165 message: "FUSE request too small".to_string(),
166 });
167 }
168
169 let opcode = u32::from_le_bytes([request[4], request[5], request[6], request[7]]);
171
172 let unique = u64::from_le_bytes([
174 request[8],
175 request[9],
176 request[10],
177 request[11],
178 request[12],
179 request[13],
180 request[14],
181 request[15],
182 ]);
183
184 match opcode {
185 Self::FUSE_INIT => {
186 let response = self.session.handle_init(request)?;
187
188 if let Some(handler) = &self.handler {
189 handler.on_init(&self.session);
190 }
191
192 Ok(response)
193 }
194 Self::FUSE_DESTROY => {
195 self.session.reset();
196
197 if let Some(handler) = &self.handler {
198 handler.on_destroy();
199 }
200
201 Ok(FuseResponse::new(unique, vec![]).into_data())
202 }
203 _ => {
204 if !self.session.is_initialized() {
205 tracing::warn!("FUSE request before INIT: opcode={}", opcode);
206 return Ok(FuseResponse::error(unique, libc::EINVAL).into_data());
207 }
208
209 if let Some(handler) = &self.handler {
210 handler.handle_request(request)
211 } else {
212 Ok(FuseResponse::error(unique, libc::ENOSYS).into_data())
214 }
215 }
216 }
217 }
218
219 pub fn process_queue(
223 &mut self,
224 queue_index: usize,
225 memory: &mut [u8],
226 ) -> Result<Vec<(u16, u32)>> {
227 let pending_requests = {
230 let queue = self.request_queues.get_mut(queue_index).ok_or_else(|| {
231 VirtioError::NotReady(format!("request queue {queue_index} not available"))
232 })?;
233
234 let mut requests = Vec::new();
235 while let Some((head_idx, chain)) = queue.pop_avail() {
236 let mut request_data = Vec::new();
237 let mut write_buffers = Vec::new();
238
239 for desc in chain {
240 let start = desc.addr as usize;
241 let end = start + desc.len as usize;
242 if end > memory.len() {
243 return Err(VirtioError::InvalidQueue(
244 "descriptor out of bounds".to_string(),
245 ));
246 }
247
248 if desc.is_write_only() {
249 write_buffers.push((start, desc.len as usize));
250 } else {
251 request_data.extend_from_slice(&memory[start..end]);
252 }
253 }
254
255 if write_buffers.is_empty() {
256 return Err(VirtioError::InvalidQueue(
257 "no writable descriptors for response".to_string(),
258 ));
259 }
260
261 requests.push((head_idx, request_data, write_buffers));
262 }
263 requests
264 };
265
266 type ResponseItem = (
273 u16,
274 std::result::Result<Vec<u8>, VirtioError>,
275 Vec<(usize, usize)>,
276 );
277
278 let handler = self.handler.clone();
279 let session_initialized = self.session.is_initialized();
280
281 let has_control = pending_requests.iter().any(|(_, data, _)| {
287 if data.len() >= 8 {
288 let op = u32::from_le_bytes([data[4], data[5], data[6], data[7]]);
289 op == Self::FUSE_INIT || op == Self::FUSE_DESTROY
290 } else {
291 true }
293 });
294 let all_valid = pending_requests.iter().all(|(_, data, _)| data.len() >= 40);
295 let can_parallel = !has_control
296 && all_valid
297 && pending_requests.len() > 1
298 && session_initialized
299 && handler.is_some();
300
301 let responses: Vec<ResponseItem> = if can_parallel {
302 let handler_ref = handler.as_ref().unwrap();
304 use rayon::prelude::*;
305 pending_requests
306 .into_par_iter()
307 .map(|(head_idx, data, bufs)| {
308 let response = handler_ref.handle_request(&data);
309 (head_idx, response, bufs)
310 })
311 .collect()
312 } else {
313 pending_requests
315 .into_iter()
316 .map(|(head_idx, request_data, write_buffers)| {
317 let response = self.process_request(&request_data);
318 (head_idx, response, write_buffers)
319 })
320 .collect()
321 };
322
323 let mut completions = Vec::with_capacity(responses.len());
326 for (head_idx, response_result, write_buffers) in responses {
327 let response = response_result?;
328 let mut remaining = response.as_slice();
329 let mut written = 0usize;
330
331 for (start, len) in write_buffers {
332 if remaining.is_empty() {
333 break;
334 }
335
336 let copy_len = len.min(remaining.len());
337 memory[start..start + copy_len].copy_from_slice(&remaining[..copy_len]);
338 remaining = &remaining[copy_len..];
339 written += copy_len;
340 }
341
342 if !remaining.is_empty() {
343 return Err(VirtioError::InvalidQueue(
344 "response buffer too small".to_string(),
345 ));
346 }
347
348 completions.push((head_idx, written as u32));
349 }
350
351 Ok(completions)
352 }
353}
354
355impl VirtioDevice for VirtioFs {
356 fn device_id(&self) -> VirtioDeviceId {
357 VirtioDeviceId::Fs
358 }
359
360 fn features(&self) -> u64 {
361 self.features
362 }
363
364 fn ack_features(&mut self, features: u64) {
365 self.acked_features = self.features & features;
366 }
367
368 fn read_config(&self, offset: u64, data: &mut [u8]) {
369 let mut config_data = vec![0u8; 40];
373
374 let tag_bytes = self.config.tag.as_bytes();
375 let tag_len = tag_bytes.len().min(36);
376 config_data[..tag_len].copy_from_slice(&tag_bytes[..tag_len]);
377
378 config_data[36..40].copy_from_slice(&self.config.num_queues.to_le_bytes());
379
380 let offset = offset as usize;
381 let len = data.len().min(config_data.len().saturating_sub(offset));
382 if len > 0 {
383 data[..len].copy_from_slice(&config_data[offset..offset + len]);
384 }
385 }
386
387 fn write_config(&mut self, _offset: u64, _data: &[u8]) {
388 }
390
391 fn activate(&mut self) -> Result<()> {
392 if self.activated {
393 return Ok(());
394 }
395
396 if self.config.shared_dir.is_empty() {
397 return Err(VirtioError::DeviceError {
398 device: "fs".to_string(),
399 message: "shared_dir not configured".to_string(),
400 });
401 }
402
403 if self.config.num_queues == 0 {
404 return Err(VirtioError::DeviceError {
405 device: "fs".to_string(),
406 message: "num_queues must be greater than 0".to_string(),
407 });
408 }
409
410 self.session.reset();
411
412 let event_idx = (self.acked_features & arcbox_virtio_core::queue::VIRTIO_F_EVENT_IDX) != 0;
413 let mut queues = Vec::with_capacity(self.config.num_queues as usize);
414 for _ in 0..self.config.num_queues {
415 let mut q = VirtQueue::new(self.config.queue_size)?;
416 q.set_event_idx(event_idx);
417 queues.push(q);
418 }
419 self.request_queues = queues;
420
421 self.activated = true;
424
425 tracing::info!(
426 "VirtIO-FS device activated: tag='{}', shared_dir='{}', queues={}",
427 self.config.tag,
428 self.config.shared_dir,
429 self.config.num_queues
430 );
431
432 Ok(())
433 }
434
435 fn reset(&mut self) {
436 self.acked_features = 0;
437 self.session.reset();
438 self.activated = false;
439 self.request_queues.clear();
440
441 if let Some(handler) = &self.handler {
442 handler.on_destroy();
443 }
444
445 tracing::debug!("VirtIO-FS device reset: tag='{}'", self.config.tag);
446 }
447
448 fn process_queue(
449 &mut self,
450 queue_idx: u16,
451 memory: &mut [u8],
452 queue_config: &QueueConfig,
453 ) -> Result<Vec<(u16, u32)>> {
454 if queue_idx == 0 {
456 return Ok(Vec::new());
457 }
458
459 if !queue_config.ready || queue_config.size == 0 {
460 return Ok(Vec::new());
461 }
462
463 let gpa_base = queue_config.gpa_base as usize;
467 let desc_addr = (queue_config.desc_addr as usize)
468 .checked_sub(gpa_base)
469 .ok_or_else(|| {
470 tracing::warn!(
471 "invalid desc GPA {:#x} below ram base {:#x}",
472 queue_config.desc_addr,
473 gpa_base
474 );
475 VirtioError::InvalidQueue("desc GPA below ram base".into())
476 })?;
477 let avail_addr = (queue_config.avail_addr as usize)
478 .checked_sub(gpa_base)
479 .ok_or_else(|| {
480 tracing::warn!(
481 "invalid avail GPA {:#x} below ram base {:#x}",
482 queue_config.avail_addr,
483 gpa_base
484 );
485 VirtioError::InvalidQueue("avail GPA below ram base".into())
486 })?;
487 let used_addr = (queue_config.used_addr as usize)
488 .checked_sub(gpa_base)
489 .ok_or_else(|| {
490 tracing::warn!(
491 "invalid used GPA {:#x} below ram base {:#x}",
492 queue_config.used_addr,
493 gpa_base
494 );
495 VirtioError::InvalidQueue("used GPA below ram base".into())
496 })?;
497 let q_size = queue_config.size as usize;
498
499 if avail_addr + 4 > memory.len() {
500 return Ok(Vec::new());
501 }
502 let avail_idx = u16::from_le_bytes([memory[avail_addr + 2], memory[avail_addr + 3]]);
503
504 let mut current_avail = self.last_avail_idx_q1;
506 let mut completions = Vec::new();
507
508 while current_avail != avail_idx {
509 let ring_off = avail_addr + 4 + 2 * (current_avail as usize % q_size);
510 if ring_off + 2 > memory.len() {
511 break;
512 }
513 let head_idx = u16::from_le_bytes([memory[ring_off], memory[ring_off + 1]]) as usize;
514
515 let mut request_data = Vec::new();
518 let mut write_bufs: Vec<(usize, usize)> = Vec::new();
519 let mut idx = head_idx;
520 for _ in 0..q_size {
521 let d_off = desc_addr + idx * 16;
522 if d_off + 16 > memory.len() {
523 break;
524 }
525 let addr = match (u64::from_le_bytes(memory[d_off..d_off + 8].try_into().unwrap())
526 as usize)
527 .checked_sub(gpa_base)
528 {
529 Some(a) => a,
530 None => continue,
531 };
532 let len =
533 u32::from_le_bytes(memory[d_off + 8..d_off + 12].try_into().unwrap()) as usize;
534 let flags = u16::from_le_bytes(memory[d_off + 12..d_off + 14].try_into().unwrap());
535 let next = u16::from_le_bytes(memory[d_off + 14..d_off + 16].try_into().unwrap());
536
537 if flags & arcbox_virtio_core::queue::flags::WRITE != 0 {
538 write_bufs.push((addr, len));
539 } else if addr + len <= memory.len() {
540 request_data.extend_from_slice(&memory[addr..addr + len]);
541 }
542
543 if flags & arcbox_virtio_core::queue::flags::NEXT == 0 {
544 break;
545 }
546 idx = next as usize;
547 }
548
549 let response = match self.process_request(&request_data) {
550 Ok(resp) => resp,
551 Err(e) => {
552 tracing::warn!("VirtioFS FUSE request error: {e}");
553 let unique = if request_data.len() >= 16 {
554 u64::from_le_bytes(request_data[8..16].try_into().unwrap())
555 } else {
556 0
557 };
558 FuseResponse::error(unique, libc::EIO).into_data()
559 }
560 };
561
562 let mut resp_offset = 0;
564 for &(buf_addr, buf_len) in &write_bufs {
565 let remaining = response.len() - resp_offset;
566 if remaining == 0 {
567 break;
568 }
569 let to_write = remaining.min(buf_len);
570 if buf_addr + to_write <= memory.len() {
571 memory[buf_addr..buf_addr + to_write]
572 .copy_from_slice(&response[resp_offset..resp_offset + to_write]);
573 }
574 resp_offset += to_write;
575 }
576
577 let used_idx_off = used_addr + 2;
579 let used_idx = u16::from_le_bytes([memory[used_idx_off], memory[used_idx_off + 1]]);
580 let used_entry = used_addr + 4 + ((used_idx as usize) % q_size) * 8;
581 if used_entry + 8 <= memory.len() {
582 memory[used_entry..used_entry + 4]
583 .copy_from_slice(&(head_idx as u32).to_le_bytes());
584 memory[used_entry + 4..used_entry + 8]
585 .copy_from_slice(&(response.len() as u32).to_le_bytes());
586 std::sync::atomic::fence(std::sync::atomic::Ordering::Release);
587 let new_used = used_idx.wrapping_add(1);
588 memory[used_idx_off..used_idx_off + 2].copy_from_slice(&new_used.to_le_bytes());
589 }
590
591 if (self.acked_features & arcbox_virtio_core::queue::VIRTIO_F_EVENT_IDX) != 0 {
593 let avail_event_off = used_addr + 4 + 8 * q_size;
594 if avail_event_off + 2 <= memory.len() {
595 let ae = current_avail.wrapping_add(1).to_le_bytes();
596 memory[avail_event_off] = ae[0];
597 memory[avail_event_off + 1] = ae[1];
598 }
599 }
600
601 completions.push((head_idx as u16, response.len() as u32));
602 current_avail = current_avail.wrapping_add(1);
603 }
604
605 self.last_avail_idx_q1 = current_avail;
606 Ok(completions)
607 }
608}
609
610#[cfg(test)]
611mod tests {
612 use super::*;
613 use crate::protocol::{
614 FUSE_ASYNC_READ, FUSE_BIG_WRITES, FUSE_KERNEL_MINOR_VERSION, FUSE_KERNEL_VERSION,
615 };
616 use arcbox_virtio_core::queue::flags;
617 use std::sync::atomic::{AtomicUsize, Ordering};
618
619 #[test]
620 fn test_fs_config_default() {
621 let config = FsConfig::default();
622 assert_eq!(config.tag, "arcbox");
623 assert_eq!(config.num_queues, 1);
624 assert_eq!(config.queue_size, 1024);
625 assert!(config.shared_dir.is_empty());
626 }
627
628 #[test]
629 fn test_fs_config_custom() {
630 let config = FsConfig {
631 tag: "myfs".to_string(),
632 num_queues: 4,
633 queue_size: 256,
634 shared_dir: "/home/user/shared".to_string(),
635 };
636 assert_eq!(config.tag, "myfs");
637 assert_eq!(config.num_queues, 4);
638 assert_eq!(config.queue_size, 256);
639 assert_eq!(config.shared_dir, "/home/user/shared");
640 }
641
642 #[test]
643 fn test_fs_config_clone() {
644 let config = FsConfig {
645 tag: "test".to_string(),
646 num_queues: 2,
647 queue_size: 512,
648 shared_dir: "/tmp".to_string(),
649 };
650 let cloned = config.clone();
651 assert_eq!(cloned.tag, "test");
652 assert_eq!(cloned.num_queues, 2);
653 }
654
655 #[test]
656 fn test_fs_new() {
657 let fs = VirtioFs::new(FsConfig::default());
658 assert_eq!(fs.tag(), "arcbox");
659 assert!(fs.shared_dir().is_empty());
660 }
661
662 #[test]
663 fn test_fs_device_id() {
664 let fs = VirtioFs::new(FsConfig::default());
665 assert_eq!(fs.device_id(), VirtioDeviceId::Fs);
666 }
667
668 #[test]
669 fn test_fs_features() {
670 let fs = VirtioFs::new(FsConfig::default());
671 assert_ne!(
672 fs.features() & (1 << virtio_bindings::virtio_config::VIRTIO_F_VERSION_1),
673 0
674 );
675 }
676
677 #[test]
678 fn test_fs_ack_features() {
679 let mut fs = VirtioFs::new(FsConfig::default());
680 fs.ack_features(VirtioFs::FEATURE_NOTIFICATION);
681 assert_eq!(fs.acked_features & VirtioFs::FEATURE_NOTIFICATION, 0);
682 }
683
684 #[test]
685 fn test_fs_read_config_tag() {
686 let config = FsConfig {
687 tag: "testfs".to_string(),
688 ..Default::default()
689 };
690 let fs = VirtioFs::new(config);
691
692 let mut data = [0u8; 36];
693 fs.read_config(0, &mut data);
694
695 assert_eq!(&data[0..6], b"testfs");
696 assert!(data[6..].iter().all(|&b| b == 0));
697 }
698
699 #[test]
700 fn test_fs_read_config_tag_long() {
701 let config = FsConfig {
702 tag: "a".repeat(50),
703 ..Default::default()
704 };
705 let fs = VirtioFs::new(config);
706
707 let mut data = [0u8; 36];
708 fs.read_config(0, &mut data);
709
710 assert!(data.iter().all(|&b| b == b'a'));
711 }
712
713 #[test]
714 fn test_fs_read_config_num_queues() {
715 let config = FsConfig {
716 num_queues: 4,
717 ..Default::default()
718 };
719 let fs = VirtioFs::new(config);
720
721 let mut data = [0u8; 4];
722 fs.read_config(36, &mut data);
723
724 let num_queues = u32::from_le_bytes(data);
725 assert_eq!(num_queues, 4);
726 }
727
728 #[test]
729 fn test_fs_read_config_partial() {
730 let fs = VirtioFs::new(FsConfig::default());
731
732 let mut data = [0u8; 10];
733 fs.read_config(35, &mut data);
734 }
735
736 #[test]
737 fn test_fs_read_config_beyond() {
738 let fs = VirtioFs::new(FsConfig::default());
739
740 let mut data = [0xFFu8; 4];
741 fs.read_config(100, &mut data);
742 }
743
744 #[test]
745 fn test_fs_write_config_noop() {
746 let config = FsConfig {
747 tag: "original".to_string(),
748 ..Default::default()
749 };
750 let mut fs = VirtioFs::new(config);
751
752 fs.write_config(0, b"newvalue");
753
754 assert_eq!(fs.tag(), "original");
755 }
756
757 #[test]
758 fn test_fs_activate() {
759 let config = FsConfig {
760 shared_dir: "/tmp".to_string(),
761 ..Default::default()
762 };
763 let mut fs = VirtioFs::new(config);
764 assert!(!fs.is_activated());
765
766 assert!(fs.activate().is_ok());
767 assert!(fs.is_activated());
768 assert_eq!(fs.request_queues.len(), 1);
769
770 assert!(fs.activate().is_ok());
772 }
773
774 #[test]
775 fn test_fs_activate_no_shared_dir() {
776 let mut fs = VirtioFs::new(FsConfig::default());
777 assert!(fs.activate().is_err());
778 }
779
780 #[test]
781 fn test_fs_reset() {
782 let config = FsConfig {
783 shared_dir: "/tmp".to_string(),
784 ..Default::default()
785 };
786 let mut fs = VirtioFs::new(config);
787 fs.acked_features = 0xFF;
788 fs.activate().unwrap();
789
790 fs.reset();
791
792 assert_eq!(fs.acked_features, 0);
793 assert!(!fs.is_activated());
794 assert!(!fs.session.is_initialized());
795 assert!(fs.request_queues.is_empty());
796 }
797
798 #[test]
799 fn test_fs_tag_accessor() {
800 let config = FsConfig {
801 tag: "mytag".to_string(),
802 ..Default::default()
803 };
804 let fs = VirtioFs::new(config);
805 assert_eq!(fs.tag(), "mytag");
806 }
807
808 #[test]
809 fn test_fs_shared_dir_accessor() {
810 let config = FsConfig {
811 shared_dir: "/mnt/share".to_string(),
812 ..Default::default()
813 };
814 let fs = VirtioFs::new(config);
815 assert_eq!(fs.shared_dir(), "/mnt/share");
816 }
817
818 #[test]
819 fn test_fs_feature_constants() {
820 assert_eq!(VirtioFs::FEATURE_NOTIFICATION, 1 << 0);
821 }
822
823 #[test]
824 fn test_fs_activate_creates_multiple_queues() {
825 let config = FsConfig {
826 shared_dir: "/tmp".to_string(),
827 num_queues: 2,
828 queue_size: 16,
829 ..Default::default()
830 };
831 let mut fs = VirtioFs::new(config);
832 fs.activate().unwrap();
833 assert_eq!(fs.request_queues.len(), 2);
834 }
835
836 #[test]
837 fn test_fs_process_queue_roundtrip() {
838 struct TestHandler {
839 calls: Arc<AtomicUsize>,
840 }
841
842 impl FuseRequestHandler for TestHandler {
843 fn handle_request(&self, request: &[u8]) -> Result<Vec<u8>> {
844 self.calls.fetch_add(1, Ordering::SeqCst);
845 let unique = u64::from_le_bytes([
846 request[8],
847 request[9],
848 request[10],
849 request[11],
850 request[12],
851 request[13],
852 request[14],
853 request[15],
854 ]);
855 Ok(FuseResponse::new(unique, b"ok".to_vec()).into_data())
856 }
857 }
858
859 let calls = Arc::new(AtomicUsize::new(0));
860 let handler = Arc::new(TestHandler {
861 calls: Arc::clone(&calls),
862 });
863
864 let config = FsConfig {
865 shared_dir: "/tmp".to_string(),
866 queue_size: 16,
867 ..Default::default()
868 };
869 let mut fs = VirtioFs::with_handler(config, handler);
870 fs.activate().unwrap();
871
872 let queue = fs.request_queues.get_mut(0).unwrap();
873 let mut memory = vec![0u8; 1024];
874
875 let mut init_req = vec![0u8; 56];
877 init_req[0..4].copy_from_slice(&56u32.to_le_bytes());
878 init_req[4..8].copy_from_slice(&VirtioFs::FUSE_INIT.to_le_bytes());
879 init_req[8..16].copy_from_slice(&1u64.to_le_bytes());
880 init_req[40..44].copy_from_slice(&FUSE_KERNEL_VERSION.to_le_bytes());
881 init_req[44..48].copy_from_slice(&FUSE_KERNEL_MINOR_VERSION.to_le_bytes());
882 init_req[48..52].copy_from_slice(&(64 * 1024u32).to_le_bytes());
883 init_req[52..56].copy_from_slice(&(FUSE_ASYNC_READ | FUSE_BIG_WRITES).to_le_bytes());
884
885 let init_req_offset = 0usize;
886 let init_resp_offset = 128usize;
887 memory[init_req_offset..init_req_offset + init_req.len()].copy_from_slice(&init_req);
888
889 queue
890 .set_descriptor(
891 0,
892 arcbox_virtio_core::queue::Descriptor {
893 addr: init_req_offset as u64,
894 len: init_req.len() as u32,
895 flags: flags::NEXT,
896 next: 1,
897 },
898 )
899 .unwrap();
900 queue
901 .set_descriptor(
902 1,
903 arcbox_virtio_core::queue::Descriptor {
904 addr: init_resp_offset as u64,
905 len: 80,
906 flags: flags::WRITE,
907 next: 0,
908 },
909 )
910 .unwrap();
911
912 let mut other_req = vec![0u8; 40];
914 other_req[0..4].copy_from_slice(&40u32.to_le_bytes());
915 other_req[4..8].copy_from_slice(&1u32.to_le_bytes());
916 other_req[8..16].copy_from_slice(&2u64.to_le_bytes());
917
918 let other_req_offset = 256usize;
919 let other_resp_offset = 512usize;
920 memory[other_req_offset..other_req_offset + other_req.len()].copy_from_slice(&other_req);
921
922 queue
923 .set_descriptor(
924 2,
925 arcbox_virtio_core::queue::Descriptor {
926 addr: other_req_offset as u64,
927 len: other_req.len() as u32,
928 flags: flags::NEXT,
929 next: 3,
930 },
931 )
932 .unwrap();
933 queue
934 .set_descriptor(
935 3,
936 arcbox_virtio_core::queue::Descriptor {
937 addr: other_resp_offset as u64,
938 len: 32,
939 flags: flags::WRITE,
940 next: 0,
941 },
942 )
943 .unwrap();
944
945 queue.add_avail(0).unwrap();
946 queue.add_avail(2).unwrap();
947
948 let completions = fs.process_queue(0, &mut memory).unwrap();
949 assert_eq!(completions.len(), 2);
950 assert!(fs.session.is_initialized());
951 assert_eq!(calls.load(Ordering::SeqCst), 1);
952
953 let response = &memory[other_resp_offset..other_resp_offset + 18];
954 assert_eq!(&response[16..18], b"ok");
955 }
956
957 #[test]
958 fn test_fs_process_request_too_small() {
959 let config = FsConfig {
960 shared_dir: "/tmp".to_string(),
961 ..Default::default()
962 };
963 let mut fs = VirtioFs::new(config);
964 fs.activate().unwrap();
965
966 let result = fs.process_request(&[0u8; 20]);
967 assert!(result.is_err());
968 }
969
970 #[test]
971 fn test_fs_process_request_before_init() {
972 let config = FsConfig {
973 shared_dir: "/tmp".to_string(),
974 ..Default::default()
975 };
976 let mut fs = VirtioFs::new(config);
977 fs.activate().unwrap();
978
979 let mut request = vec![0u8; 40];
981 request[4..8].copy_from_slice(&1u32.to_le_bytes()); request[8..16].copy_from_slice(&1u64.to_le_bytes()); let response = fs.process_request(&request).unwrap();
985
986 let error = i32::from_le_bytes([response[4], response[5], response[6], response[7]]);
987 assert_eq!(error, -libc::EINVAL);
988 }
989
990 #[test]
991 fn test_fs_process_request_no_handler() {
992 let config = FsConfig {
993 shared_dir: "/tmp".to_string(),
994 ..Default::default()
995 };
996 let mut fs = VirtioFs::new(config);
997 fs.activate().unwrap();
998
999 let mut init_request = vec![0u8; 56];
1001 init_request[4..8].copy_from_slice(&26u32.to_le_bytes()); init_request[8..16].copy_from_slice(&1u64.to_le_bytes());
1003 init_request[40..44].copy_from_slice(&FUSE_KERNEL_VERSION.to_le_bytes());
1004 init_request[44..48].copy_from_slice(&FUSE_KERNEL_MINOR_VERSION.to_le_bytes());
1005 init_request[48..52].copy_from_slice(&(64 * 1024u32).to_le_bytes());
1006 init_request[52..56].copy_from_slice(&0u32.to_le_bytes());
1007
1008 fs.process_request(&init_request).unwrap();
1009 assert!(fs.session.is_initialized());
1010
1011 let mut request = vec![0u8; 40];
1013 request[4..8].copy_from_slice(&1u32.to_le_bytes()); request[8..16].copy_from_slice(&2u64.to_le_bytes()); let response = fs.process_request(&request).unwrap();
1017
1018 let error = i32::from_le_bytes([response[4], response[5], response[6], response[7]]);
1019 assert_eq!(error, -libc::ENOSYS);
1020 }
1021
1022 #[test]
1023 fn test_fs_with_handler() {
1024 use std::sync::atomic::AtomicU32;
1025
1026 struct TestHandler {
1027 call_count: AtomicU32,
1028 }
1029
1030 impl FuseRequestHandler for TestHandler {
1031 fn handle_request(&self, _request: &[u8]) -> Result<Vec<u8>> {
1032 self.call_count.fetch_add(1, Ordering::SeqCst);
1033 Ok(FuseResponse::new(0, vec![]).into_data())
1034 }
1035 }
1036
1037 let handler = Arc::new(TestHandler {
1038 call_count: AtomicU32::new(0),
1039 });
1040
1041 let config = FsConfig {
1042 shared_dir: "/tmp".to_string(),
1043 ..Default::default()
1044 };
1045 let mut fs = VirtioFs::with_handler(config, handler.clone());
1046 fs.activate().unwrap();
1047
1048 let mut init_request = vec![0u8; 56];
1050 init_request[4..8].copy_from_slice(&26u32.to_le_bytes());
1051 init_request[8..16].copy_from_slice(&1u64.to_le_bytes());
1052 init_request[40..44].copy_from_slice(&FUSE_KERNEL_VERSION.to_le_bytes());
1053 init_request[44..48].copy_from_slice(&FUSE_KERNEL_MINOR_VERSION.to_le_bytes());
1054 init_request[48..52].copy_from_slice(&(64 * 1024u32).to_le_bytes());
1055 init_request[52..56].copy_from_slice(&0u32.to_le_bytes());
1056
1057 fs.process_request(&init_request).unwrap();
1058
1059 let mut request = vec![0u8; 40];
1061 request[4..8].copy_from_slice(&1u32.to_le_bytes());
1062 request[8..16].copy_from_slice(&2u64.to_le_bytes());
1063
1064 fs.process_request(&request).unwrap();
1065
1066 assert_eq!(handler.call_count.load(Ordering::SeqCst), 1);
1067 }
1068}