Skip to main content

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