1#![cfg_attr(not(test), no_std)]
73
74use core::cmp;
75use embedded_io::{
76 blocking::{Read, Seek, Write},
77 Io, SeekFrom,
78};
79use types::PartitionType;
80
81pub mod types;
82
83pub const RECORD_LEN: usize = 16;
85pub const RECORD_COUNT: usize = 4;
87pub const BLOCK_SIZE: u64 = 512;
89pub const RECORDS_START: u64 = 0x1be;
91pub const RELATIVE_SECTOR_OFFSET: usize = 8;
93pub const TOTAL_SECTORS_OFFSET: usize = 12;
95pub const SYSTEM_ID_OFFSET: usize = 4;
97pub const BOOT_FLAG_OFFSET: usize = 0;
99
100#[repr(usize)]
102pub enum PartitionId {
103 One = 0,
104 Two = 1,
105 Three = 2,
106 Four = 3,
107}
108
109#[inline]
110pub fn lba_to_u64(lba: u32) -> u64 {
112 (lba as u64) * BLOCK_SIZE
113}
114
115pub struct Partition<'a, IO> {
117 start_pos: u64,
118 end_pos: u64,
119 pos: u64,
120 io: &'a mut IO,
121}
122
123impl<'a, IO: Io + Seek> Partition<'a, IO> {
124 pub fn new(start_pos: u64, end_pos: u64, io: &'a mut IO) -> Result<Self, <Self as Io>::Error> {
126 io.seek(SeekFrom::Start(start_pos))?;
128
129 Ok(Self {
130 start_pos,
131 end_pos,
132 pos: 0,
133 io,
134 })
135 }
136}
137
138impl<'a, IO> Partition<'a, IO> {
139 #[inline]
140 pub fn len(&self) -> u64 {
142 self.end_pos - self.start_pos
143 }
144}
145
146impl<'a, IO: Io> Io for Partition<'a, IO> {
147 type Error = IO::Error;
148}
149
150impl<'a, IO: Read> Read for Partition<'a, IO> {
151 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
152 let available = self.len() - self.pos;
154
155 let buf_slice = match (buf.len() as u64) < available {
156 true => buf,
157 false => &mut buf[..available as usize],
158 };
159
160 self.pos += buf_slice.len() as u64;
161
162 self.io.read(buf_slice)
163 }
164}
165
166impl<'a, IO: Write> Write for Partition<'a, IO> {
167 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
168 let available = self.len() - self.pos;
170
171 let buf_slice = match (buf.len() as u64) < available {
172 true => buf,
173 false => &buf[..available as usize],
174 };
175
176 self.pos += buf_slice.len() as u64;
177
178 self.io.write(buf_slice)
179 }
180
181 #[inline]
182 fn flush(&mut self) -> Result<(), Self::Error> {
183 self.io.flush()
184 }
185}
186
187impl<'a, IO: Seek> Seek for Partition<'a, IO> {
188 fn seek(&mut self, pos: embedded_io::SeekFrom) -> Result<u64, Self::Error> {
189 self.pos = match pos {
190 SeekFrom::Start(pos) => {
191 cmp::min(pos, self.len())
193 }
194 SeekFrom::Current(pos) => {
195 cmp::max(cmp::min((pos as i64) + pos, self.len() as i64), 0) as u64
197 }
198 SeekFrom::End(pos) => {
199 cmp::max(cmp::min((self.len() as i64) + pos, self.len() as i64), 0) as u64
201 }
202 };
203
204 self.io.seek(SeekFrom::Start(self.start_pos + self.pos))?;
205
206 Ok(self.pos)
207 }
208}
209
210#[derive(Debug, Copy, Clone, Default)]
212pub struct PartitionRecord {
213 relative_sector: u32,
214 total_sectors: u32,
215 partition_type: PartitionType,
216 boot_flag: bool,
217}
218
219impl PartitionRecord {
220 pub fn from_bytes(bytes: &[u8; RECORD_LEN]) -> Self {
222 let relative_sector_array: [u8; 4] = bytes[RELATIVE_SECTOR_OFFSET..TOTAL_SECTORS_OFFSET]
223 .try_into()
224 .unwrap();
225 let total_sectors_array: [u8; 4] =
226 bytes[TOTAL_SECTORS_OFFSET..RECORD_LEN].try_into().unwrap();
227
228 let relative_sector = u32::from_le_bytes(relative_sector_array);
229 let total_sectors = u32::from_le_bytes(total_sectors_array);
230
231 let system_id: u8 = bytes[SYSTEM_ID_OFFSET];
232 let boot_flag: bool = bytes[BOOT_FLAG_OFFSET] == 0x80;
233
234 Self {
235 relative_sector,
236 total_sectors,
237 partition_type: system_id.try_into().unwrap(),
238 boot_flag,
239 }
240 }
241
242 #[inline]
243 pub fn get_start_pos(&self) -> u64 {
245 lba_to_u64(self.relative_sector)
246 }
247
248 #[inline]
249 pub fn get_end_pos(&self) -> u64 {
251 lba_to_u64(self.relative_sector) + lba_to_u64(self.total_sectors)
252 }
253
254 #[inline]
255 pub fn get_partition_type(&self) -> PartitionType {
257 self.partition_type
258 }
259
260 #[inline]
261 pub fn is_bootable(&self) -> bool {
263 self.boot_flag
264 }
265}
266
267pub struct MBR<IO: Read + Seek> {
269 partitions: [PartitionRecord; RECORD_COUNT],
270 io: IO,
271}
272
273impl<IO: Read + Seek> MBR<IO> {
274 pub fn new(mut io: IO) -> Result<Self, <IO as Io>::Error> {
276 let mut partitions: [PartitionRecord; RECORD_COUNT] =
277 [PartitionRecord::default(); RECORD_COUNT];
278 let mut buffer: [u8; RECORD_LEN * RECORD_COUNT] = [0; RECORD_LEN * RECORD_COUNT];
279
280 io.seek(SeekFrom::Start(RECORDS_START))?;
281 io.read(&mut buffer)?;
282
283 for i in 0..RECORD_COUNT {
284 let buffer_i = i * RECORD_LEN;
285
286 let record_slice = &buffer[buffer_i..buffer_i + RECORD_LEN];
287
288 partitions[i] = PartitionRecord::from_bytes(record_slice.try_into().unwrap());
289 }
290
291 Ok(Self { partitions, io })
292 }
293
294 #[inline]
295 pub fn get_partition(&mut self, id: PartitionId) -> Result<Partition<IO>, IO::Error> {
297 let record = self.partitions[id as usize];
298
299 Partition::new(record.get_start_pos(), record.get_end_pos(), &mut self.io)
300 }
301
302 #[inline]
303 pub fn get_partition_type(&self, id: PartitionId) -> PartitionType {
305 let record = self.partitions[id as usize];
306
307 record.get_partition_type()
308 }
309
310 #[inline]
311 pub fn is_partition_bootable(&self, id: PartitionId) -> bool {
313 let record = self.partitions[id as usize];
314
315 record.is_bootable()
316 }
317}
318
319#[cfg(test)]
320mod tests {
321 use core::panic::AssertUnwindSafe;
322 use std::{io::Cursor, panic};
323
324 use ape_fatfs::{fs::{FileSystem, FsOptions, FatType}, io::StdIoWrapper};
325 use embedded_io::{
326 adapters::FromStd,
327 blocking::{Read, Seek, Write},
328 };
329
330 use crate::*;
331
332 static TEST_IMG_1: &[u8] = include_bytes!("../resources/test1.img");
333 static TEST_IMG_2: &[u8] = include_bytes!("../resources/test2.img");
334 static TEST_STR_1: [u8; 10] = *b"Partition1";
335 static TEST_STR_2: [u8; 10] = *b"Partition2";
336 static TEST_STR_3: [u8; 10] = *b"Partition3";
337 static TEST_STR_4: [u8; 10] = *b"Partition4";
338
339 #[test]
340 fn test_dummy_img() {
350 let img = FromStd::new(Cursor::new(TEST_IMG_1.to_vec()));
351
352 let mut mbr = MBR::new(img).unwrap();
353
354 let mut partition_1 = mbr.get_partition(PartitionId::One).unwrap();
356
357 let mut buf: [u8; 10] = [0; 10];
358
359 partition_1.read_exact(&mut buf[..9]).unwrap();
360 partition_1.seek(embedded_io::SeekFrom::End(-1)).unwrap();
361 partition_1.read_exact(&mut buf[9..]).unwrap();
362 assert_eq!(partition_1.len(), 17 * BLOCK_SIZE);
363 assert_eq!(buf, TEST_STR_1);
364 drop(partition_1);
365
366 let mut partition_2 = mbr.get_partition(PartitionId::Two).unwrap();
368
369 partition_2.read_exact(&mut buf[..9]).unwrap();
370 partition_2.seek(embedded_io::SeekFrom::End(-1)).unwrap();
371 partition_2.read_exact(&mut buf[9..]).unwrap();
372 assert_eq!(partition_2.len(), 33 * BLOCK_SIZE);
373 assert_eq!(buf, TEST_STR_2);
374 drop(partition_2);
375
376 let mut partition_3 = mbr.get_partition(PartitionId::Three).unwrap();
378
379 partition_3.read_exact(&mut buf[..9]).unwrap();
380 partition_3.seek(embedded_io::SeekFrom::End(-1)).unwrap();
381 partition_3.read_exact(&mut buf[9..]).unwrap();
382 assert_eq!(partition_3.len(), 65 * BLOCK_SIZE);
383 assert_eq!(buf, TEST_STR_3);
384 drop(partition_3);
385
386 let mut partition_4 = mbr.get_partition(PartitionId::Four).unwrap();
388
389 partition_4.read_exact(&mut buf[..9]).unwrap();
390 partition_4.seek(embedded_io::SeekFrom::End(-1)).unwrap();
391 partition_4.read_exact(&mut buf[9..]).unwrap();
392 assert_eq!(partition_4.len(), 84 * BLOCK_SIZE);
393 assert_eq!(buf, TEST_STR_4);
394 }
395
396 #[test]
397 fn test_real_img() {
405 let img = StdIoWrapper::new(Cursor::new(TEST_IMG_2.to_vec()));
406
407 let mut mbr = MBR::new(img).unwrap();
408
409 assert_eq!(mbr.get_partition_type(PartitionId::One), PartitionType::Fat12);
411 assert!(mbr.is_partition_bootable(PartitionId::One));
412
413 {
414 let partition = mbr.get_partition(PartitionId::One).unwrap();
415
416 let fs = FileSystem::new(partition, FsOptions::new()).unwrap();
417
418 assert_eq!(fs.fat_type(), FatType::Fat12);
419 }
420
421 assert_eq!(mbr.get_partition_type(PartitionId::Two), PartitionType::Fat16);
423 assert!(!mbr.is_partition_bootable(PartitionId::Two));
424
425 {
426 let partition = mbr.get_partition(PartitionId::Two).unwrap();
427
428 let fs = FileSystem::new(partition, FsOptions::new()).unwrap();
429
430 assert_eq!(fs.fat_type(), FatType::Fat16);
431 }
432
433 assert_eq!(mbr.get_partition_type(PartitionId::Three), PartitionType::W95Fat32);
435 assert!(!mbr.is_partition_bootable(PartitionId::Three));
436
437 {
438 let partition = mbr.get_partition(PartitionId::Three).unwrap();
439
440 let fs = FileSystem::new(partition, FsOptions::new()).unwrap();
441
442 assert_eq!(fs.fat_type(), FatType::Fat32);
443 }
444 }
445
446 #[test]
447 fn test_bounds() {
449 let img = FromStd::new(Cursor::new(TEST_IMG_1.to_vec()));
450
451 let mut mbr = MBR::new(img).unwrap();
452
453 let mut buf: [u8; 10] = [0; 10];
454 let mut partition_1 = mbr.get_partition(PartitionId::One).unwrap();
455
456 partition_1.seek(embedded_io::SeekFrom::End(0)).unwrap();
457
458 partition_1.read_exact(&mut buf).unwrap_err();
459 assert!(panic::catch_unwind(AssertUnwindSafe(|| {
461 partition_1.write_all(&buf).unwrap_err();
462 }))
463 .is_err());
464 }
465}