Skip to content

Commit 68c4e4d

Browse files
authored
Merge pull request #1608 from jeckersb/cmdline-manipulation
kernel_cmdline: Add parameter manipulation methods
2 parents 029ed34 + 0555eac commit 68c4e4d

File tree

2 files changed

+253
-0
lines changed

2 files changed

+253
-0
lines changed

crates/kernel_cmdline/src/bytes.rs

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,83 @@ 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.last().unwrap().is_ascii_whitespace() {
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+
}
202+
}
203+
204+
impl<'a> AsRef<[u8]> for Cmdline<'a> {
205+
fn as_ref(&self) -> &[u8] {
206+
&self.0
207+
}
131208
}
132209

133210
/// A single kernel command line parameter key
@@ -144,6 +221,12 @@ impl<'a> std::ops::Deref for ParameterKey<'a> {
144221
}
145222
}
146223

224+
impl<'a, T: AsRef<[u8]> + ?Sized> From<&'a T> for ParameterKey<'a> {
225+
fn from(s: &'a T) -> Self {
226+
Self(s.as_ref())
227+
}
228+
}
229+
147230
impl PartialEq for ParameterKey<'_> {
148231
/// Compares two parameter keys for equality.
149232
///
@@ -171,6 +254,8 @@ impl PartialEq for ParameterKey<'_> {
171254
/// A single kernel command line parameter.
172255
#[derive(Debug, Eq)]
173256
pub struct Parameter<'a> {
257+
/// The full original value
258+
parameter: &'a [u8],
174259
/// The parameter key as raw bytes
175260
key: ParameterKey<'a>,
176261
/// The parameter value as raw bytes, if present
@@ -214,6 +299,7 @@ impl<'a> Parameter<'a> {
214299

215300
let ret = match equals {
216301
None => Self {
302+
parameter: input,
217303
key: ParameterKey(input),
218304
value: None,
219305
},
@@ -232,6 +318,7 @@ impl<'a> Parameter<'a> {
232318
};
233319

234320
Self {
321+
parameter: input,
235322
key,
236323
value: Some(value),
237324
}
@@ -537,4 +624,75 @@ mod tests {
537624
assert_eq!(rd_args[2], param("rd.foo=a"));
538625
assert_eq!(rd_args[3], param("rd.qux=c"));
539626
}
627+
628+
#[test]
629+
fn test_add_or_modify() {
630+
let mut kargs = Cmdline::from(b"foo=bar");
631+
632+
// add new
633+
assert!(kargs.add_or_modify(param("baz")));
634+
let mut iter = kargs.iter();
635+
assert_eq!(iter.next(), Some(param("foo=bar")));
636+
assert_eq!(iter.next(), Some(param("baz")));
637+
assert_eq!(iter.next(), None);
638+
639+
// modify existing
640+
assert!(kargs.add_or_modify(param("foo=fuz")));
641+
iter = kargs.iter();
642+
assert_eq!(iter.next(), Some(param("foo=fuz")));
643+
assert_eq!(iter.next(), Some(param("baz")));
644+
assert_eq!(iter.next(), None);
645+
646+
// already exists with same value returns false and doesn't
647+
// modify anything
648+
assert!(!kargs.add_or_modify(param("foo=fuz")));
649+
iter = kargs.iter();
650+
assert_eq!(iter.next(), Some(param("foo=fuz")));
651+
assert_eq!(iter.next(), Some(param("baz")));
652+
assert_eq!(iter.next(), None);
653+
}
654+
655+
#[test]
656+
fn test_add_or_modify_empty_cmdline() {
657+
let mut kargs = Cmdline::from(b"");
658+
assert!(kargs.add_or_modify(param("foo")));
659+
assert_eq!(kargs.0, b"foo".as_slice());
660+
}
661+
662+
#[test]
663+
fn test_add_or_modify_duplicate_parameters() {
664+
let mut kargs = Cmdline::from(b"a=1 a=2");
665+
assert!(kargs.add_or_modify(param("a=3")));
666+
let mut iter = kargs.iter();
667+
assert_eq!(iter.next(), Some(param("a=3")));
668+
assert_eq!(iter.next(), None);
669+
}
670+
671+
#[test]
672+
fn test_remove() {
673+
let mut kargs = Cmdline::from(b"foo bar baz");
674+
675+
// remove existing
676+
assert!(kargs.remove("bar".into()));
677+
let mut iter = kargs.iter();
678+
assert_eq!(iter.next(), Some(param("foo")));
679+
assert_eq!(iter.next(), Some(param("baz")));
680+
assert_eq!(iter.next(), None);
681+
682+
// doesn't exist? returns false and doesn't modify anything
683+
assert!(!kargs.remove("missing".into()));
684+
iter = kargs.iter();
685+
assert_eq!(iter.next(), Some(param("foo")));
686+
assert_eq!(iter.next(), Some(param("baz")));
687+
assert_eq!(iter.next(), None);
688+
}
689+
690+
#[test]
691+
fn test_remove_duplicates() {
692+
let mut kargs = Cmdline::from(b"a=1 b=2 a=3");
693+
assert!(kargs.remove("a".into()));
694+
let mut iter = kargs.iter();
695+
assert_eq!(iter.next(), Some(param("b=2")));
696+
assert_eq!(iter.next(), None);
697+
}
540698
}

crates/kernel_cmdline/src/utf8.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,30 @@ 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+
}
127+
}
128+
129+
impl<'a> AsRef<str> for Cmdline<'a> {
130+
fn as_ref(&self) -> &str {
131+
str::from_utf8(self.0.as_ref())
132+
.expect("We only construct the underlying bytes from valid UTF-8")
133+
}
110134
}
111135

