@@ -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
2020impl < ' 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+
147224impl 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 ) ]
173250pub 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}
0 commit comments