Skip to main content

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::{ControlFlow, DerefMut};
8
9use byteorder::{ByteOrder, LittleEndian};
10use heapless::Vec;
11
12use crate::{
13    Block, BlockCache, BlockCount, BlockDevice, BlockIdx, Error, PARTITION_ID_FAT16,
14    PARTITION_ID_FAT16_LBA, PARTITION_ID_FAT16_SMALL, PARTITION_ID_FAT32_CHS_LBA,
15    PARTITION_ID_FAT32_LBA, RawVolume, ShortFileName, Volume, VolumeIdx, VolumeInfo, VolumeType,
16    debug, fat,
17    filesystem::{
18        Attributes, ClusterId, DirEntry, DirectoryInfo, FileInfo, HandleGenerator, LfnBuffer,
19        MAX_FILE_SIZE, Mode, RawDirectory, RawFile, TimeSource, ToShortFileName,
20    },
21    trace,
22};
23
24/// Wraps a block device and gives access to the FAT-formatted volumes within
25/// it.
26///
27/// Tracks which files and directories are open, to prevent you from deleting
28/// a file or directory you currently have open.
29#[derive(Debug)]
30pub struct VolumeManager<
31    D,
32    T,
33    const MAX_DIRS: usize = 4,
34    const MAX_FILES: usize = 4,
35    const MAX_VOLUMES: usize = 1,
36> where
37    D: BlockDevice,
38    T: TimeSource,
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{
49    /// Create a new Volume Manager using a generic `BlockDevice`. From this
50    /// object we can open volumes (partitions) and with those we can open
51    /// files.
52    ///
53    /// This creates a `VolumeManager` with default values
54    /// MAX_DIRS = 4, MAX_FILES = 4, MAX_VOLUMES = 1. Call `VolumeManager::new_with_limits(block_device, time_source)`
55    /// if you need different limits.
56    pub fn new(block_device: D, time_source: T) -> Self {
57        // Pick a random starting point for the IDs that's not zero, because
58        // zero doesn't stand out in the logs.
59        Self::new_with_limits(block_device, time_source, 5000)
60    }
61}
62
63impl<D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
64    VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
65where
66    D: BlockDevice,
67    T: TimeSource,
68{
69    /// Create a new Volume Manager using a generic `BlockDevice`. From this
70    /// object we can open volumes (partitions) and with those we can open
71    /// files.
72    ///
73    /// You can also give an offset for all the IDs this volume manager
74    /// generates, which might help you find the IDs in your logs when
75    /// debugging.
76    pub fn new_with_limits(block_device: D, time_source: T, id_offset: u32) -> Self {
77        debug!("Creating new embedded-sdmmc::VolumeManager");
78        VolumeManager {
79            time_source,
80            data: RefCell::new(VolumeManagerData {
81                block_cache: BlockCache::new(block_device),
82                id_generator: HandleGenerator::new(id_offset),
83                open_volumes: Vec::new(),
84                open_dirs: Vec::new(),
85                open_files: Vec::new(),
86            }),
87        }
88    }
89
90    /// Temporarily get access to the underlying block device.
91    pub fn device<R, F>(&self, f: F) -> R
92    where
93        F: FnOnce(&mut D) -> R,
94    {
95        let mut data = self.data.borrow_mut();
96
97        f(data.block_cache.block_device())
98    }
99
100    /// Get a volume (or partition) based on entries in the Master Boot Record.
101    ///
102    /// We do not support GUID Partition Table disks. Nor do we support any
103    /// concept of drive letters - that is for a higher layer to handle.
104    pub fn open_volume(
105        &self,
106        volume_idx: VolumeIdx,
107    ) -> Result<Volume<'_, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>, Error<D::Error>> {
108        let v = self.open_raw_volume(volume_idx)?;
109        Ok(v.to_volume(self))
110    }
111
112    /// Get a volume (or partition) based on entries in the Master Boot Record.
113    ///
114    /// We do not support GUID Partition Table disks. Nor do we support any
115    /// concept of drive letters - that is for a higher layer to handle.
116    ///
117    /// <div class="warning">
118    ///
119    /// This function gives you a [`RawVolume`] and when you are finished with
120    /// it, you **must** close the volume by calling
121    /// [`VolumeManager::close_volume`] otherwise you will leak internal
122    /// resources.
123    ///
124    /// </div>
125    ///
126    /// If you want a volume handle that closes itself on drop, see
127    /// [`Volume`](crate::Volume).
128    pub fn open_raw_volume(&self, volume_idx: VolumeIdx) -> Result<RawVolume, Error<D::Error>> {
129        const PARTITION1_START: usize = 446;
130        const PARTITION2_START: usize = PARTITION1_START + PARTITION_INFO_LENGTH;
131        const PARTITION3_START: usize = PARTITION2_START + PARTITION_INFO_LENGTH;
132        const PARTITION4_START: usize = PARTITION3_START + PARTITION_INFO_LENGTH;
133        const FOOTER_START: usize = 510;
134        const FOOTER_VALUE: u16 = 0xAA55;
135        const PARTITION_INFO_LENGTH: usize = 16;
136        const PARTITION_INFO_STATUS_INDEX: usize = 0;
137        const PARTITION_INFO_TYPE_INDEX: usize = 4;
138        const PARTITION_INFO_LBA_START_INDEX: usize = 8;
139        const PARTITION_INFO_NUM_BLOCKS_INDEX: usize = 12;
140
141        let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
142
143        if data.open_volumes.is_full() {
144            return Err(Error::TooManyOpenVolumes);
145        }
146
147        for v in data.open_volumes.iter() {
148            if v.idx == volume_idx {
149                return Err(Error::VolumeAlreadyOpen);
150            }
151        }
152
153        let (part_type, lba_start, num_blocks) = {
154            trace!("Reading partition table");
155            let block = data
156                .block_cache
157                .read(BlockIdx(0))
158                .map_err(Error::DeviceError)?;
159            // We only support Master Boot Record (MBR) partitioned cards, not
160            // GUID Partition Table (GPT)
161            if LittleEndian::read_u16(&block[FOOTER_START..FOOTER_START + 2]) != FOOTER_VALUE {
162                return Err(Error::FormatError("Invalid MBR signature"));
163            }
164            let partition = match volume_idx {
165                VolumeIdx(0) => {
166                    &block[PARTITION1_START..(PARTITION1_START + PARTITION_INFO_LENGTH)]
167                }
168                VolumeIdx(1) => {
169                    &block[PARTITION2_START..(PARTITION2_START + PARTITION_INFO_LENGTH)]
170                }
171                VolumeIdx(2) => {
172                    &block[PARTITION3_START..(PARTITION3_START + PARTITION_INFO_LENGTH)]
173                }
174                VolumeIdx(3) => {
175                    &block[PARTITION4_START..(PARTITION4_START + PARTITION_INFO_LENGTH)]
176                }
177                _ => {
178                    return Err(Error::NoSuchVolume);
179                }
180            };
181            // Only 0x80 and 0x00 are valid (bootable, and non-bootable)
182            if (partition[PARTITION_INFO_STATUS_INDEX] & 0x7F) != 0x00 {
183                return Err(Error::FormatError("Invalid partition status"));
184            }
185            let lba_start = LittleEndian::read_u32(
186                &partition[PARTITION_INFO_LBA_START_INDEX..(PARTITION_INFO_LBA_START_INDEX + 4)],
187            );
188            let num_blocks = LittleEndian::read_u32(
189                &partition[PARTITION_INFO_NUM_BLOCKS_INDEX..(PARTITION_INFO_NUM_BLOCKS_INDEX + 4)],
190            );
191            (
192                partition[PARTITION_INFO_TYPE_INDEX],
193                BlockIdx(lba_start),
194                BlockCount(num_blocks),
195            )
196        };
197        match part_type {
198            PARTITION_ID_FAT32_CHS_LBA
199            | PARTITION_ID_FAT32_LBA
200            | PARTITION_ID_FAT16_LBA
201            | PARTITION_ID_FAT16
202            | PARTITION_ID_FAT16_SMALL => {
203                let volume = fat::parse_volume(&mut data.block_cache, lba_start, num_blocks)?;
204                let id = RawVolume(data.id_generator.generate());
205                let info = VolumeInfo {
206                    raw_volume: id,
207                    idx: volume_idx,
208                    volume_type: volume,
209                };
210                // We already checked for space
211                data.open_volumes.push(info).unwrap();
212                Ok(id)
213            }
214            _ => Err(Error::FormatError("Partition type not supported")),
215        }
216    }
217
218    /// Open the volume's root directory.
219    ///
220    /// You can then read the directory entries with `iterate_dir`, or you can
221    /// use `open_file_in_dir`.
222    ///
223    /// <div class="warning">
224    ///
225    /// This function gives you a [`RawDirectory`] and when you are finished
226    /// with it, you **must** close the directory by calling
227    /// [`VolumeManager::close_dir`] otherwise you will leak internal
228    /// resources.
229    ///
230    /// </div>
231    ///
232    /// If you want a directory handle that closes itself on drop, see
233    /// [`Directory`](crate::Directory).
234    pub fn open_root_dir(&self, volume: RawVolume) -> Result<RawDirectory, Error<D::Error>> {
235        debug!("Opening root on {:?}", volume);
236
237        // Opening a root directory twice is OK
238        let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
239
240        let directory_id = RawDirectory(data.id_generator.generate());
241        let dir_info = DirectoryInfo {
242            raw_volume: volume,
243            cluster: ClusterId::ROOT_DIR,
244            raw_directory: directory_id,
245        };
246
247        data.open_dirs
248            .push(dir_info)
249            .map_err(|_| Error::TooManyOpenDirs)?;
250
251        debug!("Opened root on {:?}, got {:?}", volume, directory_id);
252
253        Ok(directory_id)
254    }
255
256    /// Open a directory.
257    ///
258    /// You can then read the directory entries with `iterate_dir` and `open_file_in_dir`.
259    ///
260    /// Passing "." as the name results in opening the `parent_dir` a second time.
261    ///
262    /// <div class="warning">
263    ///
264    /// This function gives you a [`RawDirectory`] and when you are finished
265    /// with it, you **must** close the directory by calling
266    /// [`VolumeManager::close_dir`] otherwise you will leak internal
267    /// resources.
268    ///
269    /// </div>
270    ///
271    /// If you want a directory handle that closes itself on drop, see
272    /// [`Directory`](crate::Directory).
273    pub fn open_dir<N>(
274        &self,
275        parent_dir: RawDirectory,
276        name: N,
277    ) -> Result<RawDirectory, Error<D::Error>>
278    where
279        N: ToShortFileName,
280    {
281        let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
282        let data = data.deref_mut();
283
284        if data.open_dirs.is_full() {
285            return Err(Error::TooManyOpenDirs);
286        }
287
288        // Find dir by ID
289        let parent_dir_idx = data.get_dir_by_id(parent_dir)?;
290        let volume_idx = data.get_volume_by_id(data.open_dirs[parent_dir_idx].raw_volume)?;
291        let short_file_name = name.to_short_filename().map_err(Error::FilenameError)?;
292
293        // Open the directory
294
295        // Should we short-cut? (root dir doesn't have ".")
296        if short_file_name == ShortFileName::this_dir() {
297            let directory_id = RawDirectory(data.id_generator.generate());
298            let dir_info = DirectoryInfo {
299                raw_directory: directory_id,
300                raw_volume: data.open_volumes[volume_idx].raw_volume,
301                cluster: data.open_dirs[parent_dir_idx].cluster,
302            };
303
304            data.open_dirs
305                .push(dir_info)
306                .map_err(|_| Error::TooManyOpenDirs)?;
307
308            return Ok(directory_id);
309        }
310
311        // ok we'll actually look for the directory then
312
313        let dir_entry = match &data.open_volumes[volume_idx].volume_type {
314            VolumeType::Fat(fat) => fat.find_directory_entry(
315                &mut data.block_cache,
316                &data.open_dirs[parent_dir_idx],
317                &short_file_name,
318            )?,
319        };
320
321        debug!("Found dir entry: {:?}", dir_entry);
322
323        if !dir_entry.attributes.is_directory() {
324            return Err(Error::OpenedFileAsDir);
325        }
326
327        // We don't check if the directory is already open - directories hold
328        // no cached state and so opening a directory twice is allowable.
329
330        // Remember this open directory.
331        let directory_id = RawDirectory(data.id_generator.generate());
332        let dir_info = DirectoryInfo {
333            raw_directory: directory_id,
334            raw_volume: data.open_volumes[volume_idx].raw_volume,
335            cluster: dir_entry.cluster,
336        };
337
338        data.open_dirs
339            .push(dir_info)
340            .map_err(|_| Error::TooManyOpenDirs)?;
341
342        Ok(directory_id)
343    }
344
345    /// Close a directory.
346    ///
347    /// This releases internal resources and renders the given
348    /// [`RawDirectory`] unusable (although you should discard the value
349    /// rather than relying on getting an error if you do attempt to use it
350    /// again).
351    pub fn close_dir(&self, directory: RawDirectory) -> Result<(), Error<D::Error>> {
352        debug!("Closing {:?}", directory);
353        let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
354
355        for (idx, info) in data.open_dirs.iter().enumerate() {
356            if directory == info.raw_directory {
357                data.open_dirs.swap_remove(idx);
358                return Ok(());
359            }
360        }
361        Err(Error::BadHandle)
362    }
363
364    /// Close a volume.
365    ///
366    /// You can't close it if there are any files or directories open on it.
367    ///
368    /// If the info sector update (which is non-critical) fails, the volume is
369    /// closed anyway and the resulting error is returned.
370    ///
371    /// This releases internal resources and renders the given
372    /// [`RawVolume`] unusable (although you should discard the value rather
373    /// than relying on getting an error if you do attempt to use it again).
374    pub fn close_volume(&self, volume: RawVolume) -> Result<(), Error<D::Error>> {
375        let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
376        let data = data.deref_mut();
377
378        for f in data.open_files.iter() {
379            if f.raw_volume == volume {
380                return Err(Error::VolumeStillInUse);
381            }
382        }
383
384        for d in data.open_dirs.iter() {
385            if d.raw_volume == volume {
386                return Err(Error::VolumeStillInUse);
387            }
388        }
389
390        let volume_idx = data.get_volume_by_id(volume)?;
391
392        let update_result = match &mut data.open_volumes[volume_idx].volume_type {
393            VolumeType::Fat(fat) => fat.update_info_sector(&mut data.block_cache),
394        };
395
396        data.open_volumes.swap_remove(volume_idx);
397
398        update_result
399    }
400
401    /// Look in a directory for a named file.
402    ///
403    /// You will either get [`DirEntry`] with a matching file name, or an
404    /// error. The [`DirEntry`] will give you the file size, creation date,
405    /// etc.
406    ///
407    /// You can just drop the [`DirEntry`] when you are done with it - it's a
408    /// stand-alone object and takes up no resources in this Volume Manager.
409    ///
410    /// # Open Files
411    ///
412    /// The file length and last update time may be wrong for any currently
413    /// open files that have not been flushed.
414    pub fn find_directory_entry<N>(
415        &self,
416        directory: RawDirectory,
417        name: N,
418    ) -> Result<DirEntry, Error<D::Error>>
419    where
420        N: ToShortFileName,
421    {
422        let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
423        let data = data.deref_mut();
424
425        let directory_idx = data.get_dir_by_id(directory)?;
426        let volume_idx = data.get_volume_by_id(data.open_dirs[directory_idx].raw_volume)?;
427        match &data.open_volumes[volume_idx].volume_type {
428            VolumeType::Fat(fat) => {
429                let sfn = name.to_short_filename().map_err(Error::FilenameError)?;
430                fat.find_directory_entry(
431                    &mut data.block_cache,
432                    &data.open_dirs[directory_idx],
433                    &sfn,
434                )
435            }
436        }
437    }
438
439    /// Call a callback function for each directory entry in a directory.
440    ///
441    /// Long File Names will be ignored.
442    ///
443    /// <div class="warning">
444    ///
445    /// Do not attempt to call any methods on the VolumeManager or any of its
446    /// handles from inside the callback. You will get a lock error because the
447    /// object is already locked in order to do the iteration.
448    ///
449    /// </div>
450    ///
451    /// # Open Files
452    ///
453    /// The file length and last update time may be wrong for any currently
454    /// open files that have not been flushed.
455    pub fn iterate_dir<F>(
456        &self,
457        directory: RawDirectory,
458        mut func: F,
459    ) -> Result<(), Error<D::Error>>
460    where
461        F: FnMut(&DirEntry) -> ControlFlow<()>,
462    {
463        let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
464        let data = data.deref_mut();
465
466        let directory_idx = data.get_dir_by_id(directory)?;
467        let volume_idx = data.get_volume_by_id(data.open_dirs[directory_idx].raw_volume)?;
468        match &data.open_volumes[volume_idx].volume_type {
469            VolumeType::Fat(fat) => {
470                fat.iterate_dir(
471                    &mut data.block_cache,
472                    &data.open_dirs[directory_idx],
473                    |de| {
474                        // Hide all the LFN directory entries
475                        if !de.attributes.is_lfn() {
476                            func(de)
477                        } else {
478                            ControlFlow::Continue(())
479                        }
480                    },
481                )
482            }
483        }
484    }
485
486    /// Call a callback function for each directory entry in a directory, and
487    /// process Long File Names.
488    ///
489    /// You must supply a [`LfnBuffer`] this API can use to temporarily hold the
490    /// Long File Name. If you pass one that isn't large enough, any Long File
491    /// Names that don't fit will be ignored and presented as if they only had a
492    /// Short File Name.
493    ///
494    /// <div class="warning">
495    ///
496    /// Do not attempt to call any methods on the VolumeManager or any of its
497    /// handles from inside the callback. You will get a lock error because the
498    /// object is already locked in order to do the iteration.
499    ///
500    /// </div>
501    ///
502    /// # Open Files
503    ///
504    /// The file length and last update time may be wrong for any currently
505    /// open files that have not been flushed.
506    pub fn iterate_dir_lfn<F>(
507        &self,
508        directory: RawDirectory,
509        lfn_buffer: &mut LfnBuffer<'_>,
510        func: F,
511    ) -> Result<(), Error<D::Error>>
512    where
513        F: FnMut(&DirEntry, Option<&str>) -> ControlFlow<()>,
514    {
515        let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
516        let data = data.deref_mut();
517
518        let directory_idx = data.get_dir_by_id(directory)?;
519        let volume_idx = data.get_volume_by_id(data.open_dirs[directory_idx].raw_volume)?;
520
521        match &data.open_volumes[volume_idx].volume_type {
522            VolumeType::Fat(fat) => {
523                // This API doesn't care about the on-disk directory entry, so we discard it
524                fat.iterate_dir_lfn(
525                    &mut data.block_cache,
526                    lfn_buffer,
527                    &data.open_dirs[directory_idx],
528                    func,
529                )
530            }
531        }
532    }
533
534    /// Open a file with the given short file name, in the given directory.
535    ///
536    /// <div class="warning">
537    ///
538    /// This function gives you a [`RawFile`] and when you are finished with
539    /// it, you **must** close the file by calling
540    /// [`VolumeManager::close_file`] otherwise you will leak internal
541    /// resources and/or suffer file-system corruption and data loss.
542    ///
543    /// </div>
544    ///
545    /// If you want a file handle that closes itself on drop, see
546    /// [`File`](crate::File).
547    pub fn open_file_in_dir<N>(
548        &self,
549        directory: RawDirectory,
550        name: N,
551        mode: Mode,
552    ) -> Result<RawFile, Error<D::Error>>
553    where
554        N: ToShortFileName,
555    {
556        let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
557        let data = data.deref_mut();
558
559        // This check is load-bearing - we do an unchecked push later.
560        if data.open_files.is_full() {
561            return Err(Error::TooManyOpenFiles);
562        }
563
564        let directory_idx = data.get_dir_by_id(directory)?;
565        let volume_id = data.open_dirs[directory_idx].raw_volume;
566        let volume_idx = data.get_volume_by_id(volume_id)?;
567        let volume_info = &data.open_volumes[volume_idx];
568        let sfn = name.to_short_filename().map_err(Error::FilenameError)?;
569
570        let dir_entry = match &volume_info.volume_type {
571            VolumeType::Fat(fat) => fat.find_directory_entry(
572                &mut data.block_cache,
573                &data.open_dirs[directory_idx],
574                &sfn,
575            ),
576        };
577
578        let dir_entry = match dir_entry {
579            Ok(entry) => {
580                // we are opening an existing file
581                Some(entry)
582            }
583            Err(_)
584                if (mode == Mode::ReadWriteCreate)
585                    | (mode == Mode::ReadWriteCreateOrTruncate)
586                    | (mode == Mode::ReadWriteCreateOrAppend) =>
587            {
588                // We are opening a non-existant file, but that's OK because they
589                // asked us to create it
590                None
591            }
592            _ => {
593                // We are opening a non-existant file, and that's not OK.
594                return Err(Error::NotFound);
595            }
596        };
597
598        // Check if it's open already
599        if let Some(dir_entry) = &dir_entry {
600            if data.file_is_open(volume_info.raw_volume, dir_entry) {
601                return Err(Error::FileAlreadyOpen);
602            }
603        }
604
605        let mode = solve_mode_variant(mode, dir_entry.is_some());
606
607        match mode {
608            Mode::ReadWriteCreate => {
609                if dir_entry.is_some() {
610                    return Err(Error::FileAlreadyExists);
611                }
612                let cluster = data.open_dirs[directory_idx].cluster;
613                let att = Attributes::create_from_fat(0);
614                let volume_idx = data.get_volume_by_id(volume_id)?;
615                let entry = match &mut data.open_volumes[volume_idx].volume_type {
616                    VolumeType::Fat(fat) => fat.write_new_directory_entry(
617                        &mut data.block_cache,
618                        &self.time_source,
619                        cluster,
620                        sfn,
621                        att,
622                    )?,
623                };
624
625                let file_id = RawFile(data.id_generator.generate());
626
627                let file = FileInfo {
628                    raw_file: file_id,
629                    raw_volume: volume_id,
630                    current_cluster: (0, entry.cluster),
631                    current_offset: 0,
632                    mode,
633                    entry,
634                    dirty: false,
635                };
636
637                // Remember this open file - can't be full as we checked already
638                unsafe {
639                    data.open_files.push_unchecked(file);
640                }
641
642                Ok(file_id)
643            }
644            _ => {
645                // Safe to unwrap, since we actually have an entry if we got here
646                let dir_entry = dir_entry.unwrap();
647
648                if dir_entry.attributes.is_read_only() && mode != Mode::ReadOnly {
649                    return Err(Error::ReadOnly);
650                }
651
652                if dir_entry.attributes.is_directory() {
653                    return Err(Error::OpenedDirAsFile);
654                }
655
656                // Check it's not already open
657                if data.file_is_open(volume_id, &dir_entry) {
658                    return Err(Error::FileAlreadyOpen);
659                }
660
661                let mode = solve_mode_variant(mode, true);
662                let raw_file = RawFile(data.id_generator.generate());
663
664                let file = match mode {
665                    Mode::ReadOnly => FileInfo {
666                        raw_file,
667                        raw_volume: volume_id,
668                        current_cluster: (0, dir_entry.cluster),
669                        current_offset: 0,
670                        mode,
671                        entry: dir_entry,
672                        dirty: false,
673                    },
674                    Mode::ReadWriteAppend => {
675                        let mut file = FileInfo {
676                            raw_file,
677                            raw_volume: volume_id,
678                            current_cluster: (0, dir_entry.cluster),
679                            current_offset: 0,
680                            mode,
681                            entry: dir_entry,
682                            dirty: false,
683                        };
684                        // seek_from_end with 0 can't fail
685                        file.seek_from_end(0).ok();
686                        file
687                    }
688                    Mode::ReadWriteTruncate => {
689                        let mut file = FileInfo {
690                            raw_file,
691                            raw_volume: volume_id,
692                            current_cluster: (0, dir_entry.cluster),
693                            current_offset: 0,
694                            mode,
695                            entry: dir_entry,
696                            dirty: false,
697                        };
698                        match &mut data.open_volumes[volume_idx].volume_type {
699                            VolumeType::Fat(fat) => fat.truncate_cluster_chain(
700                                &mut data.block_cache,
701                                file.entry.cluster,
702                            )?,
703                        };
704                        file.update_length(0);
705                        match &data.open_volumes[volume_idx].volume_type {
706                            VolumeType::Fat(fat) => {
707                                file.entry.mtime = self.time_source.get_timestamp();
708                                fat.write_entry_to_disk(&mut data.block_cache, &file.entry)?;
709                            }
710                        };
711
712                        file
713                    }
714                    _ => return Err(Error::Unsupported),
715                };
716
717                // Remember this open file - can't be full as we checked already
718                unsafe {
719                    data.open_files.push_unchecked(file);
720                }
721
722                Ok(raw_file)
723            }
724        }
725    }
726
727    /// Open a file with the given Unicode long file name, in the given directory.
728    ///
729    /// You can only open existing long-file-name files - you cannot create them.
730    ///
731    /// <div class="warning">
732    ///
733    /// This function gives you a [`RawFile`] and when you are finished with
734    /// it, you **must** close the file by calling
735    /// [`VolumeManager::close_file`] otherwise you will leak internal
736    /// resources and/or suffer file-system corruption and data loss.
737    ///
738    /// </div>
739    ///
740    /// If you want a file handle that closes itself on drop, see
741    /// [`File`](crate::File).
742    pub fn open_long_name_file_in_dir(
743        &self,
744        directory: RawDirectory,
745        name: &str,
746        mode: Mode,
747    ) -> Result<RawFile, Error<D::Error>> {
748        let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
749        let data = data.deref_mut();
750
751        // This check is load-bearing - we do an unchecked push later.
752        if data.open_files.is_full() {
753            return Err(Error::TooManyOpenFiles);
754        }
755
756        let directory_idx = data.get_dir_by_id(directory)?;
757        let volume_id = data.open_dirs[directory_idx].raw_volume;
758        let volume_idx = data.get_volume_by_id(volume_id)?;
759        let volume_info = &data.open_volumes[volume_idx];
760
761        let dir_entry = match &volume_info.volume_type {
762            VolumeType::Fat(fat) => fat.find_directory_entry_by_lfn(
763                &mut data.block_cache,
764                &data.open_dirs[directory_idx],
765                name,
766            ),
767        };
768
769        let dir_entry = match dir_entry {
770            Ok(entry) => {
771                // we are opening an existing file
772                entry
773            }
774            Err(_)
775                if (mode == Mode::ReadWriteCreate)
776                    | (mode == Mode::ReadWriteCreateOrTruncate)
777                    | (mode == Mode::ReadWriteCreateOrAppend) =>
778            {
779                // We are opening a non-existant file and we cannot do that with LFNs
780                return Err(Error::NotFound);
781            }
782            _ => {
783                // We are opening a non-existant file, and that's not OK.
784                return Err(Error::NotFound);
785            }
786        };
787
788        // Check if it's open already
789        if data.file_is_open(volume_info.raw_volume, &dir_entry) {
790            return Err(Error::FileAlreadyOpen);
791        }
792
793        let mode = solve_mode_variant(mode, true);
794
795        match mode {
796            Mode::ReadWriteCreate => Err(Error::FileAlreadyExists),
797            _ => {
798                if dir_entry.attributes.is_read_only() && mode != Mode::ReadOnly {
799                    return Err(Error::ReadOnly);
800                }
801
802                if dir_entry.attributes.is_directory() {
803                    return Err(Error::OpenedDirAsFile);
804                }
805
806                // Check it's not already open
807                if data.file_is_open(volume_id, &dir_entry) {
808                    return Err(Error::FileAlreadyOpen);
809                }
810
811                let mode = solve_mode_variant(mode, true);
812                let raw_file = RawFile(data.id_generator.generate());
813
814                let file = match mode {
815                    Mode::ReadOnly => FileInfo {
816                        raw_file,
817                        raw_volume: volume_id,
818                        current_cluster: (0, dir_entry.cluster),
819                        current_offset: 0,
820                        mode,
821                        entry: dir_entry,
822                        dirty: false,
823                    },
824                    Mode::ReadWriteAppend => {
825                        let mut file = FileInfo {
826                            raw_file,
827                            raw_volume: volume_id,
828                            current_cluster: (0, dir_entry.cluster),
829                            current_offset: 0,
830                            mode,
831                            entry: dir_entry,
832                            dirty: false,
833                        };
834                        // seek_from_end with 0 can't fail
835                        file.seek_from_end(0).ok();
836                        file
837                    }
838                    Mode::ReadWriteTruncate => {
839                        let mut file = FileInfo {
840                            raw_file,
841                            raw_volume: volume_id,
842                            current_cluster: (0, dir_entry.cluster),
843                            current_offset: 0,
844                            mode,
845                            entry: dir_entry,
846                            dirty: false,
847                        };
848                        match &mut data.open_volumes[volume_idx].volume_type {
849                            VolumeType::Fat(fat) => fat.truncate_cluster_chain(
850                                &mut data.block_cache,
851                                file.entry.cluster,
852                            )?,
853                        };
854                        file.update_length(0);
855                        match &data.open_volumes[volume_idx].volume_type {
856                            VolumeType::Fat(fat) => {
857                                file.entry.mtime = self.time_source.get_timestamp();
858                                fat.write_entry_to_disk(&mut data.block_cache, &file.entry)?;
859                            }
860                        };
861
862                        file
863                    }
864                    _ => return Err(Error::Unsupported),
865                };
866
867                // Remember this open file - can't be full as we checked already
868                unsafe {
869                    data.open_files.push_unchecked(file);
870                }
871
872                Ok(raw_file)
873            }
874        }
875    }
876
877    /// Delete a closed file or empty directory with the given filename, if it exists.
878    pub fn delete_entry_in_dir<N>(
879        &self,
880        directory: RawDirectory,
881        name: N,
882    ) -> Result<(), Error<D::Error>>
883    where
884        N: ToShortFileName,
885    {
886        let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
887        let data = data.deref_mut();
888
889        let dir_idx = data.get_dir_by_id(directory)?;
890        let parent_dir_info = &data.open_dirs[dir_idx];
891        let volume_idx = data.get_volume_by_id(parent_dir_info.raw_volume)?;
892        let sfn = name.to_short_filename().map_err(Error::FilenameError)?;
893
894        let dir_entry = match &data.open_volumes[volume_idx].volume_type {
895            VolumeType::Fat(fat) => {
896                fat.find_directory_entry(&mut data.block_cache, parent_dir_info, &sfn)
897            }
898        }?;
899
900        if dir_entry.attributes.is_directory() {
901            // Find the directory to be deleted, so that we can check its contents.
902            if data
903                .open_dirs
904                .iter()
905                .any(|dir_info| dir_info.cluster == dir_entry.cluster)
906            {
907                // Subdirectory is already open.
908                return Err(Error::DirAlreadyOpen);
909            }
910            // The subdirectory isn't yet open. Open it in order to be able to list it.
911            let raw_directory = RawDirectory(data.id_generator.generate());
912            let dir_info = DirectoryInfo {
913                raw_directory,
914                raw_volume: data.open_volumes[volume_idx].raw_volume,
915                cluster: dir_entry.cluster,
916            };
917            // Can only delete directories that are already empty.
918            let mut count = 0;
919            // Equivalent to `self.iterate_dir(raw_dir, |_| count += 1)?;`, without locking again.
920            match &data.open_volumes[volume_idx].volume_type {
921                VolumeType::Fat(fat) => {
922                    fat.iterate_dir(&mut data.block_cache, &dir_info, |de| {
923                        // Hide all the LFN directory entries
924                        if !de.attributes.is_lfn()
925                            && de.name != ShortFileName::this_dir()
926                            && de.name != ShortFileName::parent_dir()
927                        {
928                            count += 1;
929                        }
930                        ControlFlow::Continue(())
931                    })?;
932                }
933            }
934            if count != 0 {
935                return Err(Error::DeleteNonEmptyDir);
936            }
937        } else if data.file_is_open(parent_dir_info.raw_volume, &dir_entry) {
938            return Err(Error::FileAlreadyOpen);
939        }
940
941        let volume_idx = data.get_volume_by_id(parent_dir_info.raw_volume)?;
942        match &data.open_volumes[volume_idx].volume_type {
943            VolumeType::Fat(fat) => {
944                fat.delete_directory_entry(&mut data.block_cache, parent_dir_info, &sfn)?
945            }
946        }
947
948        Ok(())
949    }
950
951    /// Get the volume label
952    ///
953    /// Will look in the filesystem metadata for a volume label, and if
954    /// nothing is found, will search the root directory for a volume label.
955    pub fn get_root_volume_label(
956        &self,
957        raw_volume: RawVolume,
958    ) -> Result<Option<crate::VolumeName>, Error<D::Error>> {
959        debug!("Reading volume label for {:?}", raw_volume);
960        // prefer the one in the BPB - it's easier to get
961        let data = self.data.try_borrow().map_err(|_| Error::LockError)?;
962        let volume_idx = data.get_volume_by_id(raw_volume)?;
963        match &data.open_volumes[volume_idx].volume_type {
964            VolumeType::Fat(fat) => {
965                if !fat.name.name().is_empty() {
966                    debug!(
967                        "Got volume label {:?} for {:?} from BPB",
968                        fat.name, raw_volume
969                    );
970                    return Ok(Some(fat.name.clone()));
971                }
972            }
973        }
974        drop(data);
975
976        // Nothing in the BPB, let's do it the slow way
977        let root_dir = self.open_root_dir(raw_volume)?.to_directory(self);
978        let mut maybe_volume_name = None;
979        root_dir.iterate_dir(|de| {
980            if maybe_volume_name.is_none()
981                && de.attributes == Attributes::create_from_fat(Attributes::VOLUME)
982            {
983                maybe_volume_name = Some(unsafe { de.name.to_volume_label() });
984                ControlFlow::Break(())
985            } else {
986                ControlFlow::Continue(())
987            }
988        })?;
989
990        debug!(
991            "Got volume label {:?} for {:?} from root",
992            maybe_volume_name, raw_volume
993        );
994
995        Ok(maybe_volume_name)
996    }
997
998    /// Read from an open file.
999    ///
1000    /// We read as many bytes as we can, stopping at either the length of
1001    /// `buffer`, or the end of the file.
1002    ///
1003    /// The number of bytes written to `buffer` is returned on success,
1004    /// otherwise you get an error and you should not rely on either the
1005    /// current seek position or the contents of `buffer`.
1006    pub fn read(&self, file: RawFile, buffer: &mut [u8]) -> Result<usize, Error<D::Error>> {
1007        let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
1008        let data = data.deref_mut();
1009
1010        let file_idx = data.get_file_by_id(file)?;
1011        let volume_idx = data.get_volume_by_id(data.open_files[file_idx].raw_volume)?;
1012
1013        // Calculate which file block the current offset lies within
1014        // While there is more to read, read the block and copy in to the buffer.
1015        // If we need to find the next cluster, walk the FAT.
1016        let mut space = buffer.len();
1017        let mut read = 0;
1018        while space > 0 && !data.open_files[file_idx].eof() {
1019            let mut current_cluster = data.open_files[file_idx].current_cluster;
1020            let (block_idx, block_offset, block_avail) = data.find_data_on_disk(
1021                volume_idx,
1022                &mut current_cluster,
1023                data.open_files[file_idx].entry.cluster,
1024                data.open_files[file_idx].current_offset,
1025            )?;
1026            data.open_files[file_idx].current_cluster = current_cluster;
1027            trace!("Reading file ID {:?}", file);
1028            let block = data
1029                .block_cache
1030                .read(block_idx)
1031                .map_err(Error::DeviceError)?;
1032            let to_copy = block_avail
1033                .min(space)
1034                .min(data.open_files[file_idx].left() as usize);
1035            assert!(to_copy != 0);
1036            buffer[read..read + to_copy]
1037                .copy_from_slice(&block[block_offset..block_offset + to_copy]);
1038            read += to_copy;
1039            space -= to_copy;
1040            data.open_files[file_idx]
1041                .seek_from_current(to_copy as i32)
1042                .unwrap();
1043        }
1044        Ok(read)
1045    }
1046
1047    /// Write to a open file.
1048    ///
1049    /// Endeavours to write the entire contents of the slice, stopping only if
1050    /// there is an error reading from or writing to the disk, or if the
1051    /// volume runs out of space.
1052    ///
1053    /// If you get an error, then you cannot be sure how much of `buffer` was
1054    /// successfully written, nor can you rely on the current seek position.
1055    pub fn write(&self, file: RawFile, buffer: &[u8]) -> Result<(), Error<D::Error>> {
1056        #[cfg(feature = "defmt-log")]
1057        debug!("write(file={:?}, buffer={:x}", file, buffer);
1058
1059        #[cfg(feature = "log")]
1060        debug!("write(file={:?}, buffer={:x?}", file, buffer);
1061
1062        let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
1063        let data = data.deref_mut();
1064
1065        // Clone this so we can touch our other structures. Need to ensure we
1066        // write it back at the end.
1067        let file_idx = data.get_file_by_id(file)?;
1068        let volume_idx = data.get_volume_by_id(data.open_files[file_idx].raw_volume)?;
1069
1070        if data.open_files[file_idx].mode == Mode::ReadOnly {
1071            return Err(Error::ReadOnly);
1072        }
1073
1074        data.open_files[file_idx].dirty = true;
1075
1076        if data.open_files[file_idx].entry.cluster.0 < fat::RESERVED_ENTRIES {
1077            // file doesn't have a valid allocated cluster (possible zero-length file), allocate one
1078            data.open_files[file_idx].entry.cluster =
1079                match data.open_volumes[volume_idx].volume_type {
1080                    VolumeType::Fat(ref mut fat) => {
1081                        fat.alloc_cluster(&mut data.block_cache, None, false)?
1082                    }
1083                };
1084            debug!(
1085                "Alloc first cluster {:?}",
1086                data.open_files[file_idx].entry.cluster
1087            );
1088        }
1089
1090        // Clone this so we can touch our other structures.
1091        let volume_idx = data.get_volume_by_id(data.open_files[file_idx].raw_volume)?;
1092
1093        if (data.open_files[file_idx].current_cluster.1) < data.open_files[file_idx].entry.cluster {
1094            debug!("Rewinding to start");
1095            data.open_files[file_idx].current_cluster =
1096                (0, data.open_files[file_idx].entry.cluster);
1097        }
1098        let bytes_until_max =
1099            usize::try_from(MAX_FILE_SIZE - data.open_files[file_idx].current_offset)
1100                .map_err(|_| Error::ConversionError)?;
1101        let bytes_to_write = core::cmp::min(buffer.len(), bytes_until_max);
1102        let mut written = 0;
1103
1104        while written < bytes_to_write {
1105            let mut current_cluster = data.open_files[file_idx].current_cluster;
1106            debug!(
1107                "Have written bytes {}/{}, finding cluster {:?}",
1108                written, bytes_to_write, current_cluster
1109            );
1110            let current_offset = data.open_files[file_idx].current_offset;
1111            let (block_idx, block_offset, block_avail) = match data.find_data_on_disk(
1112                volume_idx,
1113                &mut current_cluster,
1114                data.open_files[file_idx].entry.cluster,
1115                current_offset,
1116            ) {
1117                Ok(vars) => {
1118                    debug!(
1119                        "Found block_idx={:?}, block_offset={:?}, block_avail={}",
1120                        vars.0, vars.1, vars.2
1121                    );
1122                    vars
1123                }
1124                Err(Error::EndOfFile) => {
1125                    debug!("Extending file");
1126                    match data.open_volumes[volume_idx].volume_type {
1127                        VolumeType::Fat(ref mut fat) => {
1128                            if fat
1129                                .alloc_cluster(
1130                                    &mut data.block_cache,
1131                                    Some(current_cluster.1),
1132                                    false,
1133                                )
1134                                .is_err()
1135                            {
1136                                return Err(Error::DiskFull);
1137                            }
1138                            debug!("Allocated new FAT cluster, finding offsets...");
1139                            let new_offset = data
1140                                .find_data_on_disk(
1141                                    volume_idx,
1142                                    &mut current_cluster,
1143                                    data.open_files[file_idx].entry.cluster,
1144                                    data.open_files[file_idx].current_offset,
1145                                )
1146                                .map_err(|_| Error::AllocationError)?;
1147                            debug!("New offset {:?}", new_offset);
1148                            new_offset
1149                        }
1150                    }
1151                }
1152                Err(e) => return Err(e),
1153            };
1154            let to_copy = core::cmp::min(block_avail, bytes_to_write - written);
1155            let block = if (block_offset == 0) && (to_copy == block_avail) {
1156                // we're replacing the whole Block, so the previous contents
1157                // are irrelevant
1158                data.block_cache.blank_mut(block_idx)
1159            } else {
1160                debug!("Reading for partial block write");
1161                data.block_cache
1162                    .read_mut(block_idx)
1163                    .map_err(Error::DeviceError)?
1164            };
1165            block[block_offset..block_offset + to_copy]
1166                .copy_from_slice(&buffer[written..written + to_copy]);
1167            debug!("Writing block {:?}", block_idx);
1168            data.block_cache.write_back()?;
1169            written += to_copy;
1170            data.open_files[file_idx].current_cluster = current_cluster;
1171
1172            let to_copy = to_copy as u32;
1173            let new_offset = data.open_files[file_idx].current_offset + to_copy;
1174            if new_offset > data.open_files[file_idx].entry.size {
1175                // We made it longer
1176                data.open_files[file_idx].update_length(new_offset);
1177            }
1178            data.open_files[file_idx]
1179                .seek_from_start(new_offset)
1180                .unwrap();
1181            // Entry update deferred to file close, for performance.
1182        }
1183        data.open_files[file_idx].entry.attributes.set_archive(true);
1184        data.open_files[file_idx].entry.mtime = self.time_source.get_timestamp();
1185        Ok(())
1186    }
1187
1188    /// Close a file with the given raw file handle.
1189    ///
1190    /// Attempts to flush the file before closing, if necessary. If the flush
1191    /// fails, the file is closed anyway and the resulting error is returned.
1192    ///
1193    /// This is important as it causes the file metadata to be updated in the
1194    /// file's directory entry. Simply dropping the `RawFile` would leak
1195    /// internal resources and cause that metadata to be wrong.
1196    pub fn close_file(&self, file: RawFile) -> Result<(), Error<D::Error>> {
1197        let flush_result = self.flush_file(file);
1198        let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
1199        let file_idx = data.get_file_by_id(file)?;
1200        data.open_files.swap_remove(file_idx);
1201        flush_result
1202    }
1203
1204    /// Flush (update the entry) for a file with the given raw file handle.
1205    pub fn flush_file(&self, file: RawFile) -> Result<(), Error<D::Error>> {
1206        let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
1207        let data = data.deref_mut();
1208
1209        let file_id = data.get_file_by_id(file)?;
1210
1211        if data.open_files[file_id].dirty {
1212            let volume_idx = data.get_volume_by_id(data.open_files[file_id].raw_volume)?;
1213            match &mut data.open_volumes[volume_idx].volume_type {
1214                VolumeType::Fat(fat) => {
1215                    debug!("Updating FAT info sector");
1216                    fat.update_info_sector(&mut data.block_cache)?;
1217                    debug!("Updating dir entry {:?}", data.open_files[file_id].entry);
1218                    if data.open_files[file_id].entry.size != 0 {
1219                        // If you have a length, you must have a cluster
1220                        assert!(data.open_files[file_id].entry.cluster.0 != 0);
1221                    }
1222                    fat.write_entry_to_disk(
1223                        &mut data.block_cache,
1224                        &data.open_files[file_id].entry,
1225                    )?;
1226                }
1227            };
1228        }
1229        Ok(())
1230    }
1231
1232    /// Check if any files or folders are open.
1233    pub fn has_open_handles(&self) -> bool {
1234        let data = self.data.borrow();
1235        !(data.open_dirs.is_empty() && data.open_files.is_empty())
1236    }
1237
1238    /// Consume self and return BlockDevice and TimeSource
1239    pub fn free(self) -> (D, T) {
1240        let data = self.data.into_inner();
1241        (data.block_cache.free(), self.time_source)
1242    }
1243
1244    /// Check if a file is at End Of File.
1245    ///
1246    /// A file is at End of File if the seek position is equal to the length
1247    /// of the file. This means any reads will fail with an End of File
1248    /// error, and any writes will append to the file.
1249    pub fn file_eof(&self, file: RawFile) -> Result<bool, Error<D::Error>> {
1250        let data = self.data.try_borrow().map_err(|_| Error::LockError)?;
1251        let file_idx = data.get_file_by_id(file)?;
1252        Ok(data.open_files[file_idx].eof())
1253    }
1254
1255    /// Seek a file with an offset from the start of the file.
1256    ///
1257    /// The file seek position will end up equal to the offset given.
1258    ///
1259    /// Note that the offset is only a `u32`, therefore we can only handle
1260    /// files up to 4 GiB in size.
1261    pub fn file_seek_from_start(&self, file: RawFile, offset: u32) -> Result<(), Error<D::Error>> {
1262        let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
1263        let file_idx = data.get_file_by_id(file)?;
1264        data.open_files[file_idx]
1265            .seek_from_start(offset)
1266            .map_err(|_| Error::InvalidOffset)?;
1267        Ok(())
1268    }
1269
1270    /// Seek a file with an offset from the current position.
1271    ///
1272    /// The file seek position will be adjusted by the amount given.
1273    ///
1274    /// Note that the offset is only a `i32`, therefore we can only handle
1275    /// seeks that are up to 2 GiB before or after the current position. If
1276    /// this is a problem, seek in multiple steps.
1277    pub fn file_seek_from_current(
1278        &self,
1279        file: RawFile,
1280        offset: i32,
1281    ) -> Result<(), Error<D::Error>> {
1282        let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
1283        let file_idx = data.get_file_by_id(file)?;
1284        data.open_files[file_idx]
1285            .seek_from_current(offset)
1286            .map_err(|_| Error::InvalidOffset)?;
1287        Ok(())
1288    }
1289
1290    /// Seek a file with an offset back from the end of the file.
1291    ///
1292    /// The file seek position will set to the file length, minus the amount
1293    /// given.
1294    ///
1295    /// Note that the offset is only a `u32`, therefore we can only handle
1296    /// files up to 4 GiB in size.
1297    pub fn file_seek_from_end(&self, file: RawFile, offset: u32) -> Result<(), Error<D::Error>> {
1298        let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
1299        let file_idx = data.get_file_by_id(file)?;
1300        data.open_files[file_idx]
1301            .seek_from_end(offset)
1302            .map_err(|_| Error::InvalidOffset)?;
1303        Ok(())
1304    }
1305
1306    /// Get the length of a file
1307    ///
1308    /// Note that the file length is only a `u32`, therefore we can only
1309    /// handle files up to 4 GiB in size.
1310    pub fn file_length(&self, file: RawFile) -> Result<u32, Error<D::Error>> {
1311        let data = self.data.try_borrow().map_err(|_| Error::LockError)?;
1312        let file_idx = data.get_file_by_id(file)?;
1313        Ok(data.open_files[file_idx].length())
1314    }
1315
1316    /// Get the current offset of a file
1317    ///
1318    /// Note that the file offset is only a `u32`, therefore we can only
1319    /// handle files up to 4 GiB in size.
1320    pub fn file_offset(&self, file: RawFile) -> Result<u32, Error<D::Error>> {
1321        let data = self.data.try_borrow().map_err(|_| Error::LockError)?;
1322        let file_idx = data.get_file_by_id(file)?;
1323        Ok(data.open_files[file_idx].current_offset)
1324    }
1325
1326    /// Create a directory in a given directory, with the given short name.
1327    ///
1328    /// The directory will be empty (apart from any mandatory entries, such as
1329    /// the the `.` and `..` entries on FAT filesystems).
1330    pub fn make_dir_in_dir<N>(
1331        &self,
1332        directory: RawDirectory,
1333        name: N,
1334    ) -> Result<(), Error<D::Error>>
1335    where
1336        N: ToShortFileName,
1337    {
1338        let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?;
1339        let data = data.deref_mut();
1340
1341        // This check is load-bearing - we do an unchecked push later.
1342        if data.open_dirs.is_full() {
1343            return Err(Error::TooManyOpenDirs);
1344        }
1345
1346        let parent_directory_idx = data.get_dir_by_id(directory)?;
1347        let parent_directory_info = &data.open_dirs[parent_directory_idx];
1348        let volume_id = data.open_dirs[parent_directory_idx].raw_volume;
1349        let volume_idx = data.get_volume_by_id(volume_id)?;
1350        let volume_info = &data.open_volumes[volume_idx];
1351        let sfn = name.to_short_filename().map_err(Error::FilenameError)?;
1352
1353        debug!("Creating directory '{}'", sfn);
1354        debug!(
1355            "Parent dir is in cluster {:?}",
1356            parent_directory_info.cluster
1357        );
1358
1359        // Does an entry exist with this name?
1360        let maybe_dir_entry = match &volume_info.volume_type {
1361            VolumeType::Fat(fat) => {
1362                fat.find_directory_entry(&mut data.block_cache, parent_directory_info, &sfn)
1363            }
1364        };
1365
1366        match maybe_dir_entry {
1367            Ok(entry) if entry.attributes.is_directory() => {
1368                return Err(Error::DirAlreadyExists);
1369            }
1370            Ok(_entry) => {
1371                return Err(Error::FileAlreadyExists);
1372            }
1373            Err(Error::NotFound) => {
1374                // perfect, let's make it
1375            }
1376            Err(e) => {
1377                // Some other error - tell them about it
1378                return Err(e);
1379            }
1380        };
1381
1382        let att = Attributes::create_from_fat(Attributes::DIRECTORY);
1383
1384        // Need mutable access for this
1385        match &mut data.open_volumes[volume_idx].volume_type {
1386            VolumeType::Fat(fat) => {
1387                debug!("Making dir entry");
1388                fat.make_dir(
1389                    &mut data.block_cache,
1390                    &self.time_source,
1391                    parent_directory_info.cluster,
1392                    sfn,
1393                    att,
1394                )?;
1395            }
1396        };
1397
1398        Ok(())
1399    }
1400}
1401
1402/// The mutable data the VolumeManager needs to hold
1403///
1404/// Kept separate so its easier to wrap it in a RefCell
1405#[derive(Debug)]
1406
1407struct VolumeManagerData<
1408    D,
1409    const MAX_DIRS: usize = 4,
1410    const MAX_FILES: usize = 4,
1411    const MAX_VOLUMES: usize = 1,
1412> where
1413    D: BlockDevice,
1414{
1415    id_generator: HandleGenerator,
1416    block_cache: BlockCache<D>,
1417    open_volumes: Vec<VolumeInfo, MAX_VOLUMES>,
1418    open_dirs: Vec<DirectoryInfo, MAX_DIRS>,
1419    open_files: Vec<FileInfo, MAX_FILES>,
1420}
1421
1422impl<D, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
1423    VolumeManagerData<D, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
1424where
1425    D: BlockDevice,
1426    <D as BlockDevice>::Error: core::error::Error,
1427{
1428    /// Check if a file is open
1429    ///
1430    /// Returns `true` if it's open, `false`, otherwise.
1431    fn file_is_open(&self, raw_volume: RawVolume, dir_entry: &DirEntry) -> bool {
1432        for f in self.open_files.iter() {
1433            if f.raw_volume == raw_volume
1434                && f.entry.entry_block == dir_entry.entry_block
1435                && f.entry.entry_offset == dir_entry.entry_offset
1436            {
1437                return true;
1438            }
1439        }
1440        false
1441    }
1442
1443    fn get_volume_by_id<E: core::error::Error>(
1444        &self,
1445        raw_volume: RawVolume,
1446    ) -> Result<usize, Error<E>> {
1447        for (idx, v) in self.open_volumes.iter().enumerate() {
1448            if v.raw_volume == raw_volume {
1449                return Ok(idx);
1450            }
1451        }
1452        Err(Error::BadHandle)
1453    }
1454
1455    fn get_dir_by_id<E: core::error::Error>(
1456        &self,
1457        raw_directory: RawDirectory,
1458    ) -> Result<usize, Error<E>> {
1459        for (idx, d) in self.open_dirs.iter().enumerate() {
1460            if d.raw_directory == raw_directory {
1461                return Ok(idx);
1462            }
1463        }
1464        Err(Error::BadHandle)
1465    }
1466
1467    fn get_file_by_id<E: core::error::Error>(&self, raw_file: RawFile) -> Result<usize, Error<E>> {
1468        for (idx, f) in self.open_files.iter().enumerate() {
1469            if f.raw_file == raw_file {
1470                return Ok(idx);
1471            }
1472        }
1473        Err(Error::BadHandle)
1474    }
1475
1476    /// This function turns `desired_offset` into an appropriate block to be
1477    /// read. It either calculates this based on the start of the file, or
1478    /// from the given start point - whichever is better.
1479    ///
1480    /// Returns:
1481    ///
1482    /// * the index for the block on the disk that contains the data we want,
1483    /// * the byte offset into that block for the data we want, and
1484    /// * how many bytes remain in that block.
1485    fn find_data_on_disk(
1486        &mut self,
1487        volume_idx: usize,
1488        start: &mut (u32, ClusterId),
1489        file_start: ClusterId,
1490        desired_offset: u32,
1491    ) -> Result<(BlockIdx, usize, usize), Error<D::Error>>
1492    where
1493        D: BlockDevice,
1494    {
1495        let bytes_per_cluster = match &self.open_volumes[volume_idx].volume_type {
1496            VolumeType::Fat(fat) => fat.bytes_per_cluster(),
1497        };
1498        // do we need to be before our start point?
1499        if desired_offset < start.0 {
1500            // user wants to go backwards - start from the beginning of the file
1501            // because the FAT is only a singly-linked list.
1502            start.0 = 0;
1503            start.1 = file_start;
1504        }
1505        // How many clusters forward do we need to go?
1506        let offset_from_cluster = desired_offset - start.0;
1507        // walk through the FAT chain
1508        let num_clusters = offset_from_cluster / bytes_per_cluster;
1509        for _ in 0..num_clusters {
1510            start.1 = match &self.open_volumes[volume_idx].volume_type {
1511                VolumeType::Fat(fat) => fat.next_cluster(&mut self.block_cache, start.1)?,
1512            };
1513            start.0 += bytes_per_cluster;
1514        }
1515        // How many blocks in are we now?
1516        let offset_from_cluster = desired_offset - start.0;
1517        assert!(offset_from_cluster < bytes_per_cluster);
1518        let num_blocks = BlockCount(offset_from_cluster / Block::LEN_U32);
1519        let block_idx = match &self.open_volumes[volume_idx].volume_type {
1520            VolumeType::Fat(fat) => fat.cluster_to_block(start.1),
1521        } + num_blocks;
1522        let block_offset = (desired_offset % Block::LEN_U32) as usize;
1523        let available = Block::LEN - block_offset;
1524        Ok((block_idx, block_offset, available))
1525    }
1526}
1527
1528/// Transform mode variants (ReadWriteCreate_Or_Append) to simple modes ReadWriteAppend or
1529/// ReadWriteCreate
1530fn solve_mode_variant(mode: Mode, dir_entry_is_some: bool) -> Mode {
1531    let mut mode = mode;
1532    if mode == Mode::ReadWriteCreateOrAppend {
1533        if dir_entry_is_some {
1534            mode = Mode::ReadWriteAppend;
1535        } else {
1536            mode = Mode::ReadWriteCreate;
1537        }
1538    } else if mode == Mode::ReadWriteCreateOrTruncate {
1539        if dir_entry_is_some {
1540            mode = Mode::ReadWriteTruncate;
1541        } else {
1542            mode = Mode::ReadWriteCreate;
1543        }
1544    }
1545    mode
1546}
1547
1548// ****************************************************************************
1549//
1550// Unit Tests
1551//
1552// ****************************************************************************
1553
1554#[cfg(test)]
1555mod tests {
1556    use hex_literal::hex;
1557
1558    use super::*;
1559    use crate::Timestamp;
1560    use crate::filesystem::Handle;
1561
1562    struct DummyBlockDevice;
1563
1564    struct Clock;
1565
1566    #[derive(Debug, thiserror::Error)]
1567    enum Error {
1568        #[error("unknown error")]
1569        Unknown,
1570    }
1571
1572    impl TimeSource for Clock {
1573        fn get_timestamp(&self) -> Timestamp {
1574            // TODO: Return actual time
1575            Timestamp {
1576                year_since_1970: 0,
1577                zero_indexed_month: 0,
1578                zero_indexed_day: 0,
1579                hours: 0,
1580                minutes: 0,
1581                seconds: 0,
1582            }
1583        }
1584    }
1585
1586    impl BlockDevice for DummyBlockDevice {
1587        type Error = Error;
1588
1589        /// Read one or more blocks, starting at the given block index.
1590        fn read(&self, blocks: &mut [Block], start_block_idx: BlockIdx) -> Result<(), Self::Error> {
1591            // Actual blocks taken from an SD card, except I've changed the start and length of partition 0.
1592            static BLOCKS: [Block; 3] = [
1593                Block {
1594                    contents: [
1595                        0xfa, 0xb8, 0x00, 0x10, 0x8e, 0xd0, 0xbc, 0x00, 0xb0, 0xb8, 0x00, 0x00,
1596                        0x8e, 0xd8, 0x8e, 0xc0, // 0x000
1597                        0xfb, 0xbe, 0x00, 0x7c, 0xbf, 0x00, 0x06, 0xb9, 0x00, 0x02, 0xf3, 0xa4,
1598                        0xea, 0x21, 0x06, 0x00, // 0x010
1599                        0x00, 0xbe, 0xbe, 0x07, 0x38, 0x04, 0x75, 0x0b, 0x83, 0xc6, 0x10, 0x81,
1600                        0xfe, 0xfe, 0x07, 0x75, // 0x020
1601                        0xf3, 0xeb, 0x16, 0xb4, 0x02, 0xb0, 0x01, 0xbb, 0x00, 0x7c, 0xb2, 0x80,
1602                        0x8a, 0x74, 0x01, 0x8b, // 0x030
1603                        0x4c, 0x02, 0xcd, 0x13, 0xea, 0x00, 0x7c, 0x00, 0x00, 0xeb, 0xfe, 0x00,
1604                        0x00, 0x00, 0x00, 0x00, // 0x040
1605                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1606                        0x00, 0x00, 0x00, 0x00, // 0x050
1607                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1608                        0x00, 0x00, 0x00, 0x00, // 0x060
1609                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1610                        0x00, 0x00, 0x00, 0x00, // 0x070
1611                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1612                        0x00, 0x00, 0x00, 0x00, // 0x080
1613                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1614                        0x00, 0x00, 0x00, 0x00, // 0x090
1615                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1616                        0x00, 0x00, 0x00, 0x00, // 0x0A0
1617                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1618                        0x00, 0x00, 0x00, 0x00, // 0x0B0
1619                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1620                        0x00, 0x00, 0x00, 0x00, // 0x0C0
1621                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1622                        0x00, 0x00, 0x00, 0x00, // 0x0D0
1623                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1624                        0x00, 0x00, 0x00, 0x00, // 0x0E0
1625                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1626                        0x00, 0x00, 0x00, 0x00, // 0x0F0
1627                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1628                        0x00, 0x00, 0x00, 0x00, // 0x100
1629                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1630                        0x00, 0x00, 0x00, 0x00, // 0x110
1631                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1632                        0x00, 0x00, 0x00, 0x00, // 0x120
1633                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1634                        0x00, 0x00, 0x00, 0x00, // 0x130
1635                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1636                        0x00, 0x00, 0x00, 0x00, // 0x140
1637                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1638                        0x00, 0x00, 0x00, 0x00, // 0x150
1639                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1640                        0x00, 0x00, 0x00, 0x00, // 0x160
1641                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1642                        0x00, 0x00, 0x00, 0x00, // 0x170
1643                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1644                        0x00, 0x00, 0x00, 0x00, // 0x180
1645                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1646                        0x00, 0x00, 0x00, 0x00, // 0x190
1647                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1648                        0x00, 0x00, 0x00, 0x00, // 0x1A0
1649                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xca, 0xde, 0x06,
1650                        0x00, 0x00, 0x00, 0x04, // 0x1B0
1651                        0x01, 0x04, 0x0c, 0xfe, 0xc2, 0xff, 0x01, 0x00, 0x00, 0x00, 0x33, 0x22,
1652                        0x11, 0x00, 0x00, 0x00, // 0x1C0
1653                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1654                        0x00, 0x00, 0x00, 0x00, // 0x1D0
1655                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1656                        0x00, 0x00, 0x00, 0x00, // 0x1E0
1657                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1658                        0x00, 0x00, 0x55, 0xaa, // 0x1F0
1659                    ],
1660                },
1661                Block {
1662                    contents: [
1663                        0xeb, 0x58, 0x90, 0x6d, 0x6b, 0x66, 0x73, 0x2e, 0x66, 0x61, 0x74, 0x00,
1664                        0x02, 0x08, 0x20, 0x00, // 0x000
1665                        0x02, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x10, 0x00, 0x04, 0x00,
1666                        0x00, 0x08, 0x00, 0x00, // 0x010
1667                        0x00, 0x20, 0x76, 0x00, 0x80, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1668                        0x02, 0x00, 0x00, 0x00, // 0x020
1669                        0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1670                        0x00, 0x00, 0x00, 0x00, // 0x030
1671                        0x80, 0x01, 0x29, 0x0b, 0xa8, 0x89, 0x27, 0x50, 0x69, 0x63, 0x74, 0x75,
1672                        0x72, 0x65, 0x73, 0x20, // 0x040
1673                        0x20, 0x20, 0x46, 0x41, 0x54, 0x33, 0x32, 0x20, 0x20, 0x20, 0x0e, 0x1f,
1674                        0xbe, 0x77, 0x7c, 0xac, // 0x050
1675                        0x22, 0xc0, 0x74, 0x0b, 0x56, 0xb4, 0x0e, 0xbb, 0x07, 0x00, 0xcd, 0x10,
1676                        0x5e, 0xeb, 0xf0, 0x32, // 0x060
1677                        0xe4, 0xcd, 0x16, 0xcd, 0x19, 0xeb, 0xfe, 0x54, 0x68, 0x69, 0x73, 0x20,
1678                        0x69, 0x73, 0x20, 0x6e, // 0x070
1679                        0x6f, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c,
1680                        0x65, 0x20, 0x64, 0x69, // 0x080
1681                        0x73, 0x6b, 0x2e, 0x20, 0x20, 0x50, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20,
1682                        0x69, 0x6e, 0x73, 0x65, // 0x090
1683                        0x72, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c,
1684                        0x65, 0x20, 0x66, 0x6c, // 0x0A0
1685                        0x6f, 0x70, 0x70, 0x79, 0x20, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x70, 0x72,
1686                        0x65, 0x73, 0x73, 0x20, // 0x0B0
1687                        0x61, 0x6e, 0x79, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x74,
1688                        0x72, 0x79, 0x20, 0x61, // 0x0C0
1689                        0x67, 0x61, 0x69, 0x6e, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x0d, 0x0a, 0x00,
1690                        0x00, 0x00, 0x00, 0x00, // 0x0D0
1691                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1692                        0x00, 0x00, 0x00, 0x00, // 0x0E0
1693                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1694                        0x00, 0x00, 0x00, 0x00, // 0x0F0
1695                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1696                        0x00, 0x00, 0x00, 0x00, // 0x100
1697                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1698                        0x00, 0x00, 0x00, 0x00, // 0x110
1699                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1700                        0x00, 0x00, 0x00, 0x00, // 0x120
1701                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1702                        0x00, 0x00, 0x00, 0x00, // 0x130
1703                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1704                        0x00, 0x00, 0x00, 0x00, // 0x140
1705                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1706                        0x00, 0x00, 0x00, 0x00, // 0x150
1707                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1708                        0x00, 0x00, 0x00, 0x00, // 0x160
1709                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1710                        0x00, 0x00, 0x00, 0x00, // 0x170
1711                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1712                        0x00, 0x00, 0x00, 0x00, // 0x180
1713                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1714                        0x00, 0x00, 0x00, 0x00, // 0x190
1715                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1716                        0x00, 0x00, 0x00, 0x00, // 0x1A0
1717                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1718                        0x00, 0x00, 0x00, 0x00, // 0x1B0
1719                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1720                        0x00, 0x00, 0x00, 0x00, // 0x1C0
1721                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1722                        0x00, 0x00, 0x00, 0x00, // 0x1D0
1723                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1724                        0x00, 0x00, 0x00, 0x00, // 0x1E0
1725                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1726                        0x00, 0x00, 0x55, 0xaa, // 0x1F0
1727                    ],
1728                },
1729                Block {
1730                    contents: hex!(
1731                        "52 52 61 41 00 00 00 00 00 00 00 00 00 00 00 00
1732                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1733                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1734                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1735                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1736                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1737                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1738                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1739                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1740                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1741                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1742                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1743                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1744                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1745                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1746                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1747                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1748                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1749                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1750                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1751                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1752                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1753                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1754                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1755                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1756                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1757                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1758                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1759                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1760                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1761                         00 00 00 00 72 72 41 61 FF FF FF FF FF FF FF FF
1762                         00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA"
1763                    ),
1764                },
1765            ];
1766            println!(
1767                "Reading block {} to {}",
1768                start_block_idx.0,
1769                start_block_idx.0 as usize + blocks.len()
1770            );
1771            for (idx, block) in blocks.iter_mut().enumerate() {
1772                let block_idx = start_block_idx.0 as usize + idx;
1773                if block_idx < BLOCKS.len() {
1774                    *block = BLOCKS[block_idx].clone();
1775                } else {
1776                    return Err(Error::Unknown);
1777                }
1778            }
1779            Ok(())
1780        }
1781
1782        /// Write one or more blocks, starting at the given block index.
1783        fn write(&self, _blocks: &[Block], _start_block_idx: BlockIdx) -> Result<(), Self::Error> {
1784            unimplemented!();
1785        }
1786
1787        /// Determine how many blocks this device can hold.
1788        fn num_blocks(&self) -> Result<BlockCount, Self::Error> {
1789            Ok(BlockCount(2))
1790        }
1791    }
1792
1793    #[test]
1794    fn partition0() {
1795        let c: VolumeManager<DummyBlockDevice, Clock, 2, 2> =
1796            VolumeManager::new_with_limits(DummyBlockDevice, Clock, 0xAA00_0000);
1797
1798        let v = c.open_raw_volume(VolumeIdx(0)).unwrap();
1799        let expected_id = RawVolume(Handle(0xAA00_0000));
1800        assert_eq!(v, expected_id);
1801        assert_eq!(
1802            &c.data.borrow().open_volumes[0],
1803            &VolumeInfo {
1804                raw_volume: expected_id,
1805                idx: VolumeIdx(0),
1806                volume_type: VolumeType::Fat(crate::FatVolume {
1807                    lba_start: BlockIdx(1),
1808                    num_blocks: BlockCount(0x0011_2233),
1809                    blocks_per_cluster: 8,
1810                    first_data_block: BlockCount(15136),
1811                    fat_start: BlockCount(32),
1812                    second_fat_start: Some(BlockCount(32 + 0x0000_1D80)),
1813                    name: fat::VolumeName::create_from_str("Pictures").unwrap(),
1814                    free_clusters_count: None,
1815                    next_free_cluster: None,
1816                    cluster_count: 965_788,
1817                    fat_specific_info: fat::FatSpecificInfo::Fat32(fat::Fat32Info {
1818                        first_root_dir_cluster: ClusterId(2),
1819                        info_location: BlockIdx(1) + BlockCount(1),
1820                    })
1821                })
1822            }
1823        );
1824    }
1825}
1826
1827// ****************************************************************************
1828//
1829// End Of File
1830//
1831// ****************************************************************************