Skip to main content

starnix_kernel_runner/
mounts.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use anyhow::{Error, anyhow, bail};
6use fidl_fuchsia_io as fio;
7use starnix_core::fs::fuchsia::{RemoteBundle, new_remotefs_in_root};
8use starnix_core::fs::tmpfs::TmpFs;
9use starnix_core::task::{CurrentTask, Kernel};
10use starnix_core::vfs::fs_args::MountParams;
11use starnix_core::vfs::{FileSystemHandle, FileSystemOptions, FsString};
12
13use starnix_uapi::mount_flags::{MountFlags, MountpointFlags};
14
15pub struct MountAction {
16    pub path: FsString,
17    pub fs: FileSystemHandle,
18    pub flags: MountpointFlags,
19}
20
21impl MountAction {
22    pub fn new_for_root(
23        kernel: &Kernel,
24        pkg: &fio::DirectorySynchronousProxy,
25        spec: &str,
26    ) -> Result<MountAction, Error> {
27        let (spec, options) = MountSpec::parse(spec)?;
28        assert_eq!(spec.mount_point.as_slice(), b"/");
29        let rights = fio::PERM_READABLE | fio::PERM_EXECUTABLE;
30
31        // We only support mounting these file systems at the root.
32        // The root file system needs to be creatable without a task because we mount the root
33        // file system before creating the initial task.
34        let fs = match spec.fs_type.as_slice() {
35            b"remote_bundle" => RemoteBundle::new_fs_in_base(kernel, pkg, options, rights)?,
36            b"remote_pkg_subdir" => new_remotefs_in_root(kernel, pkg, options, rights)?,
37            b"tmpfs" => TmpFs::new_fs_with_options(kernel, options)?,
38            _ => bail!("unsupported root file system: {}", spec.fs_type),
39        };
40
41        Ok(spec.into_action(fs))
42    }
43
44    pub fn from_spec(
45        current_task: &CurrentTask,
46        pkg: &fio::DirectorySynchronousProxy,
47        spec: &str,
48    ) -> Result<MountAction, Error> {
49        let (spec, options) = MountSpec::parse(spec)?;
50        let rights = fio::PERM_READABLE | fio::PERM_EXECUTABLE;
51
52        let fs = match spec.fs_type.as_slice() {
53            // The remote_bundle file system is available only via the mounts declaration in CML.
54            b"remote_bundle" => {
55                RemoteBundle::new_fs_in_base(current_task.kernel(), pkg, options, rights)?
56            }
57
58            // Mounts a subdirectory of the container's `/pkg`.
59            b"remote_pkg_subdir" => {
60                new_remotefs_in_root(current_task.kernel(), pkg, options, rights)?
61            }
62
63            _ => current_task.create_filesystem(spec.fs_type.as_ref(), options)?,
64        };
65
66        Ok(spec.into_action(fs))
67    }
68}
69
70struct MountSpec {
71    mount_point: FsString,
72    fs_type: FsString,
73    flags: MountFlags,
74}
75
76impl MountSpec {
77    fn parse(spec: &str) -> Result<(MountSpec, FileSystemOptions), Error> {
78        let mut iter = spec.splitn(4, ':');
79        let mount_point =
80            iter.next().ok_or_else(|| anyhow!("mount point is missing from {:?}", spec))?;
81        let fs_type = iter.next().ok_or_else(|| anyhow!("fs type is missing from {:?}", spec))?;
82        let fs_src = match iter.next() {
83            Some(src) if !src.is_empty() => src,
84            _ => ".",
85        };
86
87        let mut params = MountParams::parse(iter.next().unwrap_or_default().into())?;
88        let flags = params.remove_mount_flags();
89
90        Ok((
91            MountSpec { fs_type: fs_type.into(), mount_point: mount_point.into(), flags },
92            FileSystemOptions {
93                source: fs_src.into(),
94                flags: flags.file_system_flags().into(),
95                params,
96            },
97        ))
98    }
99
100    fn into_action(self, fs: FileSystemHandle) -> MountAction {
101        MountAction { path: self.mount_point, fs, flags: self.flags.mountpoint_flags() }
102    }
103}