exfat_fs/dir/entry/fs/
file.rs1use 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 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 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}