Skip to content

Commit 25dffe8

Browse files
committed
kernel_cmdline: Add parameter manipulation methods
Add `add_or_modify` and `remove` methods to `Cmdline` in both `bytes` and `utf8` modules, along with unit tests. Closes: #1596 Signed-off-by: John Eckersberg <[email protected]>
1 parent d947ed1 commit 25dffe8

File tree

2 files changed

+191
-0
lines changed

2 files changed

+191
-0
lines changed

crates/kernel_cmdline/src/bytes.rs

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,78 @@ impl<'a> Cmdline<'a> {
128128
anyhow::anyhow!("Failed to find kernel argument '{key}'")
129129
})
130130
}
131+
132+
/// Add or modify a parameter to the command line
133+
///
134+
/// Returns `true` if the parameter was added or modified.
135+
///
136+
/// Returns `false` if the parameter already existed with the same
137+
/// content.
138+
pub fn add_or_modify(&mut self, param: Parameter) -> bool {
139+
let mut new_params = Vec::new();
140+
let mut modified = false;
141+
let mut seen_key = false;
142+
143+
for p in self.iter() {
144+
if p.key == param.key {
145+
if !seen_key {
146+
// This is the first time we've seen this key.
147+
// We will replace it with the new parameter.
148+
if p != param {
149+
modified = true;
150+
}
151+
new_params.push(param.parameter);
152+
} else {
153+
// This is a subsequent parameter with the same key.
154+
// We will remove it, which constitutes a modification.
155+
modified = true;
156+
}
157+
seen_key = true;
158+
} else {
159+
new_params.push(p.parameter);
160+
}
161+
}
162+
163+
if seen_key {
164+
if modified {
165+
self.0 = Cow::Owned(new_params.join(b" ".as_slice()));
166+
true
167+
} else {
168+
// The parameter already existed with the same content, and there were no duplicates.
169+
false
170+
}
171+
} else {
172+
// The parameter was not found, so we append it.
173+
let self_mut = self.0.to_mut();
174+
if !self_mut.is_empty() && !self_mut.ends_with(b" ") {
175+
self_mut.push(b' ');
176+
}
177+
self_mut.extend_from_slice(param.parameter);
178+
true
179+
}
180+
}
181+
182+
/// Remove parameter(s) with the given key from the command line
183+
///
184+
/// Returns `true` if parameter(s) were removed.
185+
pub fn remove(&mut self, key: ParameterKey) -> bool {
186+
let mut removed = false;
187+
let mut new_params = Vec::new();
188+
189+
for p in self.iter() {
190+
if p.key == key {
191+
removed = true;
192+
} else {
193+
new_params.push(p.parameter);
194+
}
195+
}
196+
197+
if removed {
198+
self.0 = Cow::Owned(new_params.join(b" ".as_slice()));
199+
}
200+
201+
removed
202+
}
131203
}
132204

133205
/// A single kernel command line parameter key
@@ -144,6 +216,12 @@ impl<'a> std::ops::Deref for ParameterKey<'a> {
144216
}
145217
}
146218

