aster_forge_cloud_files_linux/
lib.rs

1//! Product-neutral Linux FUSE bindings for Forge cloud-files core.
2//!
3//! This crate owns stable inode/generation mappings, directory-handle snapshots, file-handle
4//! revision fences, direct-I/O writeback, durable namespace mutation acceptance, remote-change
5//! overlays, kernel invalidation plans, mount-generation recovery, FUSE reply mapping, and bounded
6//! callback-to-async dispatch. Product crates retain identity allocation, backend adapters,
7//! durable inode/content storage, change cursors, remote workers, daemon/service packaging, mount
8//! UX, authentication, permissions, and user-visible errors.
9#![cfg_attr(
10    not(test),
11    deny(
12        clippy::unwrap_used,
13        clippy::unreachable,
14        clippy::expect_used,
15        clippy::panic,
16        clippy::unimplemented,
17        clippy::todo
18    )
19)]
20
21mod dispatch;
22mod engine;
23mod error;
24mod inode;
25mod namespace;
26mod remote;
27mod writeback;
28
29#[cfg(target_os = "linux")]
30mod native;
31
32pub use dispatch::{
33    LinuxDispatchMetrics, LinuxDispatchRejection, LinuxDispatchReservation, LinuxRequestDispatcher,
34};
35pub use engine::{
36    LinuxAttributePolicy, LinuxDirectoryEntry, LinuxDirectoryHandle, LinuxDirectorySnapshot,
37    LinuxFileAttributes, LinuxFileHandle, LinuxNode, LinuxNodeKind, LinuxReadOnlyEngine,
38    validate_linux_name,
39};
40pub use error::{
41    LinuxCloudFilesError, LinuxErrorCode, Result, backend_error_code, namespace_store_error_code,
42    writeback_store_error_code,
43};
44pub use inode::{
45    LINUX_ROOT_INODE, LinuxInode, LinuxInodeGeneration, LinuxInodeRecord, LinuxInodeTable,
46};
47pub use namespace::{
48    LinuxCreateDirectoryRequest, LinuxCreateFileAcceptance, LinuxCreateFileRequest,
49    LinuxCreatedFile, LinuxNamespaceItem, LinuxNamespaceMutationStore,
50    LinuxNamespaceMutationStoreError, LinuxNamespaceMutationStoreErrorKind, LinuxNamespaceOverlay,
51    LinuxNamespaceStoreResult, LinuxNamespaceTombstone, LinuxRemoveRequest, LinuxRenameAcceptance,
52    LinuxRenameDestination, LinuxRenameRequest,
53};
54#[cfg(target_os = "linux")]
55pub use native::{
56    LinuxBackgroundSession, LinuxKernelNotifier, LinuxReadOnlyFilesystem, LinuxWritableFilesystem,
57    mount_read_only, mount_writable, spawn_mount_read_only, spawn_mount_writable,
58};
59pub use remote::{
60    LinuxInvalidation, LinuxRemoteChange, LinuxRemoteDelete, LinuxRemoteEntry, LinuxRemoteLocation,
61    LinuxRemoteUpsert,
62};
63pub use writeback::{
64    LinuxFileAccess, LinuxWritableEngine, LinuxWriteCommit, LinuxWriteOpenRequest,
65    LinuxWriteSession, LinuxWriteSessionId, LinuxWritebackStore,
66};
67
68/// Native mount-session configuration kept independent from product daemon policy.
69#[derive(Debug, Clone, PartialEq, Eq)]
70pub struct LinuxMountConfig {
71    filesystem_name: String,
72    subtype: Option<String>,
73    kernel_threads: Option<usize>,
74    clone_fd: bool,
75}
76
77impl LinuxMountConfig {
78    /// Creates a read-only mount configuration with one fuser event-loop thread.
79    /// # Errors
80    ///
81    /// Returns an error when validation fails or an underlying backend, store, or platform
82    /// operation fails.
83    pub fn new(filesystem_name: impl Into<String>) -> Result<Self> {
84        let filesystem_name = filesystem_name.into();
85        validate_mount_text(&filesystem_name, "filesystem name")?;
86        Ok(Self {
87            filesystem_name,
88            subtype: None,
89            kernel_threads: None,
90            clone_fd: false,
91        })
92    }
93
94    /// Sets the optional FUSE filesystem subtype.
95    /// # Errors
96    ///
97    /// Returns an error when validation fails or an underlying backend, store, or platform
98    /// operation fails.
99    pub fn with_subtype(mut self, subtype: impl Into<String>) -> Result<Self> {
100        let subtype = subtype.into();
101        validate_mount_text(&subtype, "filesystem subtype")?;
102        self.subtype = Some(subtype);
103        Ok(self)
104    }
105
106    /// Sets native event-loop concurrency and Linux `FUSE_DEV_IOC_CLONE` use.
107    /// # Errors
108    ///
109    /// Returns an error when validation fails or an underlying backend, store, or platform
110    /// operation fails.
111    pub fn with_kernel_threads(mut self, threads: usize, clone_fd: bool) -> Result<Self> {
112        if threads == 0 {
113            return Err(LinuxCloudFilesError::InvalidConfiguration {
114                reason: "kernel event-loop thread count must be greater than zero",
115            });
116        }
117        self.kernel_threads = Some(threads);
118        self.clone_fd = clone_fd;
119        Ok(self)
120    }
121
122    /// Returns the filesystem name reported by the mount.
123    #[must_use]
124    pub fn filesystem_name(&self) -> &str {
125        &self.filesystem_name
126    }
127
128    /// Returns the optional filesystem subtype.
129    #[must_use]
130    pub fn subtype(&self) -> Option<&str> {
131        self.subtype.as_deref()
132    }
133
134    /// Returns native fuser event-loop concurrency.
135    #[must_use]
136    pub const fn kernel_threads(&self) -> Option<usize> {
137        self.kernel_threads
138    }
139
140    /// Returns whether Linux should clone the FUSE device fd for event-loop workers.
141    #[must_use]
142    pub const fn clone_fd(&self) -> bool {
143        self.clone_fd
144    }
145}
146
147fn validate_mount_text(value: &str, _field: &'static str) -> Result<()> {
148    if value.is_empty() {
149        return Err(LinuxCloudFilesError::InvalidConfiguration {
150            reason: "mount identity text must not be empty",
151        });
152    }
153    if value.contains('\0') || value.contains(',') {
154        return Err(LinuxCloudFilesError::InvalidConfiguration {
155            reason: "mount identity text must not contain NUL or comma",
156        });
157    }
158    Ok(())
159}