1use crate::constants::*;
10use crate::file_tree::BlockRange;
11use crate::types::*;
12use std::io::{self, Read, Seek, SeekFrom, Write};
13
14#[inline]
20fn div_ceil(n: u32, d: u32) -> u32 {
21 (n + d - 1) / d
22}
23
24fn fill_extent_leaves(start: u32, num_blocks: u32, num_extents: u32, offset: u32) -> Vec<ExtentLeaf> {
28 let mut leaves = Vec::with_capacity(num_extents as usize);
29 let mut remaining = num_blocks;
30 let mut phys = start;
31 let mut logical = offset;
32
33 for _ in 0..num_extents {
34 let len = remaining.min(MAX_BLOCKS_PER_EXTENT);
35 leaves.push(ExtentLeaf {
36 block: logical,
37 len: len as u16,
38 start_hi: 0,
39 start_lo: phys,
40 });
41 phys += len;
42 logical += len;
43 remaining -= len;
44 }
45
46 leaves
47}
48
49pub fn write_extents<W: Write + Seek>(
62 inode: &mut Inode,
63 blocks: BlockRange,
64 block_size: u32,
65 writer: &mut W,
66 current_block: &mut u32,
67) -> io::Result<()> {
68 let data_blocks = blocks.end - blocks.start;
69 if data_blocks == 0 {
70 return Ok(());
71 }
72
73 let num_extents = div_ceil(data_blocks, MAX_BLOCKS_PER_EXTENT);
74
75 let max_inline = 4u32;
78
79 if num_extents <= max_inline {
80 write_inline_extents(inode, blocks.start, data_blocks, num_extents, block_size);
82 } else {
83 write_indexed_extents(inode, blocks.start, data_blocks, num_extents, block_size, writer, current_block)?;
86 }
87
88 Ok(())
89}
90
91fn write_inline_extents(
93 inode: &mut Inode,
94 start: u32,
95 data_blocks: u32,
96 num_extents: u32,
97 block_size: u32,
98) {
99 let leaves = fill_extent_leaves(start, data_blocks, num_extents, 0);
100
101 let mut buf = [0u8; INODE_BLOCK_SIZE];
102
103 let header = ExtentHeader {
105 magic: EXTENT_HEADER_MAGIC,
106 entries: num_extents as u16,
107 max: 4,
108 depth: 0,
109 generation: 0,
110 };
111 header.write_to(&mut buf[..ExtentHeader::SIZE]);
112
113 for (i, leaf) in leaves.iter().enumerate() {
115 let off = ExtentHeader::SIZE + i * ExtentLeaf::SIZE;
116 leaf.write_to(&mut buf[off..off + ExtentLeaf::SIZE]);
117 }
118
119 inode.block = buf;
120
121 if inode.flags & inode_flags::HUGE_FILE != 0 {
125 inode.blocks_lo = data_blocks;
126 } else {
127 let sectors = (data_blocks as u64) * (block_size as u64 / 512);
128 inode.blocks_lo = sectors as u32;
129 }
130 inode.flags |= inode_flags::EXTENTS;
131}
132
133fn write_indexed_extents<W: Write + Seek>(
136 inode: &mut Inode,
137 start: u32,
138 data_blocks: u32,
139 num_extents: u32,
140 block_size: u32,
141 writer: &mut W,
142 current_block: &mut u32,
143) -> io::Result<()> {
144 let leaves_per_block = (block_size as usize - ExtentHeader::SIZE - ExtentTail::SIZE) / ExtentLeaf::SIZE;
148
149 let num_index_blocks = div_ceil(num_extents, leaves_per_block as u32);
151
152 debug_assert!(num_index_blocks <= 4, "files requiring >4 index blocks (depth>1) are not supported");
154
155 let all_leaves = fill_extent_leaves(start, data_blocks, num_extents, 0);
156
157 let index_block_start = *current_block;
160
161 let mut indices = Vec::with_capacity(num_index_blocks as usize);
163 for i in 0..num_index_blocks as usize {
164 let first_leaf_in_block = i * leaves_per_block;
165 let logical_block = all_leaves[first_leaf_in_block].block;
166 indices.push(ExtentIndex {
167 block: logical_block,
168 leaf_lo: index_block_start + i as u32,
169 leaf_hi: 0,
170 unused: 0,
171 });
172 }
173
174 let mut buf = [0u8; INODE_BLOCK_SIZE];
176 let header = ExtentHeader {
177 magic: EXTENT_HEADER_MAGIC,
178 entries: num_index_blocks as u16,
179 max: 4,
180 depth: 1,
181 generation: 0,
182 };
183 header.write_to(&mut buf[..ExtentHeader::SIZE]);
184
185 for (i, idx) in indices.iter().enumerate() {
186 let off = ExtentHeader::SIZE + i * ExtentIndex::SIZE;
187 idx.write_to(&mut buf[off..off + ExtentIndex::SIZE]);
188 }
189
190 inode.block = buf;
191
192 for i in 0..num_index_blocks as usize {
194 let leaf_start = i * leaves_per_block;
195 let leaf_end = ((i + 1) * leaves_per_block).min(all_leaves.len());
196 let block_leaves = &all_leaves[leaf_start..leaf_end];
197
198 let mut block_buf = vec![0u8; block_size as usize];
199
200 let leaf_header = ExtentHeader {
202 magic: EXTENT_HEADER_MAGIC,
203 entries: block_leaves.len() as u16,
204 max: leaves_per_block as u16,
205 depth: 0,
206 generation: 0,
207 };
208 leaf_header.write_to(&mut block_buf[..ExtentHeader::SIZE]);
209
210 for (j, leaf) in block_leaves.iter().enumerate() {
212 let off = ExtentHeader::SIZE + j * ExtentLeaf::SIZE;
213 leaf.write_to(&mut block_buf[off..off + ExtentLeaf::SIZE]);
214 }
215
216 let tail = ExtentTail { checksum: 0 };
218 let tail_off = block_size as usize - ExtentTail::SIZE;
219 tail.write_to(&mut block_buf[tail_off..tail_off + ExtentTail::SIZE]);
220
221 let byte_offset = (*current_block as u64) * (block_size as u64);
223 writer.seek(SeekFrom::Start(byte_offset))?;
224 writer.write_all(&block_buf)?;
225
226 *current_block += 1;
227 }
228
229 let total_blocks = data_blocks + num_index_blocks;
231 if inode.flags & inode_flags::HUGE_FILE != 0 {
232 inode.blocks_lo = total_blocks;
233 } else {
234 let sectors = (total_blocks as u64) * (block_size as u64 / 512);
235 inode.blocks_lo = sectors as u32;
236 }
237 inode.flags |= inode_flags::EXTENTS;
238
239 Ok(())
240}
241
242pub fn parse_extents<R: Read + Seek>(
252 inode: &Inode,
253 block_size: u64,
254 reader: &mut R,
255) -> Result<Vec<(u32, u32)>, crate::error::ReadError> {
256 let header = ExtentHeader::read_from(&inode.block);
257
258 if header.magic != EXTENT_HEADER_MAGIC {
259 return Ok(Vec::new());
262 }
263
264 match header.depth {
265 0 => parse_depth0(&inode.block, &header),
266 1 => parse_depth1(&inode.block, &header, block_size, reader),
267 _ => Err(crate::error::ReadError::DeepExtentsUnsupported),
268 }
269}
270
271fn parse_depth0(
273 block_field: &[u8],
274 header: &ExtentHeader,
275) -> Result<Vec<(u32, u32)>, crate::error::ReadError> {
276 let mut ranges = Vec::with_capacity(header.entries as usize);
277
278 for i in 0..header.entries as usize {
279 let off = ExtentHeader::SIZE + i * ExtentLeaf::SIZE;
280 if off + ExtentLeaf::SIZE > block_field.len() {
281 return Err(crate::error::ReadError::InvalidExtents);
282 }
283 let leaf = ExtentLeaf::read_from(&block_field[off..]);
284 let phys_start = leaf.start_lo;
285 let phys_end = phys_start + leaf.len as u32;
286 ranges.push((phys_start, phys_end));
287 }
288
289 Ok(ranges)
290}
291
292fn parse_depth1<R: Read + Seek>(
295 block_field: &[u8],
296 header: &ExtentHeader,
297 block_size: u64,
298 reader: &mut R,
299) -> Result<Vec<(u32, u32)>, crate::error::ReadError> {
300 let mut ranges = Vec::new();
301
302 for i in 0..header.entries as usize {
303 let off = ExtentHeader::SIZE + i * ExtentIndex::SIZE;
304 if off + ExtentIndex::SIZE > block_field.len() {
305 return Err(crate::error::ReadError::InvalidExtents);
306 }
307 let index = ExtentIndex::read_from(&block_field[off..]);
308
309 let phys_block = index.leaf() as u64;
311 let byte_offset = phys_block * block_size;
312
313 reader.seek(SeekFrom::Start(byte_offset))
314 .map_err(|_| crate::error::ReadError::CouldNotReadBlock(phys_block as u32))?;
315
316 let mut leaf_buf = vec![0u8; block_size as usize];
317 reader.read_exact(&mut leaf_buf)
318 .map_err(|_| crate::error::ReadError::CouldNotReadBlock(phys_block as u32))?;
319
320 let leaf_header = ExtentHeader::read_from(&leaf_buf);
321 if leaf_header.magic != EXTENT_HEADER_MAGIC || leaf_header.depth != 0 {
322 return Err(crate::error::ReadError::InvalidExtents);
323 }
324
325 for j in 0..leaf_header.entries as usize {
326 let leaf_off = ExtentHeader::SIZE + j * ExtentLeaf::SIZE;
327 if leaf_off + ExtentLeaf::SIZE > leaf_buf.len() {
328 return Err(crate::error::ReadError::InvalidExtents);
329 }
330 let leaf = ExtentLeaf::read_from(&leaf_buf[leaf_off..]);
331 let phys_start = leaf.start_lo;
332 let phys_end = phys_start + leaf.len as u32;
333 ranges.push((phys_start, phys_end));
334 }
335 }
336
337 Ok(ranges)
338}
339
340#[cfg(test)]
341mod tests {
342 use super::*;
343 use std::io::Cursor;
344
345 #[test]
346 fn test_fill_extent_leaves_single() {
347 let leaves = fill_extent_leaves(100, 10, 1, 0);
348 assert_eq!(leaves.len(), 1);
349 assert_eq!(leaves[0].block, 0);
350 assert_eq!(leaves[0].start_lo, 100);
351 assert_eq!(leaves[0].len, 10);
352 }
353
354 #[test]
355 fn test_fill_extent_leaves_multiple() {
356 let num_blocks = MAX_BLOCKS_PER_EXTENT * 2 + 5;
358 let leaves = fill_extent_leaves(0, num_blocks, 3, 0);
359 assert_eq!(leaves.len(), 3);
360 assert_eq!(leaves[0].len, MAX_BLOCKS_PER_EXTENT as u16);
361 assert_eq!(leaves[1].len, MAX_BLOCKS_PER_EXTENT as u16);
362 assert_eq!(leaves[2].len, 5);
363 assert_eq!(leaves[1].start_lo, MAX_BLOCKS_PER_EXTENT);
364 assert_eq!(leaves[2].start_lo, MAX_BLOCKS_PER_EXTENT * 2);
365 }
366
367 #[test]
368 fn test_inline_extents_roundtrip() {
369 let block_size = 4096u32;
370 let mut inode = Inode::default();
371 let blocks = BlockRange { start: 50, end: 60 };
372
373 let mut cursor = Cursor::new(Vec::new());
374 let mut current_block = 100u32;
375
376 write_extents(&mut inode, blocks, block_size, &mut cursor, &mut current_block).unwrap();
377
378 assert_eq!(cursor.get_ref().len(), 0);
380
381 let ranges = parse_extents(&inode, block_size as u64, &mut cursor).unwrap();
383 assert_eq!(ranges.len(), 1);
384 assert_eq!(ranges[0], (50, 60));
385 }
386
387 #[test]
388 fn test_zero_blocks_is_noop() {
389 let mut inode = Inode::default();
390 let blocks = BlockRange { start: 0, end: 0 };
391 let mut cursor = Cursor::new(Vec::new());
392 let mut current_block = 0u32;
393
394 write_extents(&mut inode, blocks, 4096, &mut cursor, &mut current_block).unwrap();
395 assert_eq!(inode.flags & inode_flags::EXTENTS, 0);
396 }
397
398 #[test]
402 fn test_depth1_extent_tree_roundtrip() {
403 let block_size = 4096u32;
404 let data_blocks = MAX_BLOCKS_PER_EXTENT * 5;
406 let phys_start = 200u32;
407
408 let mut inode = Inode::default();
409 let blocks = BlockRange {
410 start: phys_start,
411 end: phys_start + data_blocks,
412 };
413
414 let backing = vec![0u8; 1024 * 1024];
418 let mut cursor = Cursor::new(backing);
419 let mut current_block = phys_start + data_blocks + 10;
422
423 write_extents(
424 &mut inode,
425 blocks,
426 block_size,
427 &mut cursor,
428 &mut current_block,
429 )
430 .unwrap();
431
432 assert_ne!(inode.flags & inode_flags::EXTENTS, 0);
434
435 let header = ExtentHeader::read_from(&inode.block);
437 assert_eq!(header.magic, EXTENT_HEADER_MAGIC);
438 assert_eq!(header.depth, 1);
439 assert!(header.entries >= 1);
441
442 let ranges = parse_extents(&inode, block_size as u64, &mut cursor).unwrap();
444 assert_eq!(ranges.len(), 5);
445
446 let mut expected_phys = phys_start;
447 for (i, &(start, end)) in ranges.iter().enumerate() {
448 assert_eq!(
449 start, expected_phys,
450 "extent {} start mismatch: expected {} got {}",
451 i, expected_phys, start
452 );
453 assert_eq!(
454 end - start,
455 MAX_BLOCKS_PER_EXTENT,
456 "extent {} length mismatch",
457 i
458 );
459 expected_phys += MAX_BLOCKS_PER_EXTENT;
460 }
461 }
462
463 #[test]
465 fn test_depth1_extent_tree_uneven() {
466 let block_size = 4096u32;
467 let extra = 42u32;
468 let data_blocks = MAX_BLOCKS_PER_EXTENT * 5 + extra;
469 let phys_start = 100u32;
470
471 let mut inode = Inode::default();
472 let blocks = BlockRange {
473 start: phys_start,
474 end: phys_start + data_blocks,
475 };
476
477 let backing = vec![0u8; 1024 * 1024];
478 let mut cursor = Cursor::new(backing);
479 let mut current_block = phys_start + data_blocks + 10;
480
481 write_extents(
482 &mut inode,
483 blocks,
484 block_size,
485 &mut cursor,
486 &mut current_block,
487 )
488 .unwrap();
489
490 let ranges = parse_extents(&inode, block_size as u64, &mut cursor).unwrap();
491 assert_eq!(ranges.len(), 6);
492
493 for i in 0..5 {
495 assert_eq!(
496 ranges[i].1 - ranges[i].0,
497 MAX_BLOCKS_PER_EXTENT,
498 "extent {} should be full",
499 i
500 );
501 }
502 assert_eq!(ranges[5].1 - ranges[5].0, extra);
504
505 let mut expected_phys = phys_start;
507 for &(start, end) in &ranges {
508 assert_eq!(start, expected_phys);
509 expected_phys = end;
510 }
511 }
512}