Skip to content

Commit 872ccdd

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 872ccdd

File tree

2 files changed

+242
-1
lines changed

2 files changed

+242
-1
lines changed

crates/kernel_cmdline/src/bytes.rs

Lines changed: 154 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,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,75 @@ 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_add_or_modify_empty_cmdline() {
652+
let mut kargs = Cmdline::from(b"");
653+
assert!(kargs.add_or_modify(param("foo")));
654+
assert_eq!(kargs.0, b"foo".as_slice());
655+
}
656+
657+
#[test]
658+
fn test_add_or_modify_duplicate_parameters() {
659+
let mut kargs = Cmdline::from(b"a=1 a=2");
660+
assert!(kargs.add_or_modify(param("a=3")));
661+
let mut iter = kargs.iter();
662+
assert_eq!(iter.next(), Some(param("a=3")));
663+
assert_eq!(iter.next(), None);
664+
}
665+
666+
#[test]
667+
fn test_remove() {
668+
let mut kargs = Cmdline::from(b"foo bar baz");
669+
670+
// remove existing
671+
assert!(kargs.remove("bar".into()));
672+
let mut iter = kargs.iter();
673+
assert_eq!(iter.next(), Some(param("foo")));
674+
assert_eq!(iter.next(), Some(param("baz")));
675+
assert_eq!(iter.next(), None);
676+
677+
// doesn't exist? returns false and doesn't modify anything
678+
assert!(!kargs.remove("missing".into()));
679+
iter = kargs.iter();
680+
assert_eq!(iter.next(), Some(param("foo")));
681+
assert_eq!(iter.next(), Some(param("baz")));
682+
assert_eq!(iter.next(), None);
683+
}
684+
685+
#[test]
686+
fn test_remove_duplicates() {
687+
let mut kargs = Cmdline::from(b"a=1 b=2 a=3");
688+
assert!(kargs.remove("a".into()));
689+
let mut iter = kargs.iter();
690+
assert_eq!(iter.next(), Some(param("b=2")));
691+
assert_eq!(iter.next(), None);
692+
}
540693
}

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)