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