fs_management/
lib.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Library for filesystem management in rust.
6//!
7//! This library is analogous to the fs-management library in zircon. It provides support for
8//! formatting, mounting, unmounting, and fsck-ing. It is implemented in a similar way to the C++
9//! version.  For components v2, add `/svc/fuchsia.process.Launcher` to `use` and add the
10//! binaries as dependencies to your component.
11
12mod error;
13pub mod filesystem;
14pub mod format;
15pub mod partition;
16
17use crate::filesystem::BlockConnector;
18use fidl_fuchsia_fs_startup::{
19    CompressionAlgorithm, EvictionPolicyOverride, FormatOptions, StartOptions,
20};
21use std::convert::From;
22use std::sync::Arc;
23
24// Re-export errors as public.
25pub use error::{QueryError, ShutdownError};
26
27pub const BLOBFS_TYPE_GUID: [u8; 16] = [
28    0x0e, 0x38, 0x67, 0x29, 0x4c, 0x13, 0xbb, 0x4c, 0xb6, 0xda, 0x17, 0xe7, 0xce, 0x1c, 0xa4, 0x5d,
29];
30pub const DATA_TYPE_GUID: [u8; 16] = [
31    0x0c, 0x5f, 0x18, 0x08, 0x2d, 0x89, 0x8a, 0x42, 0xa7, 0x89, 0xdb, 0xee, 0xc8, 0xf5, 0x5e, 0x6a,
32];
33pub const FVM_TYPE_GUID: [u8; 16] = [
34    0xb8, 0x7c, 0xfd, 0x49, 0x15, 0xdf, 0x73, 0x4e, 0xb9, 0xd9, 0x99, 0x20, 0x70, 0x12, 0x7f, 0x0f,
35];
36
37pub const FVM_TYPE_GUID_STR: &str = "49fd7cb8-df15-4e73-b9d9-992070127f0f";
38
39pub const FS_COLLECTION_NAME: &'static str = "fs-collection";
40
41#[derive(Clone)]
42pub enum ComponentType {
43    /// Launch the filesystem as a static child, using the configured name in the options as the
44    /// child name. If the child doesn't exist, this will fail.
45    StaticChild,
46
47    /// Launch the filesystem as a dynamic child, in the configured collection. By default, the
48    /// collection is "fs-collection".
49    DynamicChild { collection_name: String },
50}
51
52impl Default for ComponentType {
53    fn default() -> Self {
54        ComponentType::DynamicChild { collection_name: "fs-collection".to_string() }
55    }
56}
57
58pub struct Options<'a> {
59    /// For static children, the name specifies the name of the child.  For dynamic children, the
60    /// component URL is "fuchsia-boot:///{component-name}#meta/{component-name}.cm" or
61    /// "#meta/{component-name}.cm".  The library will attempt to connect to a static child first,
62    /// and if that fails, it will launch the filesystem within a collection. It will try to
63    /// create a child component via the absolute URL and then fallback to the relative URL.
64    pub component_name: &'a str,
65
66    /// It should be possible to reuse components after serving them, but it's not universally
67    /// supported.
68    pub reuse_component_after_serving: bool,
69
70    /// Format options as defined by the startup protocol
71    pub format_options: FormatOptions,
72
73    /// Start options as defined by the startup protocol
74    pub start_options: StartOptions,
75
76    /// Whether to launch this filesystem as a dynamic or static child.
77    pub component_type: ComponentType,
78}
79
80/// Describes the configuration for a particular filesystem.
81pub trait FSConfig: Send + Sync + 'static {
82    /// Returns the options specifying how to run this filesystem.
83    fn options(&self) -> Options<'_>;
84
85    /// Whether the filesystem supports multiple volumes.
86    fn is_multi_volume(&self) -> bool {
87        false
88    }
89
90    fn disk_format(&self) -> format::DiskFormat {
91        format::DiskFormat::Unknown
92    }
93}
94
95///
96/// FILESYSTEMS
97///
98
99/// Layout of blobs in blobfs
100#[derive(Clone)]
101pub enum BlobLayout {
102    /// Merkle tree is stored in a separate block. This is deprecated and used only on Astro
103    /// devices (it takes more space).
104    DeprecatedPadded,
105
106    /// Merkle tree is appended to the last block of data
107    Compact,
108}
109
110/// Compression used for blobs in blobfs
111#[derive(Clone, Default)]
112pub enum BlobCompression {
113    #[default]
114    ZSTDChunked,
115    Uncompressed,
116}
117
118impl From<&str> for BlobCompression {
119    fn from(value: &str) -> Self {
120        match value {
121            "zstd_chunked" => Self::ZSTDChunked,
122            "uncompressed" => Self::Uncompressed,
123            _ => Default::default(),
124        }
125    }
126}
127
128/// Eviction policy used for blobs in blobfs
129#[derive(Clone, Default)]
130pub enum BlobEvictionPolicy {
131    #[default]
132    NeverEvict,
133    EvictImmediately,
134}
135
136impl From<&str> for BlobEvictionPolicy {
137    fn from(value: &str) -> Self {
138        match value {
139            "never_evict" => Self::NeverEvict,
140            "evict_immediately" => Self::EvictImmediately,
141            _ => Default::default(),
142        }
143    }
144}
145
146/// Blobfs Filesystem Configuration
147/// If fields are None or false, they will not be set in arguments.
148#[derive(Clone, Default)]
149pub struct Blobfs {
150    // Format options
151    pub verbose: bool,
152    pub deprecated_padded_blobfs_format: bool,
153    pub num_inodes: u64,
154    // Start Options
155    pub readonly: bool,
156    pub write_compression_algorithm: BlobCompression,
157    pub write_compression_level: Option<i32>,
158    pub cache_eviction_policy_override: BlobEvictionPolicy,
159    pub component_type: ComponentType,
160}
161
162impl Blobfs {
163    /// Manages a block device using the default configuration.
164    pub fn new<B: BlockConnector + 'static>(block_connector: B) -> filesystem::Filesystem {
165        filesystem::Filesystem::new(block_connector, Self::default())
166    }
167
168    /// Launch blobfs, with the default configuration, as a dynamic child in the fs-collection.
169    pub fn dynamic_child() -> Self {
170        Self {
171            component_type: ComponentType::DynamicChild {
172                collection_name: FS_COLLECTION_NAME.to_string(),
173            },
174            ..Default::default()
175        }
176    }
177}
178
179impl FSConfig for Blobfs {
180    fn options(&self) -> Options<'_> {
181        Options {
182            component_name: "blobfs",
183            reuse_component_after_serving: false,
184            format_options: FormatOptions {
185                verbose: Some(self.verbose),
186                deprecated_padded_blobfs_format: Some(self.deprecated_padded_blobfs_format),
187                num_inodes: if self.num_inodes > 0 { Some(self.num_inodes) } else { None },
188                ..Default::default()
189            },
190            start_options: {
191                let mut start_options = StartOptions {
192                    read_only: Some(self.readonly),
193                    verbose: Some(self.verbose),
194                    write_compression_level: Some(self.write_compression_level.unwrap_or(-1)),
195                    write_compression_algorithm: Some(CompressionAlgorithm::ZstdChunked),
196                    cache_eviction_policy_override: Some(EvictionPolicyOverride::None),
197                    ..Default::default()
198                };
199                start_options.write_compression_algorithm =
200                    Some(match &self.write_compression_algorithm {
201                        BlobCompression::ZSTDChunked => CompressionAlgorithm::ZstdChunked,
202                        BlobCompression::Uncompressed => CompressionAlgorithm::Uncompressed,
203                    });
204                start_options.cache_eviction_policy_override =
205                    Some(match &self.cache_eviction_policy_override {
206                        BlobEvictionPolicy::NeverEvict => EvictionPolicyOverride::NeverEvict,
207                        BlobEvictionPolicy::EvictImmediately => {
208                            EvictionPolicyOverride::EvictImmediately
209                        }
210                    });
211                start_options
212            },
213            component_type: self.component_type.clone(),
214        }
215    }
216
217    fn disk_format(&self) -> format::DiskFormat {
218        format::DiskFormat::Blobfs
219    }
220}
221
222/// Minfs Filesystem Configuration
223/// If fields are None or false, they will not be set in arguments.
224#[derive(Clone, Default)]
225pub struct Minfs {
226    // TODO(xbhatnag): Add support for fvm_data_slices
227    // Format options
228    pub verbose: bool,
229    pub fvm_data_slices: u32,
230    // Start Options
231    pub readonly: bool,
232    pub fsck_after_every_transaction: bool,
233    pub component_type: ComponentType,
234}
235
236impl Minfs {
237    /// Manages a block device using the default configuration.
238    pub fn new<B: BlockConnector + 'static>(block_connector: B) -> filesystem::Filesystem {
239        filesystem::Filesystem::new(block_connector, Self::default())
240    }
241
242    /// Launch minfs, with the default configuration, as a dynamic child in the fs-collection.
243    pub fn dynamic_child() -> Self {
244        Self {
245            component_type: ComponentType::DynamicChild {
246                collection_name: FS_COLLECTION_NAME.to_string(),
247            },
248            ..Default::default()
249        }
250    }
251}
252
253impl FSConfig for Minfs {
254    fn options(&self) -> Options<'_> {
255        Options {
256            component_name: "minfs",
257            reuse_component_after_serving: false,
258            format_options: FormatOptions {
259                verbose: Some(self.verbose),
260                fvm_data_slices: Some(self.fvm_data_slices),
261                ..Default::default()
262            },
263            start_options: StartOptions {
264                read_only: Some(self.readonly),
265                verbose: Some(self.verbose),
266                fsck_after_every_transaction: Some(self.fsck_after_every_transaction),
267                ..Default::default()
268            },
269            component_type: self.component_type.clone(),
270        }
271    }
272
273    fn disk_format(&self) -> format::DiskFormat {
274        format::DiskFormat::Minfs
275    }
276}
277
278pub type CryptClientFn = Arc<dyn Fn() -> zx::Channel + Send + Sync>;
279
280/// Fxfs Filesystem Configuration
281#[derive(Clone)]
282pub struct Fxfs {
283    // Start Options
284    pub readonly: bool,
285    pub fsck_after_every_transaction: bool,
286    pub component_type: ComponentType,
287    pub startup_profiling_seconds: Option<u32>,
288    pub inline_crypto_enabled: bool,
289    pub barriers_enabled: bool,
290}
291
292impl Default for Fxfs {
293    fn default() -> Self {
294        Self {
295            readonly: false,
296            fsck_after_every_transaction: false,
297            component_type: Default::default(),
298            startup_profiling_seconds: None,
299            inline_crypto_enabled: false,
300            barriers_enabled: false,
301        }
302    }
303}
304
305impl Fxfs {
306    /// Manages a block device using the default configuration.
307    pub fn new<B: BlockConnector + 'static>(block_connector: B) -> filesystem::Filesystem {
308        filesystem::Filesystem::new(block_connector, Self::default())
309    }
310
311    /// Launch Fxfs, with the default configuration, as a dynamic child in the fs-collection.
312    pub fn dynamic_child() -> Self {
313        Self {
314            component_type: ComponentType::DynamicChild {
315                collection_name: FS_COLLECTION_NAME.to_string(),
316            },
317            ..Default::default()
318        }
319    }
320}
321
322impl FSConfig for Fxfs {
323    fn options(&self) -> Options<'_> {
324        Options {
325            component_name: "fxfs",
326            reuse_component_after_serving: true,
327            format_options: FormatOptions { verbose: Some(false), ..Default::default() },
328            start_options: StartOptions {
329                read_only: Some(self.readonly),
330                fsck_after_every_transaction: Some(self.fsck_after_every_transaction),
331                startup_profiling_seconds: Some(self.startup_profiling_seconds.unwrap_or(0)),
332                inline_crypto_enabled: Some(self.inline_crypto_enabled),
333                barriers_enabled: Some(self.barriers_enabled),
334                ..Default::default()
335            },
336            component_type: self.component_type.clone(),
337        }
338    }
339
340    fn is_multi_volume(&self) -> bool {
341        true
342    }
343
344    fn disk_format(&self) -> format::DiskFormat {
345        format::DiskFormat::Fxfs
346    }
347}
348
349/// F2fs Filesystem Configuration
350/// If fields are None or false, they will not be set in arguments.
351#[derive(Clone, Default)]
352pub struct F2fs {
353    pub component_type: ComponentType,
354}
355
356impl F2fs {
357    /// Manages a block device using the default configuration.
358    pub fn new<B: BlockConnector + 'static>(block_connector: B) -> filesystem::Filesystem {
359        filesystem::Filesystem::new(block_connector, Self::default())
360    }
361
362    /// Launch f2fs, with the default configuration, as a dynamic child in the fs-collection.
363    pub fn dynamic_child() -> Self {
364        Self {
365            component_type: ComponentType::DynamicChild {
366                collection_name: FS_COLLECTION_NAME.to_string(),
367            },
368            ..Default::default()
369        }
370    }
371}
372
373impl FSConfig for F2fs {
374    fn options(&self) -> Options<'_> {
375        Options {
376            component_name: "f2fs",
377            reuse_component_after_serving: false,
378            format_options: FormatOptions::default(),
379            start_options: StartOptions {
380                read_only: Some(false),
381                verbose: Some(false),
382                fsck_after_every_transaction: Some(false),
383                ..Default::default()
384            },
385            component_type: self.component_type.clone(),
386        }
387    }
388    fn is_multi_volume(&self) -> bool {
389        false
390    }
391
392    fn disk_format(&self) -> format::DiskFormat {
393        format::DiskFormat::F2fs
394    }
395}
396
397/// FvmFilesystem Configuration
398#[derive(Clone)]
399pub struct Fvm {
400    pub component_type: ComponentType,
401}
402
403impl Default for Fvm {
404    fn default() -> Self {
405        Self { component_type: Default::default() }
406    }
407}
408
409impl Fvm {
410    /// Manages a block device using the default configuration.
411    pub fn new<B: BlockConnector + 'static>(block_connector: B) -> filesystem::Filesystem {
412        filesystem::Filesystem::new(block_connector, Self::default())
413    }
414
415    /// Launch Fvm, with the default configuration, as a dynamic child in the fs-collection.
416    pub fn dynamic_child() -> Self {
417        Self {
418            component_type: ComponentType::DynamicChild {
419                collection_name: FS_COLLECTION_NAME.to_string(),
420            },
421            ..Default::default()
422        }
423    }
424}
425
426impl FSConfig for Fvm {
427    fn options(&self) -> Options<'_> {
428        Options {
429            component_name: "fvm2",
430            reuse_component_after_serving: true,
431            format_options: FormatOptions::default(),
432            start_options: StartOptions::default(),
433            component_type: self.component_type.clone(),
434        }
435    }
436
437    fn is_multi_volume(&self) -> bool {
438        true
439    }
440
441    fn disk_format(&self) -> format::DiskFormat {
442        format::DiskFormat::Fvm
443    }
444}
445
446/// Gpt Configuration
447#[derive(Clone)]
448pub struct Gpt {
449    pub component_type: ComponentType,
450}
451
452impl Default for Gpt {
453    fn default() -> Self {
454        Self { component_type: Default::default() }
455    }
456}
457
458impl Gpt {
459    /// Manages a block device using the default configuration.
460    pub fn new<B: BlockConnector + 'static>(block_connector: B) -> filesystem::Filesystem {
461        filesystem::Filesystem::new(block_connector, Self::default())
462    }
463
464    /// Launch Gpt, with the default configuration, as a dynamic child in the fs-collection.
465    pub fn dynamic_child() -> Self {
466        Self {
467            component_type: ComponentType::DynamicChild {
468                collection_name: FS_COLLECTION_NAME.to_string(),
469            },
470            ..Default::default()
471        }
472    }
473}
474
475impl FSConfig for Gpt {
476    fn options(&self) -> Options<'_> {
477        Options {
478            component_name: "gpt2",
479            reuse_component_after_serving: true,
480            format_options: FormatOptions::default(),
481            start_options: StartOptions::default(),
482            component_type: self.component_type.clone(),
483        }
484    }
485
486    fn is_multi_volume(&self) -> bool {
487        true
488    }
489
490    fn disk_format(&self) -> format::DiskFormat {
491        format::DiskFormat::Gpt
492    }
493}