@@ -37,6 +37,10 @@ pub enum Error {
3737 MmioSize ,
3838 /// Operation would have made the command line too large.
3939 TooLarge ,
40+ /// Double-quotes can be used to protect spaces in values
41+ NoQuoteSpace ,
42+ /// A double quote that is in the middle of the value
43+ InvalidQuote ,
4044}
4145
4246impl fmt:: Display for Error {
@@ -56,6 +60,11 @@ impl fmt::Display for Error {
5660 "0-sized virtio MMIO device passed to the kernel command line builder."
5761 ) ,
5862 Error :: TooLarge => write ! ( f, "Inserting string would make command line too long." ) ,
63+ Error :: NoQuoteSpace => write ! (
64+ f,
65+ "Value that contains spaces need to be surrounded by quotes"
66+ ) ,
67+ Error :: InvalidQuote => write ! ( f, "Double quote can not be in the middle of the value" ) ,
5968 }
6069 }
6170}
@@ -79,7 +88,26 @@ fn valid_str(s: &str) -> Result<()> {
7988 }
8089}
8190
82- fn valid_element ( s : & str ) -> Result < ( ) > {
91+ fn is_quoted ( s : & str ) -> bool {
92+ if s. len ( ) < 2 {
93+ return false ;
94+ }
95+ if let Some ( first_char) = s. chars ( ) . next ( ) {
96+ if let Some ( last_char) = s. chars ( ) . last ( ) {
97+ return first_char == '"' && last_char == '"' ;
98+ }
99+ }
100+ false
101+ }
102+
103+ fn contains_double_quotes ( s : & str ) -> bool {
104+ if s. len ( ) < 3 {
105+ return false ;
106+ }
107+ return s. chars ( ) . skip ( 1 ) . take ( s. len ( ) - 2 ) . any ( |c| c == '"' ) ;
108+ }
109+
110+ fn valid_key ( s : & str ) -> Result < ( ) > {
83111 if !s. chars ( ) . all ( valid_char) {
84112 Err ( Error :: InvalidAscii )
85113 } else if s. contains ( ' ' ) {
@@ -91,6 +119,18 @@ fn valid_element(s: &str) -> Result<()> {
91119 }
92120}
93121
122+ fn valid_value ( s : & str ) -> Result < ( ) > {
123+ if !s. chars ( ) . all ( valid_char) {
124+ Err ( Error :: InvalidAscii )
125+ } else if contains_double_quotes ( s) {
126+ Err ( Error :: InvalidQuote )
127+ } else if s. contains ( ' ' ) && !is_quoted ( s) {
128+ Err ( Error :: NoQuoteSpace )
129+ } else {
130+ Ok ( ( ) )
131+ }
132+ }
133+
94134/// A builder for a kernel command line string that validates the string as it's being built.
95135///
96136/// # Examples
@@ -155,8 +195,8 @@ impl Cmdline {
155195 let k = key. as_ref ( ) ;
156196 let v = val. as_ref ( ) ;
157197
158- valid_element ( k) ?;
159- valid_element ( v) ?;
198+ valid_key ( k) ?;
199+ valid_value ( v) ?;
160200
161201 let kv_str = format ! ( "{}={}" , k, v) ;
162202
@@ -186,7 +226,7 @@ impl Cmdline {
186226 pub fn insert_multiple < T : AsRef < str > > ( & mut self , key : T , vals : & [ T ] ) -> Result < ( ) > {
187227 let k = key. as_ref ( ) ;
188228
189- valid_element ( k) ?;
229+ valid_key ( k) ?;
190230 if vals. is_empty ( ) {
191231 return Err ( Error :: MissingVal ( k. to_string ( ) ) ) ;
192232 }
@@ -196,7 +236,7 @@ impl Cmdline {
196236 k,
197237 vals. iter( )
198238 . map( |v| -> Result <& str > {
199- valid_element ( v. as_ref( ) ) ?;
239+ valid_value ( v. as_ref( ) ) ?;
200240 Ok ( v. as_ref( ) )
201241 } )
202242 . collect:: <Result <Vec <& str >>>( ) ?
@@ -567,20 +607,29 @@ mod tests {
567607 fn test_insert_space ( ) {
568608 let mut cl = Cmdline :: new ( 100 ) . unwrap ( ) ;
569609 assert_eq ! ( cl. insert( "a " , "b" ) , Err ( Error :: HasSpace ) ) ;
570- assert_eq ! ( cl. insert( "a" , "b " ) , Err ( Error :: HasSpace ) ) ;
610+ assert_eq ! ( cl. insert( "a" , "b " ) , Err ( Error :: NoQuoteSpace ) ) ;
571611 assert_eq ! ( cl. insert( "a " , "b " ) , Err ( Error :: HasSpace ) ) ;
572612 assert_eq ! ( cl. insert( " a" , "b" ) , Err ( Error :: HasSpace ) ) ;
613+ assert_eq ! ( cl. insert( "a" , "hello \" world" ) , Err ( Error :: InvalidQuote ) ) ;
614+ assert_eq ! (
615+ cl. insert( "a" , "\" foor bar\" \" foor bar\" " ) ,
616+ Err ( Error :: InvalidQuote )
617+ ) ;
573618 assert_eq ! ( cl. as_cstring( ) . unwrap( ) . as_bytes_with_nul( ) , b"\0 " ) ;
619+ assert ! ( cl. insert( "a" , "\" b b\" " ) . is_ok( ) ) ;
620+ assert ! ( cl. insert( "c" , "\" d\" " ) . is_ok( ) ) ;
621+ assert_eq ! (
622+ cl. as_cstring( ) . unwrap( ) . as_bytes_with_nul( ) ,
623+ b"a=\" b b\" c=\" d\" \0 "
624+ ) ;
574625 }
575626
576627 #[ test]
577628 fn test_insert_equals ( ) {
578629 let mut cl = Cmdline :: new ( 100 ) . unwrap ( ) ;
579630 assert_eq ! ( cl. insert( "a=" , "b" ) , Err ( Error :: HasEquals ) ) ;
580- assert_eq ! ( cl. insert( "a" , "b=" ) , Err ( Error :: HasEquals ) ) ;
581631 assert_eq ! ( cl. insert( "a=" , "b " ) , Err ( Error :: HasEquals ) ) ;
582632 assert_eq ! ( cl. insert( "=a" , "b" ) , Err ( Error :: HasEquals ) ) ;
583- assert_eq ! ( cl. insert( "a" , "=b" ) , Err ( Error :: HasEquals ) ) ;
584633 assert_eq ! ( cl. as_cstring( ) . unwrap( ) . as_bytes_with_nul( ) , b"\0 " ) ;
585634 }
586635
@@ -702,7 +751,10 @@ mod tests {
702751 cl. insert_multiple( "foo" , & no_vals) ,
703752 Err ( Error :: MissingVal ( "foo" . to_string( ) ) )
704753 ) ;
705- assert_eq ! ( cl. insert_multiple( "foo" , & [ "bar " ] ) , Err ( Error :: HasSpace ) ) ;
754+ assert_eq ! (
755+ cl. insert_multiple( "foo" , & [ "bar " ] ) ,
756+ Err ( Error :: NoQuoteSpace )
757+ ) ;
706758 assert_eq ! (
707759 cl. insert_multiple( "foo" , & [ "bar" , "baz" ] ) ,
708760 Err ( Error :: TooLarge )
0 commit comments