xref: /aosp_15_r20/external/crosvm/ext2/src/builder.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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 
7 use std::path::PathBuf;
8 
9 use anyhow::bail;
10 use anyhow::Context;
11 use anyhow::Result;
12 use base::MappedRegion;
13 use base::MemoryMapping;
14 use base::MemoryMappingArena;
15 use base::MemoryMappingBuilder;
16 use base::Protection;
17 use base::SharedMemory;
18 
19 use crate::arena::Arena;
20 use crate::arena::FileMappingInfo;
21 use crate::fs::Ext2;
22 use crate::BLOCK_SIZE;
23 
24 /// A struct to represent the configuration of an ext2 filesystem.
25 pub 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 
36 impl Default for Builder {
default() -> Self37     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 
47 impl Builder {
48     /// Validates field values and adjusts them if needed.
validate(&mut self) -> Result<()>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.
allocate_memory(mut self) -> Result<MemRegion>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.
build_on_shm(self, shm: &SharedMemory) -> Result<MemRegion>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.
87 pub struct MemRegion {
88     cfg: Builder,
89     mem: MemoryMapping,
90 }
91 
92 impl MemRegion {
93     /// Constructs an ext2 metadata by traversing `src_dir`.
build_mmap_info(mut self) -> Result<MemRegionWithMappingInfo>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.
116 pub struct MemRegionWithMappingInfo {
117     mem: MemoryMapping,
118     pub mapping_info: Vec<FileMappingInfo>,
119 }
120 
121 impl MemRegionWithMappingInfo {
122     /// Do mmap and returns the memory region where ext2 was created.
do_mmap(self) -> Result<MemoryMappingArena>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 }
146