apple_nvram/
v1v2.rs

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(&self) -> &Partition<'a> {
403    //     &self.partitions[self.active]
404    // }
405    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}