starnix_kernel_runner/
mounts.rs1use 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};
12use starnix_logging::log_info;
13
14use starnix_uapi::mount_flags::{MountFlags, MountpointFlags};
15
16pub struct MountAction {
17 pub path: FsString,
18 pub fs: FileSystemHandle,
19 pub flags: MountpointFlags,
20}
21
22impl MountAction {
23 pub fn new_for_root(
24 kernel: &Kernel,
25 pkg: &fio::DirectorySynchronousProxy,
26 spec: &str,
27 ) -> Result<MountAction, Error> {
28 let (spec, options) = MountSpec::parse(spec)?;
29 assert_eq!(spec.mount_point.as_slice(), b"/");
30 let rights = fio::PERM_READABLE | fio::PERM_EXECUTABLE;
31
32 let fs = match spec.fs_type.as_slice() {
36 b"remote_bundle" => RemoteBundle::new_fs_in_base(kernel, pkg, options, rights)?,
37 b"remote_pkg_subdir" => new_remotefs_in_root(kernel, pkg, options, rights)?,
38 b"tmpfs" => TmpFs::new_fs_with_options(kernel, options)?,
39 _ => bail!("unsupported root file system: {}", spec.fs_type),
40 };
41
42 Ok(spec.into_action(fs))
43 }
44
45 pub fn from_spec(
46 current_task: &CurrentTask,
47 pkg: &fio::DirectorySynchronousProxy,
48 spec_str: &str,
49 ) -> Result<Option<MountAction>, Error> {
50 let (spec, options) = MountSpec::parse(spec_str)?;
51 let rights = fio::PERM_READABLE | fio::PERM_EXECUTABLE;
52
53 let fs = match spec.fs_type.as_slice() {
54 b"remote_bundle" => {
56 RemoteBundle::new_fs_in_base(current_task.kernel(), pkg, options, rights)
57 }
58
59 b"remote_pkg_subdir" => {
61 new_remotefs_in_root(current_task.kernel(), pkg, options, rights)
62 .map_err(Error::from)
63 }
64
65 _ => {
66 current_task.create_filesystem(spec.fs_type.as_ref(), options).map_err(Error::from)
67 }
68 };
69
70 let fs = match fs {
71 Ok(fs) => fs,
72 Err(err) if spec.nofail => {
73 log_info!("Skipping mount {spec_str:?} due to nofail: {err:#}");
74 return Ok(None);
75 }
76 Err(err) => return Err(err),
77 };
78
79 Ok(Some(spec.into_action(fs)))
80 }
81}
82
83struct MountSpec {
84 mount_point: FsString,
85 fs_type: FsString,
86 flags: MountFlags,
87 nofail: bool,
88}
89
90impl MountSpec {
91 fn parse(spec: &str) -> Result<(MountSpec, FileSystemOptions), Error> {
92 let mut iter = spec.splitn(4, ':');
93 let mount_point =
94 iter.next().ok_or_else(|| anyhow!("mount point is missing from {:?}", spec))?;
95 let fs_type = iter.next().ok_or_else(|| anyhow!("fs type is missing from {:?}", spec))?;
96 let fs_src = match iter.next() {
97 Some(src) if !src.is_empty() => src,
98 _ => ".",
99 };
100
101 let mut params = MountParams::parse(iter.next().unwrap_or_default().into())?;
102 let flags = params.remove_mount_flags();
103 let nofail = params.remove(b"nofail").is_some();
104
105 Ok((
106 MountSpec { fs_type: fs_type.into(), mount_point: mount_point.into(), flags, nofail },
107 FileSystemOptions {
108 source: fs_src.into(),
109 flags: flags.file_system_flags().into(),
110 params,
111 },
112 ))
113 }
114
115 fn into_action(self, fs: FileSystemHandle) -> MountAction {
116 MountAction { path: self.mount_point, fs, flags: self.flags.mountpoint_flags() }
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123
124 #[::fuchsia::test]
125 fn test_parse_mount_spec_nofail() {
126 let (spec, options) =
127 MountSpec::parse("/product:remote_bundle:data/product:nodev,relatime,nofail").unwrap();
128 assert_eq!(spec.mount_point, "/product");
129 assert_eq!(spec.fs_type, "remote_bundle");
130 assert_eq!(spec.flags, MountFlags::NODEV | MountFlags::RELATIME);
131 assert!(spec.nofail);
132 assert_eq!(options.source, "data/product");
133 assert!(options.params.is_empty());
134
135 let (spec, options) =
136 MountSpec::parse("/vendor:remote_bundle:data/vendor:nodev,relatime").unwrap();
137 assert_eq!(spec.mount_point, "/vendor");
138 assert_eq!(spec.fs_type, "remote_bundle");
139 assert_eq!(spec.flags, MountFlags::NODEV | MountFlags::RELATIME);
140 assert!(!spec.nofail);
141 assert_eq!(options.source, "data/vendor");
142 assert!(options.params.is_empty());
143 }
144}