1use std::{
2 borrow::Cow,
3 collections::HashMap,
4 fmt::{Debug, Display, Formatter},
5};
6
7use crate::{chrp_checksum_add, slice_find, slice_rstrip, Error, Result, VarType};
8
9pub struct UnescapeVal<I> {
10 inner: I,
11 esc_out: u8,
12 remaining: u8,
13}
14
15impl<I> UnescapeVal<I>
16where
17 I: Iterator<Item = u8>,
18{
19 pub fn new(inner: I) -> Self {
20 Self {
21 inner,
22 esc_out: 0,
23 remaining: 0,
24 }
25 }
26}
27
28impl<I> Iterator for UnescapeVal<I>
29where
30 I: Iterator<Item = u8>,
31{
32 type Item = u8;
33 fn next(&mut self) -> Option<u8> {
34 if self.remaining != 0 {
35 self.remaining -= 1;
36 return Some(self.esc_out);
37 }
38 if let Some(n) = self.inner.next() {
39 if n != 0xFF {
40 return Some(n);
41 }
42 let count = self.inner.next()?;
43 self.esc_out = if count & 0x80 == 0 { 0 } else { 0xFF };
44 self.remaining = (count & 0x7F) - 1;
45 Some(self.esc_out)
46 } else {
47 None
48 }
49 }
50}
51
52#[derive(Clone)]
53pub struct CHRPHeader<'a> {
54 pub name: &'a [u8],
55 pub size: u16,
56 pub signature: u8,
57}
58
59impl Debug for CHRPHeader<'_> {
60 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
61 f.debug_struct("CHRPHeader")
62 .field("name", &String::from_utf8_lossy(self.name).into_owned())
63 .field("size", &self.size)
64 .field("signature", &self.signature)
65 .finish()
66 }
67}
68
69impl CHRPHeader<'_> {
70 pub fn parse(nvr: &[u8]) -> Result<CHRPHeader<'_>> {
71 let signature = nvr[0];
72 let cksum = nvr[1];
73 let size = u16::from_le_bytes(nvr[2..4].try_into().unwrap());
74 let name = slice_rstrip(&nvr[4..16], &0);
75 let cand = CHRPHeader {
76 name,
77 size,
78 signature,
79 };
80 if cand.checksum() != cksum {
81 return Err(Error::ParseError);
82 }
83 Ok(cand)
84 }
85 fn checksum(&self) -> u8 {
86 let mut cksum = 0;
87 for &u in self.name {
88 cksum = chrp_checksum_add(cksum, u);
89 }
90 cksum = chrp_checksum_add(cksum, self.signature);
91 cksum = chrp_checksum_add(cksum, (self.size & 0xFF) as u8);
92 chrp_checksum_add(cksum, (self.size >> 8) as u8)
93 }
94
95 pub fn serialize(&self, v: &mut Vec<u8>) {
96 v.push(self.signature);
97 v.push(self.checksum());
98 v.extend_from_slice(&self.size.to_le_bytes());
99 v.extend_from_slice(self.name);
100 for _ in 0..(12 - self.name.len()) {
101 v.push(0);
102 }
103 }
104}
105
106#[derive(Clone)]
107pub struct Variable<'a> {
108 pub key: Cow<'a, [u8]>,
109 pub value: Cow<'a, [u8]>,
110 pub typ: VarType,
111}
112
113impl<'a> crate::Variable<'a> for Variable<'a> {
114 fn value(&self) -> Cow<'a, [u8]> {
115 Cow::Owned(UnescapeVal::new(self.value.iter().copied()).collect())
116 }
117}
118
119impl Display for Variable<'_> {
120 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
121 let key = String::from_utf8_lossy(&self.key);
122 let mut value = String::new();
123 for c in UnescapeVal::new(self.value.iter().copied()) {
124 if (c as char).is_ascii() && !(c as char).is_ascii_control() {
125 value.push(c as char);
126 } else {
127 value.push_str(&format!("%{c:02x}"));
128 }
129 }
130
131 let value: String = value.chars().take(128).collect();
132 write!(f, "{}:{}={}", self.typ, key, value)
133 }
134}
135
136#[derive(Clone)]
137pub struct Section<'a> {
138 pub header: CHRPHeader<'a>,
139 pub values: HashMap<Cow<'a, [u8]>, Variable<'a>>,
140}
141
142impl Section<'_> {
143 pub fn parse(mut nvr: &[u8]) -> Result<Section<'_>> {
144 let header = CHRPHeader::parse(&nvr[..16])?;
145 nvr = &nvr[16..];
146 let mut values = HashMap::new();
147 loop {
148 let zero = slice_find(nvr, &0);
149 if zero.is_none() {
150 break;
151 }
152 let zero = zero.unwrap();
153 let cand = &nvr[..zero];
154 let eq = slice_find(cand, &b'=');
155 if eq.is_none() {
156 break;
157 }
158 let eq = eq.unwrap();
159 let key = &cand[..eq];
160 let typ = if header.name == b"common" {
161 VarType::Common
162 } else {
163 VarType::System
164 };
165 values.insert(
166 Cow::Borrowed(key),
167 Variable {
168 key: Cow::Borrowed(key),
169 value: Cow::Borrowed(&cand[(eq + 1)..]),
170 typ,
171 },
172 );
173 nvr = &nvr[(zero + 1)..]
174 }
175 Ok(Section { header, values })
176 }
177 fn size_bytes(&self) -> usize {
178 self.header.size as usize * 16
179 }
180 pub fn serialize(&self, v: &mut Vec<u8>) -> Result<()> {
181 let start_size = v.len();
182 self.header.serialize(v);
183 for val in self.values.values() {
184 v.extend_from_slice(&val.key);
185 v.push(b'=');
186 v.extend_from_slice(&val.value);
187 v.push(0);
188 }
189 let my_size = v.len() - start_size;
190 if my_size > self.size_bytes() {
191 return Err(Error::SectionTooBig);
192 }
193 for _ in 0..(self.size_bytes() - my_size) {
194 v.push(0);
195 }
196 Ok(())
197 }
198}
199
200struct SectionDebug<'a, 'b>(&'a Section<'b>);
201impl Debug for SectionDebug<'_, '_> {
202 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
203 let mut m = f.debug_map();
204 for v in self.0.values.values() {
205 m.entry(
206 &String::from_utf8_lossy(&v.key).into_owned(),
207 &String::from_utf8_lossy(
208 &UnescapeVal::new(v.value.iter().copied()).collect::<Vec<_>>(),
209 )
210 .into_owned(),
211 );
212 }
213 m.finish()
214 }
215}
216
217impl Debug for Section<'_> {
218 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
219 f.debug_struct("Section")
220 .field("header", &self.header)
221 .field("values", &SectionDebug(self))
222 .finish()
223 }
224}
225
226#[derive(Debug, Clone)]
227pub struct Partition<'a> {
228 pub header: CHRPHeader<'a>,
229 pub generation: u32,
230 pub common: Section<'a>,
231 pub system: Section<'a>,
232}
233
234impl<'a> Partition<'a> {
235 pub fn parse(nvr: &[u8]) -> Result<Partition<'_>> {
236 let header = CHRPHeader::parse(&nvr[..16])?;
237 if header.name != b"nvram" {
238 return Err(Error::ParseError);
239 }
240 let adler = u32::from_le_bytes(nvr[16..20].try_into().unwrap());
241 let generation = u32::from_le_bytes(nvr[20..24].try_into().unwrap());
242 let sec1 = Section::parse(&nvr[32..])?;
243 let sec2 = Section::parse(&nvr[(32 + sec1.size_bytes())..])?;
244 let calc_adler =
245 adler32::adler32(&nvr[20..(32 + sec1.size_bytes() + sec2.size_bytes())]).unwrap();
246 if adler != calc_adler {
247 return Err(Error::ParseError);
248 }
249 let mut com = None;
250 let mut sys = None;
251 if sec1.header.name == b"common" {
252 com = Some(sec1);
253 } else if sec1.header.name == b"system" {
254 sys = Some(sec1);
255 }
256 if sec2.header.name == b"common" {
257 com = Some(sec2);
258 } else if sec2.header.name == b"system" {
259 sys = Some(sec2);
260 }
261 if com.is_none() || sys.is_none() {
262 return Err(Error::ParseError);
263 }
264 Ok(Partition {
265 header,
266 generation,
267 common: com.unwrap(),
268 system: sys.unwrap(),
269 })
270 }
271 fn size_bytes(&self) -> usize {
272 32 + self.common.size_bytes() + self.system.size_bytes()
273 }
274 pub fn serialize(&self, v: &mut Vec<u8>) -> Result<()> {
275 self.header.serialize(v);
276 v.extend_from_slice(&[0; 4]);
277 let adler_start = v.len();
278 v.extend_from_slice(&self.generation.to_le_bytes());
279 v.extend_from_slice(&[0; 8]);
280 self.common.serialize(v)?;
281 self.system.serialize(v)?;
282 let adler_end = v.len();
283 let adler = adler32::adler32(&v[adler_start..adler_end]).unwrap();
284 v[(adler_start - 4)..adler_start].copy_from_slice(&adler.to_le_bytes());
285 Ok(())
286 }
287
288 pub fn variables(&self) -> impl Iterator<Item = &Variable<'a>> {
289 self.common
290 .values
291 .values()
292 .chain(self.system.values.values())
293 }
294}
295
296impl<'a> crate::Partition<'a> for Partition<'a> {
297 fn get_variable(&self, key: &[u8], typ: VarType) -> Option<&dyn crate::Variable<'a>> {
298 match typ {
299 VarType::Common => self
300 .common
301 .values
302 .get(key)
303 .map(|v| v as &dyn crate::Variable),
304 VarType::System => self
305 .system
306 .values
307 .get(key)
308 .map(|v| v as &dyn crate::Variable),
309 }
310 }
311
312 fn insert_variable(&mut self, key: &[u8], value: Cow<'a, [u8]>, typ: VarType) {
313 match typ {
314 VarType::Common => &mut self.common,
315 VarType::System => &mut self.system,
316 }
317 .values
318 .insert(
319 Cow::Owned(key.into()),
320 Variable {
321 key: Cow::Owned(key.into()),
322 value,
323 typ,
324 },
325 );
326 }
327
328 fn remove_variable(&mut self, key: &[u8], typ: VarType) {
329 match typ {
330 VarType::Common => &mut self.common,
331 VarType::System => &mut self.system,
332 }
333 .values
334 .remove(key);
335 }
336
337 fn variables(&self) -> Box<dyn Iterator<Item = &dyn crate::Variable<'a>> + '_> {
338 Box::new(self.variables().map(|e| e as &dyn crate::Variable<'a>))
339 }
340}
341
342impl Display for Partition<'_> {
343 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
344 write!(
345 f,
346 "size: {}, generation: {}, count: {}",
347 self.header.size,
348 self.generation,
349 self.common.values.len() + self.system.values.len(),
350 )
351 }
352}
353
354#[derive(Debug)]
355pub struct Nvram<'a> {
356 pub partitions: [Partition<'a>; 2],
357 pub active: usize,
358}
359
360impl<'a> Nvram<'a> {
361 pub fn parse(nvr: &[u8]) -> Result<Nvram<'_>> {
362 let p1;
363 let p2;
364 match (Partition::parse(nvr), Partition::parse(&nvr[0x10000..])) {
365 (Err(err), Err(_)) => return Err(err),
366 (Ok(p1r), Err(_)) => {
367 p1 = p1r;
368 p2 = p1.clone();
369 }
370 (Err(_), Ok(p2r)) => {
371 p2 = p2r;
372 p1 = p2.clone();
373 }
374 (Ok(p1r), Ok(p2r)) => {
375 p1 = p1r;
376 p2 = p2r;
377 }
378 }
379 let active = if p1.generation > p2.generation { 0 } else { 1 };
380 let partitions = [p1, p2];
381 Ok(Nvram { partitions, active })
382 }
383
384 pub fn partitions(&self) -> impl Iterator<Item = &Partition<'a>> {
385 self.partitions.iter()
386 }
387}
388
389impl<'a> crate::Nvram<'a> for Nvram<'a> {
390 fn serialize(&self) -> Result<Vec<u8>> {
391 let mut v = Vec::with_capacity(self.partitions[0].size_bytes() * 2);
392 self.partitions[0].serialize(&mut v)?;
393 self.partitions[1].serialize(&mut v)?;
394 Ok(v)
395 }
396 fn prepare_for_write(&mut self) {
397 let inactive = 1 - self.active;
398 self.partitions[inactive] = self.partitions[self.active].clone();
399 self.partitions[inactive].generation += 1;
400 self.active = inactive;
401 }
402 fn active_part_mut(&mut self) -> &mut dyn crate::Partition<'a> {
406 &mut self.partitions[self.active] as &mut dyn crate::Partition<'a>
407 }
408
409 fn partitions(&self) -> Box<dyn Iterator<Item = &dyn crate::Partition<'a>> + '_> {
410 Box::new(self.partitions().map(|e| e as &dyn crate::Partition<'a>))
411 }
412
413 fn apply(&mut self, w: &mut dyn crate::NvramWriter) -> Result<()> {
414 let data = self.serialize()?;
415 w.erase_if_needed(0, data.len());
416 w.write_all(0, &data).map_err(|e| Error::ApplyError(e))?;
417 Ok(())
418 }
419}