1use crate::error::{check_errno, check_errno_with_path, Error, Result};
4use crate::fs::Ext4Fs;
5use crate::types::{Metadata, OpenFlags, SeekFrom};
6use ext4_lwext4_sys::{
7 ext4_fclose, ext4_file, ext4_fopen2, ext4_fread, ext4_fseek, ext4_fsize, ext4_ftell,
8 ext4_ftruncate, ext4_fwrite,
9};
10use std::io::{self, Read, Seek, Write};
11use std::os::raw::c_void;
12
13pub struct File<'a> {
17 fs: &'a Ext4Fs,
18 inner: ext4_file,
19 path: String,
20}
21
22impl<'a> File<'a> {
23 pub(crate) fn open(fs: &'a Ext4Fs, path: &str, flags: OpenFlags) -> Result<Self> {
25 let full_path = fs.make_path(path)?;
26 let mut inner = ext4_file::default();
27
28 let ret = unsafe { ext4_fopen2(&mut inner, full_path.as_ptr(), flags.to_raw()) };
29 check_errno_with_path(ret, path)?;
30
31 Ok(Self {
32 fs,
33 inner,
34 path: path.to_string(),
35 })
36 }
37
38 pub fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
40 let mut rcnt: usize = 0;
41 let ret = unsafe {
42 ext4_fread(
43 &mut self.inner,
44 buf.as_mut_ptr() as *mut c_void,
45 buf.len(),
46 &mut rcnt,
47 )
48 };
49 check_errno(ret)?;
50 Ok(rcnt)
51 }
52
53 pub fn write(&mut self, buf: &[u8]) -> Result<usize> {
55 let mut wcnt: usize = 0;
56 let ret = unsafe {
57 ext4_fwrite(
58 &mut self.inner,
59 buf.as_ptr() as *const c_void,
60 buf.len(),
61 &mut wcnt,
62 )
63 };
64 check_errno(ret)?;
65 Ok(wcnt)
66 }
67
68 pub fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
70 let (offset, origin) = pos.to_raw();
71 let ret = unsafe { ext4_fseek(&mut self.inner, offset, origin) };
72 check_errno(ret)?;
73 Ok(self.position())
74 }
75
76 pub fn position(&self) -> u64 {
78 unsafe { ext4_ftell(&self.inner as *const ext4_file as *mut ext4_file) }
79 }
80
81 pub fn size(&self) -> u64 {
83 unsafe { ext4_fsize(&self.inner as *const ext4_file as *mut ext4_file) }
84 }
85
86 pub fn truncate(&mut self, size: u64) -> Result<()> {
88 let ret = unsafe { ext4_ftruncate(&mut self.inner, size) };
89 check_errno(ret)
90 }
91
92 pub fn flush(&mut self) -> Result<()> {
94 Ok(())
96 }
97
98 pub fn sync(&mut self) -> Result<()> {
100 self.fs.sync()
101 }
102
103 pub fn metadata(&self) -> Result<Metadata> {
105 self.fs.metadata(&self.path)
106 }
107
108 pub fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
110 let size = self.size() as usize;
111 buf.reserve(size);
112
113 self.seek(SeekFrom::Start(0))?;
115
116 let mut total_read = 0;
118 let mut chunk = vec![0u8; 8192];
119
120 loop {
121 let n = self.read(&mut chunk)?;
122 if n == 0 {
123 break;
124 }
125 buf.extend_from_slice(&chunk[..n]);
126 total_read += n;
127 }
128
129 Ok(total_read)
130 }
131
132 pub fn read_to_string(&mut self, buf: &mut String) -> Result<usize> {
134 let mut bytes = Vec::new();
135 let n = self.read_to_end(&mut bytes)?;
136 *buf = String::from_utf8(bytes)
137 .map_err(|_| Error::InvalidArgument("file contains invalid UTF-8".to_string()))?;
138 Ok(n)
139 }
140
141 pub fn write_all(&mut self, buf: &[u8]) -> Result<()> {
143 let mut written = 0;
144 while written < buf.len() {
145 let n = self.write(&buf[written..])?;
146 if n == 0 {
147 return Err(Error::Io(io::Error::new(
148 io::ErrorKind::WriteZero,
149 "failed to write whole buffer",
150 )));
151 }
152 written += n;
153 }
154 Ok(())
155 }
156}
157
158impl Drop for File<'_> {
159 fn drop(&mut self) {
160 unsafe {
161 ext4_fclose(&mut self.inner);
162 }
163 }
164}
165
166impl Read for File<'_> {
169 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
170 File::read(self, buf).map_err(|e| io::Error::new(io::ErrorKind::Other, e))
171 }
172}
173
174impl Write for File<'_> {
175 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
176 File::write(self, buf).map_err(|e| io::Error::new(io::ErrorKind::Other, e))
177 }
178
179 fn flush(&mut self) -> io::Result<()> {
180 File::flush(self).map_err(|e| io::Error::new(io::ErrorKind::Other, e))
181 }
182}
183
184impl Seek for File<'_> {
185 fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
186 let pos = match pos {
187 io::SeekFrom::Start(n) => SeekFrom::Start(n),
188 io::SeekFrom::End(n) => SeekFrom::End(n),
189 io::SeekFrom::Current(n) => SeekFrom::Current(n),
190 };
191 File::seek(self, pos).map_err(|e| io::Error::new(io::ErrorKind::Other, e))
192 }
193}