ext2/
builder.rs

1// Copyright 2024 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Provides structs and logic to build ext2 file system with configurations.
6
7use std::path::PathBuf;
8
9use anyhow::bail;
10use anyhow::Context;
11use anyhow::Result;
12use base::MappedRegion;
13use base::MemoryMapping;
14use base::MemoryMappingArena;
15use base::MemoryMappingBuilder;
16use base::Protection;
17use base::SharedMemory;
18
19use crate::arena::Arena;
20use crate::arena::FileMappingInfo;
21use crate::fs::Ext2;
22use crate::BLOCK_SIZE;
23
24/// A struct to represent the configuration of an ext2 filesystem.
25pub struct Builder {
26    /// The number of blocks per group.
27    pub blocks_per_group: u32,
28    /// The number of inodes per group.
29    pub inodes_per_group: u32,
30    /// The size of the memory region.
31    pub size: u32,
32    /// The roof directory to be copied to the file system.
33    pub root_dir: Option<PathBuf>,
34}
35
36impl Default for Builder {
37    fn default() -> Self {
38        Self {
39            blocks_per_group: 4096,
40            inodes_per_group: 4096,
41            size: 4096 * 4096,
42            root_dir: None,
43        }
44    }
45}
46
47impl Builder {
48    /// Validates field values and adjusts them if needed.
49    fn validate(&mut self) -> Result<()> {
50        let block_group_size = BLOCK_SIZE as u32 * self.blocks_per_group;
51        if self.size < block_group_size {
52            bail!(
53            "memory size {} is too small to have a block group: block_size={},  block_per_group={}",
54            self.size,
55            BLOCK_SIZE,
56            block_group_size
57        );
58        }
59        if self.size % block_group_size != 0 {
60            // Round down to the largest multiple of block_group_size that is smaller than self.size
61            self.size = self.size.next_multiple_of(block_group_size) - block_group_size
62        };
63        Ok(())
64    }
65
66    /// Allocates memory region with the given configuration.
67    pub fn allocate_memory(mut self) -> Result<MemRegion> {
68        self.validate()
69            .context("failed to validate the ext2 config")?;
70        let mem = MemoryMappingBuilder::new(self.size as usize)
71            .build()
72            .context("failed to allocate memory for ext2")?;
73        Ok(MemRegion { cfg: self, mem })
74    }
75
76    /// Builds memory region on the given shared memory.
77    pub fn build_on_shm(self, shm: &SharedMemory) -> Result<MemRegion> {
78        let mem = MemoryMappingBuilder::new(shm.size() as usize)
79            .from_shared_memory(shm)
80            .build()
81            .expect("failed to build MemoryMapping from shared memory");
82        Ok(MemRegion { cfg: self, mem })
83    }
84}
85
86/// Memory region for ext2 with its config.
87pub struct MemRegion {
88    cfg: Builder,
89    mem: MemoryMapping,
90}
91
92impl MemRegion {
93    /// Constructs an ext2 metadata by traversing `src_dir`.
94    pub fn build_mmap_info(mut self) -> Result<MemRegionWithMappingInfo> {
95        let arena = Arena::new(BLOCK_SIZE, &mut self.mem).context("failed to allocate arena")?;
96        let mut ext2 = Ext2::new(&self.cfg, &arena).context("failed to create Ext2 struct")?;
97        if let Some(dir) = self.cfg.root_dir {
98            ext2.copy_dirtree(&arena, dir)
99                .context("failed to copy directory tree")?;
100        }
101        ext2.copy_backup_metadata(&arena)
102            .context("failed to copy metadata for backup")?;
103        let mapping_info = arena.into_mapping_info();
104
105        self.mem
106            .msync()
107            .context("failed to msyn of ext2's memory region")?;
108        Ok(MemRegionWithMappingInfo {
109            mem: self.mem,
110            mapping_info,
111        })
112    }
113}
114
115/// Memory regions where ext2 metadata were written with information of mmap operations to be done.
116pub struct MemRegionWithMappingInfo {
117    mem: MemoryMapping,
118    pub mapping_info: Vec<FileMappingInfo>,
119}
120
121impl MemRegionWithMappingInfo {
122    /// Do mmap and returns the memory region where ext2 was created.
123    pub fn do_mmap(self) -> Result<MemoryMappingArena> {
124        let mut mmap_arena = MemoryMappingArena::from(self.mem);
125        for FileMappingInfo {
126            mem_offset,
127            file,
128            length,
129            file_offset,
130        } in self.mapping_info
131        {
132            mmap_arena
133                .add_fd_mapping(
134                    mem_offset,
135                    length,
136                    &file,
137                    file_offset as u64, /* fd_offset */
138                    Protection::read(),
139                )
140                .context("failed mmaping an fd for ext2")?;
141        }
142
143        Ok(mmap_arena)
144    }
145}