apple_dmg/
koly.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    anyhow::Result,
8    byteorder::{ReadBytesExt, WriteBytesExt, BE},
9    std::io::{Read, Seek, SeekFrom, Write},
10};
11
12#[derive(Clone, Copy, Debug, Eq, PartialEq)]
13pub struct UdifChecksum {
14    pub r#type: u32,
15    pub size: u32,
16    pub data: [u8; 128],
17}
18
19impl Default for UdifChecksum {
20    fn default() -> Self {
21        Self {
22            r#type: 2,
23            size: 32,
24            data: [0; 128],
25        }
26    }
27}
28
29impl UdifChecksum {
30    pub fn new(crc32: u32) -> Self {
31        let mut data = [0; 128];
32        data[..4].copy_from_slice(&crc32.to_be_bytes());
33        Self {
34            data,
35            ..Default::default()
36        }
37    }
38
39    pub fn from_bytes(bytes: &[u8]) -> Self {
40        Self::new(crc32fast::hash(bytes))
41    }
42
43    pub fn read_from<R: Read>(r: &mut R) -> Result<Self> {
44        let r#type = r.read_u32::<BE>()?;
45        let size = r.read_u32::<BE>()?;
46        let mut data = [0; 128];
47        r.read_exact(&mut data)?;
48        Ok(Self { r#type, size, data })
49    }
50
51    pub fn write_to<W: Write>(&self, w: &mut W) -> Result<()> {
52        w.write_u32::<BE>(self.r#type)?;
53        w.write_u32::<BE>(self.size)?;
54        w.write_all(&self.data)?;
55        Ok(())
56    }
57}
58
59impl From<UdifChecksum> for u32 {
60    fn from(checksum: UdifChecksum) -> Self {
61        let mut data = [0; 4];
62        data.copy_from_slice(&checksum.data[..4]);
63        u32::from_be_bytes(data)
64    }
65}
66
67const KOLY_SIZE: i64 = 512;
68
69/// DMG trailer describing file content.
70///
71/// This is the main structure defining a DMG.
72#[derive(Clone, Copy, Debug, Eq, PartialEq)]
73pub struct KolyTrailer {
74    // "koly" signature: [u8; 4],
75    pub version: u32,
76    //header_size: u32,
77    pub flags: u32,
78    pub running_data_fork_offset: u64,
79    pub data_fork_offset: u64,
80    pub data_fork_length: u64,
81    pub resource_fork_offset: u64,
82    pub resource_fork_length: u64,
83    pub segment_number: u32,
84    pub segment_count: u32,
85    pub segment_id: [u8; 16],
86    pub data_fork_digest: UdifChecksum,
87    pub plist_offset: u64,
88    pub plist_length: u64,
89    pub reserved1: [u8; 64],
90    pub code_signature_offset: u64,
91    pub code_signature_size: u64,
92    pub reserved2: [u8; 40],
93    pub main_digest: UdifChecksum,
94    pub image_variant: u32,
95    pub sector_count: u64,
96    pub reserved3: [u8; 12],
97}
98
99impl Default for KolyTrailer {
100    fn default() -> Self {
101        Self {
102            version: 4,
103            flags: 1,
104            running_data_fork_offset: 0,
105            data_fork_offset: 0,
106            data_fork_length: 0,
107            resource_fork_offset: 0,
108            resource_fork_length: 0,
109            segment_number: 1,
110            segment_count: 1,
111            segment_id: [0; 16],
112            data_fork_digest: UdifChecksum::default(),
113            plist_offset: 0,
114            plist_length: 0,
115            reserved1: [0; 64],
116            code_signature_offset: 0,
117            code_signature_size: 0,
118            reserved2: [0; 40],
119            main_digest: UdifChecksum::default(),
120            image_variant: 1,
121            sector_count: 0,
122            reserved3: [0; 12],
123        }
124    }
125}
126
127impl KolyTrailer {
128    pub fn new(
129        data_fork_length: u64,
130        sectors: u64,
131        plist_offset: u64,
132        plist_length: u64,
133        data_digest: u32,
134        main_digest: u32,
135    ) -> Self {
136        let mut segment_id = [0; 16];
137        getrandom::getrandom(&mut segment_id).unwrap();
138        Self {
139            data_fork_length,
140            sector_count: sectors,
141            plist_offset,
142            plist_length,
143            data_fork_digest: UdifChecksum::new(data_digest),
144            main_digest: UdifChecksum::new(main_digest),
145            segment_id,
146            ..Default::default()
147        }
148    }
149
150    /// Construct an instance by reading from a seekable reader.
151    ///
152    /// The trailer is the final 512 bytes of the seekable stream.
153    pub fn read_from<R: Read + Seek>(r: &mut R) -> Result<Self> {
154        r.seek(SeekFrom::End(-KOLY_SIZE))?;
155
156        let mut signature = [0; 4];
157        r.read_exact(&mut signature)?;
158        anyhow::ensure!(&signature == b"koly");
159        let version = r.read_u32::<BE>()?;
160        let header_size = r.read_u32::<BE>()?;
161        anyhow::ensure!(header_size == 512);
162        let flags = r.read_u32::<BE>()?;
163        let running_data_fork_offset = r.read_u64::<BE>()?;
164        let data_fork_offset = r.read_u64::<BE>()?;
165        let data_fork_length = r.read_u64::<BE>()?;
166        let resource_fork_offset = r.read_u64::<BE>()?;
167        let resource_fork_length = r.read_u64::<BE>()?;
168        let segment_number = r.read_u32::<BE>()?;
169        let segment_count = r.read_u32::<BE>()?;
170        let mut segment_id = [0; 16];
171        r.read_exact(&mut segment_id)?;
172        let data_fork_digest = UdifChecksum::read_from(r)?;
173        let plist_offset = r.read_u64::<BE>()?;
174        let plist_length = r.read_u64::<BE>()?;
175        let mut reserved1 = [0; 64];
176        r.read_exact(&mut reserved1)?;
177        let code_signature_offset = r.read_u64::<BE>()?;
178        let code_signature_size = r.read_u64::<BE>()?;
179        let mut reserved2 = [0; 40];
180        r.read_exact(&mut reserved2)?;
181        let main_digest = UdifChecksum::read_from(r)?;
182        let image_variant = r.read_u32::<BE>()?;
183        let sector_count = r.read_u64::<BE>()?;
184        let mut reserved3 = [0; 12];
185        r.read_exact(&mut reserved3)?;
186        Ok(Self {
187            version,
188            flags,
189            running_data_fork_offset,
190            data_fork_offset,
191            data_fork_length,
192            resource_fork_offset,
193            resource_fork_length,
194            segment_number,
195            segment_count,
196            segment_id,
197            data_fork_digest,
198            plist_offset,
199            plist_length,
200            reserved1,
201            code_signature_offset,
202            code_signature_size,
203            reserved2,
204            main_digest,
205            image_variant,
206            sector_count,
207            reserved3,
208        })
209    }
210
211    pub fn write_to<W: Write>(&self, w: &mut W) -> Result<()> {
212        w.write_all(b"koly")?;
213        w.write_u32::<BE>(self.version)?;
214        w.write_u32::<BE>(KOLY_SIZE as u32)?;
215        w.write_u32::<BE>(self.flags)?;
216        w.write_u64::<BE>(self.running_data_fork_offset)?;
217        w.write_u64::<BE>(self.data_fork_offset)?;
218        w.write_u64::<BE>(self.data_fork_length)?;
219        w.write_u64::<BE>(self.resource_fork_offset)?;
220        w.write_u64::<BE>(self.resource_fork_length)?;
221        w.write_u32::<BE>(self.segment_number)?;
222        w.write_u32::<BE>(self.segment_count)?;
223        w.write_all(&self.segment_id)?;
224        self.data_fork_digest.write_to(w)?;
225        w.write_u64::<BE>(self.plist_offset)?;
226        w.write_u64::<BE>(self.plist_length)?;
227        w.write_all(&self.reserved1)?;
228        w.write_u64::<BE>(self.code_signature_offset)?;
229        w.write_u64::<BE>(self.code_signature_size)?;
230        w.write_all(&self.reserved2)?;
231        self.main_digest.write_to(w)?;
232        w.write_u32::<BE>(self.image_variant)?;
233        w.write_u64::<BE>(self.sector_count)?;
234        w.write_all(&self.reserved3)?;
235        Ok(())
236    }
237}