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