embedded_sdmmc_dev/
volume_mgr.rs

1//! The Volume Manager implementation.
2//!
3//! The volume manager handles partitions and open files on a block device.
4
5use 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/// Wraps a block device and gives access to the FAT-formatted volumes within
24/// it.
25///
26/// Tracks which files and directories are open, to prevent you from deleting
27/// a file or directory you currently have open.
28#[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    /// Create a new Volume Manager using a generic `BlockDevice`. From this
51    /// object we can open volumes (partitions) and with those we can open
52    /// files.
53    ///
54    /// This creates a `VolumeManager` with default values
55    /// MAX_DIRS = 4, MAX_FILES = 4, MAX_VOLUMES = 1. Call `VolumeManager::new_with_limits(block_device, time_source)`
56    /// if you need different limits.
57    pub fn new(block_device: D, time_source: T) -> VolumeManager<D, T, 4, 4, 1> {
58        // Pick a random starting point for the IDs that's not zero, because
59        // zero doesn't stand out in the logs.
60        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    /// Create a new Volume Manager using a generic `BlockDevice`. From this
72    /// object we can open volumes (partitions) and with those we can open
73    /// files.
74    ///
75    /// You can also give an offset for all the IDs this volume manager
76    /// generates, which might help you find the IDs in your logs when
77    /// debugging.
78    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    /// Temporarily get access to the underlying block device.
97    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    /// Get a volume (or partition) based on entries in the Master Boot Record.
107    ///
108    /// We do not support GUID Partition Table disks. Nor do we support any
109    /// concept of drive letters - that is for a higher layer to handle.
110    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    /// Get a volume (or partition) based on entries in the Master Boot Record.
119    ///
120    /// We do not support GUID Partition Table disks. Nor do we support any
121    /// concept of drive letters - that is for a higher layer to handle.
122    ///
123    /// This function gives you a `RawVolume` and you must close the volume by
124    /// calling `VolumeManager::close_volume`.
125    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            // We only support Master Boot Record (MBR) partitioned cards, not
157            // GUID Partition Table (GPT)
158            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            // Only 0x80 and 0x00 are valid (bootable, and non-bootable)
179            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                // We already checked for space
208                data.open_volumes.push(info).unwrap();
209                Ok(id)
210            }
211            _ => Err(Error::FormatError("Partition type not supported")),
212        }
213    }
214
215    /// Open the volume's root directory.
216    ///
217    /// You can then read the directory entries with `iterate_dir`, or you can
218    /// use `open_file_in_dir`.
219    pub fn open_root_dir(&self, volume: RawVolume) -> Result<RawDirectory, Error<D::Error>> {
220        debug!("Opening root on {:?}", volume);
221
222        // Opening a root directory twice is OK
223        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    /// Open a directory.
242    ///
243    /// You can then read the directory entries with `iterate_dir` and `open_file_in_dir`.
244    ///
245    /// Passing "." as the name results in opening the `parent_dir` a second time.
246    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        // Find dir by ID
262        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        // Open the directory
267
268        // Should we short-cut? (root dir doesn't have ".")
269        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        // ok we'll actually look for the directory then
285
286        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        // We don't check if the directory is already open - directories hold
301        // no cached state and so opening a directory twice is allowable.
302
303        // Remember this open directory.
304        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    /// Close a directory. You cannot perform operations on an open directory
319    /// and so must close it if you want to do something with it.
320    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    /// Close a volume
334    ///
335    /// You can't close it if there are any files or directories open on it.
336    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    /// Look in a directory for a named file.
366    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    /// Call a callback function for each directory entry in a directory.
392    ///
393    /// Long File Names will be ignored.
394    ///
395    /// <div class="warning">
396    ///
397    /// Do not attempt to call any methods on the VolumeManager or any of its
398    /// handles from inside the callback. You will get a lock error because the
399    /// object is already locked in order to do the iteration.
400    ///
401    /// </div>
402    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                        // Hide all the LFN directory entries
422                        if !de.attributes.is_lfn() {
423                            func(de);
424                        }
425                    },
426                )
427            }
428        }
429    }
430
431    /// Call a callback function for each directory entry in a directory, and
432    /// process Long File Names.
433    ///
434    /// You must supply a [`LfnBuffer`] this API can use to temporarily hold the
435    /// Long File Name. If you pass one that isn't large enough, any Long File
436    /// Names that don't fit will be ignored and presented as if they only had a
437    /// Short File Name.
438    ///
439    /// <div class="warning">
440    ///
441    /// Do not attempt to call any methods on the VolumeManager or any of its
442    /// handles from inside the callback. You will get a lock error because the
443    /// object is already locked in order to do the iteration.
444    ///
445    /// </div>
446    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                // This API doesn't care about the on-disk directory entry, so we discard it
464                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    /// Open a file with the given full path. A file can only be opened once.
475    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        // This check is load-bearing - we do an unchecked push later.
488        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                // we are opening an existing file
509                Some(entry)
510            }
511            Err(_)
512                if (mode == Mode::ReadWriteCreate)
513                    | (mode == Mode::ReadWriteCreateOrTruncate)
514                    | (mode == Mode::ReadWriteCreateOrAppend) =>
515            {
516                // We are opening a non-existant file, but that's OK because they
517                // asked us to create it
518                None
519            }
520            _ => {
521                // We are opening a non-existant file, and that's not OK.
522                return Err(Error::NotFound);
523            }
524        };
525
526        // Check if it's open already
527        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                // Remember this open file - can't be full as we checked already
566                unsafe {
567                    data.open_files.push_unchecked(file);
568                }
569
570                Ok(file_id)
571            }
572            _ => {
573                // Safe to unwrap, since we actually have an entry if we got here
574                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                // Check it's not already open
585                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                        // seek_from_end with 0 can't fail
613                        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                // Remember this open file - can't be full as we checked already
646                unsafe {
647                    data.open_files.push_unchecked(file);
648                }
649
650                Ok(raw_file)
651            }
652        }
653    }
654
655    /// Delete a closed file with the given filename, if it exists.
656    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    /// Get the volume label
695    ///
696    /// Will look in the BPB for a volume label, and if nothing is found, will
697    /// search the root directory for a volume label.
698    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        // prefer the one in the BPB - it's easier to get
704        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        // Nothing in the BPB, let's do it the slow way
720        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    /// Read from an open file.
739    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        // Calculate which file block the current offset lies within
747        // While there is more to read, read the block and copy in to the buffer.
748        // If we need to find the next cluster, walk the FAT.
749        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    /// Write to a open file.
781    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        // Clone this so we can touch our other structures. Need to ensure we
792        // write it back at the end.
793        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            // file doesn't have a valid allocated cluster (possible zero-length file), allocate one
804            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        // Clone this so we can touch our other structures.
817        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                // We made it longer
900                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            // Entry update deferred to file close, for performance.
906        }
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    /// Close a file with the given raw file handle.
913    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    /// Flush (update the entry) for a file with the given raw file handle.
922    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                        // If you have a length, you must have a cluster
937                        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    /// Check if any files or folders are open.
950    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    /// Consume self and return BlockDevice and TimeSource
956    pub fn free(self) -> (D, T) {
957        let data = self.data.into_inner();
958        (data.block_cache.free(), self.time_source)
959    }
960
961    /// Check if a file is at End Of File.
962    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    /// Seek a file with an offset from the start of the file.
969    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    /// Seek a file with an offset from the current position.
979    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    /// Seek a file with an offset back from the end of the file.
993    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    /// Get the length of a file
1003    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    /// Get the current offset of a file
1010    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    /// Create a directory in a given directory.
1017    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        // This check is load-bearing - we do an unchecked push later.
1029        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        // Does an entry exist with this name?
1047        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                // perfect, let's make it
1062            }
1063            Err(e) => {
1064                // Some other error - tell them about it
1065                return Err(e);
1066            }
1067        };
1068
1069        let att = Attributes::create_from_fat(Attributes::DIRECTORY);
1070
1071        // Need mutable access for this
1072        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/// The mutable data the VolumeManager needs to hold
1090///
1091/// Kept separate so its easier to wrap it in a RefCell
1092#[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    /// Check if a file is open
1115    ///
1116    /// Returns `true` if it's open, `false`, otherwise.
1117    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    /// This function turns `desired_offset` into an appropriate block to be
1166    /// read. It either calculates this based on the start of the file, or
1167    /// from the given start point - whichever is better.
1168    ///
1169    /// Returns:
1170    ///
1171    /// * the index for the block on the disk that contains the data we want,
1172    /// * the byte offset into that block for the data we want, and
1173    /// * how many bytes remain in that block.
1174    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        // do we need to be before our start point?
1188        if desired_offset < start.0 {
1189            // user wants to go backwards - start from the beginning of the file
1190            // because the FAT is only a singly-linked list.
1191            start.0 = 0;
1192            start.1 = file_start;
1193        }
1194        // How many clusters forward do we need to go?
1195        let offset_from_cluster = desired_offset - start.0;
1196        // walk through the FAT chain
1197        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        // How many blocks in are we now?
1205        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
1217/// Transform mode variants (ReadWriteCreate_Or_Append) to simple modes ReadWriteAppend or
1218/// ReadWriteCreate
1219fn 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// ****************************************************************************
1238//
1239// Unit Tests
1240//
1241// ****************************************************************************
1242
1243#[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            // TODO: Return actual time
1261            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        /// Read one or more blocks, starting at the given block index.
1276        fn read(&self, blocks: &mut [Block], start_block_idx: BlockIdx) -> Result<(), Self::Error> {
1277            // Actual blocks taken from an SD card, except I've changed the start and length of partition 0.
1278            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, // 0x000
1283                        0xfb, 0xbe, 0x00, 0x7c, 0xbf, 0x00, 0x06, 0xb9, 0x00, 0x02, 0xf3, 0xa4,
1284                        0xea, 0x21, 0x06, 0x00, // 0x010
1285                        0x00, 0xbe, 0xbe, 0x07, 0x38, 0x04, 0x75, 0x0b, 0x83, 0xc6, 0x10, 0x81,
1286                        0xfe, 0xfe, 0x07, 0x75, // 0x020
1287                        0xf3, 0xeb, 0x16, 0xb4, 0x02, 0xb0, 0x01, 0xbb, 0x00, 0x7c, 0xb2, 0x80,
1288                        0x8a, 0x74, 0x01, 0x8b, // 0x030
1289                        0x4c, 0x02, 0xcd, 0x13, 0xea, 0x00, 0x7c, 0x00, 0x00, 0xeb, 0xfe, 0x00,
1290                        0x00, 0x00, 0x00, 0x00, // 0x040
1291                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1292                        0x00, 0x00, 0x00, 0x00, // 0x050
1293                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1294                        0x00, 0x00, 0x00, 0x00, // 0x060
1295                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1296                        0x00, 0x00, 0x00, 0x00, // 0x070
1297                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1298                        0x00, 0x00, 0x00, 0x00, // 0x080
1299                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1300                        0x00, 0x00, 0x00, 0x00, // 0x090
1301                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1302                        0x00, 0x00, 0x00, 0x00, // 0x0A0
1303                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1304                        0x00, 0x00, 0x00, 0x00, // 0x0B0
1305                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1306                        0x00, 0x00, 0x00, 0x00, // 0x0C0
1307                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1308                        0x00, 0x00, 0x00, 0x00, // 0x0D0
1309                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1310                        0x00, 0x00, 0x00, 0x00, // 0x0E0
1311                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1312                        0x00, 0x00, 0x00, 0x00, // 0x0F0
1313                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1314                        0x00, 0x00, 0x00, 0x00, // 0x100
1315                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1316                        0x00, 0x00, 0x00, 0x00, // 0x110
1317                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1318                        0x00, 0x00, 0x00, 0x00, // 0x120
1319                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1320                        0x00, 0x00, 0x00, 0x00, // 0x130
1321                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1322                        0x00, 0x00, 0x00, 0x00, // 0x140
1323                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1324                        0x00, 0x00, 0x00, 0x00, // 0x150
1325                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1326                        0x00, 0x00, 0x00, 0x00, // 0x160
1327                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1328                        0x00, 0x00, 0x00, 0x00, // 0x170
1329                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1330                        0x00, 0x00, 0x00, 0x00, // 0x180
1331                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1332                        0x00, 0x00, 0x00, 0x00, // 0x190
1333                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1334                        0x00, 0x00, 0x00, 0x00, // 0x1A0
1335                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xca, 0xde, 0x06,
1336                        0x00, 0x00, 0x00, 0x04, // 0x1B0
1337                        0x01, 0x04, 0x0c, 0xfe, 0xc2, 0xff, 0x01, 0x00, 0x00, 0x00, 0x33, 0x22,
1338                        0x11, 0x00, 0x00, 0x00, // 0x1C0
1339                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1340                        0x00, 0x00, 0x00, 0x00, // 0x1D0
1341                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1342                        0x00, 0x00, 0x00, 0x00, // 0x1E0
1343                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1344                        0x00, 0x00, 0x55, 0xaa, // 0x1F0
1345                    ],
1346                },
1347                Block {
1348                    contents: [
1349                        0xeb, 0x58, 0x90, 0x6d, 0x6b, 0x66, 0x73, 0x2e, 0x66, 0x61, 0x74, 0x00,
1350                        0x02, 0x08, 0x20, 0x00, // 0x000
1351                        0x02, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x10, 0x00, 0x04, 0x00,
1352                        0x00, 0x08, 0x00, 0x00, // 0x010
1353                        0x00, 0x20, 0x76, 0x00, 0x80, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1354                        0x02, 0x00, 0x00, 0x00, // 0x020
1355                        0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1356                        0x00, 0x00, 0x00, 0x00, // 0x030
1357                        0x80, 0x01, 0x29, 0x0b, 0xa8, 0x89, 0x27, 0x50, 0x69, 0x63, 0x74, 0x75,
1358                        0x72, 0x65, 0x73, 0x20, // 0x040
1359                        0x20, 0x20, 0x46, 0x41, 0x54, 0x33, 0x32, 0x20, 0x20, 0x20, 0x0e, 0x1f,
1360                        0xbe, 0x77, 0x7c, 0xac, // 0x050
1361                        0x22, 0xc0, 0x74, 0x0b, 0x56, 0xb4, 0x0e, 0xbb, 0x07, 0x00, 0xcd, 0x10,
1362                        0x5e, 0xeb, 0xf0, 0x32, // 0x060
1363                        0xe4, 0xcd, 0x16, 0xcd, 0x19, 0xeb, 0xfe, 0x54, 0x68, 0x69, 0x73, 0x20,
1364                        0x69, 0x73, 0x20, 0x6e, // 0x070
1365                        0x6f, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c,
1366                        0x65, 0x20, 0x64, 0x69, // 0x080
1367                        0x73, 0x6b, 0x2e, 0x20, 0x20, 0x50, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20,
1368                        0x69, 0x6e, 0x73, 0x65, // 0x090
1369                        0x72, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c,
1370                        0x65, 0x20, 0x66, 0x6c, // 0x0A0
1371                        0x6f, 0x70, 0x70, 0x79, 0x20, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x70, 0x72,
1372                        0x65, 0x73, 0x73, 0x20, // 0x0B0
1373                        0x61, 0x6e, 0x79, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x74,
1374                        0x72, 0x79, 0x20, 0x61, // 0x0C0
1375                        0x67, 0x61, 0x69, 0x6e, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x0d, 0x0a, 0x00,
1376                        0x00, 0x00, 0x00, 0x00, // 0x0D0
1377                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1378                        0x00, 0x00, 0x00, 0x00, // 0x0E0
1379                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1380                        0x00, 0x00, 0x00, 0x00, // 0x0F0
1381                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1382                        0x00, 0x00, 0x00, 0x00, // 0x100
1383                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1384                        0x00, 0x00, 0x00, 0x00, // 0x110
1385                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1386                        0x00, 0x00, 0x00, 0x00, // 0x120
1387                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1388                        0x00, 0x00, 0x00, 0x00, // 0x130
1389                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1390                        0x00, 0x00, 0x00, 0x00, // 0x140
1391                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1392                        0x00, 0x00, 0x00, 0x00, // 0x150
1393                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1394                        0x00, 0x00, 0x00, 0x00, // 0x160
1395                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1396                        0x00, 0x00, 0x00, 0x00, // 0x170
1397                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1398                        0x00, 0x00, 0x00, 0x00, // 0x180
1399                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1400                        0x00, 0x00, 0x00, 0x00, // 0x190
1401                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1402                        0x00, 0x00, 0x00, 0x00, // 0x1A0
1403                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1404                        0x00, 0x00, 0x00, 0x00, // 0x1B0
1405                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1406                        0x00, 0x00, 0x00, 0x00, // 0x1C0
1407                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1408                        0x00, 0x00, 0x00, 0x00, // 0x1D0
1409                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1410                        0x00, 0x00, 0x00, 0x00, // 0x1E0
1411                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1412                        0x00, 0x00, 0x55, 0xaa, // 0x1F0
1413                    ],
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        /// Write one or more blocks, starting at the given block index.
1469        fn write(&self, _blocks: &[Block], _start_block_idx: BlockIdx) -> Result<(), Self::Error> {
1470            unimplemented!();
1471        }
1472
1473        /// Determine how many blocks this device can hold.
1474        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// ****************************************************************************
1514//
1515// End Of File
1516//
1517// ****************************************************************************