xref: /aosp_15_r20/external/crosvm/kernel_cmdline/src/kernel_cmdline.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2017 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 //! Helper for creating valid kernel command line strings.
6*bb4ee6a4SAndroid Build Coastguard Worker 
7*bb4ee6a4SAndroid Build Coastguard Worker use std::result;
8*bb4ee6a4SAndroid Build Coastguard Worker 
9*bb4ee6a4SAndroid Build Coastguard Worker use remain::sorted;
10*bb4ee6a4SAndroid Build Coastguard Worker use thiserror::Error;
11*bb4ee6a4SAndroid Build Coastguard Worker 
12*bb4ee6a4SAndroid Build Coastguard Worker /// The error type for command line building operations.
13*bb4ee6a4SAndroid Build Coastguard Worker #[sorted]
14*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Error, PartialEq, Eq, Debug)]
15*bb4ee6a4SAndroid Build Coastguard Worker pub enum Error {
16*bb4ee6a4SAndroid Build Coastguard Worker     /// Key/Value Operation would have had an equals sign in it.
17*bb4ee6a4SAndroid Build Coastguard Worker     #[error("string contains an equals sign")]
18*bb4ee6a4SAndroid Build Coastguard Worker     HasEquals,
19*bb4ee6a4SAndroid Build Coastguard Worker     /// Key/Value Operation would have had a space in it.
20*bb4ee6a4SAndroid Build Coastguard Worker     #[error("string contains a space")]
21*bb4ee6a4SAndroid Build Coastguard Worker     HasSpace,
22*bb4ee6a4SAndroid Build Coastguard Worker     /// Operation would have resulted in a non-printable ASCII character.
23*bb4ee6a4SAndroid Build Coastguard Worker     #[error("string contains non-printable ASCII character")]
24*bb4ee6a4SAndroid Build Coastguard Worker     InvalidAscii,
25*bb4ee6a4SAndroid Build Coastguard Worker     /// Operation would have made the command line too large.
26*bb4ee6a4SAndroid Build Coastguard Worker     #[error("command line length {0} exceeds maximum {1}")]
27*bb4ee6a4SAndroid Build Coastguard Worker     TooLarge(usize, usize),
28*bb4ee6a4SAndroid Build Coastguard Worker }
29*bb4ee6a4SAndroid Build Coastguard Worker 
30*bb4ee6a4SAndroid Build Coastguard Worker /// Specialized Result type for command line operations.
31*bb4ee6a4SAndroid Build Coastguard Worker pub type Result<T> = result::Result<T, Error>;
32*bb4ee6a4SAndroid Build Coastguard Worker 
valid_char(c: char) -> bool33*bb4ee6a4SAndroid Build Coastguard Worker fn valid_char(c: char) -> bool {
34*bb4ee6a4SAndroid Build Coastguard Worker     matches!(c, ' '..='~')
35*bb4ee6a4SAndroid Build Coastguard Worker }
36*bb4ee6a4SAndroid Build Coastguard Worker 
valid_str(s: &str) -> Result<()>37*bb4ee6a4SAndroid Build Coastguard Worker fn valid_str(s: &str) -> Result<()> {
38*bb4ee6a4SAndroid Build Coastguard Worker     if s.chars().all(valid_char) {
39*bb4ee6a4SAndroid Build Coastguard Worker         Ok(())
40*bb4ee6a4SAndroid Build Coastguard Worker     } else {
41*bb4ee6a4SAndroid Build Coastguard Worker         Err(Error::InvalidAscii)
42*bb4ee6a4SAndroid Build Coastguard Worker     }
43*bb4ee6a4SAndroid Build Coastguard Worker }
44*bb4ee6a4SAndroid Build Coastguard Worker 
valid_element(s: &str) -> Result<()>45*bb4ee6a4SAndroid Build Coastguard Worker fn valid_element(s: &str) -> Result<()> {
46*bb4ee6a4SAndroid Build Coastguard Worker     if !s.chars().all(valid_char) {
47*bb4ee6a4SAndroid Build Coastguard Worker         Err(Error::InvalidAscii)
48*bb4ee6a4SAndroid Build Coastguard Worker     } else if s.contains(' ') {
49*bb4ee6a4SAndroid Build Coastguard Worker         Err(Error::HasSpace)
50*bb4ee6a4SAndroid Build Coastguard Worker     } else if s.contains('=') {
51*bb4ee6a4SAndroid Build Coastguard Worker         Err(Error::HasEquals)
52*bb4ee6a4SAndroid Build Coastguard Worker     } else {
53*bb4ee6a4SAndroid Build Coastguard Worker         Ok(())
54*bb4ee6a4SAndroid Build Coastguard Worker     }
55*bb4ee6a4SAndroid Build Coastguard Worker }
56*bb4ee6a4SAndroid Build Coastguard Worker 
57*bb4ee6a4SAndroid Build Coastguard Worker /// A builder for a kernel command line string that validates the string as it is built.
58*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Default)]
59*bb4ee6a4SAndroid Build Coastguard Worker pub struct Cmdline {
60*bb4ee6a4SAndroid Build Coastguard Worker     line: String,
61*bb4ee6a4SAndroid Build Coastguard Worker }
62*bb4ee6a4SAndroid Build Coastguard Worker 
63*bb4ee6a4SAndroid Build Coastguard Worker impl Cmdline {
64*bb4ee6a4SAndroid Build Coastguard Worker     /// Constructs an empty Cmdline.
new() -> Cmdline65*bb4ee6a4SAndroid Build Coastguard Worker     pub fn new() -> Cmdline {
66*bb4ee6a4SAndroid Build Coastguard Worker         Cmdline::default()
67*bb4ee6a4SAndroid Build Coastguard Worker     }
68*bb4ee6a4SAndroid Build Coastguard Worker 
push_space_if_needed(&mut self)69*bb4ee6a4SAndroid Build Coastguard Worker     fn push_space_if_needed(&mut self) {
70*bb4ee6a4SAndroid Build Coastguard Worker         if !self.line.is_empty() {
71*bb4ee6a4SAndroid Build Coastguard Worker             self.line.push(' ');
72*bb4ee6a4SAndroid Build Coastguard Worker         }
73*bb4ee6a4SAndroid Build Coastguard Worker     }
74*bb4ee6a4SAndroid Build Coastguard Worker 
75*bb4ee6a4SAndroid Build Coastguard Worker     /// Validates and inserts a key value pair into this command line
insert<T: AsRef<str>>(&mut self, key: T, val: T) -> Result<()>76*bb4ee6a4SAndroid Build Coastguard Worker     pub fn insert<T: AsRef<str>>(&mut self, key: T, val: T) -> Result<()> {
77*bb4ee6a4SAndroid Build Coastguard Worker         let k = key.as_ref();
78*bb4ee6a4SAndroid Build Coastguard Worker         let v = val.as_ref();
79*bb4ee6a4SAndroid Build Coastguard Worker 
80*bb4ee6a4SAndroid Build Coastguard Worker         valid_element(k)?;
81*bb4ee6a4SAndroid Build Coastguard Worker         valid_element(v)?;
82*bb4ee6a4SAndroid Build Coastguard Worker 
83*bb4ee6a4SAndroid Build Coastguard Worker         self.push_space_if_needed();
84*bb4ee6a4SAndroid Build Coastguard Worker         self.line.push_str(k);
85*bb4ee6a4SAndroid Build Coastguard Worker         self.line.push('=');
86*bb4ee6a4SAndroid Build Coastguard Worker         self.line.push_str(v);
87*bb4ee6a4SAndroid Build Coastguard Worker 
88*bb4ee6a4SAndroid Build Coastguard Worker         Ok(())
89*bb4ee6a4SAndroid Build Coastguard Worker     }
90*bb4ee6a4SAndroid Build Coastguard Worker 
91*bb4ee6a4SAndroid Build Coastguard Worker     /// Validates and inserts a string to the end of the current command line
insert_str<T: AsRef<str>>(&mut self, slug: T) -> Result<()>92*bb4ee6a4SAndroid Build Coastguard Worker     pub fn insert_str<T: AsRef<str>>(&mut self, slug: T) -> Result<()> {
93*bb4ee6a4SAndroid Build Coastguard Worker         let s = slug.as_ref();
94*bb4ee6a4SAndroid Build Coastguard Worker         valid_str(s)?;
95*bb4ee6a4SAndroid Build Coastguard Worker 
96*bb4ee6a4SAndroid Build Coastguard Worker         self.push_space_if_needed();
97*bb4ee6a4SAndroid Build Coastguard Worker         self.line.push_str(s);
98*bb4ee6a4SAndroid Build Coastguard Worker 
99*bb4ee6a4SAndroid Build Coastguard Worker         Ok(())
100*bb4ee6a4SAndroid Build Coastguard Worker     }
101*bb4ee6a4SAndroid Build Coastguard Worker 
102*bb4ee6a4SAndroid Build Coastguard Worker     /// Returns the cmdline in progress without nul termination
as_str(&self) -> &str103*bb4ee6a4SAndroid Build Coastguard Worker     pub fn as_str(&self) -> &str {
104*bb4ee6a4SAndroid Build Coastguard Worker         self.line.as_str()
105*bb4ee6a4SAndroid Build Coastguard Worker     }
106*bb4ee6a4SAndroid Build Coastguard Worker 
107*bb4ee6a4SAndroid Build Coastguard Worker     /// Returns the current command line as a string with a maximum length.
108*bb4ee6a4SAndroid Build Coastguard Worker     ///
109*bb4ee6a4SAndroid Build Coastguard Worker     /// # Arguments
110*bb4ee6a4SAndroid Build Coastguard Worker     ///
111*bb4ee6a4SAndroid Build Coastguard Worker     /// `max_len`: maximum number of bytes (not including NUL terminator)
as_str_with_max_len(&self, max_len: usize) -> Result<&str>112*bb4ee6a4SAndroid Build Coastguard Worker     pub fn as_str_with_max_len(&self, max_len: usize) -> Result<&str> {
113*bb4ee6a4SAndroid Build Coastguard Worker         let s = self.line.as_str();
114*bb4ee6a4SAndroid Build Coastguard Worker         if s.len() <= max_len {
115*bb4ee6a4SAndroid Build Coastguard Worker             Ok(s)
116*bb4ee6a4SAndroid Build Coastguard Worker         } else {
117*bb4ee6a4SAndroid Build Coastguard Worker             Err(Error::TooLarge(s.len(), max_len))
118*bb4ee6a4SAndroid Build Coastguard Worker         }
119*bb4ee6a4SAndroid Build Coastguard Worker     }
120*bb4ee6a4SAndroid Build Coastguard Worker 
121*bb4ee6a4SAndroid Build Coastguard Worker     /// Converts the command line into a `Vec<u8>` with a maximum length.
122*bb4ee6a4SAndroid Build Coastguard Worker     ///
123*bb4ee6a4SAndroid Build Coastguard Worker     /// # Arguments
124*bb4ee6a4SAndroid Build Coastguard Worker     ///
125*bb4ee6a4SAndroid Build Coastguard Worker     /// `max_len`: maximum number of bytes (not including NUL terminator)
into_bytes_with_max_len(self, max_len: usize) -> Result<Vec<u8>>126*bb4ee6a4SAndroid Build Coastguard Worker     pub fn into_bytes_with_max_len(self, max_len: usize) -> Result<Vec<u8>> {
127*bb4ee6a4SAndroid Build Coastguard Worker         let bytes: Vec<u8> = self.line.into_bytes();
128*bb4ee6a4SAndroid Build Coastguard Worker         if bytes.len() <= max_len {
129*bb4ee6a4SAndroid Build Coastguard Worker             Ok(bytes)
130*bb4ee6a4SAndroid Build Coastguard Worker         } else {
131*bb4ee6a4SAndroid Build Coastguard Worker             Err(Error::TooLarge(bytes.len(), max_len))
132*bb4ee6a4SAndroid Build Coastguard Worker         }
133*bb4ee6a4SAndroid Build Coastguard Worker     }
134*bb4ee6a4SAndroid Build Coastguard Worker }
135*bb4ee6a4SAndroid Build Coastguard Worker 
136*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(test)]
137*bb4ee6a4SAndroid Build Coastguard Worker mod tests {
138*bb4ee6a4SAndroid Build Coastguard Worker     use super::*;
139*bb4ee6a4SAndroid Build Coastguard Worker 
140*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
insert_hello_world()141*bb4ee6a4SAndroid Build Coastguard Worker     fn insert_hello_world() {
142*bb4ee6a4SAndroid Build Coastguard Worker         let mut cl = Cmdline::new();
143*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(cl.as_str(), "");
144*bb4ee6a4SAndroid Build Coastguard Worker         assert!(cl.insert("hello", "world").is_ok());
145*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(cl.as_str(), "hello=world");
146*bb4ee6a4SAndroid Build Coastguard Worker 
147*bb4ee6a4SAndroid Build Coastguard Worker         let bytes = cl
148*bb4ee6a4SAndroid Build Coastguard Worker             .into_bytes_with_max_len(100)
149*bb4ee6a4SAndroid Build Coastguard Worker             .expect("failed to convert Cmdline into bytes");
150*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(bytes, b"hello=world");
151*bb4ee6a4SAndroid Build Coastguard Worker     }
152*bb4ee6a4SAndroid Build Coastguard Worker 
153*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
insert_multi()154*bb4ee6a4SAndroid Build Coastguard Worker     fn insert_multi() {
155*bb4ee6a4SAndroid Build Coastguard Worker         let mut cl = Cmdline::new();
156*bb4ee6a4SAndroid Build Coastguard Worker         assert!(cl.insert("hello", "world").is_ok());
157*bb4ee6a4SAndroid Build Coastguard Worker         assert!(cl.insert("foo", "bar").is_ok());
158*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(cl.as_str(), "hello=world foo=bar");
159*bb4ee6a4SAndroid Build Coastguard Worker     }
160*bb4ee6a4SAndroid Build Coastguard Worker 
161*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
insert_space()162*bb4ee6a4SAndroid Build Coastguard Worker     fn insert_space() {
163*bb4ee6a4SAndroid Build Coastguard Worker         let mut cl = Cmdline::new();
164*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(cl.insert("a ", "b"), Err(Error::HasSpace));
165*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(cl.insert("a", "b "), Err(Error::HasSpace));
166*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(cl.insert("a ", "b "), Err(Error::HasSpace));
167*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(cl.insert(" a", "b"), Err(Error::HasSpace));
168*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(cl.as_str(), "");
169*bb4ee6a4SAndroid Build Coastguard Worker     }
170*bb4ee6a4SAndroid Build Coastguard Worker 
171*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
insert_equals()172*bb4ee6a4SAndroid Build Coastguard Worker     fn insert_equals() {
173*bb4ee6a4SAndroid Build Coastguard Worker         let mut cl = Cmdline::new();
174*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(cl.insert("a=", "b"), Err(Error::HasEquals));
175*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(cl.insert("a", "b="), Err(Error::HasEquals));
176*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(cl.insert("a=", "b "), Err(Error::HasEquals));
177*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(cl.insert("=a", "b"), Err(Error::HasEquals));
178*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(cl.insert("a", "=b"), Err(Error::HasEquals));
179*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(cl.as_str(), "");
180*bb4ee6a4SAndroid Build Coastguard Worker     }
181*bb4ee6a4SAndroid Build Coastguard Worker 
182*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
insert_emoji()183*bb4ee6a4SAndroid Build Coastguard Worker     fn insert_emoji() {
184*bb4ee6a4SAndroid Build Coastguard Worker         let mut cl = Cmdline::new();
185*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(cl.insert("heart", "��"), Err(Error::InvalidAscii));
186*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(cl.insert("��", "love"), Err(Error::InvalidAscii));
187*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(cl.as_str(), "");
188*bb4ee6a4SAndroid Build Coastguard Worker     }
189*bb4ee6a4SAndroid Build Coastguard Worker 
190*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
insert_string()191*bb4ee6a4SAndroid Build Coastguard Worker     fn insert_string() {
192*bb4ee6a4SAndroid Build Coastguard Worker         let mut cl = Cmdline::new();
193*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(cl.as_str(), "");
194*bb4ee6a4SAndroid Build Coastguard Worker         assert!(cl.insert_str("noapic").is_ok());
195*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(cl.as_str(), "noapic");
196*bb4ee6a4SAndroid Build Coastguard Worker         assert!(cl.insert_str("nopci").is_ok());
197*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(cl.as_str(), "noapic nopci");
198*bb4ee6a4SAndroid Build Coastguard Worker     }
199*bb4ee6a4SAndroid Build Coastguard Worker 
200*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
as_str_too_large()201*bb4ee6a4SAndroid Build Coastguard Worker     fn as_str_too_large() {
202*bb4ee6a4SAndroid Build Coastguard Worker         let mut cl = Cmdline::new();
203*bb4ee6a4SAndroid Build Coastguard Worker         assert!(cl.insert("a", "b").is_ok()); // start off with 3.
204*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(cl.as_str(), "a=b");
205*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(cl.as_str_with_max_len(2), Err(Error::TooLarge(3, 2)));
206*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(cl.as_str_with_max_len(3), Ok("a=b"));
207*bb4ee6a4SAndroid Build Coastguard Worker 
208*bb4ee6a4SAndroid Build Coastguard Worker         let mut cl = Cmdline::new();
209*bb4ee6a4SAndroid Build Coastguard Worker         assert!(cl.insert("ab", "ba").is_ok()); // adds 5 length
210*bb4ee6a4SAndroid Build Coastguard Worker         assert!(cl.insert("c", "d").is_ok()); // adds 4 (including space) length
211*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(cl.as_str(), "ab=ba c=d");
212*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(cl.as_str_with_max_len(8), Err(Error::TooLarge(9, 8)));
213*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(cl.as_str_with_max_len(9), Ok("ab=ba c=d"));
214*bb4ee6a4SAndroid Build Coastguard Worker 
215*bb4ee6a4SAndroid Build Coastguard Worker         let mut cl = Cmdline::new();
216*bb4ee6a4SAndroid Build Coastguard Worker         assert!(cl.insert("ab", "ba").is_ok()); // adds 5 length
217*bb4ee6a4SAndroid Build Coastguard Worker         assert!(cl.insert_str("123").is_ok()); // adds 4 (including space) length
218*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(cl.as_str(), "ab=ba 123");
219*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(cl.as_str_with_max_len(8), Err(Error::TooLarge(9, 8)));
220*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(cl.as_str_with_max_len(9), Ok("ab=ba 123"));
221*bb4ee6a4SAndroid Build Coastguard Worker     }
222*bb4ee6a4SAndroid Build Coastguard Worker }
223