Skip to content

Commit 5f83488

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. Signed-off-by: John Eckersberg <[email protected]>
1 parent d947ed1 commit 5f83488

File tree

2 files changed

+190
-0
lines changed

2 files changed

+190
-0
lines changed

crates/kernel_cmdline/src/bytes.rs

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

133204
/// A single kernel command line parameter key
@@ -144,6 +215,12 @@ impl<'a> std::ops::Deref for ParameterKey<'a> {
144215
}
145216
}
146217

218+
impl<'a, T: AsRef<[u8]> + ?Sized> From<&'a T> for ParameterKey<'a> {
219+
fn from(s: &'a T) -> Self {
220+
Self(s.as_ref())
221+
}
222+
}
223+
147224
impl PartialEq for ParameterKey<'_> {
148225
/// Compares two parameter keys for equality.
149226
///
@@ -171,6 +248,8 @@ impl PartialEq for ParameterKey<'_> {
171248
/// A single kernel command line parameter.
172249
#[derive(Debug, Eq)]
173250
pub struct Parameter<'a> {
251+
/// The full original value
252+
parameter: &'a [u8],
174253
/// The parameter key as raw bytes
175254
key: ParameterKey<'a>,
176255
/// The parameter value as raw bytes, if present
@@ -214,6 +293,7 @@ impl<'a> Parameter<'a> {
214293

215294
let ret = match equals {
216295
None => Self {
296+
parameter: input,
217297
key: ParameterKey(input),
218298
value: None,
219299
},
@@ -232,6 +312,7 @@ impl<'a> Parameter<'a> {
232312
};
233313

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

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)