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 byteorder::{ByteOrder, LittleEndian};
6use core::convert::TryFrom;
7
8use crate::fat::{self, BlockCache, OnDiskDirEntry, RESERVED_ENTRIES};
9
10use crate::filesystem::{
11    Attributes, ClusterId, DirEntry, DirectoryInfo, FileInfo, Mode, RawDirectory, RawFile,
12    SearchIdGenerator, TimeSource, ToShortFileName, MAX_FILE_SIZE,
13};
14use crate::{
15    debug, Block, BlockCount, BlockDevice, BlockIdx, Error, RawVolume, ShortFileName, Volume,
16    VolumeIdx, VolumeInfo, VolumeType, PARTITION_ID_FAT16, PARTITION_ID_FAT16_LBA,
17    PARTITION_ID_FAT32_CHS_LBA, PARTITION_ID_FAT32_LBA,
18};
19use heapless::Vec;
20
21/// Wraps a block device and gives access to the FAT-formatted volumes within
22/// it.
23///
24/// Tracks which files and directories are open, to prevent you from deleting
25/// a file or directory you currently have open.
26#[derive(Debug)]
27pub struct VolumeManager<
28    D,
29    T,
30    const MAX_DIRS: usize = 4,
31    const MAX_FILES: usize = 4,
32    const MAX_VOLUMES: usize = 1,
33> where
34    D: BlockDevice,
35    T: TimeSource,
36    <D as BlockDevice>::Error: core::fmt::Debug,
37{
38    pub(crate) block_device: D,
39    pub(crate) time_source: T,
40    id_generator: SearchIdGenerator,
41    open_volumes: Vec<VolumeInfo, MAX_VOLUMES>,
42    open_dirs: Vec<DirectoryInfo, MAX_DIRS>,
43    open_files: Vec<FileInfo, MAX_FILES>,
44}
45
46impl<D, T> VolumeManager<D, T, 4, 4>
47where
48    D: BlockDevice,
49    T: TimeSource,
50    <D as BlockDevice>::Error: core::fmt::Debug,
51{
52    /// Create a new Volume Manager using a generic `BlockDevice`. From this
53    /// object we can open volumes (partitions) and with those we can open
54    /// files.
55    ///
56    /// This creates a `VolumeManager` with default values
57    /// MAX_DIRS = 4, MAX_FILES = 4, MAX_VOLUMES = 1. Call `VolumeManager::new_with_limits(block_device, time_source)`
58    /// if you need different limits.
59    pub fn new(block_device: D, time_source: T) -> VolumeManager<D, T, 4, 4, 1> {
60        // Pick a random starting point for the IDs that's not zero, because
61        // zero doesn't stand out in the logs.
62        Self::new_with_limits(block_device, time_source, 5000)
63    }
64}
65
66impl<D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
67    VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
68where
69    D: BlockDevice,
70    T: TimeSource,
71    <D as BlockDevice>::Error: core::fmt::Debug,
72{
73    /// Create a new Volume Manager using a generic `BlockDevice`. From this
74    /// object we can open volumes (partitions) and with those we can open
75    /// files.
76    ///
77    /// You can also give an offset for all the IDs this volume manager
78    /// generates, which might help you find the IDs in your logs when
79    /// debugging.
80    pub fn new_with_limits(
81        block_device: D,
82        time_source: T,
83        id_offset: u32,
84    ) -> VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> {
85        debug!("Creating new embedded-sdmmc::VolumeManager");
86        VolumeManager {
87            block_device,
88            time_source,
89            id_generator: SearchIdGenerator::new(id_offset),
90            open_volumes: Vec::new(),
91            open_dirs: Vec::new(),
92            open_files: Vec::new(),
93        }
94    }
95
96    /// Temporarily get access to the underlying block device.
97    pub fn device(&mut self) -> &mut D {
98        &mut self.block_device
99    }
100
101    /// Get a volume (or partition) based on entries in the Master Boot Record.
102    ///
103    /// We do not support GUID Partition Table disks. Nor do we support any
104    /// concept of drive letters - that is for a higher layer to handle.
105    pub fn open_volume(
106        &mut self,
107        volume_idx: VolumeIdx,
108    ) -> Result<Volume<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>, Error<D::Error>> {
109        let v = self.open_raw_volume(volume_idx)?;
110        Ok(v.to_volume(self))
111    }
112
113    /// Get a volume (or partition) based on entries in the Master Boot Record.
114    ///
115    /// We do not support GUID Partition Table disks. Nor do we support any
116    /// concept of drive letters - that is for a higher layer to handle.
117    ///
118    /// This function gives you a `RawVolume` and you must close the volume by
119    /// calling `VolumeManager::close_volume`.
120    pub fn open_raw_volume(&mut self, volume_idx: VolumeIdx) -> Result<RawVolume, Error<D::Error>> {
121        const PARTITION1_START: usize = 446;
122        const PARTITION2_START: usize = PARTITION1_START + PARTITION_INFO_LENGTH;
123        const PARTITION3_START: usize = PARTITION2_START + PARTITION_INFO_LENGTH;
124        const PARTITION4_START: usize = PARTITION3_START + PARTITION_INFO_LENGTH;
125        const FOOTER_START: usize = 510;
126        const FOOTER_VALUE: u16 = 0xAA55;
127        const PARTITION_INFO_LENGTH: usize = 16;
128        const PARTITION_INFO_STATUS_INDEX: usize = 0;
129        const PARTITION_INFO_TYPE_INDEX: usize = 4;
130        const PARTITION_INFO_LBA_START_INDEX: usize = 8;
131        const PARTITION_INFO_NUM_BLOCKS_INDEX: usize = 12;
132
133        if self.open_volumes.is_full() {
134            return Err(Error::TooManyOpenVolumes);
135        }
136
137        for v in self.open_volumes.iter() {
138            if v.idx == volume_idx {
139                return Err(Error::VolumeAlreadyOpen);
140            }
141        }
142
143        let (part_type, lba_start, num_blocks) = {
144            let mut blocks = [Block::new()];
145            self.block_device
146                .read(&mut blocks, BlockIdx(0), "read_mbr")
147                .map_err(Error::DeviceError)?;
148            let block = &blocks[0];
149            // We only support Master Boot Record (MBR) partitioned cards, not
150            // GUID Partition Table (GPT)
151            if LittleEndian::read_u16(&block[FOOTER_START..FOOTER_START + 2]) != FOOTER_VALUE {
152                return Err(Error::FormatError("Invalid MBR signature"));
153            }
154            let partition = match volume_idx {
155                VolumeIdx(0) => {
156                    &block[PARTITION1_START..(PARTITION1_START + PARTITION_INFO_LENGTH)]
157                }
158                VolumeIdx(1) => {
159                    &block[PARTITION2_START..(PARTITION2_START + PARTITION_INFO_LENGTH)]
160                }
161                VolumeIdx(2) => {
162                    &block[PARTITION3_START..(PARTITION3_START + PARTITION_INFO_LENGTH)]
163                }
164                VolumeIdx(3) => {
165                    &block[PARTITION4_START..(PARTITION4_START + PARTITION_INFO_LENGTH)]
166                }
167                _ => {
168                    return Err(Error::NoSuchVolume);
169                }
170            };
171            // Only 0x80 and 0x00 are valid (bootable, and non-bootable)
172            if (partition[PARTITION_INFO_STATUS_INDEX] & 0x7F) != 0x00 {
173                return Err(Error::FormatError("Invalid partition status"));
174            }
175            let lba_start = LittleEndian::read_u32(
176                &partition[PARTITION_INFO_LBA_START_INDEX..(PARTITION_INFO_LBA_START_INDEX + 4)],
177            );
178            let num_blocks = LittleEndian::read_u32(
179                &partition[PARTITION_INFO_NUM_BLOCKS_INDEX..(PARTITION_INFO_NUM_BLOCKS_INDEX + 4)],
180            );
181            (
182                partition[PARTITION_INFO_TYPE_INDEX],
183                BlockIdx(lba_start),
184                BlockCount(num_blocks),
185            )
186        };
187        match part_type {
188            PARTITION_ID_FAT32_CHS_LBA
189            | PARTITION_ID_FAT32_LBA
190            | PARTITION_ID_FAT16_LBA
191            | PARTITION_ID_FAT16 => {
192                let volume = fat::parse_volume(&self.block_device, lba_start, num_blocks)?;
193                let id = RawVolume(self.id_generator.get());
194                let info = VolumeInfo {
195                    volume_id: id,
196                    idx: volume_idx,
197                    volume_type: volume,
198                };
199                // We already checked for space
200                self.open_volumes.push(info).unwrap();
201                Ok(id)
202            }
203            _ => Err(Error::FormatError("Partition type not supported")),
204        }
205    }
206
207    /// Open the volume's root directory.
208    ///
209    /// You can then read the directory entries with `iterate_dir`, or you can
210    /// use `open_file_in_dir`.
211    pub fn open_root_dir(&mut self, volume: RawVolume) -> Result<RawDirectory, Error<D::Error>> {
212        // Opening a root directory twice is OK
213
214        let directory_id = RawDirectory(self.id_generator.get());
215        let dir_info = DirectoryInfo {
216            volume_id: volume,
217            cluster: ClusterId::ROOT_DIR,
218            directory_id,
219        };
220
221        self.open_dirs
222            .push(dir_info)
223            .map_err(|_| Error::TooManyOpenDirs)?;
224
225        Ok(directory_id)
226    }
227
228    /// Open a directory.
229    ///
230    /// You can then read the directory entries with `iterate_dir` and `open_file_in_dir`.
231    ///
232    /// Passing "." as the name results in opening the `parent_dir` a second time.
233    pub fn open_dir<N>(
234        &mut self,
235        parent_dir: RawDirectory,
236        name: N,
237    ) -> Result<RawDirectory, Error<D::Error>>
238    where
239        N: ToShortFileName,
240    {
241        if self.open_dirs.is_full() {
242            return Err(Error::TooManyOpenDirs);
243        }
244
245        // Find dir by ID
246        let parent_dir_idx = self.get_dir_by_id(parent_dir)?;
247        let volume_idx = self.get_volume_by_id(self.open_dirs[parent_dir_idx].volume_id)?;
248        let short_file_name = name.to_short_filename().map_err(Error::FilenameError)?;
249        let parent_dir_info = &self.open_dirs[parent_dir_idx];
250
251        // Open the directory
252        if short_file_name == ShortFileName::this_dir() {
253            // short-cut (root dir doesn't have ".")
254            let directory_id = RawDirectory(self.id_generator.get());
255            let dir_info = DirectoryInfo {
256                directory_id,
257                volume_id: self.open_volumes[volume_idx].volume_id,
258                cluster: parent_dir_info.cluster,
259            };
260
261            self.open_dirs
262                .push(dir_info)
263                .map_err(|_| Error::TooManyOpenDirs)?;
264
265            return Ok(directory_id);
266        }
267
268        let dir_entry = match &self.open_volumes[volume_idx].volume_type {
269            VolumeType::Fat(fat) => {
270                fat.find_directory_entry(&self.block_device, parent_dir_info, &short_file_name)?
271            }
272        };
273
274        debug!("Found dir entry: {:?}", dir_entry);
275
276        if !dir_entry.attributes.is_directory() {
277            return Err(Error::OpenedFileAsDir);
278        }
279
280        // We don't check if the directory is already open - directories hold
281        // no cached state and so opening a directory twice is allowable.
282
283        // Remember this open directory.
284        let directory_id = RawDirectory(self.id_generator.get());
285        let dir_info = DirectoryInfo {
286            directory_id,
287            volume_id: self.open_volumes[volume_idx].volume_id,
288            cluster: dir_entry.cluster,
289        };
290
291        self.open_dirs
292            .push(dir_info)
293            .map_err(|_| Error::TooManyOpenDirs)?;
294
295        Ok(directory_id)
296    }
297
298    /// Close a directory. You cannot perform operations on an open directory
299    /// and so must close it if you want to do something with it.
300    pub fn close_dir(&mut self, directory: RawDirectory) -> Result<(), Error<D::Error>> {
301        for (idx, info) in self.open_dirs.iter().enumerate() {
302            if directory == info.directory_id {
303                self.open_dirs.swap_remove(idx);
304                return Ok(());
305            }
306        }
307        Err(Error::BadHandle)
308    }
309
310    /// Close a volume
311    ///
312    /// You can't close it if there are any files or directories open on it.
313    pub fn close_volume(&mut self, volume: RawVolume) -> Result<(), Error<D::Error>> {
314        for f in self.open_files.iter() {
315            if f.volume_id == volume {
316                return Err(Error::VolumeStillInUse);
317            }
318        }
319
320        for d in self.open_dirs.iter() {
321            if d.volume_id == volume {
322                return Err(Error::VolumeStillInUse);
323            }
324        }
325
326        let volume_idx = self.get_volume_by_id(volume)?;
327
328        match &mut self.open_volumes[volume_idx].volume_type {
329            VolumeType::Fat(fat_volume) => {
330                fat_volume.update_info_sector(&self.block_device)?;
331            }
332        }
333
334        self.open_volumes.swap_remove(volume_idx);
335
336        Ok(())
337    }
338
339    /// Look in a directory for a named file.
340    pub fn find_directory_entry<N>(
341        &mut self,
342        directory: RawDirectory,
343        name: N,
344    ) -> Result<DirEntry, Error<D::Error>>
345    where
346        N: ToShortFileName,
347    {
348        let directory_idx = self.get_dir_by_id(directory)?;
349        let volume_idx = self.get_volume_by_id(self.open_dirs[directory_idx].volume_id)?;
350        match &self.open_volumes[volume_idx].volume_type {
351            VolumeType::Fat(fat) => {
352                let sfn = name.to_short_filename().map_err(Error::FilenameError)?;
353                fat.find_directory_entry(&self.block_device, &self.open_dirs[directory_idx], &sfn)
354            }
355        }
356    }
357
358    /// Call a callback function for each directory entry in a directory.
359    pub fn iterate_dir<F>(
360        &mut self,
361        directory: RawDirectory,
362        func: F,
363    ) -> Result<(), Error<D::Error>>
364    where
365        F: FnMut(&DirEntry),
366    {
367        let directory_idx = self.get_dir_by_id(directory)?;
368        let volume_idx = self.get_volume_by_id(self.open_dirs[directory_idx].volume_id)?;
369        match &self.open_volumes[volume_idx].volume_type {
370            VolumeType::Fat(fat) => {
371                fat.iterate_dir(&self.block_device, &self.open_dirs[directory_idx], func)
372            }
373        }
374    }
375
376    /// Open a file from a DirEntry. This is obtained by calling iterate_dir.
377    ///
378    /// # Safety
379    ///
380    /// The DirEntry must be a valid DirEntry read from disk, and not just
381    /// random numbers.
382    unsafe fn open_dir_entry(
383        &mut self,
384        volume: RawVolume,
385        dir_entry: DirEntry,
386        mode: Mode,
387    ) -> Result<RawFile, Error<D::Error>> {
388        // This check is load-bearing - we do an unchecked push later.
389        if self.open_files.is_full() {
390            return Err(Error::TooManyOpenFiles);
391        }
392
393        if dir_entry.attributes.is_read_only() && mode != Mode::ReadOnly {
394            return Err(Error::ReadOnly);
395        }
396
397        if dir_entry.attributes.is_directory() {
398            return Err(Error::OpenedDirAsFile);
399        }
400
401        // Check it's not already open
402        if self.file_is_open(volume, &dir_entry) {
403            return Err(Error::FileAlreadyOpen);
404        }
405
406        let mode = solve_mode_variant(mode, true);
407        let file_id = RawFile(self.id_generator.get());
408
409        let file = match mode {
410            Mode::ReadOnly => FileInfo {
411                file_id,
412                volume_id: volume,
413                current_cluster: (0, dir_entry.cluster),
414                current_offset: 0,
415                mode,
416                entry: dir_entry,
417                dirty: false,
418            },
419            Mode::ReadWriteAppend => {
420                let mut file = FileInfo {
421                    file_id,
422                    volume_id: volume,
423                    current_cluster: (0, dir_entry.cluster),
424                    current_offset: 0,
425                    mode,
426                    entry: dir_entry,
427                    dirty: false,
428                };
429                // seek_from_end with 0 can't fail
430                file.seek_from_end(0).ok();
431                file
432            }
433            Mode::ReadWriteTruncate => {
434                let mut file = FileInfo {
435                    file_id,
436                    volume_id: volume,
437                    current_cluster: (0, dir_entry.cluster),
438                    current_offset: 0,
439                    mode,
440                    entry: dir_entry,
441                    dirty: false,
442                };
443                let volume_idx = self.get_volume_by_id(volume)?;
444                match &mut self.open_volumes[volume_idx].volume_type {
445                    VolumeType::Fat(fat) => {
446                        fat.truncate_cluster_chain(&self.block_device, file.entry.cluster)?
447                    }
448                };
449                file.update_length(0);
450                match &self.open_volumes[volume_idx].volume_type {
451                    VolumeType::Fat(fat) => {
452                        file.entry.mtime = self.time_source.get_timestamp();
453                        fat.write_entry_to_disk(&self.block_device, &file.entry)?;
454                    }
455                };
456
457                file
458            }
459            _ => return Err(Error::Unsupported),
460        };
461
462        // Remember this open file - can't be full as we checked already
463        unsafe {
464            self.open_files.push_unchecked(file);
465        }
466
467        Ok(file_id)
468    }
469
470    /// Open a file with the given full path. A file can only be opened once.
471    pub fn open_file_in_dir<N>(
472        &mut self,
473        directory: RawDirectory,
474        name: N,
475        mode: Mode,
476    ) -> Result<RawFile, Error<D::Error>>
477    where
478        N: ToShortFileName,
479    {
480        // This check is load-bearing - we do an unchecked push later.
481        if self.open_files.is_full() {
482            return Err(Error::TooManyOpenFiles);
483        }
484
485        let directory_idx = self.get_dir_by_id(directory)?;
486        let directory_info = &self.open_dirs[directory_idx];
487        let volume_id = self.open_dirs[directory_idx].volume_id;
488        let volume_idx = self.get_volume_by_id(volume_id)?;
489        let volume_info = &self.open_volumes[volume_idx];
490        let sfn = name.to_short_filename().map_err(Error::FilenameError)?;
491
492        let dir_entry = match &volume_info.volume_type {
493            VolumeType::Fat(fat) => {
494                fat.find_directory_entry(&self.block_device, directory_info, &sfn)
495            }
496        };
497
498        let dir_entry = match dir_entry {
499            Ok(entry) => {
500                // we are opening an existing file
501                Some(entry)
502            }
503            Err(_)
504                if (mode == Mode::ReadWriteCreate)
505                    | (mode == Mode::ReadWriteCreateOrTruncate)
506                    | (mode == Mode::ReadWriteCreateOrAppend) =>
507            {
508                // We are opening a non-existant file, but that's OK because they
509                // asked us to create it
510                None
511            }
512            _ => {
513                // We are opening a non-existant file, and that's not OK.
514                return Err(Error::NotFound);
515            }
516        };
517
518        // Check if it's open already
519        if let Some(dir_entry) = &dir_entry {
520            if self.file_is_open(volume_info.volume_id, dir_entry) {
521                return Err(Error::FileAlreadyOpen);
522            }
523        }
524
525        let mode = solve_mode_variant(mode, dir_entry.is_some());
526
527        match mode {
528            Mode::ReadWriteCreate => {
529                if dir_entry.is_some() {
530                    return Err(Error::FileAlreadyExists);
531                }
532                let att = Attributes::create_from_fat(0);
533                let volume_idx = self.get_volume_by_id(volume_id)?;
534                let entry = match &mut self.open_volumes[volume_idx].volume_type {
535                    VolumeType::Fat(fat) => fat.write_new_directory_entry(
536                        &self.block_device,
537                        &self.time_source,
538                        directory_info.cluster,
539                        sfn,
540                        att,
541                    )?,
542                };
543
544                let file_id = RawFile(self.id_generator.get());
545
546                let file = FileInfo {
547                    file_id,
548                    volume_id,
549                    current_cluster: (0, entry.cluster),
550                    current_offset: 0,
551                    mode,
552                    entry,
553                    dirty: false,
554                };
555
556                // Remember this open file - can't be full as we checked already
557                unsafe {
558                    self.open_files.push_unchecked(file);
559                }
560
561                Ok(file_id)
562            }
563            _ => {
564                // Safe to unwrap, since we actually have an entry if we got here
565                let dir_entry = dir_entry.unwrap();
566                // Safety: We read this dir entry off disk and didn't change it
567                unsafe { self.open_dir_entry(volume_id, dir_entry, mode) }
568            }
569        }
570    }
571
572    /// Delete a closed file with the given filename, if it exists.
573    pub fn delete_file_in_dir<N>(
574        &mut self,
575        directory: RawDirectory,
576        name: N,
577    ) -> Result<(), Error<D::Error>>
578    where
579        N: ToShortFileName,
580    {
581        let dir_idx = self.get_dir_by_id(directory)?;
582        let dir_info = &self.open_dirs[dir_idx];
583        let volume_idx = self.get_volume_by_id(dir_info.volume_id)?;
584        let sfn = name.to_short_filename().map_err(Error::FilenameError)?;
585
586        let dir_entry = match &self.open_volumes[volume_idx].volume_type {
587            VolumeType::Fat(fat) => fat.find_directory_entry(&self.block_device, dir_info, &sfn),
588        }?;
589
590        if dir_entry.attributes.is_directory() {
591            return Err(Error::DeleteDirAsFile);
592        }
593
594        if self.file_is_open(dir_info.volume_id, &dir_entry) {
595            return Err(Error::FileAlreadyOpen);
596        }
597
598        let volume_idx = self.get_volume_by_id(dir_info.volume_id)?;
599        match &self.open_volumes[volume_idx].volume_type {
600            VolumeType::Fat(fat) => {
601                fat.delete_directory_entry(&self.block_device, dir_info, &sfn)?
602            }
603        }
604
605        Ok(())
606    }
607
608    /// Check if a file is open
609    ///
610    /// Returns `true` if it's open, `false`, otherwise.
611    fn file_is_open(&self, volume: RawVolume, dir_entry: &DirEntry) -> bool {
612        for f in self.open_files.iter() {
613            if f.volume_id == volume
614                && f.entry.entry_block == dir_entry.entry_block
615                && f.entry.entry_offset == dir_entry.entry_offset
616            {
617                return true;
618            }
619        }
620        false
621    }
622
623    /// Read from an open file.
624    pub fn read(&mut self, file: RawFile, buffer: &mut [u8]) -> Result<usize, Error<D::Error>> {
625        let file_idx = self.get_file_by_id(file)?;
626        let volume_idx = self.get_volume_by_id(self.open_files[file_idx].volume_id)?;
627        // Calculate which file block the current offset lies within
628        // While there is more to read, read the block and copy in to the buffer.
629        // If we need to find the next cluster, walk the FAT.
630        let mut space = buffer.len();
631        let mut read = 0;
632        while space > 0 && !self.open_files[file_idx].eof() {
633            let mut current_cluster = self.open_files[file_idx].current_cluster;
634            let (block_idx, block_offset, block_avail) = self.find_data_on_disk(
635                volume_idx,
636                &mut current_cluster,
637                self.open_files[file_idx].entry.cluster,
638                self.open_files[file_idx].current_offset,
639            )?;
640            self.open_files[file_idx].current_cluster = current_cluster;
641            let mut blocks = [Block::new()];
642            self.block_device
643                .read(&mut blocks, block_idx, "read")
644                .map_err(Error::DeviceError)?;
645            let block = &blocks[0];
646            let to_copy = block_avail
647                .min(space)
648                .min(self.open_files[file_idx].left() as usize);
649            assert!(to_copy != 0);
650            buffer[read..read + to_copy]
651                .copy_from_slice(&block[block_offset..block_offset + to_copy]);
652            read += to_copy;
653            space -= to_copy;
654            self.open_files[file_idx]
655                .seek_from_current(to_copy as i32)
656                .unwrap();
657        }
658        Ok(read)
659    }
660
661    /// Write to a open file.
662    pub fn write(&mut self, file: RawFile, buffer: &[u8]) -> Result<(), Error<D::Error>> {
663        #[cfg(feature = "defmt-log")]
664        debug!("write(file={:?}, buffer={:x}", file, buffer);
665
666        #[cfg(feature = "log")]
667        debug!("write(file={:?}, buffer={:x?}", file, buffer);
668
669        // Clone this so we can touch our other structures. Need to ensure we
670        // write it back at the end.
671        let file_idx = self.get_file_by_id(file)?;
672        let volume_idx = self.get_volume_by_id(self.open_files[file_idx].volume_id)?;
673
674        if self.open_files[file_idx].mode == Mode::ReadOnly {
675            return Err(Error::ReadOnly);
676        }
677
678        self.open_files[file_idx].dirty = true;
679
680        if self.open_files[file_idx].entry.cluster.0 < RESERVED_ENTRIES {
681            // file doesn't have a valid allocated cluster (possible zero-length file), allocate one
682            self.open_files[file_idx].entry.cluster =
683                match self.open_volumes[volume_idx].volume_type {
684                    VolumeType::Fat(ref mut fat) => {
685                        fat.alloc_cluster(&self.block_device, None, false)?
686                    }
687                };
688            debug!(
689                "Alloc first cluster {:?}",
690                self.open_files[file_idx].entry.cluster
691            );
692        }
693
694        // Clone this so we can touch our other structures.
695        let volume_idx = self.get_volume_by_id(self.open_files[file_idx].volume_id)?;
696
697        if (self.open_files[file_idx].current_cluster.1) < self.open_files[file_idx].entry.cluster {
698            debug!("Rewinding to start");
699            self.open_files[file_idx].current_cluster =
700                (0, self.open_files[file_idx].entry.cluster);
701        }
702        let bytes_until_max =
703            usize::try_from(MAX_FILE_SIZE - self.open_files[file_idx].current_offset)
704                .map_err(|_| Error::ConversionError)?;
705        let bytes_to_write = core::cmp::min(buffer.len(), bytes_until_max);
706        let mut written = 0;
707
708        while written < bytes_to_write {
709            let mut current_cluster = self.open_files[file_idx].current_cluster;
710            debug!(
711                "Have written bytes {}/{}, finding cluster {:?}",
712                written, bytes_to_write, current_cluster
713            );
714            let current_offset = self.open_files[file_idx].current_offset;
715            let (block_idx, block_offset, block_avail) = match self.find_data_on_disk(
716                volume_idx,
717                &mut current_cluster,
718                self.open_files[file_idx].entry.cluster,
719                current_offset,
720            ) {
721                Ok(vars) => {
722                    debug!(
723                        "Found block_idx={:?}, block_offset={:?}, block_avail={}",
724                        vars.0, vars.1, vars.2
725                    );
726                    vars
727                }
728                Err(Error::EndOfFile) => {
729                    debug!("Extending file");
730                    match self.open_volumes[volume_idx].volume_type {
731                        VolumeType::Fat(ref mut fat) => {
732                            if fat
733                                .alloc_cluster(&self.block_device, Some(current_cluster.1), false)
734                                .is_err()
735                            {
736                                return Err(Error::DiskFull);
737                            }
738                            debug!("Allocated new FAT cluster, finding offsets...");
739                            let new_offset = self
740                                .find_data_on_disk(
741                                    volume_idx,
742                                    &mut current_cluster,
743                                    self.open_files[file_idx].entry.cluster,
744                                    self.open_files[file_idx].current_offset,
745                                )
746                                .map_err(|_| Error::AllocationError)?;
747                            debug!("New offset {:?}", new_offset);
748                            new_offset
749                        }
750                    }
751                }
752                Err(e) => return Err(e),
753            };
754            let mut blocks = [Block::new()];
755            let to_copy = core::cmp::min(block_avail, bytes_to_write - written);
756            if block_offset != 0 || to_copy != block_avail {
757                debug!("Partial block read/modify/write");
758                self.block_device
759                    .read(&mut blocks, block_idx, "read")
760                    .map_err(Error::DeviceError)?;
761            }
762            let block = &mut blocks[0];
763            block[block_offset..block_offset + to_copy]
764                .copy_from_slice(&buffer[written..written + to_copy]);
765            debug!("Writing block {:?}", block_idx);
766            self.block_device
767                .write(&blocks, block_idx)
768                .map_err(Error::DeviceError)?;
769            written += to_copy;
770            self.open_files[file_idx].current_cluster = current_cluster;
771
772            let to_copy = to_copy as u32;
773            let new_offset = self.open_files[file_idx].current_offset + to_copy;
774            if new_offset > self.open_files[file_idx].entry.size {
775                // We made it longer
776                self.open_files[file_idx].update_length(new_offset);
777            }
778            self.open_files[file_idx]
779                .seek_from_start(new_offset)
780                .unwrap();
781            // Entry update deferred to file close, for performance.
782        }
783        self.open_files[file_idx].entry.attributes.set_archive(true);
784        self.open_files[file_idx].entry.mtime = self.time_source.get_timestamp();
785        Ok(())
786    }
787
788    /// Close a file with the given raw file handle.
789    pub fn close_file(&mut self, file: RawFile) -> Result<(), Error<D::Error>> {
790        let flush_result = self.flush_file(file);
791        let file_idx = self.get_file_by_id(file)?;
792        self.open_files.swap_remove(file_idx);
793        flush_result
794    }
795
796    /// Flush (update the entry) for a file with the given raw file handle.
797    pub fn flush_file(&mut self, file: RawFile) -> Result<(), Error<D::Error>> {
798        let file_info = self
799            .open_files
800            .iter()
801            .find(|info| info.file_id == file)
802            .ok_or(Error::BadHandle)?;
803
804        if file_info.dirty {
805            let volume_idx = self.get_volume_by_id(file_info.volume_id)?;
806            match self.open_volumes[volume_idx].volume_type {
807                VolumeType::Fat(ref mut fat) => {
808                    debug!("Updating FAT info sector");
809                    fat.update_info_sector(&self.block_device)?;
810                    debug!("Updating dir entry {:?}", file_info.entry);
811                    if file_info.entry.size != 0 {
812                        // If you have a length, you must have a cluster
813                        assert!(file_info.entry.cluster.0 != 0);
814                    }
815                    fat.write_entry_to_disk(&self.block_device, &file_info.entry)?;
816                }
817            };
818        }
819        Ok(())
820    }
821
822    /// Check if any files or folders are open.
823    pub fn has_open_handles(&self) -> bool {
824        !(self.open_dirs.is_empty() || self.open_files.is_empty())
825    }
826
827    /// Consume self and return BlockDevice and TimeSource
828    pub fn free(self) -> (D, T) {
829        (self.block_device, self.time_source)
830    }
831
832    /// Check if a file is at End Of File.
833    pub fn file_eof(&self, file: RawFile) -> Result<bool, Error<D::Error>> {
834        let file_idx = self.get_file_by_id(file)?;
835        Ok(self.open_files[file_idx].eof())
836    }
837
838    /// Seek a file with an offset from the start of the file.
839    pub fn file_seek_from_start(
840        &mut self,
841        file: RawFile,
842        offset: u32,
843    ) -> Result<(), Error<D::Error>> {
844        let file_idx = self.get_file_by_id(file)?;
845        self.open_files[file_idx]
846            .seek_from_start(offset)
847            .map_err(|_| Error::InvalidOffset)?;
848        Ok(())
849    }
850
851    /// Seek a file with an offset from the current position.
852    pub fn file_seek_from_current(
853        &mut self,
854        file: RawFile,
855        offset: i32,
856    ) -> Result<(), Error<D::Error>> {
857        let file_idx = self.get_file_by_id(file)?;
858        self.open_files[file_idx]
859            .seek_from_current(offset)
860            .map_err(|_| Error::InvalidOffset)?;
861        Ok(())
862    }
863
864    /// Seek a file with an offset back from the end of the file.
865    pub fn file_seek_from_end(
866        &mut self,
867        file: RawFile,
868        offset: u32,
869    ) -> Result<(), Error<D::Error>> {
870        let file_idx = self.get_file_by_id(file)?;
871        self.open_files[file_idx]
872            .seek_from_end(offset)
873            .map_err(|_| Error::InvalidOffset)?;
874        Ok(())
875    }
876
877    /// Get the length of a file
878    pub fn file_length(&self, file: RawFile) -> Result<u32, Error<D::Error>> {
879        let file_idx = self.get_file_by_id(file)?;
880        Ok(self.open_files[file_idx].length())
881    }
882
883    /// Get the current offset of a file
884    pub fn file_offset(&self, file: RawFile) -> Result<u32, Error<D::Error>> {
885        let file_idx = self.get_file_by_id(file)?;
886        Ok(self.open_files[file_idx].current_offset)
887    }
888
889    /// Create a directory in a given directory.
890    pub fn make_dir_in_dir<N>(
891        &mut self,
892        directory: RawDirectory,
893        name: N,
894    ) -> Result<(), Error<D::Error>>
895    where
896        N: ToShortFileName,
897    {
898        // This check is load-bearing - we do an unchecked push later.
899        if self.open_dirs.is_full() {
900            return Err(Error::TooManyOpenDirs);
901        }
902
903        let parent_directory_idx = self.get_dir_by_id(directory)?;
904        let parent_directory_info = &self.open_dirs[parent_directory_idx];
905        let volume_id = self.open_dirs[parent_directory_idx].volume_id;
906        let volume_idx = self.get_volume_by_id(volume_id)?;
907        let volume_info = &self.open_volumes[volume_idx];
908        let sfn = name.to_short_filename().map_err(Error::FilenameError)?;
909
910        debug!("Creating directory '{}'", sfn);
911        debug!(
912            "Parent dir is in cluster {:?}",
913            parent_directory_info.cluster
914        );
915
916        // Does an entry exist with this name?
917        let maybe_dir_entry = match &volume_info.volume_type {
918            VolumeType::Fat(fat) => {
919                fat.find_directory_entry(&self.block_device, parent_directory_info, &sfn)
920            }
921        };
922
923        match maybe_dir_entry {
924            Ok(entry) if entry.attributes.is_directory() => {
925                return Err(Error::DirAlreadyExists);
926            }
927            Ok(_entry) => {
928                return Err(Error::FileAlreadyExists);
929            }
930            Err(Error::NotFound) => {
931                // perfect, let's make it
932            }
933            Err(e) => {
934                // Some other error - tell them about it
935                return Err(e);
936            }
937        };
938
939        let att = Attributes::create_from_fat(Attributes::DIRECTORY);
940
941        // Need mutable access for this
942        match &mut self.open_volumes[volume_idx].volume_type {
943            VolumeType::Fat(fat) => {
944                debug!("Making dir entry");
945                let mut new_dir_entry_in_parent = fat.write_new_directory_entry(
946                    &self.block_device,
947                    &self.time_source,
948                    parent_directory_info.cluster,
949                    sfn,
950                    att,
951                )?;
952                if new_dir_entry_in_parent.cluster == ClusterId::EMPTY {
953                    new_dir_entry_in_parent.cluster =
954                        fat.alloc_cluster(&self.block_device, None, false)?;
955                    // update the parent dir with the cluster of the new dir
956                    fat.write_entry_to_disk(&self.block_device, &new_dir_entry_in_parent)?;
957                }
958                let new_dir_start_block = fat.cluster_to_block(new_dir_entry_in_parent.cluster);
959                debug!("Made new dir entry {:?}", new_dir_entry_in_parent);
960                let now = self.time_source.get_timestamp();
961                let fat_type = fat.get_fat_type();
962                // A blank block
963                let mut blocks = [Block::new()];
964                // make the "." entry
965                let dot_entry_in_child = DirEntry {
966                    name: crate::ShortFileName::this_dir(),
967                    mtime: now,
968                    ctime: now,
969                    attributes: att,
970                    // point at ourselves
971                    cluster: new_dir_entry_in_parent.cluster,
972                    size: 0,
973                    entry_block: new_dir_start_block,
974                    entry_offset: 0,
975                };
976                debug!("New dir has {:?}", dot_entry_in_child);
977                let mut offset = 0;
978                blocks[0][offset..offset + OnDiskDirEntry::LEN]
979                    .copy_from_slice(&dot_entry_in_child.serialize(fat_type)[..]);
980                offset += OnDiskDirEntry::LEN;
981                // make the ".." entry
982                let dot_dot_entry_in_child = DirEntry {
983                    name: crate::ShortFileName::parent_dir(),
984                    mtime: now,
985                    ctime: now,
986                    attributes: att,
987                    // point at our parent
988                    cluster: {
989                        // On FAT16, indicate parent is root using Cluster(0)
990                        if parent_directory_info.cluster == ClusterId::ROOT_DIR {
991                            ClusterId::EMPTY
992                        } else {
993                            parent_directory_info.cluster
994                        }
995                    },
996                    size: 0,
997                    entry_block: new_dir_start_block,
998                    entry_offset: OnDiskDirEntry::LEN_U32,
999                };
1000                debug!("New dir has {:?}", dot_dot_entry_in_child);
1001                blocks[0][offset..offset + OnDiskDirEntry::LEN]
1002                    .copy_from_slice(&dot_dot_entry_in_child.serialize(fat_type)[..]);
1003
1004                self.block_device
1005                    .write(&blocks, new_dir_start_block)
1006                    .map_err(Error::DeviceError)?;
1007
1008                // Now zero the rest of the cluster
1009                for b in blocks[0].iter_mut() {
1010                    *b = 0;
1011                }
1012                for block in new_dir_start_block
1013                    .range(BlockCount(u32::from(fat.blocks_per_cluster)))
1014                    .skip(1)
1015                {
1016                    self.block_device
1017                        .write(&blocks, block)
1018                        .map_err(Error::DeviceError)?;
1019                }
1020            }
1021        };
1022
1023        Ok(())
1024    }
1025
1026    fn get_volume_by_id(&self, volume: RawVolume) -> Result<usize, Error<D::Error>> {
1027        for (idx, v) in self.open_volumes.iter().enumerate() {
1028            if v.volume_id == volume {
1029                return Ok(idx);
1030            }
1031        }
1032        Err(Error::BadHandle)
1033    }
1034
1035    fn get_dir_by_id(&self, directory: RawDirectory) -> Result<usize, Error<D::Error>> {
1036        for (idx, d) in self.open_dirs.iter().enumerate() {
1037            if d.directory_id == directory {
1038                return Ok(idx);
1039            }
1040        }
1041        Err(Error::BadHandle)
1042    }
1043
1044    fn get_file_by_id(&self, file: RawFile) -> Result<usize, Error<D::Error>> {
1045        for (idx, f) in self.open_files.iter().enumerate() {
1046            if f.file_id == file {
1047                return Ok(idx);
1048            }
1049        }
1050        Err(Error::BadHandle)
1051    }
1052
1053    /// This function turns `desired_offset` into an appropriate block to be
1054    /// read. It either calculates this based on the start of the file, or
1055    /// from the given start point - whichever is better.
1056    ///
1057    /// Returns:
1058    ///
1059    /// * the index for the block on the disk that contains the data we want,
1060    /// * the byte offset into that block for the data we want, and
1061    /// * how many bytes remain in that block.
1062    fn find_data_on_disk(
1063        &self,
1064        volume_idx: usize,
1065        start: &mut (u32, ClusterId),
1066        file_start: ClusterId,
1067        desired_offset: u32,
1068    ) -> Result<(BlockIdx, usize, usize), Error<D::Error>> {
1069        let bytes_per_cluster = match &self.open_volumes[volume_idx].volume_type {
1070            VolumeType::Fat(fat) => fat.bytes_per_cluster(),
1071        };
1072        // do we need to be before our start point?
1073        if desired_offset < start.0 {
1074            // user wants to go backwards - start from the beginning of the file
1075            // because the FAT is only a singly-linked list.
1076            start.0 = 0;
1077            start.1 = file_start;
1078        }
1079        // How many clusters forward do we need to go?
1080        let offset_from_cluster = desired_offset - start.0;
1081        // walk through the FAT chain
1082        let num_clusters = offset_from_cluster / bytes_per_cluster;
1083        let mut block_cache = BlockCache::empty();
1084        for _ in 0..num_clusters {
1085            start.1 = match &self.open_volumes[volume_idx].volume_type {
1086                VolumeType::Fat(fat) => {
1087                    fat.next_cluster(&self.block_device, start.1, &mut block_cache)?
1088                }
1089            };
1090            start.0 += bytes_per_cluster;
1091        }
1092        // How many blocks in are we now?
1093        let offset_from_cluster = desired_offset - start.0;
1094        assert!(offset_from_cluster < bytes_per_cluster);
1095        let num_blocks = BlockCount(offset_from_cluster / Block::LEN_U32);
1096        let block_idx = match &self.open_volumes[volume_idx].volume_type {
1097            VolumeType::Fat(fat) => fat.cluster_to_block(start.1),
1098        } + num_blocks;
1099        let block_offset = (desired_offset % Block::LEN_U32) as usize;
1100        let available = Block::LEN - block_offset;
1101        Ok((block_idx, block_offset, available))
1102    }
1103}
1104
1105/// Transform mode variants (ReadWriteCreate_Or_Append) to simple modes ReadWriteAppend or
1106/// ReadWriteCreate
1107fn solve_mode_variant(mode: Mode, dir_entry_is_some: bool) -> Mode {
1108    let mut mode = mode;
1109    if mode == Mode::ReadWriteCreateOrAppend {
1110        if dir_entry_is_some {
1111            mode = Mode::ReadWriteAppend;
1112        } else {
1113            mode = Mode::ReadWriteCreate;
1114        }
1115    } else if mode == Mode::ReadWriteCreateOrTruncate {
1116        if dir_entry_is_some {
1117            mode = Mode::ReadWriteTruncate;
1118        } else {
1119            mode = Mode::ReadWriteCreate;
1120        }
1121    }
1122    mode
1123}
1124
1125// ****************************************************************************
1126//
1127// Unit Tests
1128//
1129// ****************************************************************************
1130
1131#[cfg(test)]
1132mod tests {
1133    use super::*;
1134    use crate::filesystem::SearchId;
1135    use crate::Timestamp;
1136
1137    struct DummyBlockDevice;
1138
1139    struct Clock;
1140
1141    #[derive(Debug)]
1142    enum Error {
1143        Unknown,
1144    }
1145
1146    impl TimeSource for Clock {
1147        fn get_timestamp(&self) -> Timestamp {
1148            // TODO: Return actual time
1149            Timestamp {
1150                year_since_1970: 0,
1151                zero_indexed_month: 0,
1152                zero_indexed_day: 0,
1153                hours: 0,
1154                minutes: 0,
1155                seconds: 0,
1156            }
1157        }
1158    }
1159
1160    impl BlockDevice for DummyBlockDevice {
1161        type Error = Error;
1162
1163        /// Read one or more blocks, starting at the given block index.
1164        fn read(
1165            &self,
1166            blocks: &mut [Block],
1167            start_block_idx: BlockIdx,
1168            _reason: &str,
1169        ) -> Result<(), Self::Error> {
1170            // Actual blocks taken from an SD card, except I've changed the start and length of partition 0.
1171            static BLOCKS: [Block; 3] = [
1172                Block {
1173                    contents: [
1174                        0xfa, 0xb8, 0x00, 0x10, 0x8e, 0xd0, 0xbc, 0x00, 0xb0, 0xb8, 0x00, 0x00,
1175                        0x8e, 0xd8, 0x8e, 0xc0, // 0x000
1176                        0xfb, 0xbe, 0x00, 0x7c, 0xbf, 0x00, 0x06, 0xb9, 0x00, 0x02, 0xf3, 0xa4,
1177                        0xea, 0x21, 0x06, 0x00, // 0x010
1178                        0x00, 0xbe, 0xbe, 0x07, 0x38, 0x04, 0x75, 0x0b, 0x83, 0xc6, 0x10, 0x81,
1179                        0xfe, 0xfe, 0x07, 0x75, // 0x020
1180                        0xf3, 0xeb, 0x16, 0xb4, 0x02, 0xb0, 0x01, 0xbb, 0x00, 0x7c, 0xb2, 0x80,
1181                        0x8a, 0x74, 0x01, 0x8b, // 0x030
1182                        0x4c, 0x02, 0xcd, 0x13, 0xea, 0x00, 0x7c, 0x00, 0x00, 0xeb, 0xfe, 0x00,
1183                        0x00, 0x00, 0x00, 0x00, // 0x040
1184                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1185                        0x00, 0x00, 0x00, 0x00, // 0x050
1186                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1187                        0x00, 0x00, 0x00, 0x00, // 0x060
1188                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1189                        0x00, 0x00, 0x00, 0x00, // 0x070
1190                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1191                        0x00, 0x00, 0x00, 0x00, // 0x080
1192                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1193                        0x00, 0x00, 0x00, 0x00, // 0x090
1194                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1195                        0x00, 0x00, 0x00, 0x00, // 0x0A0
1196                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1197                        0x00, 0x00, 0x00, 0x00, // 0x0B0
1198                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1199                        0x00, 0x00, 0x00, 0x00, // 0x0C0
1200                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1201                        0x00, 0x00, 0x00, 0x00, // 0x0D0
1202                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1203                        0x00, 0x00, 0x00, 0x00, // 0x0E0
1204                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1205                        0x00, 0x00, 0x00, 0x00, // 0x0F0
1206                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1207                        0x00, 0x00, 0x00, 0x00, // 0x100
1208                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1209                        0x00, 0x00, 0x00, 0x00, // 0x110
1210                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1211                        0x00, 0x00, 0x00, 0x00, // 0x120
1212                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1213                        0x00, 0x00, 0x00, 0x00, // 0x130
1214                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1215                        0x00, 0x00, 0x00, 0x00, // 0x140
1216                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1217                        0x00, 0x00, 0x00, 0x00, // 0x150
1218                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1219                        0x00, 0x00, 0x00, 0x00, // 0x160
1220                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1221                        0x00, 0x00, 0x00, 0x00, // 0x170
1222                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1223                        0x00, 0x00, 0x00, 0x00, // 0x180
1224                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1225                        0x00, 0x00, 0x00, 0x00, // 0x190
1226                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1227                        0x00, 0x00, 0x00, 0x00, // 0x1A0
1228                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xca, 0xde, 0x06,
1229                        0x00, 0x00, 0x00, 0x04, // 0x1B0
1230                        0x01, 0x04, 0x0c, 0xfe, 0xc2, 0xff, 0x01, 0x00, 0x00, 0x00, 0x33, 0x22,
1231                        0x11, 0x00, 0x00, 0x00, // 0x1C0
1232                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1233                        0x00, 0x00, 0x00, 0x00, // 0x1D0
1234                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1235                        0x00, 0x00, 0x00, 0x00, // 0x1E0
1236                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1237                        0x00, 0x00, 0x55, 0xaa, // 0x1F0
1238                    ],
1239                },
1240                Block {
1241                    contents: [
1242                        0xeb, 0x58, 0x90, 0x6d, 0x6b, 0x66, 0x73, 0x2e, 0x66, 0x61, 0x74, 0x00,
1243                        0x02, 0x08, 0x20, 0x00, // 0x000
1244                        0x02, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x10, 0x00, 0x04, 0x00,
1245                        0x00, 0x08, 0x00, 0x00, // 0x010
1246                        0x00, 0x20, 0x76, 0x00, 0x80, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1247                        0x02, 0x00, 0x00, 0x00, // 0x020
1248                        0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1249                        0x00, 0x00, 0x00, 0x00, // 0x030
1250                        0x80, 0x01, 0x29, 0x0b, 0xa8, 0x89, 0x27, 0x50, 0x69, 0x63, 0x74, 0x75,
1251                        0x72, 0x65, 0x73, 0x20, // 0x040
1252                        0x20, 0x20, 0x46, 0x41, 0x54, 0x33, 0x32, 0x20, 0x20, 0x20, 0x0e, 0x1f,
1253                        0xbe, 0x77, 0x7c, 0xac, // 0x050
1254                        0x22, 0xc0, 0x74, 0x0b, 0x56, 0xb4, 0x0e, 0xbb, 0x07, 0x00, 0xcd, 0x10,
1255                        0x5e, 0xeb, 0xf0, 0x32, // 0x060
1256                        0xe4, 0xcd, 0x16, 0xcd, 0x19, 0xeb, 0xfe, 0x54, 0x68, 0x69, 0x73, 0x20,
1257                        0x69, 0x73, 0x20, 0x6e, // 0x070
1258                        0x6f, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c,
1259                        0x65, 0x20, 0x64, 0x69, // 0x080
1260                        0x73, 0x6b, 0x2e, 0x20, 0x20, 0x50, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20,
1261                        0x69, 0x6e, 0x73, 0x65, // 0x090
1262                        0x72, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c,
1263                        0x65, 0x20, 0x66, 0x6c, // 0x0A0
1264                        0x6f, 0x70, 0x70, 0x79, 0x20, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x70, 0x72,
1265                        0x65, 0x73, 0x73, 0x20, // 0x0B0
1266                        0x61, 0x6e, 0x79, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x74,
1267                        0x72, 0x79, 0x20, 0x61, // 0x0C0
1268                        0x67, 0x61, 0x69, 0x6e, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x0d, 0x0a, 0x00,
1269                        0x00, 0x00, 0x00, 0x00, // 0x0D0
1270                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1271                        0x00, 0x00, 0x00, 0x00, // 0x0E0
1272                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1273                        0x00, 0x00, 0x00, 0x00, // 0x0F0
1274                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1275                        0x00, 0x00, 0x00, 0x00, // 0x100
1276                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1277                        0x00, 0x00, 0x00, 0x00, // 0x110
1278                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1279                        0x00, 0x00, 0x00, 0x00, // 0x120
1280                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1281                        0x00, 0x00, 0x00, 0x00, // 0x130
1282                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1283                        0x00, 0x00, 0x00, 0x00, // 0x140
1284                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1285                        0x00, 0x00, 0x00, 0x00, // 0x150
1286                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1287                        0x00, 0x00, 0x00, 0x00, // 0x160
1288                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1289                        0x00, 0x00, 0x00, 0x00, // 0x170
1290                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1291                        0x00, 0x00, 0x00, 0x00, // 0x180
1292                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1293                        0x00, 0x00, 0x00, 0x00, // 0x190
1294                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1295                        0x00, 0x00, 0x00, 0x00, // 0x1A0
1296                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1297                        0x00, 0x00, 0x00, 0x00, // 0x1B0
1298                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1299                        0x00, 0x00, 0x00, 0x00, // 0x1C0
1300                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1301                        0x00, 0x00, 0x00, 0x00, // 0x1D0
1302                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1303                        0x00, 0x00, 0x00, 0x00, // 0x1E0
1304                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1305                        0x00, 0x00, 0x55, 0xaa, // 0x1F0
1306                    ],
1307                },
1308                Block {
1309                    contents: hex!(
1310                        "52 52 61 41 00 00 00 00 00 00 00 00 00 00 00 00
1311                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1312                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1313                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1314                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1315                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1316                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1317                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1318                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1319                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1320                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1321                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1322                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1323                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1324                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1325                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1326                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1327                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1328                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1329                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1330                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1331                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1332                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1333                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1334                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1335                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1336                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1337                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1338                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1339                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1340                         00 00 00 00 72 72 41 61 FF FF FF FF FF FF FF FF
1341                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA"
1342                    ),
1343                },
1344            ];
1345            println!(
1346                "Reading block {} to {}",
1347                start_block_idx.0,
1348                start_block_idx.0 as usize + blocks.len()
1349            );
1350            for (idx, block) in blocks.iter_mut().enumerate() {
1351                let block_idx = start_block_idx.0 as usize + idx;
1352                if block_idx < BLOCKS.len() {
1353                    *block = BLOCKS[block_idx].clone();
1354                } else {
1355                    return Err(Error::Unknown);
1356                }
1357            }
1358            Ok(())
1359        }
1360
1361        /// Write one or more blocks, starting at the given block index.
1362        fn write(&self, _blocks: &[Block], _start_block_idx: BlockIdx) -> Result<(), Self::Error> {
1363            unimplemented!();
1364        }
1365
1366        /// Determine how many blocks this device can hold.
1367        fn num_blocks(&self) -> Result<BlockCount, Self::Error> {
1368            Ok(BlockCount(2))
1369        }
1370    }
1371
1372    #[test]
1373    fn partition0() {
1374        let mut c: VolumeManager<DummyBlockDevice, Clock, 2, 2> =
1375            VolumeManager::new_with_limits(DummyBlockDevice, Clock, 0xAA00_0000);
1376
1377        let v = c.open_raw_volume(VolumeIdx(0)).unwrap();
1378        let expected_id = RawVolume(SearchId(0xAA00_0000));
1379        assert_eq!(v, expected_id);
1380        assert_eq!(
1381            &c.open_volumes[0],
1382            &VolumeInfo {
1383                volume_id: expected_id,
1384                idx: VolumeIdx(0),
1385                volume_type: VolumeType::Fat(crate::FatVolume {
1386                    lba_start: BlockIdx(1),
1387                    num_blocks: BlockCount(0x0011_2233),
1388                    blocks_per_cluster: 8,
1389                    first_data_block: BlockCount(15136),
1390                    fat_start: BlockCount(32),
1391                    second_fat_start: Some(BlockCount(32 + 0x0000_1D80)),
1392                    name: fat::VolumeName::new(*b"Pictures   "),
1393                    free_clusters_count: None,
1394                    next_free_cluster: None,
1395                    cluster_count: 965_788,
1396                    fat_specific_info: fat::FatSpecificInfo::Fat32(fat::Fat32Info {
1397                        first_root_dir_cluster: ClusterId(2),
1398                        info_location: BlockIdx(1) + BlockCount(1),
1399                    })
1400                })
1401            }
1402        );
1403    }
1404}
1405
1406// ****************************************************************************
1407//
1408// End Of File
1409//
1410// ****************************************************************************