fuse_backend_rs/
lib.rs

1// Copyright (C) 2020 Alibaba Cloud. All rights reserved.
2// Copyright © 2019 Intel Corporation
3//
4// SPDX-License-Identifier: Apache-2.0
5
6#![deny(missing_docs)]
7
8//! A rust library for Fuse(filesystem in userspace) servers and virtio-fs devices.
9//!
10//! Filesystem in Userspace [`FUSE`](https://www.kernel.org/doc/html/latest/filesystems/fuse.html)
11//! is a software interface for Unix and Unix-like computer operating systems that lets
12//! non-privileged users create their own file systems without editing kernel code.
13//! This is achieved by running file system code in user space while the FUSE module provides
14//! only a "bridge" to the actual kernel interfaces.
15//!
16//! On Linux, the FUSE device driver is a general purpose filesystem abstraction layer, which
17//! loads as a kernel module and presents a virtual device (/dev/fuse) to communicate with
18//! a user (non-kernel) program via a well defined API. The user code need not run with root
19//! priviledge if it does not need to access protected data or devices, and can implement
20//! a virtual filesystem much more simply than a traditional device driver.
21//!
22//! In addition to traditional Fuse filesystems, the
23//! [virtiofs](https://www.kernel.org/doc/html/latest/filesystems/virtiofs.html)
24//! file system for Linux implements a driver for the paravirtualized VIRTIO “virtio-fs” device
25//! for guest<->host file system sharing. It allows a guest to mount a directory that has
26//! been exported on the host.
27//!
28//! Virtio-fs uses FUSE as the foundation. Unlike traditional FUSE where the file system daemon
29//! runs in userspace, the virtio-fs daemon runs on the host. A VIRTIO device carries FUSE
30//! messages and provides extensions for advanced features not available in traditional FUSE.
31//! Since the virtio-fs device uses the FUSE protocol for file system requests, the virtiofs
32//! file system for Linux is integrated closely with the FUSE file system client. The guest acts
33//! as the FUSE client while the host acts as the FUSE server. The /dev/fuse interface between
34//! the kernel and userspace is replaced with the virtio-fs device interface.
35//!
36//! The fuse-backend-rs crate includes several subsystems:
37//! * [Fuse API](api/index.html). The Fuse API is the connection between transport layers and file
38//!   system drivers. It receives Fuse requests from transport layers, parses the request
39//!   according to Fuse ABI, invokes filesystem drivers to server the requests, and eventually
40//!   send back the result to the transport layer.
41//! * [Fuse ABI](abi/index.html). Currently only Linux Fuse ABIs since v7.27 are supported.
42//! * [Transport Layer](transport/index.html). The transport layer receives Fuse requests from
43//!   the clients and sends back replies. Currently there are two transport layers are supported:
44//!   Linux Fuse device(/dev/fuse) and virtiofs.
45//! * Filesystem Drivers. Filesystem drivers implement the concrete Fuse filesystem logic,
46//!   at what ever is suitable. A default ["passthrough"](passthrough/index.html) filesystem
47//!   driver is implemented as a sample.
48
49extern crate bitflags;
50extern crate libc;
51#[macro_use]
52extern crate log;
53extern crate vm_memory;
54
55use std::ffi::{CStr, FromBytesWithNulError};
56use std::io::ErrorKind;
57use std::{error, fmt, io};
58
59use vm_memory::bitmap::BitmapSlice;
60
61/// Error codes for Fuse related operations.
62#[derive(Debug)]
63pub enum Error {
64    /// Failed to decode protocol messages.
65    DecodeMessage(io::Error),
66    /// Failed to encode protocol messages.
67    EncodeMessage(io::Error),
68    /// One or more parameters are missing.
69    MissingParameter,
70    /// A C string parameter is invalid.
71    InvalidCString(FromBytesWithNulError),
72    /// The `len` field of the header is too small.
73    InvalidHeaderLength,
74    /// The `size` field of the `SetxattrIn` message does not match the length
75    /// of the decoded value.
76    InvalidXattrSize((u32, usize)),
77    /// Invalid message that the server cannot handle properly.
78    InvalidMessage(io::Error),
79    /// Failed to write buffer to writer.
80    FailedToWrite(io::Error),
81    /// Failed to split a writer.
82    FailedToSplitWriter(transport::Error),
83    /// Failed to remap uid/gid.
84    FailedToRemapID((u32, u32)),
85}
86
87impl error::Error for Error {}
88
89impl fmt::Display for Error {
90    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
91        use Error::*;
92        match self {
93            DecodeMessage(err) => write!(f, "failed to decode fuse message: {err}"),
94            EncodeMessage(err) => write!(f, "failed to encode fuse message: {err}"),
95            MissingParameter => write!(f, "one or more parameters are missing"),
96            InvalidHeaderLength => write!(f, "the `len` field of the header is too small"),
97            InvalidCString(err) => write!(f, "a c string parameter is invalid: {err}"),
98            InvalidXattrSize((size, len)) => write!(
99                f,
100                "The `size` field of the `SetxattrIn` message does not match the length of the \
101                 decoded value: size = {size}, value.len() = {len}"
102            ),
103            InvalidMessage(err) => write!(f, "cannot process fuse message: {err}"),
104            FailedToWrite(err) => write!(f, "cannot write to buffer: {err}"),
105            FailedToSplitWriter(err) => write!(f, "cannot split a writer: {err}"),
106            FailedToRemapID((uid, gid)) => write!(
107                f,
108                "failed to remap the context of user (uid={uid}, gid={gid})."
109            ),
110        }
111    }
112}
113
114/// Result for Fuse related operations.
115pub type Result<T> = ::std::result::Result<T, Error>;
116
117pub mod abi;
118pub mod api;
119
120#[cfg(all(any(feature = "fusedev", feature = "virtiofs"), target_os = "linux"))]
121pub mod overlayfs;
122#[cfg(all(any(feature = "fusedev", feature = "virtiofs"), target_os = "linux"))]
123pub mod passthrough;
124pub mod transport;
125
126pub mod common;
127pub use self::common::*;
128
129/// Convert io::ErrorKind to OS error code.
130/// Reference to libstd/sys/unix/mod.rs => decode_error_kind.
131pub fn encode_io_error_kind(kind: ErrorKind) -> i32 {
132    match kind {
133        //ErrorKind::ConnectionRefused => libc::ECONNREFUSED,
134        //ErrorKind::ConnectionReset => libc::ECONNRESET,
135        ErrorKind::PermissionDenied => libc::EPERM | libc::EACCES,
136        //ErrorKind::BrokenPipe => libc::EPIPE,
137        //ErrorKind::NotConnected => libc::ENOTCONN,
138        //ErrorKind::ConnectionAborted => libc::ECONNABORTED,
139        //ErrorKind::AddrNotAvailable => libc::EADDRNOTAVAIL,
140        //ErrorKind::AddrInUse => libc::EADDRINUSE,
141        ErrorKind::NotFound => libc::ENOENT,
142        ErrorKind::Interrupted => libc::EINTR,
143        //ErrorKind::InvalidInput => libc::EINVAL,
144        //ErrorKind::TimedOut => libc::ETIMEDOUT,
145        ErrorKind::AlreadyExists => libc::EEXIST,
146        ErrorKind::WouldBlock => libc::EWOULDBLOCK,
147        _ => libc::EIO,
148    }
149}
150
151/// trim all trailing nul terminators.
152pub fn bytes_to_cstr(buf: &[u8]) -> Result<&CStr> {
153    // There might be multiple 0s at the end of buf, find & use the first one and trim other zeros.
154    match buf.iter().position(|x| *x == 0) {
155        // Convert to a `CStr` so that we can drop the '\0' byte at the end and make sure
156        // there are no interior '\0' bytes.
157        Some(pos) => CStr::from_bytes_with_nul(&buf[0..=pos]).map_err(Error::InvalidCString),
158        None => {
159            // Invalid input, just call CStr::from_bytes_with_nul() for suitable error code
160            CStr::from_bytes_with_nul(buf).map_err(Error::InvalidCString)
161        }
162    }
163}
164
165#[cfg(test)]
166mod tests {
167    use super::*;
168
169    #[test]
170    fn test_bytes_to_cstr() {
171        assert_eq!(
172            bytes_to_cstr(&[0x1u8, 0x2u8, 0x0]).unwrap(),
173            CStr::from_bytes_with_nul(&[0x1u8, 0x2u8, 0x0]).unwrap()
174        );
175        assert_eq!(
176            bytes_to_cstr(&[0x1u8, 0x2u8, 0x0, 0x0]).unwrap(),
177            CStr::from_bytes_with_nul(&[0x1u8, 0x2u8, 0x0]).unwrap()
178        );
179        assert_eq!(
180            bytes_to_cstr(&[0x1u8, 0x2u8, 0x0, 0x1]).unwrap(),
181            CStr::from_bytes_with_nul(&[0x1u8, 0x2u8, 0x0]).unwrap()
182        );
183        assert_eq!(
184            bytes_to_cstr(&[0x1u8, 0x2u8, 0x0, 0x0, 0x1]).unwrap(),
185            CStr::from_bytes_with_nul(&[0x1u8, 0x2u8, 0x0]).unwrap()
186        );
187        assert_eq!(
188            bytes_to_cstr(&[0x1u8, 0x2u8, 0x0, 0x1, 0x0]).unwrap(),
189            CStr::from_bytes_with_nul(&[0x1u8, 0x2u8, 0x0]).unwrap()
190        );
191
192        assert_eq!(
193            bytes_to_cstr(&[0x0u8, 0x2u8, 0x0]).unwrap(),
194            CStr::from_bytes_with_nul(&[0x0u8]).unwrap()
195        );
196        assert_eq!(
197            bytes_to_cstr(&[0x0u8, 0x0]).unwrap(),
198            CStr::from_bytes_with_nul(&[0x0u8]).unwrap()
199        );
200        assert_eq!(
201            bytes_to_cstr(&[0x0u8]).unwrap(),
202            CStr::from_bytes_with_nul(&[0x0u8]).unwrap()
203        );
204
205        bytes_to_cstr(&[0x1u8]).unwrap_err();
206        bytes_to_cstr(&[0x1u8, 0x1]).unwrap_err();
207    }
208
209    #[test]
210    fn test_encode_io_error_kind() {
211        assert_eq!(encode_io_error_kind(ErrorKind::NotFound), libc::ENOENT);
212        assert_eq!(encode_io_error_kind(ErrorKind::Interrupted), libc::EINTR);
213        assert_eq!(encode_io_error_kind(ErrorKind::AlreadyExists), libc::EEXIST);
214        assert_eq!(
215            encode_io_error_kind(ErrorKind::WouldBlock),
216            libc::EWOULDBLOCK
217        );
218        assert_eq!(encode_io_error_kind(ErrorKind::TimedOut), libc::EIO);
219    }
220}