@@ -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,47 +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
- #[ derive( Debug , PartialEq , Eq ) ]
160
+ #[ derive( Debug , Eq ) ]
126
161
pub ( crate ) struct Parameter < ' a > {
162
+ /// The full original value
163
+ pub parameter : & ' a [ u8 ] ,
127
164
/// The parameter key as raw bytes
128
165
pub key : ParameterKey < ' a > ,
129
166
/// The parameter value as raw bytes, if present
130
167
pub value : Option < & ' a [ u8 ] > ,
131
168
}
132
169
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
+ }
151
180
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) )
157
187
}
188
+ }
158
189
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
165
193
}
166
194
}
167
195
@@ -177,6 +205,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> From<&'a T> for Parameter<'a> {
177
205
178
206
match equals {
179
207
None => Self {
208
+ parameter : input,
180
209
key : ParameterKey ( input) ,
181
210
value : None ,
182
211
} ,
@@ -196,6 +225,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> From<&'a T> for Parameter<'a> {
196
225
. unwrap_or ( value) ;
197
226
198
227
Self {
228
+ parameter : input,
199
229
key,
200
230
value : Some ( value) ,
201
231
}
@@ -228,29 +258,40 @@ impl PartialEq for ParameterKey<'_> {
228
258
}
229
259
}
230
260
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) )
248
270
} else {
249
- write ! ( f, "{key}" )
271
+ ( parameter, None )
272
+ } ;
273
+ let key = ParameterKeyStr ( key) ;
274
+ ParameterStr {
275
+ parameter,
276
+ key,
277
+ value,
250
278
}
251
279
}
252
280
}
253
281
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
+
254
295
#[ cfg( test) ]
255
296
mod tests {
256
297
use super :: * ;
@@ -293,9 +334,6 @@ mod tests {
293
334
p. push ( non_utf8_byte[ 0 ] ) ;
294
335
let p = Parameter :: from ( & p) ;
295
336
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( ) ) ;
299
337
}
300
338
301
339
#[ test]
@@ -334,14 +372,9 @@ mod tests {
334
372
let kargs = Cmdline :: from ( b"foo=bar,bar2 baz=fuz wiz" . as_slice ( ) ) ;
335
373
let mut iter = kargs. iter ( ) ;
336
374
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" ) ) ) ;
345
378
assert_eq ! ( iter. next( ) , None ) ;
346
379
347
380
// Test the find API
@@ -483,4 +516,70 @@ mod tests {
483
516
let kargs = Cmdline :: from ( & invalid_utf8) ;
484
517
assert ! ( kargs. require_value_of_utf8( "invalid" ) . is_err( ) ) ;
485
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
+ }
486
585
}
0 commit comments