@@ -8,9 +8,9 @@ use std::borrow::Cow;
8
8
use anyhow:: Result ;
9
9
10
10
/// This is used by dracut.
11
- pub ( crate ) const INITRD_ARG_PREFIX : & [ u8 ] = b "rd.";
11
+ pub ( crate ) const INITRD_ARG_PREFIX : & str = "rd." ;
12
12
/// The kernel argument for configuring the rootfs flags.
13
- pub ( crate ) const ROOTFLAGS : & [ u8 ] = b "rootflags";
13
+ pub ( crate ) const ROOTFLAGS : & str = "rootflags" ;
14
14
15
15
/// A parsed kernel command line.
16
16
///
@@ -41,7 +41,7 @@ impl<'a> Cmdline<'a> {
41
41
/// Properly handles quoted values containing whitespace and splits on
42
42
/// unquoted whitespace characters. Parameters are parsed as either
43
43
/// 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 {
45
45
let mut in_quotes = false ;
46
46
47
47
self . 0
@@ -63,6 +63,29 @@ impl<'a> Cmdline<'a> {
63
63
self . iter ( ) . find ( |p| p. key == key)
64
64
}
65
65
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
+
66
89
/// Locate the value of the kernel argument with the given key name.
67
90
///
68
91
/// 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> {
121
144
}
122
145
}
123
146
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
+
124
159
/// A single kernel command line parameter.
125
160
#[ derive( Debug , Eq ) ]
126
161
pub ( crate ) struct Parameter < ' a > {
127
162
/// The full original value
128
- #[ allow( dead_code) ]
129
163
pub parameter : & ' a [ u8 ] ,
130
164
/// The parameter key as raw bytes
131
165
pub key : ParameterKey < ' a > ,
132
166
/// The parameter value as raw bytes, if present
133
167
pub value : Option < & ' a [ u8 ] > ,
134
168
}
135
169
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
+
136
181
impl < ' 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 ) )
142
187
}
188
+ }
143
189
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
150
193
}
151
194
}
152
195
@@ -215,33 +258,37 @@ impl PartialEq for ParameterKey<'_> {
215
258
}
216
259
}
217
260
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
+
218
282
impl < ' a > PartialEq for Parameter < ' a > {
219
283
fn eq ( & self , other : & Self ) -> bool {
220
284
// Note we don't compare parameter because we want hyphen-dash insensitivity for the key
221
285
self . key == other. key && self . value == other. value
222
286
}
223
287
}
224
288
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 ( ) )
245
292
}
246
293
}
247
294
@@ -287,9 +334,6 @@ mod tests {
287
334
p. push ( non_utf8_byte[ 0 ] ) ;
288
335
let p = Parameter :: from ( & p) ;
289
336
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( ) ) ;
293
337
}
294
338
295
339
#[ test]
@@ -472,4 +516,70 @@ mod tests {
472
516
let kargs = Cmdline :: from ( & invalid_utf8) ;
473
517
assert ! ( kargs. require_value_of_utf8( "invalid" ) . is_err( ) ) ;
474
518
}
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
+ }
475
585
}
0 commit comments