nydus_service/
lib.rs

1// Copyright 2021 Ant Group. All rights reserved.
2//
3// SPDX-License-Identifier: Apache-2.0
4
5//! Nydus Image Service Management Framework
6//!
7//! The `nydus-service` crate provides facilities to manage Nydus services, such as:
8//! - `blobfs`: share processed RAFS metadata/data blobs to guest by virtio-fs, so the RAFS
9//!    filesystem can be mounted by EROFS inside guest.
10//! - `blockdev`: compose processed RAFS metadata/data as a block device, so it can be used as
11//!   backend for virtio-blk.
12//! - `fscache`: cooperate Linux fscache subsystem to mount RAFS filesystems by EROFS.
13//! - `fuse`: mount RAFS filesystems as FUSE filesystems.
14
15#[macro_use]
16extern crate log;
17#[macro_use]
18extern crate nydus_api;
19
20use std::fmt::{self, Display};
21use std::io;
22use std::str::FromStr;
23use std::sync::mpsc::{RecvError, SendError};
24
25use fuse_backend_rs::api::vfs::VfsError;
26use fuse_backend_rs::transport::Error as FuseTransportError;
27use fuse_backend_rs::Error as FuseError;
28use nydus_api::{ConfigV2, DaemonErrorKind};
29use nydus_rafs::RafsError;
30use serde::{Deserialize, Serialize};
31use serde_json::Error as SerdeError;
32use versionize::{VersionMap, Versionize, VersionizeError, VersionizeResult};
33use versionize_derive::Versionize;
34
35pub mod daemon;
36mod fs_service;
37mod fusedev;
38mod singleton;
39pub mod upgrade;
40
41pub use blob_cache::BlobCacheMgr;
42pub use fs_service::{FsBackendCollection, FsBackendMountCmd, FsBackendUmountCmd, FsService};
43pub use fusedev::{create_fuse_daemon, create_vfs_backend, FusedevDaemon};
44pub use singleton::create_daemon;
45
46#[cfg(target_os = "linux")]
47pub mod blob_cache;
48#[cfg(all(target_os = "linux", feature = "block-device"))]
49pub mod block_device;
50#[cfg(all(target_os = "linux", feature = "block-nbd"))]
51pub mod block_nbd;
52#[cfg(target_os = "linux")]
53mod fs_cache;
54
55#[cfg(target_os = "linux")]
56pub use fs_cache::FsCacheHandler;
57
58/// Error code related to Nydus library.
59#[derive(thiserror::Error, Debug)]
60pub enum Error {
61    #[error("object or filesystem already exists")]
62    AlreadyExists,
63    /// Invalid arguments provided.
64    #[error("invalid argument `{0}`")]
65    InvalidArguments(String),
66    #[error("invalid configuration, {0}")]
67    InvalidConfig(String),
68    #[error("invalid prefetch file list")]
69    InvalidPrefetchList,
70    #[error("object or filesystem doesn't exist")]
71    NotFound,
72    #[error("daemon is not ready yet")]
73    NotReady,
74    #[error("unsupported request or operation")]
75    Unsupported,
76    #[error("failed to serialize/deserialize message, {0}")]
77    Serde(SerdeError),
78    #[error("failed to spawn thread, {0}")]
79    ThreadSpawn(io::Error),
80    #[error("failed to send message to channel, {0}")]
81    ChannelSend(#[from] SendError<crate::daemon::DaemonStateMachineInput>),
82    #[error("failed to receive message from channel, {0}")]
83    ChannelReceive(#[from] RecvError),
84    #[error("failed to upgrade nydusd daemon, {0}")]
85    UpgradeManager(upgrade::UpgradeMgrError),
86    #[error("failed to start service, {0}")]
87    StartService(String),
88    /// Input event to stat-machine is not expected.
89    #[error("unexpect state machine transition event `{0:?}`")]
90    UnexpectedEvent(crate::daemon::DaemonStateMachineInput),
91    #[error("failed to wait daemon, {0}")]
92    WaitDaemon(#[source] io::Error),
93
94    #[error("filesystem type mismatch, expect {0}")]
95    FsTypeMismatch(String),
96    #[error("passthroughfs failed to handle request, {0}")]
97    PassthroughFs(#[source] io::Error),
98    #[error("RAFS failed to handle request, {0}")]
99    Rafs(#[from] RafsError),
100    #[error("VFS failed to handle request, {0:?}")]
101    Vfs(#[from] VfsError),
102
103    // fusedev
104    #[error("failed to create FUSE server, {0}")]
105    CreateFuseServer(io::Error),
106    // Fuse session has been shutdown.
107    #[error("FUSE session has been shut down, {0}")]
108    SessionShutdown(FuseTransportError),
109    #[error("FUSE notify error, {0}")]
110    NotifyError(#[from] FuseNotifyError),
111    #[error("failed to walk and notify invalidation: {0}")]
112    WalkNotifyInvalidation(#[from] std::io::Error),
113
114    // virtio-fs
115    #[error("failed to handle event other than input event")]
116    HandleEventNotEpollIn,
117    #[error("failed to handle unknown event")]
118    HandleEventUnknownEvent,
119    #[error("fail to walk descriptor chain")]
120    IterateQueue,
121    #[error("invalid Virtio descriptor chain, {0}")]
122    InvalidDescriptorChain(FuseTransportError),
123    #[error("failed to process FUSE request, {0}")]
124    ProcessQueue(#[from] FuseError),
125    #[error("failed to create epoll context, {0}")]
126    Epoll(#[source] io::Error),
127    #[error("vhost-user failed to process request, {0}")]
128    VhostUser(String),
129    #[error("missing memory configuration for virtio queue")]
130    QueueMemoryUnset,
131}
132
133impl From<Error> for io::Error {
134    fn from(e: Error) -> Self {
135        einval!(e)
136    }
137}
138
139impl From<Error> for DaemonErrorKind {
140    fn from(e: Error) -> Self {
141        use Error::*;
142        match e {
143            UpgradeManager(e) => DaemonErrorKind::UpgradeManager(format!("{:?}", e)),
144            NotReady => DaemonErrorKind::NotReady,
145            Unsupported => DaemonErrorKind::Unsupported,
146            Serde(e) => DaemonErrorKind::Serde(e),
147            UnexpectedEvent(e) => DaemonErrorKind::UnexpectedEvent(format!("{:?}", e)),
148            o => DaemonErrorKind::Other(o.to_string()),
149        }
150    }
151}
152
153/// Specialized `Result` for Nydus library.
154pub type Result<T> = std::result::Result<T, Error>;
155
156#[derive(thiserror::Error, Debug)]
157pub enum FuseNotifyError {
158    #[error("Session failure error, {0}")]
159    SessionFailure(#[from] FuseTransportError),
160    #[error("Fuse write error, {0}")]
161    FuseWriteError(#[source] FuseError),
162    #[error("Sysfs file open error, {0}")]
163    SysfsOpenError(#[source] io::Error),
164    #[error("Sysfs write error, {0}")]
165    SysfsWriteError(#[source] io::Error),
166}
167
168/// Type of supported backend filesystems.
169#[derive(Clone, Debug, Serialize, PartialEq, Deserialize, Versionize)]
170pub enum FsBackendType {
171    /// Registry Accelerated File System
172    Rafs,
173    /// Share an underlying directory as a FUSE filesystem.
174    PassthroughFs,
175}
176
177impl FromStr for FsBackendType {
178    type Err = Error;
179
180    fn from_str(s: &str) -> Result<FsBackendType> {
181        match s {
182            "rafs" => Ok(FsBackendType::Rafs),
183            "passthrough" => Ok(FsBackendType::PassthroughFs),
184            "passthroughfs" => Ok(FsBackendType::PassthroughFs),
185            "passthrough_fs" => Ok(FsBackendType::PassthroughFs),
186            o => Err(Error::InvalidArguments(format!(
187                "only 'rafs' and 'passthrough_fs' are supported, but {} was specified",
188                o
189            ))),
190        }
191    }
192}
193
194impl Display for FsBackendType {
195    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196        write!(f, "{:?}", self)
197    }
198}
199
200/// Backend filesystem descriptor.
201#[derive(Serialize, Clone, Deserialize)]
202pub struct FsBackendDescriptor {
203    /// Type of backend filesystem.
204    pub backend_type: FsBackendType,
205    /// Mount point for the filesystem.
206    pub mountpoint: String,
207    /// Timestamp for the mount operation.
208    pub mounted_time: time::OffsetDateTime,
209    /// Optional configuration information for the backend filesystem.
210    pub config: Option<ConfigV2>,
211}
212
213/// Validate thread number configuration, valid range is `[1-1024]`.
214pub fn validate_threads_configuration<V: AsRef<str>>(v: V) -> std::result::Result<usize, String> {
215    if let Ok(t) = v.as_ref().parse::<usize>() {
216        if t > 0 && t <= 1024 {
217            Ok(t)
218        } else {
219            Err(format!(
220                "invalid thread number {}, valid range: [1-1024]",
221                t
222            ))
223        }
224    } else {
225        Err(format!(
226            "invalid thread number configuration: {}",
227            v.as_ref()
228        ))
229    }
230}
231
232/// Trait to get configuration options for services.
233pub trait ServiceArgs {
234    /// Get value of commandline option `key`.
235    fn value_of(&self, key: &str) -> Option<&String>;
236
237    /// Check whether commandline optio `key` is present.
238    fn is_present(&self, key: &str) -> bool;
239}
240
241#[cfg(not(target_os = "linux"))]
242mod blob_cache {
243    use super::*;
244
245    pub struct BlobCacheMgr {}
246
247    impl Default for BlobCacheMgr {
248        fn default() -> Self {
249            Self::new()
250        }
251    }
252
253    impl BlobCacheMgr {
254        pub fn new() -> Self {
255            BlobCacheMgr {}
256        }
257
258        pub fn add_blob_list(&self, _blobs: &nydus_api::BlobCacheList) -> io::Result<()> {
259            unimplemented!()
260        }
261
262        pub fn add_blob_entry(&self, _entry: &nydus_api::BlobCacheEntry) -> Result<()> {
263            unimplemented!()
264        }
265
266        pub fn remove_blob_entry(&self, _param: &nydus_api::BlobCacheObjectId) -> Result<()> {
267            unimplemented!()
268        }
269    }
270}
271
272#[cfg(test)]
273mod tests {
274    use super::*;
275
276    #[test]
277    fn test_backend_fs_type() {
278        assert_eq!(
279            FsBackendType::from_str("rafs").unwrap(),
280            FsBackendType::Rafs
281        );
282        assert_eq!(
283            FsBackendType::from_str("passthrough").unwrap(),
284            FsBackendType::PassthroughFs
285        );
286        assert_eq!(
287            FsBackendType::from_str("passthroughfs").unwrap(),
288            FsBackendType::PassthroughFs
289        );
290        assert_eq!(
291            FsBackendType::from_str("passthrough_fs").unwrap(),
292            FsBackendType::PassthroughFs
293        );
294        assert!(FsBackendType::from_str("passthroug").is_err());
295
296        assert_eq!(format!("{}", FsBackendType::Rafs), "Rafs");
297        assert_eq!(format!("{}", FsBackendType::PassthroughFs), "PassthroughFs");
298    }
299
300    #[test]
301    fn test_validate_thread_configuration() {
302        assert_eq!(validate_threads_configuration("1").unwrap(), 1);
303        assert_eq!(validate_threads_configuration("1024").unwrap(), 1024);
304        assert!(validate_threads_configuration("0").is_err());
305        assert!(validate_threads_configuration("-1").is_err());
306        assert!(validate_threads_configuration("1.0").is_err());
307        assert!(validate_threads_configuration("1025").is_err());
308        assert!(validate_threads_configuration("test").is_err());
309    }
310}