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