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}