1use std::convert::TryInto;
8use std::io;
9use std::io::Write;
10use std::num::TryFromIntError;
11
12use crc32fast::Hasher;
13use remain::sorted;
14use thiserror::Error as ThisError;
15use uuid::Uuid;
16
17pub const SECTOR_SIZE: u64 = 1 << 9;
19const MBR_PARTITION_ENTRY_SIZE: usize = 16;
21pub const GPT_HEADER_SIZE: u32 = 92;
23pub const GPT_NUM_PARTITIONS: u32 = 128;
26pub const GPT_PARTITION_ENTRY_SIZE: u32 = 128;
28pub const GPT_BEGINNING_SIZE: u64 = SECTOR_SIZE * 40;
31pub const GPT_END_SIZE: u64 = SECTOR_SIZE * 33;
34
35#[sorted]
36#[derive(ThisError, Debug)]
37pub enum Error {
38 #[error("invalid disk size: {0}")]
40 InvalidDiskSize(TryFromIntError),
41 #[error("failed to write data: {0}")]
43 WritingData(io::Error),
44}
45
46pub fn write_protective_mbr(file: &mut impl Write, disk_size: u64) -> Result<(), Error> {
51 file.write_all(&[0; 446]).map_err(Error::WritingData)?;
53
54 file.write_all(&[0x00]).map_err(Error::WritingData)?;
56 file.write_all(&[0; 3]).map_err(Error::WritingData)?;
58 file.write_all(&[0xEE]).map_err(Error::WritingData)?;
60 file.write_all(&[0; 3]).map_err(Error::WritingData)?;
62 let first_lba: u32 = 1;
63 file.write_all(&first_lba.to_le_bytes())
64 .map_err(Error::WritingData)?;
65 let number_of_sectors: u32 = (disk_size / SECTOR_SIZE)
66 .try_into()
67 .map_err(Error::InvalidDiskSize)?;
68 file.write_all(&number_of_sectors.to_le_bytes())
69 .map_err(Error::WritingData)?;
70
71 file.write_all(&[0; MBR_PARTITION_ENTRY_SIZE * 3])
73 .map_err(Error::WritingData)?;
74
75 file.write_all(&[0x55, 0xAA]).map_err(Error::WritingData)?;
77
78 Ok(())
79}
80
81#[derive(Clone, Debug, Default, Eq, PartialEq)]
82struct GptHeader {
83 signature: [u8; 8],
84 revision: [u8; 4],
85 header_size: u32,
86 header_crc32: u32,
87 current_lba: u64,
88 backup_lba: u64,
89 first_usable_lba: u64,
90 last_usable_lba: u64,
91 disk_guid: Uuid,
92 partition_entries_lba: u64,
93 num_partition_entries: u32,
94 partition_entry_size: u32,
95 partition_entries_crc32: u32,
96}
97
98impl GptHeader {
99 fn write_bytes(&self, out: &mut impl Write) -> Result<(), Error> {
100 out.write_all(&self.signature).map_err(Error::WritingData)?;
101 out.write_all(&self.revision).map_err(Error::WritingData)?;
102 out.write_all(&self.header_size.to_le_bytes())
103 .map_err(Error::WritingData)?;
104 out.write_all(&self.header_crc32.to_le_bytes())
105 .map_err(Error::WritingData)?;
106 out.write_all(&[0; 4]).map_err(Error::WritingData)?;
108 out.write_all(&self.current_lba.to_le_bytes())
109 .map_err(Error::WritingData)?;
110 out.write_all(&self.backup_lba.to_le_bytes())
111 .map_err(Error::WritingData)?;
112 out.write_all(&self.first_usable_lba.to_le_bytes())
113 .map_err(Error::WritingData)?;
114 out.write_all(&self.last_usable_lba.to_le_bytes())
115 .map_err(Error::WritingData)?;
116
117 write_guid(out, self.disk_guid).map_err(Error::WritingData)?;
119
120 out.write_all(&self.partition_entries_lba.to_le_bytes())
121 .map_err(Error::WritingData)?;
122 out.write_all(&self.num_partition_entries.to_le_bytes())
123 .map_err(Error::WritingData)?;
124 out.write_all(&self.partition_entry_size.to_le_bytes())
125 .map_err(Error::WritingData)?;
126 out.write_all(&self.partition_entries_crc32.to_le_bytes())
127 .map_err(Error::WritingData)?;
128 Ok(())
129 }
130}
131
132pub fn write_gpt_header(
137 out: &mut impl Write,
138 disk_guid: Uuid,
139 partition_entries_crc32: u32,
140 secondary_table_offset: u64,
141 secondary: bool,
142) -> Result<(), Error> {
143 let primary_header_lba = 1;
144 let secondary_header_lba = (secondary_table_offset + GPT_END_SIZE) / SECTOR_SIZE - 1;
145 let mut gpt_header = GptHeader {
146 signature: *b"EFI PART",
147 revision: [0, 0, 1, 0],
148 header_size: GPT_HEADER_SIZE,
149 current_lba: if secondary {
150 secondary_header_lba
151 } else {
152 primary_header_lba
153 },
154 backup_lba: if secondary {
155 primary_header_lba
156 } else {
157 secondary_header_lba
158 },
159 first_usable_lba: GPT_BEGINNING_SIZE / SECTOR_SIZE,
160 last_usable_lba: secondary_table_offset / SECTOR_SIZE - 1,
161 disk_guid,
162 partition_entries_lba: 2,
163 num_partition_entries: GPT_NUM_PARTITIONS,
164 partition_entry_size: GPT_PARTITION_ENTRY_SIZE,
165 partition_entries_crc32,
166 header_crc32: 0,
167 };
168
169 let mut header_without_crc = [0u8; GPT_HEADER_SIZE as usize];
171 gpt_header.write_bytes(&mut &mut header_without_crc[..])?;
172 let mut hasher = Hasher::new();
173 hasher.update(&header_without_crc);
174 gpt_header.header_crc32 = hasher.finalize();
175
176 gpt_header.write_bytes(out)?;
177
178 Ok(())
179}
180
181#[derive(Clone, Debug, Eq, PartialEq)]
183pub struct GptPartitionEntry {
184 pub partition_type_guid: Uuid,
185 pub unique_partition_guid: Uuid,
186 pub first_lba: u64,
187 pub last_lba: u64,
188 pub attributes: u64,
189 pub partition_name: [u16; 36],
191}
192
193impl Default for GptPartitionEntry {
197 fn default() -> Self {
198 Self {
199 partition_type_guid: Default::default(),
200 unique_partition_guid: Default::default(),
201 first_lba: 0,
202 last_lba: 0,
203 attributes: 0,
204 partition_name: [0; 36],
205 }
206 }
207}
208
209impl GptPartitionEntry {
210 pub fn write_bytes(&self, out: &mut impl Write) -> Result<(), Error> {
213 write_guid(out, self.partition_type_guid).map_err(Error::WritingData)?;
214 write_guid(out, self.unique_partition_guid).map_err(Error::WritingData)?;
215 out.write_all(&self.first_lba.to_le_bytes())
216 .map_err(Error::WritingData)?;
217 out.write_all(&self.last_lba.to_le_bytes())
218 .map_err(Error::WritingData)?;
219 out.write_all(&self.attributes.to_le_bytes())
220 .map_err(Error::WritingData)?;
221 for code_unit in &self.partition_name {
222 out.write_all(&code_unit.to_le_bytes())
223 .map_err(Error::WritingData)?;
224 }
225 Ok(())
226 }
227}
228
229fn write_guid(out: &mut impl Write, guid: Uuid) -> Result<(), io::Error> {
231 let guid_fields = guid.as_fields();
232 out.write_all(&guid_fields.0.to_le_bytes())?;
233 out.write_all(&guid_fields.1.to_le_bytes())?;
234 out.write_all(&guid_fields.2.to_le_bytes())?;
235 out.write_all(guid_fields.3)?;
236
237 Ok(())
238}
239
240#[cfg(test)]
241mod tests {
242 use super::*;
243
244 #[test]
245 fn protective_mbr_size() {
246 let mut buffer = vec![];
247 write_protective_mbr(&mut buffer, 1000 * SECTOR_SIZE).unwrap();
248
249 assert_eq!(buffer.len(), SECTOR_SIZE as usize);
250 }
251
252 #[test]
253 fn header_size() {
254 let mut buffer = vec![];
255 write_gpt_header(
256 &mut buffer,
257 Uuid::from_u128(0x12345678_1234_5678_abcd_12345678abcd),
258 42,
259 1000 * SECTOR_SIZE,
260 false,
261 )
262 .unwrap();
263
264 assert_eq!(buffer.len(), GPT_HEADER_SIZE as usize);
265 }
266
267 #[test]
268 fn partition_entry_size() {
269 let mut buffer = vec![];
270 GptPartitionEntry::default()
271 .write_bytes(&mut buffer)
272 .unwrap();
273
274 assert_eq!(buffer.len(), GPT_PARTITION_ENTRY_SIZE as usize);
275 }
276}