1use core::cell::RefCell;
6use core::convert::TryFrom;
7use core::ops::DerefMut;
8
9use byteorder::{ByteOrder, LittleEndian};
10use heapless::Vec;
11
12use crate::{
13 debug, fat,
14 filesystem::{
15 Attributes, ClusterId, DirEntry, DirectoryInfo, FileInfo, HandleGenerator, LfnBuffer, Mode,
16 RawDirectory, RawFile, TimeSource, ToShortFileName, MAX_FILE_SIZE,
17 },
18 trace, Block, BlockCache, BlockCount, BlockDevice, BlockIdx, Error, RawVolume, ShortFileName,
19 Volume, VolumeIdx, VolumeInfo, VolumeType, PARTITION_ID_FAT16, PARTITION_ID_FAT16_LBA,
20 PARTITION_ID_FAT16_SMALL, PARTITION_ID_FAT32_CHS_LBA, PARTITION_ID_FAT32_LBA,
21};
22
23#[derive(Debug)]
29pub struct VolumeManager<
30 D,
31 T,
32 const MAX_DIRS: usize = 4,
33 const MAX_FILES: usize = 4,
34 const MAX_VOLUMES: usize = 1,
35> where
36 D: BlockDevice,
37 T: TimeSource,
38 <D as BlockDevice>::Error: core::fmt::Debug,
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 <D as BlockDevice>::Error: core::fmt::Debug,
49{
50 pub fn new(block_device: D, time_source: T) -> VolumeManager<D, T, 4, 4, 1> {
58 Self::new_with_limits(block_device, time_source, 5000)
61 }
62}
63
64impl<D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
65 VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
66where
67 D: BlockDevice,
68 T: TimeSource,
69 <D as BlockDevice>::Error: core::fmt::Debug,
70{
71 pub fn new_with_limits(
79 block_device: D,
80 time_source: T,
81 id_offset: u32,
82 ) -> VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> {
83 debug!("Creating new embedded-sdmmc::VolumeManager");
84 VolumeManager {
85 time_source,
86 data: RefCell::new(VolumeManagerData {
87 block_cache: BlockCache::new(block_device),
88 id_generator: HandleGenerator::new(id_offset),
89 open_volumes: Vec::new(),
90 open_dirs: Vec::new(),
91 open_files: Vec::new(),
92 }),
93 }
94 }
95
96 pub fn device<F>(&self, f: F) -> T
98 where
99 F: FnOnce(&mut D) -> T,
100 {
101 let mut data = self.data.borrow_mut();
102 let result = f(data.block_cache.block_device());
103 result
104 }
105
106 pub fn open_volume(
111 &self,
112 volume_idx: VolumeIdx,
113 ) -> Result<Volume<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>, Error<D::Error>> {
114 let v = self.open_raw_volume(volume_idx)?;
115 Ok(v.to_volume(self))
116 }
117
118 pub fn open_raw_volume(&self, volume_idx: VolumeIdx) -> Result<RawVolume, Error<D::Error>> {
126 const PARTITION1_START: usize = 446;
127 const PARTITION2_START: usize = PARTITION1_START + PARTITION_INFO_LENGTH;
128 const PARTITION3_START: usize = PARTITION2_START + PARTITION_INFO_LENGTH;
129 const PARTITION4_START: usize = PARTITION3_START + PARTITION_INFO_LENGTH;
130 const FOOTER_START: usize = 510;
131 const FOOTER_VALUE: u16 = 0xAA55;
132 const PARTITION_INFO_LENGTH: usize = 16;
133 const PARTITION_INFO_STATUS_INDEX: usize = 0;
134 const PARTITION_INFO_TYPE_INDEX: usize = 4;
135 const PARTITION_INFO_LBA_START_INDEX: usize = 8;
136 const PARTITION_INFO_NUM_BLOCKS_INDEX: usize = 12;
137
138 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
139
140 if data.open_volumes.is_full() {
141 return Err(Error::TooManyOpenVolumes);
142 }
143
144 for v in data.open_volumes.iter() {
145 if v.idx == volume_idx {
146 return Err(Error::VolumeAlreadyOpen);
147 }
148 }
149
150 let (part_type, lba_start, num_blocks) = {
151 trace!("Reading partition table");
152 let block = data
153 .block_cache
154 .read(BlockIdx(0))
155 .map_err(Error::DeviceError)?;
156 if LittleEndian::read_u16(&block[FOOTER_START..FOOTER_START + 2]) != FOOTER_VALUE {
159 return Err(Error::FormatError("Invalid MBR signature"));
160 }
161 let partition = match volume_idx {
162 VolumeIdx(0) => {
163 &block[PARTITION1_START..(PARTITION1_START + PARTITION_INFO_LENGTH)]
164 }
165 VolumeIdx(1) => {
166 &block[PARTITION2_START..(PARTITION2_START + PARTITION_INFO_LENGTH)]
167 }
168 VolumeIdx(2) => {
169 &block[PARTITION3_START..(PARTITION3_START + PARTITION_INFO_LENGTH)]
170 }
171 VolumeIdx(3) => {
172 &block[PARTITION4_START..(PARTITION4_START + PARTITION_INFO_LENGTH)]
173 }
174 _ => {
175 return Err(Error::NoSuchVolume);
176 }
177 };
178 if (partition[PARTITION_INFO_STATUS_INDEX] & 0x7F) != 0x00 {
180 return Err(Error::FormatError("Invalid partition status"));
181 }
182 let lba_start = LittleEndian::read_u32(
183 &partition[PARTITION_INFO_LBA_START_INDEX..(PARTITION_INFO_LBA_START_INDEX + 4)],
184 );
185 let num_blocks = LittleEndian::read_u32(
186 &partition[PARTITION_INFO_NUM_BLOCKS_INDEX..(PARTITION_INFO_NUM_BLOCKS_INDEX + 4)],
187 );
188 (
189 partition[PARTITION_INFO_TYPE_INDEX],
190 BlockIdx(lba_start),
191 BlockCount(num_blocks),
192 )
193 };
194 match part_type {
195 PARTITION_ID_FAT32_CHS_LBA
196 | PARTITION_ID_FAT32_LBA
197 | PARTITION_ID_FAT16_LBA
198 | PARTITION_ID_FAT16
199 | PARTITION_ID_FAT16_SMALL => {
200 let volume = fat::parse_volume(&mut data.block_cache, lba_start, num_blocks)?;
201 let id = RawVolume(data.id_generator.generate());
202 let info = VolumeInfo {
203 raw_volume: id,
204 idx: volume_idx,
205 volume_type: volume,
206 };
207 data.open_volumes.push(info).unwrap();
209 Ok(id)
210 }
211 _ => Err(Error::FormatError("Partition type not supported")),
212 }
213 }
214
215 pub fn open_root_dir(&self, volume: RawVolume) -> Result<RawDirectory, Error<D::Error>> {
220 debug!("Opening root on {:?}", volume);
221
222 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
224
225 let directory_id = RawDirectory(data.id_generator.generate());
226 let dir_info = DirectoryInfo {
227 raw_volume: volume,
228 cluster: ClusterId::ROOT_DIR,
229 raw_directory: directory_id,
230 };
231
232 data.open_dirs
233 .push(dir_info)
234 .map_err(|_| Error::TooManyOpenDirs)?;
235
236 debug!("Opened root on {:?}, got {:?}", volume, directory_id);
237
238 Ok(directory_id)
239 }
240
241 pub fn open_dir<N>(
247 &self,
248 parent_dir: RawDirectory,
249 name: N,
250 ) -> Result<RawDirectory, Error<D::Error>>
251 where
252 N: ToShortFileName,
253 {
254 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
255 let data = data.deref_mut();
256
257 if data.open_dirs.is_full() {
258 return Err(Error::TooManyOpenDirs);
259 }
260
261 let parent_dir_idx = data.get_dir_by_id(parent_dir)?;
263 let volume_idx = data.get_volume_by_id(data.open_dirs[parent_dir_idx].raw_volume)?;
264 let short_file_name = name.to_short_filename().map_err(Error::FilenameError)?;
265
266 if short_file_name == ShortFileName::this_dir() {
270 let directory_id = RawDirectory(data.id_generator.generate());
271 let dir_info = DirectoryInfo {
272 raw_directory: directory_id,
273 raw_volume: data.open_volumes[volume_idx].raw_volume,
274 cluster: data.open_dirs[parent_dir_idx].cluster,
275 };
276
277 data.open_dirs
278 .push(dir_info)
279 .map_err(|_| Error::TooManyOpenDirs)?;
280
281 return Ok(directory_id);
282 }
283
284 let dir_entry = match &data.open_volumes[volume_idx].volume_type {
287 VolumeType::Fat(fat) => fat.find_directory_entry(
288 &mut data.block_cache,
289 &data.open_dirs[parent_dir_idx],
290 &short_file_name,
291 )?,
292 };
293
294 debug!("Found dir entry: {:?}", dir_entry);
295
296 if !dir_entry.attributes.is_directory() {
297 return Err(Error::OpenedFileAsDir);
298 }
299
300 let directory_id = RawDirectory(data.id_generator.generate());
305 let dir_info = DirectoryInfo {
306 raw_directory: directory_id,
307 raw_volume: data.open_volumes[volume_idx].raw_volume,
308 cluster: dir_entry.cluster,
309 };
310
311 data.open_dirs
312 .push(dir_info)
313 .map_err(|_| Error::TooManyOpenDirs)?;
314
315 Ok(directory_id)
316 }
317
318 pub fn close_dir(&self, directory: RawDirectory) -> Result<(), Error<D::Error>> {
321 debug!("Closing {:?}", directory);
322 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
323
324 for (idx, info) in data.open_dirs.iter().enumerate() {
325 if directory == info.raw_directory {
326 data.open_dirs.swap_remove(idx);
327 return Ok(());
328 }
329 }
330 Err(Error::BadHandle)
331 }
332
333 pub fn close_volume(&self, volume: RawVolume) -> Result<(), Error<D::Error>> {
337 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
338 let data = data.deref_mut();
339
340 for f in data.open_files.iter() {
341 if f.raw_volume == volume {
342 return Err(Error::VolumeStillInUse);
343 }
344 }
345
346 for d in data.open_dirs.iter() {
347 if d.raw_volume == volume {
348 return Err(Error::VolumeStillInUse);
349 }
350 }
351
352 let volume_idx = data.get_volume_by_id(volume)?;
353
354 match &mut data.open_volumes[volume_idx].volume_type {
355 VolumeType::Fat(fat) => {
356 fat.update_info_sector(&mut data.block_cache)?;
357 }
358 }
359
360 data.open_volumes.swap_remove(volume_idx);
361
362 Ok(())
363 }
364
365 pub fn find_directory_entry<N>(
367 &self,
368 directory: RawDirectory,
369 name: N,
370 ) -> Result<DirEntry, Error<D::Error>>
371 where
372 N: ToShortFileName,
373 {
374 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
375 let data = data.deref_mut();
376
377 let directory_idx = data.get_dir_by_id(directory)?;
378 let volume_idx = data.get_volume_by_id(data.open_dirs[directory_idx].raw_volume)?;
379 match &data.open_volumes[volume_idx].volume_type {
380 VolumeType::Fat(fat) => {
381 let sfn = name.to_short_filename().map_err(Error::FilenameError)?;
382 fat.find_directory_entry(
383 &mut data.block_cache,
384 &data.open_dirs[directory_idx],
385 &sfn,
386 )
387 }
388 }
389 }
390
391 pub fn iterate_dir<F>(
403 &self,
404 directory: RawDirectory,
405 mut func: F,
406 ) -> Result<(), Error<D::Error>>
407 where
408 F: FnMut(&DirEntry),
409 {
410 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
411 let data = data.deref_mut();
412
413 let directory_idx = data.get_dir_by_id(directory)?;
414 let volume_idx = data.get_volume_by_id(data.open_dirs[directory_idx].raw_volume)?;
415 match &data.open_volumes[volume_idx].volume_type {
416 VolumeType::Fat(fat) => {
417 fat.iterate_dir(
418 &mut data.block_cache,
419 &data.open_dirs[directory_idx],
420 |de| {
421 if !de.attributes.is_lfn() {
423 func(de);
424 }
425 },
426 )
427 }
428 }
429 }
430
431 pub fn iterate_dir_lfn<F>(
447 &self,
448 directory: RawDirectory,
449 lfn_buffer: &mut LfnBuffer<'_>,
450 func: F,
451 ) -> Result<(), Error<D::Error>>
452 where
453 F: FnMut(&DirEntry, Option<&str>),
454 {
455 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
456 let data = data.deref_mut();
457
458 let directory_idx = data.get_dir_by_id(directory)?;
459 let volume_idx = data.get_volume_by_id(data.open_dirs[directory_idx].raw_volume)?;
460
461 match &data.open_volumes[volume_idx].volume_type {
462 VolumeType::Fat(fat) => {
463 fat.iterate_dir_lfn(
465 &mut data.block_cache,
466 lfn_buffer,
467 &data.open_dirs[directory_idx],
468 func,
469 )
470 }
471 }
472 }
473
474 pub fn open_file_in_dir<N>(
476 &self,
477 directory: RawDirectory,
478 name: N,
479 mode: Mode,
480 ) -> Result<RawFile, Error<D::Error>>
481 where
482 N: ToShortFileName,
483 {
484 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
485 let data = data.deref_mut();
486
487 if data.open_files.is_full() {
489 return Err(Error::TooManyOpenFiles);
490 }
491
492 let directory_idx = data.get_dir_by_id(directory)?;
493 let volume_id = data.open_dirs[directory_idx].raw_volume;
494 let volume_idx = data.get_volume_by_id(volume_id)?;
495 let volume_info = &data.open_volumes[volume_idx];
496 let sfn = name.to_short_filename().map_err(Error::FilenameError)?;
497
498 let dir_entry = match &volume_info.volume_type {
499 VolumeType::Fat(fat) => fat.find_directory_entry(
500 &mut data.block_cache,
501 &data.open_dirs[directory_idx],
502 &sfn,
503 ),
504 };
505
506 let dir_entry = match dir_entry {
507 Ok(entry) => {
508 Some(entry)
510 }
511 Err(_)
512 if (mode == Mode::ReadWriteCreate)
513 | (mode == Mode::ReadWriteCreateOrTruncate)
514 | (mode == Mode::ReadWriteCreateOrAppend) =>
515 {
516 None
519 }
520 _ => {
521 return Err(Error::NotFound);
523 }
524 };
525
526 if let Some(dir_entry) = &dir_entry {
528 if data.file_is_open(volume_info.raw_volume, dir_entry) {
529 return Err(Error::FileAlreadyOpen);
530 }
531 }
532
533 let mode = solve_mode_variant(mode, dir_entry.is_some());
534
535 match mode {
536 Mode::ReadWriteCreate => {
537 if dir_entry.is_some() {
538 return Err(Error::FileAlreadyExists);
539 }
540 let cluster = data.open_dirs[directory_idx].cluster;
541 let att = Attributes::create_from_fat(0);
542 let volume_idx = data.get_volume_by_id(volume_id)?;
543 let entry = match &mut data.open_volumes[volume_idx].volume_type {
544 VolumeType::Fat(fat) => fat.write_new_directory_entry(
545 &mut data.block_cache,
546 &self.time_source,
547 cluster,
548 sfn,
549 att,
550 )?,
551 };
552
553 let file_id = RawFile(data.id_generator.generate());
554
555 let file = FileInfo {
556 raw_file: file_id,
557 raw_volume: volume_id,
558 current_cluster: (0, entry.cluster),
559 current_offset: 0,
560 mode,
561 entry,
562 dirty: false,
563 };
564
565 unsafe {
567 data.open_files.push_unchecked(file);
568 }
569
570 Ok(file_id)
571 }
572 _ => {
573 let dir_entry = dir_entry.unwrap();
575
576 if dir_entry.attributes.is_read_only() && mode != Mode::ReadOnly {
577 return Err(Error::ReadOnly);
578 }
579
580 if dir_entry.attributes.is_directory() {
581 return Err(Error::OpenedDirAsFile);
582 }
583
584 if data.file_is_open(volume_id, &dir_entry) {
586 return Err(Error::FileAlreadyOpen);
587 }
588
589 let mode = solve_mode_variant(mode, true);
590 let raw_file = RawFile(data.id_generator.generate());
591
592 let file = match mode {
593 Mode::ReadOnly => FileInfo {
594 raw_file,
595 raw_volume: volume_id,
596 current_cluster: (0, dir_entry.cluster),
597 current_offset: 0,
598 mode,
599 entry: dir_entry,
600 dirty: false,
601 },
602 Mode::ReadWriteAppend => {
603 let mut file = FileInfo {
604 raw_file,
605 raw_volume: volume_id,
606 current_cluster: (0, dir_entry.cluster),
607 current_offset: 0,
608 mode,
609 entry: dir_entry,
610 dirty: false,
611 };
612 file.seek_from_end(0).ok();
614 file
615 }
616 Mode::ReadWriteTruncate => {
617 let mut file = FileInfo {
618 raw_file,
619 raw_volume: volume_id,
620 current_cluster: (0, dir_entry.cluster),
621 current_offset: 0,
622 mode,
623 entry: dir_entry,
624 dirty: false,
625 };
626 match &mut data.open_volumes[volume_idx].volume_type {
627 VolumeType::Fat(fat) => fat.truncate_cluster_chain(
628 &mut data.block_cache,
629 file.entry.cluster,
630 )?,
631 };
632 file.update_length(0);
633 match &data.open_volumes[volume_idx].volume_type {
634 VolumeType::Fat(fat) => {
635 file.entry.mtime = self.time_source.get_timestamp();
636 fat.write_entry_to_disk(&mut data.block_cache, &file.entry)?;
637 }
638 };
639
640 file
641 }
642 _ => return Err(Error::Unsupported),
643 };
644
645 unsafe {
647 data.open_files.push_unchecked(file);
648 }
649
650 Ok(raw_file)
651 }
652 }
653 }
654
655 pub fn delete_file_in_dir<N>(
657 &self,
658 directory: RawDirectory,
659 name: N,
660 ) -> Result<(), Error<D::Error>>
661 where
662 N: ToShortFileName,
663 {
664 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
665 let data = data.deref_mut();
666
667 let dir_idx = data.get_dir_by_id(directory)?;
668 let dir_info = &data.open_dirs[dir_idx];
669 let volume_idx = data.get_volume_by_id(dir_info.raw_volume)?;
670 let sfn = name.to_short_filename().map_err(Error::FilenameError)?;
671
672 let dir_entry = match &data.open_volumes[volume_idx].volume_type {
673 VolumeType::Fat(fat) => fat.find_directory_entry(&mut data.block_cache, dir_info, &sfn),
674 }?;
675
676 if dir_entry.attributes.is_directory() {
677 return Err(Error::DeleteDirAsFile);
678 }
679
680 if data.file_is_open(dir_info.raw_volume, &dir_entry) {
681 return Err(Error::FileAlreadyOpen);
682 }
683
684 let volume_idx = data.get_volume_by_id(dir_info.raw_volume)?;
685 match &data.open_volumes[volume_idx].volume_type {
686 VolumeType::Fat(fat) => {
687 fat.delete_directory_entry(&mut data.block_cache, dir_info, &sfn)?
688 }
689 }
690
691 Ok(())
692 }
693
694 pub fn get_root_volume_label(
699 &self,
700 raw_volume: RawVolume,
701 ) -> Result<Option<crate::VolumeName>, Error<D::Error>> {
702 debug!("Reading volume label for {:?}", raw_volume);
703 let data = self.data.try_borrow().map_err(|_| Error::LockError)?;
705 let volume_idx = data.get_volume_by_id(raw_volume)?;
706 match &data.open_volumes[volume_idx].volume_type {
707 VolumeType::Fat(fat) => {
708 if !fat.name.name().is_empty() {
709 debug!(
710 "Got volume label {:?} for {:?} from BPB",
711 fat.name, raw_volume
712 );
713 return Ok(Some(fat.name.clone()));
714 }
715 }
716 }
717 drop(data);
718
719 let root_dir = self.open_root_dir(raw_volume)?.to_directory(self);
721 let mut maybe_volume_name = None;
722 root_dir.iterate_dir(|de| {
723 if maybe_volume_name.is_none()
724 && de.attributes == Attributes::create_from_fat(Attributes::VOLUME)
725 {
726 maybe_volume_name = Some(unsafe { de.name.clone().to_volume_label() })
727 }
728 })?;
729
730 debug!(
731 "Got volume label {:?} for {:?} from root",
732 maybe_volume_name, raw_volume
733 );
734
735 Ok(maybe_volume_name)
736 }
737
738 pub fn read(&self, file: RawFile, buffer: &mut [u8]) -> Result<usize, Error<D::Error>> {
740 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
741 let data = data.deref_mut();
742
743 let file_idx = data.get_file_by_id(file)?;
744 let volume_idx = data.get_volume_by_id(data.open_files[file_idx].raw_volume)?;
745
746 let mut space = buffer.len();
750 let mut read = 0;
751 while space > 0 && !data.open_files[file_idx].eof() {
752 let mut current_cluster = data.open_files[file_idx].current_cluster;
753 let (block_idx, block_offset, block_avail) = data.find_data_on_disk(
754 volume_idx,
755 &mut current_cluster,
756 data.open_files[file_idx].entry.cluster,
757 data.open_files[file_idx].current_offset,
758 )?;
759 data.open_files[file_idx].current_cluster = current_cluster;
760 trace!("Reading file ID {:?}", file);
761 let block = data
762 .block_cache
763 .read(block_idx)
764 .map_err(Error::DeviceError)?;
765 let to_copy = block_avail
766 .min(space)
767 .min(data.open_files[file_idx].left() as usize);
768 assert!(to_copy != 0);
769 buffer[read..read + to_copy]
770 .copy_from_slice(&block[block_offset..block_offset + to_copy]);
771 read += to_copy;
772 space -= to_copy;
773 data.open_files[file_idx]
774 .seek_from_current(to_copy as i32)
775 .unwrap();
776 }
777 Ok(read)
778 }
779
780 pub fn write(&self, file: RawFile, buffer: &[u8]) -> Result<(), Error<D::Error>> {
782 #[cfg(feature = "defmt-log")]
783 debug!("write(file={:?}, buffer={:x}", file, buffer);
784
785 #[cfg(feature = "log")]
786 debug!("write(file={:?}, buffer={:x?}", file, buffer);
787
788 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
789 let data = data.deref_mut();
790
791 let file_idx = data.get_file_by_id(file)?;
794 let volume_idx = data.get_volume_by_id(data.open_files[file_idx].raw_volume)?;
795
796 if data.open_files[file_idx].mode == Mode::ReadOnly {
797 return Err(Error::ReadOnly);
798 }
799
800 data.open_files[file_idx].dirty = true;
801
802 if data.open_files[file_idx].entry.cluster.0 < fat::RESERVED_ENTRIES {
803 data.open_files[file_idx].entry.cluster =
805 match data.open_volumes[volume_idx].volume_type {
806 VolumeType::Fat(ref mut fat) => {
807 fat.alloc_cluster(&mut data.block_cache, None, false)?
808 }
809 };
810 debug!(
811 "Alloc first cluster {:?}",
812 data.open_files[file_idx].entry.cluster
813 );
814 }
815
816 let volume_idx = data.get_volume_by_id(data.open_files[file_idx].raw_volume)?;
818
819 if (data.open_files[file_idx].current_cluster.1) < data.open_files[file_idx].entry.cluster {
820 debug!("Rewinding to start");
821 data.open_files[file_idx].current_cluster =
822 (0, data.open_files[file_idx].entry.cluster);
823 }
824 let bytes_until_max =
825 usize::try_from(MAX_FILE_SIZE - data.open_files[file_idx].current_offset)
826 .map_err(|_| Error::ConversionError)?;
827 let bytes_to_write = core::cmp::min(buffer.len(), bytes_until_max);
828 let mut written = 0;
829
830 while written < bytes_to_write {
831 let mut current_cluster = data.open_files[file_idx].current_cluster;
832 debug!(
833 "Have written bytes {}/{}, finding cluster {:?}",
834 written, bytes_to_write, current_cluster
835 );
836 let current_offset = data.open_files[file_idx].current_offset;
837 let (block_idx, block_offset, block_avail) = match data.find_data_on_disk(
838 volume_idx,
839 &mut current_cluster,
840 data.open_files[file_idx].entry.cluster,
841 current_offset,
842 ) {
843 Ok(vars) => {
844 debug!(
845 "Found block_idx={:?}, block_offset={:?}, block_avail={}",
846 vars.0, vars.1, vars.2
847 );
848 vars
849 }
850 Err(Error::EndOfFile) => {
851 debug!("Extending file");
852 match data.open_volumes[volume_idx].volume_type {
853 VolumeType::Fat(ref mut fat) => {
854 if fat
855 .alloc_cluster(
856 &mut data.block_cache,
857 Some(current_cluster.1),
858 false,
859 )
860 .is_err()
861 {
862 return Err(Error::DiskFull);
863 }
864 debug!("Allocated new FAT cluster, finding offsets...");
865 let new_offset = data
866 .find_data_on_disk(
867 volume_idx,
868 &mut current_cluster,
869 data.open_files[file_idx].entry.cluster,
870 data.open_files[file_idx].current_offset,
871 )
872 .map_err(|_| Error::AllocationError)?;
873 debug!("New offset {:?}", new_offset);
874 new_offset
875 }
876 }
877 }
878 Err(e) => return Err(e),
879 };
880 let to_copy = core::cmp::min(block_avail, bytes_to_write - written);
881 let block = if block_offset != 0 {
882 debug!("Reading for partial block write");
883 data.block_cache
884 .read_mut(block_idx)
885 .map_err(Error::DeviceError)?
886 } else {
887 data.block_cache.blank_mut(block_idx)
888 };
889 block[block_offset..block_offset + to_copy]
890 .copy_from_slice(&buffer[written..written + to_copy]);
891 debug!("Writing block {:?}", block_idx);
892 data.block_cache.write_back()?;
893 written += to_copy;
894 data.open_files[file_idx].current_cluster = current_cluster;
895
896 let to_copy = to_copy as u32;
897 let new_offset = data.open_files[file_idx].current_offset + to_copy;
898 if new_offset > data.open_files[file_idx].entry.size {
899 data.open_files[file_idx].update_length(new_offset);
901 }
902 data.open_files[file_idx]
903 .seek_from_start(new_offset)
904 .unwrap();
905 }
907 data.open_files[file_idx].entry.attributes.set_archive(true);
908 data.open_files[file_idx].entry.mtime = self.time_source.get_timestamp();
909 Ok(())
910 }
911
912 pub fn close_file(&self, file: RawFile) -> Result<(), Error<D::Error>> {
914 let flush_result = self.flush_file(file);
915 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
916 let file_idx = data.get_file_by_id(file)?;
917 data.open_files.swap_remove(file_idx);
918 flush_result
919 }
920
921 pub fn flush_file(&self, file: RawFile) -> Result<(), Error<D::Error>> {
923 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
924 let data = data.deref_mut();
925
926 let file_id = data.get_file_by_id(file)?;
927
928 if data.open_files[file_id].dirty {
929 let volume_idx = data.get_volume_by_id(data.open_files[file_id].raw_volume)?;
930 match &mut data.open_volumes[volume_idx].volume_type {
931 VolumeType::Fat(fat) => {
932 debug!("Updating FAT info sector");
933 fat.update_info_sector(&mut data.block_cache)?;
934 debug!("Updating dir entry {:?}", data.open_files[file_id].entry);
935 if data.open_files[file_id].entry.size != 0 {
936 assert!(data.open_files[file_id].entry.cluster.0 != 0);
938 }
939 fat.write_entry_to_disk(
940 &mut data.block_cache,
941 &data.open_files[file_id].entry,
942 )?;
943 }
944 };
945 }
946 Ok(())
947 }
948
949 pub fn has_open_handles(&self) -> bool {
951 let data = self.data.borrow();
952 !(data.open_dirs.is_empty() || data.open_files.is_empty())
953 }
954
955 pub fn free(self) -> (D, T) {
957 let data = self.data.into_inner();
958 (data.block_cache.free(), self.time_source)
959 }
960
961 pub fn file_eof(&self, file: RawFile) -> Result<bool, Error<D::Error>> {
963 let data = self.data.try_borrow().map_err(|_| Error::LockError)?;
964 let file_idx = data.get_file_by_id(file)?;
965 Ok(data.open_files[file_idx].eof())
966 }
967
968 pub fn file_seek_from_start(&self, file: RawFile, offset: u32) -> Result<(), Error<D::Error>> {
970 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
971 let file_idx = data.get_file_by_id(file)?;
972 data.open_files[file_idx]
973 .seek_from_start(offset)
974 .map_err(|_| Error::InvalidOffset)?;
975 Ok(())
976 }
977
978 pub fn file_seek_from_current(
980 &self,
981 file: RawFile,
982 offset: i32,
983 ) -> Result<(), Error<D::Error>> {
984 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
985 let file_idx = data.get_file_by_id(file)?;
986 data.open_files[file_idx]
987 .seek_from_current(offset)
988 .map_err(|_| Error::InvalidOffset)?;
989 Ok(())
990 }
991
992 pub fn file_seek_from_end(&self, file: RawFile, offset: u32) -> Result<(), Error<D::Error>> {
994 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
995 let file_idx = data.get_file_by_id(file)?;
996 data.open_files[file_idx]
997 .seek_from_end(offset)
998 .map_err(|_| Error::InvalidOffset)?;
999 Ok(())
1000 }
1001
1002 pub fn file_length(&self, file: RawFile) -> Result<u32, Error<D::Error>> {
1004 let data = self.data.try_borrow().map_err(|_| Error::LockError)?;
1005 let file_idx = data.get_file_by_id(file)?;
1006 Ok(data.open_files[file_idx].length())
1007 }
1008
1009 pub fn file_offset(&self, file: RawFile) -> Result<u32, Error<D::Error>> {
1011 let data = self.data.try_borrow().map_err(|_| Error::LockError)?;
1012 let file_idx = data.get_file_by_id(file)?;
1013 Ok(data.open_files[file_idx].current_offset)
1014 }
1015
1016 pub fn make_dir_in_dir<N>(
1018 &self,
1019 directory: RawDirectory,
1020 name: N,
1021 ) -> Result<(), Error<D::Error>>
1022 where
1023 N: ToShortFileName,
1024 {
1025 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
1026 let data = data.deref_mut();
1027
1028 if data.open_dirs.is_full() {
1030 return Err(Error::TooManyOpenDirs);
1031 }
1032
1033 let parent_directory_idx = data.get_dir_by_id(directory)?;
1034 let parent_directory_info = &data.open_dirs[parent_directory_idx];
1035 let volume_id = data.open_dirs[parent_directory_idx].raw_volume;
1036 let volume_idx = data.get_volume_by_id(volume_id)?;
1037 let volume_info = &data.open_volumes[volume_idx];
1038 let sfn = name.to_short_filename().map_err(Error::FilenameError)?;
1039
1040 debug!("Creating directory '{}'", sfn);
1041 debug!(
1042 "Parent dir is in cluster {:?}",
1043 parent_directory_info.cluster
1044 );
1045
1046 let maybe_dir_entry = match &volume_info.volume_type {
1048 VolumeType::Fat(fat) => {
1049 fat.find_directory_entry(&mut data.block_cache, parent_directory_info, &sfn)
1050 }
1051 };
1052
1053 match maybe_dir_entry {
1054 Ok(entry) if entry.attributes.is_directory() => {
1055 return Err(Error::DirAlreadyExists);
1056 }
1057 Ok(_entry) => {
1058 return Err(Error::FileAlreadyExists);
1059 }
1060 Err(Error::NotFound) => {
1061 }
1063 Err(e) => {
1064 return Err(e);
1066 }
1067 };
1068
1069 let att = Attributes::create_from_fat(Attributes::DIRECTORY);
1070
1071 match &mut data.open_volumes[volume_idx].volume_type {
1073 VolumeType::Fat(fat) => {
1074 debug!("Making dir entry");
1075 fat.make_dir(
1076 &mut data.block_cache,
1077 &self.time_source,
1078 parent_directory_info.cluster,
1079 sfn,
1080 att,
1081 )?;
1082 }
1083 };
1084
1085 Ok(())
1086 }
1087}
1088
1089#[derive(Debug)]
1093
1094struct VolumeManagerData<
1095 D,
1096 const MAX_DIRS: usize = 4,
1097 const MAX_FILES: usize = 4,
1098 const MAX_VOLUMES: usize = 1,
1099> where
1100 D: BlockDevice,
1101{
1102 id_generator: HandleGenerator,
1103 block_cache: BlockCache<D>,
1104 open_volumes: Vec<VolumeInfo, MAX_VOLUMES>,
1105 open_dirs: Vec<DirectoryInfo, MAX_DIRS>,
1106 open_files: Vec<FileInfo, MAX_FILES>,
1107}
1108
1109impl<D, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
1110 VolumeManagerData<D, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
1111where
1112 D: BlockDevice,
1113{
1114 fn file_is_open(&self, raw_volume: RawVolume, dir_entry: &DirEntry) -> bool {
1118 for f in self.open_files.iter() {
1119 if f.raw_volume == raw_volume
1120 && f.entry.entry_block == dir_entry.entry_block
1121 && f.entry.entry_offset == dir_entry.entry_offset
1122 {
1123 return true;
1124 }
1125 }
1126 false
1127 }
1128
1129 fn get_volume_by_id<E>(&self, raw_volume: RawVolume) -> Result<usize, Error<E>>
1130 where
1131 E: core::fmt::Debug,
1132 {
1133 for (idx, v) in self.open_volumes.iter().enumerate() {
1134 if v.raw_volume == raw_volume {
1135 return Ok(idx);
1136 }
1137 }
1138 Err(Error::BadHandle)
1139 }
1140
1141 fn get_dir_by_id<E>(&self, raw_directory: RawDirectory) -> Result<usize, Error<E>>
1142 where
1143 E: core::fmt::Debug,
1144 {
1145 for (idx, d) in self.open_dirs.iter().enumerate() {
1146 if d.raw_directory == raw_directory {
1147 return Ok(idx);
1148 }
1149 }
1150 Err(Error::BadHandle)
1151 }
1152
1153 fn get_file_by_id<E>(&self, raw_file: RawFile) -> Result<usize, Error<E>>
1154 where
1155 E: core::fmt::Debug,
1156 {
1157 for (idx, f) in self.open_files.iter().enumerate() {
1158 if f.raw_file == raw_file {
1159 return Ok(idx);
1160 }
1161 }
1162 Err(Error::BadHandle)
1163 }
1164
1165 fn find_data_on_disk(
1175 &mut self,
1176 volume_idx: usize,
1177 start: &mut (u32, ClusterId),
1178 file_start: ClusterId,
1179 desired_offset: u32,
1180 ) -> Result<(BlockIdx, usize, usize), Error<D::Error>>
1181 where
1182 D: BlockDevice,
1183 {
1184 let bytes_per_cluster = match &self.open_volumes[volume_idx].volume_type {
1185 VolumeType::Fat(fat) => fat.bytes_per_cluster(),
1186 };
1187 if desired_offset < start.0 {
1189 start.0 = 0;
1192 start.1 = file_start;
1193 }
1194 let offset_from_cluster = desired_offset - start.0;
1196 let num_clusters = offset_from_cluster / bytes_per_cluster;
1198 for _ in 0..num_clusters {
1199 start.1 = match &self.open_volumes[volume_idx].volume_type {
1200 VolumeType::Fat(fat) => fat.next_cluster(&mut self.block_cache, start.1)?,
1201 };
1202 start.0 += bytes_per_cluster;
1203 }
1204 let offset_from_cluster = desired_offset - start.0;
1206 assert!(offset_from_cluster < bytes_per_cluster);
1207 let num_blocks = BlockCount(offset_from_cluster / Block::LEN_U32);
1208 let block_idx = match &self.open_volumes[volume_idx].volume_type {
1209 VolumeType::Fat(fat) => fat.cluster_to_block(start.1),
1210 } + num_blocks;
1211 let block_offset = (desired_offset % Block::LEN_U32) as usize;
1212 let available = Block::LEN - block_offset;
1213 Ok((block_idx, block_offset, available))
1214 }
1215}
1216
1217fn solve_mode_variant(mode: Mode, dir_entry_is_some: bool) -> Mode {
1220 let mut mode = mode;
1221 if mode == Mode::ReadWriteCreateOrAppend {
1222 if dir_entry_is_some {
1223 mode = Mode::ReadWriteAppend;
1224 } else {
1225 mode = Mode::ReadWriteCreate;
1226 }
1227 } else if mode == Mode::ReadWriteCreateOrTruncate {
1228 if dir_entry_is_some {
1229 mode = Mode::ReadWriteTruncate;
1230 } else {
1231 mode = Mode::ReadWriteCreate;
1232 }
1233 }
1234 mode
1235}
1236
1237#[cfg(test)]
1244mod tests {
1245 use super::*;
1246 use crate::filesystem::Handle;
1247 use crate::Timestamp;
1248
1249 struct DummyBlockDevice;
1250
1251 struct Clock;
1252
1253 #[derive(Debug)]
1254 enum Error {
1255 Unknown,
1256 }
1257
1258 impl TimeSource for Clock {
1259 fn get_timestamp(&self) -> Timestamp {
1260 Timestamp {
1262 year_since_1970: 0,
1263 zero_indexed_month: 0,
1264 zero_indexed_day: 0,
1265 hours: 0,
1266 minutes: 0,
1267 seconds: 0,
1268 }
1269 }
1270 }
1271
1272 impl BlockDevice for DummyBlockDevice {
1273 type Error = Error;
1274
1275 fn read(&self, blocks: &mut [Block], start_block_idx: BlockIdx) -> Result<(), Self::Error> {
1277 static BLOCKS: [Block; 3] = [
1279 Block {
1280 contents: [
1281 0xfa, 0xb8, 0x00, 0x10, 0x8e, 0xd0, 0xbc, 0x00, 0xb0, 0xb8, 0x00, 0x00,
1282 0x8e, 0xd8, 0x8e, 0xc0, 0xfb, 0xbe, 0x00, 0x7c, 0xbf, 0x00, 0x06, 0xb9, 0x00, 0x02, 0xf3, 0xa4,
1284 0xea, 0x21, 0x06, 0x00, 0x00, 0xbe, 0xbe, 0x07, 0x38, 0x04, 0x75, 0x0b, 0x83, 0xc6, 0x10, 0x81,
1286 0xfe, 0xfe, 0x07, 0x75, 0xf3, 0xeb, 0x16, 0xb4, 0x02, 0xb0, 0x01, 0xbb, 0x00, 0x7c, 0xb2, 0x80,
1288 0x8a, 0x74, 0x01, 0x8b, 0x4c, 0x02, 0xcd, 0x13, 0xea, 0x00, 0x7c, 0x00, 0x00, 0xeb, 0xfe, 0x00,
1290 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1292 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1294 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1296 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1298 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1300 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1302 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1304 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1306 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1308 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1310 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1312 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1314 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1316 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1318 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1320 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1322 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1324 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1326 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1328 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1330 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1332 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1334 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xca, 0xde, 0x06,
1336 0x00, 0x00, 0x00, 0x04, 0x01, 0x04, 0x0c, 0xfe, 0xc2, 0xff, 0x01, 0x00, 0x00, 0x00, 0x33, 0x22,
1338 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1340 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1342 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1344 0x00, 0x00, 0x55, 0xaa, ],
1346 },
1347 Block {
1348 contents: [
1349 0xeb, 0x58, 0x90, 0x6d, 0x6b, 0x66, 0x73, 0x2e, 0x66, 0x61, 0x74, 0x00,
1350 0x02, 0x08, 0x20, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x10, 0x00, 0x04, 0x00,
1352 0x00, 0x08, 0x00, 0x00, 0x00, 0x20, 0x76, 0x00, 0x80, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1354 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1356 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x29, 0x0b, 0xa8, 0x89, 0x27, 0x50, 0x69, 0x63, 0x74, 0x75,
1358 0x72, 0x65, 0x73, 0x20, 0x20, 0x20, 0x46, 0x41, 0x54, 0x33, 0x32, 0x20, 0x20, 0x20, 0x0e, 0x1f,
1360 0xbe, 0x77, 0x7c, 0xac, 0x22, 0xc0, 0x74, 0x0b, 0x56, 0xb4, 0x0e, 0xbb, 0x07, 0x00, 0xcd, 0x10,
1362 0x5e, 0xeb, 0xf0, 0x32, 0xe4, 0xcd, 0x16, 0xcd, 0x19, 0xeb, 0xfe, 0x54, 0x68, 0x69, 0x73, 0x20,
1364 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c,
1366 0x65, 0x20, 0x64, 0x69, 0x73, 0x6b, 0x2e, 0x20, 0x20, 0x50, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20,
1368 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c,
1370 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x70, 0x70, 0x79, 0x20, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x70, 0x72,
1372 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x74,
1374 0x72, 0x79, 0x20, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x0d, 0x0a, 0x00,
1376 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1378 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1380 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1382 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1384 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1386 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1388 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1390 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1392 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1394 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1396 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1398 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1400 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1402 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1404 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1406 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1408 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1410 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1412 0x00, 0x00, 0x55, 0xaa, ],
1414 },
1415 Block {
1416 contents: hex!(
1417 "52 52 61 41 00 00 00 00 00 00 00 00 00 00 00 00
1418 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1419 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1420 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1421 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1422 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1423 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1424 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1425 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1426 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1427 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1428 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1429 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1430 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1431 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1432 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1433 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1434 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1435 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1436 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1437 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1438 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1439 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1440 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1441 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1442 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1443 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1444 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1445 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1446 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1447 00 00 00 00 72 72 41 61 FF FF FF FF FF FF FF FF
1448 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA"
1449 ),
1450 },
1451 ];
1452 println!(
1453 "Reading block {} to {}",
1454 start_block_idx.0,
1455 start_block_idx.0 as usize + blocks.len()
1456 );
1457 for (idx, block) in blocks.iter_mut().enumerate() {
1458 let block_idx = start_block_idx.0 as usize + idx;
1459 if block_idx < BLOCKS.len() {
1460 *block = BLOCKS[block_idx].clone();
1461 } else {
1462 return Err(Error::Unknown);
1463 }
1464 }
1465 Ok(())
1466 }
1467
1468 fn write(&self, _blocks: &[Block], _start_block_idx: BlockIdx) -> Result<(), Self::Error> {
1470 unimplemented!();
1471 }
1472
1473 fn num_blocks(&self) -> Result<BlockCount, Self::Error> {
1475 Ok(BlockCount(2))
1476 }
1477 }
1478
1479 #[test]
1480 fn partition0() {
1481 let c: VolumeManager<DummyBlockDevice, Clock, 2, 2> =
1482 VolumeManager::new_with_limits(DummyBlockDevice, Clock, 0xAA00_0000);
1483
1484 let v = c.open_raw_volume(VolumeIdx(0)).unwrap();
1485 let expected_id = RawVolume(Handle(0xAA00_0000));
1486 assert_eq!(v, expected_id);
1487 assert_eq!(
1488 &c.data.borrow().open_volumes[0],
1489 &VolumeInfo {
1490 raw_volume: expected_id,
1491 idx: VolumeIdx(0),
1492 volume_type: VolumeType::Fat(crate::FatVolume {
1493 lba_start: BlockIdx(1),
1494 num_blocks: BlockCount(0x0011_2233),
1495 blocks_per_cluster: 8,
1496 first_data_block: BlockCount(15136),
1497 fat_start: BlockCount(32),
1498 second_fat_start: Some(BlockCount(32 + 0x0000_1D80)),
1499 name: fat::VolumeName::create_from_str("Pictures").unwrap(),
1500 free_clusters_count: None,
1501 next_free_cluster: None,
1502 cluster_count: 965_788,
1503 fat_specific_info: fat::FatSpecificInfo::Fat32(fat::Fat32Info {
1504 first_root_dir_cluster: ClusterId(2),
1505 info_location: BlockIdx(1) + BlockCount(1),
1506 })
1507 })
1508 }
1509 );
1510 }
1511}
1512
1513