1#![cfg_attr(not(test), no_std)]
4#![allow(dead_code)]
5#![deny(missing_docs)]
6
7use byteorder::{ByteOrder, LittleEndian};
14use core::convert::TryFrom;
15use log::debug;
16
17#[macro_use]
18mod structure;
19
20mod blockdevice;
21mod fat;
22mod filesystem;
23mod sdmmc;
24mod sdmmc_proto;
25
26pub use crate::blockdevice::{Block, BlockCount, BlockDevice, BlockIdx};
27pub use crate::fat::FatVolume;
28use crate::fat::RESERVED_ENTRIES;
29pub use crate::filesystem::{
30 Attributes, Cluster, DirEntry, Directory, File, FilenameError, Mode, ShortFileName, TimeSource,
31 Timestamp, MAX_FILE_SIZE,
32};
33pub use crate::sdmmc::Error as SdMmcError;
34pub use crate::sdmmc::SdMmcSpi;
35
36#[derive(Debug, Clone)]
44pub enum Error<E>
45where
46 E: core::fmt::Debug,
47{
48 DeviceError(E),
50 FormatError(&'static str),
52 NoSuchVolume,
54 FilenameError(FilenameError),
56 TooManyOpenDirs,
58 TooManyOpenFiles,
60 FileNotFound,
62 FileAlreadyOpen,
64 DirAlreadyOpen,
66 OpenedDirAsFile,
68 Unsupported,
70 EndOfFile,
72 BadCluster,
74 ConversionError,
76 NotEnoughSpace,
78 AllocationError,
80 JumpedFree,
82 ReadOnly,
84 FileAlreadyExists,
86}
87
88pub const MAX_OPEN_DIRS: usize = 4;
91
92pub const MAX_OPEN_FILES: usize = 4;
95
96pub struct Controller<D, T>
98where
99 D: BlockDevice,
100 T: TimeSource,
101 <D as BlockDevice>::Error: core::fmt::Debug,
102{
103 block_device: D,
104 timesource: T,
105 open_dirs: [(VolumeIdx, Cluster); MAX_OPEN_DIRS],
106 open_files: [(VolumeIdx, Cluster); MAX_OPEN_DIRS],
107}
108
109#[derive(Debug, PartialEq, Eq)]
111pub struct Volume {
112 idx: VolumeIdx,
113 volume_type: VolumeType,
114}
115
116#[derive(Debug, PartialEq, Eq)]
119pub enum VolumeType {
120 Fat(FatVolume),
122}
123
124#[derive(Debug, PartialEq, Eq, Copy, Clone)]
128pub struct VolumeIdx(pub usize);
129
130const PARTITION_ID_FAT32_LBA: u8 = 0x0C;
147const PARTITION_ID_FAT16_LBA: u8 = 0x0E;
149const PARTITION_ID_FAT16: u8 = 0x06;
152const PARTITION_ID_FAT32_CHS_LBA: u8 = 0x0B;
155
156impl<D, T> Controller<D, T>
171where
172 D: BlockDevice,
173 T: TimeSource,
174 <D as BlockDevice>::Error: core::fmt::Debug,
175{
176 pub fn new(block_device: D, timesource: T) -> Controller<D, T> {
180 debug!("Creating new embedded-sdmmc::Controller");
181 Controller {
182 block_device,
183 timesource,
184 open_dirs: [(VolumeIdx(0), Cluster::INVALID); 4],
185 open_files: [(VolumeIdx(0), Cluster::INVALID); 4],
186 }
187 }
188
189 pub fn device(&mut self) -> &mut D {
191 &mut self.block_device
192 }
193
194 pub fn get_volume(&mut self, volume_idx: VolumeIdx) -> Result<Volume, Error<D::Error>> {
199 const PARTITION1_START: usize = 446;
200 const PARTITION2_START: usize = PARTITION1_START + PARTITION_INFO_LENGTH;
201 const PARTITION3_START: usize = PARTITION2_START + PARTITION_INFO_LENGTH;
202 const PARTITION4_START: usize = PARTITION3_START + PARTITION_INFO_LENGTH;
203 const FOOTER_START: usize = 510;
204 const FOOTER_VALUE: u16 = 0xAA55;
205 const PARTITION_INFO_LENGTH: usize = 16;
206 const PARTITION_INFO_STATUS_INDEX: usize = 0;
207 const PARTITION_INFO_TYPE_INDEX: usize = 4;
208 const PARTITION_INFO_LBA_START_INDEX: usize = 8;
209 const PARTITION_INFO_NUM_BLOCKS_INDEX: usize = 12;
210
211 let (part_type, lba_start, num_blocks) = {
212 let mut blocks = [Block::new()];
213 self.block_device
214 .read(&mut blocks, BlockIdx(0), "read_mbr")
215 .map_err(Error::DeviceError)?;
216 let block = &blocks[0];
217 if LittleEndian::read_u16(&block[FOOTER_START..FOOTER_START + 2]) != FOOTER_VALUE {
220 return Err(Error::FormatError("Invalid MBR signature"));
221 }
222 let partition = match volume_idx {
223 VolumeIdx(0) => {
224 &block[PARTITION1_START..(PARTITION1_START + PARTITION_INFO_LENGTH)]
225 }
226 VolumeIdx(1) => {
227 &block[PARTITION2_START..(PARTITION2_START + PARTITION_INFO_LENGTH)]
228 }
229 VolumeIdx(2) => {
230 &block[PARTITION3_START..(PARTITION3_START + PARTITION_INFO_LENGTH)]
231 }
232 VolumeIdx(3) => {
233 &block[PARTITION4_START..(PARTITION4_START + PARTITION_INFO_LENGTH)]
234 }
235 _ => {
236 return Err(Error::NoSuchVolume);
237 }
238 };
239 if (partition[PARTITION_INFO_STATUS_INDEX] & 0x7F) != 0x00 {
241 return Err(Error::FormatError("Invalid partition status"));
242 }
243 let lba_start = LittleEndian::read_u32(
244 &partition[PARTITION_INFO_LBA_START_INDEX..(PARTITION_INFO_LBA_START_INDEX + 4)],
245 );
246 let num_blocks = LittleEndian::read_u32(
247 &partition[PARTITION_INFO_NUM_BLOCKS_INDEX..(PARTITION_INFO_NUM_BLOCKS_INDEX + 4)],
248 );
249 (
250 partition[PARTITION_INFO_TYPE_INDEX],
251 BlockIdx(lba_start),
252 BlockCount(num_blocks),
253 )
254 };
255 match part_type {
256 PARTITION_ID_FAT32_CHS_LBA
257 | PARTITION_ID_FAT32_LBA
258 | PARTITION_ID_FAT16_LBA
259 | PARTITION_ID_FAT16 => {
260 let volume = fat::parse_volume(self, lba_start, num_blocks)?;
261 Ok(Volume {
262 idx: volume_idx,
263 volume_type: volume,
264 })
265 }
266 _ => Err(Error::FormatError("Partition type not supported")),
267 }
268 }
269
270 pub fn open_root_dir(&mut self, volume: &Volume) -> Result<Directory, Error<D::Error>> {
277 let mut open_dirs_row = None;
281 for (i, d) in self.open_dirs.iter().enumerate() {
282 if *d == (volume.idx, Cluster::ROOT_DIR) {
283 return Err(Error::DirAlreadyOpen);
284 }
285 if d.1 == Cluster::INVALID {
286 open_dirs_row = Some(i);
287 break;
288 }
289 }
290 let open_dirs_row = open_dirs_row.ok_or(Error::TooManyOpenDirs)?;
291 self.open_dirs[open_dirs_row] = (volume.idx, Cluster::ROOT_DIR);
293 Ok(Directory {
294 cluster: Cluster::ROOT_DIR,
295 entry: None,
296 })
297 }
298
299 pub fn open_dir(
306 &mut self,
307 volume: &Volume,
308 parent_dir: &Directory,
309 name: &str,
310 ) -> Result<Directory, Error<D::Error>> {
311 let mut open_dirs_row = None;
313 for (i, d) in self.open_dirs.iter().enumerate() {
314 if d.1 == Cluster::INVALID {
315 open_dirs_row = Some(i);
316 }
317 }
318 let open_dirs_row = open_dirs_row.ok_or(Error::TooManyOpenDirs)?;
319
320 let dir_entry = match &volume.volume_type {
322 VolumeType::Fat(fat) => fat.find_directory_entry(self, parent_dir, name)?,
323 };
324
325 if !dir_entry.attributes.is_directory() {
326 return Err(Error::OpenedDirAsFile);
327 }
328
329 for (_i, dir_table_row) in self.open_dirs.iter().enumerate() {
331 if *dir_table_row == (volume.idx, dir_entry.cluster) {
332 return Err(Error::DirAlreadyOpen);
333 }
334 }
335 self.open_dirs[open_dirs_row] = (volume.idx, dir_entry.cluster);
337 Ok(Directory {
338 cluster: dir_entry.cluster,
339 entry: Some(dir_entry),
340 })
341 }
342
343 pub fn close_dir(&mut self, volume: &Volume, dir: Directory) {
346 let target = (volume.idx, dir.cluster);
347 for d in self.open_dirs.iter_mut() {
348 if *d == target {
349 d.1 = Cluster::INVALID;
350 break;
351 }
352 }
353 drop(dir);
354 }
355
356 pub fn find_directory_entry(
358 &mut self,
359 volume: &Volume,
360 dir: &Directory,
361 name: &str,
362 ) -> Result<DirEntry, Error<D::Error>> {
363 match &volume.volume_type {
364 VolumeType::Fat(fat) => fat.find_directory_entry(self, dir, name),
365 }
366 }
367
368 pub fn iterate_dir<F>(
370 &mut self,
371 volume: &Volume,
372 dir: &Directory,
373 func: F,
374 ) -> Result<(), Error<D::Error>>
375 where
376 F: FnMut(&DirEntry),
377 {
378 match &volume.volume_type {
379 VolumeType::Fat(fat) => fat.iterate_dir(self, dir, func),
380 }
381 }
382
383 pub fn open_file_in_dir(
385 &mut self,
386 volume: &mut Volume,
387 dir: &Directory,
388 name: &str,
389 mode: Mode,
390 ) -> Result<File, Error<D::Error>> {
391 let mut open_files_row = None;
393 for (i, d) in self.open_files.iter().enumerate() {
394 if d.1 == Cluster::INVALID {
395 open_files_row = Some(i);
396 }
397 }
398 let open_files_row = open_files_row.ok_or(Error::TooManyOpenDirs)?;
399 let dir_entry = match &volume.volume_type {
400 VolumeType::Fat(fat) => fat.find_directory_entry(self, dir, name),
401 };
402
403 let dir_entry = match dir_entry {
404 Ok(entry) => Some(entry),
405 Err(_)
406 if (mode == Mode::ReadWriteCreate)
407 | (mode == Mode::ReadWriteCreateOrTruncate)
408 | (mode == Mode::ReadWriteCreateOrAppend) =>
409 {
410 None
411 }
412 _ => return Err(Error::FileNotFound),
413 };
414
415 if let Some(entry) = &dir_entry {
416 for dir_table_row in self.open_files.iter() {
418 if *dir_table_row == (volume.idx, entry.cluster) {
419 return Err(Error::DirAlreadyOpen);
420 }
421 }
422 if entry.attributes.is_directory() {
423 return Err(Error::OpenedDirAsFile);
424 }
425 if entry.attributes.is_read_only() && mode != Mode::ReadOnly {
426 return Err(Error::ReadOnly);
427 }
428 };
429
430 let mut mode = mode;
431 if mode == Mode::ReadWriteCreateOrAppend {
432 if dir_entry.is_some() {
433 mode = Mode::ReadWriteAppend;
434 } else {
435 mode = Mode::ReadWriteCreate;
436 }
437 } else if mode == Mode::ReadWriteCreateOrTruncate {
438 if dir_entry.is_some() {
439 mode = Mode::ReadWriteTruncate;
440 } else {
441 mode = Mode::ReadWriteCreate;
442 }
443 }
444
445 let file = match mode {
446 Mode::ReadOnly => {
447 let dir_entry = dir_entry.unwrap();
449
450 File {
451 starting_cluster: dir_entry.cluster,
452 current_cluster: (0, dir_entry.cluster),
453 current_offset: 0,
454 length: dir_entry.size,
455 mode,
456 entry: dir_entry,
457 }
458 }
459 Mode::ReadWriteAppend => {
460 let dir_entry = dir_entry.unwrap();
462
463 let mut file = File {
464 starting_cluster: dir_entry.cluster,
465 current_cluster: (0, dir_entry.cluster),
466 current_offset: 0,
467 length: dir_entry.size,
468 mode,
469 entry: dir_entry,
470 };
471 file.seek_from_end(0).ok();
473 file
474 }
475 Mode::ReadWriteTruncate => {
476 let dir_entry = dir_entry.unwrap();
478
479 let mut file = File {
480 starting_cluster: dir_entry.cluster,
481 current_cluster: (0, dir_entry.cluster),
482 current_offset: 0,
483 length: dir_entry.size,
484 mode,
485 entry: dir_entry,
486 };
487 match &mut volume.volume_type {
488 VolumeType::Fat(fat) => {
489 fat.truncate_cluster_chain(self, file.starting_cluster)?
490 }
491 };
492 file.update_length(0);
493 match &volume.volume_type {
495 VolumeType::Fat(fat) => {
496 let fat_type = fat.get_fat_type();
497 self.write_entry_to_disk(fat_type, &file.entry)?;
498 }
499 };
500
501 file
502 }
503 Mode::ReadWriteCreate => {
504 if dir_entry.is_some() {
505 return Err(Error::FileAlreadyExists);
506 }
507 let file_name =
508 ShortFileName::create_from_str(name).map_err(Error::FilenameError)?;
509 let att = Attributes::create_from_fat(0);
510 let entry = match &mut volume.volume_type {
511 VolumeType::Fat(fat) => {
512 fat.write_new_directory_entry(self, dir, file_name, att)?
513 }
514 };
515
516 File {
517 starting_cluster: entry.cluster,
518 current_cluster: (0, entry.cluster),
519 current_offset: 0,
520 length: entry.size,
521 mode,
522 entry,
523 }
524 }
525 _ => return Err(Error::Unsupported),
526 };
527 self.open_files[open_files_row] = (volume.idx, file.starting_cluster);
529 Ok(file)
530 }
531
532 pub fn read(
534 &mut self,
535 volume: &Volume,
536 file: &mut File,
537 buffer: &mut [u8],
538 ) -> Result<usize, Error<D::Error>> {
539 let mut space = buffer.len();
543 let mut read = 0;
544 while space > 0 && !file.eof() {
545 let (block_idx, block_offset, block_avail) =
546 self.find_data_on_disk(volume, &mut file.current_cluster, file.current_offset)?;
547 let mut blocks = [Block::new()];
548 self.block_device
549 .read(&mut blocks, block_idx, "read")
550 .map_err(Error::DeviceError)?;
551 let block = &blocks[0];
552 let to_copy = block_avail.min(space).min(file.left() as usize);
553 assert!(to_copy != 0);
554 buffer[read..read + to_copy]
555 .copy_from_slice(&block[block_offset..block_offset + to_copy]);
556 read += to_copy;
557 space -= to_copy;
558 file.seek_from_current(to_copy as i32).unwrap();
559 }
560 Ok(read)
561 }
562
563 pub fn write(
565 &mut self,
566 volume: &mut Volume,
567 file: &mut File,
568 buffer: &[u8],
569 ) -> Result<usize, Error<D::Error>> {
570 debug!(
571 "write(volume={:?}, file={:?}, buffer={:x?}",
572 volume, file, buffer
573 );
574 if file.mode == Mode::ReadOnly {
575 return Err(Error::ReadOnly);
576 }
577 if file.starting_cluster.0 < RESERVED_ENTRIES {
578 file.starting_cluster = match &mut volume.volume_type {
580 VolumeType::Fat(fat) => fat.alloc_cluster(self, None, false)?,
581 };
582 file.entry.cluster = file.starting_cluster;
583 debug!("Alloc first cluster {:?}", file.starting_cluster);
584 }
585 if (file.current_cluster.1).0 < file.starting_cluster.0 {
586 debug!("Rewinding to start");
587 file.current_cluster = (0, file.starting_cluster);
588 }
589 let bytes_until_max = usize::try_from(MAX_FILE_SIZE - file.current_offset)
590 .map_err(|_| Error::ConversionError)?;
591 let bytes_to_write = core::cmp::min(buffer.len(), bytes_until_max);
592 let mut written = 0;
593
594 while written < bytes_to_write {
595 let mut current_cluster = file.current_cluster;
596 debug!(
597 "Have written bytes {}/{}, finding cluster {:?}",
598 written, bytes_to_write, current_cluster
599 );
600 let (block_idx, block_offset, block_avail) =
601 match self.find_data_on_disk(volume, &mut current_cluster, file.current_offset) {
602 Ok(vars) => {
603 debug!(
604 "Found block_idx={:?}, block_offset={:?}, block_avail={}",
605 vars.0, vars.1, vars.2
606 );
607 vars
608 }
609 Err(Error::EndOfFile) => {
610 debug!("Extending file");
611 match &mut volume.volume_type {
612 VolumeType::Fat(ref mut fat) => {
613 if fat
614 .alloc_cluster(self, Some(current_cluster.1), false)
615 .is_err()
616 {
617 return Ok(written);
618 }
619 debug!("Allocated new FAT cluster, finding offsets...");
620 let new_offset = self
621 .find_data_on_disk(
622 volume,
623 &mut current_cluster,
624 file.current_offset,
625 )
626 .map_err(|_| Error::AllocationError)?;
627 debug!("New offset {:?}", new_offset);
628 new_offset
629 }
630 }
631 }
632 Err(e) => return Err(e),
633 };
634 let mut blocks = [Block::new()];
635 let to_copy = core::cmp::min(block_avail, bytes_to_write - written);
636 if block_offset != 0 {
637 debug!("Partial block write");
638 self.block_device
639 .read(&mut blocks, block_idx, "read")
640 .map_err(Error::DeviceError)?;
641 }
642 let block = &mut blocks[0];
643 block[block_offset..block_offset + to_copy]
644 .copy_from_slice(&buffer[written..written + to_copy]);
645 debug!("Writing block {:?}", block_idx);
646 self.block_device
647 .write(&blocks, block_idx)
648 .map_err(Error::DeviceError)?;
649 written += to_copy;
650 file.current_cluster = current_cluster;
651 let to_copy = i32::try_from(to_copy).map_err(|_| Error::ConversionError)?;
652 file.update_length(file.length + (to_copy as u32));
654 file.seek_from_current(to_copy).unwrap();
655 file.entry.attributes.set_archive(true);
656 file.entry.mtime = self.timesource.get_timestamp();
657 debug!("Updating FAT info sector");
658 match &mut volume.volume_type {
659 VolumeType::Fat(fat) => {
660 fat.update_info_sector(self)?;
661 debug!("Updating dir entry");
662 self.write_entry_to_disk(fat.get_fat_type(), &file.entry)?;
663 }
664 }
665 }
666 Ok(written)
667 }
668
669 pub fn close_file(&mut self, volume: &Volume, file: File) -> Result<(), Error<D::Error>> {
671 let target = (volume.idx, file.starting_cluster);
672 for d in self.open_files.iter_mut() {
673 if *d == target {
674 d.1 = Cluster::INVALID;
675 break;
676 }
677 }
678 drop(file);
679 Ok(())
680 }
681
682 fn find_data_on_disk(
686 &mut self,
687 volume: &Volume,
688 start: &mut (u32, Cluster),
689 desired_offset: u32,
690 ) -> Result<(BlockIdx, usize, usize), Error<D::Error>> {
691 let bytes_per_cluster = match &volume.volume_type {
692 VolumeType::Fat(fat) => fat.bytes_per_cluster(),
693 };
694 let offset_from_cluster = desired_offset - start.0;
696 let num_clusters = offset_from_cluster / bytes_per_cluster;
697 for _ in 0..num_clusters {
698 start.1 = match &volume.volume_type {
699 VolumeType::Fat(fat) => fat.next_cluster(self, start.1)?,
700 };
701 start.0 += bytes_per_cluster;
702 }
703 let offset_from_cluster = desired_offset - start.0;
705 assert!(offset_from_cluster < bytes_per_cluster);
706 let num_blocks = BlockCount(offset_from_cluster / Block::LEN_U32);
707 let block_idx = match &volume.volume_type {
708 VolumeType::Fat(fat) => fat.cluster_to_block(start.1),
709 } + num_blocks;
710 let block_offset = (desired_offset % Block::LEN_U32) as usize;
711 let available = Block::LEN - block_offset;
712 Ok((block_idx, block_offset, available))
713 }
714
715 fn write_entry_to_disk(
717 &mut self,
718 fat_type: fat::FatType,
719 entry: &DirEntry,
720 ) -> Result<(), Error<D::Error>> {
721 let mut blocks = [Block::new()];
722 self.block_device
723 .read(&mut blocks, entry.entry_block, "read")
724 .map_err(Error::DeviceError)?;
725 let block = &mut blocks[0];
726
727 let start = usize::try_from(entry.entry_offset).map_err(|_| Error::ConversionError)?;
728 block[start..start + 32].copy_from_slice(&entry.serialize(fat_type)[..]);
729
730 self.block_device
731 .write(&blocks, entry.entry_block)
732 .map_err(Error::DeviceError)?;
733 Ok(())
734 }
735}
736
737#[cfg(test)]
752mod tests {
753 use super::*;
754
755 struct DummyBlockDevice;
756
757 struct Clock;
758
759 #[derive(Debug)]
760 enum Error {
761 Unknown,
762 }
763
764 impl TimeSource for Clock {
765 fn get_timestamp(&self) -> Timestamp {
766 Timestamp {
768 year_since_1970: 0,
769 zero_indexed_month: 0,
770 zero_indexed_day: 0,
771 hours: 0,
772 minutes: 0,
773 seconds: 0,
774 }
775 }
776 }
777
778 impl BlockDevice for DummyBlockDevice {
779 type Error = Error;
780
781 fn read(
783 &self,
784 blocks: &mut [Block],
785 start_block_idx: BlockIdx,
786 _reason: &str,
787 ) -> Result<(), Self::Error> {
788 static BLOCKS: [Block; 3] = [
790 Block {
791 contents: [
792 0xfa, 0xb8, 0x00, 0x10, 0x8e, 0xd0, 0xbc, 0x00, 0xb0, 0xb8, 0x00, 0x00,
793 0x8e, 0xd8, 0x8e, 0xc0, 0xfb, 0xbe, 0x00, 0x7c, 0xbf, 0x00, 0x06, 0xb9, 0x00, 0x02, 0xf3, 0xa4,
795 0xea, 0x21, 0x06, 0x00, 0x00, 0xbe, 0xbe, 0x07, 0x38, 0x04, 0x75, 0x0b, 0x83, 0xc6, 0x10, 0x81,
797 0xfe, 0xfe, 0x07, 0x75, 0xf3, 0xeb, 0x16, 0xb4, 0x02, 0xb0, 0x01, 0xbb, 0x00, 0x7c, 0xb2, 0x80,
799 0x8a, 0x74, 0x01, 0x8b, 0x4c, 0x02, 0xcd, 0x13, 0xea, 0x00, 0x7c, 0x00, 0x00, 0xeb, 0xfe, 0x00,
801 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
803 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
805 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
807 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
809 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
811 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
813 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
815 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
817 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
819 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
821 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
823 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
825 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
827 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
829 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
831 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
833 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
835 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
837 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
839 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
841 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
843 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
845 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xca, 0xde, 0x06,
847 0x00, 0x00, 0x00, 0x04, 0x01, 0x04, 0x0c, 0xfe, 0xc2, 0xff, 0x01, 0x00, 0x00, 0x00, 0x33, 0x22,
849 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
851 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
853 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
855 0x00, 0x00, 0x55, 0xaa, ],
857 },
858 Block {
859 contents: [
860 0xeb, 0x58, 0x90, 0x6d, 0x6b, 0x66, 0x73, 0x2e, 0x66, 0x61, 0x74, 0x00,
861 0x02, 0x08, 0x20, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x10, 0x00, 0x04, 0x00,
863 0x00, 0x08, 0x00, 0x00, 0x00, 0x20, 0x76, 0x00, 0x80, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
865 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
867 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x29, 0x0b, 0xa8, 0x89, 0x27, 0x50, 0x69, 0x63, 0x74, 0x75,
869 0x72, 0x65, 0x73, 0x20, 0x20, 0x20, 0x46, 0x41, 0x54, 0x33, 0x32, 0x20, 0x20, 0x20, 0x0e, 0x1f,
871 0xbe, 0x77, 0x7c, 0xac, 0x22, 0xc0, 0x74, 0x0b, 0x56, 0xb4, 0x0e, 0xbb, 0x07, 0x00, 0xcd, 0x10,
873 0x5e, 0xeb, 0xf0, 0x32, 0xe4, 0xcd, 0x16, 0xcd, 0x19, 0xeb, 0xfe, 0x54, 0x68, 0x69, 0x73, 0x20,
875 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c,
877 0x65, 0x20, 0x64, 0x69, 0x73, 0x6b, 0x2e, 0x20, 0x20, 0x50, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20,
879 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c,
881 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x70, 0x70, 0x79, 0x20, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x70, 0x72,
883 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x74,
885 0x72, 0x79, 0x20, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x0d, 0x0a, 0x00,
887 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
889 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
891 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
893 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
895 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
897 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
899 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
901 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
903 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
905 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
907 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
909 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
911 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
913 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
915 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
917 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
919 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
921 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
923 0x00, 0x00, 0x55, 0xaa, ],
925 },
926 Block {
927 contents: [
928 0x52, 0x52, 0x61, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
929 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
930 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
931 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
932 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
933 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
934 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
935 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
936 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
937 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
938 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
939 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
940 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
941 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
942 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
943 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
944 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
945 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
946 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
947 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
948 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
949 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
950 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
951 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
952 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
953 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
954 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
955 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
956 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
957 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
958 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
959 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
960 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
961 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
962 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
963 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
964 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
965 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
966 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
967 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
968 0x00, 0x00, 0x00, 0x00, 0x72, 0x72, 0x41, 0x61, 0xFF, 0xFF, 0xFF, 0xFF,
969 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
970 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA,
971 ],
972 },
973 ];
974 println!(
975 "Reading block {} to {}",
976 start_block_idx.0,
977 start_block_idx.0 as usize + blocks.len()
978 );
979 for (idx, block) in blocks.iter_mut().enumerate() {
980 let block_idx = start_block_idx.0 as usize + idx;
981 if block_idx < BLOCKS.len() {
982 *block = BLOCKS[block_idx].clone();
983 } else {
984 return Err(Error::Unknown);
985 }
986 }
987 Ok(())
988 }
989
990 fn write(&self, _blocks: &[Block], _start_block_idx: BlockIdx) -> Result<(), Self::Error> {
992 unimplemented!();
993 }
994
995 fn num_blocks(&self) -> Result<BlockCount, Self::Error> {
997 Ok(BlockCount(2))
998 }
999 }
1000
1001 #[test]
1002 fn partition0() {
1003 let mut c = Controller::new(DummyBlockDevice, Clock);
1004 let v = c.get_volume(VolumeIdx(0)).unwrap();
1005 assert_eq!(
1006 v,
1007 Volume {
1008 idx: VolumeIdx(0),
1009 volume_type: VolumeType::Fat(FatVolume {
1010 lba_start: BlockIdx(1),
1011 num_blocks: BlockCount(0x0011_2233),
1012 blocks_per_cluster: 8,
1013 first_data_block: BlockCount(15136),
1014 fat_start: BlockCount(32),
1015 name: fat::VolumeName {
1016 data: *b"Pictures "
1017 },
1018 free_clusters_count: None,
1019 next_free_cluster: None,
1020 cluster_count: 965_788,
1021 fat_specific_info: fat::FatSpecificInfo::Fat32(fat::Fat32Info {
1022 first_root_dir_cluster: Cluster(2),
1023 info_location: BlockIdx(1) + BlockCount(1),
1024 })
1025 })
1026 }
1027 );
1028 }
1029}
1030
1031