219+
impl<'a, T: AsRef<[u8]> + ?Sized> From<&'a T> for ParameterKey<'a> {
220+
fn from(s: &'a T) -> Self {
221+
Self(s.as_ref())
222+
}
223+
}
224+
147225
impl PartialEq for ParameterKey<'_> {
148226
/// Compares two parameter keys for equality.
149227
///
@@ -171,6 +249,8 @@ impl PartialEq for ParameterKey<'_> {
171249
/// A single kernel command line parameter.
172250
#[derive(Debug, Eq)]
173251
pub struct Parameter<'a> {
252+
/// The full original value
253+
parameter: &'a [u8],
174254
/// The parameter key as raw bytes
175255
key: ParameterKey<'a>,
176256
/// The parameter value as raw bytes, if present
@@ -214,6 +294,7 @@ impl<'a> Parameter<'a> {
214294

215295
let ret = match equals {
216296
None => Self {
297+
parameter: input,
217298
key: ParameterKey(input),
218299
value: None,
219300
},
@@ -232,6 +313,7 @@ impl<'a> Parameter<'a> {
232313
};
233314

234315
Self {
316+
parameter: input,
235317
key,
236318
value: Some(value),
237319
}
@@ -537,4 +619,50 @@ mod tests {
537619
assert_eq!(rd_args[2], param("rd.foo=a"));
538620
assert_eq!(rd_args[3], param("rd.qux=c"));
539621
}
622+
623+
#[test]
624+
fn test_add_or_modify() {
625+
let mut kargs = Cmdline::from(b"foo=bar");
626+
627+
// add new
628+
assert!(kargs.add_or_modify(param("baz")));
629+
let mut iter = kargs.iter();
630+
assert_eq!(iter.next(), Some(param("foo=bar")));
631+
assert_eq!(iter.next(), Some(param("baz")));
632+
assert_eq!(iter.next(), None);
633+
634+
// modify existing
635+
assert!(kargs.add_or_modify(param("foo=fuz")));
636+
iter = kargs.iter();
637+
assert_eq!(iter.next(), Some(param("foo=fuz")));
638+
assert_eq!(iter.next(), Some(param("baz")));
639+
assert_eq!(iter.next(), None);
640+
641+
// already exists with same value returns false and doesn't
642+
// modify anything
643+
assert!(!kargs.add_or_modify(param("foo=fuz")));
644+
iter = kargs.iter();
645+
assert_eq!(iter.next(), Some(param("foo=fuz")));
646+
assert_eq!(iter.next(), Some(param("baz")));
647+
assert_eq!(iter.next(), None);
648+
}
649+
650+
#[test]
651+
fn test_remove() {
652+
let mut kargs = Cmdline::from(b"foo bar baz");
653+
654+
// remove existing
655+
assert!(kargs.remove("bar".into()));
656+
let mut iter = kargs.iter();
657+
assert_eq!(iter.next(), Some(param("foo")));
658+
assert_eq!(iter.next(), Some(param("baz")));
659+
assert_eq!(iter.next(), None);
660+
661+
// doesn't exist? returns false and doesn't modify anything
662+
assert!(!kargs.remove("missing".into()));
663+
iter = kargs.iter();
664+
assert_eq!(iter.next(), Some(param("foo")));
665+
assert_eq!(iter.next(), Some(param("baz")));
666+
assert_eq!(iter.next(), None);
667+
}
540668
}

crates/kernel_cmdline/src/utf8.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,23 @@ impl<'a> Cmdline<'a> {
107107
self.value_of(key)
108108
.ok_or_else(|| anyhow::anyhow!("Failed to find kernel argument '{key}'"))
109109
}
110+
111+
/// Add or modify a parameter to the command line
112+
///
113+
/// Returns `true` if the parameter was added or modified.
114+
///
115+
/// Returns `false` if the parameter already existed with the same
116+
/// content.
117+
pub fn add_or_modify(&mut self, param: Parameter) -> bool {
118+
self.0.add_or_modify(param.0)
119+
}
120+
121+
/// Remove parameter(s) with the given key from the command line
122+
///
123+
/// Returns `true` if parameter(s) were removed.
124+
pub fn remove(&mut self, key: ParameterKey) -> bool {
125+
self.0.remove(key.0)
126+
}
110127
}
111128

112129
/// A single kernel command line parameter key
@@ -525,4 +542,50 @@ mod tests {
525542
let k2 = ParameterKey::from("a-c");
526543
assert_ne!(k1, k2);
527544
}
545+
546+
#[test]
547+
fn test_add_or_modify() {
548+
let mut kargs = Cmdline::from("foo=bar");
549+
550+
// add new
551+
assert!(kargs.add_or_modify(param("baz")));
552+
let mut iter = kargs.iter();
553+
assert_eq!(iter.next(), Some(param("foo=bar")));
554+
assert_eq!(iter.next(), Some(param("baz")));
555+
assert_eq!(iter.next(), None);
556+
557+
// modify existing
558+
assert!(kargs.add_or_modify(param("foo=fuz")));
559+
iter = kargs.iter();
560+
assert_eq!(iter.next(), Some(param("foo=fuz")));
561+
assert_eq!(iter.next(), Some(param("baz")));
562+
assert_eq!(iter.next(), None);
563+
564+
// already exists with same value returns false and doesn't
565+
// modify anything
566+
assert!(!kargs.add_or_modify(param("foo=fuz")));
567+
iter = kargs.iter();
568+
assert_eq!(iter.next(), Some(param("foo=fuz")));
569+
assert_eq!(iter.next(), Some(param("baz")));
570+
assert_eq!(iter.next(), None);
571+
}
572+
573+
#[test]
574+
fn test_remove() {
575+
let mut kargs = Cmdline::from("foo bar baz");
576+
577+
// remove existing
578+
assert!(kargs.remove("bar".into()));
579+
let mut iter = kargs.iter();
580+
assert_eq!(iter.next(), Some(param("foo")));
581+
assert_eq!(iter.next(), Some(param("baz")));
582+
assert_eq!(iter.next(), None);
583+
584+
// doesn't exist? returns false and doesn't modify anything
585+
assert!(!kargs.remove("missing".into()));
586+
iter = kargs.iter();
587+
assert_eq!(iter.next(), Some(param("foo")));
588+
assert_eq!(iter.next(), Some(param("baz")));
589+
assert_eq!(iter.next(), None);
590+
}
528591
}

0 commit comments

Comments
 (0)