Skip to content

Commit c12ee57

Browse files
committed
partitioning: Correctly enforce 1mib alignment and constraints
We ensure that proper 1mib alignment is place and prevent clobbering the first_usable_lba and last_usable_lba outsets. Signed-off-by: Ikey Doherty <[email protected]>
1 parent e91cb35 commit c12ee57

File tree

1 file changed

+83
-7
lines changed

1 file changed

+83
-7
lines changed

crates/partitioning/src/planner.rs

Lines changed: 83 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -163,14 +163,27 @@ pub fn format_position(pos: u64, total: u64) -> String {
163163
format!("{}% ({})", (pos as f64 / total as f64 * 100.0) as u64, format_size(pos))
164164
}
165165

166-
// Align up to the nearest multiple of alignment
166+
/// Check if a value is already aligned to the given boundary
167+
fn is_aligned(value: u64, alignment: u64) -> bool {
168+
value % alignment == 0
169+
}
170+
171+
/// Align up to the nearest multiple of alignment, unless already aligned
167172
fn align_up(value: u64, alignment: u64) -> u64 {
168-
value.div_ceil(alignment) * alignment
173+
match value % alignment {
174+
0 => value,
175+
remainder if remainder > (alignment / 2) => value + (alignment - remainder),
176+
remainder => value - remainder,
177+
}
169178
}
170179

171-
// Align down to the nearest multiple of alignment
180+
/// Align down to the nearest multiple of alignment, unless already aligned
172181
fn align_down(value: u64, alignment: u64) -> u64 {
173-
(value / alignment) * alignment
182+
match value % alignment {
183+
0 => value,
184+
remainder if remainder < (alignment / 2) => value - remainder,
185+
remainder => value + (alignment - remainder),
186+
}
174187
}
175188

176189
impl Change {
@@ -289,12 +302,30 @@ impl Planner {
289302
///
290303
pub fn plan_add_partition(&mut self, start: u64, end: u64) -> Result<(), PlanError> {
291304
debug!("Planning to add partition {}..{}", start, end);
305+
debug!("Original size requested: {}", end - start);
306+
307+
// Align start and end positions, capping to usable bounds
308+
let aligned_start = std::cmp::max(align_up(start, PARTITION_ALIGNMENT), self.usable_start);
309+
let aligned_end = std::cmp::min(align_down(end, PARTITION_ALIGNMENT), self.usable_end);
292310

293-
// Align start and end positions
294-
let aligned_start = align_up(start, PARTITION_ALIGNMENT);
295-
let aligned_end = align_down(end, PARTITION_ALIGNMENT);
296311
debug!("Aligned positions: {}..{}", aligned_start, aligned_end);
312+
debug!("Size after alignment: {}", aligned_end - aligned_start);
297313

314+
// Validate input alignments
315+
if is_aligned(start, PARTITION_ALIGNMENT) && aligned_start != start {
316+
warn!("Start position was already aligned but was re-aligned differently");
317+
return Err(PlanError::RegionOutOfBounds {
318+
start: aligned_start,
319+
end: aligned_end,
320+
});
321+
}
322+
if is_aligned(end, PARTITION_ALIGNMENT) && aligned_end != end {
323+
warn!("End position was already aligned but was re-aligned differently");
324+
return Err(PlanError::RegionOutOfBounds {
325+
start: aligned_start,
326+
end: aligned_end,
327+
});
328+
}
298329
// Validate bounds against usable disk region
299330
if aligned_start < self.usable_start || aligned_end > self.usable_end {
300331
warn!("Partition would be outside usable disk region");
@@ -404,6 +435,7 @@ impl Planner {
404435
Ok(())
405436
}
406437
}
438+
407439
#[cfg(test)]
408440
mod tests {
409441
use super::*;
@@ -573,4 +605,48 @@ mod tests {
573605
Err(PlanError::RegionOverlap { .. })
574606
));
575607
}
608+
609+
#[test]
610+
fn test_alignment() {
611+
let disk = create_mock_disk();
612+
let mut planner = Planner::new(BlockDevice::mock_device(disk));
613+
614+
// Already aligned values should not be re-aligned
615+
let aligned_start = PARTITION_ALIGNMENT;
616+
let aligned_end = 2 * PARTITION_ALIGNMENT;
617+
assert!(planner.plan_add_partition(aligned_start, aligned_end).is_ok());
618+
619+
// Test that non-aligned values get properly aligned
620+
let unaligned_start = (2 * PARTITION_ALIGNMENT) + 100;
621+
let unaligned_end = (3 * PARTITION_ALIGNMENT) - 100;
622+
assert!(planner.plan_add_partition(unaligned_start, unaligned_end).is_ok());
623+
624+
let layout = planner.current_layout();
625+
assert_eq!(layout[0].start, aligned_start);
626+
assert_eq!(layout[0].end, aligned_end);
627+
628+
assert_eq!(layout[1].start, 2 * PARTITION_ALIGNMENT); // Aligned up
629+
assert_eq!(layout[1].end, 3 * PARTITION_ALIGNMENT); // Aligned down
630+
}
631+
632+
#[test]
633+
fn test_alignment_functions() {
634+
let mb = 1024 * 1024;
635+
let kb = 1024;
636+
637+
// Test align_up
638+
assert_eq!(align_up(2 * mb + 100, mb), 2 * mb);
639+
assert_eq!(align_up(2 * mb, mb), 2 * mb); // Already aligned
640+
641+
// Test align_up past boundary
642+
assert_eq!(align_up(2 * mb + (600 * kb), mb), 3 * mb);
643+
644+
// Test align_down
645+
assert_eq!(align_down(4 * mb - 100, mb), 4 * mb);
646+
assert_eq!(align_down(4 * mb, mb), 4 * mb); // Already aligned
647+
648+
// Test align_down past boundary
649+
650+
assert_eq!(align_down(4 * mb + (600 * kb), mb), 5 * mb);
651+
}
576652
}

0 commit comments

Comments
 (0)