1use {
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#[derive(Clone, Copy, Debug, Eq, PartialEq)]
73pub struct KolyTrailer {
74 pub version: u32,
76 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 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}