1use crate::parser::Parser;
6use crate::readers::ReaderWriter;
7use crate::structs::{FIRST_BG_PADDING, InvalidAddressErrorType, ParseToStruct, ParsingError};
8use std::sync::Arc;
9
10pub struct Ext4Processor {
12 fs: Parser,
13 reader_writer: Arc<dyn ReaderWriter>,
14 read_only: bool,
15}
16
17impl std::ops::Deref for Ext4Processor {
18 type Target = Parser;
19
20 fn deref(&self) -> &Self::Target {
21 &self.fs
22 }
23}
24
25impl Ext4Processor {
26 pub fn new(reader_writer: Arc<dyn ReaderWriter>, read_only: bool) -> Self {
27 Self { fs: Parser::new(Box::new(reader_writer.clone())), reader_writer, read_only }
28 }
29
30 pub fn read_only(&self) -> bool {
31 self.read_only
32 }
33
34 fn write_blocks(&self, block_number: u64, data: &[u8]) -> Result<(), ParsingError> {
36 if self.read_only {
37 return Err(ParsingError::Incompatible("Cannot write to read-only Ext4".to_string()));
38 }
39
40 if block_number == 0 {
41 return Err(ParsingError::InvalidAddress(
42 InvalidAddressErrorType::Lower,
43 0,
44 FIRST_BG_PADDING,
45 ));
46 }
47 let block_size = self.block_size()?;
48 if data.len() as u64 % block_size != 0 {
49 return Err(ParsingError::Incompatible(format!(
50 "Data length {} is not a multiple of block size {}",
51 data.len(),
52 block_size
53 )));
54 }
55
56 let address = block_number
57 .checked_mul(block_size)
58 .ok_or(ParsingError::BlockNumberOutOfBounds(block_number))?;
59
60 self.reader_writer.write(address, data)?;
61
62 Ok(())
63 }
64
65 pub fn overwrite_extents(
75 &self,
76 inode_num: u32,
77 data: impl AsRef<[u8]>,
78 offset: u64,
79 ) -> Result<(), ParsingError> {
80 if self.read_only {
81 return Err(ParsingError::Incompatible("Cannot write to read-only Ext4".to_string()));
82 }
83
84 let mut inode = self.inode(inode_num)?;
85 let root_extent_tree_node = inode.extent_tree_node()?;
86 let request = offset..offset + data.as_ref().len() as u64;
87 let block_size = self.block_size()?;
88 let mut node_size = inode.size();
89
90 let mut overlaps = Vec::new();
93 let mut allocated_bytes_in_request = 0;
94
95 self.iterate_extents_in_tree(&root_extent_tree_node, &mut |extent| {
96 let range = (extent.e_blk.get() as u64 * block_size)
97 ..((extent.e_blk.get() as u64 + extent.e_len.get() as u64) * block_size);
98 let overlap =
99 std::cmp::max(range.start, request.start)..std::cmp::min(range.end, request.end);
100 if overlap.start >= overlap.end {
101 return Ok(());
103 }
104
105 allocated_bytes_in_request += overlap.end - overlap.start;
106 let physical_block_cursor =
107 extent.target_block_num() + ((overlap.start - range.start) / block_size);
108 overlaps.push((overlap, physical_block_cursor));
109 Ok(())
110 })?;
111
112 if allocated_bytes_in_request < data.as_ref().len() as u64 {
113 return Err(ParsingError::NotSupported("allocation".to_string()));
115 }
116
117 for (overlap, mut physical_block_cursor) in overlaps {
118 let mut current_offset = overlap.start;
119 while current_offset < overlap.end {
120 let write_buf_cursor = (current_offset - request.start) as usize;
121 let block_off = current_offset % block_size;
122 let remaining_in_overlap = overlap.end - current_offset;
123
124 if block_off == 0 && remaining_in_overlap >= block_size {
125 let full_blocks = remaining_in_overlap / block_size;
127 let write_len = full_blocks * block_size;
128 self.write_blocks(
129 physical_block_cursor,
130 &data.as_ref()[write_buf_cursor..write_buf_cursor + write_len as usize],
131 )?;
132
133 physical_block_cursor += full_blocks;
134 current_offset += write_len;
135 } else {
136 let remaining_in_block = block_size - block_off;
139 let write_len = std::cmp::min(remaining_in_block, remaining_in_overlap);
140 let mut block_data = self.block(physical_block_cursor)?.into_vec();
141 block_data[block_off as usize..block_off as usize + write_len as usize]
142 .copy_from_slice(
143 &data.as_ref()[write_buf_cursor..write_buf_cursor + write_len as usize],
144 );
145 self.write_blocks(physical_block_cursor, &block_data)?;
146
147 physical_block_cursor += 1;
148 current_offset += write_len;
149 }
150 }
151
152 node_size = std::cmp::max(node_size, overlap.end);
153 }
154 if node_size != inode.size() {
160 inode.update_size(node_size);
161 let inode_addr = self.inode_addr(inode_num)?;
162 inode.from_struct_to_writer(self.reader_writer.as_ref(), inode_addr)?;
163 }
164 Ok(())
165 }
166
167 pub fn sync(&self) -> Result<(), ParsingError> {
168 self.reader_writer.sync()?;
169 Ok(())
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176 use crate::readers::{BlockDeviceReader, VecReader};
177 use crate::structs::ParsingError;
178 use std::fs;
179 use std::path::Path;
180 use vmo_backed_block_server::{InitialContents, VmoBackedServerOptions};
181 use zx::Vmo;
182 use {fidl_fuchsia_storage_block as fblock, fuchsia_async as fasync};
183
184 #[fuchsia::test]
185 fn test_processor_read_only_blocks_write() {
186 let data = fs::read("/pkg/data/1file.img").expect("Unable to read file");
187 let read_only_processor = Ext4Processor::new(Arc::new(VecReader::new(data)), true);
188
189 let error = read_only_processor
190 .write_blocks(1, &[0u8; 1024])
191 .expect_err("passed write_blocks unexpectedly");
192 match error {
193 ParsingError::Incompatible(_) => {}
194 _ => panic!("Expected read-only error"),
195 }
196
197 let error = read_only_processor
199 .overwrite_extents(2, &[0u8; 10], 0)
200 .expect_err("passed overwrite_extents unexpectedly");
201 match error {
202 ParsingError::Incompatible(_) => {}
203 _ => panic!("Expected read-only error"),
204 }
205 }
206
207 #[fuchsia::test]
208 fn test_processor_write_block_invalid_address() {
209 let data = fs::read("/pkg/data/1file.img").expect("Unable to read file");
210 let processor = Ext4Processor::new(Arc::new(VecReader::new(data)), false);
211
212 let error =
213 processor.write_blocks(0, &[0u8; 1024]).expect_err("passed write_blocks unexpectedly");
214 match error {
215 ParsingError::InvalidAddress(InvalidAddressErrorType::Lower, 0, FIRST_BG_PADDING) => {}
216 _ => panic!("Expected invalid address error, got {:?}", error),
217 }
218 }
219
220 #[fuchsia::test]
221 fn test_processor_write_block_out_of_bounds() {
222 let data = fs::read("/pkg/data/1file.img").expect("Unable to read file");
223 let processor = Ext4Processor::new(Arc::new(VecReader::new(data)), false);
224
225 let error = processor
226 .write_blocks(u64::MAX, &[0u8; 1024])
227 .expect_err("passed write_blocks unexpectedly");
228 match error {
229 ParsingError::BlockNumberOutOfBounds(u64::MAX) => {}
230 _ => panic!("Expected out of bounds error, got {:?}", error),
231 }
232 }
233
234 #[fuchsia::test]
235 fn test_processor_writeable_overwrite_extents() {
236 let data = fs::read("/pkg/data/1file.img").expect("Unable to read file");
237 let vmo = Vmo::create(data.len() as u64).expect("failed to create VMO");
238 vmo.write(data.as_slice(), 0).expect("failed to write to VMO");
239 let server = Arc::new(
240 VmoBackedServerOptions {
241 block_size: 512,
242 initial_contents: InitialContents::FromVmo(vmo),
243 ..Default::default()
244 }
245 .build()
246 .expect("build from VmoBackedServerOptions failed"),
247 );
248
249 let server_clone = server.clone();
250 let (block_client_end1, block_server_end1) =
251 fidl::endpoints::create_endpoints::<fblock::BlockMarker>();
252 std::thread::spawn(move || {
253 let mut executor = fasync::TestExecutor::new();
254 let _task =
255 executor.run_singlethreaded(server_clone.serve(block_server_end1.into_stream()));
256 });
257 let rw_processor = Arc::new(Ext4Processor::new(
258 Arc::new(
259 BlockDeviceReader::from_client_end(block_client_end1)
260 .expect("failed to create block device reader"),
261 ),
262 false,
263 ));
264
265 let file_ino = rw_processor
266 .entry_at_path(Path::new("file1"))
267 .expect("failed entry at path")
268 .e2d_ino
269 .get();
270
271 let mut expected = rw_processor.read_data(file_ino).expect("failed to read data");
272 assert_eq!(
273 str::from_utf8(expected.as_slice()).expect("failed to read data"),
274 "file1 contents.\n"
275 );
276 let original_size = rw_processor.inode(file_ino).expect("failed to read inode").size();
277 assert_eq!(original_size, expected.len() as u64);
278
279 rw_processor
280 .overwrite_extents(file_ino, &[1u8; 1], 1)
281 .expect("failed to overwrite extents");
282 expected[1] = 1;
283
284 let new_data = rw_processor.read_data(file_ino).expect("failed to read data");
285 assert_eq!(new_data, expected);
286
287 rw_processor
290 .overwrite_extents(file_ino, &[1u8; 2], expected.len() as u64 + 2)
291 .expect("failed to overwrite extents");
292 expected.extend_from_slice(&[0, 0, 1, 1]);
293
294 let new_data = rw_processor.read_data(file_ino).expect("failed to read data");
295 assert_eq!(new_data, expected);
296
297 let new_size = rw_processor.inode(file_ino).expect("failed to read inode").size();
299 assert_eq!(new_size, original_size + 4);
300 }
301
302 #[fuchsia::test]
303 fn test_processor_overwrite_with_unallocated_blocks_fails() {
304 let data = fs::read("/pkg/data/1file.img").expect("Unable to read file");
305 let vmo = Vmo::create(data.len() as u64).expect("failed to create VMO");
306 vmo.write(data.as_slice(), 0).expect("failed to write to VMO");
307 let server = Arc::new(
308 VmoBackedServerOptions {
309 block_size: 512,
310 initial_contents: InitialContents::FromVmo(vmo),
311 ..Default::default()
312 }
313 .build()
314 .expect("build from VmoBackedServerOptions failed"),
315 );
316
317 let server_clone = server.clone();
318 let (block_client_end1, block_server_end1) =
319 fidl::endpoints::create_endpoints::<fblock::BlockMarker>();
320 std::thread::spawn(move || {
321 let mut executor = fasync::TestExecutor::new();
322 let _task =
323 executor.run_singlethreaded(server_clone.serve(block_server_end1.into_stream()));
324 });
325 let rw_processor = Arc::new(Ext4Processor::new(
326 Arc::new(
327 BlockDeviceReader::from_client_end(block_client_end1)
328 .expect("failed to create block device reader"),
329 ),
330 false,
331 ));
332
333 let file_ino = rw_processor
334 .entry_at_path(Path::new("file1"))
335 .expect("failed entry at path")
336 .e2d_ino
337 .get();
338
339 let original_contents = rw_processor.read_data(file_ino).expect("failed to read data");
340
341 let long_data = vec![2u8; 8192];
342 let error = rw_processor
343 .overwrite_extents(file_ino, &long_data, 0)
344 .expect_err("overwrite extents passed unexpectedly");
345 match error {
346 ParsingError::NotSupported(_) => {}
347 _ => panic!("Expected NotSupported error, got {:?}", error),
348 }
349
350 assert_eq!(
352 rw_processor.read_data(file_ino).expect("failed to read data"),
353 original_contents
354 );
355 }
356}