xref: /aosp_15_r20/external/crosvm/src/crosvm/sys/linux/ext2.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 a function to lanunches a process of creating ext2 filesystem on memory region
6 //! asynchronously for pmem-ext2 device.
7 //!
8 //! The ext2 file system is created in the memory area for pmem by the following three processes:
9 //! (a). The main process
10 //! (b). ext2 process launched by the `launch()` below.
11 //! (c). The virtio-pmem process
12 //!
13 //! By executing mkfs in the multiple processes, mkfs won't block other initalization steps. Also,
14 //! we can use different seccopm poliy for (b) and (c).
15 //!
16 //! The overall workflow is like the followings:
17 //! 1. At (a): `launch()` is called from (a)
18 //! 2. At (a): (b) is foked from (a) in `launch()`
19 //! 3. At (b): The given directory is traversed and metadata is constructed.
20 //! 4. At (b): File descriptors are sent to (a) with `VmMemoryRequest::MmapAndRegisterMemory`.
21 //! 5. At (a): mmap() for the file descriptors are called. The reply is sent to (b).
22 //! 6. At (b): memory slot number is sent to (c).
23 //! 7. At (c): device activation finished.
24 
25 use std::path::Path;
26 
27 use anyhow::Context;
28 use anyhow::Result;
29 use base::error;
30 use base::AsRawDescriptor;
31 use base::Pid;
32 use base::SharedMemory;
33 use base::Tube;
34 use jail::create_base_minijail;
35 use jail::create_sandbox_minijail;
36 use jail::fork_process;
37 use jail::JailConfig;
38 use jail::RunAsUser;
39 use jail::SandboxConfig;
40 use vm_control::api::VmMemoryClient;
41 use vm_control::VmMemoryFileMapping;
42 use vm_memory::GuestAddress;
43 
44 /// Starts a process to create an ext2 filesystem on a given shared memory region.
launch( mapping_address: GuestAddress, vm_memory_client: VmMemoryClient, device_tube: Tube, path: &Path, ugid: &(Option<u32>, Option<u32>), ugid_map: (&str, &str), mut builder: ext2::Builder, jail_config: &Option<JailConfig>, ) -> Result<Pid>45 pub fn launch(
46     mapping_address: GuestAddress,
47     vm_memory_client: VmMemoryClient,
48     device_tube: Tube, // Connects to a virtio device to send a memory slot number.
49     path: &Path,
50     ugid: &(Option<u32>, Option<u32>),
51     ugid_map: (&str, &str),
52     mut builder: ext2::Builder,
53     jail_config: &Option<JailConfig>,
54 ) -> Result<Pid> {
55     let max_open_files = base::linux::max_open_files()
56         .context("failed to get max number of open files")?
57         .rlim_max;
58 
59     let jail = if let Some(jail_config) = jail_config {
60         let mut config = SandboxConfig::new(jail_config, "virtual_ext2");
61         config.limit_caps = false;
62         config.ugid_map = Some(ugid_map);
63         // We want bind mounts from the parent namespaces to propagate into the mkfs's
64         // namespace.
65         config.remount_mode = Some(libc::MS_SLAVE);
66         config.run_as = match *ugid {
67             (None, None) => RunAsUser::Unspecified,
68             (uid_opt, gid_opt) => RunAsUser::Specified(uid_opt.unwrap_or(0), gid_opt.unwrap_or(0)),
69         };
70         create_sandbox_minijail(path, max_open_files, &config)?
71     } else {
72         create_base_minijail(path, max_open_files)?
73     };
74 
75     // Use "/" in the new mount namespace as the root for mkfs.
76     builder.root_dir = Some(std::path::PathBuf::from("/"));
77 
78     let shm = SharedMemory::new("pmem_ext2_shm", builder.size as u64)
79         .context("failed to create shared memory")?;
80     let mut keep_rds = vec![
81         shm.as_raw_descriptor(),
82         vm_memory_client.as_raw_descriptor(),
83         device_tube.as_raw_descriptor(),
84     ];
85     base::syslog::push_descriptors(&mut keep_rds);
86 
87     let child_process = fork_process(jail, keep_rds, Some(String::from("mkfs process")), || {
88         if let Err(e) = mkfs_callback(vm_memory_client, mapping_address, device_tube, builder, shm)
89         {
90             error!("failed to create file system: {:#}", e);
91             // SAFETY: exit() is trivially safe.
92             unsafe { libc::exit(1) };
93         }
94     })
95     .context("failed to fork a process for mkfs")?;
96     Ok(child_process.pid)
97 }
98 
99 /// A callback to create a ext2 file system on `shm`.
100 /// This is supposed to be run in a jailed child process so operations are sandboxed and limited.
mkfs_callback( mem_client: VmMemoryClient, mapping_address: GuestAddress, device_tube: Tube, builder: ext2::Builder, shm: SharedMemory, ) -> Result<()>101 fn mkfs_callback(
102     mem_client: VmMemoryClient,
103     mapping_address: GuestAddress,
104     device_tube: Tube, // Connects to a virtio device to send a memory slot number.
105     builder: ext2::Builder,
106     shm: SharedMemory,
107 ) -> Result<()> {
108     let file_mappings = builder
109         .build_on_shm(&shm)
110         .context("failed to build memory region")?
111         .build_mmap_info()
112         .context("failed to build ext2")?
113         .mapping_info;
114 
115     let file_mapping_info: Vec<_> = file_mappings
116         .into_iter()
117         .map(|info| VmMemoryFileMapping {
118             file: info.file,
119             length: info.length,
120             mem_offset: info.mem_offset,
121             file_offset: info.file_offset as u64,
122         })
123         .collect();
124 
125     let slot = mem_client
126         .mmap_and_register_memory(mapping_address, shm, file_mapping_info)
127         .context("failed to request mmaping and registering memory")?;
128     device_tube
129         .send(&slot)
130         .context("failed to send VmMemoryRequest::RegisterMemory")?;
131     Ok(())
132 }
133