gparted/src/Dialog_Partition_New.cc

454 lines
17 KiB
C++
Raw Normal View History

/* Copyright (C) 2004 Bart
* Copyright (C) 2008, 2009, 2010, 2011 Curtis Gedak
2004-09-19 14:24:53 -06:00
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
2004-09-19 14:24:53 -06:00
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
2004-09-19 14:24:53 -06:00
*/
#include "Device.h"
#include "Dialog_Partition_New.h"
#include "FileSystem.h"
#include "GParted_Core.h"
#include "Partition.h"
#include "Utils.h"
2004-09-19 14:24:53 -06:00
#include <glibmm/ustring.h>
2004-09-19 14:24:53 -06:00
namespace GParted
{
Dialog_Partition_New::Dialog_Partition_New( const Device & device,
const Partition & selected_partition,
bool any_extended,
unsigned short new_count,
const std::vector<FS> & FILESYSTEMS )
: Dialog_Base_Partition(device)
2004-09-19 14:24:53 -06:00
{
/*TO TRANSLATORS: dialogtitle */
this ->set_title( _("Create new Partition") ) ;
Set_Resizer( false ) ;
Set_Confirm_Button( NEW ) ;
//set used (in pixels)...
2004-09-19 14:24:53 -06:00
frame_resizer_base ->set_used( 0 ) ;
set_data(device, selected_partition, any_extended, new_count, FILESYSTEMS );
2004-09-19 14:24:53 -06:00
}
Dialog_Partition_New::~Dialog_Partition_New()
{
delete new_partition;
new_partition = NULL;
// Work around a Gtk issue fixed in 3.24.0.
// https://gitlab.gnome.org/GNOME/gtk/issues/125
hide();
}
void Dialog_Partition_New::set_data( const Device & device,
const Partition & selected_partition,
bool any_extended,
unsigned short new_count,
const std::vector<FS> & FILESYSTEMS )
2004-09-19 14:24:53 -06:00
{
2004-10-06 09:32:40 -06:00
this ->new_count = new_count;
new_partition = selected_partition.clone();
// Copy only supported file systems, excluding LUKS, from GParted_Core FILESYSTEMS
// vector. Add FS_CLEARED, FS_UNFORMATTED and FS_EXTENDED at the end. This
// decides the order of items in the file system menu built by
// build_filesystems_combo().
this->FILESYSTEMS.clear();
for ( unsigned i = 0 ; i < FILESYSTEMS.size() ; i ++ )
{
if (GParted_Core::supported_filesystem(FILESYSTEMS[i].fstype) &&
FILESYSTEMS[i].fstype != FS_LUKS )
this->FILESYSTEMS.push_back( FILESYSTEMS[i] );
}
FS fs_tmp ;
//... add FS_CLEARED
fs_tmp.fstype = FS_CLEARED;
fs_tmp .create = FS::GPARTED ;
this ->FILESYSTEMS .push_back( fs_tmp ) ;
//... add FS_UNFORMATTED
fs_tmp.fstype = FS_UNFORMATTED;
fs_tmp .create = FS::GPARTED ;
this ->FILESYSTEMS .push_back( fs_tmp ) ;
// ... finally add FS_EXTENDED. Needed so that when creating an extended
// partition it is identified correctly before the operation is applied.
fs_tmp = FS();
fs_tmp.fstype = FS_EXTENDED;
fs_tmp.create = FS::NONE;
this ->FILESYSTEMS .push_back( fs_tmp ) ;
// Add table with selection menu's...
grid_create.set_border_width(10);
grid_create.set_row_spacing(5);
hbox_main.pack_start(grid_create, Gtk::PACK_SHRINK);
/* TO TRANSLATORS: used as label for a list of choices. Create as: <combo box with choices> */
grid_create.attach(*Utils::mk_label(Glib::ustring(_("Create as:")) + "\t"),
0, 0, 1, 1);
// Fill partition type combo.
combo_type.items().push_back(_("Primary Partition"));
combo_type.items().push_back(_("Logical Partition"));
combo_type.items().push_back(_("Extended Partition"));
2004-09-19 14:24:53 -06:00
//determine which PartitionType is allowed
if ( device.disktype != "msdos" && device.disktype != "dvh" )
{
combo_type.items()[1].set_sensitive(false);
combo_type.items()[2].set_sensitive(false);
combo_type.set_active(0);
}
else if ( selected_partition.inside_extended )
2004-09-19 14:24:53 -06:00
{
combo_type.items()[0].set_sensitive(false);
combo_type.items()[2].set_sensitive(false);
combo_type.set_active(1);
2004-09-19 14:24:53 -06:00
}
else
{
combo_type.items()[1].set_sensitive(false);
2004-10-06 09:32:40 -06:00
if ( any_extended )
combo_type.items()[2].set_sensitive(false);
combo_type.set_active(0);
2004-09-19 14:24:53 -06:00
}
//160 is the ideal width for this table column.
//(when one widget is set, the rest wil take this width as well)
combo_type.set_size_request(160, -1);
combo_type.signal_changed().connect(
sigc::bind<bool>(sigc::mem_fun(*this, &Dialog_Partition_New::combobox_changed), true));
grid_create.attach(combo_type, 1, 0, 1, 1);
// Partition name
grid_create.attach(*Utils::mk_label(Glib::ustring(_("Partition name:")) + "\t"),
0, 1, 1, 1);
// Initialise text entry box
partition_name_entry.set_width_chars( 20 );
partition_name_entry.set_sensitive( device.partition_naming_supported() );
partition_name_entry.set_max_length( device.get_max_partition_name_length() );
// Add entry box to table
grid_create.attach(partition_name_entry, 1, 1, 1, 1);
// File systems to choose from
grid_create.attach(*Utils::mk_label(Glib::ustring(_("File system:")) + "\t"),
0, 1, 2, 3);
build_filesystems_combo(device.readonly);
combo_filesystem.signal_changed().connect(
sigc::bind<bool>(sigc::mem_fun(*this, &Dialog_Partition_New::combobox_changed), false));
grid_create.attach(combo_filesystem, 1, 2, 1, 1);
// Label
grid_create.attach(*Utils::mk_label(_("Label:")), 0, 3, 1, 1);
//Create Text entry box
filesystem_label_entry.set_width_chars( 20 );
// Add entry box to table
grid_create.attach(filesystem_label_entry, 1, 3, 1, 1);
// Set vexpand on all grid_create child widgets
std::vector<Gtk::Widget*> children = grid_create.get_children();
for (std::vector<Gtk::Widget*>::iterator it = children.begin(); it != children.end(); ++it)
(*it)->set_vexpand();
2004-09-19 14:24:53 -06:00
//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.sector_end - selected_partition.sector_start;
TOTAL_MB = Utils::round( Utils::sector_to_unit( selected_partition.get_sector_length(),
selected_partition.sector_size, UNIT_MIB ) );
MB_PER_PIXEL = TOTAL_MB / 500.00 ;
// Set default creatable file system.
// (As the change signal for combo_filesystem has already been connected,
// combobox_changed(false) is automatically called by setting the active
// selection. This is needed to initialise everything correctly).
combo_filesystem.set_active(first_creatable_fs);
//set spinbuttons initial values
spinbutton_after .set_value( 0 ) ;
spinbutton_size.set_value( ceil( fs_limits.max_size / double(MEBIBYTE) ) );
spinbutton_before .set_value( MIN_SPACE_BEFORE_MB ) ;
//Disable resizing when the total area is less than two mebibytes
if ( TOTAL_MB < 2 )
frame_resizer_base ->set_sensitive( false ) ;
// Connect signal handler for Dialog_Base_Partiton combo_alignment.
combo_alignment.signal_changed().connect(
sigc::bind<bool>(sigc::mem_fun(*this, &Dialog_Partition_New::combobox_changed), false));
this ->show_all_children() ;
2004-09-19 14:24:53 -06:00
}
const Partition & Dialog_Partition_New::Get_New_Partition()
2004-09-19 14:24:53 -06:00
{
g_assert( new_partition != NULL ); // Bug: Not initialised by constructor calling set_data()
PartitionType part_type ;
2004-09-19 14:24:53 -06:00
Sector new_start, new_end;
switch (combo_type.get_active_row_number())
2004-09-19 14:24:53 -06:00
{
case 0: part_type = TYPE_PRIMARY; break;
case 1: part_type = TYPE_LOGICAL; break;
case 2: part_type = TYPE_EXTENDED; break;
default: part_type = TYPE_UNALLOCATED;
2004-09-19 14:24:53 -06:00
}
//FIXME: Partition size is limited to just less than 1024 TeraBytes due
// to the maximum value of signed 4 byte integer.
new_start = START + Sector(spinbutton_before.get_value_as_int()) *
(MEBIBYTE / new_partition->sector_size);
new_end = new_start + Sector(spinbutton_size.get_value_as_int()) *
(MEBIBYTE / new_partition->sector_size)
- 1;
2004-09-19 14:24:53 -06:00
/* 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 try to solve this.*/
if ( new_start < new_partition->sector_start )
new_start = new_partition->sector_start;
if ( new_end > new_partition->sector_end )
new_end = new_partition->sector_end;
// Grow new partition a bit if freespaces are < 1 MiB
if ( new_start - new_partition->sector_start < MEBIBYTE / new_partition->sector_size )
new_start = new_partition->sector_start;
if ( new_partition->sector_end - new_end < MEBIBYTE / new_partition->sector_size )
new_end = new_partition->sector_end;
Fix temporary path name of new partitions (#759488) In the UI new partitions were being named "unallocated" instead of "New Partition #1", etc. Also deleting a new partition not yet created deletes all new partitions from the operation list because it matches by name only and they were all named "unallocated". Broken by this recent commit: 762cd1094aece03986226a39d1f6b4fecfd73ee9 Return class member from Dialog_Partition_New::Get_New_Partition() (#757671) Prior to this commit in the create new partition dialog code did these relevant steps: Dialog_Partition_New::Get_New_Partition() Partition part_temp; ... part_temp.Set(..., "New Partition #%1", ...); Create local Partition object using default constructor which calls Partition::Reset() and clears the paths vector. It then calls Set() which appends the new name making the vector: paths = ["New Partition #%1"] After the above commit the code did these steps: Dialog_Partition_New::Dialog_Partition_New(..., selected_partition, ...) set_data(..., selected_partition, ...); new_partition = selected_partition; Dialog_Partition_New::Get_New_Partition(...) new_partition.Set(..., "New Partition #%1", ...); New_partition is copied from the selected unallocated partition in which the new partition will be created. So new_partition is now named "unallocated". Then the Set() call appends the new name making the vector: paths = ["unallocated", "New Partition #%1"] As get_path() returns the first name in the paths vector the path name changed from "New Partition #%1" to "unallocated" causing this bug. Fix by resetting the new_partition object to clear all vestiges of it being a copy of the selected unallocated partition, Reset() call, before then calling Set(). This then appends the new name to an empty vector making it contain just the required new name: paths = ["New Partition #%1"] Bug 759488 - Pending create partitions are all getting named "unallocated"
2015-12-15 05:50:09 -07:00
// Copy a final few values needed from the original unallocated partition before
// resetting the Partition object and populating it as the new partition.
Glib::ustring device_path = new_partition->device_path;
Sector sector_size = new_partition->sector_size;
bool inside_extended = new_partition->inside_extended;
new_partition->Reset();
new_partition->Set( device_path,
Glib::ustring::compose( _("New Partition #%1"), new_count ),
new_count, part_type,
FILESYSTEMS[combo_filesystem.get_active_row_number()].fstype,
new_start, new_end,
sector_size,
inside_extended, false );
new_partition->status = STAT_NEW;
// Retrieve partition name
new_partition->name = Utils::trim( partition_name_entry.get_text() );
//Retrieve Label info
new_partition->set_filesystem_label( Utils::trim( filesystem_label_entry.get_text() ) );
2004-09-19 14:24:53 -06:00
//set alignment
switch (combo_alignment.get_active_row_number())
{
case 0:
new_partition->alignment = ALIGN_CYLINDER;
break;
case 1:
new_partition->alignment = ALIGN_MEBIBYTE;
{
// If start sector not MiB aligned and free space available
// then add ~1 MiB to partition so requested size is kept
Sector diff = (MEBIBYTE / new_partition->sector_size) -
(new_partition->sector_end + 1) % (MEBIBYTE / new_partition->sector_size);
if ( diff
&& new_partition->sector_start % (MEBIBYTE / new_partition->sector_size ) > 0
&& new_partition->sector_end - START + 1 + diff < total_length
)
new_partition->sector_end += diff;
}
break;
case 2:
new_partition->alignment = ALIGN_STRICT;
break;
default:
new_partition->alignment = ALIGN_MEBIBYTE;
break;
}
new_partition->free_space_before = Sector(spinbutton_before .get_value_as_int()) * (MEBIBYTE / new_partition->sector_size);
// Create unallocated space within this new extended partition
//
// FIXME:
// Even after moving creation of the unallocated space within this new extended
// partition to here after the above alignment adjustment, the boundaries of the
// extended partition may be further adjusted by snap_to_alignment(). However the
// UI when creating logical partitions within this extended partition will use the
// boundaries as defined now by this unallocated space. Hence boundaries of
// logical partitions may be wrong or overlapping.
//
// Test case:
// On an empty MSDOS formatted disk, create a cylinder aligned extended partition.
// Then create a default MiB aligned logical partition filling the extended
// partition. Apply the operations. Creation of logical partition fails with
// libparted message "Can't have overlapping partitions."
//
// To fix this properly all the alignment constraints need to be applied here in
// the dialogs which create and modify partition boundaries. The logic in
// snap_to_alignment() needs including in it. It will need abstracting into a set
// of methods so that it can be used in each dialog which creates and modified
// partition boundaries.
if ( new_partition->type == TYPE_EXTENDED )
{
Partition * unallocated = new Partition();
unallocated->Set_Unallocated( new_partition->device_path,
new_partition->sector_start,
new_partition->sector_end,
new_partition->sector_size,
true );
new_partition->logicals.push_back_adopt( unallocated );
}
Dialog_Base_Partition::snap_to_alignment(m_device, *new_partition);
return *new_partition;
2004-09-19 14:24:53 -06:00
}
void Dialog_Partition_New::combobox_changed(bool combo_type_changed)
2004-09-19 14:24:53 -06:00
{
g_assert( new_partition != NULL ); // Bug: Not initialised by constructor calling set_data()
// combo_type
if (combo_type_changed)
2004-09-19 14:24:53 -06:00
{
if (combo_type.get_active_row_number() == TYPE_EXTENDED &&
combo_filesystem.items().size() < FILESYSTEMS.size() )
2004-09-19 14:24:53 -06:00
{
combo_filesystem.items().push_back(Utils::get_filesystem_string(FS_EXTENDED));
combo_filesystem.set_active(combo_filesystem.items().back());
combo_filesystem.set_sensitive(false);
2004-09-19 14:24:53 -06:00
}
else if (combo_type.get_active_row_number() != TYPE_EXTENDED &&
combo_filesystem.items().size() == FILESYSTEMS.size() )
2004-09-19 14:24:53 -06:00
{
Fix crash in Create New Partition dialog when changing type (#101) On an MSDOS partitioned drive, open the Create New Partition dialog and change "created as" from Primary Partition to Extended Partition and back to Primary Partition. On Fedora and RHEL/CentOS 8, which builds packages with FORTIFY_SOURCE [1][2] and GLIBXX_Assertions [3][4] enabled, GParted will crash. Run GParted built with the default compilation options under valgrind and repeat the test. Multiple out of bounds reads are reported like this: # valgrind --track-origins=yes ./gpartedbin ... ==232613== Invalid read of size 8 ==232613== at 0x441AF6: GParted::Dialog_Partition_New::combobox_changed(bool) (Dialog_Partition_New.cc:354) ==232613== by 0x443DBD: sigc::bound_mem_functor1<void, GParted::Dialog_Partition_New, bool>::operator()(bool const&) const (mem_fun.h:2066) Coming from Dialog_Partition_New.cc: 328 void Dialog_Partition_New::combobox_changed(bool type) 329 { ... 351 // combo_filesystem and combo_alignment 352 if ( ! type ) 353 { > 354 fs = FILESYSTEMS[combo_filesystem.get_active_row_number()]; When the partition type is changed to Extended the file system is forced to be "Extended" too. This is done in ::combobox_changed() method by modifying combo_filesystem to add "Extended", making that the selected item and setting the widget as inactive. Then when the partition type is changed back to primary the file system combobox is returned to it's previous state. This is done by first removing the last "Extended" item, making the widget active and setting the selected item. However as "Extended" is the currently selected item, removing it forces their to be no selected item and triggers a change to combo_filesystem triggering a recursive call to ::combobox_changed() where combo_filesystem.get_active_row_number() returns -1 (no selection) [5] and on line 354 the code accesses item -1 of the FILESYSTEMS[] vector. Fix by setting the new combo_filesystem selection before removing the currently selected "Extended" item. This has the added benefit of only triggering a change to combo_filesystem once when the default item is selected rather than twice when the currently "Extended" item is removed and again when the default item is selected. [1] [Fedora] Security Features, Compile Time Buffer Checks (FORTIFY_SOURCE) https://fedoraproject.org/wiki/Security_Features#Compile_Time_Buffer_Checks_.28FORTIFY_SOURCE.29 [2] Enhance application security with FORTIFY_SOURCE https://access.redhat.com/blogs/766093/posts/1976213 [3] Security Features Matrix (GLIBXX_Assertions) https://fedoraproject.org/wiki/Security_Features_Matrix [4] GParted 1.2.0-1.fc33 package build.log for Fedora 33 https://kojipkgs.fedoraproject.org/packages/gparted/1.2.0/1.fc33/data/logs/x86_64/build.log CXXFLAGS='-O2 -g ... -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS ...' [5] gtkmm: Gtk::ComboBox Class Reference, get_active_row_number() https://developer.gnome.org/gtkmm/stable/classGtk_1_1ComboBox.html#a53531bc041b5a460826babb8496c363b Closes #101 - Crash changing Partition type in "Create new partition" dialog
2021-02-28 06:12:34 -07:00
combo_filesystem.set_active(first_creatable_fs);
combo_filesystem.items().erase(combo_filesystem.items().back());
combo_filesystem.set_sensitive(true);
2004-09-19 14:24:53 -06:00
}
}
// combo_filesystem and combo_alignment
if (! combo_type_changed)
2004-09-19 14:24:53 -06:00
{
fs = FILESYSTEMS[combo_filesystem.get_active_row_number()];
fs_limits = GParted_Core::get_filesystem_limits(fs.fstype, *new_partition);
if ( fs_limits.min_size < MEBIBYTE )
fs_limits.min_size = MEBIBYTE;
if ( new_partition->get_byte_length() < fs_limits.min_size )
fs_limits.min_size = new_partition->get_byte_length();
if ( ! fs_limits.max_size || ( fs_limits.max_size > ((TOTAL_MB - MIN_SPACE_BEFORE_MB) * MEBIBYTE) ) )
fs_limits.max_size = (TOTAL_MB - MIN_SPACE_BEFORE_MB) * MEBIBYTE;
frame_resizer_base ->set_x_min_space_before( Utils::round( MIN_SPACE_BEFORE_MB / MB_PER_PIXEL ) ) ;
frame_resizer_base->set_size_limits( Utils::round( fs_limits.min_size / (MB_PER_PIXEL * MEBIBYTE) ),
Utils::round( fs_limits.max_size / (MB_PER_PIXEL * MEBIBYTE) ) );
2004-09-19 14:24:53 -06:00
//set new spinbutton ranges
spinbutton_before.set_range( MIN_SPACE_BEFORE_MB,
TOTAL_MB - ceil( fs_limits.min_size / double(MEBIBYTE) ) );
spinbutton_size.set_range( ceil( fs_limits.min_size / double(MEBIBYTE) ),
ceil( fs_limits.max_size / double(MEBIBYTE) ) );
spinbutton_after.set_range( 0,
TOTAL_MB - MIN_SPACE_BEFORE_MB
- ceil( fs_limits.min_size / double(MEBIBYTE) ) );
2004-09-19 14:24:53 -06:00
//set contents of label_minmax
Set_MinMax_Text( ceil( fs_limits.min_size / double(MEBIBYTE) ),
ceil( fs_limits.max_size / double(MEBIBYTE) ) );
2004-09-19 14:24:53 -06:00
}
2004-09-19 14:24:53 -06:00
//set fitting resizer colors
{
Gdk::RGBA color_temp;
//Background color
color_temp.set((combo_type.get_active_row_number() == 2) ? "darkgrey" : "white");
frame_resizer_base->override_default_rgb_unused_color(color_temp);
//Partition color
color_temp.set(Utils::get_color(fs.fstype));
frame_resizer_base->set_rgb_partition_color(color_temp);
}
// Maximum length of the file system label varies according to the selected file system type.
filesystem_label_entry.set_max_length(Utils::get_filesystem_label_maxlength(fs.fstype));
frame_resizer_base->redraw();
2004-09-19 14:24:53 -06:00
}
void Dialog_Partition_New::build_filesystems_combo(bool only_unformatted)
2004-09-19 14:24:53 -06:00
{
g_assert( new_partition != NULL ); // Bug: Not initialised by constructor calling set_data()
combo_filesystem.items().clear();
bool set_first=false;
//fill the file system menu with the file systems (except for extended)
for ( unsigned int t = 0 ; t < FILESYSTEMS .size( ) ; t++ )
2004-10-06 09:32:40 -06:00
{
//skip extended
if (FILESYSTEMS[t].fstype == FS_EXTENDED)
continue ;
combo_filesystem.items().push_back(Utils::get_filesystem_string(FILESYSTEMS[t].fstype));
combo_filesystem.items().back().set_sensitive(
! only_unformatted && FILESYSTEMS[ t ] .create &&
new_partition->get_byte_length() >= get_filesystem_min_limit(FILESYSTEMS[t].fstype));
//use ext4/3/2 as first/second/third choice default file system
//(Depends on ordering in FILESYSTEMS for preference)
if ((FILESYSTEMS[t].fstype == FS_EXT2 ||
FILESYSTEMS[t].fstype == FS_EXT3 ||
FILESYSTEMS[t].fstype == FS_EXT4 ) &&
combo_filesystem.items().back().sensitive() )
{
first_creatable_fs = combo_filesystem.items().size() - 1;
set_first=true;
}
2004-10-06 09:32:40 -06:00
}
2004-09-19 14:24:53 -06:00
//unformatted is always available
combo_filesystem.items().back().set_sensitive(true);
if(!set_first)
{
//find and set first enabled file system as last choice default
for (unsigned int t = 0; t < combo_filesystem.items().size(); t++)
if (combo_filesystem.items()[t].sensitive())
{
first_creatable_fs = t ;
break ;
}
}
2004-09-19 14:24:53 -06:00
}
Byte_Value Dialog_Partition_New::get_filesystem_min_limit( FSType fstype )
{
return GParted_Core::get_filesystem_limits( fstype, *new_partition ).min_size;
}
2004-09-19 14:24:53 -06:00
} //GParted