crosvm/crosvm/sys/linux/
ext2.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 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
25use std::path::Path;
26
27use anyhow::Context;
28use anyhow::Result;
29use base::error;
30use base::AsRawDescriptor;
31use base::Pid;
32use base::SharedMemory;
33use base::Tube;
34use jail::create_base_minijail;
35use jail::create_sandbox_minijail;
36use jail::fork_process;
37use jail::JailConfig;
38use jail::RunAsUser;
39use jail::SandboxConfig;
40use vm_control::api::VmMemoryClient;
41use vm_control::VmMemoryFileMapping;
42use vm_memory::GuestAddress;
43
44/// Starts a process to create an ext2 filesystem on a given shared memory region.
45pub 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.
101fn 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}