@@ -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
167172fn 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
172181fn 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
176189impl 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) ]
408440mod 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