exfat/cluster_heap/
file.rs

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