1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// Copyright 2020 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! FUSE (Filesystem in Userspace) server and filesystem mounting support.

#![cfg(any(target_os = "android", target_os = "linux"))]

use std::ffi::FromBytesWithNulError;
use std::fs::File;
use std::io;

use remain::sorted;
use thiserror::Error as ThisError;

pub mod filesystem;
pub mod fuzzing;
pub mod mount;
mod server;
#[allow(dead_code)]
pub mod sys;
pub mod worker;

use filesystem::FileSystem;
pub use mount::mount;
pub use server::Mapper;
pub use server::Reader;
pub use server::Server;
pub use server::Writer;

/// Errors that may occur during the creation or operation of an Fs device.
#[sorted]
#[derive(ThisError, Debug)]
pub enum Error {
    /// A request is missing readable descriptors.
    /// Failed to decode protocol messages.
    #[error("failed to decode fuse message: {0}")]
    DecodeMessage(io::Error),
    /// Failed to encode protocol messages.
    #[error("failed to encode fuse message: {0}")]
    EncodeMessage(io::Error),
    /// Failed to set up FUSE endpoint to talk with.
    #[error("failed to set up FUSE endpoint to talk with: {0}")]
    EndpointSetup(io::Error),
    /// Failed to flush protocol messages.
    #[error("failed to flush fuse message: {0}")]
    FlushMessage(io::Error),
    /// A C string parameter is invalid.
    #[error("a c string parameter is invalid: {0}")]
    InvalidCString(FromBytesWithNulError),
    /// The `len` field of the header is too small.
    #[error("the `len` field of the header is too small")]
    InvalidHeaderLength,
    /// The `size` field of the `SetxattrIn` message does not match the length
    /// of the decoded value.
    #[error(
        "The `size` field of the `SetxattrIn` message does not match the\
             length of the decoded value: size = {0}, value.len() = {1}"
    )]
    InvalidXattrSize(u32, usize),
    /// One or more parameters are missing.
    #[error("one or more parameters are missing")]
    MissingParameter,
    /// Thread exited
    #[error("Thread exited")]
    ThreadExited,
    /// Requested too many `iovec`s for an `ioctl` retry.
    #[error(
        "requested too many `iovec`s for an `ioctl` retry reply: requested\
            {0}, max: {1}"
    )]
    TooManyIovecs(usize, usize),
}

pub type Result<T> = ::std::result::Result<T, Error>;

#[derive(Default)]
pub struct FuseConfig {
    dev_fuse_file: Option<File>,
    max_write_bytes: Option<u32>,
    max_read_bytes: Option<u32>,
    num_of_threads: Option<usize>,
}

impl FuseConfig {
    pub fn new() -> Self {
        FuseConfig {
            ..Default::default()
        }
    }

    /// Set the FUSE device.
    pub fn dev_fuse(&mut self, file: File) -> &mut Self {
        self.dev_fuse_file = Some(file);
        self
    }

    /// Set the maximum data in a read request. Must be large enough (usually equal) to `n` in
    /// `MountOption::MaxRead(n)`.
    pub fn max_read(&mut self, bytes: u32) -> &mut Self {
        self.max_read_bytes = Some(bytes);
        self
    }

    /// Set the maximum data in a write request.
    pub fn max_write(&mut self, bytes: u32) -> &mut Self {
        self.max_write_bytes = Some(bytes);
        self
    }

    /// Set the number of threads to run the `FileSystem`.
    pub fn num_threads(&mut self, num: usize) -> &mut Self {
        self.num_of_threads = Some(num);
        self
    }

    pub fn enter_message_loop<F: FileSystem + Sync + Send>(self, fs: F) -> Result<()> {
        let FuseConfig {
            dev_fuse_file,
            max_write_bytes,
            max_read_bytes,
            num_of_threads,
        } = self;
        let num = num_of_threads.unwrap_or(1);
        if num == 1 {
            worker::start_message_loop(
                dev_fuse_file.ok_or(Error::MissingParameter)?,
                max_read_bytes.ok_or(Error::MissingParameter)?,
                max_write_bytes.ok_or(Error::MissingParameter)?,
                fs,
            )
        } else {
            worker::internal::start_message_loop_mt(
                dev_fuse_file.ok_or(Error::MissingParameter)?,
                max_read_bytes.ok_or(Error::MissingParameter)?,
                max_write_bytes.ok_or(Error::MissingParameter)?,
                num,
                fs,
            )
        }
    }
}