xref: /aosp_15_r20/external/crosvm/src/crosvm/plugin/config.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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