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) && (to_copy == block_avail) {
882 data.block_cache.blank_mut(block_idx)
885 } else {
886 debug!("Reading for partial block write");
887 data.block_cache
888 .read_mut(block_idx)
889 .map_err(Error::DeviceError)?
890 };
891 block[block_offset..block_offset + to_copy]
892 .copy_from_slice(&buffer[written..written + to_copy]);
893 debug!("Writing block {:?}", block_idx);
894 data.block_cache.write_back()?;
895 written += to_copy;
896 data.open_files[file_idx].current_cluster = current_cluster;
897
898 let to_copy = to_copy as u32;
899 let new_offset = data.open_files[file_idx].current_offset + to_copy;
900 if new_offset > data.open_files[file_idx].entry.size {
901 data.open_files[file_idx].update_length(new_offset);
903 }
904 data.open_files[file_idx]
905 .seek_from_start(new_offset)
906 .unwrap();
907 }
909 data.open_files[file_idx].entry.attributes.set_archive(true);
910 data.open_files[file_idx].entry.mtime = self.time_source.get_timestamp();
911 Ok(())
912 }
913
914 pub fn close_file(&self, file: RawFile) -> Result<(), Error<D::Error>> {
916 let flush_result = self.flush_file(file);
917 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
918 let file_idx = data.get_file_by_id(file)?;
919 data.open_files.swap_remove(file_idx);
920 flush_result
921 }
922
923 pub fn flush_file(&self, file: RawFile) -> Result<(), Error<D::Error>> {
925 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
926 let data = data.deref_mut();
927
928 let file_id = data.get_file_by_id(file)?;
929
930 if data.open_files[file_id].dirty {
931 let volume_idx = data.get_volume_by_id(data.open_files[file_id].raw_volume)?;
932 match &mut data.open_volumes[volume_idx].volume_type {
933 VolumeType::Fat(fat) => {
934 debug!("Updating FAT info sector");
935 fat.update_info_sector(&mut data.block_cache)?;
936 debug!("Updating dir entry {:?}", data.open_files[file_id].entry);
937 if data.open_files[file_id].entry.size != 0 {
938 assert!(data.open_files[file_id].entry.cluster.0 != 0);
940 }
941 fat.write_entry_to_disk(
942 &mut data.block_cache,
943 &data.open_files[file_id].entry,
944 )?;
945 }
946 };
947 }
948 Ok(())
949 }
950
951 pub fn has_open_handles(&self) -> bool {
953 let data = self.data.borrow();
954 !(data.open_dirs.is_empty() || data.open_files.is_empty())
955 }
956
957 pub fn free(self) -> (D, T) {
959 let data = self.data.into_inner();
960 (data.block_cache.free(), self.time_source)
961 }
962
963 pub fn file_eof(&self, file: RawFile) -> Result<bool, Error<D::Error>> {
965 let data = self.data.try_borrow().map_err(|_| Error::LockError)?;
966 let file_idx = data.get_file_by_id(file)?;
967 Ok(data.open_files[file_idx].eof())
968 }
969
970 pub fn file_seek_from_start(&self, file: RawFile, offset: u32) -> Result<(), Error<D::Error>> {
972 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
973 let file_idx = data.get_file_by_id(file)?;
974 data.open_files[file_idx]
975 .seek_from_start(offset)
976 .map_err(|_| Error::InvalidOffset)?;
977 Ok(())
978 }
979
980 pub fn file_seek_from_current(
982 &self,
983 file: RawFile,
984 offset: i32,
985 ) -> Result<(), Error<D::Error>> {
986 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
987 let file_idx = data.get_file_by_id(file)?;
988 data.open_files[file_idx]
989 .seek_from_current(offset)
990 .map_err(|_| Error::InvalidOffset)?;
991 Ok(())
992 }
993
994 pub fn file_seek_from_end(&self, file: RawFile, offset: u32) -> Result<(), Error<D::Error>> {
996 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
997 let file_idx = data.get_file_by_id(file)?;
998 data.open_files[file_idx]
999 .seek_from_end(offset)
1000 .map_err(|_| Error::InvalidOffset)?;
1001 Ok(())
1002 }
1003
1004 pub fn file_length(&self, file: RawFile) -> Result<u32, Error<D::Error>> {
1006 let data = self.data.try_borrow().map_err(|_| Error::LockError)?;
1007 let file_idx = data.get_file_by_id(file)?;
1008 Ok(data.open_files[file_idx].length())
1009 }
1010
1011 pub fn file_offset(&self, file: RawFile) -> Result<u32, Error<D::Error>> {
1013 let data = self.data.try_borrow().map_err(|_| Error::LockError)?;
1014 let file_idx = data.get_file_by_id(file)?;
1015 Ok(data.open_files[file_idx].current_offset)
1016 }
1017
1018 pub fn make_dir_in_dir<N>(
1020 &self,
1021 directory: RawDirectory,
1022 name: N,
1023 ) -> Result<(), Error<D::Error>>
1024 where
1025 N: ToShortFileName,
1026 {
1027 let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
1028 let data = data.deref_mut();
1029
1030 if data.open_dirs.is_full() {
1032 return Err(Error::TooManyOpenDirs);
1033 }
1034
1035 let parent_directory_idx = data.get_dir_by_id(directory)?;
1036 let parent_directory_info = &data.open_dirs[parent_directory_idx];
1037 let volume_id = data.open_dirs[parent_directory_idx].raw_volume;
1038 let volume_idx = data.get_volume_by_id(volume_id)?;
1039 let volume_info = &data.open_volumes[volume_idx];
1040 let sfn = name.to_short_filename().map_err(Error::FilenameError)?;
1041
1042 debug!("Creating directory '{}'", sfn);
1043 debug!(
1044 "Parent dir is in cluster {:?}",
1045 parent_directory_info.cluster
1046 );
1047
1048 let maybe_dir_entry = match &volume_info.volume_type {
1050 VolumeType::Fat(fat) => {
1051 fat.find_directory_entry(&mut data.block_cache, parent_directory_info, &sfn)
1052 }
1053 };
1054
1055 match maybe_dir_entry {
1056 Ok(entry) if entry.attributes.is_directory() => {
1057 return Err(Error::DirAlreadyExists);
1058 }
1059 Ok(_entry) => {
1060 return Err(Error::FileAlreadyExists);
1061 }
1062 Err(Error::NotFound) => {
1063 }
1065 Err(e) => {
1066 return Err(e);
1068 }
1069 };
1070
1071 let att = Attributes::create_from_fat(Attributes::DIRECTORY);
1072
1073 match &mut data.open_volumes[volume_idx].volume_type {
1075 VolumeType::Fat(fat) => {
1076 debug!("Making dir entry");
1077 fat.make_dir(
1078 &mut data.block_cache,
1079 &self.time_source,
1080 parent_directory_info.cluster,
1081 sfn,
1082 att,
1083 )?;
1084 }
1085 };
1086
1087 Ok(())
1088 }
1089}
1090
1091#[derive(Debug)]
1095
1096struct VolumeManagerData<
1097 D,
1098 const MAX_DIRS: usize = 4,
1099 const MAX_FILES: usize = 4,
1100 const MAX_VOLUMES: usize = 1,
1101> where
1102 D: BlockDevice,
1103{
1104 id_generator: HandleGenerator,
1105 block_cache: BlockCache<D>,
1106 open_volumes: Vec<VolumeInfo, MAX_VOLUMES>,
1107 open_dirs: Vec<DirectoryInfo, MAX_DIRS>,
1108 open_files: Vec<FileInfo, MAX_FILES>,
1109}
1110
1111impl<D, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
1112 VolumeManagerData<D, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
1113where
1114 D: BlockDevice,
1115{
1116 fn file_is_open(&self, raw_volume: RawVolume, dir_entry: &DirEntry) -> bool {
1120 for f in self.open_files.iter() {
1121 if f.raw_volume == raw_volume
1122 && f.entry.entry_block == dir_entry.entry_block
1123 && f.entry.entry_offset == dir_entry.entry_offset
1124 {
1125 return true;
1126 }
1127 }
1128 false
1129 }
1130
1131 fn get_volume_by_id<E>(&self, raw_volume: RawVolume) -> Result<usize, Error<E>>
1132 where
1133 E: core::fmt::Debug,
1134 {
1135 for (idx, v) in self.open_volumes.iter().enumerate() {
1136 if v.raw_volume == raw_volume {
1137 return Ok(idx);
1138 }
1139 }
1140 Err(Error::BadHandle)
1141 }
1142
1143 fn get_dir_by_id<E>(&self, raw_directory: RawDirectory) -> Result<usize, Error<E>>
1144 where
1145 E: core::fmt::Debug,
1146 {
1147 for (idx, d) in self.open_dirs.iter().enumerate() {
1148 if d.raw_directory == raw_directory {
1149 return Ok(idx);
1150 }
1151 }
1152 Err(Error::BadHandle)
1153 }
1154
1155 fn get_file_by_id<E>(&self, raw_file: RawFile) -> Result<usize, Error<E>>
1156 where
1157 E: core::fmt::Debug,
1158 {
1159 for (idx, f) in self.open_files.iter().enumerate() {
1160 if f.raw_file == raw_file {
1161 return Ok(idx);
1162 }
1163 }
1164 Err(Error::BadHandle)
1165 }
1166
1167 fn find_data_on_disk(
1177 &mut self,
1178 volume_idx: usize,
1179 start: &mut (u32, ClusterId),
1180 file_start: ClusterId,
1181 desired_offset: u32,
1182 ) -> Result<(BlockIdx, usize, usize), Error<D::Error>>
1183 where
1184 D: BlockDevice,
1185 {
1186 let bytes_per_cluster = match &self.open_volumes[volume_idx].volume_type {
1187 VolumeType::Fat(fat) => fat.bytes_per_cluster(),
1188 };
1189 if desired_offset < start.0 {
1191 start.0 = 0;
1194 start.1 = file_start;
1195 }
1196 let offset_from_cluster = desired_offset - start.0;
1198 let num_clusters = offset_from_cluster / bytes_per_cluster;
1200 for _ in 0..num_clusters {
1201 start.1 = match &self.open_volumes[volume_idx].volume_type {
1202 VolumeType::Fat(fat) => fat.next_cluster(&mut self.block_cache, start.1)?,
1203 };
1204 start.0 += bytes_per_cluster;
1205 }
1206 let offset_from_cluster = desired_offset - start.0;
1208 assert!(offset_from_cluster < bytes_per_cluster);
1209 let num_blocks = BlockCount(offset_from_cluster / Block::LEN_U32);
1210 let block_idx = match &self.open_volumes[volume_idx].volume_type {
1211 VolumeType::Fat(fat) => fat.cluster_to_block(start.1),
1212 } + num_blocks;
1213 let block_offset = (desired_offset % Block::LEN_U32) as usize;
1214 let available = Block::LEN - block_offset;
1215 Ok((block_idx, block_offset, available))
1216 }
1217}
1218
1219fn solve_mode_variant(mode: Mode, dir_entry_is_some: bool) -> Mode {
1222 let mut mode = mode;
1223 if mode == Mode::ReadWriteCreateOrAppend {
1224 if dir_entry_is_some {
1225 mode = Mode::ReadWriteAppend;
1226 } else {
1227 mode = Mode::ReadWriteCreate;
1228 }
1229 } else if mode == Mode::ReadWriteCreateOrTruncate {
1230 if dir_entry_is_some {
1231 mode = Mode::ReadWriteTruncate;
1232 } else {
1233 mode = Mode::ReadWriteCreate;
1234 }
1235 }
1236 mode
1237}
1238
1239#[cfg(test)]
1246mod tests {
1247 use super::*;
1248 use crate::filesystem::Handle;
1249 use crate::Timestamp;
1250
1251 struct DummyBlockDevice;
1252
1253 struct Clock;
1254
1255 #[derive(Debug)]
1256 enum Error {
1257 Unknown,
1258 }
1259
1260 impl TimeSource for Clock {
1261 fn get_timestamp(&self) -> Timestamp {
1262 Timestamp {
1264 year_since_1970: 0,
1265 zero_indexed_month: 0,
1266 zero_indexed_day: 0,
1267 hours: 0,
1268 minutes: 0,
1269 seconds: 0,
1270 }
1271 }
1272 }
1273
1274 impl BlockDevice for DummyBlockDevice {
1275 type Error = Error;
1276
1277 fn read(&self, blocks: &mut [Block], start_block_idx: BlockIdx) -> Result<(), Self::Error> {
1279 static BLOCKS: [Block; 3] = [
1281 Block {
1282 contents: [
1283 0xfa, 0xb8, 0x00, 0x10, 0x8e, 0xd0, 0xbc, 0x00, 0xb0, 0xb8, 0x00, 0x00,
1284 0x8e, 0xd8, 0x8e, 0xc0, 0xfb, 0xbe, 0x00, 0x7c, 0xbf, 0x00, 0x06, 0xb9, 0x00, 0x02, 0xf3, 0xa4,
1286 0xea, 0x21, 0x06, 0x00, 0x00, 0xbe, 0xbe, 0x07, 0x38, 0x04, 0x75, 0x0b, 0x83, 0xc6, 0x10, 0x81,
1288 0xfe, 0xfe, 0x07, 0x75, 0xf3, 0xeb, 0x16, 0xb4, 0x02, 0xb0, 0x01, 0xbb, 0x00, 0x7c, 0xb2, 0x80,
1290 0x8a, 0x74, 0x01, 0x8b, 0x4c, 0x02, 0xcd, 0x13, 0xea, 0x00, 0x7c, 0x00, 0x00, 0xeb, 0xfe, 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, 0x00, 0x00, 0x00, 0x00,
1336 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xca, 0xde, 0x06,
1338 0x00, 0x00, 0x00, 0x04, 0x01, 0x04, 0x0c, 0xfe, 0xc2, 0xff, 0x01, 0x00, 0x00, 0x00, 0x33, 0x22,
1340 0x11, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1346 0x00, 0x00, 0x55, 0xaa, ],
1348 },
1349 Block {
1350 contents: [
1351 0xeb, 0x58, 0x90, 0x6d, 0x6b, 0x66, 0x73, 0x2e, 0x66, 0x61, 0x74, 0x00,
1352 0x02, 0x08, 0x20, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x10, 0x00, 0x04, 0x00,
1354 0x00, 0x08, 0x00, 0x00, 0x00, 0x20, 0x76, 0x00, 0x80, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1356 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1358 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x29, 0x0b, 0xa8, 0x89, 0x27, 0x50, 0x69, 0x63, 0x74, 0x75,
1360 0x72, 0x65, 0x73, 0x20, 0x20, 0x20, 0x46, 0x41, 0x54, 0x33, 0x32, 0x20, 0x20, 0x20, 0x0e, 0x1f,
1362 0xbe, 0x77, 0x7c, 0xac, 0x22, 0xc0, 0x74, 0x0b, 0x56, 0xb4, 0x0e, 0xbb, 0x07, 0x00, 0xcd, 0x10,
1364 0x5e, 0xeb, 0xf0, 0x32, 0xe4, 0xcd, 0x16, 0xcd, 0x19, 0xeb, 0xfe, 0x54, 0x68, 0x69, 0x73, 0x20,
1366 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c,
1368 0x65, 0x20, 0x64, 0x69, 0x73, 0x6b, 0x2e, 0x20, 0x20, 0x50, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20,
1370 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c,
1372 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x70, 0x70, 0x79, 0x20, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x70, 0x72,
1374 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x74,
1376 0x72, 0x79, 0x20, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x0d, 0x0a, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1414 0x00, 0x00, 0x55, 0xaa, ],
1416 },
1417 Block {
1418 contents: hex!(
1419 "52 52 61 41 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 00 00 00 00 00 00 00 00 00 00 00 00
1448 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1449 00 00 00 00 72 72 41 61 FF FF FF FF FF FF FF FF
1450 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA"
1451 ),
1452 },
1453 ];
1454 println!(
1455 "Reading block {} to {}",
1456 start_block_idx.0,
1457 start_block_idx.0 as usize + blocks.len()
1458 );
1459 for (idx, block) in blocks.iter_mut().enumerate() {
1460 let block_idx = start_block_idx.0 as usize + idx;
1461 if block_idx < BLOCKS.len() {
1462 *block = BLOCKS[block_idx].clone();
1463 } else {
1464 return Err(Error::Unknown);
1465 }
1466 }
1467 Ok(())
1468 }
1469
1470 fn write(&self, _blocks: &[Block], _start_block_idx: BlockIdx) -> Result<(), Self::Error> {
1472 unimplemented!();
1473 }
1474
1475 fn num_blocks(&self) -> Result<BlockCount, Self::Error> {
1477 Ok(BlockCount(2))
1478 }
1479 }
1480
1481 #[test]
1482 fn partition0() {
1483 let c: VolumeManager<DummyBlockDevice, Clock, 2, 2> =
1484 VolumeManager::new_with_limits(DummyBlockDevice, Clock, 0xAA00_0000);
1485
1486 let v = c.open_raw_volume(VolumeIdx(0)).unwrap();
1487 let expected_id = RawVolume(Handle(0xAA00_0000));
1488 assert_eq!(v, expected_id);
1489 assert_eq!(
1490 &c.data.borrow().open_volumes[0],
1491 &VolumeInfo {
1492 raw_volume: expected_id,
1493 idx: VolumeIdx(0),
1494 volume_type: VolumeType::Fat(crate::FatVolume {
1495 lba_start: BlockIdx(1),
1496 num_blocks: BlockCount(0x0011_2233),
1497 blocks_per_cluster: 8,
1498 first_data_block: BlockCount(15136),
1499 fat_start: BlockCount(32),
1500 second_fat_start: Some(BlockCount(32 + 0x0000_1D80)),
1501 name: fat::VolumeName::create_from_str("Pictures").unwrap(),
1502 free_clusters_count: None,
1503 next_free_cluster: None,
1504 cluster_count: 965_788,
1505 fat_specific_info: fat::FatSpecificInfo::Fat32(fat::Fat32Info {
1506 first_root_dir_cluster: ClusterId(2),
1507 info_location: BlockIdx(1) + BlockCount(1),
1508 })
1509 })
1510 }
1511 );
1512 }
1513}
1514
1515