44//! This module writes Flattened Devicetree blobs as defined here:
55//! <https://devicetree-specification.readthedocs.io/en/stable/flattened-format.html>
66
7+ use alloc:: borrow:: ToOwned ;
78use alloc:: collections:: BTreeMap ;
89use alloc:: ffi:: CString ;
910use alloc:: string:: String ;
1011use alloc:: vec:: Vec ;
1112use core:: cmp:: { Ord , Ordering } ;
1213use core:: convert:: TryInto ;
14+ use core:: ffi:: CStr ;
1315use core:: fmt;
1416use core:: mem:: size_of_val;
1517#[ cfg( feature = "std" ) ]
@@ -216,21 +218,28 @@ fn node_name_valid_first_char(c: char) -> bool {
216218// Check if `name` is a valid property name.
217219// https://devicetree-specification.readthedocs.io/en/stable/devicetree-basics.html#property-names
218220fn property_name_valid ( name : & str ) -> bool {
219- if name. is_empty ( ) || name. len ( ) > PROPERTY_NAME_MAX_LEN {
220- return false ;
221- }
222-
223- if name. contains ( |c : char | !property_name_valid_char ( c) ) {
224- return false ;
225- }
221+ !( name. is_empty ( )
222+ || name. len ( ) > PROPERTY_NAME_MAX_LEN
223+ || name. contains ( |c| !property_name_valid_char ( c) ) )
224+ }
226225
227- true
226+ // Check if `name` is a valid property name.
227+ // https://devicetree-specification.readthedocs.io/en/stable/devicetree-basics.html#property-names
228+ fn property_cstr_name_valid ( name : & CStr ) -> bool {
229+ let name_bytes = name. to_bytes ( ) ;
230+ !( name_bytes. is_empty ( )
231+ || name_bytes. len ( ) > PROPERTY_NAME_MAX_LEN
232+ || name_bytes. iter ( ) . any ( |& c| !property_name_valid_byte ( c) ) )
228233}
229234
230235fn property_name_valid_char ( c : char ) -> bool {
231236 matches ! ( c, '0' ..='9' | 'a' ..='z' | 'A' ..='Z' | ',' | '.' | '_' | '+' | '?' | '#' | '-' )
232237}
233238
239+ fn property_name_valid_byte ( c : u8 ) -> bool {
240+ matches ! ( c, b'0' ..=b'9' | b'a' ..=b'z' | b'A' ..=b'Z' | b',' | b'.' | b'_' | b'+' | b'?' | b'#' | b'-' )
241+ }
242+
234243/// Handle to an open node created by `FdtWriter::begin_node`.
235244///
236245/// This must be passed back to `FdtWriter::end_node` to close the nodes.
@@ -390,19 +399,19 @@ impl FdtWriter {
390399
391400 // Find an existing instance of a string `s`, or add it to the strings block.
392401 // Returns the offset into the strings block.
393- fn intern_string ( & mut self , s : CString ) -> Result < u32 > {
394- if let Some ( off) = self . string_offsets . get ( & s) {
395- Ok ( * off)
402+ fn intern_string ( & mut self , s : & CStr ) -> Result < u32 > {
403+ Ok ( if let Some ( off) = self . string_offsets . get ( s) {
404+ * off
396405 } else {
397406 let off = self
398407 . strings
399408 . len ( )
400409 . try_into ( )
401410 . map_err ( |_| Error :: TotalSizeTooLarge ) ?;
402411 self . strings . extend_from_slice ( s. to_bytes_with_nul ( ) ) ;
403- self . string_offsets . insert ( s, off) ;
404- Ok ( off)
405- }
412+ self . string_offsets . insert ( s. to_owned ( ) , off) ;
413+ off
414+ } )
406415 }
407416
408417 /// Write a property.
@@ -431,11 +440,45 @@ impl FdtWriter {
431440 . try_into ( )
432441 . map_err ( |_| Error :: PropertyValueTooLarge ) ?;
433442
434- let nameoff = self . intern_string ( name_cstr) ?;
443+ let nameoff = self . intern_string ( & name_cstr) ?;
444+ self . append_u32 ( FDT_PROP ) ;
445+ self . append_u32 ( len) ;
446+ self . append_u32 ( nameoff) ;
447+ self . data . extend_from_slice ( val) ;
448+ self . align ( 4 ) ;
449+ Ok ( ( ) )
450+ }
451+
452+ /// Write a property, but add a NUL byte after `val` and make sure that `val` doesn't contain NUL bytes.
453+ /// Otherwise identical to [`FdtWriter::property`].
454+ ///
455+ /// The purpose of this is to avoid unnecessary intermediate allocations.
456+ fn property_and_nul ( & mut self , name : & CStr , val : & [ u8 ] ) -> Result < ( ) > {
457+ if self . node_ended {
458+ return Err ( Error :: PropertyAfterEndNode ) ;
459+ }
460+
461+ if self . node_depth == 0 {
462+ return Err ( Error :: PropertyBeforeBeginNode ) ;
463+ }
464+
465+ if !property_cstr_name_valid ( name) {
466+ return Err ( Error :: InvalidPropertyName ) ;
467+ }
468+ if val. contains ( & 0x00 ) {
469+ return Err ( Error :: InvalidString ) ;
470+ }
471+
472+ let len = ( val. len ( ) + 1 )
473+ . try_into ( )
474+ . map_err ( |_| Error :: PropertyValueTooLarge ) ?;
475+
476+ let nameoff = self . intern_string ( name) ?;
435477 self . append_u32 ( FDT_PROP ) ;
436478 self . append_u32 ( len) ;
437479 self . append_u32 ( nameoff) ;
438480 self . data . extend_from_slice ( val) ;
481+ self . data . push ( 0x00 ) ;
439482 self . align ( 4 ) ;
440483 Ok ( ( ) )
441484 }
@@ -447,16 +490,22 @@ impl FdtWriter {
447490
448491 /// Write a string property.
449492 pub fn property_string ( & mut self , name : & str , val : & str ) -> Result < ( ) > {
450- let cstr_value = CString :: new ( val) . map_err ( |_| Error :: InvalidString ) ?;
451- self . property ( name, cstr_value. to_bytes_with_nul ( ) )
493+ self . property_and_nul (
494+ & CString :: new ( name) . map_err ( |_| Error :: InvalidString ) ?,
495+ val. as_bytes ( ) ,
496+ )
452497 }
453498
454499 /// Write a stringlist property.
455500 pub fn property_string_list ( & mut self , name : & str , values : Vec < String > ) -> Result < ( ) > {
456501 let mut bytes = Vec :: new ( ) ;
457502 for s in values {
458- let cstr = CString :: new ( s) . map_err ( |_| Error :: InvalidString ) ?;
459- bytes. extend_from_slice ( cstr. to_bytes_with_nul ( ) ) ;
503+ let s = s. as_bytes ( ) ;
504+ if s. contains ( & 0x00 ) {
505+ return Err ( Error :: InvalidString ) ;
506+ }
507+ bytes. extend_from_slice ( s) ;
508+ bytes. push ( 0x00 ) ;
460509 }
461510 self . property ( name, & bytes)
462511 }
0 commit comments