112136
/// A single kernel command line parameter key
@@ -525,4 +549,75 @@ mod tests {
525549
let k2 = ParameterKey::from("a-c");
526550
assert_ne!(k1, k2);
527551
}
552+
553+
#[test]
554+
fn test_add_or_modify() {
555+
let mut kargs = Cmdline::from("foo=bar");
556+
557+
// add new
558+
assert!(kargs.add_or_modify(param("baz")));
559+
let mut iter = kargs.iter();
560+
assert_eq!(iter.next(), Some(param("foo=bar")));
561+
assert_eq!(iter.next(), Some(param("baz")));
562+
assert_eq!(iter.next(), None);
563+
564+
// modify existing
565+
assert!(kargs.add_or_modify(param("foo=fuz")));
566+
iter = kargs.iter();
567+
assert_eq!(iter.next(), Some(param("foo=fuz")));
568+
assert_eq!(iter.next(), Some(param("baz")));
569+
assert_eq!(iter.next(), None);
570+
571+
// already exists with same value returns false and doesn't
572+
// modify anything
573+
assert!(!kargs.add_or_modify(param("foo=fuz")));
574+
iter = kargs.iter();
575+
assert_eq!(iter.next(), Some(param("foo=fuz")));
576+
assert_eq!(iter.next(), Some(param("baz")));
577+
assert_eq!(iter.next(), None);
578+
}
579+
580+
#[test]
581+
fn test_add_or_modify_empty_cmdline() {
582+
let mut kargs = Cmdline::from("");
583+
assert!(kargs.add_or_modify(param("foo")));
584+
assert_eq!(kargs.as_ref(), "foo");
585+
}
586+
587+
#[test]
588+
fn test_add_or_modify_duplicate_parameters() {
589+
let mut kargs = Cmdline::from("a=1 a=2");
590+
assert!(kargs.add_or_modify(param("a=3")));
591+
let mut iter = kargs.iter();
592+
assert_eq!(iter.next(), Some(param("a=3")));
593+
assert_eq!(iter.next(), None);
594+
}
595+
596+
#[test]
597+
fn test_remove() {
598+
let mut kargs = Cmdline::from("foo bar baz");
599+
600+
// remove existing
601+
assert!(kargs.remove("bar".into()));
602+
let mut 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+
// doesn't exist? returns false and doesn't modify anything
608+
assert!(!kargs.remove("missing".into()));
609+
iter = kargs.iter();
610+
assert_eq!(iter.next(), Some(param("foo")));
611+
assert_eq!(iter.next(), Some(param("baz")));
612+
assert_eq!(iter.next(), None);
613+
}
614+
615+
#[test]
616+
fn test_remove_duplicates() {
617+
let mut kargs = Cmdline::from("a=1 b=2 a=3");
618+
assert!(kargs.remove("a".into()));
619+
let mut iter = kargs.iter();
620+
assert_eq!(iter.next(), Some(param("b=2")));
621+
assert_eq!(iter.next(), None);
622+
}
528623
}

0 commit comments

Comments
 (0)