exfat_fs/
disk.rs

1use core::ops::Deref;
2
3use alloc::sync::Arc;
4/// Writes zeroes to a file from the given absolute offset (in bytes), up to the given size.
5pub fn write_zeroes<T>(f: &mut T, size: u64, offset: u64) -> Result<(), T::Err>
6where
7    T: WriteSeek,
8{
9    let buffer = [0u8; 4 * crate::KB as usize];
10
11    // seek to offset
12    f.seek(SeekFrom::Start(offset))?;
13
14    let mut remaining = size;
15    while remaining > 0 {
16        let iter_size = remaining.min(buffer.len() as u64);
17        // `iter_size` is max 4KB so this cast is fine
18        if f.write(&buffer[..iter_size as usize])? != iter_size as usize {
19            return Err(f.failed_to_write());
20        }
21        remaining -= iter_size;
22    }
23    Ok(())
24}
25
26pub trait WriteSeek {
27    type Err;
28    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Err>;
29    fn failed_to_write(&self) -> Self::Err;
30    fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Err>;
31    fn seek(&mut self, pos: SeekFrom) -> Result<u64, Self::Err>;
32    fn stream_position(&mut self) -> Result<u64, Self::Err>;
33}
34#[cfg(feature = "std")]
35impl<T> WriteSeek for T
36where
37    T: std::io::Write + std::io::Seek,
38{
39    type Err = std::io::Error;
40
41    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Err> {
42        std::io::Write::write(self, buf)
43    }
44    fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Err> {
45        std::io::Write::write_all(self, buf)
46    }
47    fn failed_to_write(&self) -> Self::Err {
48        Self::Err::new(std::io::ErrorKind::WriteZero, "Failed to write 0s")
49    }
50    fn seek(&mut self, pos: SeekFrom) -> Result<u64, Self::Err> {
51        std::io::Seek::seek(self, pos.into())
52    }
53    fn stream_position(&mut self) -> Result<u64, Self::Err> {
54        std::io::Seek::stream_position(self)
55    }
56}
57
58pub enum SeekFrom {
59    Start(u64),
60    End(i64),
61    Current(i64),
62}
63
64#[cfg(feature = "std")]
65impl From<SeekFrom> for std::io::SeekFrom {
66    fn from(value: SeekFrom) -> Self {
67        match value {
68            SeekFrom::Start(x) => std::io::SeekFrom::Start(x),
69            SeekFrom::End(x) => std::io::SeekFrom::End(x),
70            SeekFrom::Current(x) => std::io::SeekFrom::Current(x),
71        }
72    }
73}
74
75pub trait PartitionError: core::fmt::Debug {
76    fn unexpected_eop() -> Self;
77
78    fn cluster_not_found(cluster: u32) -> Self;
79}
80
81pub trait ReadOffset {
82    type Err: PartitionError + 'static;
83
84    fn read_at(&self, offset: u64, buffer: &mut [u8]) -> Result<usize, Self::Err>;
85
86    fn read_exact(&self, mut offset: u64, mut buffer: &mut [u8]) -> Result<(), Self::Err> {
87        while !buffer.is_empty() {
88            match self.read_at(offset, buffer) {
89                Ok(0) => break,
90                Ok(n) => {
91                    buffer = &mut buffer[n..];
92                    offset = offset
93                        .checked_add(n as u64)
94                        .ok_or(PartitionError::unexpected_eop())?;
95                }
96                Err(e) => return Err(e),
97            }
98        }
99        Ok(())
100    }
101}
102
103#[cfg(feature = "std")]
104impl PartitionError for std::io::Error {
105    fn unexpected_eop() -> Self {
106        std::io::Error::from(std::io::ErrorKind::UnexpectedEof)
107    }
108
109    fn cluster_not_found(cluster: u32) -> Self {
110        std::io::Error::new(
111            std::io::ErrorKind::Other,
112            format!("cluster #{cluster} is not available"),
113        )
114    }
115}
116
117impl<T: ReadOffset> ReadOffset for &T {
118    type Err = T::Err;
119
120    fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<usize, Self::Err> {
121        (*self).read_at(offset, buf)
122    }
123}
124impl<T: ReadOffset> ReadOffset for Arc<T> {
125    type Err = T::Err;
126
127    fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<usize, Self::Err> {
128        self.deref().read_at(offset, buf)
129    }
130}
131#[cfg(feature = "std")]
132impl ReadOffset for std::fs::File {
133    type Err = std::io::Error;
134
135    #[cfg(unix)]
136    fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<usize, Self::Err> {
137        std::os::unix::fs::FileExt::read_at(self, buf, offset)
138    }
139
140    #[cfg(windows)]
141    fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<usize, Self::Err> {
142        std::os::windows::fs::FileExt::seek_read(self, buf, offset)
143    }
144}