Skip to content

Commit 7833932

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 7833932

File tree

2 files changed

+241
-1
lines changed

2 files changed

+241
-1
lines changed

crates/kernel_cmdline/src/bytes.rs

Lines changed: 153 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use anyhow::Result;
1515
/// over individual parameters. Uses copy-on-write semantics to avoid unnecessary
1616
/// allocations when working with borrowed data.
1717
#[derive(Debug)]
18-
pub struct Cmdline<'a>(Cow<'a, [u8]>);
18+
pub struct Cmdline<'a>(pub(crate) Cow<'a, [u8]>);
1919

2020
impl<'a, T: AsRef<[u8]> + ?Sized> From<&'a T> for Cmdline<'a> {
2121
/// Creates a new `Cmdline` from any type that can be referenced as bytes.
@@ -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 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+
// The parameter was not found, so we append it.
165+
let self_mut = self.0.to_mut();
166+
if !self_mut.is_empty() && !self_mut.ends_with(b" ") {
167+
self_mut.push(b' ');
168+
}
169+
self_mut.extend_from_slice(param.parameter);
170+
return true;
171+
}
172+
if modified {
173+
self.0 = Cow::Owned(new_params.join(b" ".as_slice()));
174+
true
175+
} else {
176+
// The parameter already existed with the same content, and there were no duplicates.
177+
false
178+
}
179+
}
180+
181+
/// Remove parameter(s) with the given key from the command line
182+
///
183+
/// Returns `true` if parameter(s) were removed.
184+
pub fn remove(&mut self, key: ParameterKey) -> bool {
185+
let mut removed = false;
186+
let mut new_params = Vec::new();
187+
188+
for p in self.iter() {
189+
if p.key == key {
190+
removed = true;
191+
} else {
192+
new_params.push(p.parameter);
193+
}
194+
}
195+
196+
if removed {
197+
self.0 = Cow::Owned(new_params.join(b" ".as_slice()));
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,75 @@ 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_add_or_modify_empty_cmdline() {
651+
let mut kargs = Cmdline::from(b"");
652+
assert!(kargs.add_or_modify(param("foo")));
653+
assert_eq!(kargs.0, b"foo".as_slice());
654+
}
655+
656+
#[test]
657+
fn test_add_or_modify_duplicate_parameters() {
658+
let mut kargs = Cmdline::from(b"a=1 a=2");
659+
assert!(kargs.add_or_modify(param("a=3")));
660+
let mut iter = kargs.iter();
661+
assert_eq!(iter.next(), Some(param("a=3")));
662+
assert_eq!(iter.next(), None);
663+
}
664+
665+
#[test]
666+
fn test_remove() {
667+
let mut kargs = Cmdline::from(b"foo bar baz");
668+
669+
// remove existing
670+
assert!(kargs.remove("bar".into()));
671+
let mut iter = kargs.iter();
672+
assert_eq!(iter.next(), Some(param("foo")));
673+
assert_eq!(iter.next(), Some(param("baz")));
674+
assert_eq!(iter.next(), None);
675+
676+
// doesn't exist? returns false and doesn't modify anything
677+
assert!(!kargs.remove("missing".into()));
678+
iter = kargs.iter();
679+
assert_eq!(iter.next(), Some(param("foo")));
680+
assert_eq!(iter.next(), Some(param("baz")));
681+
assert_eq!(iter.next(), None);
682+
}
683+
684+
#[test]
685+
fn test_remove_duplicates() {
686+
let mut kargs = Cmdline::from(b"a=1 b=2 a=3");
687+
assert!(kargs.remove("a".into()));
688+
let mut iter = kargs.iter();
689+
assert_eq!(iter.next(), Some(param("b=2")));
690+
assert_eq!(iter.next(), None);
691+
}
540692
}

crates/kernel_cmdline/src/utf8.rs

Lines changed: 88 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,75 @@ 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_add_or_modify_empty_cmdline() {
575+
let mut kargs = Cmdline::from("");
576+
assert!(kargs.add_or_modify(param("foo")));
577+
assert_eq!(kargs.0 .0, b"foo".as_slice());
578+
}
579+
580+
#[test]
581+
fn test_add_or_modify_duplicate_parameters() {
582+
let mut kargs = Cmdline::from("a=1 a=2");
583+
assert!(kargs.add_or_modify(param("a=3")));
584+
let mut iter = kargs.iter();
585+
assert_eq!(iter.next(), Some(param("a=3")));
586+
assert_eq!(iter.next(), None);
587+
}
588+
589+
#[test]
590+
fn test_remove() {
591+
let mut kargs = Cmdline::from("foo bar baz");
592+
593+
// remove existing
594+
assert!(kargs.remove("bar".into()));
595+
let mut iter = kargs.iter();
596+
assert_eq!(iter.next(), Some(param("foo")));
597+
assert_eq!(iter.next(), Some(param("baz")));
598+
assert_eq!(iter.next(), None);
599+
600+
// doesn't exist? returns false and doesn't modify anything
601+
assert!(!kargs.remove("missing".into()));
602+
iter = kargs.iter();
603+
assert_eq!(iter.next(), Some(param("foo")));
604+
assert_eq!(iter.next(), Some(param("baz")));
605+
assert_eq!(iter.next(), None);
606+
}
607+
608+
#[test]
609+
fn test_remove_duplicates() {
610+
let mut kargs = Cmdline::from("a=1 b=2 a=3");
611+
assert!(kargs.remove("a".into()));
612+
let mut iter = kargs.iter();
613+
assert_eq!(iter.next(), Some(param("b=2")));
614+
assert_eq!(iter.next(), None);
615+
}
528616
}

0 commit comments

Comments
 (0)