1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2023 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker
5*bb4ee6a4SAndroid Build Coastguard Worker //! plugin configuration options
6*bb4ee6a4SAndroid Build Coastguard Worker
7*bb4ee6a4SAndroid Build Coastguard Worker use std::path::PathBuf;
8*bb4ee6a4SAndroid Build Coastguard Worker use std::str::FromStr;
9*bb4ee6a4SAndroid Build Coastguard Worker
10*bb4ee6a4SAndroid Build Coastguard Worker use serde::Deserialize;
11*bb4ee6a4SAndroid Build Coastguard Worker use serde::Serialize;
12*bb4ee6a4SAndroid Build Coastguard Worker
13*bb4ee6a4SAndroid Build Coastguard Worker use crate::crosvm::config::invalid_value_err;
14*bb4ee6a4SAndroid Build Coastguard Worker
15*bb4ee6a4SAndroid Build Coastguard Worker /// A bind mount for directories in the plugin process.
16*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Debug, Serialize, Deserialize)]
17*bb4ee6a4SAndroid Build Coastguard Worker pub struct BindMount {
18*bb4ee6a4SAndroid Build Coastguard Worker pub src: PathBuf,
19*bb4ee6a4SAndroid Build Coastguard Worker pub dst: PathBuf,
20*bb4ee6a4SAndroid Build Coastguard Worker pub writable: bool,
21*bb4ee6a4SAndroid Build Coastguard Worker }
22*bb4ee6a4SAndroid Build Coastguard Worker
23*bb4ee6a4SAndroid Build Coastguard Worker impl FromStr for BindMount {
24*bb4ee6a4SAndroid Build Coastguard Worker type Err = String;
25*bb4ee6a4SAndroid Build Coastguard Worker
from_str(value: &str) -> Result<Self, Self::Err>26*bb4ee6a4SAndroid Build Coastguard Worker fn from_str(value: &str) -> Result<Self, Self::Err> {
27*bb4ee6a4SAndroid Build Coastguard Worker let components: Vec<&str> = value.split(':').collect();
28*bb4ee6a4SAndroid Build Coastguard Worker if components.is_empty() || components.len() > 3 || components[0].is_empty() {
29*bb4ee6a4SAndroid Build Coastguard Worker return Err(invalid_value_err(
30*bb4ee6a4SAndroid Build Coastguard Worker value,
31*bb4ee6a4SAndroid Build Coastguard Worker "`plugin-mount` should be in a form of: <src>[:[<dst>][:<writable>]]",
32*bb4ee6a4SAndroid Build Coastguard Worker ));
33*bb4ee6a4SAndroid Build Coastguard Worker }
34*bb4ee6a4SAndroid Build Coastguard Worker
35*bb4ee6a4SAndroid Build Coastguard Worker let src = PathBuf::from(components[0]);
36*bb4ee6a4SAndroid Build Coastguard Worker if src.is_relative() {
37*bb4ee6a4SAndroid Build Coastguard Worker return Err(invalid_value_err(
38*bb4ee6a4SAndroid Build Coastguard Worker components[0],
39*bb4ee6a4SAndroid Build Coastguard Worker "the source path for `plugin-mount` must be absolute",
40*bb4ee6a4SAndroid Build Coastguard Worker ));
41*bb4ee6a4SAndroid Build Coastguard Worker }
42*bb4ee6a4SAndroid Build Coastguard Worker if !src.exists() {
43*bb4ee6a4SAndroid Build Coastguard Worker return Err(invalid_value_err(
44*bb4ee6a4SAndroid Build Coastguard Worker components[0],
45*bb4ee6a4SAndroid Build Coastguard Worker "the source path for `plugin-mount` does not exist",
46*bb4ee6a4SAndroid Build Coastguard Worker ));
47*bb4ee6a4SAndroid Build Coastguard Worker }
48*bb4ee6a4SAndroid Build Coastguard Worker
49*bb4ee6a4SAndroid Build Coastguard Worker let dst = PathBuf::from(match components.get(1) {
50*bb4ee6a4SAndroid Build Coastguard Worker None | Some(&"") => components[0],
51*bb4ee6a4SAndroid Build Coastguard Worker Some(path) => path,
52*bb4ee6a4SAndroid Build Coastguard Worker });
53*bb4ee6a4SAndroid Build Coastguard Worker if dst.is_relative() {
54*bb4ee6a4SAndroid Build Coastguard Worker return Err(invalid_value_err(
55*bb4ee6a4SAndroid Build Coastguard Worker components[1],
56*bb4ee6a4SAndroid Build Coastguard Worker "the destination path for `plugin-mount` must be absolute",
57*bb4ee6a4SAndroid Build Coastguard Worker ));
58*bb4ee6a4SAndroid Build Coastguard Worker }
59*bb4ee6a4SAndroid Build Coastguard Worker
60*bb4ee6a4SAndroid Build Coastguard Worker let writable: bool = match components.get(2) {
61*bb4ee6a4SAndroid Build Coastguard Worker None => false,
62*bb4ee6a4SAndroid Build Coastguard Worker Some(s) => s.parse().map_err(|_| {
63*bb4ee6a4SAndroid Build Coastguard Worker invalid_value_err(
64*bb4ee6a4SAndroid Build Coastguard Worker components[2],
65*bb4ee6a4SAndroid Build Coastguard Worker "the <writable> component for `plugin-mount` is not valid bool",
66*bb4ee6a4SAndroid Build Coastguard Worker )
67*bb4ee6a4SAndroid Build Coastguard Worker })?,
68*bb4ee6a4SAndroid Build Coastguard Worker };
69*bb4ee6a4SAndroid Build Coastguard Worker
70*bb4ee6a4SAndroid Build Coastguard Worker Ok(BindMount { src, dst, writable })
71*bb4ee6a4SAndroid Build Coastguard Worker }
72*bb4ee6a4SAndroid Build Coastguard Worker }
73*bb4ee6a4SAndroid Build Coastguard Worker
74*bb4ee6a4SAndroid Build Coastguard Worker /// A mapping of linux group IDs for the plugin process.
75*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Debug, Deserialize, Serialize)]
76*bb4ee6a4SAndroid Build Coastguard Worker pub struct GidMap {
77*bb4ee6a4SAndroid Build Coastguard Worker pub inner: base::Gid,
78*bb4ee6a4SAndroid Build Coastguard Worker pub outer: base::Gid,
79*bb4ee6a4SAndroid Build Coastguard Worker pub count: u32,
80*bb4ee6a4SAndroid Build Coastguard Worker }
81*bb4ee6a4SAndroid Build Coastguard Worker
82*bb4ee6a4SAndroid Build Coastguard Worker impl FromStr for GidMap {
83*bb4ee6a4SAndroid Build Coastguard Worker type Err = String;
84*bb4ee6a4SAndroid Build Coastguard Worker
from_str(value: &str) -> Result<Self, Self::Err>85*bb4ee6a4SAndroid Build Coastguard Worker fn from_str(value: &str) -> Result<Self, Self::Err> {
86*bb4ee6a4SAndroid Build Coastguard Worker let components: Vec<&str> = value.split(':').collect();
87*bb4ee6a4SAndroid Build Coastguard Worker if components.is_empty() || components.len() > 3 || components[0].is_empty() {
88*bb4ee6a4SAndroid Build Coastguard Worker return Err(invalid_value_err(
89*bb4ee6a4SAndroid Build Coastguard Worker value,
90*bb4ee6a4SAndroid Build Coastguard Worker "`plugin-gid-map` must have exactly 3 components: <inner>[:[<outer>][:<count>]]",
91*bb4ee6a4SAndroid Build Coastguard Worker ));
92*bb4ee6a4SAndroid Build Coastguard Worker }
93*bb4ee6a4SAndroid Build Coastguard Worker
94*bb4ee6a4SAndroid Build Coastguard Worker let inner: base::Gid = components[0].parse().map_err(|_| {
95*bb4ee6a4SAndroid Build Coastguard Worker invalid_value_err(
96*bb4ee6a4SAndroid Build Coastguard Worker components[0],
97*bb4ee6a4SAndroid Build Coastguard Worker "the <inner> component for `plugin-gid-map` is not valid gid",
98*bb4ee6a4SAndroid Build Coastguard Worker )
99*bb4ee6a4SAndroid Build Coastguard Worker })?;
100*bb4ee6a4SAndroid Build Coastguard Worker
101*bb4ee6a4SAndroid Build Coastguard Worker let outer: base::Gid = match components.get(1) {
102*bb4ee6a4SAndroid Build Coastguard Worker None | Some(&"") => inner,
103*bb4ee6a4SAndroid Build Coastguard Worker Some(s) => s.parse().map_err(|_| {
104*bb4ee6a4SAndroid Build Coastguard Worker invalid_value_err(
105*bb4ee6a4SAndroid Build Coastguard Worker components[1],
106*bb4ee6a4SAndroid Build Coastguard Worker "the <outer> component for `plugin-gid-map` is not valid gid",
107*bb4ee6a4SAndroid Build Coastguard Worker )
108*bb4ee6a4SAndroid Build Coastguard Worker })?,
109*bb4ee6a4SAndroid Build Coastguard Worker };
110*bb4ee6a4SAndroid Build Coastguard Worker
111*bb4ee6a4SAndroid Build Coastguard Worker let count: u32 = match components.get(2) {
112*bb4ee6a4SAndroid Build Coastguard Worker None => 1,
113*bb4ee6a4SAndroid Build Coastguard Worker Some(s) => s.parse().map_err(|_| {
114*bb4ee6a4SAndroid Build Coastguard Worker invalid_value_err(
115*bb4ee6a4SAndroid Build Coastguard Worker components[2],
116*bb4ee6a4SAndroid Build Coastguard Worker "the <count> component for `plugin-gid-map` is not valid number",
117*bb4ee6a4SAndroid Build Coastguard Worker )
118*bb4ee6a4SAndroid Build Coastguard Worker })?,
119*bb4ee6a4SAndroid Build Coastguard Worker };
120*bb4ee6a4SAndroid Build Coastguard Worker
121*bb4ee6a4SAndroid Build Coastguard Worker Ok(GidMap {
122*bb4ee6a4SAndroid Build Coastguard Worker inner,
123*bb4ee6a4SAndroid Build Coastguard Worker outer,
124*bb4ee6a4SAndroid Build Coastguard Worker count,
125*bb4ee6a4SAndroid Build Coastguard Worker })
126*bb4ee6a4SAndroid Build Coastguard Worker }
127*bb4ee6a4SAndroid Build Coastguard Worker }
128*bb4ee6a4SAndroid Build Coastguard Worker
parse_plugin_mount_option(value: &str) -> Result<BindMount, String>129*bb4ee6a4SAndroid Build Coastguard Worker pub fn parse_plugin_mount_option(value: &str) -> Result<BindMount, String> {
130*bb4ee6a4SAndroid Build Coastguard Worker let components: Vec<&str> = value.split(':').collect();
131*bb4ee6a4SAndroid Build Coastguard Worker if components.is_empty() || components.len() > 3 || components[0].is_empty() {
132*bb4ee6a4SAndroid Build Coastguard Worker return Err(invalid_value_err(
133*bb4ee6a4SAndroid Build Coastguard Worker value,
134*bb4ee6a4SAndroid Build Coastguard Worker "`plugin-mount` should be in a form of: <src>[:[<dst>][:<writable>]]",
135*bb4ee6a4SAndroid Build Coastguard Worker ));
136*bb4ee6a4SAndroid Build Coastguard Worker }
137*bb4ee6a4SAndroid Build Coastguard Worker
138*bb4ee6a4SAndroid Build Coastguard Worker let src = PathBuf::from(components[0]);
139*bb4ee6a4SAndroid Build Coastguard Worker if src.is_relative() {
140*bb4ee6a4SAndroid Build Coastguard Worker return Err(invalid_value_err(
141*bb4ee6a4SAndroid Build Coastguard Worker components[0],
142*bb4ee6a4SAndroid Build Coastguard Worker "the source path for `plugin-mount` must be absolute",
143*bb4ee6a4SAndroid Build Coastguard Worker ));
144*bb4ee6a4SAndroid Build Coastguard Worker }
145*bb4ee6a4SAndroid Build Coastguard Worker if !src.exists() {
146*bb4ee6a4SAndroid Build Coastguard Worker return Err(invalid_value_err(
147*bb4ee6a4SAndroid Build Coastguard Worker components[0],
148*bb4ee6a4SAndroid Build Coastguard Worker "the source path for `plugin-mount` does not exist",
149*bb4ee6a4SAndroid Build Coastguard Worker ));
150*bb4ee6a4SAndroid Build Coastguard Worker }
151*bb4ee6a4SAndroid Build Coastguard Worker
152*bb4ee6a4SAndroid Build Coastguard Worker let dst = PathBuf::from(match components.get(1) {
153*bb4ee6a4SAndroid Build Coastguard Worker None | Some(&"") => components[0],
154*bb4ee6a4SAndroid Build Coastguard Worker Some(path) => path,
155*bb4ee6a4SAndroid Build Coastguard Worker });
156*bb4ee6a4SAndroid Build Coastguard Worker if dst.is_relative() {
157*bb4ee6a4SAndroid Build Coastguard Worker return Err(invalid_value_err(
158*bb4ee6a4SAndroid Build Coastguard Worker components[1],
159*bb4ee6a4SAndroid Build Coastguard Worker "the destination path for `plugin-mount` must be absolute",
160*bb4ee6a4SAndroid Build Coastguard Worker ));
161*bb4ee6a4SAndroid Build Coastguard Worker }
162*bb4ee6a4SAndroid Build Coastguard Worker
163*bb4ee6a4SAndroid Build Coastguard Worker let writable: bool = match components.get(2) {
164*bb4ee6a4SAndroid Build Coastguard Worker None => false,
165*bb4ee6a4SAndroid Build Coastguard Worker Some(s) => s.parse().map_err(|_| {
166*bb4ee6a4SAndroid Build Coastguard Worker invalid_value_err(
167*bb4ee6a4SAndroid Build Coastguard Worker components[2],
168*bb4ee6a4SAndroid Build Coastguard Worker "the <writable> component for `plugin-mount` is not valid bool",
169*bb4ee6a4SAndroid Build Coastguard Worker )
170*bb4ee6a4SAndroid Build Coastguard Worker })?,
171*bb4ee6a4SAndroid Build Coastguard Worker };
172*bb4ee6a4SAndroid Build Coastguard Worker
173*bb4ee6a4SAndroid Build Coastguard Worker Ok(BindMount { src, dst, writable })
174*bb4ee6a4SAndroid Build Coastguard Worker }
175*bb4ee6a4SAndroid Build Coastguard Worker
176*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(test)]
177*bb4ee6a4SAndroid Build Coastguard Worker mod tests {
178*bb4ee6a4SAndroid Build Coastguard Worker use super::*;
179*bb4ee6a4SAndroid Build Coastguard Worker
180*bb4ee6a4SAndroid Build Coastguard Worker #[test]
parse_plugin_mount_invalid()181*bb4ee6a4SAndroid Build Coastguard Worker fn parse_plugin_mount_invalid() {
182*bb4ee6a4SAndroid Build Coastguard Worker "".parse::<BindMount>().expect_err("parse should fail");
183*bb4ee6a4SAndroid Build Coastguard Worker "/dev/null:/dev/null:true:false"
184*bb4ee6a4SAndroid Build Coastguard Worker .parse::<BindMount>()
185*bb4ee6a4SAndroid Build Coastguard Worker .expect_err("parse should fail because too many arguments");
186*bb4ee6a4SAndroid Build Coastguard Worker
187*bb4ee6a4SAndroid Build Coastguard Worker "null:/dev/null:true"
188*bb4ee6a4SAndroid Build Coastguard Worker .parse::<BindMount>()
189*bb4ee6a4SAndroid Build Coastguard Worker .expect_err("parse should fail because source is not absolute");
190*bb4ee6a4SAndroid Build Coastguard Worker "/dev/null:null:true"
191*bb4ee6a4SAndroid Build Coastguard Worker .parse::<BindMount>()
192*bb4ee6a4SAndroid Build Coastguard Worker .expect_err("parse should fail because source is not absolute");
193*bb4ee6a4SAndroid Build Coastguard Worker "/dev/null:null:blah"
194*bb4ee6a4SAndroid Build Coastguard Worker .parse::<BindMount>()
195*bb4ee6a4SAndroid Build Coastguard Worker .expect_err("parse should fail because flag is not boolean");
196*bb4ee6a4SAndroid Build Coastguard Worker }
197*bb4ee6a4SAndroid Build Coastguard Worker
198*bb4ee6a4SAndroid Build Coastguard Worker #[test]
parse_plugin_mount_valid()199*bb4ee6a4SAndroid Build Coastguard Worker fn parse_plugin_mount_valid() {
200*bb4ee6a4SAndroid Build Coastguard Worker let opt: BindMount = "/dev/null:/dev/zero:true".parse().unwrap();
201*bb4ee6a4SAndroid Build Coastguard Worker
202*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(opt.src, PathBuf::from("/dev/null"));
203*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(opt.dst, PathBuf::from("/dev/zero"));
204*bb4ee6a4SAndroid Build Coastguard Worker assert!(opt.writable);
205*bb4ee6a4SAndroid Build Coastguard Worker }
206*bb4ee6a4SAndroid Build Coastguard Worker
207*bb4ee6a4SAndroid Build Coastguard Worker #[test]
parse_plugin_mount_valid_shorthand()208*bb4ee6a4SAndroid Build Coastguard Worker fn parse_plugin_mount_valid_shorthand() {
209*bb4ee6a4SAndroid Build Coastguard Worker let opt: BindMount = "/dev/null".parse().unwrap();
210*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(opt.dst, PathBuf::from("/dev/null"));
211*bb4ee6a4SAndroid Build Coastguard Worker assert!(!opt.writable);
212*bb4ee6a4SAndroid Build Coastguard Worker
213*bb4ee6a4SAndroid Build Coastguard Worker let opt: BindMount = "/dev/null:/dev/zero".parse().unwrap();
214*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(opt.dst, PathBuf::from("/dev/zero"));
215*bb4ee6a4SAndroid Build Coastguard Worker assert!(!opt.writable);
216*bb4ee6a4SAndroid Build Coastguard Worker
217*bb4ee6a4SAndroid Build Coastguard Worker let opt: BindMount = "/dev/null::true".parse().unwrap();
218*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(opt.dst, PathBuf::from("/dev/null"));
219*bb4ee6a4SAndroid Build Coastguard Worker assert!(opt.writable);
220*bb4ee6a4SAndroid Build Coastguard Worker }
221*bb4ee6a4SAndroid Build Coastguard Worker
222*bb4ee6a4SAndroid Build Coastguard Worker #[test]
parse_plugin_gid_map_valid()223*bb4ee6a4SAndroid Build Coastguard Worker fn parse_plugin_gid_map_valid() {
224*bb4ee6a4SAndroid Build Coastguard Worker let opt: GidMap = "1:2:3".parse().expect("parse should succeed");
225*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(opt.inner, 1);
226*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(opt.outer, 2);
227*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(opt.count, 3);
228*bb4ee6a4SAndroid Build Coastguard Worker }
229*bb4ee6a4SAndroid Build Coastguard Worker
230*bb4ee6a4SAndroid Build Coastguard Worker #[test]
parse_plugin_gid_map_valid_shorthand()231*bb4ee6a4SAndroid Build Coastguard Worker fn parse_plugin_gid_map_valid_shorthand() {
232*bb4ee6a4SAndroid Build Coastguard Worker let opt: GidMap = "1".parse().expect("parse should succeed");
233*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(opt.inner, 1);
234*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(opt.outer, 1);
235*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(opt.count, 1);
236*bb4ee6a4SAndroid Build Coastguard Worker
237*bb4ee6a4SAndroid Build Coastguard Worker let opt: GidMap = "1:2".parse().expect("parse should succeed");
238*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(opt.inner, 1);
239*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(opt.outer, 2);
240*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(opt.count, 1);
241*bb4ee6a4SAndroid Build Coastguard Worker
242*bb4ee6a4SAndroid Build Coastguard Worker let opt: GidMap = "1::3".parse().expect("parse should succeed");
243*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(opt.inner, 1);
244*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(opt.outer, 1);
245*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(opt.count, 3);
246*bb4ee6a4SAndroid Build Coastguard Worker }
247*bb4ee6a4SAndroid Build Coastguard Worker
248*bb4ee6a4SAndroid Build Coastguard Worker #[test]
parse_plugin_gid_map_invalid()249*bb4ee6a4SAndroid Build Coastguard Worker fn parse_plugin_gid_map_invalid() {
250*bb4ee6a4SAndroid Build Coastguard Worker "".parse::<GidMap>().expect_err("parse should fail");
251*bb4ee6a4SAndroid Build Coastguard Worker "1:2:3:4"
252*bb4ee6a4SAndroid Build Coastguard Worker .parse::<GidMap>()
253*bb4ee6a4SAndroid Build Coastguard Worker .expect_err("parse should fail because too many arguments");
254*bb4ee6a4SAndroid Build Coastguard Worker "blah:2:3"
255*bb4ee6a4SAndroid Build Coastguard Worker .parse::<GidMap>()
256*bb4ee6a4SAndroid Build Coastguard Worker .expect_err("parse should fail because inner is not a number");
257*bb4ee6a4SAndroid Build Coastguard Worker "1:blah:3"
258*bb4ee6a4SAndroid Build Coastguard Worker .parse::<GidMap>()
259*bb4ee6a4SAndroid Build Coastguard Worker .expect_err("parse should fail because outer is not a number");
260*bb4ee6a4SAndroid Build Coastguard Worker "1:2:blah"
261*bb4ee6a4SAndroid Build Coastguard Worker .parse::<GidMap>()
262*bb4ee6a4SAndroid Build Coastguard Worker .expect_err("parse should fail because count is not a number");
263*bb4ee6a4SAndroid Build Coastguard Worker }
264*bb4ee6a4SAndroid Build Coastguard Worker }
265