fatfs/
file.rs

1use crate::core;
2use crate::core::cmp;
3use crate::io;
4use crate::io::prelude::*;
5use crate::io::{ErrorKind, SeekFrom};
6
7use crate::dir_entry::DirEntryEditor;
8use crate::error::FatfsError;
9use crate::fs::{FileSystem, ReadWriteSeek};
10use crate::time::{Date, DateTime, TimeProvider};
11
12pub const MAX_FILE_SIZE: u32 = core::u32::MAX;
13
14/// A FAT filesystem file object used for reading and writing data.
15///
16/// This struct is created by the `open_file` or `create_file` methods on `Dir`.
17pub struct File<'a, IO: ReadWriteSeek, TP, OCC> {
18    // Note first_cluster is None if file is empty
19    first_cluster: Option<u32>,
20    // Note: if offset points between clusters current_cluster is the previous cluster
21    current_cluster: Option<u32>,
22    // current position in this file
23    offset: u32,
24    // file dir entry editor - None for root dir
25    entry: Option<DirEntryEditor>,
26    // file-system reference
27    fs: &'a FileSystem<IO, TP, OCC>,
28}
29
30impl<'a, IO: ReadWriteSeek, TP, OCC> File<'a, IO, TP, OCC> {
31    pub(crate) fn new(
32        first_cluster: Option<u32>,
33        entry: Option<DirEntryEditor>,
34        fs: &'a FileSystem<IO, TP, OCC>,
35    ) -> Self {
36        File {
37            first_cluster,
38            entry,
39            fs,
40            current_cluster: None, // cluster before first one
41            offset: 0,
42        }
43    }
44
45    /// Truncate file in current position.
46    pub fn truncate(&mut self) -> io::Result<()> {
47        if let Some(ref mut e) = self.entry {
48            e.set_size(self.offset);
49            if self.offset == 0 {
50                e.set_first_cluster(None, self.fs.fat_type());
51            }
52        }
53        if self.offset > 0 {
54            debug_assert!(self.current_cluster.is_some());
55            // if offset is not 0 current cluster cannot be empty
56            self.fs.truncate_cluster_chain(self.current_cluster.unwrap()) // SAFE
57        } else {
58            debug_assert!(self.current_cluster.is_none());
59            if let Some(n) = self.first_cluster {
60                self.fs.free_cluster_chain(n)?;
61                self.first_cluster = None;
62            }
63            Ok(())
64        }
65    }
66
67    pub(crate) fn abs_pos(&self) -> Result<Option<u64>, FatfsError> {
68        // Returns current position relative to filesystem start
69        // Note: when between clusters it returns position after previous cluster
70        Ok(match self.current_cluster {
71            Some(n) => {
72                assert!(self.offset > 0); // offset == 0 should mean current_clusters is None.
73                let cluster_size = self.fs.cluster_size();
74                // We want offset_in_cluster to be in the range [1..cluster_size], and not
75                // [0..cluster_size - 1].
76                let offset_in_cluster = (self.offset - 1) % cluster_size + 1;
77                let offset_in_fs = self.fs.offset_from_cluster(n)? + (offset_in_cluster as u64);
78                Some(offset_in_fs)
79            }
80            None => None,
81        })
82    }
83
84    pub fn flush_dir_entry(&mut self) -> io::Result<()> {
85        if let Some(ref mut e) = self.entry {
86            e.flush(self.fs)?;
87        }
88        Ok(())
89    }
90
91    /// Sets date and time of creation for this file.
92    ///
93    /// Note: it is set to a value from the `TimeProvider` when creating a file.
94    /// Deprecated: if needed implement a custom `TimeProvider`.
95    #[deprecated]
96    pub fn set_created(&mut self, date_time: DateTime) {
97        if let Some(ref mut e) = self.entry {
98            e.set_created(date_time);
99        }
100    }
101
102    /// Sets date of last access for this file.
103    ///
104    /// Note: it is overwritten by a value from the `TimeProvider` on every file read operation.
105    /// Deprecated: if needed implement a custom `TimeProvider`.
106    #[deprecated]
107    pub fn set_accessed(&mut self, date: Date) {
108        if let Some(ref mut e) = self.entry {
109            e.set_accessed(date);
110        }
111    }
112
113    /// Sets date and time of last modification for this file.
114    ///
115    /// Note: it is overwritten by a value from the `TimeProvider` on every file write operation.
116    /// Deprecated: if needed implement a custom `TimeProvider`.
117    #[deprecated]
118    pub fn set_modified(&mut self, date_time: DateTime) {
119        if let Some(ref mut e) = self.entry {
120            e.set_modified(date_time);
121        }
122    }
123
124    /// Get the current length of this file.
125    pub fn len(&self) -> u32 {
126        match self.entry {
127            Some(ref e) => e.inner().size().unwrap_or(0),
128            None => 0,
129        }
130    }
131
132    /// Get the access time of this file.
133    pub fn accessed(&self) -> Date {
134        match self.entry {
135            Some(ref e) => e.inner().accessed(),
136            None => Date::epoch(),
137        }
138    }
139
140    /// Get the creation time of this file.
141    pub fn created(&self) -> DateTime {
142        match self.entry {
143            Some(ref e) => e.inner().created(),
144            None => DateTime::epoch(),
145        }
146    }
147
148    /// Get the modification time of this file.
149    pub fn modified(&self) -> DateTime {
150        match self.entry {
151            Some(ref e) => e.inner().modified(),
152            None => DateTime::epoch(),
153        }
154    }
155
156    /// Avoid writing any more updates to the file's dirent. This should be called if the file's
157    /// dirent is being deleted but the actual file clusters are being left as-is.
158    pub(crate) fn mark_deleted(&mut self) {
159        if let Some(ref mut e) = self.entry {
160            e.mark_deleted();
161        }
162    }
163
164    /// Avoid writing any more updates to the file's dirent. This should be called if the file's
165    /// dirent is being deleted but the actual file clusters are being left as-is.
166    pub(crate) fn mark_deleted_and_purged(&mut self) {
167        if let Some(ref mut e) = self.entry {
168            e.mark_deleted();
169            self.first_cluster.take();
170        }
171    }
172
173    /// True if mark_deleted() has been called on this file.
174    pub(crate) fn is_deleted(&self) -> bool {
175        if let Some(ref e) = self.entry {
176            e.inner().is_deleted()
177        } else {
178            false
179        }
180    }
181
182    /// Delete the file if it has been removed from its parent directory using `Dir::remove_dirent`.
183    pub fn purge(mut self) -> io::Result<()> {
184        let entry = match self.entry {
185            Some(ref e) => e,
186            None => {
187                return Err(io::Error::new(
188                    ErrorKind::InvalidInput,
189                    "Can't delete file with no dirent",
190                ))
191            }
192        };
193
194        if !entry.inner().is_deleted() {
195            return Err(io::Error::new(ErrorKind::InvalidInput, "Must call remove_dirent() first"));
196        }
197
198        if let Some(cluster) = self.first_cluster.take() {
199            self.fs.free_cluster_chain(cluster)?;
200        }
201
202        Ok(())
203    }
204
205    pub(crate) fn editor_mut(&mut self) -> Option<&mut DirEntryEditor> {
206        self.entry.as_mut()
207    }
208
209    pub(crate) fn editor(&self) -> Option<&DirEntryEditor> {
210        self.entry.as_ref()
211    }
212
213    fn size(&self) -> Option<u32> {
214        match self.entry {
215            Some(ref e) => e.inner().size(),
216            None => None,
217        }
218    }
219
220    fn is_dir(&self) -> bool {
221        match self.entry {
222            Some(ref e) => e.inner().is_dir(),
223            None => true, // root directory
224        }
225    }
226
227    fn bytes_left_in_file(&self) -> Option<usize> {
228        // Note: seeking beyond end of file is not allowed so overflow is impossible
229        self.size().map(|s| (s - self.offset) as usize)
230    }
231
232    fn set_first_cluster(&mut self, cluster: u32) {
233        self.first_cluster = Some(cluster);
234        if let Some(ref mut e) = self.entry {
235            e.set_first_cluster(self.first_cluster, self.fs.fat_type());
236        }
237    }
238
239    pub(crate) fn first_cluster(&self) -> Option<u32> {
240        self.first_cluster
241    }
242
243    fn flush(&mut self) -> io::Result<()> {
244        self.flush_dir_entry()?;
245        let mut disk = self.fs.disk.borrow_mut();
246        disk.flush()
247    }
248}
249
250impl<IO: ReadWriteSeek, TP: TimeProvider, OCC> File<'_, IO, TP, OCC> {
251    fn update_dir_entry_after_write(&mut self) {
252        let offset = self.offset;
253        if let Some(ref mut e) = self.entry {
254            let now = self.fs.options.time_provider.get_current_date_time();
255            e.set_modified(now);
256            if e.inner().size().map_or(false, |s| offset > s) {
257                e.set_size(offset);
258            }
259        }
260    }
261}
262
263impl<IO: ReadWriteSeek, TP, OCC> Drop for File<'_, IO, TP, OCC> {
264    fn drop(&mut self) {
265        if let Err(err) = self.flush() {
266            error!("flush failed {}", err);
267        }
268
269        // If we've been deleted, then mark clusters as free.
270        if let Some(ref entry) = self.entry {
271            if entry.inner().is_deleted() {
272                if let Some(cluster) = self.first_cluster.take() {
273                    let _ = self
274                        .fs
275                        .free_cluster_chain(cluster)
276                        .map_err(|e| error!("free_cluster_chain failed {}", e));
277                }
278            }
279        }
280    }
281}
282
283impl<IO: ReadWriteSeek, TP: TimeProvider, OCC> Read for File<'_, IO, TP, OCC> {
284    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
285        let cluster_size = self.fs.cluster_size();
286        let current_cluster_opt = if self.offset % cluster_size == 0 {
287            // next cluster
288            match self.current_cluster {
289                None => self.first_cluster,
290                Some(n) => {
291                    let r = self.fs.cluster_iter(n).next();
292                    match r {
293                        Some(Err(err)) => return Err(err),
294                        Some(Ok(n)) => Some(n),
295                        None => None,
296                    }
297                }
298            }
299        } else {
300            self.current_cluster
301        };
302        let current_cluster = match current_cluster_opt {
303            Some(n) => n,
304            None => return Ok(0),
305        };
306        let offset_in_cluster = self.offset % cluster_size;
307        let bytes_left_in_cluster = (cluster_size - offset_in_cluster) as usize;
308        let bytes_left_in_file = self.bytes_left_in_file().unwrap_or(bytes_left_in_cluster);
309        let read_size = cmp::min(cmp::min(buf.len(), bytes_left_in_cluster), bytes_left_in_file);
310        if read_size == 0 {
311            return Ok(0);
312        }
313        trace!("read {} bytes in cluster {}", read_size, current_cluster);
314        let offset_in_fs =
315            self.fs.offset_from_cluster(current_cluster)? + (offset_in_cluster as u64);
316        let read_bytes = {
317            let mut disk = self.fs.disk.borrow_mut();
318            disk.seek(SeekFrom::Start(offset_in_fs))?;
319            disk.read(&mut buf[..read_size])?
320        };
321        if read_bytes == 0 {
322            return Ok(0);
323        }
324        self.offset += read_bytes as u32;
325        self.current_cluster = Some(current_cluster);
326
327        if let Some(ref mut e) = self.entry {
328            if self.fs.options.update_accessed_date {
329                let now = self.fs.options.time_provider.get_current_date();
330                e.set_accessed(now);
331            }
332        }
333        Ok(read_bytes)
334    }
335}
336
337impl<IO: ReadWriteSeek, TP: TimeProvider, OCC> Write for File<'_, IO, TP, OCC> {
338    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
339        let cluster_size = self.fs.cluster_size();
340        let offset_in_cluster = self.offset % cluster_size;
341        let bytes_left_in_cluster = (cluster_size - offset_in_cluster) as usize;
342        let bytes_left_until_max_file_size = (MAX_FILE_SIZE - self.offset) as usize;
343        let write_size = cmp::min(buf.len(), bytes_left_in_cluster);
344        let write_size = cmp::min(write_size, bytes_left_until_max_file_size);
345        // Exit early if we are going to write no data
346        if write_size == 0 {
347            return Ok(0);
348        }
349        // Mark the volume 'dirty'
350        self.fs.set_dirty_flag(true)?;
351        // Get cluster for write possibly allocating new one
352        let current_cluster = if self.offset % cluster_size == 0 {
353            // next cluster
354            let next_cluster = match self.current_cluster {
355                None => self.first_cluster,
356                Some(n) => {
357                    let r = self.fs.cluster_iter(n).next();
358                    match r {
359                        Some(Err(err)) => return Err(err),
360                        Some(Ok(n)) => Some(n),
361                        None => None,
362                    }
363                }
364            };
365            match next_cluster {
366                Some(n) => n,
367                None => {
368                    // end of chain reached - allocate new cluster
369                    let new_cluster = self.fs.alloc_cluster(self.current_cluster, self.is_dir())?;
370                    trace!("allocated cluser {}", new_cluster);
371                    if self.first_cluster.is_none() {
372                        self.set_first_cluster(new_cluster);
373                    }
374                    new_cluster
375                }
376            }
377        } else {
378            // self.current_cluster should be a valid cluster
379            match self.current_cluster {
380                Some(n) => n,
381                None => panic!("Offset inside cluster but no cluster allocated"),
382            }
383        };
384        trace!("write {} bytes in cluster {}", write_size, current_cluster);
385        let offset_in_fs =
386            self.fs.offset_from_cluster(current_cluster)? + (offset_in_cluster as u64);
387        let written_bytes = {
388            let mut disk = self.fs.disk.borrow_mut();
389            disk.seek(SeekFrom::Start(offset_in_fs))?;
390            disk.write(&buf[..write_size])?
391        };
392        if written_bytes == 0 {
393            return Ok(0);
394        }
395        // some bytes were written - update position and optionally size
396        self.offset += written_bytes as u32;
397        self.current_cluster = Some(current_cluster);
398        self.update_dir_entry_after_write();
399        Ok(written_bytes)
400    }
401
402    fn flush(&mut self) -> io::Result<()> {
403        Self::flush(self)
404    }
405}
406
407impl<IO: ReadWriteSeek, TP, OCC> Seek for File<'_, IO, TP, OCC> {
408    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
409        let mut new_pos = match pos {
410            SeekFrom::Current(x) => self.offset as i64 + x,
411            SeekFrom::Start(x) => x as i64,
412            SeekFrom::End(x) => {
413                let size = self.size().expect("cannot seek from end if size is unknown") as i64;
414                size + x
415            }
416        };
417        if new_pos < 0 {
418            return Err(io::Error::new(ErrorKind::InvalidInput, "Seek to a negative offset"));
419        }
420        if let Some(s) = self.size() {
421            if new_pos > s as i64 {
422                trace!("seek beyond end of file");
423                new_pos = s as i64;
424            }
425        }
426        let mut new_pos = new_pos as u32;
427        trace!("file seek {} -> {} - entry {:?}", self.offset, new_pos, self.entry);
428        if new_pos == self.offset {
429            // position is the same - nothing to do
430            return Ok(self.offset as u64);
431        }
432        // get number of clusters to seek (favoring previous cluster in corner case)
433        let cluster_count = (self.fs.clusters_from_bytes(new_pos as u64) as i32 - 1) as isize;
434        let old_cluster_count =
435            (self.fs.clusters_from_bytes(self.offset as u64) as i32 - 1) as isize;
436        let new_cluster = if new_pos == 0 {
437            None
438        } else if cluster_count == old_cluster_count {
439            self.current_cluster
440        } else {
441            match self.first_cluster {
442                Some(n) => {
443                    let mut cluster = n;
444                    let mut iter = self.fs.cluster_iter(n);
445                    for i in 0..cluster_count {
446                        cluster = match iter.next() {
447                            Some(r) => r?,
448                            None => {
449                                // chain ends before new position - seek to end of last cluster
450                                new_pos = self.fs.bytes_from_clusters((i + 1) as u32)? as u32;
451                                break;
452                            }
453                        };
454                    }
455                    Some(cluster)
456                }
457                None => {
458                    // empty file - always seek to 0
459                    new_pos = 0;
460                    None
461                }
462            }
463        };
464        self.offset = new_pos as u32;
465        self.current_cluster = new_cluster;
466        Ok(self.offset as u64)
467    }
468}