exfat_fs/dir/entry/fs/
file.rs

1use alloc::sync::Arc;
2
3use crate::{
4    dir::{BootSector, ClusterChainOptions, ClusterChainReader, Fat, entry::StreamExtensionEntry},
5    disk::{self, ReadOffset},
6    error::ClusterChainError,
7    timestamp::Timestamps,
8};
9
10#[derive(Clone)]
11pub struct File<O: disk::ReadOffset> {
12    name: String,
13    len: u64,
14    reader: Option<ClusterChainReader<Arc<O>, Arc<BootSector>>>,
15    timestamps: Timestamps,
16}
17impl<O: disk::ReadOffset> File<O> {
18    pub(crate) fn try_new(
19        disk: &Arc<O>,
20        boot: &Arc<BootSector>,
21        fat: &Fat,
22        name: String,
23        stream: StreamExtensionEntry,
24        timestamps: Timestamps,
25    ) -> Result<Self, ClusterChainError>
26    where
27        <O as ReadOffset>::Err: core::fmt::Debug,
28    {
29        // create a cluster reader
30        let first_cluster = stream.first_cluster;
31        let len = stream.valid_data_length;
32        let reader = if first_cluster == 0 {
33            None
34        } else {
35            let options = if stream.general_secondary_flags.no_fat_chain() {
36                ClusterChainOptions::Contiguous { data_length: len }
37            } else {
38                ClusterChainOptions::Fat {
39                    data_length: Some(len),
40                }
41            };
42            Some(ClusterChainReader::try_new(
43                Arc::clone(boot),
44                fat,
45                first_cluster,
46                options,
47                Arc::clone(disk),
48            )?)
49        };
50
51        Ok(Self {
52            name,
53            len,
54            reader,
55            timestamps,
56        })
57    }
58
59    pub fn name(&self) -> &str {
60        self.name.as_ref()
61    }
62
63    pub fn is_empty(&self) -> bool {
64        self.len == 0
65    }
66
67    pub fn len(&self) -> u64 {
68        self.len
69    }
70
71    pub fn timestamps(&self) -> &Timestamps {
72        &self.timestamps
73    }
74}
75
76#[cfg(feature = "std")]
77impl<O: disk::ReadOffset> std::io::Seek for File<O> {
78    fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
79        use std::io::{Error, ErrorKind, SeekFrom};
80
81        let Some(r) = &mut self.reader else {
82            return std::io::empty().seek(pos);
83        };
84
85        // get absolute offset.
86        let o = match pos {
87            SeekFrom::Start(v) => v.min(r.data_length()),
88            SeekFrom::End(v) => {
89                if v >= 0 {
90                    r.data_length()
91                } else if let Some(v) = r.data_length().checked_sub(v.unsigned_abs()) {
92                    v
93                } else {
94                    return Err(Error::from(ErrorKind::InvalidInput));
95                }
96            }
97            SeekFrom::Current(v) => v.try_into().map_or_else(
98                |_| {
99                    r.stream_position()
100                        .checked_sub(v.unsigned_abs())
101                        .ok_or_else(|| Error::from(ErrorKind::InvalidInput))
102                },
103                |v| Ok(r.stream_position().saturating_add(v).min(r.data_length())),
104            )?,
105        };
106
107        assert!(r.seek(o));
108
109        Ok(o)
110    }
111
112    fn rewind(&mut self) -> std::io::Result<()> {
113        let r = match &mut self.reader {
114            Some(v) => v,
115            None => return Ok(()),
116        };
117
118        r.rewind();
119
120        Ok(())
121    }
122
123    fn stream_position(&mut self) -> std::io::Result<u64> {
124        let r = match &mut self.reader {
125            Some(v) => v,
126            None => return Ok(0),
127        };
128
129        Ok(r.stream_position())
130    }
131}
132
133#[cfg(feature = "std")]
134impl<D: ReadOffset> std::io::Read for File<D>
135where
136    D::Err: Into<std::io::Error>,
137{
138    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
139        match &mut self.reader {
140            Some(v) => v.read(buf).map_err(Into::into),
141            None => Ok(0),
142        }
143    }
144}