From e62a23b5b5f5dffff0bf83b0584bf33d0cc8e81e Mon Sep 17 00:00:00 2001 From: Curtis Gedak Date: Thu, 20 May 2010 10:00:14 -0600 Subject: [PATCH] Add partition alignment option to align to MiB (#617409) Make align to MiB the default setting instead of align to cylinder. Migrate logic for alignment to cylinder into its own method snap_to_cylinder, and place common logic in snap_to_alignment. Add alignment checks for situations where space is needed for Master Boot Record or Extended Boot Record. Adjust ranges on spin buttons according to required boot record space. Copy fix for off by one sector (#596552) from Dialog_Partition_New::Get_New_Partition to Dialog_Base_Partition::Get_New_Partition Enhance resize / move logic for checking locations of nearby logical partitions to not depend on the partition ordering. Note: This commit does not include limiting graphic movement according to required boot record space. --- include/Dialog_Base_Partition.h | 7 +- include/GParted_Core.h | 2 + include/Partition.h | 4 +- src/Dialog_Base_Partition.cc | 47 ++++-- src/Dialog_Partition_Copy.cc | 74 +++------ src/Dialog_Partition_New.cc | 50 ++++--- src/Dialog_Partition_Resize_Move.cc | 102 +++++++------ src/GParted_Core.cc | 224 ++++++++++++++++++++-------- src/Partition.cc | 1 + src/Win_GParted.cc | 74 ++++----- 10 files changed, 347 insertions(+), 238 deletions(-) diff --git a/include/Dialog_Base_Partition.h b/include/Dialog_Base_Partition.h index d1b41310..58d94397 100644 --- a/include/Dialog_Base_Partition.h +++ b/include/Dialog_Base_Partition.h @@ -75,7 +75,12 @@ protected: //used to enable/disable OKbutton... int ORIG_BEFORE, ORIG_SIZE, ORIG_AFTER ; - + + //used to reserve space for Master or Extended Boot Record (1 MiB) + int MIN_SPACE_BEFORE_MB ; + + int MB_Needed_for_Boot_Record( const Partition & partition ) ; + //signal handlers void on_signal_move( int, int ); void on_signal_resize( int, int, Frame_Resizer_Base::ArrowType ); diff --git a/include/GParted_Core.h b/include/GParted_Core.h index ddcfa5cb..52020552 100644 --- a/include/GParted_Core.h +++ b/include/GParted_Core.h @@ -40,6 +40,8 @@ public: void set_devices( std::vector & devices ) ; bool snap_to_cylinder( const Device & device, Partition & partition, Glib::ustring & error ) ; + bool snap_to_mebibyte( const Device & device, Partition & partition, Glib::ustring & error ) ; + bool snap_to_alignment( const Device & device, Partition & partition, Glib::ustring & error ) ; bool apply_operation_to_disk( Operation * operation ); bool set_disklabel( const Glib::ustring & device_path, const Glib::ustring & disklabel ) ; diff --git a/include/Partition.h b/include/Partition.h index a014b720..c7eaed38 100644 --- a/include/Partition.h +++ b/include/Partition.h @@ -46,7 +46,8 @@ enum PartitionStatus { enum PartitionAlignment { ALIGN_CYLINDER = 0, //Align to nearest cylinder - ALIGN_STRICT = 1 //Strict alignment - no rounding + ALIGN_MEBIBYTE = 1, //Align to nearest mebibyte + ALIGN_STRICT = 2 //Strict alignment - no rounding // Indicator if start and end sectors must remain unchanged }; @@ -124,6 +125,7 @@ public: std::vector logicals ; bool strict_start ; //Indicator if start sector must stay unchanged + Sector free_space_before ; //Free space preceding partition value Byte_Value sector_size ; //Sector size of the disk device needed for converting to/from sectors and bytes. diff --git a/src/Dialog_Base_Partition.cc b/src/Dialog_Base_Partition.cc index c6d279cb..7c48557f 100644 --- a/src/Dialog_Base_Partition.cc +++ b/src/Dialog_Base_Partition.cc @@ -24,13 +24,12 @@ namespace GParted Dialog_Base_Partition::Dialog_Base_Partition() { this ->set_has_separator( false ) ; -//FIXME: somehow the 'off by a few' MiB's warning disappeared. -//I need to display it whenever the round to cylinders isn't checked. frame_resizer_base = NULL; GRIP = false ; this ->fixed_start = false ; this ->set_resizable( false ); ORIG_BEFORE = ORIG_SIZE = ORIG_AFTER = -1 ; + MIN_SPACE_BEFORE_MB = -1 ; //pack resizer hbox this ->get_vbox() ->pack_start( hbox_resizer, Gtk::PACK_SHRINK ); @@ -100,10 +99,13 @@ Dialog_Base_Partition::Dialog_Base_Partition() //fill partition alignment menu /*TO TRANSLATORS: Menu option for drop down menu "Align to:" */ menu_alignment .items() .push_back( Gtk::Menu_Helpers::MenuElem( _("Cylinder") ) ) ; + /*TO TRANSLATORS: Menu option for label "Align to:" */ + menu_alignment .items() .push_back( Gtk::Menu_Helpers::MenuElem( _("MiB") ) ) ; /*TO TRANSLATORS: Menu option for drop down menu "Align to:" */ menu_alignment .items() .push_back( Gtk::Menu_Helpers::MenuElem( _("None") ) ) ; optionmenu_alignment .set_menu( menu_alignment ); + optionmenu_alignment .set_history( ALIGN_MEBIBYTE); //Default setting table_resize .attach( optionmenu_alignment, 1, 2, 3, 4, Gtk::FILL ); @@ -143,8 +145,10 @@ Partition Dialog_Base_Partition::Get_New_Partition( Byte_Value sector_size ) selected_partition .sector_start = START + Sector(spinbutton_before .get_value_as_int()) * (MEBIBYTE / sector_size) ; if ( ORIG_AFTER != spinbutton_after .get_value_as_int() ) - selected_partition .sector_end = - selected_partition .sector_start + Sector(spinbutton_size .get_value_as_int()) * (MEBIBYTE / sector_size) ; + selected_partition .sector_end = + selected_partition .sector_start + + Sector(spinbutton_size .get_value_as_int()) * (MEBIBYTE / sector_size) + - 1 /* one sector short of exact mebibyte multiple */; //due to loss of precision during calcs from Sector -> MiB and back, it is possible //the new partition thinks it's bigger then it can be. Here we solve this. @@ -167,11 +171,14 @@ Partition Dialog_Base_Partition::Get_New_Partition( Byte_Value sector_size ) switch ( optionmenu_alignment .get_history() ) { case 0 : selected_partition .alignment = ALIGN_CYLINDER; break; - case 1 : selected_partition .alignment = ALIGN_STRICT; break; + case 1 : selected_partition .alignment = ALIGN_MEBIBYTE; break; + case 2 : selected_partition .alignment = ALIGN_STRICT; break; default : selected_partition .alignment = ALIGN_CYLINDER ; } + selected_partition .free_space_before = Sector(spinbutton_before .get_value_as_int()) * (MEBIBYTE / sector_size) ; + //if the original before value has not changed, then set indicator to keep start sector unchanged if ( ORIG_BEFORE == spinbutton_before .get_value_as_int() ) selected_partition .strict_start = TRUE ; @@ -212,11 +219,29 @@ void Dialog_Base_Partition::Set_MinMax_Text( Sector min, Sector max ) label_minmax .set_text( str_temp ) ; } +int Dialog_Base_Partition::MB_Needed_for_Boot_Record( const Partition & partition ) +{ + //Determine if space is needed for the Master Boot Record or + // the Extended Boot Record. Generally an an additional track or MEBIBYTE + // is required so for our purposes reserve a MEBIBYTE in front of the partition. + // NOTE: This logic also contained in Win_GParted::set_valid_operations + if ( ( partition .inside_extended + && partition .type == TYPE_UNALLOCATED + ) + || ( partition .type == TYPE_LOGICAL ) + /* Beginning of disk device */ + || ( partition .sector_start <= (MEBIBYTE / partition .sector_size) ) + ) + return 1 ; + else + return 0 ; +} + void Dialog_Base_Partition::on_signal_move( int x_start, int x_end ) { GRIP = true ; - spinbutton_before .set_value( x_start == 0 ? 0 : x_start * MB_PER_PIXEL ) ; + spinbutton_before .set_value( x_start <= MIN_SPACE_BEFORE_MB * MB_PER_PIXEL ? MIN_SPACE_BEFORE_MB : x_start * MB_PER_PIXEL ) ; if ( x_end == 500 ) { @@ -237,7 +262,7 @@ void Dialog_Base_Partition::on_signal_resize( int x_start, int x_end, Frame_Resi spinbutton_size .set_value( ( x_end - x_start ) * MB_PER_PIXEL ) ; - before_value = fixed_start ? 0 : spinbutton_before .get_value() ; + before_value = fixed_start ? MIN_SPACE_BEFORE_MB : spinbutton_before .get_value() ; if ( arrow == Frame_Resizer_Base::ARROW_RIGHT ) //don't touch freespace before, leave it as it is { @@ -251,10 +276,10 @@ void Dialog_Base_Partition::on_signal_resize( int x_start, int x_end, Frame_Resi } else if ( arrow == Frame_Resizer_Base::ARROW_LEFT ) //don't touch freespace after, leave it as it is { - if ( x_start == 0 ) + if ( x_start <= MIN_SPACE_BEFORE_MB * MB_PER_PIXEL ) { - spinbutton_before .set_value( 0 ); - spinbutton_size .set_value( TOTAL_MB - spinbutton_after.get_value() ) ; + spinbutton_before .set_value( MIN_SPACE_BEFORE_MB ); + spinbutton_size .set_value( TOTAL_MB - MIN_SPACE_BEFORE_MB - spinbutton_after.get_value() ) ; } else spinbutton_before .set_value( @@ -270,7 +295,7 @@ void Dialog_Base_Partition::on_spinbutton_value_changed( SPINBUTTON spinbutton ) { if ( ! GRIP ) { - before_value = fixed_start ? 0 : spinbutton_before .get_value() ; + before_value = fixed_start ? MIN_SPACE_BEFORE_MB : spinbutton_before .get_value() ; //Balance the spinbuttons switch ( spinbutton ) diff --git a/src/Dialog_Partition_Copy.cc b/src/Dialog_Partition_Copy.cc index be217335..88912cc3 100644 --- a/src/Dialog_Partition_Copy.cc +++ b/src/Dialog_Partition_Copy.cc @@ -39,6 +39,7 @@ void Dialog_Partition_Copy::Set_Data( const Partition & selected_partition, cons frame_resizer_base ->set_rgb_partition_color( copied_partition .color ) ; //set some widely used values... + MIN_SPACE_BEFORE_MB = Dialog_Base_Partition::MB_Needed_for_Boot_Record( selected_partition ) ; START = selected_partition .sector_start ; total_length = selected_partition .get_sector_length() ; TOTAL_MB = Utils::round( Utils::sector_to_unit( selected_partition .get_sector_length(), selected_partition .sector_size, UNIT_MIB ) ) ; @@ -48,51 +49,7 @@ void Dialog_Partition_Copy::Set_Data( const Partition & selected_partition, cons // handle situation where src sector size is smaller than dst sector size and an additional partial dst sector is required. Sector copied_min_sectors = ( copied_partition .get_byte_length() + (selected_partition .sector_size - 1) ) / selected_partition .sector_size ; - long COPIED_LENGTH_MB = Utils::round( Utils::sector_to_unit( copied_min_sectors, selected_partition .sector_size, UNIT_MIB ) ) ; - // /* Copy Primary not at start of disk to within Extended partition */ - // Adjust when a primary partition is copied and pasted - // into an unallocated space in an extended partition - // of an MSDOS partition table. - // Since the Extended Boot Record requires an additional track, - // this must be considered in the required space for the - // destination (selected) partition. - // NOTE: This problem does not occur for a primary partition - // at the the start of the disk because the size of the EBR and - // Master Boot Record are the same. - // - // /* Copy Primary not at start of disk to Primary at start of disk */ - // Also Adjust when a primary partition that does not start at the - // beginning of the disk is copied and pasted - // into an unallocated space at the start of the disk device. - // Since the Master Boot Record requires an additional track, - // this must be considered in the required space for the - // destination (selected) partition. - // - // Because the largest unit used in the GUI is one - // cylinder size (round to cylinders), the space - // needed in the destination partition needs to be increased - // by enough to round up one cylinder size. - // Increase by half a cylinder size (or 4 MB) because this - // will round up to the next cylinder size. - // 8 MB is typical cylinder size with todays larger disks. - // 8 MB = (255 heads) * (63 sectors) * (512 bytes) - // - //FIXME: Should confirm MSDOS partition table type, track sector size, and use cylinder size from device - if ( (/* Copy Primary not at start of disk to within Extended partition */ - copied_partition .type == TYPE_PRIMARY - && copied_partition .sector_start > 63 - && selected_partition .type == TYPE_UNALLOCATED - && selected_partition .inside_extended - ) - || ( /* Copy Primary not at start of disk to Primary at start of disk */ - copied_partition .type == TYPE_PRIMARY - && copied_partition .sector_start > 63 - && selected_partition .type == TYPE_UNALLOCATED - && selected_partition .sector_start <=63 /* Beginning of disk device */ - && ! selected_partition .inside_extended - ) - ) - COPIED_LENGTH_MB += 4 ; + long COPIED_LENGTH_MB = Utils::round( Utils::sector_to_unit( copied_min_sectors, selected_partition .sector_size, UNIT_MIB ) ) ; //now calculate proportional length of partition frame_resizer_base ->set_x_start( 0 ) ; @@ -103,7 +60,10 @@ void Dialog_Partition_Copy::Set_Data( const Partition & selected_partition, cons copied_partition .sectors_used, copied_partition .sector_size, UNIT_MIB ) / (TOTAL_MB/500.00) ) ) ; if ( fs .grow ) - fs .MAX = ( ! fs .MAX || fs .MAX > (TOTAL_MB * MEBIBYTE) ) ? (TOTAL_MB * MEBIBYTE) : fs .MAX - (BUF * selected_partition .sector_size) ; + if ( ! fs .MAX || fs .MAX > ((TOTAL_MB - MIN_SPACE_BEFORE_MB) * MEBIBYTE) ) + fs .MAX = ((TOTAL_MB - MIN_SPACE_BEFORE_MB) * MEBIBYTE) ; + else + fs .MAX = fs .MAX - (BUF * selected_partition .sector_size) ; else fs .MAX = copied_partition .get_byte_length() ; @@ -115,27 +75,27 @@ void Dialog_Partition_Copy::Set_Data( const Partition & selected_partition, cons GRIP = true ; //set values of spinbutton_before - spinbutton_before .set_range( 0, TOTAL_MB - Utils::round( Utils::sector_to_unit( fs .MIN, 1 /* Byte */, UNIT_MIB ) ) ) ; - spinbutton_before .set_value( 0 ) ; - + spinbutton_before .set_range( MIN_SPACE_BEFORE_MB, TOTAL_MB - ceil( fs .MIN / double(MEBIBYTE) ) ) ; + spinbutton_before .set_value( MIN_SPACE_BEFORE_MB ) ; + //set values of spinbutton_size - spinbutton_size .set_range( - Utils::round( Utils::sector_to_unit( fs .MIN, 1 /* Byte */, UNIT_MIB ) ), - Utils::round( Utils::sector_to_unit( fs .MAX, 1 /* Byte */, UNIT_MIB ) ) ) ; + spinbutton_size .set_range( ceil( fs .MIN / double(MEBIBYTE) ) + , ceil( fs .MAX / double(MEBIBYTE) ) + ) ; spinbutton_size .set_value( COPIED_LENGTH_MB ) ; //set values of spinbutton_after - spinbutton_after .set_range( 0, TOTAL_MB - Utils::round( Utils::sector_to_unit( fs .MIN, 1 /* Byte */, UNIT_MIB ) ) ) ; - spinbutton_after .set_value( TOTAL_MB - COPIED_LENGTH_MB ) ; + spinbutton_after .set_range( 0, TOTAL_MB - MIN_SPACE_BEFORE_MB - ceil( fs .MIN / double(MEBIBYTE) ) ) ; + spinbutton_after .set_value( TOTAL_MB - MIN_SPACE_BEFORE_MB - COPIED_LENGTH_MB ) ; GRIP = false ; frame_resizer_base ->set_size_limits( Utils::round( fs .MIN / (MB_PER_PIXEL * MEBIBYTE) ), Utils::round( fs .MAX / (MB_PER_PIXEL * MEBIBYTE) ) ) ; //set contents of label_minmax - Set_MinMax_Text( - Utils::round( Utils::sector_to_unit( fs .MIN, 1 /* Byte */, UNIT_MIB ) ), - Utils::round( Utils::sector_to_unit( fs .MAX, 1 /* Byte */, UNIT_MIB ) ) ) ; + Set_MinMax_Text( ceil( fs .MIN / double(MEBIBYTE) ) + , ceil( fs .MAX / double(MEBIBYTE) ) + ) ; //set global selected_partition (see Dialog_Base_Partition::Get_New_Partition ) this ->selected_partition = copied_partition ; diff --git a/src/Dialog_Partition_New.cc b/src/Dialog_Partition_New.cc index eb38f3c5..f542f25b 100644 --- a/src/Dialog_Partition_New.cc +++ b/src/Dialog_Partition_New.cc @@ -140,6 +140,7 @@ void Dialog_Partition_New::Set_Data( const Partition & partition, table_create .attach( entry, 1, 2, 3, 4, Gtk::FILL ) ; //set some widely used values... + MIN_SPACE_BEFORE_MB = Dialog_Base_Partition::MB_Needed_for_Boot_Record( selected_partition ) ; START = partition.sector_start ; total_length = partition.sector_end - partition.sector_start ; TOTAL_MB = Utils::round( Utils::sector_to_unit( this ->selected_partition .get_sector_length(), this ->selected_partition .sector_size, UNIT_MIB ) ) ; @@ -151,8 +152,8 @@ void Dialog_Partition_New::Set_Data( const Partition & partition, //set spinbuttons initial values spinbutton_after .set_value( 0 ) ; - spinbutton_size .set_value( Utils::round( Utils::sector_to_unit( fs .MAX, 1 /* Byte */, UNIT_MIB ) ) ) ; - spinbutton_before .set_value( 0 ) ; + spinbutton_size .set_value( ceil( fs .MAX / double(MEBIBYTE) ) ) ; + spinbutton_before .set_value( MIN_SPACE_BEFORE_MB ) ; //euhrm, this wil only happen when there's a very small free space (usually the effect of a bad partitionmanager) if ( TOTAL_MB * (MEBIBYTE / this ->selected_partition .sector_size) < this ->cylinder_size ) @@ -226,11 +227,14 @@ Partition Dialog_Partition_New::Get_New_Partition( Byte_Value sector_size ) switch ( optionmenu_alignment .get_history() ) { case 0 : part_temp .alignment = GParted::ALIGN_CYLINDER; break; - case 1 : part_temp .alignment = GParted::ALIGN_STRICT; break; + case 1 : part_temp .alignment = GParted::ALIGN_MEBIBYTE; break; + case 2 : part_temp .alignment = GParted::ALIGN_STRICT; break; - default : part_temp .alignment = GParted::ALIGN_CYLINDER ; + default : part_temp .alignment = GParted::ALIGN_MEBIBYTE ; } + part_temp .free_space_before = Sector(spinbutton_before .get_value_as_int()) * (MEBIBYTE / sector_size) ; + return part_temp; } @@ -268,30 +272,34 @@ void Dialog_Partition_New::optionmenu_changed( bool type ) } else if ( fs .MIN < MEBIBYTE ) fs .MIN = MEBIBYTE ; - + if ( selected_partition .get_byte_length() < fs .MIN ) fs .MIN = selected_partition .get_byte_length() ; - - fs .MAX = ( fs .MAX && ( fs .MAX < (TOTAL_MB * MEBIBYTE) ) ) ? fs .MAX : (TOTAL_MB * MEBIBYTE) ; - + + if ( ! fs .MAX || ( fs .MAX > ((TOTAL_MB - MIN_SPACE_BEFORE_MB) * MEBIBYTE) ) ) + fs .MAX = ((TOTAL_MB - MIN_SPACE_BEFORE_MB) * MEBIBYTE) ; + frame_resizer_base ->set_size_limits( Utils::round( fs .MIN / (MB_PER_PIXEL * MEBIBYTE) ), Utils::round( fs .MAX / (MB_PER_PIXEL * MEBIBYTE) ) ) ; - + //set new spinbutton ranges - spinbutton_before .set_range( - 0, TOTAL_MB - Utils::round( Utils::sector_to_unit( fs .MIN, 1 /* Byte */, UNIT_MIB ) ) ) ; - spinbutton_size .set_range( - Utils::round( Utils::sector_to_unit( fs .MIN, 1 /* Byte */, UNIT_MIB ) ), - Utils::round( Utils::sector_to_unit( fs .MAX, 1 /* Byte */, UNIT_MIB ) ) ) ; - spinbutton_after .set_range( - 0, TOTAL_MB - Utils::round( Utils::sector_to_unit( fs .MIN, 1 /* Byte */, UNIT_MIB ) ) ) ; - + spinbutton_before .set_range( MIN_SPACE_BEFORE_MB + , TOTAL_MB - ceil( fs .MIN / double(MEBIBYTE) ) + ) ; + spinbutton_size .set_range( ceil( fs .MIN / double(MEBIBYTE) ) + , ceil( fs .MAX / double(MEBIBYTE) ) + ) ; + spinbutton_after .set_range( 0 + , TOTAL_MB - MIN_SPACE_BEFORE_MB + - ceil( fs .MIN / double(MEBIBYTE) ) + ) ; + //set contents of label_minmax - Set_MinMax_Text( - Utils::round( Utils::sector_to_unit( fs .MIN, 1 /* Byte */, UNIT_MIB ) ), - Utils::round( Utils::sector_to_unit( fs .MAX, 1 /* Byte */, UNIT_MIB ) ) ) ; + Set_MinMax_Text( ceil( fs .MIN / double(MEBIBYTE) ) + , ceil( fs .MAX / double(MEBIBYTE) ) + ) ; } - + //set fitting resizer colors //backgroundcolor.. color_temp .set( optionmenu_type .get_history() == 2 ? "darkgrey" : "white" ) ; diff --git a/src/Dialog_Partition_Resize_Move.cc b/src/Dialog_Partition_Resize_Move.cc index acd7e97b..4caf886a 100644 --- a/src/Dialog_Partition_Resize_Move.cc +++ b/src/Dialog_Partition_Resize_Move.cc @@ -102,7 +102,8 @@ void Dialog_Partition_Resize_Move::Resize_Move_Normal( const std::vector (fs .MIN / selected_partition .sector_size) ) fs .MIN = selected_partition .sectors_used * selected_partition .sector_size ; - //if fs. MIN is 0 here (means used == 0 as well) it's safe to have BUF / 2 - fs .MIN += fs .MIN ? (BUF * selected_partition .sector_size) : (BUF/2 * selected_partition .sector_size) ; - - //in certain (rare) cases fs .MIN is (now) larger than 'selected_partition'.. - if ( fs .MIN > selected_partition .get_byte_length() ) - fs .MIN = selected_partition .get_byte_length() ; + //ensure that minimum size is at least one mebibyte + if ( ! fs .MIN || fs .MIN < MEBIBYTE ) + fs .MIN = MEBIBYTE ; } else fs .MIN = selected_partition .get_byte_length() ; //set MAX if ( fs .grow ) - { - if ( ! fs .MAX || fs .MAX > (TOTAL_MB * MEBIBYTE) ) - fs .MAX = TOTAL_MB * MEBIBYTE ; - else - fs .MAX -= (BUF/2 * selected_partition .sector_size) ; - } + fs .MAX = (TOTAL_MB - MIN_SPACE_BEFORE_MB) * MEBIBYTE ; else fs .MAX = selected_partition .get_byte_length() ; - //set values of spinbutton_before if ( ! fixed_start ) { - spinbutton_before .set_range( - 0, - TOTAL_MB - Utils::round( Utils::sector_to_unit( fs .MIN, 1 /* Byte */, UNIT_MIB ) ) ) ; + spinbutton_before .set_range( MIN_SPACE_BEFORE_MB + , TOTAL_MB - ceil( fs .MIN / double(MEBIBYTE) ) + ) ; spinbutton_before .set_value( - Utils::round( Utils::sector_to_unit( previous, selected_partition .sector_size, UNIT_MIB ) ) ) ; + Utils::round( Utils::sector_to_unit( previous, selected_partition .sector_size, UNIT_MIB ) ) + - MIN_SPACE_BEFORE_MB ) ; } - + //set values of spinbutton_size - spinbutton_size .set_range( - Utils::round( Utils::sector_to_unit( fs .MIN, 1 /* Byte */, UNIT_MIB ) ), - Utils::round( Utils::sector_to_unit( fs .MAX, 1 /* Byte */, UNIT_MIB ) ) ) ; + spinbutton_size .set_range( ceil( fs .MIN / double(MEBIBYTE) ) + , ceil( fs .MAX / double(MEBIBYTE) ) + ) ; spinbutton_size .set_value( Utils::round( Utils::sector_to_unit( selected_partition .get_sector_length(), selected_partition .sector_size, UNIT_MIB ) ) ) ; @@ -164,17 +157,17 @@ void Dialog_Partition_Resize_Move::Resize_Move_Normal( const std::vectorset_size_limits( Utils::round( fs .MIN / (MB_PER_PIXEL * MEBIBYTE) ), Utils::round( fs .MAX / (MB_PER_PIXEL * MEBIBYTE) ) ) ; - + //set contents of label_minmax - Set_MinMax_Text( - Utils::round( Utils::sector_to_unit( fs .MIN, 1 /* Byte */, UNIT_MIB ) ), - Utils::round( Utils::sector_to_unit( fs .MAX, 1 /* Byte */, UNIT_MIB ) ) ) ; + Set_MinMax_Text( ceil( fs .MIN / double(MEBIBYTE) ) + , ceil( fs .MAX / double(MEBIBYTE) ) + ) ; } void Dialog_Partition_Resize_Move::Resize_Move_Extended( const std::vector & partitions ) @@ -200,6 +193,7 @@ void Dialog_Partition_Resize_Move::Resize_Move_Extended( const std::vectorset_x_end( Utils::round( selected_partition .get_sector_length() / ( total_length / 500.00 ) ) + frame_resizer_base ->get_x_start() ) ; //used is a bit different here... we consider start of first logical to end last logical as used space - Sector first =0, used =0 ; - for ( unsigned int i = 0 ; i < partitions[ t ] .logicals .size() ; i++ ) + Sector first =0, last = 0, used =0 ; + if ( ! ( selected_partition .logicals .size() == 1 + && selected_partition .logicals .back() .type == GParted::TYPE_UNALLOCATED + ) + ) { - if ( partitions[ t ] .logicals[ i ] .type == GParted::TYPE_LOGICAL ) + //logical partitions other than unallocated exist + first = selected_partition .sector_end ; + last = selected_partition .sector_start ; + for ( unsigned int i = 0 ; i < partitions[ t ] .logicals .size() ; i++ ) { - if ( first == 0 ) - first = partitions[ t ] .logicals[ i ] .sector_start ; - - used = partitions[ t ] .logicals[ i ] .sector_end - first; + if ( partitions[ t ] .logicals[ i ] .type == GParted::TYPE_LOGICAL ) + { + if ( partitions[ t ] .logicals[ i ] .sector_start < first ) + first = partitions[ t ] .logicals[ i ] .sector_start - (MEBIBYTE / selected_partition .sector_size) ; + if ( first < 0 ) + first = 0 ; + if ( partitions[ t ] .logicals[ i ] .sector_end > last ) + last = partitions[ t ] .logicals[ i ] .sector_end ; + } } + used = last - first; } + //set MIN + if ( used == 0 ) + { + //Reasonable minimum of 1 MiB for EBR plus 1 MiB for small partition + fs .MIN = MEBIBYTE ; + } + else + fs .MIN = used * selected_partition .sector_size ; + + //set MAX + fs .MAX = (TOTAL_MB - MIN_SPACE_BEFORE_MB) * MEBIBYTE ; dynamic_cast( frame_resizer_base ) -> set_used_start( Utils::round( (first - START) / ( total_length / 500.00 ) ) ) ; @@ -227,17 +244,14 @@ void Dialog_Partition_Resize_Move::Resize_Move_Extended( const std::vector device .length ) - partition .sector_end = device .length -1 ; + //Must account the relative offset between: + // (A) the Extended Boot Record sector and the next track of the + // logical partition (usually 63 sectors), and + // (B) the Master Boot Record sector and the next track of the first + // primary partition + diff = (partition .sector_start - device .sectors) % device .cylsize ; + } + else if ( partition.sector_start == 34 ) + { + // (C) the GUID Partition Table (GPT) and the start of the data + // partition at sector 34 + diff = (partition .sector_start - 34 ) % device .cylsize ; + } + else + { + diff = partition .sector_start % device .cylsize ; + } + if ( diff && ! partition .strict_start ) + { + if ( diff < ( device .cylsize / 2 ) ) + partition .sector_start -= diff ; + else + partition .sector_start += (device .cylsize - diff ) ; + } - //ok, do some basic checks on the partition.. - if ( partition .get_sector_length() <= 0 ) - { - error = String::ucompose( _("A partition cannot have a length of %1 sectors"), - partition .get_sector_length() ) ; - return false ; - } - - if ( partition .get_sector_length() < partition .sectors_used ) - { - error = String::ucompose( - _("A partition with used sectors (%1) greater than its length (%2) is not valid"), - partition .sectors_used, - partition .get_sector_length() ) ; - return false ; - } - - //FIXME: it would be perfect if we could check for overlapping with adjacent partitions as well, - //however, finding the adjacent partitions is not as easy as it seems and at this moment all the dialogs - //already perform these checks. A perfect 'fixme-later' ;) + diff = (partition .sector_end +1) % device .cylsize ; + if ( diff ) + { + if ( diff < ( device .cylsize / 2 ) ) + partition .sector_end -= diff ; + else + partition .sector_end += (device .cylsize - diff ) ; } return true ; } +bool GParted_Core::snap_to_mebibyte( const Device & device, Partition & partition, Glib::ustring & error ) +{ + Sector diff = 0; + if ( partition .sector_start < 2 || partition .type == TYPE_LOGICAL ) + { + //Must account the relative offset between: + // (A) the Master Boot Record sector and the first primary/extended partition, and + // (B) the Extended Boot Record sector and the logical partition + + //If strict_start is set then do not adjust sector start. + //If this partition is not simply queued for a reformat then + // add space minimum to force alignment to next mebibyte. + if ( (! partition .strict_start) + && (partition .free_space_before == 0) + && ( partition .status != STAT_FORMATTED) + ) + { + //Unless specifically told otherwise, the Linux kernel considers extended + // boot records to be two sectors long, in order to "leave room for LILO". + partition .sector_start += 2 ; + } + } + + //Align start sector + diff = Sector(partition .sector_start % ( MEBIBYTE / partition .sector_size )); + if ( diff && ( (! partition .strict_start) + || ( partition .strict_start + && ( partition .status == STAT_NEW + || partition .status == STAT_COPY + ) + ) + ) + ) + partition .sector_start += ( (MEBIBYTE / partition .sector_size) - diff) ; + + //If this is a logical partition not at end of drive then check to see if space is + // required for a following logical partition Extended Boot Record + if ( partition .type == TYPE_LOGICAL ) + { + //Locate the extended partition that contains the logical partitions. + int index_extended = -1 ; + for ( unsigned int t = 0 ; t < device .partitions .size() ; t++ ) + { + if ( device .partitions[ t ] .type == TYPE_EXTENDED ) + index_extended = t ; + } + + //If there is a following logical partition that starts a mebibyte or less + // from the end of this partition, then reserve a mebibyte for the EBR. + if ( index_extended != -1 ) + { + for ( unsigned int t = 0; t < device .partitions[ index_extended ] .logicals .size(); t++ ) + { + if ( ( device .partitions[ index_extended ] .logicals[ t ] .type == TYPE_LOGICAL ) + && ( device .partitions[ index_extended ] .logicals[ t ] .sector_start > partition .sector_end ) + && ( ( device .partitions[ index_extended ] .logicals[ t ] .sector_start - partition .sector_end ) + < ( MEBIBYTE / device .sector_size ) + ) + ) + partition .sector_end -= 1 ; + } + } + } + + //If this is a GPT partition table then reserve a mebibyte at the end of the device + // for the backup partition table + if ( device .disktype == "gpt" + && ( ( device .length - partition .sector_end ) <= ( MEBIBYTE / device .sector_size ) ) + ) + { + partition .sector_end -= 1 ; + } + + //Align end sector + diff = (partition .sector_end + 1) % ( MEBIBYTE / partition .sector_size); + if ( diff ) + partition .sector_end -= diff ; + + return true ; +} + +bool GParted_Core::snap_to_alignment( const Device & device, Partition & partition, Glib::ustring & error ) +{ + bool rc = true ; + + if ( partition .alignment == ALIGN_CYLINDER ) + rc = snap_to_cylinder( device, partition, error ) ; + else if ( partition .alignment == ALIGN_MEBIBYTE ) + rc = snap_to_mebibyte( device, partition, error ) ; + + //Ensure that partition start and end are not beyond the ends of the disk device + if ( partition .sector_start < 0 ) + partition .sector_start = 0 ; + if ( partition .sector_end > device .length ) + partition .sector_end = device .length - 1 ; + + //do some basic checks on the partition + if ( partition .get_sector_length() <= 0 ) + { + error = String::ucompose( _("A partition cannot have a length of %1 sectors"), + partition .get_sector_length() ) ; + return false ; + } + + if ( partition .get_sector_length() < partition .sectors_used ) + { + error = String::ucompose( + _("A partition with used sectors (%1) greater than its length (%2) is not valid"), + partition .sectors_used, + partition .get_sector_length() ) ; + return false ; + } + + //FIXME: it would be perfect if we could check for overlapping with adjacent partitions as well, + //however, finding the adjacent partitions is not as easy as it seems and at this moment all the dialogs + //already perform these checks. A perfect 'fixme-later' ;) + + return rc ; +} + bool GParted_Core::apply_operation_to_disk( Operation * operation ) { bool succes = false ; @@ -1375,7 +1471,9 @@ bool GParted_Core::create_partition( Partition & new_partition, OperationDetail if ( lp_partition ) { - if ( new_partition .alignment == ALIGN_STRICT ) + if ( new_partition .alignment == ALIGN_STRICT + || new_partition .alignment == ALIGN_MEBIBYTE + ) { PedGeometry *geom = ped_geometry_new( lp_device, new_partition .sector_start, @@ -1548,6 +1646,7 @@ bool GParted_Core::resize_move( const Device & device, OperationDetail & operationdetail ) { if ( (partition_new .alignment == ALIGN_STRICT) + || (partition_new .alignment == ALIGN_MEBIBYTE) || partition_new .strict_start || calculate_exact_geom( partition_old, partition_new, operationdetail ) ) @@ -1890,7 +1989,10 @@ bool GParted_Core::resize_move_partition( const Partition & partition_old, if ( lp_partition ) { - if ( (partition_new .alignment == ALIGN_STRICT) || partition_new .strict_start ) { + if ( (partition_new .alignment == ALIGN_STRICT) + || (partition_new .alignment == ALIGN_MEBIBYTE) + || partition_new .strict_start + ) { PedGeometry *geom = ped_geometry_new( lp_device, partition_new .sector_start, partition_new .get_sector_length() ) ; diff --git a/src/Partition.cc b/src/Partition.cc index 66ca19a2..882da8aa 100644 --- a/src/Partition.cc +++ b/src/Partition.cc @@ -43,6 +43,7 @@ void Partition::Reset() label .clear() ; uuid .clear() ; partition_number = sector_start = sector_end = sectors_used = sectors_unused = -1; + free_space_before = -1 ; sector_size = 0 ; color .set( "black" ) ; inside_extended = busy = strict_start = false ; diff --git a/src/Win_GParted.cc b/src/Win_GParted.cc index 1156ebf6..fb4eb065 100644 --- a/src/Win_GParted.cc +++ b/src/Win_GParted.cc @@ -669,10 +669,12 @@ void Win_GParted::Add_Operation( Operation * operation, int index ) { Glib::ustring error ; //FIXME: this is becoming a mess.. maybe it's better to check if partition_new > 0 - if ( operation ->type == OPERATION_DELETE || - operation ->type == OPERATION_FORMAT || + if ( operation ->type == OPERATION_DELETE || + operation ->type == OPERATION_FORMAT || operation ->type == OPERATION_CHECK || - gparted_core .snap_to_cylinder( operation ->device, operation ->partition_new, error ) ) + operation ->type == OPERATION_LABEL_PARTITION || + gparted_core .snap_to_alignment( operation ->device, operation ->partition_new, error ) + ) { operation ->create_description() ; @@ -858,46 +860,34 @@ void Win_GParted::set_valid_operations() else required_size = copied_partition .get_byte_length() ; - // /* Copy Primary not at start of disk to within Extended partition */ - // Adjust when a primary partition is copied and pasted - // into an unallocated space in an extended partition - // of an MSDOS partition table. - // Since the Extended Boot Record requires an additional track, - // this must be considered in the required space for the - // destination (selected) partition. - // NOTE: This problem does not occur for a primary partition - // at the the start of the disk because the size of the EBR and - // Master Boot Record are the same. - // - // /* Copy Primary not at start of disk to Primary at start of disk */ - // Also Adjust when a primary partition that does not start at the - // beginning of the disk is copied and pasted - // into an unallocated space at the start of the disk device. - // Since the Master Boot Record requires an additional track, - // this must be considered in the required space for the - // destination (selected) partition. - // - // Because the largest unit used in the GUI is one - // cylinder size (round to cylinders), the space - // needed in the destination partition needs to be increased - // by one cylinder size. - if ( (/* Copy Primary not at start of disk to within Extended partition */ - copied_partition .type == TYPE_PRIMARY - && copied_partition .sector_start > devices[ current_device ] .sectors /* 63 for MSDOS Partition Table */ - && devices[ current_device ] .disktype == "msdos" - && selected_partition .type == TYPE_UNALLOCATED - && selected_partition .inside_extended - ) - || ( /* Copy Primary not at start of disk to Primary at start of disk */ - copied_partition .type == TYPE_PRIMARY - && copied_partition .sector_start > devices[ current_device ] .sectors /* 63 for MSDOS Partition Table */ - && devices[ current_device ] .disktype == "msdos" - && selected_partition .type == TYPE_UNALLOCATED - && selected_partition .sector_start <= devices[ current_device ] .sectors /* Beginning of disk device */ - && ! selected_partition .inside_extended - ) + //Determine if space is needed for the Master Boot Record or + // the Extended Boot Record. Generally an an additional track or MEBIBYTE + // is required so for our purposes reserve a MEBIBYTE in front of the partition. + // NOTE: This logic also contained in Dialog_Base_Partition::MB_Needed_for_Boot_Record + if ( ( selected_partition .inside_extended + && selected_partition .type == TYPE_UNALLOCATED + ) + || ( selected_partition .type == TYPE_LOGICAL ) + /* Beginning of disk device */ + || ( selected_partition .sector_start <= (MEBIBYTE / selected_partition .sector_size) ) ) - required_size += devices[ current_device ] .cylsize * devices[ current_device ] .sector_size ; + required_size += MEBIBYTE; + + //Determine if space is needed for the Extended Boot Record for a logical partition + // after this partition. Generally an an additional track or MEBIBYTE + // is required so for our purposes reserve a MEBIBYTE in front of the partition. + if ( ( ( selected_partition .inside_extended + && selected_partition .type == TYPE_UNALLOCATED + ) + || ( selected_partition .type == TYPE_LOGICAL ) + ) + && ( selected_partition .sector_end + < ( devices[ current_device ] .length + - ( 2 * MEBIBYTE / devices[ current_device ] .sector_size ) + ) + ) + ) + required_size += MEBIBYTE; if ( required_size <= selected_partition .get_byte_length() ) allow_paste( true ) ;