exfat/cluster_heap/
meta.rs1use 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 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}