1io_transform! {
2
3#[cfg(feature = "alloc")]
4extern crate alloc;
5
6#[cfg(all(feature = "alloc", feature = "read"))]
7use super::super::Read;
8#[cfg(all(feature = "alloc", any(feature = "read", feature = "write")))]
9use super::super::{Seek, SeekFrom};
10#[cfg(all(feature = "alloc", feature = "write"))]
11use super::super::Write;
12#[cfg(all(feature = "alloc", any(feature = "read", feature = "write")))]
13use crate::error::{Error, Result};
14#[cfg(all(feature = "alloc", feature = "read"))]
15use crate::gpt::{GptHeader, GptPartitionEntry};
16#[cfg(all(feature = "alloc", feature = "read"))]
17use crate::mbr::MasterBootRecord;
18#[cfg(all(feature = "alloc", feature = "read"))]
19use crate::scheme::{PartitionSchemeType, detect_scheme_from_mbr};
20
21#[cfg(all(feature = "alloc", feature = "read"))]
22use super::gpt_io::GptHeaderReadExt;
23#[cfg(all(feature = "alloc", feature = "write"))]
24use super::gpt_io::GptHeaderWriteExt;
25#[cfg(all(feature = "alloc", feature = "read"))]
26use super::mbr_io::MasterBootRecordReadExt;
27#[cfg(all(feature = "alloc", feature = "write"))]
28use super::mbr_io::MasterBootRecordWriteExt;
29
30#[cfg(feature = "alloc")]
31use crate::scheme::GptDisk;
32
33#[cfg(feature = "alloc")]
34use crate::scheme::PartitionTable;
35
36#[cfg(all(feature = "alloc", feature = "read"))]
40#[cfg_attr(docsrs, doc(cfg(all(feature = "alloc", feature = "read"))))]
41pub trait GptDiskReadExt: Sized {
42 async fn read_from<R: Read + Seek>(
56 reader: &mut R,
57 block_size: u32,
58 ) -> Result<Self>;
59}
60
61#[cfg(all(feature = "alloc", feature = "read"))]
62impl GptDiskReadExt for GptDisk {
63 async fn read_from<R: Read + Seek>(
64 reader: &mut R,
65 block_size: u32,
66 ) -> Result<Self> {
67 if block_size < GptHeader::STANDARD_HEADER_SIZE {
68 return Err(Error::InvalidBlockSize {
69 size: block_size,
70 minimum: GptHeader::STANDARD_HEADER_SIZE,
71 });
72 }
73
74 let primary_header = GptHeader::read_from_lba(reader, 1, block_size).await?;
76
77 #[cfg(feature = "crc")]
79 if !primary_header.verify_crc32() {
80 return Err(Error::GptHeaderCrcMismatch {
81 expected: primary_header.header_crc32.to_ne(),
82 actual: primary_header.calculate_crc32(),
83 });
84 }
85
86 let entry_size = primary_header.size_of_partition_entry.to_ne();
88 if entry_size != core::mem::size_of::<GptPartitionEntry>() as u32 {
89 return Err(Error::InvalidPartitionEntrySize { size: entry_size });
90 }
91
92 let num_entries = primary_header.num_partition_entries.to_ne() as usize;
94
95 let image_len = reader
99 .seek(SeekFrom::End(0))
100 .await
101 .map_err(Error::from)?;
102 let entry_array = primary_header
103 .partition_entry_lba
104 .to_ne()
105 .checked_mul(u64::from(block_size))
106 .and_then(|start| {
107 start
108 .checked_add(num_entries as u64 * u64::from(entry_size))
109 .map(|end| (start, end))
110 });
111 let available = image_len / u64::from(block_size);
112 let Some((entries_start, entries_end)) = entry_array else {
113 return Err(Error::DiskTooSmall {
114 required: u64::MAX,
115 available,
116 });
117 };
118 if entries_end > image_len {
119 return Err(Error::DiskTooSmall {
120 required: entries_end.div_ceil(u64::from(block_size)),
121 available,
122 });
123 }
124
125 let mut entries = alloc::vec![GptPartitionEntry::default(); num_entries];
126
127 reader
128 .seek(SeekFrom::Start(entries_start))
129 .await
130 .map_err(Error::from)?;
131
132 for entry in entries.iter_mut() {
133 let mut buf = [0u8; 128];
134 reader
135 .read_exact(&mut buf)
136 .await
137 .map_err(Error::from)?;
138 *entry = bytemuck::cast(buf);
139 }
140
141 #[cfg(feature = "crc")]
143 {
144 let entries_crc = crate::gpt::calculate_partition_array_crc32(&entries);
145 if primary_header.partition_entry_array_crc32.to_ne() != entries_crc {
146 return Err(Error::GptEntriesCrcMismatch {
147 expected: primary_header.partition_entry_array_crc32.to_ne(),
148 actual: entries_crc,
149 });
150 }
151 }
152
153 let backup_lba = primary_header.alternate_lba.to_ne();
154 let backup_header = match GptHeader::read_from_lba(reader, backup_lba, block_size).await {
155 Ok(header) => header,
156 Err(Error::Io(source)) => {
157 return Err(Error::BackupHeaderIo {
158 lba: backup_lba,
159 source,
160 });
161 }
162 Err(Error::InvalidGptSignature { found }) => {
163 return Err(Error::InvalidBackupGptSignature { found });
164 }
165 Err(error) => return Err(error),
166 };
167
168 #[cfg(feature = "crc")]
169 if !backup_header.verify_crc32() {
170 return Err(Error::BackupGptHeaderCrcMismatch {
171 expected: backup_header.header_crc32.to_ne(),
172 actual: backup_header.calculate_crc32(),
173 });
174 }
175
176 let entry_array_bytes = u64::from(primary_header.num_partition_entries.to_ne())
177 .checked_mul(u64::from(primary_header.size_of_partition_entry.to_ne()))
178 .ok_or(Error::BackupHeaderMismatch)?;
179 let entry_array_blocks = entry_array_bytes.div_ceil(u64::from(block_size));
180 let expected_backup_entries_lba = backup_lba
181 .checked_sub(entry_array_blocks)
182 .ok_or(Error::BackupHeaderMismatch)?;
183
184 if backup_header.my_lba != primary_header.alternate_lba
185 || backup_header.alternate_lba != primary_header.my_lba
186 || backup_header.revision != primary_header.revision
187 || backup_header.header_size != primary_header.header_size
188 || backup_header.first_usable_lba != primary_header.first_usable_lba
189 || backup_header.last_usable_lba != primary_header.last_usable_lba
190 || backup_header.disk_guid != primary_header.disk_guid
191 || backup_header.num_partition_entries != primary_header.num_partition_entries
192 || backup_header.size_of_partition_entry != primary_header.size_of_partition_entry
193 || backup_header.partition_entry_array_crc32
194 != primary_header.partition_entry_array_crc32
195 || backup_header.partition_entry_lba.to_ne() != expected_backup_entries_lba
196 {
197 return Err(Error::BackupHeaderMismatch);
198 }
199
200 Ok(Self {
201 primary_header,
202 backup_header,
203 entries,
204 block_size,
205 })
206 }
207}
208
209#[cfg(all(feature = "alloc", feature = "write"))]
211#[cfg_attr(docsrs, doc(cfg(all(feature = "alloc", feature = "write"))))]
212pub trait GptDiskWriteExt {
213 async fn write_to<W: Write + Seek>(&self, writer: &mut W) -> Result<()>;
230
231 async fn write_to_with_mbr<W: Write + Seek>(
244 &self,
245 writer: &mut W,
246 mbr: &MasterBootRecord,
247 ) -> Result<()>;
248}
249
250#[cfg(all(feature = "alloc", feature = "write"))]
251impl GptDiskWriteExt for GptDisk {
252 async fn write_to<W: Write + Seek>(&self, writer: &mut W) -> Result<()> {
253 writer
255 .seek(SeekFrom::Start(0))
256 .await
257 .map_err(Error::from)?;
258 let protective_mbr = self.create_protective_mbr();
259 protective_mbr.write_to(writer).await?;
260
261 self.primary_header
263 .write_to_lba(writer, 1, self.block_size)
264 .await?;
265
266 let Some(primary_entries_offset) = self
268 .primary_header
269 .partition_entry_lba
270 .to_ne()
271 .checked_mul(u64::from(self.block_size))
272 else {
273 return Err(Error::lba_offset_overflow());
274 };
275 writer
276 .seek(SeekFrom::Start(primary_entries_offset))
277 .await
278 .map_err(Error::from)?;
279
280 for entry in &self.entries {
281 writer
282 .write_all(bytemuck::bytes_of(entry))
283 .await
284 .map_err(Error::from)?;
285 }
286
287 let Some(backup_entries_offset) = self
289 .backup_header
290 .partition_entry_lba
291 .to_ne()
292 .checked_mul(u64::from(self.block_size))
293 else {
294 return Err(Error::lba_offset_overflow());
295 };
296 writer
297 .seek(SeekFrom::Start(backup_entries_offset))
298 .await
299 .map_err(Error::from)?;
300
301 for entry in &self.entries {
302 writer
303 .write_all(bytemuck::bytes_of(entry))
304 .await
305 .map_err(Error::from)?;
306 }
307
308 self.backup_header
310 .write_to_lba(writer, self.backup_header.my_lba.to_ne(), self.block_size)
311 .await?;
312
313 Ok(())
314 }
315
316 async fn write_to_with_mbr<W: Write + Seek>(
317 &self,
318 writer: &mut W,
319 mbr: &MasterBootRecord,
320 ) -> Result<()> {
321 writer
323 .seek(SeekFrom::Start(0))
324 .await
325 .map_err(Error::from)?;
326 mbr.write_to(writer).await?;
327
328 self.primary_header
330 .write_to_lba(writer, 1, self.block_size)
331 .await?;
332
333 let Some(primary_entries_offset) = self
335 .primary_header
336 .partition_entry_lba
337 .to_ne()
338 .checked_mul(u64::from(self.block_size))
339 else {
340 return Err(Error::lba_offset_overflow());
341 };
342 writer
343 .seek(SeekFrom::Start(primary_entries_offset))
344 .await
345 .map_err(Error::from)?;
346
347 for entry in &self.entries {
348 writer
349 .write_all(bytemuck::bytes_of(entry))
350 .await
351 .map_err(Error::from)?;
352 }
353
354 let Some(backup_entries_offset) = self
356 .backup_header
357 .partition_entry_lba
358 .to_ne()
359 .checked_mul(u64::from(self.block_size))
360 else {
361 return Err(Error::lba_offset_overflow());
362 };
363 writer
364 .seek(SeekFrom::Start(backup_entries_offset))
365 .await
366 .map_err(Error::from)?;
367
368 for entry in &self.entries {
369 writer
370 .write_all(bytemuck::bytes_of(entry))
371 .await
372 .map_err(Error::from)?;
373 }
374
375 self.backup_header
377 .write_to_lba(writer, self.backup_header.my_lba.to_ne(), self.block_size)
378 .await?;
379
380 Ok(())
381 }
382}
383
384#[cfg(all(feature = "alloc", feature = "read"))]
388#[cfg_attr(docsrs, doc(cfg(all(feature = "alloc", feature = "read"))))]
389pub trait PartitionTableReadExt: Sized {
390 async fn read_from<R: Read + Seek>(
407 reader: &mut R,
408 block_size: u32,
409 ) -> Result<Self>;
410}
411
412#[cfg(all(feature = "alloc", feature = "read"))]
413impl PartitionTableReadExt for PartitionTable {
414 async fn read_from<R: Read + Seek>(
415 reader: &mut R,
416 block_size: u32,
417 ) -> Result<Self> {
418 reader
420 .seek(SeekFrom::Start(0))
421 .await
422 .map_err(Error::from)?;
423
424 let mbr = MasterBootRecord::read_from(reader).await?;
425 let scheme_type = detect_scheme_from_mbr(&mbr);
426
427 match scheme_type {
428 PartitionSchemeType::Mbr => Ok(Self::Mbr(mbr)),
429 PartitionSchemeType::Gpt => {
430 let gpt = GptDisk::read_from(reader, block_size).await?;
431 Ok(Self::Gpt {
432 protective_mbr: mbr,
433 gpt,
434 })
435 }
436 PartitionSchemeType::Hybrid => {
437 let gpt = GptDisk::read_from(reader, block_size).await?;
438 Ok(Self::Hybrid {
439 hybrid_mbr: mbr,
440 gpt,
441 })
442 }
443 }
444 }
445}
446
447#[cfg(all(feature = "alloc", feature = "write"))]
449#[cfg_attr(docsrs, doc(cfg(all(feature = "alloc", feature = "write"))))]
450pub trait PartitionTableWriteExt {
451 async fn write_to<W: Write + Seek>(&self, writer: &mut W) -> Result<()>;
461}
462
463#[cfg(all(feature = "alloc", feature = "write"))]
464impl PartitionTableWriteExt for PartitionTable {
465 async fn write_to<W: Write + Seek>(&self, writer: &mut W) -> Result<()> {
466 match self {
467 Self::Mbr(mbr) => {
468 writer
469 .seek(SeekFrom::Start(0))
470 .await
471 .map_err(Error::from)?;
472 mbr.write_to(writer).await
473 }
474 Self::Gpt { gpt, .. } => gpt.write_to(writer).await,
475 Self::Hybrid { hybrid_mbr, gpt } => gpt.write_to_with_mbr(writer, hybrid_mbr).await,
476 }
477 }
478}
479
480}