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 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
// Copyright (C) 2020 Alibaba Cloud. All rights reserved.
// Copyright © 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
#![deny(missing_docs)]
//! A rust library for Fuse(filesystem in userspace) servers and virtio-fs devices.
//!
//! Filesystem in Userspace [`FUSE`](https://www.kernel.org/doc/html/latest/filesystems/fuse.html)
//! is a software interface for Unix and Unix-like computer operating systems that lets
//! non-privileged users create their own file systems without editing kernel code.
//! This is achieved by running file system code in user space while the FUSE module provides
//! only a "bridge" to the actual kernel interfaces.
//!
//! On Linux, the FUSE device driver is a general purpose filesystem abstraction layer, which
//! loads as a kernel module and presents a virtual device (/dev/fuse) to communicate with
//! a user (non-kernel) program via a well defined API. The user code need not run with root
//! priviledge if it does not need to access protected data or devices, and can implement
//! a virtual filesystem much more simply than a traditional device driver.
//!
//! In addition to traditional Fuse filesystems, the
//! [virtiofs](https://www.kernel.org/doc/html/latest/filesystems/virtiofs.html)
//! file system for Linux implements a driver for the paravirtualized VIRTIO “virtio-fs” device
//! for guest<->host file system sharing. It allows a guest to mount a directory that has
//! been exported on the host.
//!
//! Virtio-fs uses FUSE as the foundation. Unlike traditional FUSE where the file system daemon
//! runs in userspace, the virtio-fs daemon runs on the host. A VIRTIO device carries FUSE
//! messages and provides extensions for advanced features not available in traditional FUSE.
//! Since the virtio-fs device uses the FUSE protocol for file system requests, the virtiofs
//! file system for Linux is integrated closely with the FUSE file system client. The guest acts
//! as the FUSE client while the host acts as the FUSE server. The /dev/fuse interface between
//! the kernel and userspace is replaced with the virtio-fs device interface.
//!
//! The fuse-backend-rs crate includes several subsystems:
//! * [Fuse API](api/index.html). The Fuse API is the connection between transport layers and file
//! system drivers. It receives Fuse requests from transport layers, parses the request
//! according to Fuse ABI, invokes filesystem drivers to server the requests, and eventually
//! send back the result to the transport layer.
//! * [Fuse ABI](abi/index.html). Currently only Linux Fuse ABIs since v7.27 are supported.
//! * [Transport Layer](transport/index.html). The transport layer receives Fuse requests from
//! the clients and sends back replies. Currently there are two transport layers are supported:
//! Linux Fuse device(/dev/fuse) and virtiofs.
//! * Filesystem Drivers. Filesystem drivers implement the concrete Fuse filesystem logic,
//! at what ever is suitable. A default ["passthrough"](passthrough/index.html) filesystem
//! driver is implemented as a sample.
extern crate bitflags;
extern crate libc;
#[macro_use]
extern crate log;
extern crate vm_memory;
use std::ffi::{CStr, FromBytesWithNulError};
use std::io::ErrorKind;
use std::{error, fmt, io};
use vm_memory::bitmap::BitmapSlice;
/// Error codes for Fuse related operations.
#[derive(Debug)]
pub enum Error {
/// Failed to decode protocol messages.
DecodeMessage(io::Error),
/// Failed to encode protocol messages.
EncodeMessage(io::Error),
/// One or more parameters are missing.
MissingParameter,
/// A C string parameter is invalid.
InvalidCString(FromBytesWithNulError),
/// 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.
InvalidXattrSize((u32, usize)),
/// Invalid message that the server cannot handle properly.
InvalidMessage(io::Error),
/// Failed to write buffer to writer.
FailedToWrite(io::Error),
/// Failed to split a writer.
FailedToSplitWriter(transport::Error),
/// Failed to remap uid/gid.
FailedToRemapID((u32, u32)),
}
impl error::Error for Error {}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Error::*;
match self {
DecodeMessage(err) => write!(f, "failed to decode fuse message: {err}"),
EncodeMessage(err) => write!(f, "failed to encode fuse message: {err}"),
MissingParameter => write!(f, "one or more parameters are missing"),
InvalidHeaderLength => write!(f, "the `len` field of the header is too small"),
InvalidCString(err) => write!(f, "a c string parameter is invalid: {err}"),
InvalidXattrSize((size, len)) => write!(
f,
"The `size` field of the `SetxattrIn` message does not match the length of the \
decoded value: size = {size}, value.len() = {len}"
),
InvalidMessage(err) => write!(f, "cannot process fuse message: {err}"),
FailedToWrite(err) => write!(f, "cannot write to buffer: {err}"),
FailedToSplitWriter(err) => write!(f, "cannot split a writer: {err}"),
FailedToRemapID((uid, gid)) => write!(
f,
"failed to remap the context of user (uid={uid}, gid={gid})."
),
}
}
}
/// Result for Fuse related operations.
pub type Result<T> = ::std::result::Result<T, Error>;
pub mod abi;
pub mod api;
#[cfg(all(any(feature = "fusedev", feature = "virtiofs"), target_os = "linux"))]
pub mod overlayfs;
#[cfg(all(any(feature = "fusedev", feature = "virtiofs"), target_os = "linux"))]
pub mod passthrough;
pub mod transport;
pub mod common;
pub use self::common::*;
/// Convert io::ErrorKind to OS error code.
/// Reference to libstd/sys/unix/mod.rs => decode_error_kind.
pub fn encode_io_error_kind(kind: ErrorKind) -> i32 {
match kind {
//ErrorKind::ConnectionRefused => libc::ECONNREFUSED,
//ErrorKind::ConnectionReset => libc::ECONNRESET,
ErrorKind::PermissionDenied => libc::EPERM | libc::EACCES,
//ErrorKind::BrokenPipe => libc::EPIPE,
//ErrorKind::NotConnected => libc::ENOTCONN,
//ErrorKind::ConnectionAborted => libc::ECONNABORTED,
//ErrorKind::AddrNotAvailable => libc::EADDRNOTAVAIL,
//ErrorKind::AddrInUse => libc::EADDRINUSE,
ErrorKind::NotFound => libc::ENOENT,
ErrorKind::Interrupted => libc::EINTR,
//ErrorKind::InvalidInput => libc::EINVAL,
//ErrorKind::TimedOut => libc::ETIMEDOUT,
ErrorKind::AlreadyExists => libc::EEXIST,
ErrorKind::WouldBlock => libc::EWOULDBLOCK,
_ => libc::EIO,
}
}
/// trim all trailing nul terminators.
pub fn bytes_to_cstr(buf: &[u8]) -> Result<&CStr> {
// There might be multiple 0s at the end of buf, find & use the first one and trim other zeros.
match buf.iter().position(|x| *x == 0) {
// Convert to a `CStr` so that we can drop the '\0' byte at the end and make sure
// there are no interior '\0' bytes.
Some(pos) => CStr::from_bytes_with_nul(&buf[0..=pos]).map_err(Error::InvalidCString),
None => {
// Invalid input, just call CStr::from_bytes_with_nul() for suitable error code
CStr::from_bytes_with_nul(buf).map_err(Error::InvalidCString)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bytes_to_cstr() {
assert_eq!(
bytes_to_cstr(&[0x1u8, 0x2u8, 0x0]).unwrap(),
CStr::from_bytes_with_nul(&[0x1u8, 0x2u8, 0x0]).unwrap()
);
assert_eq!(
bytes_to_cstr(&[0x1u8, 0x2u8, 0x0, 0x0]).unwrap(),
CStr::from_bytes_with_nul(&[0x1u8, 0x2u8, 0x0]).unwrap()
);
assert_eq!(
bytes_to_cstr(&[0x1u8, 0x2u8, 0x0, 0x1]).unwrap(),
CStr::from_bytes_with_nul(&[0x1u8, 0x2u8, 0x0]).unwrap()
);
assert_eq!(
bytes_to_cstr(&[0x1u8, 0x2u8, 0x0, 0x0, 0x1]).unwrap(),
CStr::from_bytes_with_nul(&[0x1u8, 0x2u8, 0x0]).unwrap()
);
assert_eq!(
bytes_to_cstr(&[0x1u8, 0x2u8, 0x0, 0x1, 0x0]).unwrap(),
CStr::from_bytes_with_nul(&[0x1u8, 0x2u8, 0x0]).unwrap()
);
assert_eq!(
bytes_to_cstr(&[0x0u8, 0x2u8, 0x0]).unwrap(),
CStr::from_bytes_with_nul(&[0x0u8]).unwrap()
);
assert_eq!(
bytes_to_cstr(&[0x0u8, 0x0]).unwrap(),
CStr::from_bytes_with_nul(&[0x0u8]).unwrap()
);
assert_eq!(
bytes_to_cstr(&[0x0u8]).unwrap(),
CStr::from_bytes_with_nul(&[0x0u8]).unwrap()
);
bytes_to_cstr(&[0x1u8]).unwrap_err();
bytes_to_cstr(&[0x1u8, 0x1]).unwrap_err();
}
#[test]
fn test_encode_io_error_kind() {
assert_eq!(encode_io_error_kind(ErrorKind::NotFound), libc::ENOENT);
assert_eq!(encode_io_error_kind(ErrorKind::Interrupted), libc::EINTR);
assert_eq!(encode_io_error_kind(ErrorKind::AlreadyExists), libc::EEXIST);
assert_eq!(
encode_io_error_kind(ErrorKind::WouldBlock),
libc::EWOULDBLOCK
);
assert_eq!(encode_io_error_kind(ErrorKind::TimedOut), libc::EIO);
}
}