exfat/cluster_heap/
meta.rs

1use core::mem::transmute;
2use core::ops::Deref;
3
4use super::context::Context;
5use super::entryset::EntryID;
6use super::metadata::Metadata;
7use crate::error::{AllocationError, DataError, Error, OperationError};
8use crate::fat;
9use crate::file::{FileOptions, TouchOptions};
10use crate::fs::{self, SectorIndex};
11use crate::io::{self, Block, Wrap};
12use crate::region::data::entryset::primary::DateTime;
13use crate::region::data::entryset::{ENTRY_SIZE, RawEntry};
14use crate::region::fat::Entry;
15use crate::sync::Shared;
16use crate::types::ClusterID;
17
18pub(crate) struct MetaFileDirectory<IO> {
19    pub io: Shared<IO>,
20    pub context: Shared<Context<IO>>,
21    pub fat: fat::Info,
22    pub fs: fs::Info,
23    pub metadata: Metadata,
24    pub options: FileOptions,
25    pub sector_index: SectorIndex,
26}
27
28impl<IO> Clone for MetaFileDirectory<IO> {
29    fn clone(&self) -> Self {
30        Self {
31            io: self.io.clone(),
32            context: self.context.clone(),
33            metadata: self.metadata.clone(),
34            ..*self
35        }
36    }
37}
38
39impl<IO> MetaFileDirectory<IO> {
40    pub(crate) fn id(&self) -> EntryID {
41        let entry_index = &self.metadata.entry_index;
42        EntryID { sector_id: entry_index.sector_index.id(&self.fs), index: entry_index.index }
43    }
44}
45
46#[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)]
47impl<B: Deref<Target = [Block]>, E, IO> MetaFileDirectory<IO>
48where
49    IO: io::IO<Block<'static> = B, Error = E>,
50{
51    pub async fn next(&mut self, sector_index: SectorIndex) -> Result<SectorIndex, Error<E>> {
52        let fat_chain = self.metadata.stream_extension.general_secondary_flags.fat_chain();
53        if sector_index.sector_index != self.fs.sectors_per_cluster() {
54            return Ok(sector_index.next(self.fs.sectors_per_cluster_shift));
55        }
56        if !fat_chain {
57            let num_clusters = (self.metadata.length() / self.fs.cluster_size() as u64) as u32;
58            let max_cluster_id = self.sector_index.cluster_id + num_clusters;
59            if sector_index.cluster_id + 1u32 >= max_cluster_id {
60                return Err(OperationError::EOF.into());
61            }
62            return Ok(sector_index.next(self.fs.sectors_per_cluster_shift));
63        }
64        let cluster_id = sector_index.cluster_id;
65        let option = self.fat.fat_sector_id(cluster_id);
66        let sector_id = option.ok_or(Error::Data(DataError::FATChain))?;
67        let mut io = self.io.acquire().await.wrap();
68        let block = io.read(sector_id).await?;
69        match self.fat.next_cluster_id(&block, sector_index.cluster_id) {
70            Ok(Entry::Next(cluster_id)) => Ok(SectorIndex::new(cluster_id, 0)),
71            Ok(Entry::Last) => Err(OperationError::EOF.into()),
72            _ => Err(DataError::FATChain.into()),
73        }
74    }
75
76    pub async fn touch(&mut self, datetime: DateTime, opts: TouchOptions) -> Result<(), Error<E>> {
77        let metadata = &mut self.metadata;
78        if opts.access {
79            metadata.file_directory.update_last_accessed_timestamp(datetime);
80        }
81        if opts.modified {
82            metadata.file_directory.update_last_modified_timestamp(datetime);
83        }
84        metadata.update_checksum();
85        Ok(())
86    }
87
88    pub async fn allocate(&mut self, last: ClusterID) -> Result<ClusterID, Error<E>> {
89        trace!("Allocate cluster with last cluster {}", last);
90        if !self.metadata.stream_extension.general_secondary_flags.allocation_possible() {
91            return Err(AllocationError::NotPossible.into());
92        }
93        let nofrag = if self.options.dont_fragment { Some(last) } else { None };
94        let mut context = self.context.acquire().await;
95        let cluster_id = context.allocation_bitmap.allocate(nofrag).await?;
96
97        let cluster_size = self.fs.cluster_size() as u64;
98        let metadata = &mut self.metadata;
99
100        let fat_chain = metadata.stream_extension.general_secondary_flags.fat_chain();
101        if !last.valid() {
102            metadata.stream_extension.first_cluster = u32::from(cluster_id).into();
103            metadata.stream_extension.general_secondary_flags.clear_fat_chain();
104        } else if last + 1u32 != cluster_id || fat_chain {
105            let mut io = self.io.acquire().await.wrap();
106            if !fat_chain && metadata.capacity() > cluster_size {
107                let first = self.sector_index.cluster_id;
108                for i in 0..(metadata.capacity() / cluster_size - 1) {
109                    let cluster_id = first + i as u32;
110                    let next = cluster_id + 1u32;
111                    let sector_id = self.fat.fat_sector_id(cluster_id).unwrap();
112                    let bytes = u32::to_le_bytes(next.into());
113                    io.write(sector_id, self.fat.offset(next), &bytes).await?;
114                }
115                metadata.stream_extension.general_secondary_flags.set_fat_chain();
116            }
117            let sector_id = self.fat.fat_sector_id(last).unwrap();
118            let bytes = u32::to_le_bytes(cluster_id.into());
119            io.write(sector_id, self.fat.offset(last), &bytes).await?;
120            let bytes = u32::to_ne_bytes(Entry::Last.into());
121            io.write(sector_id, self.fat.offset(cluster_id), &bytes).await?;
122        }
123        if metadata.file_directory.file_attributes().directory() > 0 {
124            let length = metadata.length() + cluster_size;
125            metadata.stream_extension.custom_defined.valid_data_length = length.into()
126        }
127        metadata.stream_extension.data_length = (metadata.capacity() + cluster_size).into();
128        metadata.update_checksum();
129        Ok(cluster_id)
130    }
131
132    pub async fn sync(&mut self) -> Result<(), Error<E>> {
133        let metadata = &mut self.metadata;
134        if !metadata.entry_index.sector_index.cluster_id.valid() {
135            // Probably root directory
136            return Ok(());
137        }
138        if metadata.dirty {
139            trace!("Flush metadatadata since dirty");
140            let mut sector_id = metadata.entry_index.sector_index.id(&self.fs);
141            let bytes: &RawEntry = unsafe { transmute(&metadata.file_directory) };
142            let offset = metadata.entry_index.index as usize * ENTRY_SIZE;
143            let mut io = self.io.acquire().await.wrap();
144            io.write(sector_id, offset, &bytes[..]).await?;
145            let mut offset = (metadata.entry_index.index as usize + 1) * ENTRY_SIZE;
146            if offset == self.fs.sector_size() as usize {
147                offset = 0;
148                sector_id += 1u32;
149            }
150            let bytes: &RawEntry = unsafe { transmute(&metadata.stream_extension) };
151            io.write(sector_id, offset, &bytes[..]).await?;
152            io.flush().await?;
153            metadata.dirty = false;
154        }
155        Ok(())
156    }
157
158    pub async fn close(&mut self) -> Result<(), Error<E>> {
159        self.sync().await?;
160        self.context.acquire().await.opened_entries.remove(self.id());
161        Ok(())
162    }
163}