@@ -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,32 +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.
125160#[ derive( Debug , Eq ) ]
126161pub ( crate ) struct Parameter < ' a > {
127162 /// The full original value
128- #[ allow( dead_code) ]
129163 pub parameter : & ' a [ u8 ] ,
130164 /// The parameter key as raw bytes
131165 pub key : ParameterKey < ' a > ,
132166 /// The parameter value as raw bytes, if present
133167 pub value : Option < & ' a [ u8 ] > ,
134168}
135169
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+ }
180+
136181impl < ' a > Parameter < ' a > {
137- /// Returns the key as a lossy UTF-8 string.
138- ///
139- /// Invalid UTF-8 sequences are replaced with the Unicode replacement character.
140- pub fn key_lossy ( & self ) -> String {
141- String :: from_utf8_lossy ( & self . key ) . to_string ( )
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 ) )
142187 }
188+ }
143189
144- /// Returns the value as a lossy UTF-8 string.
145- ///
146- /// Invalid UTF-8 sequences are replaced with the Unicode replacement character.
147- /// Returns an empty string if no value is present.
148- pub fn value_lossy ( & self ) -> String {
149- 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
150193 }
151194}
152195
@@ -215,33 +258,37 @@ impl PartialEq for ParameterKey<'_> {
215258 }
216259}
217260
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) )
270+ } else {
271+ ( parameter, None )
272+ } ;
273+ let key = ParameterKeyStr ( key) ;
274+ ParameterStr {
275+ parameter,
276+ key,
277+ value,
278+ }
279+ }
280+ }
281+
218282impl < ' a > PartialEq for Parameter < ' a > {
219283 fn eq ( & self , other : & Self ) -> bool {
220284 // Note we don't compare parameter because we want hyphen-dash insensitivity for the key
221285 self . key == other. key && self . value == other. value
222286 }
223287}
224288
225- impl std:: fmt:: Display for Parameter < ' _ > {
226- /// Formats the parameter for display.
227- ///
228- /// Key-only parameters are displayed as just the key.
229- /// Key-value parameters are displayed as `key=value`.
230- /// Values containing whitespace are automatically quoted.
231- fn fmt ( & self , f : & mut std:: fmt:: Formatter ) -> std:: fmt:: Result {
232- let key = self . key_lossy ( ) ;
233-
234- if self . value . is_some ( ) {
235- let value = self . value_lossy ( ) ;
236-
237- if value. chars ( ) . any ( |c| c. is_ascii_whitespace ( ) ) {
238- write ! ( f, "{key}=\" {value}\" " )
239- } else {
240- write ! ( f, "{key}={value}" )
241- }
242- } else {
243- write ! ( f, "{key}" )
244- }
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 ( ) )
245292 }
246293}
247294
@@ -287,9 +334,6 @@ mod tests {
287334 p. push ( non_utf8_byte[ 0 ] ) ;
288335 let p = Parameter :: from ( & p) ;
289336 assert_eq ! ( p. value, Some ( non_utf8_byte. as_slice( ) ) ) ;
290-
291- // lossy replacement sanity check
292- assert_eq ! ( p. value_lossy( ) , char :: REPLACEMENT_CHARACTER . to_string( ) ) ;
293337 }
294338
295339 #[ test]
@@ -472,4 +516,70 @@ mod tests {
472516 let kargs = Cmdline :: from ( & invalid_utf8) ;
473517 assert ! ( kargs. require_value_of_utf8( "invalid" ) . is_err( ) ) ;
474518 }
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+ }
475585}
0 commit comments