Skip to content

Commit fbee71b

Browse files
committed
feat: support shrinking and growing properties
This expands FdtPropertyMut::set_value with: - shrinking the values by padding with NOPs - expanding the values if there is a sufficient number of null bytes or NOPs after the original value
1 parent 9bd0305 commit fbee71b

3 files changed

Lines changed: 93 additions & 5 deletions

File tree

src/error.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,4 +113,8 @@ pub enum PropertyError {
113113
/// An error that can occur when mutating a device tree.
114114
#[derive(Clone, Debug, Eq, Error, PartialEq)]
115115
#[non_exhaustive]
116-
pub enum FdtMutError {}
116+
pub enum FdtMutError {
117+
/// Shifting data is required, but not supported.
118+
#[error("shifting data is required, but not supported")]
119+
ShiftingRequired,
120+
}

src/fdt_mut/property.rs

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use zerocopy::{FromBytes, big_endian};
1414
use crate::Property;
1515
use crate::error::FdtMutError;
1616
use crate::fdt::property::InnerPropIter;
17-
use crate::fdt::{FDT_TAGSIZE, Fdt, FdtProperty};
17+
use crate::fdt::{FDT_NOP, FDT_TAGSIZE, Fdt, FdtProperty};
1818

1919
/// A mutable property of a device tree node.
2020
#[derive(Debug)]
@@ -58,9 +58,7 @@ impl FdtPropertyMut<'_> {
5858
let old_padded = Fdt::align_tag_offset(self.len);
5959
let new_padded = Fdt::align_tag_offset(new_value.len());
6060

61-
if new_padded != old_padded {
62-
todo!("the new value requires shifting data, which is not yet supported");
63-
}
61+
self.ensure_new_length_fits(old_padded, new_padded)?;
6462

6563
// Update the length in the FDT property header
6664
let (len_bytes, _) =
@@ -82,11 +80,49 @@ impl FdtPropertyMut<'_> {
8280
self.data[self.value_offset + i] = 0;
8381
}
8482

83+
self.pad_with_nops(old_padded, new_padded);
84+
8585
self.len = new_value.len();
8686

8787
Ok(())
8888
}
8989

90+
fn ensure_new_length_fits(
91+
&mut self,
92+
old_padded: usize,
93+
new_padded: usize,
94+
) -> Result<(), FdtMutError> {
95+
if new_padded > old_padded {
96+
let needed_bytes = new_padded - old_padded;
97+
98+
let mut offset = self.value_offset + old_padded;
99+
for _ in 0..(needed_bytes / FDT_TAGSIZE) {
100+
if offset + FDT_TAGSIZE > self.data.len() {
101+
return Err(FdtMutError::ShiftingRequired);
102+
}
103+
104+
let tag_bytes = &self.data[offset..offset + FDT_TAGSIZE];
105+
if tag_bytes != FDT_NOP.to_be_bytes() {
106+
return Err(FdtMutError::ShiftingRequired);
107+
}
108+
109+
offset += FDT_TAGSIZE;
110+
}
111+
}
112+
113+
Ok(())
114+
}
115+
116+
fn pad_with_nops(&mut self, old_padded: usize, new_padded: usize) {
117+
if new_padded < old_padded {
118+
let mut offset = self.value_offset + new_padded;
119+
while offset < self.value_offset + old_padded {
120+
self.data[offset..offset + FDT_TAGSIZE].copy_from_slice(&FDT_NOP.to_be_bytes());
121+
offset += FDT_TAGSIZE;
122+
}
123+
}
124+
}
125+
90126
/// Returns the value of this property.
91127
#[must_use]
92128
pub fn value(&self) -> &[u8] {

tests/fdt_mut.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
// option. This file may not be copied, modified, or distributed
77
// except according to those terms.
88

9+
use dtoolkit::error::FdtMutError;
910
use dtoolkit::fdt::Fdt;
1011
use dtoolkit::fdt_mut::FdtMut;
1112
use dtoolkit::{Node, Property};
@@ -31,3 +32,50 @@ fn modify_property_in_place() {
3132
let prop = node.property("str-prop").unwrap();
3233
assert_eq!(prop.as_str().unwrap(), "hello there");
3334
}
35+
36+
#[test]
37+
fn modify_property_shrink_and_grow() {
38+
let dtb = include_bytes!("dtb/test_props.dtb");
39+
let mut data = dtb.to_vec();
40+
41+
let mut fdt_mut = FdtMut::new(&mut data).unwrap();
42+
let mut node_mut = fdt_mut.find_node_mut("/test-props").unwrap();
43+
let mut prop_mut = node_mut.property_mut("str-prop").unwrap();
44+
45+
let orig_val = b"hello world\0";
46+
assert_eq!(prop_mut.value(), orig_val);
47+
48+
// Shrink the value
49+
let short_val = b"hi\0";
50+
prop_mut.set_value(short_val).unwrap();
51+
assert_eq!(prop_mut.value(), short_val);
52+
53+
// Check it correctly parses back
54+
let fdt = Fdt::new(&data).unwrap();
55+
let node = fdt.find_node("/test-props").unwrap();
56+
let prop = node.property("str-prop").unwrap();
57+
assert_eq!(prop.as_str().unwrap(), "hi");
58+
59+
// Now grow it back, since the space is now NOPs
60+
let mut fdt_mut = FdtMut::new(&mut data).unwrap();
61+
let mut node_mut = fdt_mut.find_node_mut("/test-props").unwrap();
62+
let mut prop_mut = node_mut.property_mut("str-prop").unwrap();
63+
64+
let medium_val = b"hello\0";
65+
prop_mut.set_value(medium_val).unwrap();
66+
assert_eq!(prop_mut.value(), medium_val);
67+
68+
let fdt = Fdt::new(&data).unwrap();
69+
let node = fdt.find_node("/test-props").unwrap();
70+
let prop = node.property("str-prop").unwrap();
71+
assert_eq!(prop.as_str().unwrap(), "hello");
72+
73+
// Growing beyond the original space should fail because there are no NOPs
74+
let mut fdt_mut = FdtMut::new(&mut data).unwrap();
75+
let mut node_mut = fdt_mut.find_node_mut("/test-props").unwrap();
76+
let mut prop_mut = node_mut.property_mut("str-prop").unwrap();
77+
78+
let long_val = b"this is too long\0";
79+
let err = prop_mut.set_value(long_val).unwrap_err();
80+
assert_eq!(err, FdtMutError::ShiftingRequired);
81+
}

0 commit comments

Comments
 (0)