Skip to content

Commit ab3a94f

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 ab3a94f

File tree

2 files changed

+186
-0
lines changed

2 files changed

+186
-0
lines changed

crates/kernel_cmdline/src/bytes.rs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,73 @@ 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 seen = false;
140+
let mut modified = false;
141+
let mut new_params = Vec::new();
142+
143+
for p in self.iter() {
144+
if p.key == param.key {
145+
seen = true;
146+
if p == param {
147+
new_params.push(p.parameter);
148+
} else {
149+
modified = true;
150+
new_params.push(param.parameter);
151+
}
152+
} else {
153+
new_params.push(p.parameter);
154+
}
155+
}
156+
157+
if seen {
158+
if modified {
159+
// re-"serialize" ourselves since we modified in-place
160+
self.0 = Cow::Owned(new_params.join(b" ".as_slice()));
161+
true
162+
} else {
163+
// It was already correct
164+
false
165+
}
166+
} else {
167+
// We need to append it
168+
let self_mut = self.0.to_mut();
169+
if !self_mut.ends_with(b" ") {
170+
self_mut.push(b' ');
171+
}
172+
self_mut.extend_from_slice(param.parameter);
173+
true
174+
}
175+
}
176+
177+
/// Remove parameter(s) with the given key from the command line
178+
///
179+
/// Returns `true` if parameter(s) were removed.
180+
pub fn remove(&mut self, key: ParameterKey) -> bool {
181+
let mut removed = false;
182+
let mut new_params = Vec::new();
183+
184+
for p in self.iter() {
185+
if p.key == key {
186+
removed = true;
187+
} else {
188+
new_params.push(p.parameter);
189+
}
190+
}
191+
192+
if removed {
193+
self.0 = Cow::Owned(new_params.join(b" ".as_slice()));
194+
}
195+
196+
removed
197+
}
131198
}
132199

133200
/// A single kernel command line parameter key
@@ -144,6 +211,12 @@ impl<'a> std::ops::Deref for ParameterKey<'a> {
144211
}
145212
}
146213

214+
impl<'a, T: AsRef<[u8]> + ?Sized> From<&'a T> for ParameterKey<'a> {
215+
fn from(s: &'a T) -> Self {
216+
Self(s.as_ref())
217+
}
218+
}
219+
147220
impl PartialEq for ParameterKey<'_> {
148221
/// Compares two parameter keys for equality.
149222
///
@@ -171,6 +244,8 @@ impl PartialEq for ParameterKey<'_> {
171244
/// A single kernel command line parameter.
172245
#[derive(Debug, Eq)]
173246
pub struct Parameter<'a> {
247+
/// The full original value
248+
parameter: &'a [u8],
174249
/// The parameter key as raw bytes
175250
key: ParameterKey<'a>,
176251
/// The parameter value as raw bytes, if present
@@ -214,6 +289,7 @@ impl<'a> Parameter<'a> {
214289

215290
let ret = match equals {
216291
None => Self {
292+
parameter: input,
217293
key: ParameterKey(input),
218294
value: None,
219295
},
@@ -232,6 +308,7 @@ impl<'a> Parameter<'a> {
232308
};
233309

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

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)