@@ -8,9 +8,9 @@ use std::borrow::Cow;
88use anyhow:: Result ;
99
1010/// This is used by dracut.
11- pub ( crate ) const INITRD_ARG_PREFIX : & [ u8 ] = b "rd.";
11+ pub ( crate ) const INITRD_ARG_PREFIX : & str = "rd." ;
1212/// The kernel argument for configuring the rootfs flags.
13- pub ( crate ) const ROOTFLAGS : & [ u8 ] = b "rootflags";
13+ pub ( crate ) const ROOTFLAGS : & str = "rootflags" ;
1414
1515/// A parsed kernel command line.
1616///
@@ -41,7 +41,7 @@ impl<'a> Cmdline<'a> {
4141 /// Properly handles quoted values containing whitespace and splits on
4242 /// unquoted whitespace characters. Parameters are parsed as either
4343 /// key-only switches or key=value pairs.
44- pub fn iter ( & ' a self ) -> impl Iterator < Item = Parameter < ' a > > {
44+ pub fn iter ( & ' a self ) -> impl Iterator < Item = Parameter < ' a > > + ' a {
4545 let mut in_quotes = false ;
4646
4747 self . 0
@@ -63,6 +63,29 @@ impl<'a> Cmdline<'a> {
6363 self . iter ( ) . find ( |p| p. key == key)
6464 }
6565
66+ /// Locate a kernel argument with the given key name that must be UTF-8.
67+ ///
68+ /// Otherwise the same as [`Self::find`].
69+ pub fn find_str ( & ' a self , key : & str ) -> Option < ParameterStr < ' a > > {
70+ let key = ParameterKeyStr ( key) ;
71+ self . iter ( )
72+ . filter_map ( |p| p. to_str ( ) )
73+ . find ( move |p| p. key == key)
74+ }
75+
76+ /// Find all kernel arguments starting with the given prefix which must be UTF-8.
77+ /// Non-UTF8 values are ignored.
78+ ///
79+ /// This is a variant of [`Self::find`].
80+ pub fn find_all_starting_with_str (
81+ & ' a self ,
82+ prefix : & ' a str ,
83+ ) -> impl Iterator < Item = ParameterStr < ' a > > + ' a {
84+ self . iter ( )
85+ . filter_map ( |p| p. to_str ( ) )
86+ . filter ( move |p| p. key . 0 . starts_with ( prefix) )
87+ }
88+
6689 /// Locate the value of the kernel argument with the given key name.
6790 ///
6891 /// Returns the first value matching the given key, or `None` if not found.
@@ -121,47 +144,52 @@ impl<'a> From<&'a [u8]> for ParameterKey<'a> {
121144 }
122145}
123146
147+ /// A single kernel command line parameter key that is known to be UTF-8.
148+ ///
149+ /// Otherwise the same as [`ParameterKey`].
150+ #[ derive( Debug , Eq ) ]
151+ pub ( crate ) struct ParameterKeyStr < ' a > ( & ' a str ) ;
152+
153+ impl < ' a > From < & ' a str > for ParameterKeyStr < ' a > {
154+ fn from ( value : & ' a str ) -> Self {
155+ Self ( value)
156+ }
157+ }
158+
124159/// A single kernel command line parameter.
125- #[ derive( Debug , PartialEq , Eq ) ]
160+ #[ derive( Debug , Eq ) ]
126161pub ( crate ) struct Parameter < ' a > {
162+ /// The full original value
163+ pub parameter : & ' a [ u8 ] ,
127164 /// The parameter key as raw bytes
128165 pub key : ParameterKey < ' a > ,
129166 /// The parameter value as raw bytes, if present
130167 pub value : Option < & ' a [ u8 ] > ,
131168}
132169
133- impl < ' a > Parameter < ' a > {
134- /// Create a new parameter with the provided key and value.
135- #[ cfg( test) ]
136- pub fn new_kv < ' k : ' a , ' v : ' a > ( key : & ' k [ u8 ] , value : & ' v [ u8 ] ) -> Self {
137- Self {
138- key : ParameterKey ( key) ,
139- value : Some ( value) ,
140- }
141- }
142-
143- /// Create a new parameter with the provided key.
144- #[ cfg( test) ]
145- pub fn new_key ( key : & ' a [ u8 ] ) -> Self {
146- Self {
147- key : ParameterKey ( key) ,
148- value : None ,
149- }
150- }
170+ /// A single kernel command line parameter.
171+ #[ derive( Debug , PartialEq , Eq ) ]
172+ pub ( crate ) struct ParameterStr < ' a > {
173+ /// The original value
174+ pub parameter : & ' a str ,
175+ /// The parameter key
176+ pub key : ParameterKeyStr < ' a > ,
177+ /// The parameter value, if present
178+ pub value : Option < & ' a str > ,
179+ }
151180
152- /// Returns the key as a lossy UTF-8 string.
153- ///
154- /// Invalid UTF-8 sequences are replaced with the Unicode replacement character.
155- pub fn key_lossy ( & self ) -> String {
156- String :: from_utf8_lossy ( & self . key ) . to_string ( )
181+ impl < ' a > Parameter < ' a > {
182+ pub fn to_str ( & self ) -> Option < ParameterStr < ' a > > {
183+ let Ok ( parameter) = std:: str:: from_utf8 ( self . parameter ) else {
184+ return None ;
185+ } ;
186+ Some ( ParameterStr :: from ( parameter) )
157187 }
188+ }
158189
159- /// Returns the value as a lossy UTF-8 string.
160- ///
161- /// Invalid UTF-8 sequences are replaced with the Unicode replacement character.
162- /// Returns an empty string if no value is present.
163- pub fn value_lossy ( & self ) -> String {
164- String :: from_utf8_lossy ( self . value . unwrap_or ( & [ ] ) ) . to_string ( )
190+ impl < ' a > AsRef < str > for ParameterStr < ' a > {
191+ fn as_ref ( & self ) -> & str {
192+ self . parameter
165193 }
166194}
167195
@@ -177,6 +205,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> From<&'a T> for Parameter<'a> {
177205
178206 match equals {
179207 None => Self {
208+ parameter : input,
180209 key : ParameterKey ( input) ,
181210 value : None ,
182211 } ,
@@ -196,6 +225,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> From<&'a T> for Parameter<'a> {
196225 . unwrap_or ( value) ;
197226
198227 Self {
228+ parameter : input,
199229 key,
200230 value : Some ( value) ,
201231 }
@@ -228,29 +258,40 @@ impl PartialEq for ParameterKey<'_> {
228258 }
229259}
230260
231- impl std:: fmt:: Display for Parameter < ' _ > {
232- /// Formats the parameter for display.
233- ///
234- /// Key-only parameters are displayed as just the key.
235- /// Key-value parameters are displayed as `key=value`.
236- /// Values containing whitespace are automatically quoted.
237- fn fmt ( & self , f : & mut std:: fmt:: Formatter ) -> std:: fmt:: Result {
238- let key = self . key_lossy ( ) ;
239-
240- if self . value . is_some ( ) {
241- let value = self . value_lossy ( ) ;
242-
243- if value. chars ( ) . any ( |c| c. is_ascii_whitespace ( ) ) {
244- write ! ( f, "{key}=\" {value}\" " )
245- } else {
246- write ! ( f, "{key}={value}" )
247- }
261+ impl < ' a > From < & ' a str > for ParameterStr < ' a > {
262+ fn from ( parameter : & ' a str ) -> Self {
263+ let ( key, value) = if let Some ( ( key, value) ) = parameter. split_once ( '=' ) {
264+ let value = value
265+ . strip_prefix ( '"' )
266+ . unwrap_or ( value)
267+ . strip_suffix ( '"' )
268+ . unwrap_or ( value) ;
269+ ( key, Some ( value) )
248270 } else {
249- write ! ( f, "{key}" )
271+ ( parameter, None )
272+ } ;
273+ let key = ParameterKeyStr ( key) ;
274+ ParameterStr {
275+ parameter,
276+ key,
277+ value,
250278 }
251279 }
252280}
253281
282+ impl < ' a > PartialEq for Parameter < ' a > {
283+ fn eq ( & self , other : & Self ) -> bool {
284+ // Note we don't compare parameter because we want hyphen-dash insensitivity for the key
285+ self . key == other. key && self . value == other. value
286+ }
287+ }
288+
289+ impl < ' a > PartialEq for ParameterKeyStr < ' a > {
290+ fn eq ( & self , other : & Self ) -> bool {
291+ ParameterKey ( self . 0 . as_bytes ( ) ) == ParameterKey ( other. 0 . as_bytes ( ) )
292+ }
293+ }
294+
254295#[ cfg( test) ]
255296mod tests {
256297 use super :: * ;
@@ -293,9 +334,6 @@ mod tests {
293334 p. push ( non_utf8_byte[ 0 ] ) ;
294335 let p = Parameter :: from ( & p) ;
295336 assert_eq ! ( p. value, Some ( non_utf8_byte. as_slice( ) ) ) ;
296-
297- // lossy replacement sanity check
298- assert_eq ! ( p. value_lossy( ) , char :: REPLACEMENT_CHARACTER . to_string( ) ) ;
299337 }
300338
301339 #[ test]
@@ -334,14 +372,9 @@ mod tests {
334372 let kargs = Cmdline :: from ( b"foo=bar,bar2 baz=fuz wiz" . as_slice ( ) ) ;
335373 let mut iter = kargs. iter ( ) ;
336374
337- assert_eq ! ( iter. next( ) , Some ( Parameter :: new_kv( b"foo" , b"bar,bar2" ) ) ) ;
338-
339- assert_eq ! (
340- iter. next( ) ,
341- Some ( Parameter :: new_kv( b"baz" , b"fuz" . as_slice( ) ) )
342- ) ;
343-
344- assert_eq ! ( iter. next( ) , Some ( Parameter :: new_key( b"wiz" ) ) ) ;
375+ assert_eq ! ( iter. next( ) , Some ( Parameter :: from( b"foo=bar,bar2" ) ) ) ;
376+ assert_eq ! ( iter. next( ) , Some ( Parameter :: from( b"baz=fuz" ) ) ) ;
377+ assert_eq ! ( iter. next( ) , Some ( Parameter :: from( b"wiz" ) ) ) ;
345378 assert_eq ! ( iter. next( ) , None ) ;
346379
347380 // Test the find API
@@ -483,4 +516,70 @@ mod tests {
483516 let kargs = Cmdline :: from ( & invalid_utf8) ;
484517 assert ! ( kargs. require_value_of_utf8( "invalid" ) . is_err( ) ) ;
485518 }
519+
520+ #[ test]
521+ fn test_find_str ( ) {
522+ let kargs = Cmdline :: from ( b"foo=bar baz=qux switch rd.break" . as_slice ( ) ) ;
523+ let p = kargs. find_str ( "foo" ) . unwrap ( ) ;
524+ assert_eq ! ( p, ParameterStr :: from( "foo=bar" ) ) ;
525+ assert_eq ! ( p. as_ref( ) , "foo=bar" ) ;
526+ let p = kargs. find_str ( "rd.break" ) . unwrap ( ) ;
527+ assert_eq ! ( p, ParameterStr :: from( "rd.break" ) ) ;
528+ assert ! ( kargs. find_str( "missing" ) . is_none( ) ) ;
529+ }
530+
531+ #[ test]
532+ fn test_find_all_str ( ) {
533+ let kargs =
534+ Cmdline :: from ( b"foo=bar rd.foo=a rd.bar=b rd.baz rd.qux=c notrd.val=d" . as_slice ( ) ) ;
535+ let mut rd_args: Vec < _ > = kargs. find_all_starting_with_str ( "rd." ) . collect ( ) ;
536+ rd_args. sort_by ( |a, b| a. key . 0 . cmp ( b. key . 0 ) ) ;
537+ assert_eq ! ( rd_args. len( ) , 4 ) ;
538+ assert_eq ! ( rd_args[ 0 ] , ParameterStr :: from( "rd.bar=b" ) ) ;
539+ assert_eq ! ( rd_args[ 1 ] , ParameterStr :: from( "rd.baz" ) ) ;
540+ assert_eq ! ( rd_args[ 2 ] , ParameterStr :: from( "rd.foo=a" ) ) ;
541+ assert_eq ! ( rd_args[ 3 ] , ParameterStr :: from( "rd.qux=c" ) ) ;
542+ }
543+
544+ #[ test]
545+ fn test_param_to_str ( ) {
546+ let p = Parameter :: from ( "foo=bar" ) ;
547+ let p_str = p. to_str ( ) . unwrap ( ) ;
548+ assert_eq ! ( p_str, ParameterStr :: from( "foo=bar" ) ) ;
549+ let non_utf8_byte = b"\xff " ;
550+ let mut p_u8 = b"foo=" . to_vec ( ) ;
551+ p_u8. push ( non_utf8_byte[ 0 ] ) ;
552+ let p = Parameter :: from ( & p_u8) ;
553+ assert ! ( p. to_str( ) . is_none( ) ) ;
554+ }
555+
556+ #[ test]
557+ fn test_param_key_str_eq ( ) {
558+ let k1 = ParameterKeyStr ( "a-b" ) ;
559+ let k2 = ParameterKeyStr ( "a_b" ) ;
560+ assert_eq ! ( k1, k2) ;
561+ let k1 = ParameterKeyStr ( "a-b" ) ;
562+ let k2 = ParameterKeyStr ( "a-c" ) ;
563+ assert_ne ! ( k1, k2) ;
564+ }
565+
566+ #[ test]
567+ fn test_kargs_non_utf8 ( ) {
568+ let non_utf8_val = b"an_invalid_key=\xff " ;
569+ let mut kargs_bytes = b"foo=bar " . to_vec ( ) ;
570+ kargs_bytes. extend_from_slice ( non_utf8_val) ;
571+ kargs_bytes. extend_from_slice ( b" baz=qux" ) ;
572+ let kargs = Cmdline :: from ( kargs_bytes. as_slice ( ) ) ;
573+
574+ // We should be able to find the valid kargs
575+ assert_eq ! ( kargs. find_str( "foo" ) . unwrap( ) . value, Some ( "bar" ) ) ;
576+ assert_eq ! ( kargs. find_str( "baz" ) . unwrap( ) . value, Some ( "qux" ) ) ;
577+
578+ // But we should not find the invalid one via find_str
579+ assert ! ( kargs. find( "an_invalid_key" ) . unwrap( ) . to_str( ) . is_none( ) ) ;
580+
581+ // And even using the raw find, trying to convert it to_str will fail.
582+ let raw_param = kargs. find ( "an_invalid_key" ) . unwrap ( ) ;
583+ assert_eq ! ( raw_param. value. unwrap( ) , b"\xff " ) ;
584+ }
486585}
0 commit comments