exfat/
directory.rs

1use crate::cluster::ClustersReader;
2use crate::entries::{ClusterAllocation, EntriesReader, EntryType, FileEntry, StreamEntry};
3use crate::file::File;
4use crate::image::Image;
5use std::io::{Read, Seek};
6use std::ops::DerefMut;
7use std::sync::Arc;
8use thiserror::Error;
9
10/// Represents a directory in the exFAT.
11pub struct Directory<I: Read + Seek> {
12    image: Arc<Image<I>>,
13    name: String,
14    stream: StreamEntry,
15}
16
17impl<I: Read + Seek> Directory<I> {
18    pub(crate) fn new(image: Arc<Image<I>>, name: String, stream: StreamEntry) -> Self {
19        Self {
20            image,
21            name,
22            stream,
23        }
24    }
25
26    pub fn name(&self) -> &str {
27        self.name.as_ref()
28    }
29
30    pub fn open(&self) -> Result<Vec<Item<I>>, OpenError> {
31        // Create an entries reader.
32        let params = self.image.params();
33        let fat = self.image.fat();
34        let mut image = self.image.reader();
35        let alloc = self.stream.allocation();
36        let mut reader = match ClustersReader::new(
37            params,
38            fat,
39            image.deref_mut(),
40            alloc.first_cluster(),
41            Some(alloc.data_length()),
42            Some(self.stream.no_fat_chain()),
43        ) {
44            Ok(v) => EntriesReader::new(v),
45            Err(e) => return Err(OpenError::CreateClustersReaderFailed(alloc.clone(), e)),
46        };
47
48        // Read file entries.
49        let mut items: Vec<Item<I>> = Vec::new();
50
51        loop {
52            // Read primary entry.
53            let entry = match reader.read() {
54                Ok(v) => v,
55                Err(e) => return Err(OpenError::ReadEntryFailed(e)),
56            };
57
58            // Check entry type.
59            let ty = entry.ty();
60
61            if !ty.is_regular() {
62                break;
63            } else if ty.type_category() != EntryType::PRIMARY {
64                return Err(OpenError::NotPrimaryEntry(entry.index(), entry.cluster()));
65            } else if ty.type_importance() != EntryType::CRITICAL || ty.type_code() != 5 {
66                return Err(OpenError::NotFileEntry(entry.index(), entry.cluster()));
67            }
68
69            // Parse file entry.
70            let file = match FileEntry::load(entry, &mut reader) {
71                Ok(v) => v,
72                Err(e) => return Err(OpenError::LoadFileEntryFailed(e)),
73            };
74
75            // Construct item.
76            let name = file.name;
77            let attrs = file.attributes;
78            let stream = file.stream;
79
80            let item = if attrs.is_directory() {
81                Item::Directory(Directory::new(self.image.clone(), name, stream))
82            } else {
83                Item::File(File::new(self.image.clone(), name, stream))
84            };
85
86            items.push(item);
87        }
88
89        Ok(items)
90    }
91}
92
93/// Represents an item in the directory.
94pub enum Item<I: Read + Seek> {
95    Directory(Directory<I>),
96    File(File<I>),
97}
98
99/// Represents an error for [`open()`][Directory::open].
100#[derive(Debug, Error)]
101pub enum OpenError {
102    #[error("cannot create a clusters reader for allocation {0}")]
103    CreateClustersReaderFailed(ClusterAllocation, #[source] crate::cluster::NewError),
104
105    #[error("cannot read an entry")]
106    ReadEntryFailed(#[source] crate::entries::ReaderError),
107
108    #[error("entry #{0} on cluster #{1} is not a primary entry")]
109    NotPrimaryEntry(usize, usize),
110
111    #[error("entry #{0} on cluster #{1} is not a file entry")]
112    NotFileEntry(usize, usize),
113
114    #[error("cannot load file entry")]
115    LoadFileEntryFailed(#[source] crate::entries::FileEntryError),
116}