1use crate::error::{Error, Result};
20use crate::extent::{Extent, ExtentHeader};
21use crate::extent_mut::{plan_free_extent, ExtentMutation};
22
23pub fn plan_truncate_shrink_deep(
29 old_size: u64,
30 new_size: u64,
31 root: &[u8],
32 block_size: u32,
33 blocks_count: u64,
34 read: &mut impl FnMut(u64) -> Result<Vec<u8>>,
35) -> Result<(SizeChange, Vec<ExtentMutation>)> {
36 if new_size > old_size || !(1024..=65536).contains(&block_size) || !block_size.is_power_of_two()
37 {
38 return Err(Error::InvalidArgument(
39 "invalid truncate size or block size",
40 ));
41 }
42 let header = ExtentHeader::parse(root)?;
43 if root.len() != 60 || header.depth > crate::extent::EXT4_EXT_MAX_DEPTH {
44 return Err(Error::CorruptExtentTree("invalid inline extent root"));
45 }
46 let mut walk = ShrinkWalk {
47 end: new_size.div_ceil(block_size as u64),
48 block_size,
49 blocks_count,
50 read,
51 visited: std::collections::BTreeSet::new(),
52 physical: Vec::new(),
53 logical_end: 0,
54 mutations: Vec::new(),
55 };
56 let (mut bytes, first) = walk.node(root, header.depth, true)?;
57 if first.is_none() {
58 bytes[6..8].copy_from_slice(&0u16.to_le_bytes());
59 }
60 walk.physical.sort_unstable();
63 for pair in walk.physical.windows(2) {
64 if pair[0].1 > pair[1].0 {
65 return Err(Error::CorruptExtentTree(
66 "overlapping physical extent ranges",
67 ));
68 }
69 }
70 walk.mutations
71 .insert(0, ExtentMutation::WriteRoot { bytes });
72 Ok((SizeChange { old_size, new_size }, walk.mutations))
73}
74
75struct ShrinkWalk<'a, R> {
76 end: u64,
77 block_size: u32,
78 blocks_count: u64,
79 read: &'a mut R,
80 visited: std::collections::BTreeSet<u64>,
81 physical: Vec<(u64, u64)>,
82 logical_end: u64,
83 mutations: Vec<ExtentMutation>,
84}
85
86impl<R: FnMut(u64) -> Result<Vec<u8>>> ShrinkWalk<'_, R> {
87 fn node(
88 &mut self,
89 original: &[u8],
90 depth: u16,
91 inline: bool,
92 ) -> Result<(Vec<u8>, Option<u32>)> {
93 use crate::extent::{ExtentIdx, EXT4_EXT_NODE_SIZE as N};
94 let header = ExtentHeader::parse(original)?;
95 let body = original.len().saturating_sub(if inline { 0 } else { 4 });
96 if header.depth != depth || header.max == 0 || N * (1 + header.max as usize) > body {
97 return Err(Error::CorruptExtentTree(
98 "invalid extent node depth or capacity",
99 ));
100 }
101 let mut bytes = original.to_vec();
102 let mut kept = 0usize;
104 let mut first = None;
105 let mut previous_index = None;
106 for index in 0..header.entries as usize {
107 let off = N * (index + 1);
108 let mut encoded: [u8; N] = original[off..off + N].try_into().unwrap();
109 if depth == 0 {
110 let extent = Extent::parse(&encoded)?;
111 let begin = extent.logical_block as u64;
112 let end = begin + extent.length as u64;
113 let physical_end = extent.physical_block + extent.length as u64;
114 if extent.length == 0
115 || begin < self.logical_end
116 || end > u32::MAX as u64 + 1
117 || extent.physical_block == 0
118 || physical_end > self.blocks_count
119 {
120 return Err(Error::CorruptExtentTree(
121 "invalid or overlapping leaf extent",
122 ));
123 }
124 self.logical_end = end;
125 self.physical.push((extent.physical_block, physical_end));
126 let keep = self.end.saturating_sub(begin).min(extent.length as u64);
127 if keep < extent.length as u64 {
128 self.mutations.push(ExtentMutation::FreePhysicalRun {
129 start: extent.physical_block + keep,
130 len: extent.length as u32 - keep as u32,
131 });
132 }
133 if keep == 0 {
134 continue;
135 }
136 let encoded_len = keep as u16
137 + if extent.uninitialized {
138 crate::extent::EXT_INIT_MAX_LEN
139 } else {
140 0
141 };
142 encoded[4..6].copy_from_slice(&encoded_len.to_le_bytes());
143 first.get_or_insert(extent.logical_block);
144 } else {
145 let child = ExtentIdx::parse(&encoded)?;
146 if previous_index.is_some_and(|prior| prior >= child.logical_block)
147 || child.leaf_block == 0
148 || child.leaf_block >= self.blocks_count
149 || !self.visited.insert(child.leaf_block)
150 {
151 return Err(Error::CorruptExtentTree("invalid or repeated extent index"));
152 }
153 previous_index = Some(child.logical_block);
154 self.physical.push((child.leaf_block, child.leaf_block + 1));
155 let node = (self.read)(child.leaf_block)?;
156 if node.len() != self.block_size as usize {
157 return Err(Error::CorruptExtentTree("short extent node read"));
158 }
159 let child_header = ExtentHeader::parse(&node)?;
160 if child_header.entries == 0
161 || u32::from_le_bytes(node[12..16].try_into().unwrap()) != child.logical_block
162 {
163 return Err(Error::CorruptExtentTree(
164 "extent index does not match its child",
165 ));
166 }
167 let (updated, child_first) = self.node(&node, depth - 1, false)?;
168 let Some(child_first) = child_first else {
169 self.mutations.push(ExtentMutation::FreePhysicalRun {
170 start: child.leaf_block,
171 len: 1,
172 });
173 continue;
174 };
175 encoded[..4].copy_from_slice(&child_first.to_le_bytes());
176 if updated != node {
177 self.mutations.push(ExtentMutation::WriteTreeBlock {
178 block: child.leaf_block,
179 bytes: updated,
180 });
181 }
182 first.get_or_insert(child_first);
183 }
184 bytes[N * (kept + 1)..N * (kept + 2)].copy_from_slice(&encoded);
185 kept += 1;
186 }
187 bytes[2..4].copy_from_slice(&(kept as u16).to_le_bytes());
188 Ok((bytes, first))
189 }
190}
191
192#[derive(Debug, Clone, PartialEq, Eq)]
194pub struct BlockWrite {
195 pub logical_block: u64,
197 pub offset_in_block: u32,
199 pub payload: Vec<u8>,
202}
203
204pub fn split_into_block_writes(offset: u64, data: &[u8], block_size: u32) -> Vec<BlockWrite> {
211 if data.is_empty() {
212 return Vec::new();
213 }
214 let bs = block_size as u64;
215 let mut out = Vec::new();
216 let mut cur = offset;
217 let end = offset + data.len() as u64;
218 let mut src = 0usize;
219
220 while cur < end {
221 let logical_block = cur / bs;
222 let off_in_block = (cur % bs) as u32;
223 let block_remaining = (bs - off_in_block as u64) as usize;
224 let take = block_remaining.min(end.saturating_sub(cur) as usize);
225 out.push(BlockWrite {
226 logical_block,
227 offset_in_block: off_in_block,
228 payload: data[src..src + take].to_vec(),
229 });
230 src += take;
231 cur += take as u64;
232 }
233 out
234}
235
236#[derive(Debug, Clone, Copy, PartialEq, Eq)]
238pub struct WriteBlockRange {
239 pub first: u64,
241 pub last: u64,
243 pub full_blocks: bool,
246}
247
248pub fn compute_write_range(offset: u64, length: u64, block_size: u32) -> Option<WriteBlockRange> {
250 if length == 0 {
251 return None;
252 }
253 let bs = block_size as u64;
254 let first = offset / bs;
255 let last = (offset + length - 1) / bs;
256 let full_blocks = offset.is_multiple_of(bs) && (offset + length).is_multiple_of(bs);
257 Some(WriteBlockRange {
258 first,
259 last,
260 full_blocks,
261 })
262}
263
264#[derive(Debug, Clone, Copy, PartialEq, Eq)]
268pub struct SizeChange {
269 pub old_size: u64,
270 pub new_size: u64,
271}
272
273pub fn plan_truncate_grow(old_size: u64, new_size: u64) -> Result<SizeChange> {
274 if new_size < old_size {
275 return Err(Error::Corrupt("plan_truncate_grow: new_size < old_size"));
276 }
277 Ok(SizeChange { old_size, new_size })
278}
279
280pub fn plan_truncate_shrink(
294 old_size: u64,
295 new_size: u64,
296 root_bytes: &[u8],
297 block_size: u32,
298) -> Result<(SizeChange, Vec<ExtentMutation>)> {
299 if new_size > old_size {
300 return Err(Error::Corrupt("plan_truncate_shrink: new_size > old_size"));
301 }
302 let header = ExtentHeader::parse(root_bytes)?;
303 if !header.is_leaf() {
304 return Err(Error::Unsupported(
305 "inline truncate planner requires an inline extent root",
306 ));
307 }
308
309 let bs = block_size as u64;
310 let new_logical_end = new_size.div_ceil(bs); let mut entries: Vec<Extent> = Vec::new();
314 for i in 0..header.entries {
315 let off = crate::extent::EXT4_EXT_NODE_SIZE * (1 + i as usize);
316 entries.push(Extent::parse(
317 &root_bytes[off..off + crate::extent::EXT4_EXT_NODE_SIZE],
318 )?);
319 }
320
321 let mut muts: Vec<ExtentMutation> = Vec::new();
322 let mut kept: Vec<Extent> = Vec::new();
328 for e in entries.into_iter() {
329 let e_start = e.logical_block as u64;
330 let e_end = e_start + e.length as u64;
331 if e_end <= new_logical_end {
332 kept.push(e);
334 } else if e_start >= new_logical_end {
335 muts.push(ExtentMutation::FreePhysicalRun {
337 start: e.physical_block,
338 len: e.length as u32,
339 });
340 } else {
341 let keep_len = (new_logical_end - e_start) as u16;
343 let free_len = e.length - keep_len;
344 let free_phys = e.physical_block + keep_len as u64;
345 kept.push(Extent {
346 logical_block: e.logical_block,
347 length: keep_len,
348 physical_block: e.physical_block,
349 uninitialized: e.uninitialized,
350 });
351 muts.push(ExtentMutation::FreePhysicalRun {
352 start: free_phys,
353 len: free_len as u32,
354 });
355 }
356 }
357
358 let mut new_root = vec![0u8; root_bytes.len()];
360 new_root[0..2].copy_from_slice(&crate::extent::EXT4_EXT_MAGIC.to_le_bytes());
361 new_root[2..4].copy_from_slice(&(kept.len() as u16).to_le_bytes());
362 new_root[4..6].copy_from_slice(&header.max.to_le_bytes());
363 new_root[6..8].copy_from_slice(&header.depth.to_le_bytes());
364 new_root[8..12].copy_from_slice(&header.generation.to_le_bytes());
365 for (i, e) in kept.iter().enumerate() {
366 let off = crate::extent::EXT4_EXT_NODE_SIZE * (1 + i);
367 new_root[off..off + 4].copy_from_slice(&e.logical_block.to_le_bytes());
368 let ee_len = if e.uninitialized {
369 e.length + crate::extent::EXT_INIT_MAX_LEN
370 } else {
371 e.length
372 };
373 new_root[off + 4..off + 6].copy_from_slice(&ee_len.to_le_bytes());
374 let phys_hi = ((e.physical_block >> 32) & 0xFFFF) as u16;
375 let phys_lo = (e.physical_block & 0xFFFF_FFFF) as u32;
376 new_root[off + 6..off + 8].copy_from_slice(&phys_hi.to_le_bytes());
377 new_root[off + 8..off + 12].copy_from_slice(&phys_lo.to_le_bytes());
378 }
379 muts.insert(0, ExtentMutation::WriteRoot { bytes: new_root });
380
381 let _ = plan_free_extent;
384
385 Ok((SizeChange { old_size, new_size }, muts))
386}
387
388#[cfg(test)]
389mod tests {
390 use super::*;
391
392 #[test]
397 fn split_aligned_single_block() {
398 let data = vec![7u8; 4096];
399 let out = split_into_block_writes(0, &data, 4096);
400 assert_eq!(out.len(), 1);
401 assert_eq!(out[0].logical_block, 0);
402 assert_eq!(out[0].offset_in_block, 0);
403 assert_eq!(out[0].payload.len(), 4096);
404 }
405
406 #[test]
407 fn split_crosses_block_boundary() {
408 let data = vec![1u8; 6000]; let out = split_into_block_writes(3000, &data, 4096);
410 assert_eq!(out.len(), 3);
411 assert_eq!(out[0].logical_block, 0);
413 assert_eq!(out[0].offset_in_block, 3000);
414 assert_eq!(out[0].payload.len(), 1096);
415 assert_eq!(out[1].logical_block, 1);
417 assert_eq!(out[1].offset_in_block, 0);
418 assert_eq!(out[1].payload.len(), 4096);
419 assert_eq!(out[2].logical_block, 2);
421 assert_eq!(out[2].offset_in_block, 0);
422 assert_eq!(out[2].payload.len(), 808);
423 }
424
425 #[test]
426 fn split_empty_data_returns_empty() {
427 assert!(split_into_block_writes(100, &[], 4096).is_empty());
428 }
429
430 #[test]
431 fn split_sub_block_write() {
432 let data = vec![2u8; 100];
433 let out = split_into_block_writes(1000, &data, 4096);
434 assert_eq!(out.len(), 1);
435 assert_eq!(out[0].offset_in_block, 1000);
436 assert_eq!(out[0].payload.len(), 100);
437 }
438
439 #[test]
444 fn write_range_full_blocks() {
445 let r = compute_write_range(0, 8192, 4096).unwrap();
446 assert_eq!(r.first, 0);
447 assert_eq!(r.last, 1);
448 assert!(r.full_blocks);
449 }
450
451 #[test]
452 fn write_range_partial_head_and_tail() {
453 let r = compute_write_range(100, 5000, 4096).unwrap();
454 assert_eq!(r.first, 0);
455 assert_eq!(r.last, 1);
456 assert!(!r.full_blocks);
457 }
458
459 #[test]
460 fn write_range_zero_length_returns_none() {
461 assert!(compute_write_range(100, 0, 4096).is_none());
462 }
463
464 #[test]
469 fn grow_returns_delta() {
470 let s = plan_truncate_grow(1000, 5000).unwrap();
471 assert_eq!(s.old_size, 1000);
472 assert_eq!(s.new_size, 5000);
473 }
474
475 #[test]
476 fn grow_rejects_shrink() {
477 assert!(plan_truncate_grow(5000, 1000).is_err());
478 }
479
480 fn mk_root(extents: &[Extent]) -> Vec<u8> {
486 let mut buf = vec![0u8; 60];
487 buf[0..2].copy_from_slice(&crate::extent::EXT4_EXT_MAGIC.to_le_bytes());
488 buf[2..4].copy_from_slice(&(extents.len() as u16).to_le_bytes());
489 buf[4..6].copy_from_slice(&4u16.to_le_bytes());
490 buf[6..8].copy_from_slice(&0u16.to_le_bytes());
491 for (i, e) in extents.iter().enumerate() {
492 let off = crate::extent::EXT4_EXT_NODE_SIZE * (1 + i);
493 buf[off..off + 4].copy_from_slice(&e.logical_block.to_le_bytes());
494 let ee_len = if e.uninitialized {
495 e.length + crate::extent::EXT_INIT_MAX_LEN
496 } else {
497 e.length
498 };
499 buf[off + 4..off + 6].copy_from_slice(&ee_len.to_le_bytes());
500 let phys_hi = ((e.physical_block >> 32) & 0xFFFF) as u16;
501 let phys_lo = (e.physical_block & 0xFFFF_FFFF) as u32;
502 buf[off + 6..off + 8].copy_from_slice(&phys_hi.to_le_bytes());
503 buf[off + 8..off + 12].copy_from_slice(&phys_lo.to_le_bytes());
504 }
505 buf
506 }
507
508 fn ext(log: u32, len: u16, phys: u64) -> Extent {
509 Extent {
510 logical_block: log,
511 length: len,
512 physical_block: phys,
513 uninitialized: false,
514 }
515 }
516
517 #[test]
518 fn shrink_drops_whole_tail_extent() {
519 let root = mk_root(&[ext(0, 10, 1000), ext(10, 10, 2000)]);
522 let (sc, muts) = plan_truncate_shrink(20 * 4096, 10 * 4096, &root, 4096).unwrap();
523 assert_eq!(sc.new_size, 10 * 4096);
524 assert_eq!(muts.len(), 2);
527 match &muts[1] {
528 ExtentMutation::FreePhysicalRun { start, len } => {
529 assert_eq!(*start, 2000);
530 assert_eq!(*len, 10);
531 }
532 _ => panic!("expected FreePhysicalRun"),
533 }
534 }
535
536 #[test]
537 fn shrink_splits_straddling_extent() {
538 let root = mk_root(&[ext(0, 10, 1000)]);
540 let (_, muts) = plan_truncate_shrink(10 * 4096, 4 * 4096, &root, 4096).unwrap();
541 assert_eq!(muts.len(), 2);
542 match &muts[1] {
543 ExtentMutation::FreePhysicalRun { start, len } => {
544 assert_eq!(*start, 1004);
545 assert_eq!(*len, 6);
546 }
547 _ => panic!("expected FreePhysicalRun for tail"),
548 }
549 }
550
551 #[test]
552 fn shrink_to_zero_frees_all() {
553 let root = mk_root(&[ext(0, 5, 500), ext(5, 5, 1000)]);
554 let (_, muts) = plan_truncate_shrink(10 * 4096, 0, &root, 4096).unwrap();
555 assert_eq!(muts.len(), 3);
557 let freed: Vec<_> = muts
558 .iter()
559 .filter_map(|m| match m {
560 ExtentMutation::FreePhysicalRun { start, len } => Some((*start, *len)),
561 _ => None,
562 })
563 .collect();
564 assert_eq!(freed, vec![(500, 5), (1000, 5)]);
565 }
566
567 #[test]
568 fn shrink_rejects_grow_direction() {
569 let root = mk_root(&[ext(0, 10, 1000)]);
570 assert!(plan_truncate_shrink(4096, 8192, &root, 4096).is_err());
571 }
572}