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