1#![cfg_attr(not(any(feature = "std", test, doc)), no_std)]
2#![deny(unsafe_op_in_unsafe_fn)]
3
4#[cfg(any(feature = "alloc", doc))]
5extern crate alloc;
6
7#[cfg(any(feature = "alloc", doc))]
8use alloc::vec::Vec;
9
10extern crate core;
11
12use block_device::BlockDevice;
13
14pub use crate::error::{GPTError, GPTParseError, GptRepair, Result};
15use crate::header::{GPTHeader, GptHeaderType};
16
17pub const BLOCK_SIZE: u32 = 512;
18pub const DEFAULT_PARTTABLE_SIZE: u32 = 16384;
20pub const DEFAULT_PARTTABLE_BLOCKS: u32 = DEFAULT_PARTTABLE_SIZE / BLOCK_SIZE;
21
22macro_rules! read_le_bytes {
23 ($in:tt, $size:tt, $pos:expr) => {
24 $size::from_le_bytes(($in[$pos]).try_into().unwrap())
26 };
27
28 ($in:tt, $pos:expr) => {
29 ($in[$pos]).try_into().unwrap()
30 };
31}
32pub(crate) use read_le_bytes; mod guid;
35
36pub mod error;
37pub mod header;
38pub mod mbr;
39pub mod part;
40#[cfg(any(feature = "std", doc))]
41pub mod std;
42
43use crate::mbr::{MBRPartitionRecord, MasterBootRecord};
44use crate::part::{GPTPartHeader, GPTTypeGuid};
45
46#[doc(inline)]
47pub use guid::GUID;
48
49pub struct GPT<T> {
50 block: T,
51 header: GPTHeader,
52}
53
54impl<T> GPT<T>
55where
56 T: BlockDevice,
57 GPTError: From<T::Error>,
58{
59 #[allow(clippy::unnecessary_unwrap)]
61 pub fn open(block: T) -> Result<Self, GPTParseError<T>> {
62 #[cfg(not(feature = "alloc"))]
63 let mut buf = [0u8; DEFAULT_PARTTABLE_SIZE as usize];
64
65 #[cfg(feature = "alloc")]
66 let mut buf = {
67 let mut buf = Vec::with_capacity(DEFAULT_PARTTABLE_SIZE as usize);
68 buf.try_reserve_exact(DEFAULT_PARTTABLE_SIZE as usize)?; buf.resize(DEFAULT_PARTTABLE_SIZE as usize, 0);
70 buf
71 };
72
73 block.read(&mut buf, 0, 1)?;
75 let mbr = unsafe { MasterBootRecord::from_buf(&buf) }?;
76
77 mbr.verify(None)?;
78 if mbr.partition[0].os_indicator != MBRPartitionRecord::GPT_PROTECTIVE_OS_TYPE {
79 return Err(GPTError::NoGPT.into());
82 }
83
84 let header_lba = mbr.partition[0].starting_lba() as usize;
85
86 drop(mbr);
88 block.read(&mut buf, header_lba, 1)?;
89
90 let m_header = GPTHeader::parse(&buf)?;
91
92 let p_table_size = m_header.size_of_p_entry * m_header.num_parts;
93 #[cfg(not(feature = "alloc"))]
94 if p_table_size > DEFAULT_PARTTABLE_SIZE {
95 return Err(GPTError::NoAllocator.into());
96 }
97
98 #[cfg(feature = "alloc")]
99 if p_table_size > buf.len() as u32 {
100 buf.try_reserve_exact(p_table_size as usize - buf.len())?; buf.resize(p_table_size as usize, 0);
102 }
103
104 let blocks = if p_table_size > DEFAULT_PARTTABLE_SIZE as u32 {
105 p_table_size / BLOCK_SIZE + 1 } else {
107 DEFAULT_PARTTABLE_BLOCKS
108 };
109
110 block.read(&mut buf, m_header.p_entry_lba as usize, blocks as usize)?;
111
112 let m_header_valid = m_header.validate(header_lba as u64, &buf);
113
114 block.read(&mut buf, m_header.other_lba as usize, 1)?;
115 let b_header = GPTHeader::parse(&buf)?;
116
117 block.read(&mut buf, b_header.p_entry_lba as usize, blocks as usize)?;
118
119 let b_header_valid = b_header.validate(m_header.other_lba as u64, &buf);
120
121 if m_header_valid.is_err() || b_header_valid.is_err() {
122 return if m_header_valid.is_ok() {
123 Err(GPTParseError::BrokenHeader(
124 Self {
125 block,
126 header: m_header,
127 },
128 GptHeaderType::Backup,
129 b_header_valid.unwrap_err(),
130 ))
131 } else if b_header_valid.is_ok() {
132 Err(GPTParseError::BrokenHeader(
133 Self {
134 block,
135 header: b_header,
136 },
137 GptHeaderType::Main,
138 m_header_valid.unwrap_err(),
139 ))
140 } else {
141 Err(GPTError::NoGPT.into())
142 };
143 }
144
145 Ok(Self {
146 block,
147 header: m_header,
148 })
149 }
150
151 pub fn get_partition_buf<PT, PA>(&self, idx: u32, buf: &[u8]) -> Result<GPTPartHeader<PT, PA>>
152 where
153 PT: GPTTypeGuid,
154 GPTError: From<<PT as TryFrom<[u8; 16]>>::Error>,
155 GPTError: From<<PT as TryInto<[u8; 16]>>::Error>,
156 PA: TryFrom<u64>,
157 GPTError: From<<PA as TryFrom<u64>>::Error>,
158 {
159 if idx >= self.header.num_parts {
160 return Err(GPTError::InvalidData);
161 }
162
163 let offset: u64 = self.header.size_of_p_entry as u64 * idx as u64;
165
166 GPTPartHeader::parse(&buf[offset as usize..])
167 }
168
169 pub fn get_partition<PT, PA>(&self, idx: u32) -> Result<GPTPartHeader<PT, PA>>
170 where
171 PT: GPTTypeGuid,
172 GPTError: From<<PT as TryFrom<[u8; 16]>>::Error>,
173 GPTError: From<<PT as TryInto<[u8; 16]>>::Error>,
174 PA: TryFrom<u64>,
175 GPTError: From<<PA as TryFrom<u64>>::Error>,
176 {
177 if idx >= self.header.num_parts {
178 return Err(GPTError::InvalidData);
179 }
180
181 let p_table_size = self.header.size_of_p_entry as usize * self.header.num_parts as usize;
182
183 let blocks = if p_table_size > DEFAULT_PARTTABLE_SIZE as usize {
184 (p_table_size / BLOCK_SIZE as usize + 1) as usize } else {
186 DEFAULT_PARTTABLE_BLOCKS as usize
187 };
188
189 let buf = read_buf(
190 self.header.p_entry_lba as usize,
191 p_table_size,
192 &self.block,
193 blocks,
194 )?;
195
196 self.get_partition_buf(idx, &buf)
197 }
198
199 pub fn get_first_partition_of_type_buf<PT, PA>(
200 &self,
201 guid: PT,
202 buf: &[u8],
203 ) -> Result<GPTPartHeader<PT, PA>>
204 where
205 PT: GPTTypeGuid,
206 GPTError: From<<PT as TryFrom<[u8; 16]>>::Error>,
207 GPTError: From<<PT as TryInto<[u8; 16]>>::Error>,
208 PA: TryFrom<u64>,
209 GPTError: From<<PA as TryFrom<u64>>::Error>,
210 PT: Eq,
211 {
212 let mut idx = 0;
213
214 loop {
215 let part = self.get_partition_buf(idx, buf)?;
216 if part.type_guid == guid {
217 return Ok(part);
218 }
219
220 idx += 1;
221 }
222 }
223
224 pub fn get_first_partition_of_type<PT, PA>(&self, guid: PT) -> Result<GPTPartHeader<PT, PA>>
225 where
226 PT: GPTTypeGuid,
227 GPTError: From<<PT as TryFrom<[u8; 16]>>::Error>,
228 GPTError: From<<PT as TryInto<[u8; 16]>>::Error>,
229 PA: TryFrom<u64>,
230 GPTError: From<<PA as TryFrom<u64>>::Error>,
231 PT: Eq,
232 {
233 let p_table_size = self.header.size_of_p_entry as usize * self.header.num_parts as usize;
234
235 let blocks = if p_table_size > DEFAULT_PARTTABLE_SIZE as usize {
236 (p_table_size / BLOCK_SIZE as usize + 1) as usize } else {
238 DEFAULT_PARTTABLE_BLOCKS as usize
239 };
240
241 let buf = read_buf(
242 self.header.p_entry_lba as usize,
243 p_table_size,
244 &self.block,
245 blocks,
246 )?;
247
248 self.get_first_partition_of_type_buf(guid, &buf)
249 }
250}
251
252#[cfg(not(feature = "alloc"))]
253fn read_buf<T: BlockDevice>(
254 start_lba: usize,
255 size: usize,
256 block: &T,
257 blocks: usize,
258) -> Result<[u8; DEFAULT_PARTTABLE_SIZE as usize]>
259where
260 GPTError: From<T::Error>,
261{
262 let mut buf = [0u8; DEFAULT_PARTTABLE_SIZE as usize];
263 if size > DEFAULT_PARTTABLE_SIZE as usize {
264 return Err(GPTError::NoAllocator);
265 }
266
267 block.read(&mut buf, start_lba, blocks)?;
268
269 Ok(buf)
270}
271
272#[cfg(feature = "alloc")]
273fn read_buf<T: BlockDevice>(
274 start_lba: usize,
275 size: usize,
276 block: &T,
277 blocks: usize,
278) -> Result<alloc::vec::Vec<u8>>
279where
280 GPTError: From<T::Error>,
281{
282 let mut buf = {
283 let mut buf = Vec::with_capacity(DEFAULT_PARTTABLE_SIZE as usize);
284 buf.try_reserve_exact(DEFAULT_PARTTABLE_SIZE as usize)?; buf.resize(DEFAULT_PARTTABLE_SIZE as usize, 0);
286 buf
287 };
288
289 if size > buf.len() {
290 buf.try_reserve_exact(size - buf.len())?; buf.resize(size, 0);
292 }
293
294 block.read(&mut buf, start_lba, blocks)?;
295
296 Ok(buf)
297}