1use byteorder::{ByteOrder, LittleEndian};
6use core::convert::TryFrom;
7
8use crate::fat::{self, BlockCache, OnDiskDirEntry, RESERVED_ENTRIES};
9
10use crate::filesystem::{
11 Attributes, ClusterId, DirEntry, DirectoryInfo, FileInfo, Mode, RawDirectory, RawFile,
12 SearchIdGenerator, TimeSource, ToShortFileName, MAX_FILE_SIZE,
13};
14use crate::{
15 debug, Block, BlockCount, BlockDevice, BlockIdx, Error, RawVolume, ShortFileName, Volume,
16 VolumeIdx, VolumeInfo, VolumeType, PARTITION_ID_FAT16, PARTITION_ID_FAT16_LBA,
17 PARTITION_ID_FAT32_CHS_LBA, PARTITION_ID_FAT32_LBA,
18};
19use heapless::Vec;
20
21#[derive(Debug)]
27pub struct VolumeManager<
28 D,
29 T,
30 const MAX_DIRS: usize = 4,
31 const MAX_FILES: usize = 4,
32 const MAX_VOLUMES: usize = 1,
33> where
34 D: BlockDevice,
35 T: TimeSource,
36 <D as BlockDevice>::Error: core::fmt::Debug,
37{
38 pub(crate) block_device: D,
39 pub(crate) time_source: T,
40 id_generator: SearchIdGenerator,
41 open_volumes: Vec<VolumeInfo, MAX_VOLUMES>,
42 open_dirs: Vec<DirectoryInfo, MAX_DIRS>,
43 open_files: Vec<FileInfo, MAX_FILES>,
44}
45
46impl<D, T> VolumeManager<D, T, 4, 4>
47where
48 D: BlockDevice,
49 T: TimeSource,
50 <D as BlockDevice>::Error: core::fmt::Debug,
51{
52 pub fn new(block_device: D, time_source: T) -> VolumeManager<D, T, 4, 4, 1> {
60 Self::new_with_limits(block_device, time_source, 5000)
63 }
64}
65
66impl<D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
67 VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
68where
69 D: BlockDevice,
70 T: TimeSource,
71 <D as BlockDevice>::Error: core::fmt::Debug,
72{
73 pub fn new_with_limits(
81 block_device: D,
82 time_source: T,
83 id_offset: u32,
84 ) -> VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> {
85 debug!("Creating new embedded-sdmmc::VolumeManager");
86 VolumeManager {
87 block_device,
88 time_source,
89 id_generator: SearchIdGenerator::new(id_offset),
90 open_volumes: Vec::new(),
91 open_dirs: Vec::new(),
92 open_files: Vec::new(),
93 }
94 }
95
96 pub fn device(&mut self) -> &mut D {
98 &mut self.block_device
99 }
100
101 pub fn open_volume(
106 &mut self,
107 volume_idx: VolumeIdx,
108 ) -> Result<Volume<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>, Error<D::Error>> {
109 let v = self.open_raw_volume(volume_idx)?;
110 Ok(v.to_volume(self))
111 }
112
113 pub fn open_raw_volume(&mut self, volume_idx: VolumeIdx) -> Result<RawVolume, Error<D::Error>> {
121 const PARTITION1_START: usize = 446;
122 const PARTITION2_START: usize = PARTITION1_START + PARTITION_INFO_LENGTH;
123 const PARTITION3_START: usize = PARTITION2_START + PARTITION_INFO_LENGTH;
124 const PARTITION4_START: usize = PARTITION3_START + PARTITION_INFO_LENGTH;
125 const FOOTER_START: usize = 510;
126 const FOOTER_VALUE: u16 = 0xAA55;
127 const PARTITION_INFO_LENGTH: usize = 16;
128 const PARTITION_INFO_STATUS_INDEX: usize = 0;
129 const PARTITION_INFO_TYPE_INDEX: usize = 4;
130 const PARTITION_INFO_LBA_START_INDEX: usize = 8;
131 const PARTITION_INFO_NUM_BLOCKS_INDEX: usize = 12;
132
133 if self.open_volumes.is_full() {
134 return Err(Error::TooManyOpenVolumes);
135 }
136
137 for v in self.open_volumes.iter() {
138 if v.idx == volume_idx {
139 return Err(Error::VolumeAlreadyOpen);
140 }
141 }
142
143 let (part_type, lba_start, num_blocks) = {
144 let mut blocks = [Block::new()];
145 self.block_device
146 .read(&mut blocks, BlockIdx(0), "read_mbr")
147 .map_err(Error::DeviceError)?;
148 let block = &blocks[0];
149 if LittleEndian::read_u16(&block[FOOTER_START..FOOTER_START + 2]) != FOOTER_VALUE {
152 return Err(Error::FormatError("Invalid MBR signature"));
153 }
154 let partition = match volume_idx {
155 VolumeIdx(0) => {
156 &block[PARTITION1_START..(PARTITION1_START + PARTITION_INFO_LENGTH)]
157 }
158 VolumeIdx(1) => {
159 &block[PARTITION2_START..(PARTITION2_START + PARTITION_INFO_LENGTH)]
160 }
161 VolumeIdx(2) => {
162 &block[PARTITION3_START..(PARTITION3_START + PARTITION_INFO_LENGTH)]
163 }
164 VolumeIdx(3) => {
165 &block[PARTITION4_START..(PARTITION4_START + PARTITION_INFO_LENGTH)]
166 }
167 _ => {
168 return Err(Error::NoSuchVolume);
169 }
170 };
171 if (partition[PARTITION_INFO_STATUS_INDEX] & 0x7F) != 0x00 {
173 return Err(Error::FormatError("Invalid partition status"));
174 }
175 let lba_start = LittleEndian::read_u32(
176 &partition[PARTITION_INFO_LBA_START_INDEX..(PARTITION_INFO_LBA_START_INDEX + 4)],
177 );
178 let num_blocks = LittleEndian::read_u32(
179 &partition[PARTITION_INFO_NUM_BLOCKS_INDEX..(PARTITION_INFO_NUM_BLOCKS_INDEX + 4)],
180 );
181 (
182 partition[PARTITION_INFO_TYPE_INDEX],
183 BlockIdx(lba_start),
184 BlockCount(num_blocks),
185 )
186 };
187 match part_type {
188 PARTITION_ID_FAT32_CHS_LBA
189 | PARTITION_ID_FAT32_LBA
190 | PARTITION_ID_FAT16_LBA
191 | PARTITION_ID_FAT16 => {
192 let volume = fat::parse_volume(&self.block_device, lba_start, num_blocks)?;
193 let id = RawVolume(self.id_generator.get());
194 let info = VolumeInfo {
195 volume_id: id,
196 idx: volume_idx,
197 volume_type: volume,
198 };
199 self.open_volumes.push(info).unwrap();
201 Ok(id)
202 }
203 _ => Err(Error::FormatError("Partition type not supported")),
204 }
205 }
206
207 pub fn open_root_dir(&mut self, volume: RawVolume) -> Result<RawDirectory, Error<D::Error>> {
212 let directory_id = RawDirectory(self.id_generator.get());
215 let dir_info = DirectoryInfo {
216 volume_id: volume,
217 cluster: ClusterId::ROOT_DIR,
218 directory_id,
219 };
220
221 self.open_dirs
222 .push(dir_info)
223 .map_err(|_| Error::TooManyOpenDirs)?;
224
225 Ok(directory_id)
226 }
227
228 pub fn open_dir<N>(
234 &mut self,
235 parent_dir: RawDirectory,
236 name: N,
237 ) -> Result<RawDirectory, Error<D::Error>>
238 where
239 N: ToShortFileName,
240 {
241 if self.open_dirs.is_full() {
242 return Err(Error::TooManyOpenDirs);
243 }
244
245 let parent_dir_idx = self.get_dir_by_id(parent_dir)?;
247 let volume_idx = self.get_volume_by_id(self.open_dirs[parent_dir_idx].volume_id)?;
248 let short_file_name = name.to_short_filename().map_err(Error::FilenameError)?;
249 let parent_dir_info = &self.open_dirs[parent_dir_idx];
250
251 if short_file_name == ShortFileName::this_dir() {
253 let directory_id = RawDirectory(self.id_generator.get());
255 let dir_info = DirectoryInfo {
256 directory_id,
257 volume_id: self.open_volumes[volume_idx].volume_id,
258 cluster: parent_dir_info.cluster,
259 };
260
261 self.open_dirs
262 .push(dir_info)
263 .map_err(|_| Error::TooManyOpenDirs)?;
264
265 return Ok(directory_id);
266 }
267
268 let dir_entry = match &self.open_volumes[volume_idx].volume_type {
269 VolumeType::Fat(fat) => {
270 fat.find_directory_entry(&self.block_device, parent_dir_info, &short_file_name)?
271 }
272 };
273
274 debug!("Found dir entry: {:?}", dir_entry);
275
276 if !dir_entry.attributes.is_directory() {
277 return Err(Error::OpenedFileAsDir);
278 }
279
280 let directory_id = RawDirectory(self.id_generator.get());
285 let dir_info = DirectoryInfo {
286 directory_id,
287 volume_id: self.open_volumes[volume_idx].volume_id,
288 cluster: dir_entry.cluster,
289 };
290
291 self.open_dirs
292 .push(dir_info)
293 .map_err(|_| Error::TooManyOpenDirs)?;
294
295 Ok(directory_id)
296 }
297
298 pub fn close_dir(&mut self, directory: RawDirectory) -> Result<(), Error<D::Error>> {
301 for (idx, info) in self.open_dirs.iter().enumerate() {
302 if directory == info.directory_id {
303 self.open_dirs.swap_remove(idx);
304 return Ok(());
305 }
306 }
307 Err(Error::BadHandle)
308 }
309
310 pub fn close_volume(&mut self, volume: RawVolume) -> Result<(), Error<D::Error>> {
314 for f in self.open_files.iter() {
315 if f.volume_id == volume {
316 return Err(Error::VolumeStillInUse);
317 }
318 }
319
320 for d in self.open_dirs.iter() {
321 if d.volume_id == volume {
322 return Err(Error::VolumeStillInUse);
323 }
324 }
325
326 let volume_idx = self.get_volume_by_id(volume)?;
327
328 match &mut self.open_volumes[volume_idx].volume_type {
329 VolumeType::Fat(fat_volume) => {
330 fat_volume.update_info_sector(&self.block_device)?;
331 }
332 }
333
334 self.open_volumes.swap_remove(volume_idx);
335
336 Ok(())
337 }
338
339 pub fn find_directory_entry<N>(
341 &mut self,
342 directory: RawDirectory,
343 name: N,
344 ) -> Result<DirEntry, Error<D::Error>>
345 where
346 N: ToShortFileName,
347 {
348 let directory_idx = self.get_dir_by_id(directory)?;
349 let volume_idx = self.get_volume_by_id(self.open_dirs[directory_idx].volume_id)?;
350 match &self.open_volumes[volume_idx].volume_type {
351 VolumeType::Fat(fat) => {
352 let sfn = name.to_short_filename().map_err(Error::FilenameError)?;
353 fat.find_directory_entry(&self.block_device, &self.open_dirs[directory_idx], &sfn)
354 }
355 }
356 }
357
358 pub fn iterate_dir<F>(
360 &mut self,
361 directory: RawDirectory,
362 func: F,
363 ) -> Result<(), Error<D::Error>>
364 where
365 F: FnMut(&DirEntry),
366 {
367 let directory_idx = self.get_dir_by_id(directory)?;
368 let volume_idx = self.get_volume_by_id(self.open_dirs[directory_idx].volume_id)?;
369 match &self.open_volumes[volume_idx].volume_type {
370 VolumeType::Fat(fat) => {
371 fat.iterate_dir(&self.block_device, &self.open_dirs[directory_idx], func)
372 }
373 }
374 }
375
376 unsafe fn open_dir_entry(
383 &mut self,
384 volume: RawVolume,
385 dir_entry: DirEntry,
386 mode: Mode,
387 ) -> Result<RawFile, Error<D::Error>> {
388 if self.open_files.is_full() {
390 return Err(Error::TooManyOpenFiles);
391 }
392
393 if dir_entry.attributes.is_read_only() && mode != Mode::ReadOnly {
394 return Err(Error::ReadOnly);
395 }
396
397 if dir_entry.attributes.is_directory() {
398 return Err(Error::OpenedDirAsFile);
399 }
400
401 if self.file_is_open(volume, &dir_entry) {
403 return Err(Error::FileAlreadyOpen);
404 }
405
406 let mode = solve_mode_variant(mode, true);
407 let file_id = RawFile(self.id_generator.get());
408
409 let file = match mode {
410 Mode::ReadOnly => FileInfo {
411 file_id,
412 volume_id: volume,
413 current_cluster: (0, dir_entry.cluster),
414 current_offset: 0,
415 mode,
416 entry: dir_entry,
417 dirty: false,
418 },
419 Mode::ReadWriteAppend => {
420 let mut file = FileInfo {
421 file_id,
422 volume_id: volume,
423 current_cluster: (0, dir_entry.cluster),
424 current_offset: 0,
425 mode,
426 entry: dir_entry,
427 dirty: false,
428 };
429 file.seek_from_end(0).ok();
431 file
432 }
433 Mode::ReadWriteTruncate => {
434 let mut file = FileInfo {
435 file_id,
436 volume_id: volume,
437 current_cluster: (0, dir_entry.cluster),
438 current_offset: 0,
439 mode,
440 entry: dir_entry,
441 dirty: false,
442 };
443 let volume_idx = self.get_volume_by_id(volume)?;
444 match &mut self.open_volumes[volume_idx].volume_type {
445 VolumeType::Fat(fat) => {
446 fat.truncate_cluster_chain(&self.block_device, file.entry.cluster)?
447 }
448 };
449 file.update_length(0);
450 match &self.open_volumes[volume_idx].volume_type {
451 VolumeType::Fat(fat) => {
452 file.entry.mtime = self.time_source.get_timestamp();
453 fat.write_entry_to_disk(&self.block_device, &file.entry)?;
454 }
455 };
456
457 file
458 }
459 _ => return Err(Error::Unsupported),
460 };
461
462 unsafe {
464 self.open_files.push_unchecked(file);
465 }
466
467 Ok(file_id)
468 }
469
470 pub fn open_file_in_dir<N>(
472 &mut self,
473 directory: RawDirectory,
474 name: N,
475 mode: Mode,
476 ) -> Result<RawFile, Error<D::Error>>
477 where
478 N: ToShortFileName,
479 {
480 if self.open_files.is_full() {
482 return Err(Error::TooManyOpenFiles);
483 }
484
485 let directory_idx = self.get_dir_by_id(directory)?;
486 let directory_info = &self.open_dirs[directory_idx];
487 let volume_id = self.open_dirs[directory_idx].volume_id;
488 let volume_idx = self.get_volume_by_id(volume_id)?;
489 let volume_info = &self.open_volumes[volume_idx];
490 let sfn = name.to_short_filename().map_err(Error::FilenameError)?;
491
492 let dir_entry = match &volume_info.volume_type {
493 VolumeType::Fat(fat) => {
494 fat.find_directory_entry(&self.block_device, directory_info, &sfn)
495 }
496 };
497
498 let dir_entry = match dir_entry {
499 Ok(entry) => {
500 Some(entry)
502 }
503 Err(_)
504 if (mode == Mode::ReadWriteCreate)
505 | (mode == Mode::ReadWriteCreateOrTruncate)
506 | (mode == Mode::ReadWriteCreateOrAppend) =>
507 {
508 None
511 }
512 _ => {
513 return Err(Error::NotFound);
515 }
516 };
517
518 if let Some(dir_entry) = &dir_entry {
520 if self.file_is_open(volume_info.volume_id, dir_entry) {
521 return Err(Error::FileAlreadyOpen);
522 }
523 }
524
525 let mode = solve_mode_variant(mode, dir_entry.is_some());
526
527 match mode {
528 Mode::ReadWriteCreate => {
529 if dir_entry.is_some() {
530 return Err(Error::FileAlreadyExists);
531 }
532 let att = Attributes::create_from_fat(0);
533 let volume_idx = self.get_volume_by_id(volume_id)?;
534 let entry = match &mut self.open_volumes[volume_idx].volume_type {
535 VolumeType::Fat(fat) => fat.write_new_directory_entry(
536 &self.block_device,
537 &self.time_source,
538 directory_info.cluster,
539 sfn,
540 att,
541 )?,
542 };
543
544 let file_id = RawFile(self.id_generator.get());
545
546 let file = FileInfo {
547 file_id,
548 volume_id,
549 current_cluster: (0, entry.cluster),
550 current_offset: 0,
551 mode,
552 entry,
553 dirty: false,
554 };
555
556 unsafe {
558 self.open_files.push_unchecked(file);
559 }
560
561 Ok(file_id)
562 }
563 _ => {
564 let dir_entry = dir_entry.unwrap();
566 unsafe { self.open_dir_entry(volume_id, dir_entry, mode) }
568 }
569 }
570 }
571
572 pub fn delete_file_in_dir<N>(
574 &mut self,
575 directory: RawDirectory,
576 name: N,
577 ) -> Result<(), Error<D::Error>>
578 where
579 N: ToShortFileName,
580 {
581 let dir_idx = self.get_dir_by_id(directory)?;
582 let dir_info = &self.open_dirs[dir_idx];
583 let volume_idx = self.get_volume_by_id(dir_info.volume_id)?;
584 let sfn = name.to_short_filename().map_err(Error::FilenameError)?;
585
586 let dir_entry = match &self.open_volumes[volume_idx].volume_type {
587 VolumeType::Fat(fat) => fat.find_directory_entry(&self.block_device, dir_info, &sfn),
588 }?;
589
590 if dir_entry.attributes.is_directory() {
591 return Err(Error::DeleteDirAsFile);
592 }
593
594 if self.file_is_open(dir_info.volume_id, &dir_entry) {
595 return Err(Error::FileAlreadyOpen);
596 }
597
598 let volume_idx = self.get_volume_by_id(dir_info.volume_id)?;
599 match &self.open_volumes[volume_idx].volume_type {
600 VolumeType::Fat(fat) => {
601 fat.delete_directory_entry(&self.block_device, dir_info, &sfn)?
602 }
603 }
604
605 Ok(())
606 }
607
608 fn file_is_open(&self, volume: RawVolume, dir_entry: &DirEntry) -> bool {
612 for f in self.open_files.iter() {
613 if f.volume_id == volume
614 && f.entry.entry_block == dir_entry.entry_block
615 && f.entry.entry_offset == dir_entry.entry_offset
616 {
617 return true;
618 }
619 }
620 false
621 }
622
623 pub fn read(&mut self, file: RawFile, buffer: &mut [u8]) -> Result<usize, Error<D::Error>> {
625 let file_idx = self.get_file_by_id(file)?;
626 let volume_idx = self.get_volume_by_id(self.open_files[file_idx].volume_id)?;
627 let mut space = buffer.len();
631 let mut read = 0;
632 while space > 0 && !self.open_files[file_idx].eof() {
633 let mut current_cluster = self.open_files[file_idx].current_cluster;
634 let (block_idx, block_offset, block_avail) = self.find_data_on_disk(
635 volume_idx,
636 &mut current_cluster,
637 self.open_files[file_idx].entry.cluster,
638 self.open_files[file_idx].current_offset,
639 )?;
640 self.open_files[file_idx].current_cluster = current_cluster;
641 let mut blocks = [Block::new()];
642 self.block_device
643 .read(&mut blocks, block_idx, "read")
644 .map_err(Error::DeviceError)?;
645 let block = &blocks[0];
646 let to_copy = block_avail
647 .min(space)
648 .min(self.open_files[file_idx].left() as usize);
649 assert!(to_copy != 0);
650 buffer[read..read + to_copy]
651 .copy_from_slice(&block[block_offset..block_offset + to_copy]);
652 read += to_copy;
653 space -= to_copy;
654 self.open_files[file_idx]
655 .seek_from_current(to_copy as i32)
656 .unwrap();
657 }
658 Ok(read)
659 }
660
661 pub fn write(&mut self, file: RawFile, buffer: &[u8]) -> Result<(), Error<D::Error>> {
663 #[cfg(feature = "defmt-log")]
664 debug!("write(file={:?}, buffer={:x}", file, buffer);
665
666 #[cfg(feature = "log")]
667 debug!("write(file={:?}, buffer={:x?}", file, buffer);
668
669 let file_idx = self.get_file_by_id(file)?;
672 let volume_idx = self.get_volume_by_id(self.open_files[file_idx].volume_id)?;
673
674 if self.open_files[file_idx].mode == Mode::ReadOnly {
675 return Err(Error::ReadOnly);
676 }
677
678 self.open_files[file_idx].dirty = true;
679
680 if self.open_files[file_idx].entry.cluster.0 < RESERVED_ENTRIES {
681 self.open_files[file_idx].entry.cluster =
683 match self.open_volumes[volume_idx].volume_type {
684 VolumeType::Fat(ref mut fat) => {
685 fat.alloc_cluster(&self.block_device, None, false)?
686 }
687 };
688 debug!(
689 "Alloc first cluster {:?}",
690 self.open_files[file_idx].entry.cluster
691 );
692 }
693
694 let volume_idx = self.get_volume_by_id(self.open_files[file_idx].volume_id)?;
696
697 if (self.open_files[file_idx].current_cluster.1) < self.open_files[file_idx].entry.cluster {
698 debug!("Rewinding to start");
699 self.open_files[file_idx].current_cluster =
700 (0, self.open_files[file_idx].entry.cluster);
701 }
702 let bytes_until_max =
703 usize::try_from(MAX_FILE_SIZE - self.open_files[file_idx].current_offset)
704 .map_err(|_| Error::ConversionError)?;
705 let bytes_to_write = core::cmp::min(buffer.len(), bytes_until_max);
706 let mut written = 0;
707
708 while written < bytes_to_write {
709 let mut current_cluster = self.open_files[file_idx].current_cluster;
710 debug!(
711 "Have written bytes {}/{}, finding cluster {:?}",
712 written, bytes_to_write, current_cluster
713 );
714 let current_offset = self.open_files[file_idx].current_offset;
715 let (block_idx, block_offset, block_avail) = match self.find_data_on_disk(
716 volume_idx,
717 &mut current_cluster,
718 self.open_files[file_idx].entry.cluster,
719 current_offset,
720 ) {
721 Ok(vars) => {
722 debug!(
723 "Found block_idx={:?}, block_offset={:?}, block_avail={}",
724 vars.0, vars.1, vars.2
725 );
726 vars
727 }
728 Err(Error::EndOfFile) => {
729 debug!("Extending file");
730 match self.open_volumes[volume_idx].volume_type {
731 VolumeType::Fat(ref mut fat) => {
732 if fat
733 .alloc_cluster(&self.block_device, Some(current_cluster.1), false)
734 .is_err()
735 {
736 return Err(Error::DiskFull);
737 }
738 debug!("Allocated new FAT cluster, finding offsets...");
739 let new_offset = self
740 .find_data_on_disk(
741 volume_idx,
742 &mut current_cluster,
743 self.open_files[file_idx].entry.cluster,
744 self.open_files[file_idx].current_offset,
745 )
746 .map_err(|_| Error::AllocationError)?;
747 debug!("New offset {:?}", new_offset);
748 new_offset
749 }
750 }
751 }
752 Err(e) => return Err(e),
753 };
754 let mut blocks = [Block::new()];
755 let to_copy = core::cmp::min(block_avail, bytes_to_write - written);
756 if block_offset != 0 || to_copy != block_avail {
757 debug!("Partial block read/modify/write");
758 self.block_device
759 .read(&mut blocks, block_idx, "read")
760 .map_err(Error::DeviceError)?;
761 }
762 let block = &mut blocks[0];
763 block[block_offset..block_offset + to_copy]
764 .copy_from_slice(&buffer[written..written + to_copy]);
765 debug!("Writing block {:?}", block_idx);
766 self.block_device
767 .write(&blocks, block_idx)
768 .map_err(Error::DeviceError)?;
769 written += to_copy;
770 self.open_files[file_idx].current_cluster = current_cluster;
771
772 let to_copy = to_copy as u32;
773 let new_offset = self.open_files[file_idx].current_offset + to_copy;
774 if new_offset > self.open_files[file_idx].entry.size {
775 self.open_files[file_idx].update_length(new_offset);
777 }
778 self.open_files[file_idx]
779 .seek_from_start(new_offset)
780 .unwrap();
781 }
783 self.open_files[file_idx].entry.attributes.set_archive(true);
784 self.open_files[file_idx].entry.mtime = self.time_source.get_timestamp();
785 Ok(())
786 }
787
788 pub fn close_file(&mut self, file: RawFile) -> Result<(), Error<D::Error>> {
790 let flush_result = self.flush_file(file);
791 let file_idx = self.get_file_by_id(file)?;
792 self.open_files.swap_remove(file_idx);
793 flush_result
794 }
795
796 pub fn flush_file(&mut self, file: RawFile) -> Result<(), Error<D::Error>> {
798 let file_info = self
799 .open_files
800 .iter()
801 .find(|info| info.file_id == file)
802 .ok_or(Error::BadHandle)?;
803
804 if file_info.dirty {
805 let volume_idx = self.get_volume_by_id(file_info.volume_id)?;
806 match self.open_volumes[volume_idx].volume_type {
807 VolumeType::Fat(ref mut fat) => {
808 debug!("Updating FAT info sector");
809 fat.update_info_sector(&self.block_device)?;
810 debug!("Updating dir entry {:?}", file_info.entry);
811 if file_info.entry.size != 0 {
812 assert!(file_info.entry.cluster.0 != 0);
814 }
815 fat.write_entry_to_disk(&self.block_device, &file_info.entry)?;
816 }
817 };
818 }
819 Ok(())
820 }
821
822 pub fn has_open_handles(&self) -> bool {
824 !(self.open_dirs.is_empty() || self.open_files.is_empty())
825 }
826
827 pub fn free(self) -> (D, T) {
829 (self.block_device, self.time_source)
830 }
831
832 pub fn file_eof(&self, file: RawFile) -> Result<bool, Error<D::Error>> {
834 let file_idx = self.get_file_by_id(file)?;
835 Ok(self.open_files[file_idx].eof())
836 }
837
838 pub fn file_seek_from_start(
840 &mut self,
841 file: RawFile,
842 offset: u32,
843 ) -> Result<(), Error<D::Error>> {
844 let file_idx = self.get_file_by_id(file)?;
845 self.open_files[file_idx]
846 .seek_from_start(offset)
847 .map_err(|_| Error::InvalidOffset)?;
848 Ok(())
849 }
850
851 pub fn file_seek_from_current(
853 &mut self,
854 file: RawFile,
855 offset: i32,
856 ) -> Result<(), Error<D::Error>> {
857 let file_idx = self.get_file_by_id(file)?;
858 self.open_files[file_idx]
859 .seek_from_current(offset)
860 .map_err(|_| Error::InvalidOffset)?;
861 Ok(())
862 }
863
864 pub fn file_seek_from_end(
866 &mut self,
867 file: RawFile,
868 offset: u32,
869 ) -> Result<(), Error<D::Error>> {
870 let file_idx = self.get_file_by_id(file)?;
871 self.open_files[file_idx]
872 .seek_from_end(offset)
873 .map_err(|_| Error::InvalidOffset)?;
874 Ok(())
875 }
876
877 pub fn file_length(&self, file: RawFile) -> Result<u32, Error<D::Error>> {
879 let file_idx = self.get_file_by_id(file)?;
880 Ok(self.open_files[file_idx].length())
881 }
882
883 pub fn file_offset(&self, file: RawFile) -> Result<u32, Error<D::Error>> {
885 let file_idx = self.get_file_by_id(file)?;
886 Ok(self.open_files[file_idx].current_offset)
887 }
888
889 pub fn make_dir_in_dir<N>(
891 &mut self,
892 directory: RawDirectory,
893 name: N,
894 ) -> Result<(), Error<D::Error>>
895 where
896 N: ToShortFileName,
897 {
898 if self.open_dirs.is_full() {
900 return Err(Error::TooManyOpenDirs);
901 }
902
903 let parent_directory_idx = self.get_dir_by_id(directory)?;
904 let parent_directory_info = &self.open_dirs[parent_directory_idx];
905 let volume_id = self.open_dirs[parent_directory_idx].volume_id;
906 let volume_idx = self.get_volume_by_id(volume_id)?;
907 let volume_info = &self.open_volumes[volume_idx];
908 let sfn = name.to_short_filename().map_err(Error::FilenameError)?;
909
910 debug!("Creating directory '{}'", sfn);
911 debug!(
912 "Parent dir is in cluster {:?}",
913 parent_directory_info.cluster
914 );
915
916 let maybe_dir_entry = match &volume_info.volume_type {
918 VolumeType::Fat(fat) => {
919 fat.find_directory_entry(&self.block_device, parent_directory_info, &sfn)
920 }
921 };
922
923 match maybe_dir_entry {
924 Ok(entry) if entry.attributes.is_directory() => {
925 return Err(Error::DirAlreadyExists);
926 }
927 Ok(_entry) => {
928 return Err(Error::FileAlreadyExists);
929 }
930 Err(Error::NotFound) => {
931 }
933 Err(e) => {
934 return Err(e);
936 }
937 };
938
939 let att = Attributes::create_from_fat(Attributes::DIRECTORY);
940
941 match &mut self.open_volumes[volume_idx].volume_type {
943 VolumeType::Fat(fat) => {
944 debug!("Making dir entry");
945 let mut new_dir_entry_in_parent = fat.write_new_directory_entry(
946 &self.block_device,
947 &self.time_source,
948 parent_directory_info.cluster,
949 sfn,
950 att,
951 )?;
952 if new_dir_entry_in_parent.cluster == ClusterId::EMPTY {
953 new_dir_entry_in_parent.cluster =
954 fat.alloc_cluster(&self.block_device, None, false)?;
955 fat.write_entry_to_disk(&self.block_device, &new_dir_entry_in_parent)?;
957 }
958 let new_dir_start_block = fat.cluster_to_block(new_dir_entry_in_parent.cluster);
959 debug!("Made new dir entry {:?}", new_dir_entry_in_parent);
960 let now = self.time_source.get_timestamp();
961 let fat_type = fat.get_fat_type();
962 let mut blocks = [Block::new()];
964 let dot_entry_in_child = DirEntry {
966 name: crate::ShortFileName::this_dir(),
967 mtime: now,
968 ctime: now,
969 attributes: att,
970 cluster: new_dir_entry_in_parent.cluster,
972 size: 0,
973 entry_block: new_dir_start_block,
974 entry_offset: 0,
975 };
976 debug!("New dir has {:?}", dot_entry_in_child);
977 let mut offset = 0;
978 blocks[0][offset..offset + OnDiskDirEntry::LEN]
979 .copy_from_slice(&dot_entry_in_child.serialize(fat_type)[..]);
980 offset += OnDiskDirEntry::LEN;
981 let dot_dot_entry_in_child = DirEntry {
983 name: crate::ShortFileName::parent_dir(),
984 mtime: now,
985 ctime: now,
986 attributes: att,
987 cluster: {
989 if parent_directory_info.cluster == ClusterId::ROOT_DIR {
991 ClusterId::EMPTY
992 } else {
993 parent_directory_info.cluster
994 }
995 },
996 size: 0,
997 entry_block: new_dir_start_block,
998 entry_offset: OnDiskDirEntry::LEN_U32,
999 };
1000 debug!("New dir has {:?}", dot_dot_entry_in_child);
1001 blocks[0][offset..offset + OnDiskDirEntry::LEN]
1002 .copy_from_slice(&dot_dot_entry_in_child.serialize(fat_type)[..]);
1003
1004 self.block_device
1005 .write(&blocks, new_dir_start_block)
1006 .map_err(Error::DeviceError)?;
1007
1008 for b in blocks[0].iter_mut() {
1010 *b = 0;
1011 }
1012 for block in new_dir_start_block
1013 .range(BlockCount(u32::from(fat.blocks_per_cluster)))
1014 .skip(1)
1015 {
1016 self.block_device
1017 .write(&blocks, block)
1018 .map_err(Error::DeviceError)?;
1019 }
1020 }
1021 };
1022
1023 Ok(())
1024 }
1025
1026 fn get_volume_by_id(&self, volume: RawVolume) -> Result<usize, Error<D::Error>> {
1027 for (idx, v) in self.open_volumes.iter().enumerate() {
1028 if v.volume_id == volume {
1029 return Ok(idx);
1030 }
1031 }
1032 Err(Error::BadHandle)
1033 }
1034
1035 fn get_dir_by_id(&self, directory: RawDirectory) -> Result<usize, Error<D::Error>> {
1036 for (idx, d) in self.open_dirs.iter().enumerate() {
1037 if d.directory_id == directory {
1038 return Ok(idx);
1039 }
1040 }
1041 Err(Error::BadHandle)
1042 }
1043
1044 fn get_file_by_id(&self, file: RawFile) -> Result<usize, Error<D::Error>> {
1045 for (idx, f) in self.open_files.iter().enumerate() {
1046 if f.file_id == file {
1047 return Ok(idx);
1048 }
1049 }
1050 Err(Error::BadHandle)
1051 }
1052
1053 fn find_data_on_disk(
1063 &self,
1064 volume_idx: usize,
1065 start: &mut (u32, ClusterId),
1066 file_start: ClusterId,
1067 desired_offset: u32,
1068 ) -> Result<(BlockIdx, usize, usize), Error<D::Error>> {
1069 let bytes_per_cluster = match &self.open_volumes[volume_idx].volume_type {
1070 VolumeType::Fat(fat) => fat.bytes_per_cluster(),
1071 };
1072 if desired_offset < start.0 {
1074 start.0 = 0;
1077 start.1 = file_start;
1078 }
1079 let offset_from_cluster = desired_offset - start.0;
1081 let num_clusters = offset_from_cluster / bytes_per_cluster;
1083 let mut block_cache = BlockCache::empty();
1084 for _ in 0..num_clusters {
1085 start.1 = match &self.open_volumes[volume_idx].volume_type {
1086 VolumeType::Fat(fat) => {
1087 fat.next_cluster(&self.block_device, start.1, &mut block_cache)?
1088 }
1089 };
1090 start.0 += bytes_per_cluster;
1091 }
1092 let offset_from_cluster = desired_offset - start.0;
1094 assert!(offset_from_cluster < bytes_per_cluster);
1095 let num_blocks = BlockCount(offset_from_cluster / Block::LEN_U32);
1096 let block_idx = match &self.open_volumes[volume_idx].volume_type {
1097 VolumeType::Fat(fat) => fat.cluster_to_block(start.1),
1098 } + num_blocks;
1099 let block_offset = (desired_offset % Block::LEN_U32) as usize;
1100 let available = Block::LEN - block_offset;
1101 Ok((block_idx, block_offset, available))
1102 }
1103}
1104
1105fn solve_mode_variant(mode: Mode, dir_entry_is_some: bool) -> Mode {
1108 let mut mode = mode;
1109 if mode == Mode::ReadWriteCreateOrAppend {
1110 if dir_entry_is_some {
1111 mode = Mode::ReadWriteAppend;
1112 } else {
1113 mode = Mode::ReadWriteCreate;
1114 }
1115 } else if mode == Mode::ReadWriteCreateOrTruncate {
1116 if dir_entry_is_some {
1117 mode = Mode::ReadWriteTruncate;
1118 } else {
1119 mode = Mode::ReadWriteCreate;
1120 }
1121 }
1122 mode
1123}
1124
1125#[cfg(test)]
1132mod tests {
1133 use super::*;
1134 use crate::filesystem::SearchId;
1135 use crate::Timestamp;
1136
1137 struct DummyBlockDevice;
1138
1139 struct Clock;
1140
1141 #[derive(Debug)]
1142 enum Error {
1143 Unknown,
1144 }
1145
1146 impl TimeSource for Clock {
1147 fn get_timestamp(&self) -> Timestamp {
1148 Timestamp {
1150 year_since_1970: 0,
1151 zero_indexed_month: 0,
1152 zero_indexed_day: 0,
1153 hours: 0,
1154 minutes: 0,
1155 seconds: 0,
1156 }
1157 }
1158 }
1159
1160 impl BlockDevice for DummyBlockDevice {
1161 type Error = Error;
1162
1163 fn read(
1165 &self,
1166 blocks: &mut [Block],
1167 start_block_idx: BlockIdx,
1168 _reason: &str,
1169 ) -> Result<(), Self::Error> {
1170 static BLOCKS: [Block; 3] = [
1172 Block {
1173 contents: [
1174 0xfa, 0xb8, 0x00, 0x10, 0x8e, 0xd0, 0xbc, 0x00, 0xb0, 0xb8, 0x00, 0x00,
1175 0x8e, 0xd8, 0x8e, 0xc0, 0xfb, 0xbe, 0x00, 0x7c, 0xbf, 0x00, 0x06, 0xb9, 0x00, 0x02, 0xf3, 0xa4,
1177 0xea, 0x21, 0x06, 0x00, 0x00, 0xbe, 0xbe, 0x07, 0x38, 0x04, 0x75, 0x0b, 0x83, 0xc6, 0x10, 0x81,
1179 0xfe, 0xfe, 0x07, 0x75, 0xf3, 0xeb, 0x16, 0xb4, 0x02, 0xb0, 0x01, 0xbb, 0x00, 0x7c, 0xb2, 0x80,
1181 0x8a, 0x74, 0x01, 0x8b, 0x4c, 0x02, 0xcd, 0x13, 0xea, 0x00, 0x7c, 0x00, 0x00, 0xeb, 0xfe, 0x00,
1183 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1185 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1187 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1189 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1191 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1193 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1195 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1197 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1199 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1201 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1203 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1205 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1207 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1209 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1211 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1213 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1215 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1217 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1219 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1221 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1223 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1225 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1227 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xca, 0xde, 0x06,
1229 0x00, 0x00, 0x00, 0x04, 0x01, 0x04, 0x0c, 0xfe, 0xc2, 0xff, 0x01, 0x00, 0x00, 0x00, 0x33, 0x22,
1231 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1233 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1235 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1237 0x00, 0x00, 0x55, 0xaa, ],
1239 },
1240 Block {
1241 contents: [
1242 0xeb, 0x58, 0x90, 0x6d, 0x6b, 0x66, 0x73, 0x2e, 0x66, 0x61, 0x74, 0x00,
1243 0x02, 0x08, 0x20, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x10, 0x00, 0x04, 0x00,
1245 0x00, 0x08, 0x00, 0x00, 0x00, 0x20, 0x76, 0x00, 0x80, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1247 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1249 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x29, 0x0b, 0xa8, 0x89, 0x27, 0x50, 0x69, 0x63, 0x74, 0x75,
1251 0x72, 0x65, 0x73, 0x20, 0x20, 0x20, 0x46, 0x41, 0x54, 0x33, 0x32, 0x20, 0x20, 0x20, 0x0e, 0x1f,
1253 0xbe, 0x77, 0x7c, 0xac, 0x22, 0xc0, 0x74, 0x0b, 0x56, 0xb4, 0x0e, 0xbb, 0x07, 0x00, 0xcd, 0x10,
1255 0x5e, 0xeb, 0xf0, 0x32, 0xe4, 0xcd, 0x16, 0xcd, 0x19, 0xeb, 0xfe, 0x54, 0x68, 0x69, 0x73, 0x20,
1257 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c,
1259 0x65, 0x20, 0x64, 0x69, 0x73, 0x6b, 0x2e, 0x20, 0x20, 0x50, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20,
1261 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c,
1263 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x70, 0x70, 0x79, 0x20, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x70, 0x72,
1265 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x74,
1267 0x72, 0x79, 0x20, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x0d, 0x0a, 0x00,
1269 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1271 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1273 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1275 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1277 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1279 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1281 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1283 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1285 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1287 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1289 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1291 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1293 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1295 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1297 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1299 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1301 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1303 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1305 0x00, 0x00, 0x55, 0xaa, ],
1307 },
1308 Block {
1309 contents: hex!(
1310 "52 52 61 41 00 00 00 00 00 00 00 00 00 00 00 00
1311 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1312 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1313 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1314 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1315 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1316 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1317 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1318 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1319 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1320 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1321 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1322 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1323 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1324 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1325 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1326 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1327 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1328 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1329 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1330 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1331 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1332 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1333 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1334 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1335 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1336 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1337 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1338 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1339 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1340 00 00 00 00 72 72 41 61 FF FF FF FF FF FF FF FF
1341 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA"
1342 ),
1343 },
1344 ];
1345 println!(
1346 "Reading block {} to {}",
1347 start_block_idx.0,
1348 start_block_idx.0 as usize + blocks.len()
1349 );
1350 for (idx, block) in blocks.iter_mut().enumerate() {
1351 let block_idx = start_block_idx.0 as usize + idx;
1352 if block_idx < BLOCKS.len() {
1353 *block = BLOCKS[block_idx].clone();
1354 } else {
1355 return Err(Error::Unknown);
1356 }
1357 }
1358 Ok(())
1359 }
1360
1361 fn write(&self, _blocks: &[Block], _start_block_idx: BlockIdx) -> Result<(), Self::Error> {
1363 unimplemented!();
1364 }
1365
1366 fn num_blocks(&self) -> Result<BlockCount, Self::Error> {
1368 Ok(BlockCount(2))
1369 }
1370 }
1371
1372 #[test]
1373 fn partition0() {
1374 let mut c: VolumeManager<DummyBlockDevice, Clock, 2, 2> =
1375 VolumeManager::new_with_limits(DummyBlockDevice, Clock, 0xAA00_0000);
1376
1377 let v = c.open_raw_volume(VolumeIdx(0)).unwrap();
1378 let expected_id = RawVolume(SearchId(0xAA00_0000));
1379 assert_eq!(v, expected_id);
1380 assert_eq!(
1381 &c.open_volumes[0],
1382 &VolumeInfo {
1383 volume_id: expected_id,
1384 idx: VolumeIdx(0),
1385 volume_type: VolumeType::Fat(crate::FatVolume {
1386 lba_start: BlockIdx(1),
1387 num_blocks: BlockCount(0x0011_2233),
1388 blocks_per_cluster: 8,
1389 first_data_block: BlockCount(15136),
1390 fat_start: BlockCount(32),
1391 second_fat_start: Some(BlockCount(32 + 0x0000_1D80)),
1392 name: fat::VolumeName::new(*b"Pictures "),
1393 free_clusters_count: None,
1394 next_free_cluster: None,
1395 cluster_count: 965_788,
1396 fat_specific_info: fat::FatSpecificInfo::Fat32(fat::Fat32Info {
1397 first_root_dir_cluster: ClusterId(2),
1398 info_location: BlockIdx(1) + BlockCount(1),
1399 })
1400 })
1401 }
1402 );
1403 }
1404}
1405
1406