exfat/cluster_heap/
file.rs

1use core::fmt::Debug;
2use core::ops::Deref;
3
4use super::meta::MetaFileDirectory;
5use crate::error::{Error, InputError, OperationError};
6use crate::file::{FileOptions, TouchOptions};
7use crate::fs::SectorIndex;
8use crate::io::{self, Block, Wrap};
9use crate::region::data::entryset::primary::DateTime;
10
11#[derive(Copy, Clone, Debug, PartialEq, Eq)]
12pub enum SeekFrom {
13    Start(u64),
14    End(i64),
15    Current(i64),
16}
17
18pub struct File<B: Deref<Target = [Block]>, E: Debug, IO>
19where
20    IO: io::IO<Block<'static> = B, Error = E>,
21{
22    pub(crate) meta: MetaFileDirectory<IO>,
23    pub(crate) sector_index: SectorIndex,
24    pub(crate) size: u64,
25    cursor: u64,
26    dirty: bool,
27    #[cfg(feature = "async")]
28    closed: bool,
29}
30
31impl<B: Deref<Target = [Block]>, E: Debug, IO> File<B, E, IO>
32where
33    IO: io::IO<Block<'static> = B, Error = E>,
34{
35    pub(crate) fn new(meta: MetaFileDirectory<IO>, sector_index: SectorIndex) -> Self {
36        let size = meta.metadata.length();
37        match () {
38            #[cfg(not(feature = "async"))]
39            () => Self { meta, sector_index, size, cursor: 0, dirty: false },
40            #[cfg(feature = "async")]
41            () => Self { meta, sector_index, size, cursor: 0, dirty: false, closed: false },
42        }
43    }
44
45    pub fn change_options(&mut self, f: impl Fn(&mut FileOptions)) {
46        f(&mut self.meta.options)
47    }
48}
49
50#[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)]
51impl<B: Deref<Target = [Block]>, E: Debug, IO> File<B, E, IO>
52where
53    IO: io::IO<Block<'static> = B, Error = E>,
54{
55    pub fn size(&self) -> u64 {
56        self.size
57    }
58
59    /// Change file timestamp, will not take effect immediately untill flush or sync_all called
60    pub async fn touch(&mut self, datetime: DateTime, opts: TouchOptions) -> Result<(), Error<E>> {
61        self.meta.touch(datetime, opts).await?;
62        self.meta.io.acquire().await.wrap().flush().await
63    }
64
65    /// Read some bytes
66    /// If sector remain bytes fits in buf,
67    /// all remain bytes will be read,
68    /// Otherwise a sector size or a buf size will be read.
69    pub async fn read(&mut self, mut buf: &mut [u8]) -> Result<usize, Error<E>> {
70        if self.cursor == self.size {
71            return Err(OperationError::EOF.into());
72        }
73        if buf.len() > (self.size - self.cursor) as usize {
74            buf = &mut buf[..(self.size - self.cursor) as usize];
75        }
76        let sector_size = self.meta.fs.sector_size() as usize;
77        let offset = self.cursor as usize % sector_size;
78        let sector_id = self.sector_index.id(&self.meta.fs);
79        let sector_remain = sector_size - offset;
80        let mut io = self.meta.io.acquire().await.wrap();
81        let sector = io.read(sector_id).await?;
82        let bytes = crate::io::flatten(&*sector);
83        if buf.len() <= sector_remain {
84            buf.copy_from_slice(&bytes[offset..offset + buf.len()]);
85            drop(io);
86            if buf.len() == sector_remain {
87                self.sector_index = self.meta.next(self.sector_index).await?;
88            }
89            self.cursor += buf.len() as u64;
90            return Ok(buf.len());
91        }
92        buf[..sector_remain].copy_from_slice(&bytes[offset..]);
93        drop(io);
94        let mut remain = &mut buf[sector_remain..];
95        self.sector_index = self.meta.next(self.sector_index).await?;
96        for _ in 0..remain.len() / sector_size {
97            let mut io = self.meta.io.acquire().await.wrap();
98            let sector = io.read(sector_id).await?;
99            let bytes = crate::io::flatten(&*sector);
100            remain[..sector_size].copy_from_slice(bytes);
101            drop(io);
102            self.sector_index = self.meta.next(self.sector_index).await?;
103            remain = &mut remain[sector_size..];
104        }
105        let mut io = self.meta.io.acquire().await.wrap();
106        let sector = io.read(sector_id).await?;
107        let bytes = crate::io::flatten(&*sector);
108        remain.copy_from_slice(&bytes[..remain.len()]);
109        self.cursor += buf.len() as u64;
110        Ok(buf.len())
111    }
112
113    /// Write some bytes
114    /// If bytes length fits in current sector remain size,
115    /// all bytes will be successfully written,
116    /// Otherwise a sector size will be written.
117    ///
118    /// Write operation will not apply file metadata change immediately until
119    /// flush or sync_all called.
120    pub async fn write(&mut self, bytes: &[u8]) -> Result<usize, Error<E>> {
121        if bytes.len() == 0 {
122            return Ok(0);
123        }
124        self.dirty = true;
125        let sector_size = self.meta.fs.sector_size() as usize;
126        let mut capacity = self.meta.metadata.capacity();
127        let sector_remain = (capacity - self.cursor) as usize % sector_size;
128        if sector_remain > 0 {
129            let length = core::cmp::min(bytes.len(), sector_remain);
130            let chunk = &bytes[..length];
131            trace!("Write to sector-ref {}", self.sector_index);
132            let sector_id = self.sector_index.id(&self.meta.fs);
133            let mut io = self.meta.io.acquire().await.wrap();
134            io.write(sector_id, self.cursor as usize % sector_size, chunk).await?;
135            drop(io);
136            self.cursor += length as u64;
137            self.size = core::cmp::max(self.cursor, self.size);
138            if length == sector_remain && self.cursor < capacity {
139                self.sector_index = self.meta.next(self.sector_index).await?;
140            }
141            return Ok(sector_remain);
142        }
143        if self.cursor >= capacity {
144            let cluster_id = self.meta.allocate(self.sector_index.cluster_id).await?;
145            self.sector_index = SectorIndex::new(cluster_id, 0);
146            capacity = self.meta.metadata.capacity();
147        }
148        trace!("Write to sector-ref {}", self.sector_index);
149        let sector_id = self.sector_index.id(&self.meta.fs);
150        let length = core::cmp::min(bytes.len(), sector_size);
151        let chunk = &bytes[..length];
152        self.meta.io.acquire().await.wrap().write(sector_id, 0, chunk).await?;
153        self.cursor += length as u64;
154        self.size = core::cmp::max(self.cursor, self.size);
155        if length == sector_size && self.cursor < capacity {
156            self.sector_index = self.meta.next(self.sector_index).await?;
157        }
158        self.meta.metadata.set_length(self.size);
159        Ok(length)
160    }
161
162    pub async fn write_all(&mut self, bytes: &[u8]) -> Result<(), Error<E>> {
163        let written = self.write(bytes).await?; // Fill remain of current sector
164        for chunk in bytes[written..].chunks(self.meta.fs.sector_size() as usize) {
165            self.write(chunk).await?;
166        }
167        Ok(())
168    }
169
170    /// Flush data write operations
171    pub async fn sync_data(&mut self) -> Result<(), Error<E>> {
172        if self.dirty {
173            self.meta.io.acquire().await.wrap().flush().await?;
174            self.dirty = false;
175        }
176        Ok(())
177    }
178
179    /// Flush data write operations and metadata changes
180    pub async fn sync_all(&mut self) -> Result<(), Error<E>> {
181        self.sync_data().await?;
182        self.meta.sync().await
183    }
184
185    /// Alias of sync_all
186    pub async fn flush(&mut self) -> Result<(), Error<E>> {
187        self.sync_all().await
188    }
189
190    /// Change current cursor position
191    pub async fn seek(&mut self, seek_from: SeekFrom) -> Result<u64, Error<E>> {
192        let option = match seek_from {
193            SeekFrom::Start(cursor) => i64::try_from(cursor).ok(),
194            SeekFrom::End(offset) => Some((self.cursor as i64) + offset),
195            SeekFrom::Current(offset) => (self.cursor as i64).checked_add(offset),
196        };
197        let cursor = option.ok_or(Error::Input(InputError::SeekPosition))?;
198        if cursor < 0 || cursor >= self.size as i64 {
199            return Err(InputError::SeekPosition.into());
200        }
201        let cursor = cursor as u64;
202        let sector_size = self.meta.fs.sector_size() as u64;
203        let num_sectors = match () {
204            _ if cursor > self.cursor => (cursor - self.cursor + sector_size - 1) / sector_size,
205            _ if cursor < self.cursor => {
206                self.sector_index = self.meta.sector_index;
207                (cursor + sector_size - 1) / sector_size
208            }
209            _ => 0,
210        };
211        for _ in 0..num_sectors {
212            self.sector_index = self.meta.next(self.sector_index).await?;
213        }
214        self.cursor = cursor;
215        Ok(cursor)
216    }
217
218    /// Shrink current file size
219    pub async fn truncate(&mut self, size: u64) -> Result<(), Error<E>> {
220        if size > self.size {
221            return Err(InputError::Size.into());
222        }
223        if self.cursor > size {
224            self.cursor = size;
225            self.seek(SeekFrom::Start(size)).await?;
226        }
227        self.meta.metadata.set_length(size);
228        self.size = size;
229        Ok(())
230    }
231
232    #[cfg(all(feature = "async", not(feature = "std")))]
233    /// `no_std` async only which must be explicitly called
234    pub async fn close(mut self) -> Result<(), Error<E>> {
235        self.closed = true;
236        self.flush().await.and(self.meta.close().await)
237    }
238}
239
240#[cfg(any(not(feature = "async"), feature = "std"))]
241impl<B: Deref<Target = [Block]>, E: Debug, IO> Drop for File<B, E, IO>
242where
243    IO: io::IO<Block<'static> = B, Error = E>,
244{
245    fn drop(&mut self) {
246        #[cfg(feature = "async")]
247        if !self.closed {
248            panic!("Close must be explicitly called")
249        }
250        #[cfg(not(feature = "async"))]
251        self.flush().and(self.meta.close()).unwrap();
252    }
253}