embedded_sdmmc/
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) && (to_copy == block_avail) {
882                // we're replacing the whole Block, so the previous contents
883                // are irrelevant
884                data.block_cache.blank_mut(block_idx)
885            } else {
886                debug!("Reading for partial block write");
887                data.block_cache
888                    .read_mut(block_idx)
889                    .map_err(Error::DeviceError)?
890            };
891            block[block_offset..block_offset + to_copy]
892                .copy_from_slice(&buffer[written..written + to_copy]);
893            debug!("Writing block {:?}", block_idx);
894            data.block_cache.write_back()?;
895            written += to_copy;
896            data.open_files[file_idx].current_cluster = current_cluster;
897
898            let to_copy = to_copy as u32;
899            let new_offset = data.open_files[file_idx].current_offset + to_copy;
900            if new_offset > data.open_files[file_idx].entry.size {
901                // We made it longer
902                data.open_files[file_idx].update_length(new_offset);
903            }
904            data.open_files[file_idx]
905                .seek_from_start(new_offset)
906                .unwrap();
907            // Entry update deferred to file close, for performance.
908        }
909        data.open_files[file_idx].entry.attributes.set_archive(true);
910        data.open_files[file_idx].entry.mtime = self.time_source.get_timestamp();
911        Ok(())
912    }
913
914    /// Close a file with the given raw file handle.
915    pub fn close_file(&self, file: RawFile) -> Result<(), Error<D::Error>> {
916        let flush_result = self.flush_file(file);
917        let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
918        let file_idx = data.get_file_by_id(file)?;
919        data.open_files.swap_remove(file_idx);
920        flush_result
921    }
922
923    /// Flush (update the entry) for a file with the given raw file handle.
924    pub fn flush_file(&self, file: RawFile) -> Result<(), Error<D::Error>> {
925        let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
926        let data = data.deref_mut();
927
928        let file_id = data.get_file_by_id(file)?;
929
930        if data.open_files[file_id].dirty {
931            let volume_idx = data.get_volume_by_id(data.open_files[file_id].raw_volume)?;
932            match &mut data.open_volumes[volume_idx].volume_type {
933                VolumeType::Fat(fat) => {
934                    debug!("Updating FAT info sector");
935                    fat.update_info_sector(&mut data.block_cache)?;
936                    debug!("Updating dir entry {:?}", data.open_files[file_id].entry);
937                    if data.open_files[file_id].entry.size != 0 {
938                        // If you have a length, you must have a cluster
939                        assert!(data.open_files[file_id].entry.cluster.0 != 0);
940                    }
941                    fat.write_entry_to_disk(
942                        &mut data.block_cache,
943                        &data.open_files[file_id].entry,
944                    )?;
945                }
946            };
947        }
948        Ok(())
949    }
950
951    /// Check if any files or folders are open.
952    pub fn has_open_handles(&self) -> bool {
953        let data = self.data.borrow();
954        !(data.open_dirs.is_empty() || data.open_files.is_empty())
955    }
956
957    /// Consume self and return BlockDevice and TimeSource
958    pub fn free(self) -> (D, T) {
959        let data = self.data.into_inner();
960        (data.block_cache.free(), self.time_source)
961    }
962
963    /// Check if a file is at End Of File.
964    pub fn file_eof(&self, file: RawFile) -> Result<bool, Error<D::Error>> {
965        let data = self.data.try_borrow().map_err(|_| Error::LockError)?;
966        let file_idx = data.get_file_by_id(file)?;
967        Ok(data.open_files[file_idx].eof())
968    }
969
970    /// Seek a file with an offset from the start of the file.
971    pub fn file_seek_from_start(&self, file: RawFile, offset: u32) -> Result<(), Error<D::Error>> {
972        let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
973        let file_idx = data.get_file_by_id(file)?;
974        data.open_files[file_idx]
975            .seek_from_start(offset)
976            .map_err(|_| Error::InvalidOffset)?;
977        Ok(())
978    }
979
980    /// Seek a file with an offset from the current position.
981    pub fn file_seek_from_current(
982        &self,
983        file: RawFile,
984        offset: i32,
985    ) -> Result<(), Error<D::Error>> {
986        let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
987        let file_idx = data.get_file_by_id(file)?;
988        data.open_files[file_idx]
989            .seek_from_current(offset)
990            .map_err(|_| Error::InvalidOffset)?;
991        Ok(())
992    }
993
994    /// Seek a file with an offset back from the end of the file.
995    pub fn file_seek_from_end(&self, file: RawFile, offset: u32) -> Result<(), Error<D::Error>> {
996        let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
997        let file_idx = data.get_file_by_id(file)?;
998        data.open_files[file_idx]
999            .seek_from_end(offset)
1000            .map_err(|_| Error::InvalidOffset)?;
1001        Ok(())
1002    }
1003
1004    /// Get the length of a file
1005    pub fn file_length(&self, file: RawFile) -> Result<u32, Error<D::Error>> {
1006        let data = self.data.try_borrow().map_err(|_| Error::LockError)?;
1007        let file_idx = data.get_file_by_id(file)?;
1008        Ok(data.open_files[file_idx].length())
1009    }
1010
1011    /// Get the current offset of a file
1012    pub fn file_offset(&self, file: RawFile) -> Result<u32, Error<D::Error>> {
1013        let data = self.data.try_borrow().map_err(|_| Error::LockError)?;
1014        let file_idx = data.get_file_by_id(file)?;
1015        Ok(data.open_files[file_idx].current_offset)
1016    }
1017
1018    /// Create a directory in a given directory.
1019    pub fn make_dir_in_dir<N>(
1020        &self,
1021        directory: RawDirectory,
1022        name: N,
1023    ) -> Result<(), Error<D::Error>>
1024    where
1025        N: ToShortFileName,
1026    {
1027        let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
1028        let data = data.deref_mut();
1029
1030        // This check is load-bearing - we do an unchecked push later.
1031        if data.open_dirs.is_full() {
1032            return Err(Error::TooManyOpenDirs);
1033        }
1034
1035        let parent_directory_idx = data.get_dir_by_id(directory)?;
1036        let parent_directory_info = &data.open_dirs[parent_directory_idx];
1037        let volume_id = data.open_dirs[parent_directory_idx].raw_volume;
1038        let volume_idx = data.get_volume_by_id(volume_id)?;
1039        let volume_info = &data.open_volumes[volume_idx];
1040        let sfn = name.to_short_filename().map_err(Error::FilenameError)?;
1041
1042        debug!("Creating directory '{}'", sfn);
1043        debug!(
1044            "Parent dir is in cluster {:?}",
1045            parent_directory_info.cluster
1046        );
1047
1048        // Does an entry exist with this name?
1049        let maybe_dir_entry = match &volume_info.volume_type {
1050            VolumeType::Fat(fat) => {
1051                fat.find_directory_entry(&mut data.block_cache, parent_directory_info, &sfn)
1052            }
1053        };
1054
1055        match maybe_dir_entry {
1056            Ok(entry) if entry.attributes.is_directory() => {
1057                return Err(Error::DirAlreadyExists);
1058            }
1059            Ok(_entry) => {
1060                return Err(Error::FileAlreadyExists);
1061            }
1062            Err(Error::NotFound) => {
1063                // perfect, let's make it
1064            }
1065            Err(e) => {
1066                // Some other error - tell them about it
1067                return Err(e);
1068            }
1069        };
1070
1071        let att = Attributes::create_from_fat(Attributes::DIRECTORY);
1072
1073        // Need mutable access for this
1074        match &mut data.open_volumes[volume_idx].volume_type {
1075            VolumeType::Fat(fat) => {
1076                debug!("Making dir entry");
1077                fat.make_dir(
1078                    &mut data.block_cache,
1079                    &self.time_source,
1080                    parent_directory_info.cluster,
1081                    sfn,
1082                    att,
1083                )?;
1084            }
1085        };
1086
1087        Ok(())
1088    }
1089}
1090
1091/// The mutable data the VolumeManager needs to hold
1092///
1093/// Kept separate so its easier to wrap it in a RefCell
1094#[derive(Debug)]
1095
1096struct VolumeManagerData<
1097    D,
1098    const MAX_DIRS: usize = 4,
1099    const MAX_FILES: usize = 4,
1100    const MAX_VOLUMES: usize = 1,
1101> where
1102    D: BlockDevice,
1103{
1104    id_generator: HandleGenerator,
1105    block_cache: BlockCache<D>,
1106    open_volumes: Vec<VolumeInfo, MAX_VOLUMES>,
1107    open_dirs: Vec<DirectoryInfo, MAX_DIRS>,
1108    open_files: Vec<FileInfo, MAX_FILES>,
1109}
1110
1111impl<D, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
1112    VolumeManagerData<D, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
1113where
1114    D: BlockDevice,
1115{
1116    /// Check if a file is open
1117    ///
1118    /// Returns `true` if it's open, `false`, otherwise.
1119    fn file_is_open(&self, raw_volume: RawVolume, dir_entry: &DirEntry) -> bool {
1120        for f in self.open_files.iter() {
1121            if f.raw_volume == raw_volume
1122                && f.entry.entry_block == dir_entry.entry_block
1123                && f.entry.entry_offset == dir_entry.entry_offset
1124            {
1125                return true;
1126            }
1127        }
1128        false
1129    }
1130
1131    fn get_volume_by_id<E>(&self, raw_volume: RawVolume) -> Result<usize, Error<E>>
1132    where
1133        E: core::fmt::Debug,
1134    {
1135        for (idx, v) in self.open_volumes.iter().enumerate() {
1136            if v.raw_volume == raw_volume {
1137                return Ok(idx);
1138            }
1139        }
1140        Err(Error::BadHandle)
1141    }
1142
1143    fn get_dir_by_id<E>(&self, raw_directory: RawDirectory) -> Result<usize, Error<E>>
1144    where
1145        E: core::fmt::Debug,
1146    {
1147        for (idx, d) in self.open_dirs.iter().enumerate() {
1148            if d.raw_directory == raw_directory {
1149                return Ok(idx);
1150            }
1151        }
1152        Err(Error::BadHandle)
1153    }
1154
1155    fn get_file_by_id<E>(&self, raw_file: RawFile) -> Result<usize, Error<E>>
1156    where
1157        E: core::fmt::Debug,
1158    {
1159        for (idx, f) in self.open_files.iter().enumerate() {
1160            if f.raw_file == raw_file {
1161                return Ok(idx);
1162            }
1163        }
1164        Err(Error::BadHandle)
1165    }
1166
1167    /// This function turns `desired_offset` into an appropriate block to be
1168    /// read. It either calculates this based on the start of the file, or
1169    /// from the given start point - whichever is better.
1170    ///
1171    /// Returns:
1172    ///
1173    /// * the index for the block on the disk that contains the data we want,
1174    /// * the byte offset into that block for the data we want, and
1175    /// * how many bytes remain in that block.
1176    fn find_data_on_disk(
1177        &mut self,
1178        volume_idx: usize,
1179        start: &mut (u32, ClusterId),
1180        file_start: ClusterId,
1181        desired_offset: u32,
1182    ) -> Result<(BlockIdx, usize, usize), Error<D::Error>>
1183    where
1184        D: BlockDevice,
1185    {
1186        let bytes_per_cluster = match &self.open_volumes[volume_idx].volume_type {
1187            VolumeType::Fat(fat) => fat.bytes_per_cluster(),
1188        };
1189        // do we need to be before our start point?
1190        if desired_offset < start.0 {
1191            // user wants to go backwards - start from the beginning of the file
1192            // because the FAT is only a singly-linked list.
1193            start.0 = 0;
1194            start.1 = file_start;
1195        }
1196        // How many clusters forward do we need to go?
1197        let offset_from_cluster = desired_offset - start.0;
1198        // walk through the FAT chain
1199        let num_clusters = offset_from_cluster / bytes_per_cluster;
1200        for _ in 0..num_clusters {
1201            start.1 = match &self.open_volumes[volume_idx].volume_type {
1202                VolumeType::Fat(fat) => fat.next_cluster(&mut self.block_cache, start.1)?,
1203            };
1204            start.0 += bytes_per_cluster;
1205        }
1206        // How many blocks in are we now?
1207        let offset_from_cluster = desired_offset - start.0;
1208        assert!(offset_from_cluster < bytes_per_cluster);
1209        let num_blocks = BlockCount(offset_from_cluster / Block::LEN_U32);
1210        let block_idx = match &self.open_volumes[volume_idx].volume_type {
1211            VolumeType::Fat(fat) => fat.cluster_to_block(start.1),
1212        } + num_blocks;
1213        let block_offset = (desired_offset % Block::LEN_U32) as usize;
1214        let available = Block::LEN - block_offset;
1215        Ok((block_idx, block_offset, available))
1216    }
1217}
1218
1219/// Transform mode variants (ReadWriteCreate_Or_Append) to simple modes ReadWriteAppend or
1220/// ReadWriteCreate
1221fn solve_mode_variant(mode: Mode, dir_entry_is_some: bool) -> Mode {
1222    let mut mode = mode;
1223    if mode == Mode::ReadWriteCreateOrAppend {
1224        if dir_entry_is_some {
1225            mode = Mode::ReadWriteAppend;
1226        } else {
1227            mode = Mode::ReadWriteCreate;
1228        }
1229    } else if mode == Mode::ReadWriteCreateOrTruncate {
1230        if dir_entry_is_some {
1231            mode = Mode::ReadWriteTruncate;
1232        } else {
1233            mode = Mode::ReadWriteCreate;
1234        }
1235    }
1236    mode
1237}
1238
1239// ****************************************************************************
1240//
1241// Unit Tests
1242//
1243// ****************************************************************************
1244
1245#[cfg(test)]
1246mod tests {
1247    use super::*;
1248    use crate::filesystem::Handle;
1249    use crate::Timestamp;
1250
1251    struct DummyBlockDevice;
1252
1253    struct Clock;
1254
1255    #[derive(Debug)]
1256    enum Error {
1257        Unknown,
1258    }
1259
1260    impl TimeSource for Clock {
1261        fn get_timestamp(&self) -> Timestamp {
1262            // TODO: Return actual time
1263            Timestamp {
1264                year_since_1970: 0,
1265                zero_indexed_month: 0,
1266                zero_indexed_day: 0,
1267                hours: 0,
1268                minutes: 0,
1269                seconds: 0,
1270            }
1271        }
1272    }
1273
1274    impl BlockDevice for DummyBlockDevice {
1275        type Error = Error;
1276
1277        /// Read one or more blocks, starting at the given block index.
1278        fn read(&self, blocks: &mut [Block], start_block_idx: BlockIdx) -> Result<(), Self::Error> {
1279            // Actual blocks taken from an SD card, except I've changed the start and length of partition 0.
1280            static BLOCKS: [Block; 3] = [
1281                Block {
1282                    contents: [
1283                        0xfa, 0xb8, 0x00, 0x10, 0x8e, 0xd0, 0xbc, 0x00, 0xb0, 0xb8, 0x00, 0x00,
1284                        0x8e, 0xd8, 0x8e, 0xc0, // 0x000
1285                        0xfb, 0xbe, 0x00, 0x7c, 0xbf, 0x00, 0x06, 0xb9, 0x00, 0x02, 0xf3, 0xa4,
1286                        0xea, 0x21, 0x06, 0x00, // 0x010
1287                        0x00, 0xbe, 0xbe, 0x07, 0x38, 0x04, 0x75, 0x0b, 0x83, 0xc6, 0x10, 0x81,
1288                        0xfe, 0xfe, 0x07, 0x75, // 0x020
1289                        0xf3, 0xeb, 0x16, 0xb4, 0x02, 0xb0, 0x01, 0xbb, 0x00, 0x7c, 0xb2, 0x80,
1290                        0x8a, 0x74, 0x01, 0x8b, // 0x030
1291                        0x4c, 0x02, 0xcd, 0x13, 0xea, 0x00, 0x7c, 0x00, 0x00, 0xeb, 0xfe, 0x00,
1292                        0x00, 0x00, 0x00, 0x00, // 0x040
1293                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1294                        0x00, 0x00, 0x00, 0x00, // 0x050
1295                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1296                        0x00, 0x00, 0x00, 0x00, // 0x060
1297                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1298                        0x00, 0x00, 0x00, 0x00, // 0x070
1299                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1300                        0x00, 0x00, 0x00, 0x00, // 0x080
1301                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1302                        0x00, 0x00, 0x00, 0x00, // 0x090
1303                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1304                        0x00, 0x00, 0x00, 0x00, // 0x0A0
1305                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1306                        0x00, 0x00, 0x00, 0x00, // 0x0B0
1307                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1308                        0x00, 0x00, 0x00, 0x00, // 0x0C0
1309                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1310                        0x00, 0x00, 0x00, 0x00, // 0x0D0
1311                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1312                        0x00, 0x00, 0x00, 0x00, // 0x0E0
1313                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1314                        0x00, 0x00, 0x00, 0x00, // 0x0F0
1315                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1316                        0x00, 0x00, 0x00, 0x00, // 0x100
1317                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1318                        0x00, 0x00, 0x00, 0x00, // 0x110
1319                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1320                        0x00, 0x00, 0x00, 0x00, // 0x120
1321                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1322                        0x00, 0x00, 0x00, 0x00, // 0x130
1323                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1324                        0x00, 0x00, 0x00, 0x00, // 0x140
1325                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1326                        0x00, 0x00, 0x00, 0x00, // 0x150
1327                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1328                        0x00, 0x00, 0x00, 0x00, // 0x160
1329                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1330                        0x00, 0x00, 0x00, 0x00, // 0x170
1331                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1332                        0x00, 0x00, 0x00, 0x00, // 0x180
1333                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1334                        0x00, 0x00, 0x00, 0x00, // 0x190
1335                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1336                        0x00, 0x00, 0x00, 0x00, // 0x1A0
1337                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xca, 0xde, 0x06,
1338                        0x00, 0x00, 0x00, 0x04, // 0x1B0
1339                        0x01, 0x04, 0x0c, 0xfe, 0xc2, 0xff, 0x01, 0x00, 0x00, 0x00, 0x33, 0x22,
1340                        0x11, 0x00, 0x00, 0x00, // 0x1C0
1341                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1342                        0x00, 0x00, 0x00, 0x00, // 0x1D0
1343                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1344                        0x00, 0x00, 0x00, 0x00, // 0x1E0
1345                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1346                        0x00, 0x00, 0x55, 0xaa, // 0x1F0
1347                    ],
1348                },
1349                Block {
1350                    contents: [
1351                        0xeb, 0x58, 0x90, 0x6d, 0x6b, 0x66, 0x73, 0x2e, 0x66, 0x61, 0x74, 0x00,
1352                        0x02, 0x08, 0x20, 0x00, // 0x000
1353                        0x02, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x10, 0x00, 0x04, 0x00,
1354                        0x00, 0x08, 0x00, 0x00, // 0x010
1355                        0x00, 0x20, 0x76, 0x00, 0x80, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1356                        0x02, 0x00, 0x00, 0x00, // 0x020
1357                        0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1358                        0x00, 0x00, 0x00, 0x00, // 0x030
1359                        0x80, 0x01, 0x29, 0x0b, 0xa8, 0x89, 0x27, 0x50, 0x69, 0x63, 0x74, 0x75,
1360                        0x72, 0x65, 0x73, 0x20, // 0x040
1361                        0x20, 0x20, 0x46, 0x41, 0x54, 0x33, 0x32, 0x20, 0x20, 0x20, 0x0e, 0x1f,
1362                        0xbe, 0x77, 0x7c, 0xac, // 0x050
1363                        0x22, 0xc0, 0x74, 0x0b, 0x56, 0xb4, 0x0e, 0xbb, 0x07, 0x00, 0xcd, 0x10,
1364                        0x5e, 0xeb, 0xf0, 0x32, // 0x060
1365                        0xe4, 0xcd, 0x16, 0xcd, 0x19, 0xeb, 0xfe, 0x54, 0x68, 0x69, 0x73, 0x20,
1366                        0x69, 0x73, 0x20, 0x6e, // 0x070
1367                        0x6f, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c,
1368                        0x65, 0x20, 0x64, 0x69, // 0x080
1369                        0x73, 0x6b, 0x2e, 0x20, 0x20, 0x50, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20,
1370                        0x69, 0x6e, 0x73, 0x65, // 0x090
1371                        0x72, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c,
1372                        0x65, 0x20, 0x66, 0x6c, // 0x0A0
1373                        0x6f, 0x70, 0x70, 0x79, 0x20, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x70, 0x72,
1374                        0x65, 0x73, 0x73, 0x20, // 0x0B0
1375                        0x61, 0x6e, 0x79, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x74,
1376                        0x72, 0x79, 0x20, 0x61, // 0x0C0
1377                        0x67, 0x61, 0x69, 0x6e, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x0d, 0x0a, 0x00,
1378                        0x00, 0x00, 0x00, 0x00, // 0x0D0
1379                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1380                        0x00, 0x00, 0x00, 0x00, // 0x0E0
1381                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1382                        0x00, 0x00, 0x00, 0x00, // 0x0F0
1383                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1384                        0x00, 0x00, 0x00, 0x00, // 0x100
1385                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1386                        0x00, 0x00, 0x00, 0x00, // 0x110
1387                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1388                        0x00, 0x00, 0x00, 0x00, // 0x120
1389                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1390                        0x00, 0x00, 0x00, 0x00, // 0x130
1391                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1392                        0x00, 0x00, 0x00, 0x00, // 0x140
1393                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1394                        0x00, 0x00, 0x00, 0x00, // 0x150
1395                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1396                        0x00, 0x00, 0x00, 0x00, // 0x160
1397                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1398                        0x00, 0x00, 0x00, 0x00, // 0x170
1399                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1400                        0x00, 0x00, 0x00, 0x00, // 0x180
1401                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1402                        0x00, 0x00, 0x00, 0x00, // 0x190
1403                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1404                        0x00, 0x00, 0x00, 0x00, // 0x1A0
1405                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1406                        0x00, 0x00, 0x00, 0x00, // 0x1B0
1407                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1408                        0x00, 0x00, 0x00, 0x00, // 0x1C0
1409                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1410                        0x00, 0x00, 0x00, 0x00, // 0x1D0
1411                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1412                        0x00, 0x00, 0x00, 0x00, // 0x1E0
1413                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1414                        0x00, 0x00, 0x55, 0xaa, // 0x1F0
1415                    ],
1416                },
1417                Block {
1418                    contents: hex!(
1419                        "52 52 61 41 00 00 00 00 00 00 00 00 00 00 00 00
1420                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1421                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1422                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1423                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1424                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1425                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1426                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1427                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1428                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1429                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1430                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1431                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1432                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1433                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1434                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1435                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1436                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1437                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1438                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1439                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1440                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1441                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1442                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1443                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1444                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1445                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1446                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1447                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1448                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1449                         00 00 00 00 72 72 41 61 FF FF FF FF FF FF FF FF
1450                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA"
1451                    ),
1452                },
1453            ];
1454            println!(
1455                "Reading block {} to {}",
1456                start_block_idx.0,
1457                start_block_idx.0 as usize + blocks.len()
1458            );
1459            for (idx, block) in blocks.iter_mut().enumerate() {
1460                let block_idx = start_block_idx.0 as usize + idx;
1461                if block_idx < BLOCKS.len() {
1462                    *block = BLOCKS[block_idx].clone();
1463                } else {
1464                    return Err(Error::Unknown);
1465                }
1466            }
1467            Ok(())
1468        }
1469
1470        /// Write one or more blocks, starting at the given block index.
1471        fn write(&self, _blocks: &[Block], _start_block_idx: BlockIdx) -> Result<(), Self::Error> {
1472            unimplemented!();
1473        }
1474
1475        /// Determine how many blocks this device can hold.
1476        fn num_blocks(&self) -> Result<BlockCount, Self::Error> {
1477            Ok(BlockCount(2))
1478        }
1479    }
1480
1481    #[test]
1482    fn partition0() {
1483        let c: VolumeManager<DummyBlockDevice, Clock, 2, 2> =
1484            VolumeManager::new_with_limits(DummyBlockDevice, Clock, 0xAA00_0000);
1485
1486        let v = c.open_raw_volume(VolumeIdx(0)).unwrap();
1487        let expected_id = RawVolume(Handle(0xAA00_0000));
1488        assert_eq!(v, expected_id);
1489        assert_eq!(
1490            &c.data.borrow().open_volumes[0],
1491            &VolumeInfo {
1492                raw_volume: expected_id,
1493                idx: VolumeIdx(0),
1494                volume_type: VolumeType::Fat(crate::FatVolume {
1495                    lba_start: BlockIdx(1),
1496                    num_blocks: BlockCount(0x0011_2233),
1497                    blocks_per_cluster: 8,
1498                    first_data_block: BlockCount(15136),
1499                    fat_start: BlockCount(32),
1500                    second_fat_start: Some(BlockCount(32 + 0x0000_1D80)),
1501                    name: fat::VolumeName::create_from_str("Pictures").unwrap(),
1502                    free_clusters_count: None,
1503                    next_free_cluster: None,
1504                    cluster_count: 965_788,
1505                    fat_specific_info: fat::FatSpecificInfo::Fat32(fat::Fat32Info {
1506                        first_root_dir_cluster: ClusterId(2),
1507                        info_location: BlockIdx(1) + BlockCount(1),
1508                    })
1509                })
1510            }
1511        );
1512    }
1513}
1514
1515// ****************************************************************************
1516//
1517// End Of File
1518//
1519// ****************************************************************************