1use std::sync::atomic::{AtomicBool, Ordering};
4
5use arcbox_virtio_core::error::{Result, VirtioError};
6
7use crate::protocol::{
8 DEFAULT_MAX_PAGES, DEFAULT_MAX_READAHEAD, DEFAULT_MAX_WRITE, FUSE_ASYNC_READ, FUSE_BIG_WRITES,
9 FUSE_KERNEL_MINOR_VERSION, FUSE_KERNEL_VERSION, FUSE_MAP_ALIGNMENT, FUSE_WRITEBACK_CACHE,
10};
11
12#[derive(Debug)]
17pub struct FuseSession {
18 initialized: AtomicBool,
20 major: u32,
22 minor: u32,
24 flags: u32,
26 max_readahead: u32,
28 max_write: u32,
30 max_pages: u16,
32 dax_enabled: bool,
34}
35
36impl FuseSession {
37 #[must_use]
39 pub const fn new() -> Self {
40 Self {
41 initialized: AtomicBool::new(false),
42 major: FUSE_KERNEL_VERSION,
43 minor: FUSE_KERNEL_MINOR_VERSION,
44 flags: FUSE_ASYNC_READ | FUSE_BIG_WRITES | FUSE_WRITEBACK_CACHE | FUSE_MAP_ALIGNMENT,
45 max_readahead: DEFAULT_MAX_READAHEAD,
46 max_write: DEFAULT_MAX_WRITE,
47 max_pages: DEFAULT_MAX_PAGES,
48 dax_enabled: false,
49 }
50 }
51
52 #[must_use]
54 pub fn is_initialized(&self) -> bool {
55 self.initialized.load(Ordering::Acquire)
56 }
57
58 #[must_use]
60 pub const fn major(&self) -> u32 {
61 self.major
62 }
63
64 #[must_use]
66 pub const fn minor(&self) -> u32 {
67 self.minor
68 }
69
70 #[must_use]
72 pub const fn flags(&self) -> u32 {
73 self.flags
74 }
75
76 #[must_use]
78 pub const fn max_readahead(&self) -> u32 {
79 self.max_readahead
80 }
81
82 #[must_use]
84 pub const fn max_write(&self) -> u32 {
85 self.max_write
86 }
87
88 #[must_use]
90 pub const fn max_pages(&self) -> u16 {
91 self.max_pages
92 }
93
94 pub fn handle_init(&mut self, request: &[u8]) -> Result<Vec<u8>> {
98 if request.len() < 56 {
101 return Err(VirtioError::DeviceError {
102 device: "fs".to_string(),
103 message: "FUSE_INIT request too small".to_string(),
104 });
105 }
106
107 let body = &request[40..];
108 let guest_major = u32::from_le_bytes([body[0], body[1], body[2], body[3]]);
109 let guest_minor = u32::from_le_bytes([body[4], body[5], body[6], body[7]]);
110 let guest_max_readahead = u32::from_le_bytes([body[8], body[9], body[10], body[11]]);
111 let guest_flags = u32::from_le_bytes([body[12], body[13], body[14], body[15]]);
112
113 tracing::debug!(
114 "FUSE_INIT: guest version {}.{}, max_readahead={}, flags={:#x}",
115 guest_major,
116 guest_minor,
117 guest_max_readahead,
118 guest_flags
119 );
120
121 if guest_major < FUSE_KERNEL_VERSION {
122 return Err(VirtioError::DeviceError {
123 device: "fs".to_string(),
124 message: format!(
125 "FUSE version mismatch: guest {guest_major}.{guest_minor} < required {FUSE_KERNEL_VERSION}.{FUSE_KERNEL_MINOR_VERSION}"
126 ),
127 });
128 }
129
130 self.major = FUSE_KERNEL_VERSION;
132 self.minor = FUSE_KERNEL_MINOR_VERSION;
133 self.max_readahead = guest_max_readahead.min(DEFAULT_MAX_READAHEAD);
134 self.flags &= guest_flags; self.dax_enabled = self.flags & FUSE_MAP_ALIGNMENT != 0;
136 if self.dax_enabled {
137 tracing::info!("FUSE DAX negotiated");
138 }
139
140 let mut response = Vec::with_capacity(80);
145
146 let unique = u64::from_le_bytes([
147 request[8],
148 request[9],
149 request[10],
150 request[11],
151 request[12],
152 request[13],
153 request[14],
154 request[15],
155 ]);
156 let len = 80u32;
157 response.extend_from_slice(&len.to_le_bytes());
158 response.extend_from_slice(&0i32.to_le_bytes()); response.extend_from_slice(&unique.to_le_bytes());
160
161 response.extend_from_slice(&self.major.to_le_bytes());
171 response.extend_from_slice(&self.minor.to_le_bytes());
172 response.extend_from_slice(&self.max_readahead.to_le_bytes());
173 response.extend_from_slice(&self.flags.to_le_bytes());
174 response.extend_from_slice(&16u16.to_le_bytes()); response.extend_from_slice(&12u16.to_le_bytes()); response.extend_from_slice(&self.max_write.to_le_bytes());
177 response.extend_from_slice(&1u32.to_le_bytes()); response.extend_from_slice(&self.max_pages.to_le_bytes());
179 let map_alignment: u16 = if self.dax_enabled {
186 let host_page = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
190 let page_size: u64 = if host_page > 0 {
191 host_page as u64
192 } else {
193 16 * 1024
194 };
195 u16::try_from(page_size.trailing_zeros()).unwrap_or(14)
196 } else {
197 0
198 };
199 response.extend_from_slice(&map_alignment.to_le_bytes());
200 response.extend_from_slice(&[0u8; 32]); self.initialized.store(true, Ordering::Release);
203
204 tracing::info!(
205 "FUSE session initialized: version {}.{}, flags={:#x}, max_write={}",
206 self.major,
207 self.minor,
208 self.flags,
209 self.max_write
210 );
211
212 Ok(response)
213 }
214
215 pub fn reset(&mut self) {
217 self.initialized.store(false, Ordering::Release);
218 self.major = FUSE_KERNEL_VERSION;
219 self.minor = FUSE_KERNEL_MINOR_VERSION;
220 self.flags = FUSE_ASYNC_READ | FUSE_BIG_WRITES | FUSE_WRITEBACK_CACHE | FUSE_MAP_ALIGNMENT;
221 self.max_readahead = DEFAULT_MAX_READAHEAD;
222 self.max_write = DEFAULT_MAX_WRITE;
223 self.max_pages = DEFAULT_MAX_PAGES;
224 self.dax_enabled = false;
225 }
226}
227
228impl Default for FuseSession {
229 fn default() -> Self {
230 Self::new()
231 }
232}
233
234#[cfg(test)]
235mod tests {
236 use super::*;
237
238 #[test]
239 fn test_fuse_session_new() {
240 let session = FuseSession::new();
241 assert!(!session.is_initialized());
242 assert_eq!(session.major(), FUSE_KERNEL_VERSION);
243 assert_eq!(session.minor(), FUSE_KERNEL_MINOR_VERSION);
244 assert_eq!(session.max_readahead(), DEFAULT_MAX_READAHEAD);
245 assert_eq!(session.max_write(), DEFAULT_MAX_WRITE);
246 assert_eq!(session.max_pages(), DEFAULT_MAX_PAGES);
247 }
248
249 #[test]
250 fn test_fuse_session_default() {
251 let session = FuseSession::default();
252 assert!(!session.is_initialized());
253 }
254
255 #[test]
256 fn test_fuse_session_reset() {
257 let mut session = FuseSession::new();
258 session.initialized.store(true, Ordering::SeqCst);
259
260 session.reset();
261
262 assert!(!session.is_initialized());
263 assert_eq!(session.major(), FUSE_KERNEL_VERSION);
264 }
265
266 #[test]
267 fn test_fuse_session_handle_init() {
268 let mut session = FuseSession::new();
269
270 let mut request = vec![0u8; 56];
272
273 request[0..4].copy_from_slice(&56u32.to_le_bytes()); request[4..8].copy_from_slice(&26u32.to_le_bytes()); request[8..16].copy_from_slice(&1u64.to_le_bytes()); request[16..24].copy_from_slice(&0u64.to_le_bytes()); request[24..28].copy_from_slice(&0u32.to_le_bytes()); request[28..32].copy_from_slice(&0u32.to_le_bytes()); request[32..36].copy_from_slice(&0u32.to_le_bytes()); request[36..40].copy_from_slice(&0u32.to_le_bytes()); request[40..44].copy_from_slice(&FUSE_KERNEL_VERSION.to_le_bytes());
284 request[44..48].copy_from_slice(&FUSE_KERNEL_MINOR_VERSION.to_le_bytes());
285 request[48..52].copy_from_slice(&(64 * 1024u32).to_le_bytes());
286 request[52..56].copy_from_slice(&(FUSE_ASYNC_READ | FUSE_BIG_WRITES).to_le_bytes());
287
288 let response = session.handle_init(&request).unwrap();
289
290 assert!(session.is_initialized());
291 assert_eq!(response.len(), 80);
292
293 let resp_len = u32::from_le_bytes([response[0], response[1], response[2], response[3]]);
294 let resp_error = i32::from_le_bytes([response[4], response[5], response[6], response[7]]);
295 let resp_unique = u64::from_le_bytes([
296 response[8],
297 response[9],
298 response[10],
299 response[11],
300 response[12],
301 response[13],
302 response[14],
303 response[15],
304 ]);
305
306 assert_eq!(resp_len, 80);
307 assert_eq!(resp_error, 0);
308 assert_eq!(resp_unique, 1);
309
310 assert_eq!(session.max_readahead(), 64 * 1024);
311 }
312
313 #[test]
314 fn test_fuse_session_handle_init_too_small() {
315 let mut session = FuseSession::new();
316 let request = vec![0u8; 40]; let result = session.handle_init(&request);
319 assert!(result.is_err());
320 }
321
322 #[test]
323 fn test_fuse_session_handle_init_old_version() {
324 let mut session = FuseSession::new();
325
326 let mut request = vec![0u8; 56];
327 request[0..4].copy_from_slice(&56u32.to_le_bytes());
328 request[4..8].copy_from_slice(&26u32.to_le_bytes());
329 request[8..16].copy_from_slice(&1u64.to_le_bytes());
330
331 request[40..44].copy_from_slice(&5u32.to_le_bytes());
333 request[44..48].copy_from_slice(&0u32.to_le_bytes());
334 request[48..52].copy_from_slice(&(64 * 1024u32).to_le_bytes());
335 request[52..56].copy_from_slice(&0u32.to_le_bytes());
336
337 let result = session.handle_init(&request);
338 assert!(result.is_err());
339 }
340}