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}