apple_dmg/
blkx.rs

1// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
2// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
3// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
4// option. This file may not be copied, modified, or distributed
5// except according to those terms.
6use {
7    crate::koly::UdifChecksum,
8    anyhow::Result,
9    byteorder::{ReadBytesExt, WriteBytesExt, BE},
10    std::io::{Read, Write},
11};
12
13#[derive(Clone, Debug, Eq, PartialEq)]
14pub struct BlkxTable {
15    /// currently 1
16    pub version: u32,
17    /// starting sector
18    pub sector_number: u64,
19    /// number of sectors
20    pub sector_count: u64,
21    /// seems to always be 0
22    pub data_offset: u64,
23    /// seems to be a magic constant for zlib describing the buffer size
24    /// required for decompressing a chunk.
25    pub buffers_needed: u32,
26    /// not sure what this is, setting it to the partition index
27    pub block_descriptors: u32,
28    pub reserved: [u8; 24],
29    pub checksum: UdifChecksum,
30    /// chunk table
31    pub chunks: Vec<BlkxChunk>,
32}
33
34impl Default for BlkxTable {
35    fn default() -> Self {
36        Self {
37            version: 1,
38            sector_number: 0,
39            sector_count: 0,
40            data_offset: 0,
41            //  number was taken from hdiutil
42            buffers_needed: 2056,
43            block_descriptors: 0,
44            reserved: [0; 24],
45            checksum: UdifChecksum::default(),
46            chunks: vec![],
47        }
48    }
49}
50
51impl BlkxTable {
52    pub fn new(index: u32, sector: u64, checksum: u32) -> Self {
53        Self {
54            block_descriptors: index,
55            sector_number: sector,
56            checksum: UdifChecksum::new(checksum),
57            ..Default::default()
58        }
59    }
60
61    pub fn add_chunk(&mut self, mut chunk: BlkxChunk) {
62        chunk.sector_number = self.sector_count;
63        self.sector_count += chunk.sector_count;
64        self.chunks.push(chunk);
65    }
66
67    pub fn read_from<R: Read>(r: &mut R) -> Result<Self> {
68        let mut signature = [0; 4];
69        r.read_exact(&mut signature)?;
70        anyhow::ensure!(&signature == b"mish");
71        let version = r.read_u32::<BE>()?;
72        let sector_number = r.read_u64::<BE>()?;
73        let sector_count = r.read_u64::<BE>()?;
74        let data_offset = r.read_u64::<BE>()?;
75        let buffers_needed = r.read_u32::<BE>()?;
76        let block_descriptors = r.read_u32::<BE>()?;
77        let mut reserved = [0; 24];
78        r.read_exact(&mut reserved)?;
79        let checksum = UdifChecksum::read_from(r)?;
80        let num_chunks = r.read_u32::<BE>()?;
81        let mut chunks = Vec::with_capacity(num_chunks as _);
82        for _ in 0..num_chunks {
83            chunks.push(BlkxChunk::read_from(r)?);
84        }
85        Ok(Self {
86            version,
87            sector_number,
88            sector_count,
89            data_offset,
90            buffers_needed,
91            block_descriptors,
92            reserved,
93            checksum,
94            chunks,
95        })
96    }
97
98    pub fn write_to<W: Write>(&self, w: &mut W) -> Result<()> {
99        w.write_all(b"mish")?;
100        w.write_u32::<BE>(self.version)?;
101        w.write_u64::<BE>(self.sector_number)?;
102        w.write_u64::<BE>(self.sector_count)?;
103        w.write_u64::<BE>(self.data_offset)?;
104        w.write_u32::<BE>(self.buffers_needed)?;
105        w.write_u32::<BE>(self.block_descriptors)?;
106        w.write_all(&self.reserved)?;
107        self.checksum.write_to(w)?;
108        w.write_u32::<BE>(self.chunks.len() as u32)?;
109        for chunk in &self.chunks {
110            chunk.write_to(w)?;
111        }
112        Ok(())
113    }
114}
115
116#[derive(Clone, Copy, Debug, Eq, PartialEq)]
117pub struct BlkxChunk {
118    /// compression type used for this chunk
119    pub r#type: u32,
120    pub comment: u32,
121    pub sector_number: u64,
122    pub sector_count: u64,
123    pub compressed_offset: u64,
124    pub compressed_length: u64,
125}
126
127impl Default for BlkxChunk {
128    fn default() -> Self {
129        Self {
130            r#type: ChunkType::Raw as _,
131            comment: 0,
132            sector_number: 0,
133            sector_count: 0,
134            compressed_offset: 0,
135            compressed_length: 0,
136        }
137    }
138}
139
140impl BlkxChunk {
141    pub fn new(
142        ty: ChunkType,
143        sector_number: u64,
144        sector_count: u64,
145        compressed_offset: u64,
146        compressed_length: u64,
147    ) -> Self {
148        Self {
149            r#type: ty as _,
150            sector_number,
151            sector_count,
152            compressed_offset,
153            compressed_length,
154            ..Default::default()
155        }
156    }
157
158    pub fn term(sector_number: u64, compressed_offset: u64) -> Self {
159        Self::new(ChunkType::Term, sector_number, 0, compressed_offset, 0)
160    }
161
162    pub fn read_from<R: Read>(r: &mut R) -> Result<Self> {
163        let r#type = r.read_u32::<BE>()?;
164        let comment = r.read_u32::<BE>()?;
165        let sector_number = r.read_u64::<BE>()?;
166        let sector_count = r.read_u64::<BE>()?;
167        let compressed_offset = r.read_u64::<BE>()?;
168        let compressed_length = r.read_u64::<BE>()?;
169        Ok(Self {
170            r#type,
171            comment,
172            sector_number,
173            sector_count,
174            compressed_offset,
175            compressed_length,
176        })
177    }
178
179    pub fn write_to<W: Write>(&self, w: &mut W) -> Result<()> {
180        w.write_u32::<BE>(self.r#type)?;
181        w.write_u32::<BE>(self.comment)?;
182        w.write_u64::<BE>(self.sector_number)?;
183        w.write_u64::<BE>(self.sector_count)?;
184        w.write_u64::<BE>(self.compressed_offset)?;
185        w.write_u64::<BE>(self.compressed_length)?;
186        Ok(())
187    }
188
189    pub fn ty(self) -> Option<ChunkType> {
190        ChunkType::from_u32(self.r#type)
191    }
192}
193
194/// Possible compression types of the BlkxChunk.
195#[repr(u32)]
196#[derive(Clone, Copy, Debug, Eq, PartialEq)]
197pub enum ChunkType {
198    Zero = 0x0000_0000,
199    Raw = 0x0000_0001,
200    Ignore = 0x0000_0002,
201    Comment = 0x7fff_fffe,
202    Adc = 0x8000_0004,
203    Zlib = 0x8000_0005,
204    Bzlib = 0x8000_0006,
205    Lzfse = 0x8000_0007,
206    Term = 0xffff_ffff,
207}
208
209impl ChunkType {
210    pub fn from_u32(ty: u32) -> Option<Self> {
211        Some(match ty {
212            x if x == ChunkType::Zero as u32 => ChunkType::Zero,
213            x if x == ChunkType::Raw as u32 => ChunkType::Raw,
214            x if x == ChunkType::Ignore as u32 => ChunkType::Ignore,
215            x if x == ChunkType::Comment as u32 => ChunkType::Comment,
216            x if x == ChunkType::Adc as u32 => ChunkType::Adc,
217            x if x == ChunkType::Zlib as u32 => ChunkType::Zlib,
218            x if x == ChunkType::Bzlib as u32 => ChunkType::Bzlib,
219            x if x == ChunkType::Lzfse as u32 => ChunkType::Lzfse,
220            x if x == ChunkType::Term as u32 => ChunkType::Term,
221            _ => return None,
222        })
223    }
224}