fuse/
request.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4#[macro_use]
5mod macros;
6
7use crate::protocol::*;
8use std::io;
9use zerocopy::FromBytes;
10use zerocopy::Immutable;
11use zerocopy::IntoBytes;
12use zerocopy::KnownLayout;
13
14// Define an enum for all operations and their arguments.
15fuse_operations! {
16    FUSE_LOOKUP Lookup name:name;
17    FUSE_FORGET Forget arg:fuse_forget_in;
18    FUSE_GETATTR GetAttr arg:fuse_getattr_in;
19    FUSE_SETATTR SetAttr arg:fuse_setattr_in;
20    FUSE_READLINK ReadLink;
21    FUSE_SYMLINK Symlink name:name target:str;
22    FUSE_MKNOD MkNod arg:fuse_mknod_in name:name;
23    FUSE_MKDIR MkDir arg:fuse_mkdir_in name:name;
24    FUSE_UNLINK Unlink name:name;
25    FUSE_RMDIR RmDir name:name;
26    FUSE_RENAME Rename arg:fuse_rename_in name:name new_name:name;
27    FUSE_LINK Link arg:fuse_link_in name:name;
28    FUSE_OPEN Open arg:fuse_open_in;
29    FUSE_READ Read arg:fuse_read_in;
30    FUSE_WRITE Write arg:fuse_write_in data:[u8; arg.size];
31    FUSE_STATFS StatFs;
32    FUSE_RELEASE Release arg:fuse_release_in;
33    FUSE_FSYNC FSync arg:fuse_fsync_in;
34    FUSE_SETXATTR SetXAttr arg:fuse_setxattr_in name:str value:[u8; arg.size];
35    FUSE_GETXATTR GetXAttr arg:fuse_getxattr_in name:str;
36    FUSE_LISTXATTR ListXAttr arg:fuse_getxattr_in;
37    FUSE_REMOVEXATTR RemoveXAttr name:str;
38    FUSE_FLUSH Flush arg:fuse_flush_in;
39    FUSE_INIT Init arg:fuse_init_in;
40    FUSE_OPENDIR OpenDir arg:fuse_open_in;
41    FUSE_READDIR ReadDir arg:fuse_read_in;
42    FUSE_RELEASEDIR ReleaseDir arg:fuse_release_in;
43    FUSE_FSYNCDIR FSyncDir arg:fuse_fsync_in;
44    FUSE_GETLK GetLock arg:fuse_lk_in;
45    FUSE_SETLK SetLock arg:fuse_lk_in;
46    FUSE_SETLKW SetLockSleep arg:fuse_lk_in;
47    FUSE_ACCESS Access arg:fuse_access_in;
48    FUSE_CREATE Create arg:fuse_create_in name:str;
49    FUSE_INTERRUPT Interrupt arg:fuse_interrupt_in;
50    FUSE_BMAP BMap arg:fuse_bmap_in;
51    FUSE_DESTROY Destroy;
52    FUSE_IOCTL Ioctl arg:fuse_ioctl_in data:[u8; arg.in_size];
53    FUSE_POLL Poll arg:fuse_poll_in;
54    FUSE_NOTIFY_REPLY NotifyReply arg:fuse_notify_retrieve_in data:[u8];
55    FUSE_BATCH_FORGET BatchForget arg:fuse_batch_forget_in nodes:[u8];
56    FUSE_FALLOCATE FAllocate arg:fuse_fallocate_in;
57    FUSE_READDIRPLUS ReadDirPlus arg:fuse_read_in;
58    FUSE_RENAME2 Rename2 arg:fuse_rename2_in name:name new_name:name;
59    FUSE_LSEEK LSeek arg:fuse_lseek_in;
60    FUSE_COPY_FILE_RANGE CopyFileRange arg:fuse_copy_file_range_in;
61    FUSE_SETUPMAPPING SetupMapping arg:fuse_setupmapping_in;
62    FUSE_REMOVEMAPPING RemoveMapping arg:fuse_removemapping_in mappings:[u8];
63    FUSE_SYNCFS SyncFs _arg:fuse_syncfs_in;
64    FUSE_STATX StatX arg:fuse_statx_in;
65    FUSE_CANONICAL_PATH CanonicalPath;
66}
67
68/// A request received from the FUSE kernel module.
69pub struct Request {
70    header: fuse_in_header,
71    operation: FuseOperation,
72}
73
74impl Request {
75    /// Create a new request from the specified data.
76    pub fn new(mut reader: impl RequestReader) -> lx::Result<Self> {
77        let header: fuse_in_header = reader.read_type()?;
78        let operation = Self::read_operation(&header, reader);
79        Ok(Self { header, operation })
80    }
81
82    /// Gets the FUSE opcode for this request.
83    pub fn opcode(&self) -> u32 {
84        self.header.opcode
85    }
86
87    /// Gets the unique identifier of this request.
88    pub fn unique(&self) -> u64 {
89        self.header.unique
90    }
91
92    /// Gets the FUSE node ID of the inode that this request is for.
93    pub fn node_id(&self) -> u64 {
94        self.header.nodeid
95    }
96
97    /// Gets the user ID of the user that issued this request.
98    pub fn uid(&self) -> lx::uid_t {
99        self.header.uid
100    }
101
102    /// Gets the group ID of the user that issued this request.
103    pub fn gid(&self) -> lx::gid_t {
104        self.header.gid
105    }
106
107    /// Gets the process ID of the process that issued this request.
108    pub fn pid(&self) -> u32 {
109        self.header.pid
110    }
111
112    /// Gets the operation that this request should perform.
113    pub fn operation(&self) -> &FuseOperation {
114        &self.operation
115    }
116
117    /// Log the request.
118    pub fn log(&self) {
119        tracing::trace!(
120            unique = self.unique(),
121            node_id = self.node_id(),
122            uid = self.uid(),
123            gid = self.gid(),
124            pid = self.pid(),
125            operation = ?self.operation,
126            "Request",
127        );
128    }
129
130    fn read_operation(header: &fuse_in_header, reader: impl RequestReader) -> FuseOperation {
131        if header.len as usize > reader.remaining_len() + size_of_val(header) {
132            tracing::error!(
133                opcode = header.opcode,
134                unique = header.unique,
135                header_len = header.len,
136                len = reader.remaining_len() + size_of_val(header),
137                "Invalid message length",
138            );
139            return FuseOperation::Invalid;
140        }
141
142        match FuseOperation::read(header.opcode, reader) {
143            Ok(operation) => operation,
144            Err(e) => {
145                tracing::error!(
146                    opcode = header.opcode,
147                    unique = header.unique,
148                    error = &e as &dyn std::error::Error,
149                    "Invalid message payload",
150                );
151                FuseOperation::Error(e)
152            }
153        }
154    }
155}
156
157/// Helpers to parse FUSE messages.
158pub trait RequestReader: io::Read {
159    /// Read until a matching byte is found.
160    ///
161    /// This should advance the read position beyond the matching byte, and return the data up to
162    /// (but not including) the matching byte.
163    ///
164    /// This is used to read NULL-terminated strings.
165    fn read_until(&mut self, byte: u8) -> lx::Result<Vec<u8>>;
166
167    /// Gets the remaining, unread length of the input data.
168    fn remaining_len(&self) -> usize;
169
170    /// Consume the next `count` bytes.
171    fn read_count(&mut self, count: usize) -> lx::Result<Box<[u8]>> {
172        let mut buffer = vec![0u8; count];
173        self.read_exact(&mut buffer)?;
174        Ok(buffer.into_boxed_slice())
175    }
176
177    /// Read all the remaining data.
178    fn read_all(&mut self) -> lx::Result<Box<[u8]>> {
179        self.read_count(self.remaining_len())
180    }
181
182    /// Read a struct of type `T`.
183    fn read_type<T: IntoBytes + FromBytes + Immutable + KnownLayout>(&mut self) -> lx::Result<T> {
184        let mut value: T = T::new_zeroed();
185        self.read_exact(value.as_mut_bytes())?;
186        Ok(value)
187    }
188
189    /// Read a NULL-terminated string
190    fn string(&mut self) -> lx::Result<lx::LxString> {
191        let buffer = self.read_until(b'\0')?;
192        Ok(lx::LxString::from_vec(buffer))
193    }
194
195    /// Maximum length of a file name component (NAME_MAX on Linux).
196    const NAME_MAX: usize = 255;
197
198    /// Read a NULL-terminated string and ensure it's a valid path name component.
199    fn name(&mut self) -> lx::Result<lx::LxString> {
200        let name = self.string()?;
201        if name.is_empty() || name == "." || name == ".." || name.as_bytes().contains(&b'/') {
202            return Err(lx::Error::EINVAL);
203        }
204        if name.len() > Self::NAME_MAX {
205            return Err(lx::Error::ENAMETOOLONG);
206        }
207
208        Ok(name)
209    }
210}
211
212impl RequestReader for &[u8] {
213    fn read_until(&mut self, byte: u8) -> lx::Result<Vec<u8>> {
214        let length = self
215            .iter()
216            .position(|&c| c == byte)
217            .ok_or(lx::Error::EINVAL)?;
218
219        let result = Vec::from(&self[..length]);
220        *self = &self[length + 1..];
221        Ok(result)
222    }
223
224    fn remaining_len(&self) -> usize {
225        self.len()
226    }
227}
228
229#[cfg(test)]
230pub(crate) mod tests {
231    use super::*;
232
233    #[test]
234    fn parse_init() {
235        let request = Request::new(FUSE_INIT_REQUEST).unwrap();
236        check_header(&request, 1, FUSE_INIT, 0);
237        if let FuseOperation::Init { arg } = request.operation {
238            assert_eq!(arg.major, 7);
239            assert_eq!(arg.minor, 27);
240            assert_eq!(arg.max_readahead, 131072);
241            assert_eq!(arg.flags, 0x3FFFFB);
242        } else {
243            panic!("Incorrect operation {:?}", request.operation);
244        }
245    }
246
247    #[test]
248    fn parse_get_attr() {
249        let request = Request::new(FUSE_GETATTR_REQUEST).unwrap();
250        check_header(&request, 2, FUSE_GETATTR, 1);
251        if let FuseOperation::GetAttr { arg } = request.operation {
252            assert_eq!(arg.fh, 0);
253            assert_eq!(arg.getattr_flags, 0);
254        } else {
255            panic!("Incorrect operation {:?}", request.operation);
256        }
257    }
258
259    #[test]
260    fn parse_statx() {
261        let request = Request::new(FUSE_STATX_REQUEST).unwrap();
262        check_header(&request, 2, FUSE_STATX, 1);
263        if let FuseOperation::StatX { arg } = request.operation {
264            assert_eq!(arg.fh, 0);
265            assert_eq!(arg.getattr_flags, 0);
266            let mask = lx::StatExMask::new()
267                .with_file_type(true)
268                .with_mode(true)
269                .with_nlink(true)
270                .with_uid(true)
271                .with_gid(true)
272                .with_atime(true)
273                .with_mtime(true)
274                .with_ctime(true)
275                .with_ino(true)
276                .with_size(true)
277                .with_blocks(true)
278                .with_btime(true);
279            assert_eq!(arg.mask, mask.into_bits());
280            let flags = StatxFlags::new().with_dont_sync(true);
281            assert_eq!(arg.flags.into_bits(), flags.into_bits());
282        } else {
283            panic!("Incorrect operation {:?}", request.operation);
284        }
285    }
286
287    #[test]
288    fn parse_lookup() {
289        let request = Request::new(FUSE_LOOKUP_REQUEST).unwrap();
290        check_header(&request, 3, FUSE_LOOKUP, 1);
291        if let FuseOperation::Lookup { name } = request.operation {
292            assert_eq!(name, "hello");
293        } else {
294            panic!("Incorrect operation {:?}", request.operation);
295        }
296    }
297
298    #[test]
299    fn parse_open() {
300        let request = Request::new(FUSE_OPEN_REQUEST).unwrap();
301        check_header(&request, 4, FUSE_OPEN, 2);
302        if let FuseOperation::Open { arg } = request.operation {
303            assert_eq!(arg.flags, 0x8000);
304        } else {
305            panic!("Incorrect operation {:?}", request.operation);
306        }
307    }
308
309    #[test]
310    fn parse_read() {
311        let request = Request::new(FUSE_READ_REQUEST).unwrap();
312        check_header(&request, 5, FUSE_READ, 2);
313        if let FuseOperation::Read { arg } = request.operation {
314            assert_eq!(arg.fh, 1);
315            assert_eq!(arg.offset, 0);
316            assert_eq!(arg.size, 4096);
317            assert_eq!(arg.read_flags, 0);
318            assert_eq!(arg.lock_owner, 0);
319            assert_eq!(arg.flags, 0x8000);
320        } else {
321            panic!("Incorrect operation {:?}", request.operation);
322        }
323    }
324
325    #[test]
326    fn parse_flush() {
327        let request = Request::new(FUSE_FLUSH_REQUEST).unwrap();
328        check_header(&request, 7, FUSE_FLUSH, 2);
329        if let FuseOperation::Flush { arg } = request.operation {
330            assert_eq!(arg.fh, 1);
331            // This was copied from a real fuse request; I have no idea why it sends this number
332            // for lock owner especially since locks were not being used.
333            assert_eq!(arg.lock_owner, 13021892616250331871);
334        } else {
335            panic!("Incorrect operation {:?}", request.operation);
336        }
337    }
338
339    #[test]
340    fn parse_release() {
341        let request = Request::new(FUSE_RELEASE_REQUEST).unwrap();
342        check_header(&request, 8, FUSE_RELEASE, 2);
343        if let FuseOperation::Release { arg } = request.operation {
344            assert_eq!(arg.fh, 1);
345            assert_eq!(arg.flags, 0x8000);
346            assert_eq!(arg.release_flags, 0);
347            assert_eq!(arg.lock_owner, 0);
348        } else {
349            panic!("Incorrect operation {:?}", request.operation);
350        }
351    }
352
353    #[test]
354    fn parse_opendir() {
355        let request = Request::new(FUSE_OPENDIR_REQUEST).unwrap();
356        check_header(&request, 9, FUSE_OPENDIR, 1);
357        if let FuseOperation::OpenDir { arg } = request.operation {
358            assert_eq!(arg.flags, 0x18800);
359        } else {
360            panic!("Incorrect operation {:?}", request.operation);
361        }
362    }
363
364    #[test]
365    fn parse_readdir() {
366        let request = Request::new(FUSE_READDIR_REQUEST).unwrap();
367        check_header(&request, 11, FUSE_READDIR, 1);
368        if let FuseOperation::ReadDir { arg } = request.operation {
369            assert_eq!(arg.fh, 0);
370            assert_eq!(arg.offset, 3);
371            assert_eq!(arg.size, 4096);
372            assert_eq!(arg.read_flags, 0);
373            assert_eq!(arg.lock_owner, 0);
374            assert_eq!(arg.flags, 0x18800);
375        } else {
376            panic!("Incorrect operation {:?}", request.operation);
377        }
378    }
379
380    #[test]
381    fn parse_releasedir() {
382        let request = Request::new(FUSE_RELEASEDIR_REQUEST).unwrap();
383        check_header(&request, 12, FUSE_RELEASEDIR, 1);
384        if let FuseOperation::ReleaseDir { arg } = request.operation {
385            assert_eq!(arg.fh, 0);
386            assert_eq!(arg.flags, 0x18800);
387            assert_eq!(arg.release_flags, 0);
388            assert_eq!(arg.lock_owner, 0);
389        } else {
390            panic!("Incorrect operation {:?}", request.operation);
391        }
392    }
393
394    fn check_header(request: &Request, unique: u64, opcode: u32, ino: u64) {
395        assert_eq!(request.unique(), unique);
396        assert_eq!(request.opcode(), opcode);
397        assert_eq!(request.node_id(), ino);
398        assert_eq!(request.uid(), 0);
399        assert_eq!(request.gid(), 0);
400        assert_eq!(
401            request.pid(),
402            if opcode == FUSE_INIT || opcode == FUSE_RELEASE || opcode == FUSE_RELEASEDIR {
403                0
404            } else {
405                971
406            }
407        );
408    }
409
410    pub const FUSE_INIT_REQUEST: &[u8] = &[
411        56, 0, 0, 0, 26, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
412        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 27, 0, 0, 0, 0, 0, 2, 0, 251, 255, 63, 0,
413    ];
414
415    pub const FUSE_GETATTR_REQUEST: &[u8] = &[
416        56, 0, 0, 0, 3, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
417        0, 0, 203, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
418    ];
419
420    pub const FUSE_LOOKUP_REQUEST: &[u8] = &[
421        46, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
422        0, 0, 203, 3, 0, 0, 0, 0, 0, 0, 104, 101, 108, 108, 111, 0,
423    ];
424
425    const FUSE_OPEN_REQUEST: &[u8] = &[
426        48, 0, 0, 0, 14, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
427        0, 0, 203, 3, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0,
428    ];
429
430    const FUSE_READ_REQUEST: &[u8] = &[
431        80, 0, 0, 0, 15, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
432        0, 0, 203, 3, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0,
433        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0,
434    ];
435
436    const FUSE_FLUSH_REQUEST: &[u8] = &[
437        64, 0, 0, 0, 25, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
438        0, 0, 203, 3, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 223, 18,
439        226, 110, 87, 14, 183, 180,
440    ];
441
442    const FUSE_RELEASE_REQUEST: &[u8] = &[
443        64, 0, 0, 0, 18, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
444        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
445        0, 0, 0, 0,
446    ];
447
448    const FUSE_OPENDIR_REQUEST: &[u8] = &[
449        48, 0, 0, 0, 27, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
450        0, 0, 203, 3, 0, 0, 0, 0, 0, 0, 0, 136, 1, 0, 0, 0, 0, 0,
451    ];
452
453    const FUSE_READDIR_REQUEST: &[u8] = &[
454        80, 0, 0, 0, 28, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
455        0, 0, 0, 203, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 16,
456        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 1, 0, 0, 0, 0, 0,
457    ];
458
459    const FUSE_RELEASEDIR_REQUEST: &[u8] = &[
460        64, 0, 0, 0, 29, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
461        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 1, 0, 0, 0, 0, 0, 0, 0, 0,
462        0, 0, 0, 0, 0,
463    ];
464
465    const FUSE_STATX_REQUEST: &[u8] = &[
466        64, 0, 0, 0, 52, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
467        0, 0, 203, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0,
468        0, 255, 15, 0, 0,
469    ];
470
471    #[test]
472    fn name_too_long_returns_enametoolong() {
473        // Create a name with 256 characters (exceeds NAME_MAX of 255) + null terminator
474        let mut data = vec![b'a'; 256];
475        data.push(0); // null terminator
476
477        let mut reader: &[u8] = &data;
478        let result = reader.name();
479
480        assert_eq!(result, Err(lx::Error::ENAMETOOLONG));
481    }
482}