1use core::cell::RefCell;
6use core::convert::TryFrom;
7use core::ops::{ControlFlow, DerefMut};
8
9use byteorder::{ByteOrder, LittleEndian};
10use heapless::Vec;
11
12use crate::{
13 Block, BlockCache, BlockCount, BlockDevice, BlockIdx, Error, PARTITION_ID_FAT16,
14 PARTITION_ID_FAT16_LBA, PARTITION_ID_FAT16_SMALL, PARTITION_ID_FAT32_CHS_LBA,
15 PARTITION_ID_FAT32_LBA, RawVolume, ShortFileName, Volume, VolumeIdx, VolumeInfo, VolumeType,
16 debug, fat,
17 filesystem::{
18 Attributes, ClusterId, DirEntry, DirectoryInfo, FileInfo, HandleGenerator, LfnBuffer,
19 MAX_FILE_SIZE, Mode, RawDirectory, RawFile, TimeSource, ToShortFileName,
20 },
21 trace,
22};
23
24#[derive(Debug)]
30pub struct VolumeManager<
31 D,
32 T,
33 const MAX_DIRS: usize = 4,
34 const MAX_FILES: usize = 4,
35 const MAX_VOLUMES: usize = 1,
36> where
37 D: BlockDevice,
38 T: TimeSource,
39{
40 time_source: T,
41 data: RefCell<VolumeManagerData<D, MAX_DIRS, MAX_FILES, MAX_VOLUMES>>,
42}
43
44impl<D, T> VolumeManager<D, T, 4, 4>
45where
46 D: BlockDevice,
47 T: TimeSource,
48{
49 pub fn new(block_device: D, time_source: T) -> Self {
57 Self::new_with_limits(block_device, time_source, 5000)
60 }
61}
62
63impl<D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
64 VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
65where
66 D: BlockDevice,
67 T: TimeSource,
68{
69 pub fn new_with_limits(block_device: D, time_source: T, id_offset: u32) -> Self {
77 debug!("Creating new embedded-sdmmc::VolumeManager");
78 VolumeManager {
79 time_source,
80 data: RefCell::new(VolumeManagerData {
81 block_cache: BlockCache::new(block_device),
82 id_generator: HandleGenerator::new(id_offset),
83 open_volumes: Vec::new(),
84 open_dirs: Vec::new(),
85 open_files: Vec::new(),
86 }),
87 }
88 }
89
90 pub fn device<R, F>(&self, f: F) -> R
92 where
93 F: FnOnce(&mut D) -> R,
94 {
95 let mut data = self.data.borrow_mut();
96
97 f(data.block_cache.block_device())
98 }
99
100 pub fn open_volume(
105 &self,
106 volume_idx: VolumeIdx,
107 ) -> Result<Volume<'_, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>, Error<D::Error>> {
108 let v = self.open_raw_volume(volume_idx)?;
109 Ok(v.to_volume(self))
110 }
111
112 pub fn open_raw_volume(&self, volume_idx: VolumeIdx) -> Result<RawVolume, Error<D::Error>> {
129 const PARTITION1_START: usize = 446;
130 const PARTITION2_START: usize = PARTITION1_START + PARTITION_INFO_LENGTH;
131 const PARTITION3_START: usize = PARTITION2_START + PARTITION_INFO_LENGTH;
132 const PARTITION4_START: usize = PARTITION3_START + PARTITION_INFO_LENGTH;
133 const FOOTER_START: usize = 510;
134 const FOOTER_VALUE: u16 = 0xAA55;
135 const PARTITION_INFO_LENGTH: usize = 16;
136 const PARTITION_INFO_STATUS_INDEX: usize = 0;
137 const PARTITION_INFO_TYPE_INDEX: usize = 4;
138 const PARTITION_INFO_LBA_START_INDEX: usize = 8;
139 const PARTITION_INFO_NUM_BLOCKS_INDEX: usize = 12;
140
141 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
142
143 if data.open_volumes.is_full() {
144 return Err(Error::TooManyOpenVolumes);
145 }
146
147 for v in data.open_volumes.iter() {
148 if v.idx == volume_idx {
149 return Err(Error::VolumeAlreadyOpen);
150 }
151 }
152
153 let (part_type, lba_start, num_blocks) = {
154 trace!("Reading partition table");
155 let block = data
156 .block_cache
157 .read(BlockIdx(0))
158 .map_err(Error::DeviceError)?;
159 if LittleEndian::read_u16(&block[FOOTER_START..FOOTER_START + 2]) != FOOTER_VALUE {
162 return Err(Error::FormatError("Invalid MBR signature"));
163 }
164 let partition = match volume_idx {
165 VolumeIdx(0) => {
166 &block[PARTITION1_START..(PARTITION1_START + PARTITION_INFO_LENGTH)]
167 }
168 VolumeIdx(1) => {
169 &block[PARTITION2_START..(PARTITION2_START + PARTITION_INFO_LENGTH)]
170 }
171 VolumeIdx(2) => {
172 &block[PARTITION3_START..(PARTITION3_START + PARTITION_INFO_LENGTH)]
173 }
174 VolumeIdx(3) => {
175 &block[PARTITION4_START..(PARTITION4_START + PARTITION_INFO_LENGTH)]
176 }
177 _ => {
178 return Err(Error::NoSuchVolume);
179 }
180 };
181 if (partition[PARTITION_INFO_STATUS_INDEX] & 0x7F) != 0x00 {
183 return Err(Error::FormatError("Invalid partition status"));
184 }
185 let lba_start = LittleEndian::read_u32(
186 &partition[PARTITION_INFO_LBA_START_INDEX..(PARTITION_INFO_LBA_START_INDEX + 4)],
187 );
188 let num_blocks = LittleEndian::read_u32(
189 &partition[PARTITION_INFO_NUM_BLOCKS_INDEX..(PARTITION_INFO_NUM_BLOCKS_INDEX + 4)],
190 );
191 (
192 partition[PARTITION_INFO_TYPE_INDEX],
193 BlockIdx(lba_start),
194 BlockCount(num_blocks),
195 )
196 };
197 match part_type {
198 PARTITION_ID_FAT32_CHS_LBA
199 | PARTITION_ID_FAT32_LBA
200 | PARTITION_ID_FAT16_LBA
201 | PARTITION_ID_FAT16
202 | PARTITION_ID_FAT16_SMALL => {
203 let volume = fat::parse_volume(&mut data.block_cache, lba_start, num_blocks)?;
204 let id = RawVolume(data.id_generator.generate());
205 let info = VolumeInfo {
206 raw_volume: id,
207 idx: volume_idx,
208 volume_type: volume,
209 };
210 data.open_volumes.push(info).unwrap();
212 Ok(id)
213 }
214 _ => Err(Error::FormatError("Partition type not supported")),
215 }
216 }
217
218 pub fn open_root_dir(&self, volume: RawVolume) -> Result<RawDirectory, Error<D::Error>> {
235 debug!("Opening root on {:?}", volume);
236
237 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
239
240 let directory_id = RawDirectory(data.id_generator.generate());
241 let dir_info = DirectoryInfo {
242 raw_volume: volume,
243 cluster: ClusterId::ROOT_DIR,
244 raw_directory: directory_id,
245 };
246
247 data.open_dirs
248 .push(dir_info)
249 .map_err(|_| Error::TooManyOpenDirs)?;
250
251 debug!("Opened root on {:?}, got {:?}", volume, directory_id);
252
253 Ok(directory_id)
254 }
255
256 pub fn open_dir<N>(
274 &self,
275 parent_dir: RawDirectory,
276 name: N,
277 ) -> Result<RawDirectory, Error<D::Error>>
278 where
279 N: ToShortFileName,
280 {
281 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
282 let data = data.deref_mut();
283
284 if data.open_dirs.is_full() {
285 return Err(Error::TooManyOpenDirs);
286 }
287
288 let parent_dir_idx = data.get_dir_by_id(parent_dir)?;
290 let volume_idx = data.get_volume_by_id(data.open_dirs[parent_dir_idx].raw_volume)?;
291 let short_file_name = name.to_short_filename().map_err(Error::FilenameError)?;
292
293 if short_file_name == ShortFileName::this_dir() {
297 let directory_id = RawDirectory(data.id_generator.generate());
298 let dir_info = DirectoryInfo {
299 raw_directory: directory_id,
300 raw_volume: data.open_volumes[volume_idx].raw_volume,
301 cluster: data.open_dirs[parent_dir_idx].cluster,
302 };
303
304 data.open_dirs
305 .push(dir_info)
306 .map_err(|_| Error::TooManyOpenDirs)?;
307
308 return Ok(directory_id);
309 }
310
311 let dir_entry = match &data.open_volumes[volume_idx].volume_type {
314 VolumeType::Fat(fat) => fat.find_directory_entry(
315 &mut data.block_cache,
316 &data.open_dirs[parent_dir_idx],
317 &short_file_name,
318 )?,
319 };
320
321 debug!("Found dir entry: {:?}", dir_entry);
322
323 if !dir_entry.attributes.is_directory() {
324 return Err(Error::OpenedFileAsDir);
325 }
326
327 let directory_id = RawDirectory(data.id_generator.generate());
332 let dir_info = DirectoryInfo {
333 raw_directory: directory_id,
334 raw_volume: data.open_volumes[volume_idx].raw_volume,
335 cluster: dir_entry.cluster,
336 };
337
338 data.open_dirs
339 .push(dir_info)
340 .map_err(|_| Error::TooManyOpenDirs)?;
341
342 Ok(directory_id)
343 }
344
345 pub fn close_dir(&self, directory: RawDirectory) -> Result<(), Error<D::Error>> {
352 debug!("Closing {:?}", directory);
353 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
354
355 for (idx, info) in data.open_dirs.iter().enumerate() {
356 if directory == info.raw_directory {
357 data.open_dirs.swap_remove(idx);
358 return Ok(());
359 }
360 }
361 Err(Error::BadHandle)
362 }
363
364 pub fn close_volume(&self, volume: RawVolume) -> Result<(), Error<D::Error>> {
375 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
376 let data = data.deref_mut();
377
378 for f in data.open_files.iter() {
379 if f.raw_volume == volume {
380 return Err(Error::VolumeStillInUse);
381 }
382 }
383
384 for d in data.open_dirs.iter() {
385 if d.raw_volume == volume {
386 return Err(Error::VolumeStillInUse);
387 }
388 }
389
390 let volume_idx = data.get_volume_by_id(volume)?;
391
392 let update_result = match &mut data.open_volumes[volume_idx].volume_type {
393 VolumeType::Fat(fat) => fat.update_info_sector(&mut data.block_cache),
394 };
395
396 data.open_volumes.swap_remove(volume_idx);
397
398 update_result
399 }
400
401 pub fn find_directory_entry<N>(
415 &self,
416 directory: RawDirectory,
417 name: N,
418 ) -> Result<DirEntry, Error<D::Error>>
419 where
420 N: ToShortFileName,
421 {
422 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
423 let data = data.deref_mut();
424
425 let directory_idx = data.get_dir_by_id(directory)?;
426 let volume_idx = data.get_volume_by_id(data.open_dirs[directory_idx].raw_volume)?;
427 match &data.open_volumes[volume_idx].volume_type {
428 VolumeType::Fat(fat) => {
429 let sfn = name.to_short_filename().map_err(Error::FilenameError)?;
430 fat.find_directory_entry(
431 &mut data.block_cache,
432 &data.open_dirs[directory_idx],
433 &sfn,
434 )
435 }
436 }
437 }
438
439 pub fn iterate_dir<F>(
456 &self,
457 directory: RawDirectory,
458 mut func: F,
459 ) -> Result<(), Error<D::Error>>
460 where
461 F: FnMut(&DirEntry) -> ControlFlow<()>,
462 {
463 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
464 let data = data.deref_mut();
465
466 let directory_idx = data.get_dir_by_id(directory)?;
467 let volume_idx = data.get_volume_by_id(data.open_dirs[directory_idx].raw_volume)?;
468 match &data.open_volumes[volume_idx].volume_type {
469 VolumeType::Fat(fat) => {
470 fat.iterate_dir(
471 &mut data.block_cache,
472 &data.open_dirs[directory_idx],
473 |de| {
474 if !de.attributes.is_lfn() {
476 func(de)
477 } else {
478 ControlFlow::Continue(())
479 }
480 },
481 )
482 }
483 }
484 }
485
486 pub fn iterate_dir_lfn<F>(
507 &self,
508 directory: RawDirectory,
509 lfn_buffer: &mut LfnBuffer<'_>,
510 func: F,
511 ) -> Result<(), Error<D::Error>>
512 where
513 F: FnMut(&DirEntry, Option<&str>) -> ControlFlow<()>,
514 {
515 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
516 let data = data.deref_mut();
517
518 let directory_idx = data.get_dir_by_id(directory)?;
519 let volume_idx = data.get_volume_by_id(data.open_dirs[directory_idx].raw_volume)?;
520
521 match &data.open_volumes[volume_idx].volume_type {
522 VolumeType::Fat(fat) => {
523 fat.iterate_dir_lfn(
525 &mut data.block_cache,
526 lfn_buffer,
527 &data.open_dirs[directory_idx],
528 func,
529 )
530 }
531 }
532 }
533
534 pub fn open_file_in_dir<N>(
548 &self,
549 directory: RawDirectory,
550 name: N,
551 mode: Mode,
552 ) -> Result<RawFile, Error<D::Error>>
553 where
554 N: ToShortFileName,
555 {
556 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
557 let data = data.deref_mut();
558
559 if data.open_files.is_full() {
561 return Err(Error::TooManyOpenFiles);
562 }
563
564 let directory_idx = data.get_dir_by_id(directory)?;
565 let volume_id = data.open_dirs[directory_idx].raw_volume;
566 let volume_idx = data.get_volume_by_id(volume_id)?;
567 let volume_info = &data.open_volumes[volume_idx];
568 let sfn = name.to_short_filename().map_err(Error::FilenameError)?;
569
570 let dir_entry = match &volume_info.volume_type {
571 VolumeType::Fat(fat) => fat.find_directory_entry(
572 &mut data.block_cache,
573 &data.open_dirs[directory_idx],
574 &sfn,
575 ),
576 };
577
578 let dir_entry = match dir_entry {
579 Ok(entry) => {
580 Some(entry)
582 }
583 Err(_)
584 if (mode == Mode::ReadWriteCreate)
585 | (mode == Mode::ReadWriteCreateOrTruncate)
586 | (mode == Mode::ReadWriteCreateOrAppend) =>
587 {
588 None
591 }
592 _ => {
593 return Err(Error::NotFound);
595 }
596 };
597
598 if let Some(dir_entry) = &dir_entry {
600 if data.file_is_open(volume_info.raw_volume, dir_entry) {
601 return Err(Error::FileAlreadyOpen);
602 }
603 }
604
605 let mode = solve_mode_variant(mode, dir_entry.is_some());
606
607 match mode {
608 Mode::ReadWriteCreate => {
609 if dir_entry.is_some() {
610 return Err(Error::FileAlreadyExists);
611 }
612 let cluster = data.open_dirs[directory_idx].cluster;
613 let att = Attributes::create_from_fat(0);
614 let volume_idx = data.get_volume_by_id(volume_id)?;
615 let entry = match &mut data.open_volumes[volume_idx].volume_type {
616 VolumeType::Fat(fat) => fat.write_new_directory_entry(
617 &mut data.block_cache,
618 &self.time_source,
619 cluster,
620 sfn,
621 att,
622 )?,
623 };
624
625 let file_id = RawFile(data.id_generator.generate());
626
627 let file = FileInfo {
628 raw_file: file_id,
629 raw_volume: volume_id,
630 current_cluster: (0, entry.cluster),
631 current_offset: 0,
632 mode,
633 entry,
634 dirty: false,
635 };
636
637 unsafe {
639 data.open_files.push_unchecked(file);
640 }
641
642 Ok(file_id)
643 }
644 _ => {
645 let dir_entry = dir_entry.unwrap();
647
648 if dir_entry.attributes.is_read_only() && mode != Mode::ReadOnly {
649 return Err(Error::ReadOnly);
650 }
651
652 if dir_entry.attributes.is_directory() {
653 return Err(Error::OpenedDirAsFile);
654 }
655
656 if data.file_is_open(volume_id, &dir_entry) {
658 return Err(Error::FileAlreadyOpen);
659 }
660
661 let mode = solve_mode_variant(mode, true);
662 let raw_file = RawFile(data.id_generator.generate());
663
664 let file = match mode {
665 Mode::ReadOnly => FileInfo {
666 raw_file,
667 raw_volume: volume_id,
668 current_cluster: (0, dir_entry.cluster),
669 current_offset: 0,
670 mode,
671 entry: dir_entry,
672 dirty: false,
673 },
674 Mode::ReadWriteAppend => {
675 let mut file = FileInfo {
676 raw_file,
677 raw_volume: volume_id,
678 current_cluster: (0, dir_entry.cluster),
679 current_offset: 0,
680 mode,
681 entry: dir_entry,
682 dirty: false,
683 };
684 file.seek_from_end(0).ok();
686 file
687 }
688 Mode::ReadWriteTruncate => {
689 let mut file = FileInfo {
690 raw_file,
691 raw_volume: volume_id,
692 current_cluster: (0, dir_entry.cluster),
693 current_offset: 0,
694 mode,
695 entry: dir_entry,
696 dirty: false,
697 };
698 match &mut data.open_volumes[volume_idx].volume_type {
699 VolumeType::Fat(fat) => fat.truncate_cluster_chain(
700 &mut data.block_cache,
701 file.entry.cluster,
702 )?,
703 };
704 file.update_length(0);
705 match &data.open_volumes[volume_idx].volume_type {
706 VolumeType::Fat(fat) => {
707 file.entry.mtime = self.time_source.get_timestamp();
708 fat.write_entry_to_disk(&mut data.block_cache, &file.entry)?;
709 }
710 };
711
712 file
713 }
714 _ => return Err(Error::Unsupported),
715 };
716
717 unsafe {
719 data.open_files.push_unchecked(file);
720 }
721
722 Ok(raw_file)
723 }
724 }
725 }
726
727 pub fn open_long_name_file_in_dir(
743 &self,
744 directory: RawDirectory,
745 name: &str,
746 mode: Mode,
747 ) -> Result<RawFile, Error<D::Error>> {
748 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
749 let data = data.deref_mut();
750
751 if data.open_files.is_full() {
753 return Err(Error::TooManyOpenFiles);
754 }
755
756 let directory_idx = data.get_dir_by_id(directory)?;
757 let volume_id = data.open_dirs[directory_idx].raw_volume;
758 let volume_idx = data.get_volume_by_id(volume_id)?;
759 let volume_info = &data.open_volumes[volume_idx];
760
761 let dir_entry = match &volume_info.volume_type {
762 VolumeType::Fat(fat) => fat.find_directory_entry_by_lfn(
763 &mut data.block_cache,
764 &data.open_dirs[directory_idx],
765 name,
766 ),
767 };
768
769 let dir_entry = match dir_entry {
770 Ok(entry) => {
771 entry
773 }
774 Err(_)
775 if (mode == Mode::ReadWriteCreate)
776 | (mode == Mode::ReadWriteCreateOrTruncate)
777 | (mode == Mode::ReadWriteCreateOrAppend) =>
778 {
779 return Err(Error::NotFound);
781 }
782 _ => {
783 return Err(Error::NotFound);
785 }
786 };
787
788 if data.file_is_open(volume_info.raw_volume, &dir_entry) {
790 return Err(Error::FileAlreadyOpen);
791 }
792
793 let mode = solve_mode_variant(mode, true);
794
795 match mode {
796 Mode::ReadWriteCreate => Err(Error::FileAlreadyExists),
797 _ => {
798 if dir_entry.attributes.is_read_only() && mode != Mode::ReadOnly {
799 return Err(Error::ReadOnly);
800 }
801
802 if dir_entry.attributes.is_directory() {
803 return Err(Error::OpenedDirAsFile);
804 }
805
806 if data.file_is_open(volume_id, &dir_entry) {
808 return Err(Error::FileAlreadyOpen);
809 }
810
811 let mode = solve_mode_variant(mode, true);
812 let raw_file = RawFile(data.id_generator.generate());
813
814 let file = match mode {
815 Mode::ReadOnly => FileInfo {
816 raw_file,
817 raw_volume: volume_id,
818 current_cluster: (0, dir_entry.cluster),
819 current_offset: 0,
820 mode,
821 entry: dir_entry,
822 dirty: false,
823 },
824 Mode::ReadWriteAppend => {
825 let mut file = FileInfo {
826 raw_file,
827 raw_volume: volume_id,
828 current_cluster: (0, dir_entry.cluster),
829 current_offset: 0,
830 mode,
831 entry: dir_entry,
832 dirty: false,
833 };
834 file.seek_from_end(0).ok();
836 file
837 }
838 Mode::ReadWriteTruncate => {
839 let mut file = FileInfo {
840 raw_file,
841 raw_volume: volume_id,
842 current_cluster: (0, dir_entry.cluster),
843 current_offset: 0,
844 mode,
845 entry: dir_entry,
846 dirty: false,
847 };
848 match &mut data.open_volumes[volume_idx].volume_type {
849 VolumeType::Fat(fat) => fat.truncate_cluster_chain(
850 &mut data.block_cache,
851 file.entry.cluster,
852 )?,
853 };
854 file.update_length(0);
855 match &data.open_volumes[volume_idx].volume_type {
856 VolumeType::Fat(fat) => {
857 file.entry.mtime = self.time_source.get_timestamp();
858 fat.write_entry_to_disk(&mut data.block_cache, &file.entry)?;
859 }
860 };
861
862 file
863 }
864 _ => return Err(Error::Unsupported),
865 };
866
867 unsafe {
869 data.open_files.push_unchecked(file);
870 }
871
872 Ok(raw_file)
873 }
874 }
875 }
876
877 pub fn delete_entry_in_dir<N>(
879 &self,
880 directory: RawDirectory,
881 name: N,
882 ) -> Result<(), Error<D::Error>>
883 where
884 N: ToShortFileName,
885 {
886 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
887 let data = data.deref_mut();
888
889 let dir_idx = data.get_dir_by_id(directory)?;
890 let parent_dir_info = &data.open_dirs[dir_idx];
891 let volume_idx = data.get_volume_by_id(parent_dir_info.raw_volume)?;
892 let sfn = name.to_short_filename().map_err(Error::FilenameError)?;
893
894 let dir_entry = match &data.open_volumes[volume_idx].volume_type {
895 VolumeType::Fat(fat) => {
896 fat.find_directory_entry(&mut data.block_cache, parent_dir_info, &sfn)
897 }
898 }?;
899
900 if dir_entry.attributes.is_directory() {
901 if data
903 .open_dirs
904 .iter()
905 .any(|dir_info| dir_info.cluster == dir_entry.cluster)
906 {
907 return Err(Error::DirAlreadyOpen);
909 }
910 let raw_directory = RawDirectory(data.id_generator.generate());
912 let dir_info = DirectoryInfo {
913 raw_directory,
914 raw_volume: data.open_volumes[volume_idx].raw_volume,
915 cluster: dir_entry.cluster,
916 };
917 let mut count = 0;
919 match &data.open_volumes[volume_idx].volume_type {
921 VolumeType::Fat(fat) => {
922 fat.iterate_dir(&mut data.block_cache, &dir_info, |de| {
923 if !de.attributes.is_lfn()
925 && de.name != ShortFileName::this_dir()
926 && de.name != ShortFileName::parent_dir()
927 {
928 count += 1;
929 }
930 ControlFlow::Continue(())
931 })?;
932 }
933 }
934 if count != 0 {
935 return Err(Error::DeleteNonEmptyDir);
936 }
937 } else if data.file_is_open(parent_dir_info.raw_volume, &dir_entry) {
938 return Err(Error::FileAlreadyOpen);
939 }
940
941 let volume_idx = data.get_volume_by_id(parent_dir_info.raw_volume)?;
942 match &data.open_volumes[volume_idx].volume_type {
943 VolumeType::Fat(fat) => {
944 fat.delete_directory_entry(&mut data.block_cache, parent_dir_info, &sfn)?
945 }
946 }
947
948 Ok(())
949 }
950
951 pub fn get_root_volume_label(
956 &self,
957 raw_volume: RawVolume,
958 ) -> Result<Option<crate::VolumeName>, Error<D::Error>> {
959 debug!("Reading volume label for {:?}", raw_volume);
960 let data = self.data.try_borrow().map_err(|_| Error::LockError)?;
962 let volume_idx = data.get_volume_by_id(raw_volume)?;
963 match &data.open_volumes[volume_idx].volume_type {
964 VolumeType::Fat(fat) => {
965 if !fat.name.name().is_empty() {
966 debug!(
967 "Got volume label {:?} for {:?} from BPB",
968 fat.name, raw_volume
969 );
970 return Ok(Some(fat.name.clone()));
971 }
972 }
973 }
974 drop(data);
975
976 let root_dir = self.open_root_dir(raw_volume)?.to_directory(self);
978 let mut maybe_volume_name = None;
979 root_dir.iterate_dir(|de| {
980 if maybe_volume_name.is_none()
981 && de.attributes == Attributes::create_from_fat(Attributes::VOLUME)
982 {
983 maybe_volume_name = Some(unsafe { de.name.to_volume_label() });
984 ControlFlow::Break(())
985 } else {
986 ControlFlow::Continue(())
987 }
988 })?;
989
990 debug!(
991 "Got volume label {:?} for {:?} from root",
992 maybe_volume_name, raw_volume
993 );
994
995 Ok(maybe_volume_name)
996 }
997
998 pub fn read(&self, file: RawFile, buffer: &mut [u8]) -> Result<usize, Error<D::Error>> {
1007 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
1008 let data = data.deref_mut();
1009
1010 let file_idx = data.get_file_by_id(file)?;
1011 let volume_idx = data.get_volume_by_id(data.open_files[file_idx].raw_volume)?;
1012
1013 let mut space = buffer.len();
1017 let mut read = 0;
1018 while space > 0 && !data.open_files[file_idx].eof() {
1019 let mut current_cluster = data.open_files[file_idx].current_cluster;
1020 let (block_idx, block_offset, block_avail) = data.find_data_on_disk(
1021 volume_idx,
1022 &mut current_cluster,
1023 data.open_files[file_idx].entry.cluster,
1024 data.open_files[file_idx].current_offset,
1025 )?;
1026 data.open_files[file_idx].current_cluster = current_cluster;
1027 trace!("Reading file ID {:?}", file);
1028 let block = data
1029 .block_cache
1030 .read(block_idx)
1031 .map_err(Error::DeviceError)?;
1032 let to_copy = block_avail
1033 .min(space)
1034 .min(data.open_files[file_idx].left() as usize);
1035 assert!(to_copy != 0);
1036 buffer[read..read + to_copy]
1037 .copy_from_slice(&block[block_offset..block_offset + to_copy]);
1038 read += to_copy;
1039 space -= to_copy;
1040 data.open_files[file_idx]
1041 .seek_from_current(to_copy as i32)
1042 .unwrap();
1043 }
1044 Ok(read)
1045 }
1046
1047 pub fn write(&self, file: RawFile, buffer: &[u8]) -> Result<(), Error<D::Error>> {
1056 #[cfg(feature = "defmt-log")]
1057 debug!("write(file={:?}, buffer={:x}", file, buffer);
1058
1059 #[cfg(feature = "log")]
1060 debug!("write(file={:?}, buffer={:x?}", file, buffer);
1061
1062 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
1063 let data = data.deref_mut();
1064
1065 let file_idx = data.get_file_by_id(file)?;
1068 let volume_idx = data.get_volume_by_id(data.open_files[file_idx].raw_volume)?;
1069
1070 if data.open_files[file_idx].mode == Mode::ReadOnly {
1071 return Err(Error::ReadOnly);
1072 }
1073
1074 data.open_files[file_idx].dirty = true;
1075
1076 if data.open_files[file_idx].entry.cluster.0 < fat::RESERVED_ENTRIES {
1077 data.open_files[file_idx].entry.cluster =
1079 match data.open_volumes[volume_idx].volume_type {
1080 VolumeType::Fat(ref mut fat) => {
1081 fat.alloc_cluster(&mut data.block_cache, None, false)?
1082 }
1083 };
1084 debug!(
1085 "Alloc first cluster {:?}",
1086 data.open_files[file_idx].entry.cluster
1087 );
1088 }
1089
1090 let volume_idx = data.get_volume_by_id(data.open_files[file_idx].raw_volume)?;
1092
1093 if (data.open_files[file_idx].current_cluster.1) < data.open_files[file_idx].entry.cluster {
1094 debug!("Rewinding to start");
1095 data.open_files[file_idx].current_cluster =
1096 (0, data.open_files[file_idx].entry.cluster);
1097 }
1098 let bytes_until_max =
1099 usize::try_from(MAX_FILE_SIZE - data.open_files[file_idx].current_offset)
1100 .map_err(|_| Error::ConversionError)?;
1101 let bytes_to_write = core::cmp::min(buffer.len(), bytes_until_max);
1102 let mut written = 0;
1103
1104 while written < bytes_to_write {
1105 let mut current_cluster = data.open_files[file_idx].current_cluster;
1106 debug!(
1107 "Have written bytes {}/{}, finding cluster {:?}",
1108 written, bytes_to_write, current_cluster
1109 );
1110 let current_offset = data.open_files[file_idx].current_offset;
1111 let (block_idx, block_offset, block_avail) = match data.find_data_on_disk(
1112 volume_idx,
1113 &mut current_cluster,
1114 data.open_files[file_idx].entry.cluster,
1115 current_offset,
1116 ) {
1117 Ok(vars) => {
1118 debug!(
1119 "Found block_idx={:?}, block_offset={:?}, block_avail={}",
1120 vars.0, vars.1, vars.2
1121 );
1122 vars
1123 }
1124 Err(Error::EndOfFile) => {
1125 debug!("Extending file");
1126 match data.open_volumes[volume_idx].volume_type {
1127 VolumeType::Fat(ref mut fat) => {
1128 if fat
1129 .alloc_cluster(
1130 &mut data.block_cache,
1131 Some(current_cluster.1),
1132 false,
1133 )
1134 .is_err()
1135 {
1136 return Err(Error::DiskFull);
1137 }
1138 debug!("Allocated new FAT cluster, finding offsets...");
1139 let new_offset = data
1140 .find_data_on_disk(
1141 volume_idx,
1142 &mut current_cluster,
1143 data.open_files[file_idx].entry.cluster,
1144 data.open_files[file_idx].current_offset,
1145 )
1146 .map_err(|_| Error::AllocationError)?;
1147 debug!("New offset {:?}", new_offset);
1148 new_offset
1149 }
1150 }
1151 }
1152 Err(e) => return Err(e),
1153 };
1154 let to_copy = core::cmp::min(block_avail, bytes_to_write - written);
1155 let block = if (block_offset == 0) && (to_copy == block_avail) {
1156 data.block_cache.blank_mut(block_idx)
1159 } else {
1160 debug!("Reading for partial block write");
1161 data.block_cache
1162 .read_mut(block_idx)
1163 .map_err(Error::DeviceError)?
1164 };
1165 block[block_offset..block_offset + to_copy]
1166 .copy_from_slice(&buffer[written..written + to_copy]);
1167 debug!("Writing block {:?}", block_idx);
1168 data.block_cache.write_back()?;
1169 written += to_copy;
1170 data.open_files[file_idx].current_cluster = current_cluster;
1171
1172 let to_copy = to_copy as u32;
1173 let new_offset = data.open_files[file_idx].current_offset + to_copy;
1174 if new_offset > data.open_files[file_idx].entry.size {
1175 data.open_files[file_idx].update_length(new_offset);
1177 }
1178 data.open_files[file_idx]
1179 .seek_from_start(new_offset)
1180 .unwrap();
1181 }
1183 data.open_files[file_idx].entry.attributes.set_archive(true);
1184 data.open_files[file_idx].entry.mtime = self.time_source.get_timestamp();
1185 Ok(())
1186 }
1187
1188 pub fn close_file(&self, file: RawFile) -> Result<(), Error<D::Error>> {
1197 let flush_result = self.flush_file(file);
1198 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
1199 let file_idx = data.get_file_by_id(file)?;
1200 data.open_files.swap_remove(file_idx);
1201 flush_result
1202 }
1203
1204 pub fn flush_file(&self, file: RawFile) -> Result<(), Error<D::Error>> {
1206 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
1207 let data = data.deref_mut();
1208
1209 let file_id = data.get_file_by_id(file)?;
1210
1211 if data.open_files[file_id].dirty {
1212 let volume_idx = data.get_volume_by_id(data.open_files[file_id].raw_volume)?;
1213 match &mut data.open_volumes[volume_idx].volume_type {
1214 VolumeType::Fat(fat) => {
1215 debug!("Updating FAT info sector");
1216 fat.update_info_sector(&mut data.block_cache)?;
1217 debug!("Updating dir entry {:?}", data.open_files[file_id].entry);
1218 if data.open_files[file_id].entry.size != 0 {
1219 assert!(data.open_files[file_id].entry.cluster.0 != 0);
1221 }
1222 fat.write_entry_to_disk(
1223 &mut data.block_cache,
1224 &data.open_files[file_id].entry,
1225 )?;
1226 }
1227 };
1228 }
1229 Ok(())
1230 }
1231
1232 pub fn has_open_handles(&self) -> bool {
1234 let data = self.data.borrow();
1235 !(data.open_dirs.is_empty() && data.open_files.is_empty())
1236 }
1237
1238 pub fn free(self) -> (D, T) {
1240 let data = self.data.into_inner();
1241 (data.block_cache.free(), self.time_source)
1242 }
1243
1244 pub fn file_eof(&self, file: RawFile) -> Result<bool, Error<D::Error>> {
1250 let data = self.data.try_borrow().map_err(|_| Error::LockError)?;
1251 let file_idx = data.get_file_by_id(file)?;
1252 Ok(data.open_files[file_idx].eof())
1253 }
1254
1255 pub fn file_seek_from_start(&self, file: RawFile, offset: u32) -> Result<(), Error<D::Error>> {
1262 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
1263 let file_idx = data.get_file_by_id(file)?;
1264 data.open_files[file_idx]
1265 .seek_from_start(offset)
1266 .map_err(|_| Error::InvalidOffset)?;
1267 Ok(())
1268 }
1269
1270 pub fn file_seek_from_current(
1278 &self,
1279 file: RawFile,
1280 offset: i32,
1281 ) -> Result<(), Error<D::Error>> {
1282 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
1283 let file_idx = data.get_file_by_id(file)?;
1284 data.open_files[file_idx]
1285 .seek_from_current(offset)
1286 .map_err(|_| Error::InvalidOffset)?;
1287 Ok(())
1288 }
1289
1290 pub fn file_seek_from_end(&self, file: RawFile, offset: u32) -> Result<(), Error<D::Error>> {
1298 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
1299 let file_idx = data.get_file_by_id(file)?;
1300 data.open_files[file_idx]
1301 .seek_from_end(offset)
1302 .map_err(|_| Error::InvalidOffset)?;
1303 Ok(())
1304 }
1305
1306 pub fn file_length(&self, file: RawFile) -> Result<u32, Error<D::Error>> {
1311 let data = self.data.try_borrow().map_err(|_| Error::LockError)?;
1312 let file_idx = data.get_file_by_id(file)?;
1313 Ok(data.open_files[file_idx].length())
1314 }
1315
1316 pub fn file_offset(&self, file: RawFile) -> Result<u32, Error<D::Error>> {
1321 let data = self.data.try_borrow().map_err(|_| Error::LockError)?;
1322 let file_idx = data.get_file_by_id(file)?;
1323 Ok(data.open_files[file_idx].current_offset)
1324 }
1325
1326 pub fn make_dir_in_dir<N>(
1331 &self,
1332 directory: RawDirectory,
1333 name: N,
1334 ) -> Result<(), Error<D::Error>>
1335 where
1336 N: ToShortFileName,
1337 {
1338 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
1339 let data = data.deref_mut();
1340
1341 if data.open_dirs.is_full() {
1343 return Err(Error::TooManyOpenDirs);
1344 }
1345
1346 let parent_directory_idx = data.get_dir_by_id(directory)?;
1347 let parent_directory_info = &data.open_dirs[parent_directory_idx];
1348 let volume_id = data.open_dirs[parent_directory_idx].raw_volume;
1349 let volume_idx = data.get_volume_by_id(volume_id)?;
1350 let volume_info = &data.open_volumes[volume_idx];
1351 let sfn = name.to_short_filename().map_err(Error::FilenameError)?;
1352
1353 debug!("Creating directory '{}'", sfn);
1354 debug!(
1355 "Parent dir is in cluster {:?}",
1356 parent_directory_info.cluster
1357 );
1358
1359 let maybe_dir_entry = match &volume_info.volume_type {
1361 VolumeType::Fat(fat) => {
1362 fat.find_directory_entry(&mut data.block_cache, parent_directory_info, &sfn)
1363 }
1364 };
1365
1366 match maybe_dir_entry {
1367 Ok(entry) if entry.attributes.is_directory() => {
1368 return Err(Error::DirAlreadyExists);
1369 }
1370 Ok(_entry) => {
1371 return Err(Error::FileAlreadyExists);
1372 }
1373 Err(Error::NotFound) => {
1374 }
1376 Err(e) => {
1377 return Err(e);
1379 }
1380 };
1381
1382 let att = Attributes::create_from_fat(Attributes::DIRECTORY);
1383
1384 match &mut data.open_volumes[volume_idx].volume_type {
1386 VolumeType::Fat(fat) => {
1387 debug!("Making dir entry");
1388 fat.make_dir(
1389 &mut data.block_cache,
1390 &self.time_source,
1391 parent_directory_info.cluster,
1392 sfn,
1393 att,
1394 )?;
1395 }
1396 };
1397
1398 Ok(())
1399 }
1400}
1401
1402#[derive(Debug)]
1406
1407struct VolumeManagerData<
1408 D,
1409 const MAX_DIRS: usize = 4,
1410 const MAX_FILES: usize = 4,
1411 const MAX_VOLUMES: usize = 1,
1412> where
1413 D: BlockDevice,
1414{
1415 id_generator: HandleGenerator,
1416 block_cache: BlockCache<D>,
1417 open_volumes: Vec<VolumeInfo, MAX_VOLUMES>,
1418 open_dirs: Vec<DirectoryInfo, MAX_DIRS>,
1419 open_files: Vec<FileInfo, MAX_FILES>,
1420}
1421
1422impl<D, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
1423 VolumeManagerData<D, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
1424where
1425 D: BlockDevice,
1426 <D as BlockDevice>::Error: core::error::Error,
1427{
1428 fn file_is_open(&self, raw_volume: RawVolume, dir_entry: &DirEntry) -> bool {
1432 for f in self.open_files.iter() {
1433 if f.raw_volume == raw_volume
1434 && f.entry.entry_block == dir_entry.entry_block
1435 && f.entry.entry_offset == dir_entry.entry_offset
1436 {
1437 return true;
1438 }
1439 }
1440 false
1441 }
1442
1443 fn get_volume_by_id<E: core::error::Error>(
1444 &self,
1445 raw_volume: RawVolume,
1446 ) -> Result<usize, Error<E>> {
1447 for (idx, v) in self.open_volumes.iter().enumerate() {
1448 if v.raw_volume == raw_volume {
1449 return Ok(idx);
1450 }
1451 }
1452 Err(Error::BadHandle)
1453 }
1454
1455 fn get_dir_by_id<E: core::error::Error>(
1456 &self,
1457 raw_directory: RawDirectory,
1458 ) -> Result<usize, Error<E>> {
1459 for (idx, d) in self.open_dirs.iter().enumerate() {
1460 if d.raw_directory == raw_directory {
1461 return Ok(idx);
1462 }
1463 }
1464 Err(Error::BadHandle)
1465 }
1466
1467 fn get_file_by_id<E: core::error::Error>(&self, raw_file: RawFile) -> Result<usize, Error<E>> {
1468 for (idx, f) in self.open_files.iter().enumerate() {
1469 if f.raw_file == raw_file {
1470 return Ok(idx);
1471 }
1472 }
1473 Err(Error::BadHandle)
1474 }
1475
1476 fn find_data_on_disk(
1486 &mut self,
1487 volume_idx: usize,
1488 start: &mut (u32, ClusterId),
1489 file_start: ClusterId,
1490 desired_offset: u32,
1491 ) -> Result<(BlockIdx, usize, usize), Error<D::Error>>
1492 where
1493 D: BlockDevice,
1494 {
1495 let bytes_per_cluster = match &self.open_volumes[volume_idx].volume_type {
1496 VolumeType::Fat(fat) => fat.bytes_per_cluster(),
1497 };
1498 if desired_offset < start.0 {
1500 start.0 = 0;
1503 start.1 = file_start;
1504 }
1505 let offset_from_cluster = desired_offset - start.0;
1507 let num_clusters = offset_from_cluster / bytes_per_cluster;
1509 for _ in 0..num_clusters {
1510 start.1 = match &self.open_volumes[volume_idx].volume_type {
1511 VolumeType::Fat(fat) => fat.next_cluster(&mut self.block_cache, start.1)?,
1512 };
1513 start.0 += bytes_per_cluster;
1514 }
1515 let offset_from_cluster = desired_offset - start.0;
1517 assert!(offset_from_cluster < bytes_per_cluster);
1518 let num_blocks = BlockCount(offset_from_cluster / Block::LEN_U32);
1519 let block_idx = match &self.open_volumes[volume_idx].volume_type {
1520 VolumeType::Fat(fat) => fat.cluster_to_block(start.1),
1521 } + num_blocks;
1522 let block_offset = (desired_offset % Block::LEN_U32) as usize;
1523 let available = Block::LEN - block_offset;
1524 Ok((block_idx, block_offset, available))
1525 }
1526}
1527
1528fn solve_mode_variant(mode: Mode, dir_entry_is_some: bool) -> Mode {
1531 let mut mode = mode;
1532 if mode == Mode::ReadWriteCreateOrAppend {
1533 if dir_entry_is_some {
1534 mode = Mode::ReadWriteAppend;
1535 } else {
1536 mode = Mode::ReadWriteCreate;
1537 }
1538 } else if mode == Mode::ReadWriteCreateOrTruncate {
1539 if dir_entry_is_some {
1540 mode = Mode::ReadWriteTruncate;
1541 } else {
1542 mode = Mode::ReadWriteCreate;
1543 }
1544 }
1545 mode
1546}
1547
1548#[cfg(test)]
1555mod tests {
1556 use hex_literal::hex;
1557
1558 use super::*;
1559 use crate::Timestamp;
1560 use crate::filesystem::Handle;
1561
1562 struct DummyBlockDevice;
1563
1564 struct Clock;
1565
1566 #[derive(Debug, thiserror::Error)]
1567 enum Error {
1568 #[error("unknown error")]
1569 Unknown,
1570 }
1571
1572 impl TimeSource for Clock {
1573 fn get_timestamp(&self) -> Timestamp {
1574 Timestamp {
1576 year_since_1970: 0,
1577 zero_indexed_month: 0,
1578 zero_indexed_day: 0,
1579 hours: 0,
1580 minutes: 0,
1581 seconds: 0,
1582 }
1583 }
1584 }
1585
1586 impl BlockDevice for DummyBlockDevice {
1587 type Error = Error;
1588
1589 fn read(&self, blocks: &mut [Block], start_block_idx: BlockIdx) -> Result<(), Self::Error> {
1591 static BLOCKS: [Block; 3] = [
1593 Block {
1594 contents: [
1595 0xfa, 0xb8, 0x00, 0x10, 0x8e, 0xd0, 0xbc, 0x00, 0xb0, 0xb8, 0x00, 0x00,
1596 0x8e, 0xd8, 0x8e, 0xc0, 0xfb, 0xbe, 0x00, 0x7c, 0xbf, 0x00, 0x06, 0xb9, 0x00, 0x02, 0xf3, 0xa4,
1598 0xea, 0x21, 0x06, 0x00, 0x00, 0xbe, 0xbe, 0x07, 0x38, 0x04, 0x75, 0x0b, 0x83, 0xc6, 0x10, 0x81,
1600 0xfe, 0xfe, 0x07, 0x75, 0xf3, 0xeb, 0x16, 0xb4, 0x02, 0xb0, 0x01, 0xbb, 0x00, 0x7c, 0xb2, 0x80,
1602 0x8a, 0x74, 0x01, 0x8b, 0x4c, 0x02, 0xcd, 0x13, 0xea, 0x00, 0x7c, 0x00, 0x00, 0xeb, 0xfe, 0x00,
1604 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1606 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1608 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1610 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1612 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1614 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1616 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1618 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1620 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1622 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1624 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1626 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1628 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1630 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1632 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1634 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1636 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1638 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1640 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1642 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1644 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1646 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1648 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xca, 0xde, 0x06,
1650 0x00, 0x00, 0x00, 0x04, 0x01, 0x04, 0x0c, 0xfe, 0xc2, 0xff, 0x01, 0x00, 0x00, 0x00, 0x33, 0x22,
1652 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1654 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1656 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1658 0x00, 0x00, 0x55, 0xaa, ],
1660 },
1661 Block {
1662 contents: [
1663 0xeb, 0x58, 0x90, 0x6d, 0x6b, 0x66, 0x73, 0x2e, 0x66, 0x61, 0x74, 0x00,
1664 0x02, 0x08, 0x20, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x10, 0x00, 0x04, 0x00,
1666 0x00, 0x08, 0x00, 0x00, 0x00, 0x20, 0x76, 0x00, 0x80, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1668 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1670 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x29, 0x0b, 0xa8, 0x89, 0x27, 0x50, 0x69, 0x63, 0x74, 0x75,
1672 0x72, 0x65, 0x73, 0x20, 0x20, 0x20, 0x46, 0x41, 0x54, 0x33, 0x32, 0x20, 0x20, 0x20, 0x0e, 0x1f,
1674 0xbe, 0x77, 0x7c, 0xac, 0x22, 0xc0, 0x74, 0x0b, 0x56, 0xb4, 0x0e, 0xbb, 0x07, 0x00, 0xcd, 0x10,
1676 0x5e, 0xeb, 0xf0, 0x32, 0xe4, 0xcd, 0x16, 0xcd, 0x19, 0xeb, 0xfe, 0x54, 0x68, 0x69, 0x73, 0x20,
1678 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c,
1680 0x65, 0x20, 0x64, 0x69, 0x73, 0x6b, 0x2e, 0x20, 0x20, 0x50, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20,
1682 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c,
1684 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x70, 0x70, 0x79, 0x20, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x70, 0x72,
1686 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x74,
1688 0x72, 0x79, 0x20, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x0d, 0x0a, 0x00,
1690 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1692 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1694 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1696 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1698 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1700 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1702 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1704 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1706 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1708 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1710 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1712 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1714 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1716 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1718 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1720 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1722 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1724 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1726 0x00, 0x00, 0x55, 0xaa, ],
1728 },
1729 Block {
1730 contents: hex!(
1731 "52 52 61 41 00 00 00 00 00 00 00 00 00 00 00 00
1732 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1733 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1734 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1735 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1736 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1737 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1738 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1739 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1740 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1741 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1742 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1743 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1744 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1745 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1746 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1747 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1748 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1749 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1750 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1751 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1752 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1753 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1754 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1755 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1756 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1757 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1758 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1759 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1760 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1761 00 00 00 00 72 72 41 61 FF FF FF FF FF FF FF FF
1762 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA"
1763 ),
1764 },
1765 ];
1766 println!(
1767 "Reading block {} to {}",
1768 start_block_idx.0,
1769 start_block_idx.0 as usize + blocks.len()
1770 );
1771 for (idx, block) in blocks.iter_mut().enumerate() {
1772 let block_idx = start_block_idx.0 as usize + idx;
1773 if block_idx < BLOCKS.len() {
1774 *block = BLOCKS[block_idx].clone();
1775 } else {
1776 return Err(Error::Unknown);
1777 }
1778 }
1779 Ok(())
1780 }
1781
1782 fn write(&self, _blocks: &[Block], _start_block_idx: BlockIdx) -> Result<(), Self::Error> {
1784 unimplemented!();
1785 }
1786
1787 fn num_blocks(&self) -> Result<BlockCount, Self::Error> {
1789 Ok(BlockCount(2))
1790 }
1791 }
1792
1793 #[test]
1794 fn partition0() {
1795 let c: VolumeManager<DummyBlockDevice, Clock, 2, 2> =
1796 VolumeManager::new_with_limits(DummyBlockDevice, Clock, 0xAA00_0000);
1797
1798 let v = c.open_raw_volume(VolumeIdx(0)).unwrap();
1799 let expected_id = RawVolume(Handle(0xAA00_0000));
1800 assert_eq!(v, expected_id);
1801 assert_eq!(
1802 &c.data.borrow().open_volumes[0],
1803 &VolumeInfo {
1804 raw_volume: expected_id,
1805 idx: VolumeIdx(0),
1806 volume_type: VolumeType::Fat(crate::FatVolume {
1807 lba_start: BlockIdx(1),
1808 num_blocks: BlockCount(0x0011_2233),
1809 blocks_per_cluster: 8,
1810 first_data_block: BlockCount(15136),
1811 fat_start: BlockCount(32),
1812 second_fat_start: Some(BlockCount(32 + 0x0000_1D80)),
1813 name: fat::VolumeName::create_from_str("Pictures").unwrap(),
1814 free_clusters_count: None,
1815 next_free_cluster: None,
1816 cluster_count: 965_788,
1817 fat_specific_info: fat::FatSpecificInfo::Fat32(fat::Fat32Info {
1818 first_root_dir_cluster: ClusterId(2),
1819 info_location: BlockIdx(1) + BlockCount(1),
1820 })
1821 })
1822 }
1823 );
1824 }
1825}
1826
1827