fuse_backend_rs/api/server/
mod.rs

1// Copyright (C) 2020-2022 Alibaba Cloud. All rights reserved.
2// Copyright 2019 The Chromium OS Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE-BSD-3-Clause file.
5
6//! Fuse API Server to interconnect transport layers with filesystem drivers.
7//!
8//! The Fuse API server is a adapter layer between transport layers and file system drivers.
9//! The main functionalities of the Fuse API server is:
10//! * Support different types of transport layers, fusedev, virtio-fs or vhost-user-fs.
11//! * Hide different transport layers details from file system drivers.
12//! * Parse transport messages according to the Fuse ABI to avoid duplicated message decoding
13//!   in every file system driver.
14//! * Invoke file system driver handler to serve each request and send the reply.
15//!
16//! The Fuse API server is performance critical, so it's designed to support multi-threading by
17//! adopting interior-mutability. And the arcswap crate is used to implement interior-mutability.
18
19use std::ffi::CStr;
20use std::io::{self, Read};
21use std::marker::PhantomData;
22use std::mem::size_of;
23use std::sync::Arc;
24
25use arc_swap::ArcSwap;
26
27use crate::abi::fuse_abi::*;
28use crate::api::filesystem::{Context, FileSystem, ZeroCopyReader, ZeroCopyWriter};
29use crate::file_traits::FileReadWriteVolatile;
30use crate::transport::{Reader, Writer};
31use crate::{bytes_to_cstr, BitmapSlice, Error, Result};
32
33#[cfg(feature = "async-io")]
34mod async_io;
35mod sync_io;
36
37/// Maximum buffer size of FUSE requests.
38#[cfg(target_os = "linux")]
39pub const MAX_BUFFER_SIZE: u32 = 1 << 20;
40/// Maximum buffer size of FUSE requests.
41#[cfg(target_os = "macos")]
42pub const MAX_BUFFER_SIZE: u32 = 1 << 25;
43const MIN_READ_BUFFER: u32 = 8192;
44const BUFFER_HEADER_SIZE: u32 = 0x1000;
45const DIRENT_PADDING: [u8; 8] = [0; 8];
46
47/// Maximum number of pages required for FUSE requests.
48pub const MAX_REQ_PAGES: u16 = 256; // 1MB
49
50/// Fuse Server to handle requests from the Fuse client and vhost user master.
51pub struct Server<F: FileSystem + Sync> {
52    fs: F,
53    vers: ArcSwap<ServerVersion>,
54}
55
56impl<F: FileSystem + Sync> Server<F> {
57    /// Create a Server instance from a filesystem driver object.
58    pub fn new(fs: F) -> Server<F> {
59        Server {
60            fs,
61            vers: ArcSwap::new(Arc::new(ServerVersion {
62                major: KERNEL_VERSION,
63                minor: KERNEL_MINOR_VERSION,
64            })),
65        }
66    }
67}
68
69struct ZcReader<'a, S: BitmapSlice = ()>(Reader<'a, S>);
70
71impl<'a, S: BitmapSlice> ZeroCopyReader for ZcReader<'a, S> {
72    fn read_to(
73        &mut self,
74        f: &mut dyn FileReadWriteVolatile,
75        count: usize,
76        off: u64,
77    ) -> io::Result<usize> {
78        self.0.read_to_at(f, count, off)
79    }
80}
81
82impl<'a, S: BitmapSlice> io::Read for ZcReader<'a, S> {
83    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
84        self.0.read(buf)
85    }
86}
87
88struct ZcWriter<'a, S: BitmapSlice = ()>(Writer<'a, S>);
89
90impl<'a, S: BitmapSlice> ZeroCopyWriter for ZcWriter<'a, S> {
91    fn write_from(
92        &mut self,
93        f: &mut dyn FileReadWriteVolatile,
94        count: usize,
95        off: u64,
96    ) -> io::Result<usize> {
97        self.0.write_from_at(f, count, off)
98    }
99
100    fn available_bytes(&self) -> usize {
101        self.0.available_bytes()
102    }
103}
104
105impl<'a, S: BitmapSlice> io::Write for ZcWriter<'a, S> {
106    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
107        self.0.write(buf)
108    }
109
110    fn flush(&mut self) -> io::Result<()> {
111        self.0.flush()
112    }
113}
114
115#[allow(dead_code)]
116struct ServerVersion {
117    major: u32,
118    minor: u32,
119}
120
121struct ServerUtil();
122
123impl ServerUtil {
124    fn get_message_body<S: BitmapSlice>(
125        r: &mut Reader<'_, S>,
126        in_header: &InHeader,
127        sub_hdr_sz: usize,
128    ) -> Result<Vec<u8>> {
129        let len = (in_header.len as usize)
130            .checked_sub(size_of::<InHeader>())
131            .and_then(|l| l.checked_sub(sub_hdr_sz))
132            .ok_or(Error::InvalidHeaderLength)?;
133
134        // Allocate buffer without zeroing out the content for performance.
135        let mut buf = Vec::<u8>::with_capacity(len);
136        // It's safe because read_exact() is called to fill all the allocated buffer.
137        #[allow(clippy::uninit_vec)]
138        unsafe {
139            buf.set_len(len)
140        };
141        r.read_exact(&mut buf).map_err(Error::DecodeMessage)?;
142
143        Ok(buf)
144    }
145
146    fn extract_two_cstrs(buf: &[u8]) -> Result<(&CStr, &CStr)> {
147        if let Some(mut pos) = buf.iter().position(|x| *x == 0) {
148            let first = CStr::from_bytes_with_nul(&buf[0..=pos]).map_err(Error::InvalidCString)?;
149            pos += 1;
150            if pos < buf.len() {
151                return Ok((first, bytes_to_cstr(&buf[pos..])?));
152            }
153        }
154
155        Err(Error::DecodeMessage(std::io::Error::from_raw_os_error(
156            libc::EINVAL,
157        )))
158    }
159}
160
161/// Provide concrete backend filesystem a way to catch information/metrics from fuse.
162pub trait MetricsHook {
163    /// `collect()` will be invoked before the real request is processed
164    fn collect(&self, ih: &InHeader);
165    /// `release()` will be invoked after the real request is processed
166    fn release(&self, oh: Option<&OutHeader>);
167}
168
169struct SrvContext<'a, F, S: BitmapSlice = ()> {
170    in_header: InHeader,
171    context: Context,
172    r: Reader<'a, S>,
173    w: Writer<'a, S>,
174    phantom: PhantomData<F>,
175    phantom2: PhantomData<S>,
176}
177
178impl<'a, F: FileSystem, S: BitmapSlice> SrvContext<'a, F, S> {
179    fn new(in_header: InHeader, r: Reader<'a, S>, w: Writer<'a, S>) -> Self {
180        let context = Context::from(&in_header);
181
182        SrvContext {
183            in_header,
184            context,
185            r,
186            w,
187            phantom: PhantomData,
188            phantom2: PhantomData,
189        }
190    }
191
192    fn context(&self) -> &Context {
193        &self.context
194    }
195
196    fn unique(&self) -> u64 {
197        self.in_header.unique
198    }
199
200    fn nodeid(&self) -> F::Inode {
201        self.in_header.nodeid.into()
202    }
203
204    fn take_reader(&mut self) -> Reader<'a, S> {
205        let mut reader = Reader::default();
206
207        std::mem::swap(&mut self.r, &mut reader);
208
209        reader
210    }
211}
212
213#[cfg(test)]
214mod tests {
215    use super::*;
216
217    #[test]
218    fn test_extract_cstrs() {
219        assert_eq!(
220            ServerUtil::extract_two_cstrs(&[0x1u8, 0x2u8, 0x0, 0x3, 0x0]).unwrap(),
221            (
222                CStr::from_bytes_with_nul(&[0x1u8, 0x2u8, 0x0]).unwrap(),
223                CStr::from_bytes_with_nul(&[0x3u8, 0x0]).unwrap(),
224            )
225        );
226        assert_eq!(
227            ServerUtil::extract_two_cstrs(&[0x1u8, 0x2u8, 0x0, 0x3, 0x0, 0x0]).unwrap(),
228            (
229                CStr::from_bytes_with_nul(&[0x1u8, 0x2u8, 0x0]).unwrap(),
230                CStr::from_bytes_with_nul(&[0x3u8, 0x0]).unwrap(),
231            )
232        );
233        assert_eq!(
234            ServerUtil::extract_two_cstrs(&[0x1u8, 0x2u8, 0x0, 0x3, 0x0, 0x4]).unwrap(),
235            (
236                CStr::from_bytes_with_nul(&[0x1u8, 0x2u8, 0x0]).unwrap(),
237                CStr::from_bytes_with_nul(&[0x3u8, 0x0]).unwrap(),
238            )
239        );
240        assert_eq!(
241            ServerUtil::extract_two_cstrs(&[0x1u8, 0x2u8, 0x0, 0x0, 0x4]).unwrap(),
242            (
243                CStr::from_bytes_with_nul(&[0x1u8, 0x2u8, 0x0]).unwrap(),
244                CStr::from_bytes_with_nul(&[0x0]).unwrap(),
245            )
246        );
247
248        ServerUtil::extract_two_cstrs(&[0x1u8, 0x2u8, 0x0, 0x3]).unwrap_err();
249        ServerUtil::extract_two_cstrs(&[0x1u8, 0x2u8, 0x0]).unwrap_err();
250        ServerUtil::extract_two_cstrs(&[0x1u8, 0x2u8]).unwrap_err();
251    }
252}