gparted/src/Win_GParted.cc

3051 lines
110 KiB
C++
Raw Normal View History

/* Copyright (C) 2004 Bart
2015-02-01 13:14:05 -07:00
* Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 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
*/
2004-09-19 14:24:53 -06:00
#include "../include/Win_GParted.h"
#include "../include/Dialog_Progress.h"
#include "../include/DialogFeatures.h"
#include "../include/Dialog_Disklabel.h"
#include "../include/Dialog_Rescue_Data.h"
#include "../include/Dialog_Partition_Resize_Move.h"
#include "../include/Dialog_Partition_Copy.h"
#include "../include/Dialog_Partition_New.h"
#include "../include/Dialog_Partition_Info.h"
#include "../include/Dialog_FileSystem_Label.h"
#include "../include/Dialog_Partition_Name.h"
#include "../include/DialogManageFlags.h"
#include "../include/OperationCopy.h"
#include "../include/OperationCheck.h"
#include "../include/OperationCreate.h"
#include "../include/OperationDelete.h"
#include "../include/OperationFormat.h"
#include "../include/OperationResizeMove.h"
#include "../include/OperationChangeUUID.h"
#include "../include/OperationLabelFileSystem.h"
#include "../include/OperationNamePartition.h"
#include "../include/LVM2_PV_Info.h"
#include "../config.h"
2004-09-19 14:24:53 -06:00
#include <gtkmm/aboutdialog.h>
#include <gtkmm/messagedialog.h>
#include <gtkmm/radiobuttongroup.h>
#include <gtkmm/main.h>
#include <gtkmm/separator.h>
2004-09-19 14:24:53 -06:00
namespace GParted
{
Win_GParted::Win_GParted( const std::vector<Glib::ustring> & user_devices )
2004-09-19 14:24:53 -06:00
{
copied_partition .Reset() ;
selected_partition_ptr = NULL;
2004-09-19 14:24:53 -06:00
new_count = 1;
current_device = 0 ;
OPERATIONSLIST_OPEN = true ;
gparted_core .set_user_devices( user_devices ) ;
MENU_NEW = TOOLBAR_NEW =
MENU_DEL = TOOLBAR_DEL =
MENU_RESIZE_MOVE = TOOLBAR_RESIZE_MOVE =
MENU_COPY = TOOLBAR_COPY =
MENU_PASTE = TOOLBAR_PASTE =
MENU_FORMAT =
MENU_TOGGLE_BUSY =
MENU_MOUNT =
MENU_NAME_PARTITION =
MENU_FLAGS =
MENU_INFO =
MENU_LABEL_PARTITION =
MENU_CHANGE_UUID =
TOOLBAR_UNDO =
TOOLBAR_APPLY = -1 ;
2004-09-19 14:24:53 -06:00
//==== GUI =========================
this ->set_title( _("GParted") );
this ->set_default_size( 775, 500 );
try
{
#ifdef HAVE_SET_DEFAULT_ICON_NAME
this ->set_default_icon_name( "gparted" ) ;
#else
this ->set_icon_from_file( GNOME_ICONDIR "/gparted.png" ) ;
#endif
}
catch ( Glib::Exception & e )
{
std::cout << e .what() << std::endl ;
}
2004-09-19 14:24:53 -06:00
//Pack the main box
this ->add( vbox_main );
2004-09-19 14:24:53 -06:00
//menubar....
init_menubar() ;
2004-09-19 14:24:53 -06:00
vbox_main .pack_start( menubar_main, Gtk::PACK_SHRINK );
2004-09-19 14:24:53 -06:00
//toolbar....
init_toolbar() ;
2004-09-19 14:24:53 -06:00
vbox_main.pack_start( hbox_toolbar, Gtk::PACK_SHRINK );
//drawingarea_visualdisk... ( contains the visual represenation of the disks )
drawingarea_visualdisk .signal_partition_selected .connect(
sigc::mem_fun( this, &Win_GParted::on_partition_selected ) ) ;
drawingarea_visualdisk .signal_partition_activated .connect(
sigc::mem_fun( this, &Win_GParted::on_partition_activated ) ) ;
drawingarea_visualdisk .signal_popup_menu .connect(
sigc::mem_fun( this, &Win_GParted::on_partition_popup_menu ) );
vbox_main .pack_start( drawingarea_visualdisk, Gtk::PACK_SHRINK ) ;
2004-09-19 14:24:53 -06:00
//hpaned_main (NOTE: added to vpaned_main)
init_hpaned_main() ;
2004-09-19 14:24:53 -06:00
vpaned_main .pack1( hpaned_main, true, true ) ;
2004-09-19 14:24:53 -06:00
//vpaned_main....
vbox_main .pack_start( vpaned_main );
2004-09-19 14:24:53 -06:00
//device info...
init_device_info() ;
2004-09-19 14:24:53 -06:00
//operationslist...
hbox_operations .signal_undo .connect( sigc::mem_fun( this, &Win_GParted::activate_undo ) ) ;
hbox_operations .signal_clear .connect( sigc::mem_fun( this, &Win_GParted::clear_operationslist ) ) ;
hbox_operations .signal_apply .connect( sigc::mem_fun( this, &Win_GParted::activate_apply ) ) ;
hbox_operations .signal_close .connect( sigc::mem_fun( this, &Win_GParted::close_operationslist ) ) ;
2004-09-19 14:24:53 -06:00
vpaned_main .pack2( hbox_operations, true, true ) ;
//statusbar...
pulsebar .set_pulse_step( 0.01 );
statusbar .add( pulsebar );
vbox_main .pack_start( statusbar, Gtk::PACK_SHRINK );
this ->show_all_children();
//make sure harddisk information is closed..
hpaned_main .get_child1() ->hide() ;
2004-09-19 14:24:53 -06:00
}
void Win_GParted::init_menubar()
2004-09-19 14:24:53 -06:00
{
//fill menubar_main and connect callbacks
2004-09-19 14:24:53 -06:00
//gparted
menu = manage( new Gtk::Menu() ) ;
2004-09-19 14:24:53 -06:00
image = manage( new Gtk::Image( Gtk::Stock::REFRESH, Gtk::ICON_SIZE_MENU ) );
menu ->items() .push_back( Gtk::Menu_Helpers::ImageMenuElem(
_("_Refresh Devices"),
Gtk::AccelKey("<control>r"),
*image,
sigc::mem_fun(*this, &Win_GParted::menu_gparted_refresh_devices) ) );
image = manage( new Gtk::Image( Gtk::Stock::HARDDISK, Gtk::ICON_SIZE_MENU ) );
menu ->items() .push_back( Gtk::Menu_Helpers::ImageMenuElem( _("_Devices"), *image ) ) ;
menu ->items() .push_back( Gtk::Menu_Helpers::SeparatorElem( ) );
menu ->items() .push_back( Gtk::Menu_Helpers::StockMenuElem(
Gtk::Stock::QUIT, sigc::mem_fun(*this, &Win_GParted::menu_gparted_quit) ) );
menubar_main .items() .push_back( Gtk::Menu_Helpers::MenuElem( _("_GParted"), *menu ) );
//edit
menu = manage( new Gtk::Menu() ) ;
menu ->items() .push_back( Gtk::Menu_Helpers::ImageMenuElem(
_("_Undo Last Operation"),
Gtk::AccelKey("<control>z"),
* manage( new Gtk::Image( Gtk::Stock::UNDO, Gtk::ICON_SIZE_MENU ) ),
sigc::mem_fun(*this, &Win_GParted::activate_undo) ) );
menu ->items() .push_back( Gtk::Menu_Helpers::ImageMenuElem(
_("_Clear All Operations"),
* manage( new Gtk::Image( Gtk::Stock::CLEAR, Gtk::ICON_SIZE_MENU ) ),
sigc::mem_fun(*this, &Win_GParted::clear_operationslist) ) );
menu ->items() .push_back( Gtk::Menu_Helpers::ImageMenuElem(
_("_Apply All Operations"),
Gtk::AccelKey(GDK_Return, Gdk::CONTROL_MASK),
* manage( new Gtk::Image( Gtk::Stock::APPLY, Gtk::ICON_SIZE_MENU ) ),
sigc::mem_fun(*this, &Win_GParted::activate_apply) ) );
menubar_main .items() .push_back( Gtk::Menu_Helpers::MenuElem( _("_Edit"), *menu ) );
2004-09-27 14:12:47 -06:00
//view
menu = manage( new Gtk::Menu() ) ;
menu ->items() .push_back( Gtk::Menu_Helpers::CheckMenuElem(
_("Device _Information"), sigc::mem_fun(*this, &Win_GParted::menu_view_harddisk_info) ) );
menu ->items() .push_back( Gtk::Menu_Helpers::CheckMenuElem(
_("Pending _Operations"), sigc::mem_fun(*this, &Win_GParted::menu_view_operations) ) );
menubar_main .items() .push_back( Gtk::Menu_Helpers::MenuElem( _("_View"), *menu ) );
menu ->items() .push_back( Gtk::Menu_Helpers::SeparatorElem( ) );
menu ->items() .push_back( Gtk::Menu_Helpers::MenuElem(
_("_File System Support"), sigc::mem_fun( *this, &Win_GParted::menu_gparted_features ) ) );
//device
menu = manage( new Gtk::Menu() ) ;
menu ->items() .push_back( Gtk::Menu_Helpers::MenuElem( Glib::ustring( _("_Create Partition Table") ) + "...",
sigc::mem_fun(*this, &Win_GParted::activate_disklabel) ) );
menu ->items() .push_back( Gtk::Menu_Helpers::MenuElem( Glib::ustring( _("_Attempt Data Rescue") ) + "...",
sigc::mem_fun(*this, &Win_GParted::activate_attempt_rescue_data) ) );
menubar_main .items() .push_back( Gtk::Menu_Helpers::MenuElem( _("_Device"), *menu ) );
//partition
init_partition_menu() ;
menubar_main .items() .push_back( Gtk::Menu_Helpers::MenuElem( _("_Partition"), menu_partition ) );
2004-09-19 14:24:53 -06:00
//help
menu = manage( new Gtk::Menu() ) ;
menu ->items() .push_back( Gtk::Menu_Helpers::ImageMenuElem(
_("_Contents"),
Gtk::AccelKey("F1"),
* manage( new Gtk::Image( Gtk::Stock::HELP, Gtk::ICON_SIZE_MENU ) ),
sigc::mem_fun(*this, &Win_GParted::menu_help_contents) ) );
menu ->items() .push_back( Gtk::Menu_Helpers::SeparatorElem( ) );
menu ->items() .push_back( Gtk::Menu_Helpers::StockMenuElem(
Gtk::Stock::ABOUT, sigc::mem_fun(*this, &Win_GParted::menu_help_about) ) );
menubar_main.items() .push_back( Gtk::Menu_Helpers::MenuElem(_("_Help"), *menu ) );
2004-09-19 14:24:53 -06:00
}
void Win_GParted::init_toolbar()
2004-09-19 14:24:53 -06:00
{
int index = 0 ;
//initialize and pack toolbar_main
2004-09-19 14:24:53 -06:00
hbox_toolbar.pack_start( toolbar_main );
2004-09-19 14:24:53 -06:00
//NEW and DELETE
image = manage( new Gtk::Image( Gtk::Stock::NEW, Gtk::ICON_SIZE_BUTTON ) );
/*TO TRANSLATORS: "New" is a tool bar item for partition actions. */
Glib::ustring str_temp = _("New") ;
toolbutton = Gtk::manage(new Gtk::ToolButton( *image, str_temp ));
toolbutton ->signal_clicked() .connect( sigc::mem_fun( *this, &Win_GParted::activate_new ) );
toolbar_main .append( *toolbutton );
TOOLBAR_NEW = index++ ;
toolbutton ->set_tooltip(tooltips, _("Create a new partition in the selected unallocated space") );
2004-09-19 14:24:53 -06:00
toolbutton = Gtk::manage(new Gtk::ToolButton(Gtk::Stock::DELETE));
toolbutton ->signal_clicked().connect( sigc::mem_fun(*this, &Win_GParted::activate_delete) );
toolbar_main.append(*toolbutton);
TOOLBAR_DEL = index++ ;
toolbutton ->set_tooltip(tooltips, _("Delete the selected partition") );
2004-09-19 14:24:53 -06:00
toolbar_main.append( *(Gtk::manage(new Gtk::SeparatorToolItem)) );
index++ ;
2004-09-19 14:24:53 -06:00
//RESIZE/MOVE
image = manage( new Gtk::Image( Gtk::Stock::GOTO_LAST, Gtk::ICON_SIZE_BUTTON ) );
str_temp = _("Resize/Move") ;
//Condition string split and Undo button.
// for longer translated string, split string in two and skip the Undo button to permit full toolbar to display
// FIXME: Is there a better way to do this, perhaps without the conditional? At the moment this seems to be the best compromise.
bool display_undo = true ;
if( str_temp .length() > 14 ) {
size_t index = str_temp .find( "/" ) ;
if ( index != Glib::ustring::npos ) {
str_temp .replace( index, 1, "\n/" ) ;
display_undo = false ;
}
}
toolbutton = Gtk::manage(new Gtk::ToolButton( *image, str_temp ));
toolbutton ->signal_clicked().connect( sigc::mem_fun(*this, &Win_GParted::activate_resize) );
toolbar_main.append(*toolbutton);
TOOLBAR_RESIZE_MOVE = index++ ;
toolbutton ->set_tooltip(tooltips, _("Resize/Move the selected partition") );
2004-09-19 14:24:53 -06:00
toolbar_main.append( *(Gtk::manage(new Gtk::SeparatorToolItem)) );
index++ ;
2004-09-19 14:24:53 -06:00
//COPY and PASTE
toolbutton = Gtk::manage(new Gtk::ToolButton(Gtk::Stock::COPY));
toolbutton ->signal_clicked().connect( sigc::mem_fun(*this, &Win_GParted::activate_copy) );
toolbar_main.append(*toolbutton);
TOOLBAR_COPY = index++ ;
toolbutton ->set_tooltip(tooltips, _("Copy the selected partition to the clipboard") );
2004-09-19 14:24:53 -06:00
toolbutton = Gtk::manage(new Gtk::ToolButton(Gtk::Stock::PASTE));
toolbutton ->signal_clicked().connect( sigc::mem_fun(*this, &Win_GParted::activate_paste) );
toolbar_main.append(*toolbutton);
TOOLBAR_PASTE = index++ ;
toolbutton ->set_tooltip(tooltips, _("Paste the partition from the clipboard") );
2004-09-19 14:24:53 -06:00
toolbar_main.append( *(Gtk::manage(new Gtk::SeparatorToolItem)) );
index++ ;
2004-09-19 14:24:53 -06:00
//UNDO and APPLY
if ( display_undo ) {
//Undo button is displayed only if translated language "Resize/Move" is not too long. See above setting of this condition.
toolbutton = Gtk::manage(new Gtk::ToolButton(Gtk::Stock::UNDO));
toolbutton ->signal_clicked().connect( sigc::mem_fun(*this, &Win_GParted::activate_undo) );
toolbar_main.append(*toolbutton);
TOOLBAR_UNDO = index++ ;
toolbutton ->set_sensitive( false );
toolbutton ->set_tooltip(tooltips, _("Undo Last Operation") );
}
2004-09-19 14:24:53 -06:00
toolbutton = Gtk::manage(new Gtk::ToolButton(Gtk::Stock::APPLY));
toolbutton ->signal_clicked().connect( sigc::mem_fun(*this, &Win_GParted::activate_apply) );
toolbar_main.append(*toolbutton);
TOOLBAR_APPLY = index++ ;
toolbutton ->set_sensitive( false );
toolbutton ->set_tooltip(tooltips, _("Apply All Operations") );
//initialize and pack combo_devices
liststore_devices = Gtk::ListStore::create( treeview_devices_columns ) ;
combo_devices .set_model( liststore_devices ) ;
combo_devices .pack_start( treeview_devices_columns .icon, false ) ;
combo_devices .pack_start( treeview_devices_columns .device ) ;
combo_devices .pack_start( treeview_devices_columns .size, false ) ;
Prevent flashing redraw of the devices combobox (#696149) The device combobox was getting drawn blank, then getting drawn again with the selected device. This was happening because at the start of Win_GParted::refresh_combo_devices() the GTK model behind the combobox, liststore_devices, was cleared, changing the active item, causing the combobox to get redrawn empty. After the GTK model had been repopulated the active item was reset causing the comboxbox to get redrawn again, now showing the selected device. Call flow: Win_GParted::refresh_combo_devices() liststore_devices->clear() //Gtk::Combobox emits signal_change. Registered callbacks //called. Win_GParted::combo_devices_changed() Win_GParted::Refresh_Visual() ... ... combo_devices.set_active(current_device); //Gtk::Combobox emits signal_change. Registered callbacks //called. Win_GParted::combo_devices_changed() Win_GParted::Refresh_Visual() ... This has always been the case, since the device combobox was first added to GParted before version 0.1 by commit: 3a4b43e0adb3974dea656fcbe90d5e191e0e9784 replaced deprecated OptionMenu with ComboBox ... Fix by temporarily blocking the devices comboxbox from emitting signal_changed while the GTK model behind the combobox is recreated. However, since automatic selection of the largest free space was added [1] in GParted 0.15.0, a more noticeable flashing redraw issue was caused in which the partition graphic and partition list were both drawn blank then redrawn fully populated. Some distributions were not affected by this at all, some only experienced a single flash and others suffered from two or more flashing redraws. Some affected distributions: CentOS 5.10, 6.5, 7.0, Debian 6, Fedora 14, 19, 20, Ubuntu 13.10, Xubuntu 14.04 LTS. Did not occur on Kubuntu 12.04 LTS. [1] 5b53c12f6ee12312a9bacb44b8b05b12a540d3d6 Select largest unallocated partition by default (#667365) Bug #696149 - Double refresh of display introduced with default unallocated space
2014-08-22 04:35:39 -06:00
combo_devices_changed_connection =
combo_devices .signal_changed() .connect( sigc::mem_fun(*this, &Win_GParted::combo_devices_changed) );
hbox_toolbar .pack_start( combo_devices, Gtk::PACK_SHRINK ) ;
2004-09-19 14:24:53 -06:00
}
void Win_GParted::init_partition_menu()
2004-09-19 14:24:53 -06:00
{
int index = 0 ;
//fill menu_partition
image = manage( new Gtk::Image( Gtk::Stock::NEW, Gtk::ICON_SIZE_MENU ) );
menu_partition .items() .push_back(
/*TO TRANSLATORS: "_New" is a sub menu item for the partition menu. */
Gtk::Menu_Helpers::ImageMenuElem( _("_New"),
Gtk::AccelKey( GDK_Insert, Gdk::BUTTON1_MASK),
*image,
sigc::mem_fun(*this, &Win_GParted::activate_new) ) );
MENU_NEW = index++ ;
menu_partition .items() .push_back(
Gtk::Menu_Helpers::StockMenuElem( Gtk::Stock::DELETE,
Gtk::AccelKey( GDK_Delete, Gdk::BUTTON1_MASK ),
sigc::mem_fun(*this, &Win_GParted::activate_delete) ) );
MENU_DEL = index++ ;
menu_partition .items() .push_back( Gtk::Menu_Helpers::SeparatorElem() );
index++ ;
2004-09-19 14:24:53 -06:00
image = manage( new Gtk::Image( Gtk::Stock::GOTO_LAST, Gtk::ICON_SIZE_MENU ) );
menu_partition .items() .push_back(
Gtk::Menu_Helpers::ImageMenuElem( _("_Resize/Move"),
*image,
sigc::mem_fun(*this, &Win_GParted::activate_resize) ) );
MENU_RESIZE_MOVE = index++ ;
menu_partition .items() .push_back( Gtk::Menu_Helpers::SeparatorElem() );
index++ ;
menu_partition .items() .push_back(
Gtk::Menu_Helpers::StockMenuElem( Gtk::Stock::COPY,
sigc::mem_fun(*this, &Win_GParted::activate_copy) ) );
MENU_COPY = index++ ;
menu_partition .items() .push_back(
Gtk::Menu_Helpers::StockMenuElem( Gtk::Stock::PASTE,
sigc::mem_fun(*this, &Win_GParted::activate_paste) ) );
MENU_PASTE = index++ ;
menu_partition .items() .push_back( Gtk::Menu_Helpers::SeparatorElem() );
index++ ;
2004-09-19 14:24:53 -06:00
image = manage( new Gtk::Image( Gtk::Stock::CONVERT, Gtk::ICON_SIZE_MENU ) );
/*TO TRANSLATORS: menuitem which holds a submenu with file systems.. */
menu_partition .items() .push_back(
Gtk::Menu_Helpers::ImageMenuElem( _("_Format to"),
*image,
* create_format_menu() ) ) ;
MENU_FORMAT = index++ ;
menu_partition .items() .push_back( Gtk::Menu_Helpers::SeparatorElem() ) ;
index++ ;
menu_partition .items() .push_back(
//This is a placeholder text. It will be replaced with some other text before it is used
Gtk::Menu_Helpers::MenuElem( "--placeholder--",
sigc::mem_fun( *this, &Win_GParted::toggle_busy_state ) ) );
MENU_TOGGLE_BUSY = index++ ;
/*TO TRANSLATORS: menuitem which holds a submenu with mount points.. */
menu_partition .items() .push_back(
Gtk::Menu_Helpers::MenuElem( _("_Mount on"), * manage( new Gtk::Menu() ) ) ) ;
MENU_MOUNT = index++ ;
menu_partition .items() .push_back( Gtk::Menu_Helpers::SeparatorElem() ) ;
index++ ;
menu_partition.items().push_back(
Gtk::Menu_Helpers::MenuElem( _("_Name Partition"),
sigc::mem_fun( *this, &Win_GParted::activate_name_partition ) ) );
MENU_NAME_PARTITION = index++;
menu_partition .items() .push_back(
Gtk::Menu_Helpers::MenuElem( _("M_anage Flags"),
sigc::mem_fun( *this, &Win_GParted::activate_manage_flags ) ) );
MENU_FLAGS = index++ ;
menu_partition .items() .push_back(
Gtk::Menu_Helpers::MenuElem( _("C_heck"),
sigc::mem_fun( *this, &Win_GParted::activate_check ) ) );
MENU_CHECK = index++ ;
menu_partition .items() .push_back(
Gtk::Menu_Helpers::MenuElem( _("_Label File System"),
sigc::mem_fun( *this, &Win_GParted::activate_label_filesystem ) ) );
MENU_LABEL_PARTITION = index++ ;
menu_partition .items() .push_back(
Gtk::Menu_Helpers::MenuElem( _("New UU_ID"),
sigc::mem_fun( *this, &Win_GParted::activate_change_uuid ) ) );
MENU_CHANGE_UUID = index++ ;
menu_partition .items() .push_back( Gtk::Menu_Helpers::SeparatorElem() ) ;
index++ ;
menu_partition .items() .push_back(
Gtk::Menu_Helpers::StockMenuElem( Gtk::Stock::DIALOG_INFO,
sigc::mem_fun(*this, &Win_GParted::activate_info) ) );
MENU_INFO = index++ ;
menu_partition .accelerate( *this ) ;
2004-09-19 14:24:53 -06:00
}
//Create the Partition --> Format to --> (file system list) menu
Gtk::Menu * Win_GParted::create_format_menu()
2004-09-19 14:24:53 -06:00
{
const std::vector<FS> & fss = gparted_core .get_filesystems() ;
menu = manage( new Gtk::Menu() ) ;
for ( unsigned int t = 0 ; t < fss .size() ; t++ )
2004-09-19 14:24:53 -06:00
{
if ( GParted_Core::supported_filesystem( fss[t].filesystem ) )
create_format_menu_add_item( fss[t].filesystem, fss[t].create );
2004-09-19 14:24:53 -06:00
}
//Add cleared at the end of the list
create_format_menu_add_item( FS_CLEARED, true ) ;
return menu ;
2004-09-19 14:24:53 -06:00
}
//Add one entry to the Partition --> Format to --> (file system list) menu
void Win_GParted::create_format_menu_add_item( FILESYSTEM filesystem, bool activate )
{
hbox = manage( new Gtk::HBox() ) ;
//the colored square
hbox ->pack_start( * manage( new Gtk::Image( Utils::get_color_as_pixbuf( filesystem, 16, 16 ) ) ),
Gtk::PACK_SHRINK ) ;
//the label...
hbox ->pack_start( * Utils::mk_label( " " + Utils::get_filesystem_string( filesystem ) ),
Gtk::PACK_SHRINK ) ;
menu ->items() .push_back( * manage( new Gtk::MenuItem( *hbox ) ) ) ;
if ( activate )
menu ->items() .back() .signal_activate() .connect(
sigc::bind<GParted::FILESYSTEM>( sigc::mem_fun( *this, &Win_GParted::activate_format ),
filesystem ) ) ;
else
menu ->items() .back() .set_sensitive( false ) ;
}
2004-09-19 14:24:53 -06:00
void Win_GParted::init_device_info()
{
vbox_info.set_spacing( 5 );
2006-03-28 05:40:29 -07:00
int top = 0, bottom = 1;
//title
vbox_info .pack_start(
* Utils::mk_label( " <b>" + static_cast<Glib::ustring>( _("Device Information") ) + "</b>" ),
2006-03-28 05:40:29 -07:00
Gtk::PACK_SHRINK );
//GENERAL DEVICE INFO
2004-09-19 14:24:53 -06:00
table = manage( new Gtk::Table() ) ;
table ->set_col_spacings( 10 ) ;
//model
2006-03-28 05:40:29 -07:00
table ->attach( * Utils::mk_label( " <b>" + static_cast<Glib::ustring>( _("Model:") ) + "</b>" ),
0, 1,
top, bottom,
Gtk::FILL ) ;
device_info .push_back( Utils::mk_label( "", true, false, true ) ) ;
2006-03-28 05:40:29 -07:00
table ->attach( * device_info .back(), 1, 2, top++, bottom++, Gtk::FILL ) ;
//size
2006-03-28 05:40:29 -07:00
table ->attach( * Utils::mk_label( " <b>" + static_cast<Glib::ustring>( _("Size:") ) + "</b>" ),
0, 1,
top, bottom,
Gtk::FILL ) ;
device_info .push_back( Utils::mk_label( "", true, false, true ) ) ;
2006-03-28 05:40:29 -07:00
table ->attach( * device_info .back(), 1, 2, top++, bottom++, Gtk::FILL ) ;
//path
table ->attach( * Utils::mk_label( " <b>" + static_cast<Glib::ustring>( _("Path:") ) + "</b>" ),
2006-03-28 05:40:29 -07:00
0, 1,
top, bottom,
Gtk::FILL ) ;
device_info .push_back( Utils::mk_label( "", true, false, true ) ) ;
2006-03-28 05:40:29 -07:00
table ->attach( * device_info .back(), 1, 2, top++, bottom++, Gtk::FILL ) ;
vbox_info .pack_start( *table, Gtk::PACK_SHRINK );
//DETAILED DEVICE INFO
2006-03-28 05:40:29 -07:00
top = 0 ; bottom = 1;
table = manage( new Gtk::Table() ) ;
table ->set_col_spacings( 10 ) ;
//one blank line
table ->attach( * Utils::mk_label( "" ), 1, 2, top++, bottom++, Gtk::FILL );
//disktype
table ->attach( * Utils::mk_label( " <b>" + static_cast<Glib::ustring>( _("Partition table:") ) + "</b>" ),
2006-03-28 05:40:29 -07:00
0, 1,
top, bottom,
Gtk::FILL );
device_info .push_back( Utils::mk_label( "", true, false, true ) ) ;
2006-03-28 05:40:29 -07:00
table ->attach( * device_info .back(), 1, 2, top++, bottom++, Gtk::FILL ) ;
//heads
2006-03-28 05:40:29 -07:00
table ->attach( * Utils::mk_label( " <b>" + static_cast<Glib::ustring>( _("Heads:") ) + "</b>" ),
0, 1,
top, bottom,
Gtk::FILL ) ;
device_info .push_back( Utils::mk_label( "", true, false, true ) ) ;
2006-03-28 05:40:29 -07:00
table ->attach( * device_info .back(), 1, 2, top++, bottom++, Gtk::FILL ) ;
//sectors/track
table ->attach( * Utils::mk_label( " <b>" + static_cast<Glib::ustring>( _("Sectors/track:") ) + "</b>" ),
2006-03-28 05:40:29 -07:00
0, 1,
top, bottom,
Gtk::FILL ) ;
device_info .push_back( Utils::mk_label( "", true, false, true ) ) ;
2006-03-28 05:40:29 -07:00
table ->attach( * device_info .back(), 1, 2, top++, bottom++, Gtk::FILL );
//cylinders
2006-03-28 05:40:29 -07:00
table ->attach( * Utils::mk_label( " <b>" + static_cast<Glib::ustring>( _("Cylinders:") ) + "</b>" ),
0, 1,
top, bottom,
Gtk::FILL ) ;
device_info .push_back( Utils::mk_label( "", true, false, true ) ) ;
2006-03-28 05:40:29 -07:00
table ->attach( * device_info .back(), 1, 2, top++, bottom++, Gtk::FILL ) ;
//total sectors
table ->attach( * Utils::mk_label( " <b>" + static_cast<Glib::ustring>( _("Total sectors:") ) + "</b>" ),
2006-03-28 05:40:29 -07:00
0, 1,
top, bottom,
Gtk::FILL );
device_info .push_back( Utils::mk_label( "", true, false, true ) ) ;
2006-03-28 05:40:29 -07:00
table ->attach( * device_info .back(), 1, 2, top++, bottom++, Gtk::FILL ) ;
//sector size
table ->attach( * Utils::mk_label( " <b>" + static_cast<Glib::ustring>( _("Sector size:") ) + "</b>" ),
0, 1,
top, bottom,
Gtk::FILL );
device_info .push_back( Utils::mk_label( "", true, false, true ) ) ;
table ->attach( * device_info .back(), 1, 2, top++, bottom++, Gtk::FILL ) ;
vbox_info .pack_start( *table, Gtk::PACK_SHRINK );
2004-09-19 14:24:53 -06:00
}
void Win_GParted::init_hpaned_main()
2004-09-19 14:24:53 -06:00
{
//left scrollwindow (holds device info)
scrollwindow = manage( new Gtk::ScrolledWindow() ) ;
scrollwindow ->set_shadow_type( Gtk::SHADOW_ETCHED_IN );
scrollwindow ->set_policy( Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC );
2004-09-19 14:24:53 -06:00
hpaned_main .pack1( *scrollwindow, true, true );
2004-09-19 14:24:53 -06:00
scrollwindow ->add( vbox_info );
//right scrollwindow (holds treeview with partitions)
scrollwindow = manage( new Gtk::ScrolledWindow() ) ;
scrollwindow ->set_shadow_type( Gtk::SHADOW_ETCHED_IN );
scrollwindow ->set_policy( Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC );
//connect signals and add treeview_detail
treeview_detail .signal_partition_selected .connect( sigc::mem_fun( this, &Win_GParted::on_partition_selected ) );
treeview_detail .signal_partition_activated .connect( sigc::mem_fun( this, &Win_GParted::on_partition_activated ) );
treeview_detail .signal_popup_menu .connect( sigc::mem_fun( this, &Win_GParted::on_partition_popup_menu ) );
scrollwindow ->add( treeview_detail );
hpaned_main .pack2( *scrollwindow, true, true );
2004-09-19 14:24:53 -06:00
}
void Win_GParted::refresh_combo_devices()
{
Prevent flashing redraw of the devices combobox (#696149) The device combobox was getting drawn blank, then getting drawn again with the selected device. This was happening because at the start of Win_GParted::refresh_combo_devices() the GTK model behind the combobox, liststore_devices, was cleared, changing the active item, causing the combobox to get redrawn empty. After the GTK model had been repopulated the active item was reset causing the comboxbox to get redrawn again, now showing the selected device. Call flow: Win_GParted::refresh_combo_devices() liststore_devices->clear() //Gtk::Combobox emits signal_change. Registered callbacks //called. Win_GParted::combo_devices_changed() Win_GParted::Refresh_Visual() ... ... combo_devices.set_active(current_device); //Gtk::Combobox emits signal_change. Registered callbacks //called. Win_GParted::combo_devices_changed() Win_GParted::Refresh_Visual() ... This has always been the case, since the device combobox was first added to GParted before version 0.1 by commit: 3a4b43e0adb3974dea656fcbe90d5e191e0e9784 replaced deprecated OptionMenu with ComboBox ... Fix by temporarily blocking the devices comboxbox from emitting signal_changed while the GTK model behind the combobox is recreated. However, since automatic selection of the largest free space was added [1] in GParted 0.15.0, a more noticeable flashing redraw issue was caused in which the partition graphic and partition list were both drawn blank then redrawn fully populated. Some distributions were not affected by this at all, some only experienced a single flash and others suffered from two or more flashing redraws. Some affected distributions: CentOS 5.10, 6.5, 7.0, Debian 6, Fedora 14, 19, 20, Ubuntu 13.10, Xubuntu 14.04 LTS. Did not occur on Kubuntu 12.04 LTS. [1] 5b53c12f6ee12312a9bacb44b8b05b12a540d3d6 Select largest unallocated partition by default (#667365) Bug #696149 - Double refresh of display introduced with default unallocated space
2014-08-22 04:35:39 -06:00
// Temporarily block the on change callback while re-creating the device list
// behind the combobox to prevent flashing redraw by being redrawn with an empty
// device list.
combo_devices_changed_connection .block();
liststore_devices ->clear() ;
menu = manage( new Gtk::Menu() ) ;
Gtk::RadioButtonGroup radio_group ;
for ( unsigned int i = 0 ; i < devices .size( ) ; i++ )
{
//combo...
treerow = *( liststore_devices ->append() ) ;
treerow[ treeview_devices_columns .icon ] =
render_icon( Gtk::Stock::HARDDISK, Gtk::ICON_SIZE_LARGE_TOOLBAR ) ;
treerow[ treeview_devices_columns .device ] = devices[ i ] .get_path() ;
treerow[ treeview_devices_columns .size ] = "(" + Utils::format_size( devices[ i ] .length, devices[ i ] .sector_size ) + ")" ;
//devices submenu....
hbox = manage( new Gtk::HBox() ) ;
hbox ->pack_start( * Utils::mk_label( devices[ i ] .get_path() ), Gtk::PACK_EXPAND_WIDGET ) ;
hbox ->pack_start( * Utils::mk_label( " (" + Utils::format_size( devices[ i ] .length, devices[ i ] .sector_size ) + ")" ),
Gtk::PACK_SHRINK ) ;
menu ->items() .push_back( * manage( new Gtk::RadioMenuItem( radio_group ) ) ) ;
menu ->items() .back() .add( *hbox ) ;
menu ->items() .back() .signal_activate() .connect(
sigc::bind<unsigned int>( sigc::mem_fun(*this, &Win_GParted::radio_devices_changed), i ) ) ;
2004-09-19 14:24:53 -06:00
}
menubar_main .items()[ 0 ] .get_submenu() ->items()[ 1 ] .remove_submenu() ;
if ( menu ->items() .size() )
{
menu ->show_all() ;
menubar_main .items()[ 0 ] .get_submenu() ->items()[ 1 ] .set_submenu( *menu ) ;
}
Prevent flashing redraw of the devices combobox (#696149) The device combobox was getting drawn blank, then getting drawn again with the selected device. This was happening because at the start of Win_GParted::refresh_combo_devices() the GTK model behind the combobox, liststore_devices, was cleared, changing the active item, causing the combobox to get redrawn empty. After the GTK model had been repopulated the active item was reset causing the comboxbox to get redrawn again, now showing the selected device. Call flow: Win_GParted::refresh_combo_devices() liststore_devices->clear() //Gtk::Combobox emits signal_change. Registered callbacks //called. Win_GParted::combo_devices_changed() Win_GParted::Refresh_Visual() ... ... combo_devices.set_active(current_device); //Gtk::Combobox emits signal_change. Registered callbacks //called. Win_GParted::combo_devices_changed() Win_GParted::Refresh_Visual() ... This has always been the case, since the device combobox was first added to GParted before version 0.1 by commit: 3a4b43e0adb3974dea656fcbe90d5e191e0e9784 replaced deprecated OptionMenu with ComboBox ... Fix by temporarily blocking the devices comboxbox from emitting signal_changed while the GTK model behind the combobox is recreated. However, since automatic selection of the largest free space was added [1] in GParted 0.15.0, a more noticeable flashing redraw issue was caused in which the partition graphic and partition list were both drawn blank then redrawn fully populated. Some distributions were not affected by this at all, some only experienced a single flash and others suffered from two or more flashing redraws. Some affected distributions: CentOS 5.10, 6.5, 7.0, Debian 6, Fedora 14, 19, 20, Ubuntu 13.10, Xubuntu 14.04 LTS. Did not occur on Kubuntu 12.04 LTS. [1] 5b53c12f6ee12312a9bacb44b8b05b12a540d3d6 Select largest unallocated partition by default (#667365) Bug #696149 - Double refresh of display introduced with default unallocated space
2014-08-22 04:35:39 -06:00
combo_devices_changed_connection .unblock();
combo_devices .set_active( current_device ) ;
}
bool Win_GParted::pulsebar_pulse()
{
pulsebar.pulse();
Glib::ustring tmp_msg = gparted_core .get_thread_status_message() ;
if ( tmp_msg != "" ) {
statusbar.pop();
statusbar.push( tmp_msg );
}
return true;
}
void Win_GParted::show_pulsebar( const Glib::ustring & status_message )
{
pulsebar .show();
statusbar .push( status_message) ;
//disable all input stuff
toolbar_main .set_sensitive( false ) ;
menubar_main .set_sensitive( false ) ;
combo_devices .set_sensitive( false ) ;
menu_partition .set_sensitive( false ) ;
treeview_detail .set_sensitive( false ) ;
drawingarea_visualdisk .set_sensitive( false ) ;
// connect pulse update timer
pulsetimer = Glib::signal_timeout().connect( sigc::mem_fun(*this, &Win_GParted::pulsebar_pulse), 100 );
}
void Win_GParted::hide_pulsebar()
{
pulsetimer.disconnect();
pulsebar .hide();
statusbar .pop() ;
//enable all disabled stuff
toolbar_main .set_sensitive( true ) ;
menubar_main .set_sensitive( true ) ;
combo_devices .set_sensitive( true ) ;
menu_partition .set_sensitive( true ) ;
treeview_detail .set_sensitive( true ) ;
drawingarea_visualdisk .set_sensitive( true ) ;
2004-09-19 14:24:53 -06:00
}
void Win_GParted::Fill_Label_Device_Info( bool clear )
2004-09-19 14:24:53 -06:00
{
if ( clear )
for ( unsigned int t = 0 ; t < device_info .size( ) ; t++ )
device_info[ t ] ->set_text( "" ) ;
else
{
short t = 0;
//global info...
device_info[ t++ ] ->set_text( devices[ current_device ] .model ) ;
device_info[ t++ ] ->set_text( Utils::format_size( devices[ current_device ] .length, devices[ current_device ] .sector_size ) ) ;
device_info[ t++ ] ->set_text( Glib::build_path( "\n", devices[ current_device ] .get_paths() ) ) ;
//detailed info
device_info[ t++ ] ->set_text( devices[ current_device ] .disktype ) ;
device_info[ t++ ] ->set_text( Utils::num_to_str( devices[ current_device ] .heads ) );
device_info[ t++ ] ->set_text( Utils::num_to_str( devices[ current_device ] .sectors ) );
device_info[ t++ ] ->set_text( Utils::num_to_str( devices[ current_device ] .cylinders ) );
device_info[ t++ ] ->set_text( Utils::num_to_str( devices[ current_device ] .length ) );
device_info[ t++ ] ->set_text( Utils::num_to_str( devices[ current_device ] .sector_size ) );
}
2004-09-19 14:24:53 -06:00
}
bool Win_GParted::on_delete_event( GdkEventAny *event )
2004-09-19 14:24:53 -06:00
{
return ! Quit_Check_Operations();
}
2004-09-19 14:24:53 -06:00
void Win_GParted::Add_Operation( Operation * operation, int index )
2004-09-19 14:24:53 -06:00
{
if ( operation )
{
Glib::ustring error ;
Fix uninitialised read when pasting into an existing partition GParted_Core::set_device_partitions() creates and initialises the partition objects based on the partitions on the disk using partition.Reset() and partition.Set(). These methods never set the alignment attribute. Copy and pasting into an existing partition calls GParted_Core:: snap_to_alignment() to adjust the start and end of the newly created in memory partition object. When pasting into unallocated space the user has selected the required alignment and this is exactly what is needed. However when pasting into an existing partition the in memory partition object should always match the actual partition boundaries on disk. Unfortunately the partition boundaries are adjusted based on reading the uninitialised alignment attribute. Initialise the alignment attribute of newly created partition objects to ALIGN_STRICT. Also, when pasting into an existing partition set the alignment of that partition object to ALIGN_STRICT so that no boundary adjustment is performed. valgrind: ==6845== Conditional jump or move depends on uninitialised value(s) ==6845== at 0x80C779A: GParted::GParted_Core::snap_to_alignment(...) (GParted_Core.cc:566) ==6845== by 0x810C115: GParted::Win_GParted::Add_Operation(...) (Win_GParted.cc:692) ==6845== by 0x8110499: GParted::Win_GParted::activate_paste() (Win_GParted.cc:1649) ... ==6845== Conditional jump or move depends on uninitialised value(s) ==6845== at 0x80C77A8: GParted::GParted_Core::snap_to_alignment(...) (GParted_Core.cc:568) ==6845== by 0x810C115: GParted::Win_GParted::Add_Operation(...) (Win_GParted.cc:692) ==6845== by 0x8110499: GParted::Win_GParted::activate_paste() (Win_GParted.cc:1649) GParted_Core.cc: 562 bool GParted_Core::snap_to_alignment( const Device & device, Partition & partition, Glib::ustring & error ) 563 { 564 bool rc = true ; 565 >> 566 if ( partition .alignment == ALIGN_CYLINDER ) 567 rc = snap_to_cylinder( device, partition, error ) ; >> 568 else if ( partition .alignment == ALIGN_MEBIBYTE ) 569 rc = snap_to_mebibyte( device, partition, error ) ; 570 Closes Bug #672654 - Pasting into an existing partition may shrink GParted's representation of it
2012-03-23 09:51:08 -06:00
//Add any of the listed operations without further checking, but
// for the other operations (_CREATE, _RESIZE_MOVE and _COPY)
// ensure the partition is correctly aligned.
//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 ||
operation ->type == OPERATION_CHECK ||
operation ->type == OPERATION_CHANGE_UUID ||
operation ->type == OPERATION_LABEL_FILESYSTEM ||
operation ->type == OPERATION_NAME_PARTITION ||
gparted_core .snap_to_alignment( operation ->device, operation ->partition_new, error )
)
{
operation ->create_description() ;
if ( index >= 0 && index < static_cast<int>( operations .size() ) )
operations .insert( operations .begin() + index, operation ) ;
else
operations .push_back( operation );
}
else
{
Gtk::MessageDialog dialog( *this,
_("Could not add this operation to the list"),
false,
Gtk::MESSAGE_ERROR,
Gtk::BUTTONS_OK,
true );
dialog .set_secondary_text( error ) ;
dialog .run() ;
}
}
2004-09-19 14:24:53 -06:00
}
bool Win_GParted::Merge_Operations( unsigned int first, unsigned int second )
{
if( first >= operations .size() || second >= operations .size() )
return false;
// Two resize operations of the same partition
if ( operations[ first ]->type == OPERATION_RESIZE_MOVE &&
operations[ second ]->type == OPERATION_RESIZE_MOVE &&
operations[ first ]->partition_new == operations[ second ]->partition_original
)
{
operations[ first ]->partition_new = operations[ second ]->partition_new;
operations[ first ]->create_description() ;
remove_operation( second );
return true;
}
// Two label change operations on the same partition
else if ( operations[ first ]->type == OPERATION_LABEL_FILESYSTEM &&
operations[ second ]->type == OPERATION_LABEL_FILESYSTEM &&
operations[ first ]->partition_new == operations[ second ]->partition_original
)
{
operations[ first ]->partition_new.set_filesystem_label(
operations[ second ]->partition_new.get_filesystem_label() );
operations[ first ]->create_description() ;
remove_operation( second );
return true;
}
// Two name change operations on the same partition
else if ( operations[first]->type == OPERATION_NAME_PARTITION &&
operations[second]->type == OPERATION_NAME_PARTITION &&
operations[first]->partition_new == operations[second]->partition_original
)
{
operations[first]->partition_new.name = operations[second]->partition_new.name;
operations[first]->create_description();
remove_operation( second );
return true;
}
// Two change-uuid change operations on the same partition
else if ( operations[ first ]->type == OPERATION_CHANGE_UUID &&
operations[ second ]->type == OPERATION_CHANGE_UUID &&
operations[ first ]->partition_new == operations[ second ]->partition_original
)
{
// Changing half the UUID should not override changing all of it
if ( operations[ first ]->partition_new.uuid == UUID_RANDOM_NTFS_HALF ||
operations[ second ]->partition_new.uuid == UUID_RANDOM )
operations[ first ]->partition_new.uuid = operations[ second ]->partition_new.uuid;
operations[ first ]->create_description() ;
remove_operation( second );
return true;
}
// Two check operations of the same partition
else if ( operations[ first ]->type == OPERATION_CHECK &&
operations[ second ]->type == OPERATION_CHECK &&
operations[ first ]->partition_original == operations[ second ]->partition_original
)
{
remove_operation( second );
return true;
}
// Two format operations of the same partition
else if ( operations[ first ]->type == OPERATION_FORMAT &&
operations[ second ]->type == OPERATION_FORMAT &&
operations[ first ]->partition_new == operations[ second ]->partition_original
)
{
operations[ first ]->partition_new = operations[ second ]->partition_new;
operations[ first ]->create_description() ;
remove_operation( second );
return true;
}
return false;
}
void Win_GParted::Refresh_Visual()
2004-09-19 14:24:53 -06:00
{
// How GParted displays partitions in the GUI and manages the lifetime and
// ownership of that data:
//
// (1) Queries the devices and partitions on disk and refreshes the model.
//
// Data owner: std::vector<Devices> Win_GParted::devices
// Lifetime: Valid until the next call to Refresh_Visual()
// Call chain:
//
// Win_GParted::menu_gparted_refresh_devices()
// gparted_core.set_devices( devices )
// GParted_Core::set_devices_thread( devices )
// devices.clear()
// etc.
//
// (2) Takes a copy of the partitions for the device currently being shown in the
// GUI and visually applies pending operations.
//
// Data owner: std::vector<Partition> Win_GParted::display_partitions
// Lifetime: Valid until the next call to Refresh_Visual().
// Function: Refresh_Visual()
//
// (3) Loads the disk graphic and partition list with partitions to be shown in
// the GUI. Both classes store pointers pointing back to each partition
// object in the vector of display partitions.
//
// Aliases: Win_GParted::display_partitions[]
// Call chain:
//
// Win_GParted::Refresh_Visual()
// drawingarea_visualdisk.load_partitions( display_partitions, device_sectors )
// DrawingAreaVisualDisk::set_static_data( ... )
// treeview_detail.load_partitions( display_partitions )
// TreeView_Detail::create_row()
// TreeView_Detail::load_partitions()
// TreeView_Detail::create_row()
//
// (4) Selecting a partition in the disk graphic or in the partition list fires
// the callback which passes a pointer to the selected partition stored in
// step (3). The callback saves the selected partition and calls the opposite
// class to update it's selection.
//
// Data owner: const Partition * Win_GParted::selected_partition_ptr
// Aliases: Win_GParted::display_partitions[]
// Lifetime: Valid until the next call to Refresh_Visual().
// Call chain: (example clicking on a partition in the disk graphic)
//
// DrawingAreaVisualDisk::on_button_press_event()
// DawingAreaVisualDisk::set_selected( visual_partitions, x, y )
// signal_partition_selected.emit( ..., false )
// Win_GParted::on_partition_selected( partition_ptr, src_is_treeview )
// treeview_detail.set_selected( treestore_detail->children(), partition_ptr )
// TreeView::set_selected( rows, partition_ptr, inside_extended )
// set_cursor()
// TreeView::set_selected( rows, partition_ptr, true )
// set_cursor()
//
// (5) Each new operation is added to the vector of pending operations.
// Eventually Refresh_Visual() is call to update the GUI. This goes to step
// (2) which visually reapplies all pending operations again, including the
// newly added operation.
//
// Data owner: std::vector<Operation *> Win_GParted::operations
// Lifetime: Valid until operations have been applied by
// GParted_Core::apply_operation_to_disk(). Specifically longer
// than the next call call to Refresh_Visual().
// Call chain: (example setting a file system label)
//
// Win_GParted::activate_label_filesystem()
// Win_GParted::Add_Operation( operation )
// Win_GParted::Merge_Operations( ... )
// Win_GParted::show_operationslist()
// Win_GParted::Refresh_Visual()
//
// (6) Selecting a partition as a copy source makes a copy of that partition
// object.
//
// Data owner: Partition Win_GParted::copied_partition
// Lifetime: Valid until GParted closed or the device is removed.
// Specifically longer than the next call to Refresh_Visual().
// Function: Win_GParted::activate_copy()
Move vector of partition objects to a Win_GParted class member (#750168) Win_GParted::Refresh_Visual() used a local variable containing a copy of the vector of partitions in the current device to be displayed. After visually applying pending operations it loaded copies of each partition object into the GUI widgets to display the disk graphic and partition list, DrawingAreaVisualDisk and TreeView_Details classes respectively. When a partition is selected in the UI, again a partition object is copied. Also several of the partition dialogs, including the information dialog, take a copy of the partition object. All these are copies of the same set of partition objects, those currently being displayed in the UI. Move the vector of displayed partitions from a local variable in Refresh_Visual() to a Win_GParted member variable. This will allow for the above cases to be changed to used pointers and references to the same set of partition objects. The valid lifetime of pointers to elements in this partition object vector is from one refresh to the next, when the vector is cleared and repopulated with a new set of partition objects. This is exactly what is needed as the GUI widgets are reloaded on each refresh, the selected partition is reset and none of the partition dialog objects exist. Dialog objects being created and destroyed on each use. On the other hand some copies of partition objects currently being displayed, still need to be made because they have lifetimes which need to last longer than the next call to Refresh_Visual(). Specifically the source of the copy partition and the partition objects copied into the in the list of pending operations. Bug 750168 - Reduce the amount of copying of partition objects
2015-05-15 08:51:26 -06:00
display_partitions = devices[current_device].partitions;
2004-09-19 14:24:53 -06:00
//make all operations visible
for ( unsigned int t = 0 ; t < operations .size(); t++ )
if ( operations[ t ] ->device == devices[ current_device ] )
Move vector of partition objects to a Win_GParted class member (#750168) Win_GParted::Refresh_Visual() used a local variable containing a copy of the vector of partitions in the current device to be displayed. After visually applying pending operations it loaded copies of each partition object into the GUI widgets to display the disk graphic and partition list, DrawingAreaVisualDisk and TreeView_Details classes respectively. When a partition is selected in the UI, again a partition object is copied. Also several of the partition dialogs, including the information dialog, take a copy of the partition object. All these are copies of the same set of partition objects, those currently being displayed in the UI. Move the vector of displayed partitions from a local variable in Refresh_Visual() to a Win_GParted member variable. This will allow for the above cases to be changed to used pointers and references to the same set of partition objects. The valid lifetime of pointers to elements in this partition object vector is from one refresh to the next, when the vector is cleared and repopulated with a new set of partition objects. This is exactly what is needed as the GUI widgets are reloaded on each refresh, the selected partition is reset and none of the partition dialog objects exist. Dialog objects being created and destroyed on each use. On the other hand some copies of partition objects currently being displayed, still need to be made because they have lifetimes which need to last longer than the next call to Refresh_Visual(). Specifically the source of the copy partition and the partition objects copied into the in the list of pending operations. Bug 750168 - Reduce the amount of copying of partition objects
2015-05-15 08:51:26 -06:00
operations[t]->apply_to_visual( display_partitions );
hbox_operations .load_operations( operations ) ;
2004-09-19 14:24:53 -06:00
//set new statusbartext
statusbar .pop() ;
statusbar .push( String::ucompose( ngettext( "%1 operation pending"
, "%1 operations pending"
, operations .size()
)
, operations .size()
)
);
if ( ! operations .size() )
allow_undo_clear_apply( false ) ;
//Count primary partitions for check in max_amount_prim_reached(),
// check for an extended partition and select the largest unallocated
// partition if there is one.
index_extended = -1 ;
primary_count = 0;
selected_partition_ptr = NULL;
Sector largest_unalloc_size = -1 ;
Sector current_size ;
Move vector of partition objects to a Win_GParted class member (#750168) Win_GParted::Refresh_Visual() used a local variable containing a copy of the vector of partitions in the current device to be displayed. After visually applying pending operations it loaded copies of each partition object into the GUI widgets to display the disk graphic and partition list, DrawingAreaVisualDisk and TreeView_Details classes respectively. When a partition is selected in the UI, again a partition object is copied. Also several of the partition dialogs, including the information dialog, take a copy of the partition object. All these are copies of the same set of partition objects, those currently being displayed in the UI. Move the vector of displayed partitions from a local variable in Refresh_Visual() to a Win_GParted member variable. This will allow for the above cases to be changed to used pointers and references to the same set of partition objects. The valid lifetime of pointers to elements in this partition object vector is from one refresh to the next, when the vector is cleared and repopulated with a new set of partition objects. This is exactly what is needed as the GUI widgets are reloaded on each refresh, the selected partition is reset and none of the partition dialog objects exist. Dialog objects being created and destroyed on each use. On the other hand some copies of partition objects currently being displayed, still need to be made because they have lifetimes which need to last longer than the next call to Refresh_Visual(). Specifically the source of the copy partition and the partition objects copied into the in the list of pending operations. Bug 750168 - Reduce the amount of copying of partition objects
2015-05-15 08:51:26 -06:00
for ( unsigned int t = 0 ; t < display_partitions.size() ; t++ )
2004-09-19 14:24:53 -06:00
{
Move vector of partition objects to a Win_GParted class member (#750168) Win_GParted::Refresh_Visual() used a local variable containing a copy of the vector of partitions in the current device to be displayed. After visually applying pending operations it loaded copies of each partition object into the GUI widgets to display the disk graphic and partition list, DrawingAreaVisualDisk and TreeView_Details classes respectively. When a partition is selected in the UI, again a partition object is copied. Also several of the partition dialogs, including the information dialog, take a copy of the partition object. All these are copies of the same set of partition objects, those currently being displayed in the UI. Move the vector of displayed partitions from a local variable in Refresh_Visual() to a Win_GParted member variable. This will allow for the above cases to be changed to used pointers and references to the same set of partition objects. The valid lifetime of pointers to elements in this partition object vector is from one refresh to the next, when the vector is cleared and repopulated with a new set of partition objects. This is exactly what is needed as the GUI widgets are reloaded on each refresh, the selected partition is reset and none of the partition dialog objects exist. Dialog objects being created and destroyed on each use. On the other hand some copies of partition objects currently being displayed, still need to be made because they have lifetimes which need to last longer than the next call to Refresh_Visual(). Specifically the source of the copy partition and the partition objects copied into the in the list of pending operations. Bug 750168 - Reduce the amount of copying of partition objects
2015-05-15 08:51:26 -06:00
if ( display_partitions[t].get_path() == copied_partition.get_path() )
copied_partition = display_partitions[t];
switch ( display_partitions[t].type )
2004-09-19 14:24:53 -06:00
{
case TYPE_PRIMARY:
primary_count++;
break;
case TYPE_EXTENDED:
index_extended = t ;
primary_count++;
Move vector of partition objects to a Win_GParted class member (#750168) Win_GParted::Refresh_Visual() used a local variable containing a copy of the vector of partitions in the current device to be displayed. After visually applying pending operations it loaded copies of each partition object into the GUI widgets to display the disk graphic and partition list, DrawingAreaVisualDisk and TreeView_Details classes respectively. When a partition is selected in the UI, again a partition object is copied. Also several of the partition dialogs, including the information dialog, take a copy of the partition object. All these are copies of the same set of partition objects, those currently being displayed in the UI. Move the vector of displayed partitions from a local variable in Refresh_Visual() to a Win_GParted member variable. This will allow for the above cases to be changed to used pointers and references to the same set of partition objects. The valid lifetime of pointers to elements in this partition object vector is from one refresh to the next, when the vector is cleared and repopulated with a new set of partition objects. This is exactly what is needed as the GUI widgets are reloaded on each refresh, the selected partition is reset and none of the partition dialog objects exist. Dialog objects being created and destroyed on each use. On the other hand some copies of partition objects currently being displayed, still need to be made because they have lifetimes which need to last longer than the next call to Refresh_Visual(). Specifically the source of the copy partition and the partition objects copied into the in the list of pending operations. Bug 750168 - Reduce the amount of copying of partition objects
2015-05-15 08:51:26 -06:00
for ( unsigned int u = 0 ; u < display_partitions[t].logicals.size() ; u ++ )
{
Move vector of partition objects to a Win_GParted class member (#750168) Win_GParted::Refresh_Visual() used a local variable containing a copy of the vector of partitions in the current device to be displayed. After visually applying pending operations it loaded copies of each partition object into the GUI widgets to display the disk graphic and partition list, DrawingAreaVisualDisk and TreeView_Details classes respectively. When a partition is selected in the UI, again a partition object is copied. Also several of the partition dialogs, including the information dialog, take a copy of the partition object. All these are copies of the same set of partition objects, those currently being displayed in the UI. Move the vector of displayed partitions from a local variable in Refresh_Visual() to a Win_GParted member variable. This will allow for the above cases to be changed to used pointers and references to the same set of partition objects. The valid lifetime of pointers to elements in this partition object vector is from one refresh to the next, when the vector is cleared and repopulated with a new set of partition objects. This is exactly what is needed as the GUI widgets are reloaded on each refresh, the selected partition is reset and none of the partition dialog objects exist. Dialog objects being created and destroyed on each use. On the other hand some copies of partition objects currently being displayed, still need to be made because they have lifetimes which need to last longer than the next call to Refresh_Visual(). Specifically the source of the copy partition and the partition objects copied into the in the list of pending operations. Bug 750168 - Reduce the amount of copying of partition objects
2015-05-15 08:51:26 -06:00
switch ( display_partitions[t].logicals[u].type )
{
case TYPE_UNALLOCATED:
Move vector of partition objects to a Win_GParted class member (#750168) Win_GParted::Refresh_Visual() used a local variable containing a copy of the vector of partitions in the current device to be displayed. After visually applying pending operations it loaded copies of each partition object into the GUI widgets to display the disk graphic and partition list, DrawingAreaVisualDisk and TreeView_Details classes respectively. When a partition is selected in the UI, again a partition object is copied. Also several of the partition dialogs, including the information dialog, take a copy of the partition object. All these are copies of the same set of partition objects, those currently being displayed in the UI. Move the vector of displayed partitions from a local variable in Refresh_Visual() to a Win_GParted member variable. This will allow for the above cases to be changed to used pointers and references to the same set of partition objects. The valid lifetime of pointers to elements in this partition object vector is from one refresh to the next, when the vector is cleared and repopulated with a new set of partition objects. This is exactly what is needed as the GUI widgets are reloaded on each refresh, the selected partition is reset and none of the partition dialog objects exist. Dialog objects being created and destroyed on each use. On the other hand some copies of partition objects currently being displayed, still need to be made because they have lifetimes which need to last longer than the next call to Refresh_Visual(). Specifically the source of the copy partition and the partition objects copied into the in the list of pending operations. Bug 750168 - Reduce the amount of copying of partition objects
2015-05-15 08:51:26 -06:00
current_size = display_partitions[t].logicals[u].get_sector_length();
if ( current_size > largest_unalloc_size )
{
largest_unalloc_size = current_size ;
selected_partition_ptr = & display_partitions[t].logicals[u];
}
break;
default:
break;
}
}
break;
case TYPE_UNALLOCATED:
Move vector of partition objects to a Win_GParted class member (#750168) Win_GParted::Refresh_Visual() used a local variable containing a copy of the vector of partitions in the current device to be displayed. After visually applying pending operations it loaded copies of each partition object into the GUI widgets to display the disk graphic and partition list, DrawingAreaVisualDisk and TreeView_Details classes respectively. When a partition is selected in the UI, again a partition object is copied. Also several of the partition dialogs, including the information dialog, take a copy of the partition object. All these are copies of the same set of partition objects, those currently being displayed in the UI. Move the vector of displayed partitions from a local variable in Refresh_Visual() to a Win_GParted member variable. This will allow for the above cases to be changed to used pointers and references to the same set of partition objects. The valid lifetime of pointers to elements in this partition object vector is from one refresh to the next, when the vector is cleared and repopulated with a new set of partition objects. This is exactly what is needed as the GUI widgets are reloaded on each refresh, the selected partition is reset and none of the partition dialog objects exist. Dialog objects being created and destroyed on each use. On the other hand some copies of partition objects currently being displayed, still need to be made because they have lifetimes which need to last longer than the next call to Refresh_Visual(). Specifically the source of the copy partition and the partition objects copied into the in the list of pending operations. Bug 750168 - Reduce the amount of copying of partition objects
2015-05-15 08:51:26 -06:00
current_size = display_partitions[t].get_sector_length();
if ( current_size > largest_unalloc_size )
{
largest_unalloc_size = current_size ;
selected_partition_ptr = & display_partitions[t];
}
break;
default :
break;
2004-09-19 14:24:53 -06:00
}
}
Move vector of partition objects to a Win_GParted class member (#750168) Win_GParted::Refresh_Visual() used a local variable containing a copy of the vector of partitions in the current device to be displayed. After visually applying pending operations it loaded copies of each partition object into the GUI widgets to display the disk graphic and partition list, DrawingAreaVisualDisk and TreeView_Details classes respectively. When a partition is selected in the UI, again a partition object is copied. Also several of the partition dialogs, including the information dialog, take a copy of the partition object. All these are copies of the same set of partition objects, those currently being displayed in the UI. Move the vector of displayed partitions from a local variable in Refresh_Visual() to a Win_GParted member variable. This will allow for the above cases to be changed to used pointers and references to the same set of partition objects. The valid lifetime of pointers to elements in this partition object vector is from one refresh to the next, when the vector is cleared and repopulated with a new set of partition objects. This is exactly what is needed as the GUI widgets are reloaded on each refresh, the selected partition is reset and none of the partition dialog objects exist. Dialog objects being created and destroyed on each use. On the other hand some copies of partition objects currently being displayed, still need to be made because they have lifetimes which need to last longer than the next call to Refresh_Visual(). Specifically the source of the copy partition and the partition objects copied into the in the list of pending operations. Bug 750168 - Reduce the amount of copying of partition objects
2015-05-15 08:51:26 -06:00
// frame visualdisk
drawingarea_visualdisk.load_partitions( display_partitions, devices[current_device].length );
// treeview details
treeview_detail.load_partitions( display_partitions );
set_valid_operations() ;
// Process Gtk events to redraw visuals with reloaded partition details
while ( Gtk::Main::events_pending() )
Gtk::Main::iteration();
if ( largest_unalloc_size >= 0 )
{
// Flashing redraw work around. Inform visuals of selection of the
// largest unallocated partition after drawing those visuals above.
drawingarea_visualdisk.set_selected( selected_partition_ptr );
treeview_detail.set_selected( selected_partition_ptr );
// Process Gtk events to draw selection
while ( Gtk::Main::events_pending() )
Gtk::Main::iteration();
}
2004-09-19 14:24:53 -06:00
}
// Confirms that the pointer points to one of the partition objects in the vector of
// displayed partitions, Win_GParted::display_partitions[].
// Usage: g_assert( valid_display_partition_ptr( my_partition_ptr ) );
bool Win_GParted::valid_display_partition_ptr( const Partition * partition_ptr )
{
for ( unsigned int i = 0 ; i < display_partitions.size() ; i++ )
{
if ( & display_partitions[i] == partition_ptr )
return true;
else if ( display_partitions[i].type == TYPE_EXTENDED )
{
for ( unsigned int j = 0 ; j < display_partitions[i].logicals.size() ; j++ )
{
if ( & display_partitions[i].logicals[j] == partition_ptr )
return true;
}
}
}
return false;
}
bool Win_GParted::Quit_Check_Operations()
2004-09-19 14:24:53 -06:00
{
if ( operations .size() )
2004-09-19 14:24:53 -06:00
{
Gtk::MessageDialog dialog( *this,
_("Quit GParted?"),
false,
Gtk::MESSAGE_QUESTION,
Gtk::BUTTONS_NONE,
true );
dialog .set_secondary_text( String::ucompose( ngettext( "%1 operation is currently pending."
, "%1 operations are currently pending."
, operations .size()
)
, operations .size()
)
) ;
2004-09-19 14:24:53 -06:00
dialog .add_button( Gtk::Stock::QUIT, Gtk::RESPONSE_CLOSE );
dialog .add_button( Gtk::Stock::CANCEL,Gtk::RESPONSE_CANCEL );
if ( dialog .run() == Gtk::RESPONSE_CANCEL )
return false;//don't close GParted
2004-09-19 14:24:53 -06:00
}
return true; //close GParted
}
void Win_GParted::set_valid_operations()
2004-09-19 14:24:53 -06:00
{
allow_new( false ); allow_delete( false ); allow_resize( false ); allow_copy( false );
allow_paste( false ); allow_format( false ); allow_toggle_busy_state( false ) ;
allow_name_partition( false ); allow_manage_flags( false ); allow_check( false );
allow_label_filesystem( false ); allow_change_uuid( false ); allow_info( false );
dynamic_cast<Gtk::Label*>( menu_partition .items()[ MENU_TOGGLE_BUSY ] .get_child() )
->set_label( FileSystem::get_generic_text ( CTEXT_DEACTIVATE_FILESYSTEM ) ) ;
menu_partition .items()[ MENU_TOGGLE_BUSY ] .show() ;
menu_partition .items()[ MENU_MOUNT ] .hide() ;
// No partition selected ...
if ( ! selected_partition_ptr )
return ;
g_assert( valid_display_partition_ptr( selected_partition_ptr ) ); // Bug: Not pointing at a valid display partition object
// Get filesystem capabilities
fs = gparted_core.get_fs( selected_partition_ptr->filesystem );
//if there's something, there's some info ;)
allow_info( true ) ;
// Set an appropriate name for the activate/deactivate menu item.
const FileSystem * filesystem_object = gparted_core.get_filesystem_object( selected_partition_ptr->filesystem );
if ( filesystem_object )
dynamic_cast<Gtk::Label*>( menu_partition .items()[ MENU_TOGGLE_BUSY ] .get_child() )
->set_label( filesystem_object->get_custom_text( selected_partition_ptr->busy
? CTEXT_DEACTIVATE_FILESYSTEM
: CTEXT_ACTIVATE_FILESYSTEM ) );
else
dynamic_cast<Gtk::Label*>( menu_partition .items()[ MENU_TOGGLE_BUSY ] .get_child() )
->set_label( FileSystem::get_generic_text ( selected_partition_ptr->busy
? CTEXT_DEACTIVATE_FILESYSTEM
: CTEXT_ACTIVATE_FILESYSTEM )
) ;
// Only permit mount/unmount, swapon/swapoff, activate/deactivate if action is available
if ( selected_partition_ptr->status == STAT_REAL
&& selected_partition_ptr->type != TYPE_EXTENDED
&& selected_partition_ptr->filesystem != FS_LVM2_PV
&& selected_partition_ptr->filesystem != FS_LINUX_SWRAID
&& ( selected_partition_ptr->busy
|| selected_partition_ptr->get_mountpoints().size() /* Have mount point(s) */
|| selected_partition_ptr->filesystem == FS_LINUX_SWAP
)
)
allow_toggle_busy_state( true ) ;
//Only permit VG deactivation if busy, or activation if not busy and a member of a VG.
// For now specifically allow activation of an exported VG, which LVM will fail
// with "Volume group "VGNAME" is exported", otherwise user won't know why the
// inactive PV can't be activated.
if ( selected_partition_ptr->status == STAT_REAL
&& selected_partition_ptr->type != TYPE_EXTENDED
&& selected_partition_ptr->filesystem == FS_LVM2_PV
&& ( selected_partition_ptr->busy
|| ( ! selected_partition_ptr->busy
&& ! selected_partition_ptr->get_mountpoints().empty() // VGNAME from mount point
)
)
)
allow_toggle_busy_state( true ) ;
// Allow naming on devices that support it
if ( selected_partition_ptr->type != TYPE_UNALLOCATED &&
selected_partition_ptr->status == STAT_REAL &&
devices[current_device].partition_naming_supported() )
allow_name_partition( true );
// Manage flags
if ( selected_partition_ptr->type != TYPE_UNALLOCATED &&
selected_partition_ptr->status == STAT_REAL &&
! selected_partition_ptr->whole_device )
allow_manage_flags( true );
#ifdef ENABLE_ONLINE_RESIZE
// Find out if online resizing is possible
if ( selected_partition_ptr->busy )
{
if ( ( fs .online_grow || fs .online_shrink ) && ! devices[ current_device ] .readonly )
allow_resize( true ) ;
}
#endif
// Only unmount/swapoff/VG deactivate or online actions allowed if busy
if ( selected_partition_ptr->busy )
return ;
// UNALLOCATED
if ( selected_partition_ptr->type == TYPE_UNALLOCATED )
2004-09-19 14:24:53 -06:00
{
allow_new( true );
2004-09-19 14:24:53 -06:00
//find out if there is a copied partition and if it fits inside this unallocated space
if ( ! copied_partition .get_path() .empty() && ! devices[ current_device ] .readonly )
{
Byte_Value required_size ;
if ( copied_partition .filesystem == GParted::FS_XFS )
required_size = copied_partition .estimated_min_size() * copied_partition .sector_size;
else
required_size = copied_partition .get_byte_length() ;
//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_ptr->inside_extended
&& selected_partition_ptr->type == TYPE_UNALLOCATED
)
|| ( selected_partition_ptr->type == TYPE_LOGICAL )
/* Beginning of disk device */
|| ( selected_partition_ptr->sector_start <= (MEBIBYTE / selected_partition_ptr->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_ptr->inside_extended
&& selected_partition_ptr->type == TYPE_UNALLOCATED
)
|| ( selected_partition_ptr->type == TYPE_LOGICAL )
)
&& ( selected_partition_ptr->sector_end
< ( devices[ current_device ] .length
- ( 2 * MEBIBYTE / devices[ current_device ] .sector_size )
)
)
)
required_size += MEBIBYTE;
//Determine if space is needed for the backup partition on a GPT partition table
if ( ( devices[ current_device ] .disktype == "gpt" )
&& ( ( devices[current_device].length - selected_partition_ptr->sector_end )
< ( MEBIBYTE / devices[ current_device ] .sector_size )
)
)
required_size += MEBIBYTE ;
if ( required_size <= selected_partition_ptr->get_byte_length() )
allow_paste( true ) ;
}
2004-09-19 14:24:53 -06:00
return ;
}
// EXTENDED
if ( selected_partition_ptr->type == TYPE_EXTENDED )
{
// Deletion is only allowed when there are no logical partitions inside.
if ( selected_partition_ptr->logicals.size() == 1 &&
selected_partition_ptr->logicals.back().type == TYPE_UNALLOCATED )
allow_delete( true ) ;
if ( ! devices[ current_device ] .readonly )
allow_resize( true ) ;
return ;
}
// PRIMARY and LOGICAL
if ( selected_partition_ptr->type == TYPE_PRIMARY || selected_partition_ptr->type == TYPE_LOGICAL )
2004-09-19 14:24:53 -06:00
{
allow_format( true ) ;
Enable operations on whole disk device virtual partitions (#743181) Enable operations on whole disk devices containing any recognised file system. The new partition operation on an empty whole disk device continues to display the "No partition table found on device /dev/DEVICE" information dialog. Specifically unsupported operations: * Delete - Deletion of a partition only involves removal of the entry in the partition table leaving the file system intact on the disk. However this doesn't work for a whole disk device file system. Instead the file system signatures would have to be erased which is much more destructive and virtually impossible to undo. Therefore don't implement whole disk device file system deletion. Alternatives are to format the file system to cleared or create a partition table on the device. Both of these imply overwriting the existing data and set the expectation that undo is not possible. * Manage flags - There's no partition table, so there's no partition, so there's no flags. Resize/Move operation is being supported so that a whole disk device file system can be resized to handle devices which can be resize, such as those from SANs or Linux Software RAID arrays. The start of the file system must remain fixed so move won't be allowed. So far only simple operations work if they don't need libparted support at all [1], or only need libparted support for the calibrate step AND the file system on the whole disk device is recognised by libparted [2]. (Needs libparted to provide a "loop" partition, hence the recognition requirement, so that the calibrate step can successfully read the virtual "loop" partition table. Doesn't matter whether it's an old version of libparted and it gets the name of the device wrong as GParted is already using the whole disk device name anyway). [1] Operations not needing any libparted support: Mount on, Unmount, Swapon, Swapoff, Activate and Deactivate [2] Operations only needing libparted support for the calibrate step: Check, Label, New UUID Bug 743181 - Add unpartitioned drive read-write support
2014-12-31 05:41:03 -07:00
// only allow deletion of partitions within a partition table
if ( ! selected_partition_ptr->whole_device )
Enable operations on whole disk device virtual partitions (#743181) Enable operations on whole disk devices containing any recognised file system. The new partition operation on an empty whole disk device continues to display the "No partition table found on device /dev/DEVICE" information dialog. Specifically unsupported operations: * Delete - Deletion of a partition only involves removal of the entry in the partition table leaving the file system intact on the disk. However this doesn't work for a whole disk device file system. Instead the file system signatures would have to be erased which is much more destructive and virtually impossible to undo. Therefore don't implement whole disk device file system deletion. Alternatives are to format the file system to cleared or create a partition table on the device. Both of these imply overwriting the existing data and set the expectation that undo is not possible. * Manage flags - There's no partition table, so there's no partition, so there's no flags. Resize/Move operation is being supported so that a whole disk device file system can be resized to handle devices which can be resize, such as those from SANs or Linux Software RAID arrays. The start of the file system must remain fixed so move won't be allowed. So far only simple operations work if they don't need libparted support at all [1], or only need libparted support for the calibrate step AND the file system on the whole disk device is recognised by libparted [2]. (Needs libparted to provide a "loop" partition, hence the recognition requirement, so that the calibrate step can successfully read the virtual "loop" partition table. Doesn't matter whether it's an old version of libparted and it gets the name of the device wrong as GParted is already using the whole disk device name anyway). [1] Operations not needing any libparted support: Mount on, Unmount, Swapon, Swapoff, Activate and Deactivate [2] Operations only needing libparted support for the calibrate step: Check, Label, New UUID Bug 743181 - Add unpartitioned drive read-write support
2014-12-31 05:41:03 -07:00
allow_delete( true );
//find out if resizing/moving is possible
if ( (fs .grow || fs .shrink || fs .move ) && ! devices[ current_device ] .readonly )
2004-09-19 14:24:53 -06:00
allow_resize( true ) ;
//only allow copying of real partitions
if ( selected_partition_ptr->status == STAT_REAL && fs.copy )
allow_copy( true ) ;
//only allow labelling of real partitions that support labelling
if ( selected_partition_ptr->status == STAT_REAL && fs.write_label )
allow_label_filesystem( true );
//only allow changing UUID of real partitions that support it
if ( selected_partition_ptr->status == STAT_REAL && fs.write_uuid )
allow_change_uuid( true ) ;
// Generate Mount on submenu, except for LVM2 PVs
// borrowing mount point to display the VGNAME
if ( selected_partition_ptr->filesystem != FS_LVM2_PV &&
selected_partition_ptr->get_mountpoints().size() )
{
menu = menu_partition .items()[ MENU_MOUNT ] .get_submenu() ;
menu ->items() .clear() ;
for ( unsigned int t = 0 ; t < selected_partition_ptr->get_mountpoints().size() ; t++ )
{
menu ->items() .push_back(
Gtk::Menu_Helpers::MenuElem(
selected_partition_ptr->get_mountpoints()[t],
sigc::bind<unsigned int>( sigc::mem_fun(*this, &Win_GParted::activate_mount_partition), t ) ) );
dynamic_cast<Gtk::Label*>( menu ->items() .back() .get_child() ) ->set_use_underline( false ) ;
}
menu_partition .items()[ MENU_TOGGLE_BUSY ] .hide() ;
menu_partition .items()[ MENU_MOUNT ] .show() ;
}
//see if there is an copied partition and if it passes all tests
if ( ! copied_partition .get_path() .empty() &&
copied_partition.get_byte_length() <= selected_partition_ptr->get_byte_length() &&
selected_partition_ptr->status == STAT_REAL &&
copied_partition != *selected_partition_ptr )
allow_paste( true ) ;
//see if we can somehow check/repair this file system....
if ( fs.check && selected_partition_ptr->status == STAT_REAL )
allow_check( true ) ;
2004-09-19 14:24:53 -06:00
}
}
Only allow Undo and Apply after merging operations (#699452) It was possible to make GParted crash by adding a label, check or new UUID operation and then applying the operation before the view of pending operations had finished fully opening. The operation would be successfully applied but GParted would crash afterwards. The fault was that Add_Operation() still enabled the Undo and Apply buttons and processed the GTK event loop before merging the list of pending operations. Faulty code flow went like this: activate_*() Add_Operation() Add operation to the operations[] vector Enable Undo and Apply buttons Refresh_Visual() Process GTK event loop Process Apply button callback applying operations, refreshing display and clearing operations[] vector Merge operations in the operations[] vector << Core dump here >> Merge_Operations() Refresh_Visual() This faulty code flow came about when merging of operations was added and it didn't appreciate that the operations[] vector could have been processed and cleared by Add_Operations() before the merge step. Relevant commit: b10349ae37df06b7bf7de91ea93a3913535ade3a Merge overlapping operations (#438573) Fragment of code in the label operation case: 2454 void Win_GParted::activate_label_partition() 2455 { ... 2472 Add_Operation( operation ) ; 2473 2474 // Verify if the two operations can be merged 2475 for ( unsigned int t = 0 ; t < operations .size() - 1 ; t++ ) 2476 { 2477 if ( operations[ t ] ->type == OPERATION_LABEL_PARTITION ) 2478 { 2479 if ( Merge_Operations( t, operations .size() - 1 ) ) 2480 break; 2481 } 2482 } Commentary in the crashing label operation case: 2472 The pending operation was already applied when Add_Operation() returned resulting in the operations[] vector being cleared setting its size to 0. 2475 The return type of operations.size() is an unsigned integral, so the upper limit of the for loop is t < 0UL - 1. Assuming a 32-bit machine that's t < 4294967295. 2477 operations[] vector is access from out of bounds offset 0 upwards until unallocated memory is accessed resulting in a core dump. Fix this by not enabling the Undo and Apply buttons and processing the GTK event loop until after merging of operations has been performed. Fixed code flow goes like this: activate_*() Add_Operation() Add operation to the operations[] vector Merge operations in the operations[] vector Merge_Operations() show_operationslist() Enable Undo and Apply buttons Refresh_Visual() Process GTK event loop Process Apply button callback applying operations, refreshing display and clearing operations[] vector Not allowing the operations list to be process until after the merge step is the be correct ordering. This also prevents the new operation from flashing up in the operations list and then immediately disappearing if merged. In the case of adding the first operation, delaying enabling the Undo and Apply buttons is enough as the buttons were previously disabled preventing the operation being applied before the merge. In the case of adding further operations, processing of the GTK event loop must also be delayed until after the merge to prevent the operations being applied before the merge. Although that window of opportunity would only be microseconds. Bug #699452 - Crash when applying operations before pending operations fully displayed
2013-05-17 10:01:42 -06:00
void Win_GParted::show_operationslist()
{
//Enable (or disable) Undo and Apply buttons
allow_undo_clear_apply( operations .size() ) ;
//Updates view of operations list and sensitivity of Undo and Apply buttons
Refresh_Visual();
if ( operations .size() == 1 ) //first operation, open operationslist
open_operationslist() ;
//FIXME: A slight flicker may be introduced by this extra display refresh.
//An extra display refresh seems to prevent the disk area visual disk from
// disappearing when enough operations are added to require a scrollbar
// (about 4 operations with default window size).
// Note that commenting out the code to
// "//make scrollwindow focus on the last operation in the list"
// in HBoxOperations::load_operations() prevents this problem from occurring as well.
// See also Win_GParted::activate_undo().
drawingarea_visualdisk .queue_draw() ;
}
void Win_GParted::open_operationslist()
{
if ( ! OPERATIONSLIST_OPEN )
{
OPERATIONSLIST_OPEN = true ;
hbox_operations .show() ;
for ( int t = vpaned_main .get_height() ; t > ( vpaned_main .get_height() - 100 ) ; t -= 5 )
{
vpaned_main .set_position( t );
while ( Gtk::Main::events_pending() )
Gtk::Main::iteration() ;
}
static_cast<Gtk::CheckMenuItem *>( & menubar_main .items()[ 2 ] .get_submenu() ->items()[ 1 ] )
->set_active( true ) ;
}
}
void Win_GParted::close_operationslist()
2004-09-19 14:24:53 -06:00
{
if ( OPERATIONSLIST_OPEN )
2004-09-19 14:24:53 -06:00
{
OPERATIONSLIST_OPEN = false ;
for ( int t = vpaned_main .get_position() ; t < vpaned_main .get_height() ; t += 5 )
{
vpaned_main .set_position( t ) ;
while ( Gtk::Main::events_pending() )
Gtk::Main::iteration();
}
hbox_operations .hide() ;
static_cast<Gtk::CheckMenuItem *>( & menubar_main .items()[ 2 ] .get_submenu() ->items()[ 1 ] )
->set_active( false ) ;
2004-09-19 14:24:53 -06:00
}
}
void Win_GParted::clear_operationslist()
2004-09-19 14:24:53 -06:00
{
remove_operation( -1, true ) ;
close_operationslist() ;
Refresh_Visual() ;
2004-09-19 14:24:53 -06:00
}
void Win_GParted::combo_devices_changed()
{
unsigned int old_current_device = current_device;
2004-09-19 14:24:53 -06:00
//set new current device
current_device = combo_devices .get_active_row_number() ;
if ( current_device == (unsigned int) -1 )
current_device = old_current_device;
if ( current_device >= devices .size() )
current_device = 0 ;
set_title( String::ucompose( _("%1 - GParted"), devices[ current_device ] .get_path() ) );
2004-09-19 14:24:53 -06:00
//refresh label_device_info
Fill_Label_Device_Info();
2004-09-19 14:24:53 -06:00
//rebuild visualdisk and treeview
Refresh_Visual();
//uodate radiobuttons..
if ( menubar_main .items()[ 0 ] .get_submenu() ->items()[ 1 ] .get_submenu() )
static_cast<Gtk::RadioMenuItem *>(
& menubar_main .items()[ 0 ] .get_submenu() ->items()[ 1 ] .get_submenu() ->
items()[ current_device ] ) ->set_active( true ) ;
}
void Win_GParted::radio_devices_changed( unsigned int item )
{
if ( static_cast<Gtk::RadioMenuItem *>(
& menubar_main .items()[ 0 ] .get_submenu() ->items()[ 1 ] .get_submenu() ->
items()[ item ] ) ->get_active() )
{
combo_devices .set_active( item ) ;
}
2004-09-19 14:24:53 -06:00
}
void Win_GParted::on_show()
{
Gtk::Window::on_show() ;
vpaned_main .set_position( vpaned_main .get_height() ) ;
close_operationslist() ;
menu_gparted_refresh_devices() ;
}
void Win_GParted::menu_gparted_refresh_devices()
2004-09-19 14:24:53 -06:00
{
show_pulsebar( _("Scanning all devices...") ) ;
gparted_core.set_devices( devices );
hide_pulsebar();
//check if current_device is still available (think about hotpluggable stuff like usbdevices)
if ( current_device >= devices .size() )
current_device = 0 ;
//see if there are any pending operations on non-existent devices
//NOTE that this isn't 100% foolproof since some stuff (e.g. sourcedevice of copy) may slip through.
//but anyone who removes the sourcedevice before applying the operations gets what he/she deserves :-)
//FIXME: this actually sucks ;) see if we can use STL predicates here..
unsigned int i ;
for ( unsigned int t = 0 ; t < operations .size() ; t++ )
{
for ( i = 0 ; i < devices .size() && devices[ i ] != operations[ t ] ->device ; i++ ) {}
if ( i >= devices .size() )
remove_operation( t-- ) ;
}
//if no devices were detected we disable some stuff and show a message in the statusbar
if ( devices .empty() )
{
this ->set_title( _("GParted") );
combo_devices .hide() ;
menubar_main .items()[ 0 ] .get_submenu() ->items()[ 1 ] .set_sensitive( false ) ;
menubar_main .items()[ 1 ] .set_sensitive( false ) ;
menubar_main .items()[ 2 ] .set_sensitive( false ) ;
menubar_main .items()[ 3 ] .set_sensitive( false ) ;
menubar_main .items()[ 4 ] .set_sensitive( false ) ;
toolbar_main .set_sensitive( false ) ;
drawingarea_visualdisk .set_sensitive( false ) ;
treeview_detail .set_sensitive( false ) ;
Fill_Label_Device_Info( true ) ;
drawingarea_visualdisk .clear() ;
treeview_detail .clear() ;
//hmzz, this is really paranoid, but i think it's the right thing to do ;)
hbox_operations .clear() ;
close_operationslist() ;
remove_operation( -1, true ) ;
statusbar .pop() ;
statusbar .push( _( "No devices detected" ) );
}
else //at least one device detected
{
combo_devices .show() ;
menubar_main .items()[ 0 ] .get_submenu() ->items()[ 1 ] .set_sensitive( true ) ;
menubar_main .items()[ 1 ] .set_sensitive( true ) ;
menubar_main .items()[ 2 ] .set_sensitive( true ) ;
menubar_main .items()[ 3 ] .set_sensitive( true ) ;
menubar_main .items()[ 4 ] .set_sensitive( true ) ;
toolbar_main .set_sensitive( true ) ;
drawingarea_visualdisk .set_sensitive( true ) ;
treeview_detail .set_sensitive( true ) ;
refresh_combo_devices() ;
}
2004-09-19 14:24:53 -06:00
}
void Win_GParted::menu_gparted_features()
{
DialogFeatures dialog ;
dialog .set_transient_for( *this ) ;
dialog .load_filesystems( gparted_core .get_filesystems() ) ;
while ( dialog .run() == Gtk::RESPONSE_OK )
{
gparted_core .find_supported_filesystems() ;
dialog .load_filesystems( gparted_core .get_filesystems() ) ;
//recreate format menu...
menu_partition .items()[ MENU_FORMAT ] .remove_submenu() ;
menu_partition .items()[ MENU_FORMAT ] .set_submenu( * create_format_menu() ) ;
menu_partition .items()[ MENU_FORMAT ] .get_submenu() ->show_all_children() ;
}
}
void Win_GParted::menu_gparted_quit()
2004-09-19 14:24:53 -06:00
{
if ( Quit_Check_Operations() )
this ->hide();
2004-09-19 14:24:53 -06:00
}
void Win_GParted::menu_view_harddisk_info()
{
if ( static_cast<Gtk::CheckMenuItem *>( & menubar_main .items()[ 2 ] .get_submenu() ->items()[ 0 ] ) ->get_active() )
{ //open harddisk information
hpaned_main .get_child1() ->show() ;
for ( int t = hpaned_main .get_position() ; t < 250 ; t += 15 )
2004-09-27 14:12:47 -06:00
{
hpaned_main .set_position( t );
while ( Gtk::Main::events_pending() )
Gtk::Main::iteration();
2004-09-27 14:12:47 -06:00
}
}
else
2004-09-27 14:12:47 -06:00
{ //close harddisk information
for ( int t = hpaned_main .get_position() ; t > 0 ; t -= 15 )
2004-09-27 14:12:47 -06:00
{
hpaned_main .set_position( t );
while ( Gtk::Main::events_pending() )
Gtk::Main::iteration();
2004-09-27 14:12:47 -06:00
}
hpaned_main .get_child1() ->hide() ;
2004-09-27 14:12:47 -06:00
}
}
void Win_GParted::menu_view_operations()
{
if ( static_cast<Gtk::CheckMenuItem *>( & menubar_main .items()[ 2 ] .get_submenu() ->items()[ 1 ] ) ->get_active() )
open_operationslist() ;
else
close_operationslist() ;
}
void Win_GParted::show_disklabel_unrecognized ( Glib::ustring device_name )
{
//Display dialog box indicating that no partition table was found on the device
Gtk::MessageDialog dialog( *this,
/*TO TRANSLATORS: looks like No partition table found on device /dev/sda */
String::ucompose( _( "No partition table found on device %1" ), device_name ),
false,
Gtk::MESSAGE_INFO,
Gtk::BUTTONS_OK,
true ) ;
Glib::ustring tmp_msg = _( "A partition table is required before partitions can be added." ) ;
tmp_msg += "\n" ;
tmp_msg += _( "To create a new partition table choose the menu item:" ) ;
tmp_msg += "\n" ;
/*TO TRANSLATORS: this message represents the menu item Create Partition Table under the Device menu. */
tmp_msg += _( "Device --> Create Partition Table." ) ;
dialog .set_secondary_text( tmp_msg ) ;
dialog .run() ;
}
void Win_GParted::show_help_dialog( const Glib::ustring & filename /* E.g., gparted */
, const Glib::ustring & link_id /* For context sensitive help */
)
2004-09-19 14:24:53 -06:00
{
GError *error = NULL ;
GdkScreen *gscreen = NULL ;
Glib::ustring uri = "ghelp:" + filename ;
if (link_id .size() > 0 ) {
uri = uri + "?" + link_id ;
}
gscreen = gdk_screen_get_default() ;
#ifdef HAVE_GTK_SHOW_URI
gtk_show_uri( gscreen, uri .c_str(), gtk_get_current_event_time(), &error ) ;
#else
Glib::ustring command = "gnome-open " + uri ;
gdk_spawn_command_line_on_screen( gscreen, command .c_str(), &error ) ;
#endif
if ( error != NULL )
{
//Try opening yelp application directly
g_clear_error( &error ) ; //Clear error from trying to open gparted help manual above (gtk_show_uri or gnome-open).
Glib::ustring command = "yelp " + uri ;
gdk_spawn_command_line_on_screen( gscreen, command .c_str(), &error ) ;
}
if ( error != NULL )
{
Gtk::MessageDialog dialog( *this
, _( "Unable to open GParted Manual help file" )
, false
, Gtk::MESSAGE_ERROR
, Gtk::BUTTONS_OK
, true
) ;
dialog .set_secondary_text( error ->message ) ;
dialog .run() ;
}
2004-09-19 14:24:53 -06:00
}
void Win_GParted::menu_help_contents()
{
#ifdef ENABLE_HELP_DOC
//GParted was built with help documentation
show_help_dialog( "gparted", "" );
#else
//GParted was built *without* help documentation using --disable-doc
Gtk::MessageDialog dialog( *this,
_( "Documentation is not available" ),
false,
Gtk::MESSAGE_INFO,
Gtk::BUTTONS_OK,
true ) ;
Glib::ustring tmp_msg = _( "This build of gparted is configured without documentation." ) ;
tmp_msg += "\n" ;
tmp_msg += _( "Documentation is available at the project web site." ) ;
tmp_msg += "\n" ;
tmp_msg += "http://gparted.org" ;
dialog .set_secondary_text( tmp_msg ) ;
dialog .run() ;
#endif
}
2004-09-19 14:24:53 -06:00
void Win_GParted::menu_help_about()
2004-09-19 14:24:53 -06:00
{
std::vector<Glib::ustring> strings ;
Gtk::AboutDialog dialog ;
2004-09-19 14:24:53 -06:00
dialog .set_transient_for( *this ) ;
dialog .set_name( _("GParted") ) ;
dialog .set_logo_icon_name( "gparted" ) ;
dialog .set_version( VERSION ) ;
dialog .set_comments( _( "GNOME Partition Editor" ) ) ;
2015-02-01 13:14:05 -07:00
std::string names ;
names = "Copyright © 2004-2006 Bart Hakvoort" ;
names += "\nCopyright © 2008-2015 Curtis Gedak" ;
names += "\nCopyright © 2011-2015 Mike Fleetwood" ;
dialog .set_copyright( names ) ;
//authors
//Names listed in alphabetical order by LAST name.
//See also AUTHORS file -- names listed in opposite order to try to be fair.
strings .push_back( "Sinlu Bes <e80f00@gmail.com>" ) ;
strings .push_back( "Luca Bruno <lucab@debian.org>" ) ;
strings .push_back( "Jérôme Dumesnil <jerome.dumesnil@gmail.com>" ) ;
strings .push_back( "Markus Elfring <elfring@users.sourceforge.net>" ) ;
strings .push_back( "Mike Fleetwood <mike.fleetwood@googlemail.com>" ) ;
strings .push_back( "Curtis Gedak <gedakc@users.sf.net>" ) ;
strings .push_back( "Matthias Gehre <m.gehre@gmx.de>" ) ;
strings .push_back( "Rogier Goossens <goossens.rogier@gmail.com>" ) ;
strings .push_back( "Bart Hakvoort <gparted@users.sf.net>" ) ;
strings .push_back( "Seth Heeren <sgheeren@gmail.com>" ) ;
strings .push_back( "Joan Lledó <joanlluislledo@gmail.com>" ) ;
strings .push_back( "Phillip Susi <psusi@cfl.rr.com>" ) ;
strings. push_back( "Michael Zimmermann <sigmaepsilon92@gmail.com>" ) ;
dialog .set_authors( strings ) ;
strings .clear() ;
//artists
strings .push_back( "Sebastian Kraft <kraft.sebastian@gmail.com>" ) ;
dialog .set_artists( strings ) ;
strings .clear() ;
/*TO TRANSLATORS: your name(s) here please, if there are more translators put newlines (\n) between the names.
It's a good idea to provide the url of your translation team as well. Thanks! */
Glib::ustring str_credits = _("translator-credits") ;
if ( str_credits != "translator-credits" )
dialog .set_translator_credits( str_credits ) ;
//the url is not clickable because this would introduce an new dep (gnome-vfsmm)
dialog .set_website( "http://gparted.org" ) ;
dialog .run() ;
2004-09-19 14:24:53 -06:00
}
void Win_GParted::on_partition_selected( const Partition * partition_ptr, bool src_is_treeview )
2004-09-19 14:24:53 -06:00
{
selected_partition_ptr = partition_ptr;
set_valid_operations() ;
if ( src_is_treeview )
drawingarea_visualdisk.set_selected( partition_ptr );
else
treeview_detail.set_selected( partition_ptr );
}
void Win_GParted::on_partition_activated()
{
activate_info() ;
}
void Win_GParted::on_partition_popup_menu( unsigned int button, unsigned int time )
{
menu_partition .popup( button, time );
2004-09-19 14:24:53 -06:00
}
bool Win_GParted::max_amount_prim_reached()
{
//FIXME: this is the only place where primary_count is used... instead of counting the primaries on each
//refresh, we could just count them here.
//Display error if user tries to create more primary partitions than the partition table can hold.
if ( ! selected_partition_ptr->inside_extended && primary_count >= devices[current_device].max_prims )
{
Gtk::MessageDialog dialog(
*this,
String::ucompose( ngettext( "It is not possible to create more than %1 primary partition"
, "It is not possible to create more than %1 primary partitions"
, devices[ current_device ] .max_prims
)
, devices[ current_device ] .max_prims
),
false,
Gtk::MESSAGE_ERROR,
Gtk::BUTTONS_OK,
true ) ;
dialog .set_secondary_text(
_( "If you want more partitions you should first create an extended partition. Such a partition can contain other partitions. Because an extended partition is also a primary partition it might be necessary to remove a primary partition first.") ) ;
dialog .run() ;
return true ;
}
return false ;
}
2004-09-19 14:24:53 -06:00
void Win_GParted::activate_resize()
2004-09-19 14:24:53 -06:00
{
g_assert( selected_partition_ptr != NULL ); // Bug: Partition callback without a selected partition
g_assert( valid_display_partition_ptr( selected_partition_ptr ) ); // Bug: Not pointing at a valid display partition object
std::vector<Partition> partitions = devices[ current_device ] .partitions ;
if ( operations .size() )
for (unsigned int t = 0 ; t < operations .size() ; t++ )
if ( operations[ t ] ->device == devices[ current_device ] )
operations[ t ] ->apply_to_visual( partitions ) ;
Dialog_Partition_Resize_Move dialog( gparted_core.get_fs( selected_partition_ptr->filesystem ) );
if ( selected_partition_ptr->type == TYPE_LOGICAL )
{
unsigned int ext = 0 ;
while ( ext < partitions .size() && partitions[ ext ] .type != GParted::TYPE_EXTENDED ) ext++ ;
dialog.Set_Data( *selected_partition_ptr, partitions[ext].logicals );
}
else
dialog.Set_Data( *selected_partition_ptr, partitions );
dialog .set_transient_for( *this ) ;
if ( dialog .run() == Gtk::RESPONSE_OK )
2004-09-19 14:24:53 -06:00
{
dialog .hide() ;
// If selected partition is NEW we simply remove the NEW operation from the list and add
// it again with the new size and position ( unless it's an EXTENDED )
if ( selected_partition_ptr->status == STAT_NEW && selected_partition_ptr->type != TYPE_EXTENDED )
2004-09-19 14:24:53 -06:00
{
//remove operation which creates this partition
for ( unsigned int t = 0 ; t < operations .size() ; t++ )
2004-09-19 14:24:53 -06:00
{
if ( operations[t]->partition_new == *selected_partition_ptr )
2004-09-19 14:24:53 -06:00
{
remove_operation( t ) ;
// And add the new partition to the end of the operations list.
// Create a suitable partition from the selected partition.
Partition temp_partition = *selected_partition_ptr;
temp_partition.Set_Unallocated( devices[current_device].get_path(),
selected_partition_ptr->whole_device,
selected_partition_ptr->sector_start,
selected_partition_ptr->sector_end,
devices[current_device].sector_size,
selected_partition_ptr->inside_extended );
Operation * operation = new OperationCreate( devices[ current_device ],
temp_partition,
dialog.Get_New_Partition( devices[current_device].sector_size ) );
operation ->icon = render_icon( Gtk::Stock::NEW, Gtk::ICON_SIZE_MENU );
Add_Operation( operation ) ;
2004-09-19 14:24:53 -06:00
break;
}
}
}
else // Normal move/resize on existing partition
{
Operation * operation = new OperationResizeMove( devices[current_device],
*selected_partition_ptr,
dialog.Get_New_Partition( devices[current_device].sector_size) );
operation ->icon = render_icon( Gtk::Stock::GOTO_LAST, Gtk::ICON_SIZE_MENU );
Add_Operation( operation ) ;
//Display notification if move operation has been queued
if ( operation ->partition_original .sector_start != operation ->partition_new .sector_start
&& operation ->partition_original .type != TYPE_EXTENDED
)
{
//Warn that move operation might break boot process
Gtk::MessageDialog dialog( *this
, _( "Moving a partition might cause your operating system to fail to boot" )
, false
, Gtk::MESSAGE_WARNING
, Gtk::BUTTONS_OK
, true
) ;
Glib::ustring tmp_msg =
/*TO TRANSLATORS: looks like You queued an operation to move the start sector of partition /dev/sda3. */
String::ucompose( _( "You have queued an operation to move the start sector of partition %1." )
, operation ->partition_original .get_path()
) ;
tmp_msg += _( " Failure to boot is most likely to occur if you move the GNU/Linux partition containing /boot, or if you move the Windows system partition C:." ) ;
tmp_msg += "\n" ;
tmp_msg += _( "You can learn how to repair the boot configuration in the GParted FAQ." ) ;
tmp_msg += "\n" ;
tmp_msg += "http://gparted.org/faq.php" ;
tmp_msg += "\n\n" ;
tmp_msg += _( "Moving a partition might take a very long time to apply." ) ;
dialog .set_secondary_text( tmp_msg ) ;
dialog .run() ;
}
// Try to merge with previous operation
if ( operations .size() >= 2 )
{
Merge_Operations(operations .size() - 2, operations .size() - 1);
}
}
2004-09-19 14:24:53 -06:00
}
Only allow Undo and Apply after merging operations (#699452) It was possible to make GParted crash by adding a label, check or new UUID operation and then applying the operation before the view of pending operations had finished fully opening. The operation would be successfully applied but GParted would crash afterwards. The fault was that Add_Operation() still enabled the Undo and Apply buttons and processed the GTK event loop before merging the list of pending operations. Faulty code flow went like this: activate_*() Add_Operation() Add operation to the operations[] vector Enable Undo and Apply buttons Refresh_Visual() Process GTK event loop Process Apply button callback applying operations, refreshing display and clearing operations[] vector Merge operations in the operations[] vector << Core dump here >> Merge_Operations() Refresh_Visual() This faulty code flow came about when merging of operations was added and it didn't appreciate that the operations[] vector could have been processed and cleared by Add_Operations() before the merge step. Relevant commit: b10349ae37df06b7bf7de91ea93a3913535ade3a Merge overlapping operations (#438573) Fragment of code in the label operation case: 2454 void Win_GParted::activate_label_partition() 2455 { ... 2472 Add_Operation( operation ) ; 2473 2474 // Verify if the two operations can be merged 2475 for ( unsigned int t = 0 ; t < operations .size() - 1 ; t++ ) 2476 { 2477 if ( operations[ t ] ->type == OPERATION_LABEL_PARTITION ) 2478 { 2479 if ( Merge_Operations( t, operations .size() - 1 ) ) 2480 break; 2481 } 2482 } Commentary in the crashing label operation case: 2472 The pending operation was already applied when Add_Operation() returned resulting in the operations[] vector being cleared setting its size to 0. 2475 The return type of operations.size() is an unsigned integral, so the upper limit of the for loop is t < 0UL - 1. Assuming a 32-bit machine that's t < 4294967295. 2477 operations[] vector is access from out of bounds offset 0 upwards until unallocated memory is accessed resulting in a core dump. Fix this by not enabling the Undo and Apply buttons and processing the GTK event loop until after merging of operations has been performed. Fixed code flow goes like this: activate_*() Add_Operation() Add operation to the operations[] vector Merge operations in the operations[] vector Merge_Operations() show_operationslist() Enable Undo and Apply buttons Refresh_Visual() Process GTK event loop Process Apply button callback applying operations, refreshing display and clearing operations[] vector Not allowing the operations list to be process until after the merge step is the be correct ordering. This also prevents the new operation from flashing up in the operations list and then immediately disappearing if merged. In the case of adding the first operation, delaying enabling the Undo and Apply buttons is enough as the buttons were previously disabled preventing the operation being applied before the merge. In the case of adding further operations, processing of the GTK event loop must also be delayed until after the merge to prevent the operations being applied before the merge. Although that window of opportunity would only be microseconds. Bug #699452 - Crash when applying operations before pending operations fully displayed
2013-05-17 10:01:42 -06:00
show_operationslist() ;
2004-09-19 14:24:53 -06:00
}
void Win_GParted::activate_copy()
2004-09-19 14:24:53 -06:00
{
g_assert( selected_partition_ptr != NULL ); // Bug: Partition callback without a selected partition
g_assert( valid_display_partition_ptr( selected_partition_ptr ) ); // Bug: Not pointing at a valid display partition object
copied_partition = *selected_partition_ptr;
2004-09-19 14:24:53 -06:00
}
void Win_GParted::activate_paste()
2004-09-19 14:24:53 -06:00
{
g_assert( selected_partition_ptr != NULL ); // Bug: Partition callback without a selected partition
g_assert( valid_display_partition_ptr( selected_partition_ptr ) ); // Bug: Not pointing at a valid display partition object
// Unrecognised whole disk device (See GParted_Core::get_devices_threads(), "unrecognized")
if ( selected_partition_ptr->whole_device && selected_partition_ptr->type == TYPE_UNALLOCATED )
2004-09-19 14:24:53 -06:00
{
show_disklabel_unrecognized( devices [current_device ] .get_path() ) ;
return ;
}
if ( selected_partition_ptr->type == TYPE_UNALLOCATED )
{
if ( ! max_amount_prim_reached() )
{
Remove cylinder size adjustments in the copy dialog (#749867) BUF in the copy dialog class, Dialog_Partition_Copy, is use to adjust limits in 2 cases: 1) Minimum size when copying an XFS file system Minimum size was set to the used space + 2 * cylinder size (typically plus ~16 MiB). This commit from 2004-12-20 added it: a54b52ea33abb1f5a44b52bcad5858ba41cd135d xfs copy now uses xfsdump and xfsrestore. icw some hacks in the other 2 Issues: * This is increasing the minimum XFS file system size when copying it, which doesn't happen in the resize case for other file systems. * It allows an XFS file system to be created which is smaller than the minimum size allowed by GParted. Copying an empty XFS file system can create a new file system as small as 26 MiB. This is smaller than the minimum GParted allows of 32 MiB because that is the minimum xfs_repair can handle. Remove this addition when copying an XFS file system and enforce minimum file system size. 2) Maximum size when copying a file system into empty space larger than it's maximum size Maximum size was set to maximum file system size - cylinder size (typically minus ~8 MiB). Only applied to FAT16 which has a maximum file system size set in and can be grown. Added by this commit from 2004-12-15: 10e8f3338d76e74c5fba22ff6c86888a67b99ae9 :get_fs now returns a const reference. in copy and resizedialog ... * in copy and resizedialog filesystems with MAX set now have a max size of MAX - one cylinder . Issue: * This is applying a lower maximum resize when copying the file system compared to that when creating the file system. NOTE: GParted currently allows all file systems to be resize to any size, regardless of the maximum file system size. This is probably an oversight, but it does allow libparted to convert FAT16 to FAT32 file system when resizing. Remove this lower maximum file system size when copying and resizing, compared to creating. Bug 749867 - Some limits are adjusted by arcane cylinder size amount when copying and resizing in a single operation
2015-05-25 13:09:46 -06:00
Dialog_Partition_Copy dialog( gparted_core.get_fs( copied_partition.filesystem ) );
// We don't want the messages, mount points or name of the source
// partition for the new partition being created.
copied_partition .messages .clear() ;
copied_partition .clear_mountpoints() ;
copied_partition .name.clear() ;
dialog.Set_Data( *selected_partition_ptr, copied_partition );
dialog .set_transient_for( *this );
if ( dialog .run() == Gtk::RESPONSE_OK )
{
dialog .hide() ;
Operation * operation = new OperationCopy( devices[current_device],
*selected_partition_ptr,
dialog.Get_New_Partition( devices[current_device].sector_size ),
copied_partition );
operation ->icon = render_icon( Gtk::Stock::COPY, Gtk::ICON_SIZE_MENU );
Add_Operation( operation ) ;
}
}
2004-09-19 14:24:53 -06:00
}
else
{
bool shown_dialog = false ;
// VGNAME from mount mount
if ( selected_partition_ptr->filesystem == FS_LVM2_PV &&
! selected_partition_ptr->get_mountpoint().empty() )
{
if ( ! remove_non_empty_lvm2_pv_dialog( OPERATION_COPY ) )
return ;
shown_dialog = true ;
}
Partition partition_new = *selected_partition_ptr;
Fix uninitialised read when pasting into an existing partition GParted_Core::set_device_partitions() creates and initialises the partition objects based on the partitions on the disk using partition.Reset() and partition.Set(). These methods never set the alignment attribute. Copy and pasting into an existing partition calls GParted_Core:: snap_to_alignment() to adjust the start and end of the newly created in memory partition object. When pasting into unallocated space the user has selected the required alignment and this is exactly what is needed. However when pasting into an existing partition the in memory partition object should always match the actual partition boundaries on disk. Unfortunately the partition boundaries are adjusted based on reading the uninitialised alignment attribute. Initialise the alignment attribute of newly created partition objects to ALIGN_STRICT. Also, when pasting into an existing partition set the alignment of that partition object to ALIGN_STRICT so that no boundary adjustment is performed. valgrind: ==6845== Conditional jump or move depends on uninitialised value(s) ==6845== at 0x80C779A: GParted::GParted_Core::snap_to_alignment(...) (GParted_Core.cc:566) ==6845== by 0x810C115: GParted::Win_GParted::Add_Operation(...) (Win_GParted.cc:692) ==6845== by 0x8110499: GParted::Win_GParted::activate_paste() (Win_GParted.cc:1649) ... ==6845== Conditional jump or move depends on uninitialised value(s) ==6845== at 0x80C77A8: GParted::GParted_Core::snap_to_alignment(...) (GParted_Core.cc:568) ==6845== by 0x810C115: GParted::Win_GParted::Add_Operation(...) (Win_GParted.cc:692) ==6845== by 0x8110499: GParted::Win_GParted::activate_paste() (Win_GParted.cc:1649) GParted_Core.cc: 562 bool GParted_Core::snap_to_alignment( const Device & device, Partition & partition, Glib::ustring & error ) 563 { 564 bool rc = true ; 565 >> 566 if ( partition .alignment == ALIGN_CYLINDER ) 567 rc = snap_to_cylinder( device, partition, error ) ; >> 568 else if ( partition .alignment == ALIGN_MEBIBYTE ) 569 rc = snap_to_mebibyte( device, partition, error ) ; 570 Closes Bug #672654 - Pasting into an existing partition may shrink GParted's representation of it
2012-03-23 09:51:08 -06:00
partition_new .alignment = ALIGN_STRICT ;
partition_new .filesystem = copied_partition .filesystem ;
partition_new.set_filesystem_label( copied_partition.get_filesystem_label() );
partition_new .uuid = copied_partition .uuid ;
partition_new .color = copied_partition .color ;
Sector new_size = partition_new .get_sector_length() ;
if ( copied_partition .get_sector_length() == new_size )
{
//Pasting into same size existing partition, therefore only block copy operation
// will be performed maintaining the file system size.
partition_new .set_sector_usage(
copied_partition .sectors_used + copied_partition. sectors_unused,
copied_partition. sectors_unused ) ;
}
else
{
//Pasting into larger existing partition, therefore block copy followed by file system
// grow operations (if supported) will be performed making the file system fill the
// partition.
partition_new .set_sector_usage(
new_size,
new_size - copied_partition .sectors_used ) ;
}
partition_new .messages .clear() ;
Operation * operation = new OperationCopy( devices[current_device],
*selected_partition_ptr,
partition_new,
copied_partition );
operation ->icon = render_icon( Gtk::Stock::COPY, Gtk::ICON_SIZE_MENU );
Add_Operation( operation ) ;
if ( ! shown_dialog )
{
//Only warn that this paste operation will overwrite data in the existing
// partition if not already shown the remove non-empty LVM2 PV dialog.
Gtk::MessageDialog dialog( *this
, _( "You have pasted into an existing partition" )
, false
, Gtk::MESSAGE_WARNING
, Gtk::BUTTONS_OK
, true
) ;
/*TO TRANSLATORS: looks like The data in /dev/sda3 will be lost if you apply this operation. */
dialog .set_secondary_text(
String::ucompose( _( "The data in %1 will be lost if you apply this operation." ),
partition_new .get_path() ) ) ;
dialog .run() ;
}
}
Only allow Undo and Apply after merging operations (#699452) It was possible to make GParted crash by adding a label, check or new UUID operation and then applying the operation before the view of pending operations had finished fully opening. The operation would be successfully applied but GParted would crash afterwards. The fault was that Add_Operation() still enabled the Undo and Apply buttons and processed the GTK event loop before merging the list of pending operations. Faulty code flow went like this: activate_*() Add_Operation() Add operation to the operations[] vector Enable Undo and Apply buttons Refresh_Visual() Process GTK event loop Process Apply button callback applying operations, refreshing display and clearing operations[] vector Merge operations in the operations[] vector << Core dump here >> Merge_Operations() Refresh_Visual() This faulty code flow came about when merging of operations was added and it didn't appreciate that the operations[] vector could have been processed and cleared by Add_Operations() before the merge step. Relevant commit: b10349ae37df06b7bf7de91ea93a3913535ade3a Merge overlapping operations (#438573) Fragment of code in the label operation case: 2454 void Win_GParted::activate_label_partition() 2455 { ... 2472 Add_Operation( operation ) ; 2473 2474 // Verify if the two operations can be merged 2475 for ( unsigned int t = 0 ; t < operations .size() - 1 ; t++ ) 2476 { 2477 if ( operations[ t ] ->type == OPERATION_LABEL_PARTITION ) 2478 { 2479 if ( Merge_Operations( t, operations .size() - 1 ) ) 2480 break; 2481 } 2482 } Commentary in the crashing label operation case: 2472 The pending operation was already applied when Add_Operation() returned resulting in the operations[] vector being cleared setting its size to 0. 2475 The return type of operations.size() is an unsigned integral, so the upper limit of the for loop is t < 0UL - 1. Assuming a 32-bit machine that's t < 4294967295. 2477 operations[] vector is access from out of bounds offset 0 upwards until unallocated memory is accessed resulting in a core dump. Fix this by not enabling the Undo and Apply buttons and processing the GTK event loop until after merging of operations has been performed. Fixed code flow goes like this: activate_*() Add_Operation() Add operation to the operations[] vector Merge operations in the operations[] vector Merge_Operations() show_operationslist() Enable Undo and Apply buttons Refresh_Visual() Process GTK event loop Process Apply button callback applying operations, refreshing display and clearing operations[] vector Not allowing the operations list to be process until after the merge step is the be correct ordering. This also prevents the new operation from flashing up in the operations list and then immediately disappearing if merged. In the case of adding the first operation, delaying enabling the Undo and Apply buttons is enough as the buttons were previously disabled preventing the operation being applied before the merge. In the case of adding further operations, processing of the GTK event loop must also be delayed until after the merge to prevent the operations being applied before the merge. Although that window of opportunity would only be microseconds. Bug #699452 - Crash when applying operations before pending operations fully displayed
2013-05-17 10:01:42 -06:00
show_operationslist() ;
2004-09-19 14:24:53 -06:00
}
void Win_GParted::activate_new()
2004-09-19 14:24:53 -06:00
{
g_assert( selected_partition_ptr != NULL ); // Bug: Partition callback without a selected partition
g_assert( valid_display_partition_ptr( selected_partition_ptr ) ); // Bug: Not pointing at a valid display partition object
// Unrecognised whole disk device (See GParted_Core::get_devices_threads(), "unrecognized")
if ( selected_partition_ptr->whole_device && selected_partition_ptr->type == TYPE_UNALLOCATED )
{
show_disklabel_unrecognized( devices [current_device ] .get_path() ) ;
}
else if ( ! max_amount_prim_reached() )
{
Dialog_Partition_New dialog;
dialog.Set_Data( devices[current_device],
*selected_partition_ptr,
index_extended > -1,
new_count,
gparted_core.get_filesystems() );
dialog .set_transient_for( *this );
if ( dialog .run() == Gtk::RESPONSE_OK )
{
dialog .hide() ;
new_count++ ;
Operation * operation = new OperationCreate( devices[current_device],
*selected_partition_ptr,
dialog.Get_New_Partition( devices[current_device].sector_size ) );
operation ->icon = render_icon( Gtk::Stock::NEW, Gtk::ICON_SIZE_MENU );
Add_Operation( operation );
Only allow Undo and Apply after merging operations (#699452) It was possible to make GParted crash by adding a label, check or new UUID operation and then applying the operation before the view of pending operations had finished fully opening. The operation would be successfully applied but GParted would crash afterwards. The fault was that Add_Operation() still enabled the Undo and Apply buttons and processed the GTK event loop before merging the list of pending operations. Faulty code flow went like this: activate_*() Add_Operation() Add operation to the operations[] vector Enable Undo and Apply buttons Refresh_Visual() Process GTK event loop Process Apply button callback applying operations, refreshing display and clearing operations[] vector Merge operations in the operations[] vector << Core dump here >> Merge_Operations() Refresh_Visual() This faulty code flow came about when merging of operations was added and it didn't appreciate that the operations[] vector could have been processed and cleared by Add_Operations() before the merge step. Relevant commit: b10349ae37df06b7bf7de91ea93a3913535ade3a Merge overlapping operations (#438573) Fragment of code in the label operation case: 2454 void Win_GParted::activate_label_partition() 2455 { ... 2472 Add_Operation( operation ) ; 2473 2474 // Verify if the two operations can be merged 2475 for ( unsigned int t = 0 ; t < operations .size() - 1 ; t++ ) 2476 { 2477 if ( operations[ t ] ->type == OPERATION_LABEL_PARTITION ) 2478 { 2479 if ( Merge_Operations( t, operations .size() - 1 ) ) 2480 break; 2481 } 2482 } Commentary in the crashing label operation case: 2472 The pending operation was already applied when Add_Operation() returned resulting in the operations[] vector being cleared setting its size to 0. 2475 The return type of operations.size() is an unsigned integral, so the upper limit of the for loop is t < 0UL - 1. Assuming a 32-bit machine that's t < 4294967295. 2477 operations[] vector is access from out of bounds offset 0 upwards until unallocated memory is accessed resulting in a core dump. Fix this by not enabling the Undo and Apply buttons and processing the GTK event loop until after merging of operations has been performed. Fixed code flow goes like this: activate_*() Add_Operation() Add operation to the operations[] vector Merge operations in the operations[] vector Merge_Operations() show_operationslist() Enable Undo and Apply buttons Refresh_Visual() Process GTK event loop Process Apply button callback applying operations, refreshing display and clearing operations[] vector Not allowing the operations list to be process until after the merge step is the be correct ordering. This also prevents the new operation from flashing up in the operations list and then immediately disappearing if merged. In the case of adding the first operation, delaying enabling the Undo and Apply buttons is enough as the buttons were previously disabled preventing the operation being applied before the merge. In the case of adding further operations, processing of the GTK event loop must also be delayed until after the merge to prevent the operations being applied before the merge. Although that window of opportunity would only be microseconds. Bug #699452 - Crash when applying operations before pending operations fully displayed
2013-05-17 10:01:42 -06:00
show_operationslist() ;
}
}
2004-09-19 14:24:53 -06:00
}
void Win_GParted::activate_delete()
{
g_assert( selected_partition_ptr != NULL ); // Bug: Partition callback without a selected partition
g_assert( valid_display_partition_ptr( selected_partition_ptr ) ); // Bug: Not pointing at a valid display partition object
// VGNAME from mount mount
if ( selected_partition_ptr->filesystem == FS_LVM2_PV && ! selected_partition_ptr->get_mountpoint().empty() )
{
if ( ! remove_non_empty_lvm2_pv_dialog( OPERATION_DELETE ) )
return ;
}
/* since logicals are *always* numbered from 5 to <last logical> there can be a shift
* in numbers after deletion.
* e.g. consider /dev/hda5 /dev/hda6 /dev/hda7. Now after removal of /dev/hda6,
* /dev/hda7 is renumbered to /dev/hda6
* the new situation is now /dev/hda5 /dev/hda6. If /dev/hda7 was mounted
* the OS cannot find /dev/hda7 anymore and the results aren't that pretty.
* It seems best to check for this and prohibit deletion with some explanation to the user.*/
if ( selected_partition_ptr->type == TYPE_LOGICAL &&
selected_partition_ptr->status != STAT_NEW &&
selected_partition_ptr->partition_number < devices[current_device].highest_busy )
{
Gtk::MessageDialog dialog( *this,
String::ucompose( _("Unable to delete %1!"), selected_partition_ptr->get_path() ),
false,
Gtk::MESSAGE_ERROR,
Gtk::BUTTONS_OK,
true );
dialog .set_secondary_text(
String::ucompose( _("Please unmount any logical partitions having a number higher than %1"),
selected_partition_ptr->partition_number ) );
dialog .run() ;
return;
2004-09-19 14:24:53 -06:00
}
//if partition is on the clipboard...(NOTE: we can't use Partition::== here..)
if ( selected_partition_ptr->get_path() == copied_partition.get_path() )
{
Gtk::MessageDialog dialog( *this,
String::ucompose( _("Are you sure you want to delete %1?"),
selected_partition_ptr->get_path() ),
false,
Gtk::MESSAGE_QUESTION,
Gtk::BUTTONS_NONE,
true );
dialog .set_secondary_text( _("After deletion this partition is no longer available for copying.") ) ;
/*TO TRANSLATORS: dialogtitle, looks like Delete /dev/hda2 (ntfs, 2345 MiB) */
dialog.set_title( String::ucompose( _("Delete %1 (%2, %3)"),
selected_partition_ptr->get_path(),
Utils::get_filesystem_string( selected_partition_ptr->filesystem ),
Utils::format_size( selected_partition_ptr->get_sector_length(), selected_partition_ptr->sector_size ) ) );
dialog .add_button( Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL );
dialog .add_button( Gtk::Stock::DELETE, Gtk::RESPONSE_OK );
dialog .show_all_children() ;
if ( dialog .run() != Gtk::RESPONSE_OK )
return ;
}
//if deleted partition was on the clipboard we erase it...
if ( selected_partition_ptr->get_path() == copied_partition.get_path() )
copied_partition .Reset() ;
// If deleted one is NEW, it doesn't make sense to add it to the operationslist,
// we erase its creation and possible modifications like resize etc.. from the operationslist.
// Calling Refresh_Visual will wipe every memory of its existence ;-)
if ( selected_partition_ptr->status == STAT_NEW )
{
//remove all operations done on this new partition (this includes creation)
for ( int t = 0 ; t < static_cast<int>( operations .size() ) ; t++ )
if ( operations[t]->partition_new.get_path() == selected_partition_ptr->get_path() )
remove_operation( t-- ) ;
//determine lowest possible new_count
new_count = 0 ;
for ( unsigned int t = 0 ; t < operations .size() ; t++ )
if ( operations[ t ] ->partition_new .status == GParted::STAT_NEW &&
operations[ t ] ->partition_new .partition_number > new_count )
new_count = operations[ t ] ->partition_new .partition_number ;
new_count += 1 ;
// Verify if the two operations can be merged
for ( int t = 0 ; t < static_cast<int>( operations .size() - 1 ) ; t++ )
{
Merge_Operations( t, t+1 );
}
Refresh_Visual();
if ( ! operations .size() )
close_operationslist() ;
2004-09-19 14:24:53 -06:00
}
else //deletion of a real partition...
{
Operation * operation = new OperationDelete( devices[ current_device ], *selected_partition_ptr );
operation ->icon = render_icon( Gtk::Stock::DELETE, Gtk::ICON_SIZE_MENU ) ;
Add_Operation( operation ) ;
}
Only allow Undo and Apply after merging operations (#699452) It was possible to make GParted crash by adding a label, check or new UUID operation and then applying the operation before the view of pending operations had finished fully opening. The operation would be successfully applied but GParted would crash afterwards. The fault was that Add_Operation() still enabled the Undo and Apply buttons and processed the GTK event loop before merging the list of pending operations. Faulty code flow went like this: activate_*() Add_Operation() Add operation to the operations[] vector Enable Undo and Apply buttons Refresh_Visual() Process GTK event loop Process Apply button callback applying operations, refreshing display and clearing operations[] vector Merge operations in the operations[] vector << Core dump here >> Merge_Operations() Refresh_Visual() This faulty code flow came about when merging of operations was added and it didn't appreciate that the operations[] vector could have been processed and cleared by Add_Operations() before the merge step. Relevant commit: b10349ae37df06b7bf7de91ea93a3913535ade3a Merge overlapping operations (#438573) Fragment of code in the label operation case: 2454 void Win_GParted::activate_label_partition() 2455 { ... 2472 Add_Operation( operation ) ; 2473 2474 // Verify if the two operations can be merged 2475 for ( unsigned int t = 0 ; t < operations .size() - 1 ; t++ ) 2476 { 2477 if ( operations[ t ] ->type == OPERATION_LABEL_PARTITION ) 2478 { 2479 if ( Merge_Operations( t, operations .size() - 1 ) ) 2480 break; 2481 } 2482 } Commentary in the crashing label operation case: 2472 The pending operation was already applied when Add_Operation() returned resulting in the operations[] vector being cleared setting its size to 0. 2475 The return type of operations.size() is an unsigned integral, so the upper limit of the for loop is t < 0UL - 1. Assuming a 32-bit machine that's t < 4294967295. 2477 operations[] vector is access from out of bounds offset 0 upwards until unallocated memory is accessed resulting in a core dump. Fix this by not enabling the Undo and Apply buttons and processing the GTK event loop until after merging of operations has been performed. Fixed code flow goes like this: activate_*() Add_Operation() Add operation to the operations[] vector Merge operations in the operations[] vector Merge_Operations() show_operationslist() Enable Undo and Apply buttons Refresh_Visual() Process GTK event loop Process Apply button callback applying operations, refreshing display and clearing operations[] vector Not allowing the operations list to be process until after the merge step is the be correct ordering. This also prevents the new operation from flashing up in the operations list and then immediately disappearing if merged. In the case of adding the first operation, delaying enabling the Undo and Apply buttons is enough as the buttons were previously disabled preventing the operation being applied before the merge. In the case of adding further operations, processing of the GTK event loop must also be delayed until after the merge to prevent the operations being applied before the merge. Although that window of opportunity would only be microseconds. Bug #699452 - Crash when applying operations before pending operations fully displayed
2013-05-17 10:01:42 -06:00
show_operationslist() ;
2004-09-19 14:24:53 -06:00
}
void Win_GParted::activate_info()
2004-09-19 14:24:53 -06:00
{
g_assert( selected_partition_ptr != NULL ); // Bug: Partition callback without a selected partition
g_assert( valid_display_partition_ptr( selected_partition_ptr ) ); // Bug: Not pointing at a valid display partition object
Dialog_Partition_Info dialog( *selected_partition_ptr );
dialog .set_transient_for( *this );
dialog .run();
2004-09-19 14:24:53 -06:00
}
void Win_GParted::activate_format( GParted::FILESYSTEM new_fs )
2004-09-19 14:24:53 -06:00
{
g_assert( selected_partition_ptr != NULL ); // Bug: Partition callback without a selected partition
g_assert( valid_display_partition_ptr( selected_partition_ptr ) ); // Bug: Not pointing at a valid display partition object
// VGNAME from mount mount
if ( selected_partition_ptr->filesystem == FS_LVM2_PV && ! selected_partition_ptr->get_mountpoint().empty() )
{
if ( ! remove_non_empty_lvm2_pv_dialog( OPERATION_FORMAT ) )
return ;
}
//check for some limits...
fs = gparted_core .get_fs( new_fs ) ;
if ( ( selected_partition_ptr->get_byte_length() < fs.MIN ) ||
( fs.MAX && selected_partition_ptr->get_byte_length() > fs.MAX ) )
2004-09-19 14:24:53 -06:00
{
Gtk::MessageDialog dialog( *this,
String::ucompose(
/* TO TRANSLATORS: looks like
* Cannot format this file system to fat16.
*/
_( "Cannot format this file system to %1" ),
Utils::get_filesystem_string( new_fs ) ) ,
false,
Gtk::MESSAGE_ERROR,
Gtk::BUTTONS_OK,
true );
if ( selected_partition_ptr->get_byte_length() < fs.MIN )
dialog .set_secondary_text( String::ucompose(
/* TO TRANSLATORS: looks like
* A fat16 file system requires a partition of at least 16.00 MiB.
*/
_( "A %1 file system requires a partition of at least %2."),
2006-03-28 05:40:29 -07:00
Utils::get_filesystem_string( new_fs ),
Utils::format_size( fs .MIN, 1 /* Byte */ ) ) );
else
dialog .set_secondary_text( String::ucompose(
/* TO TRANSLATORS: looks like
* A partition with a hfs file system has a maximum size of 2.00 GiB.
*/
_( "A partition with a %1 file system has a maximum size of %2."),
2006-03-28 05:40:29 -07:00
Utils::get_filesystem_string( new_fs ),
Utils::format_size( fs .MAX, 1 /* Byte */ ) ) );
dialog .run() ;
return ;
2004-09-19 14:24:53 -06:00
}
//ok we made it. lets create an fitting partition object
Partition part_temp ;
part_temp.Set( devices[current_device].get_path(),
selected_partition_ptr->get_path(),
selected_partition_ptr->partition_number,
selected_partition_ptr->type,
selected_partition_ptr->whole_device,
new_fs,
selected_partition_ptr->sector_start,
selected_partition_ptr->sector_end,
devices[current_device].sector_size,
selected_partition_ptr->inside_extended,
false );
part_temp.name = selected_partition_ptr->name;
//Leave sector usage figures to new Partition object defaults of
// -1, -1, 0 (_used, _unused, _unallocated) representing unknown.
part_temp .status = GParted::STAT_FORMATTED ;
// If selected partition is NEW we simply remove the NEW operation from the list and
// add it again with the new file system
if ( selected_partition_ptr->status == STAT_NEW )
2004-09-19 14:24:53 -06:00
{
//remove operation which creates this partition
for ( unsigned int t = 0 ; t < operations .size() ; t++ )
2004-09-19 14:24:53 -06:00
{
if ( operations[t]->partition_new == *selected_partition_ptr )
2004-09-19 14:24:53 -06:00
{
remove_operation( t ) ;
//And insert the new partition at the old position in the operations list
//(NOTE: in this case we set status to STAT_NEW)
part_temp .status = STAT_NEW ;
Operation * operation = new OperationCreate( devices[current_device],
*selected_partition_ptr,
part_temp );
operation ->icon = render_icon( Gtk::Stock::NEW, Gtk::ICON_SIZE_MENU );
Add_Operation( operation, t ) ;
2004-09-19 14:24:53 -06:00
break;
}
}
}
else//normal formatting of an existing partition
{
Operation * operation = new OperationFormat( devices[current_device],
*selected_partition_ptr,
part_temp );
operation ->icon = render_icon( Gtk::Stock::CONVERT, Gtk::ICON_SIZE_MENU );
Add_Operation( operation ) ;
// Try to merge with previous operation
if ( operations .size() >= 2 )
{
Merge_Operations( operations .size() - 2, operations .size() - 1 );
}
}
Only allow Undo and Apply after merging operations (#699452) It was possible to make GParted crash by adding a label, check or new UUID operation and then applying the operation before the view of pending operations had finished fully opening. The operation would be successfully applied but GParted would crash afterwards. The fault was that Add_Operation() still enabled the Undo and Apply buttons and processed the GTK event loop before merging the list of pending operations. Faulty code flow went like this: activate_*() Add_Operation() Add operation to the operations[] vector Enable Undo and Apply buttons Refresh_Visual() Process GTK event loop Process Apply button callback applying operations, refreshing display and clearing operations[] vector Merge operations in the operations[] vector << Core dump here >> Merge_Operations() Refresh_Visual() This faulty code flow came about when merging of operations was added and it didn't appreciate that the operations[] vector could have been processed and cleared by Add_Operations() before the merge step. Relevant commit: b10349ae37df06b7bf7de91ea93a3913535ade3a Merge overlapping operations (#438573) Fragment of code in the label operation case: 2454 void Win_GParted::activate_label_partition() 2455 { ... 2472 Add_Operation( operation ) ; 2473 2474 // Verify if the two operations can be merged 2475 for ( unsigned int t = 0 ; t < operations .size() - 1 ; t++ ) 2476 { 2477 if ( operations[ t ] ->type == OPERATION_LABEL_PARTITION ) 2478 { 2479 if ( Merge_Operations( t, operations .size() - 1 ) ) 2480 break; 2481 } 2482 } Commentary in the crashing label operation case: 2472 The pending operation was already applied when Add_Operation() returned resulting in the operations[] vector being cleared setting its size to 0. 2475 The return type of operations.size() is an unsigned integral, so the upper limit of the for loop is t < 0UL - 1. Assuming a 32-bit machine that's t < 4294967295. 2477 operations[] vector is access from out of bounds offset 0 upwards until unallocated memory is accessed resulting in a core dump. Fix this by not enabling the Undo and Apply buttons and processing the GTK event loop until after merging of operations has been performed. Fixed code flow goes like this: activate_*() Add_Operation() Add operation to the operations[] vector Merge operations in the operations[] vector Merge_Operations() show_operationslist() Enable Undo and Apply buttons Refresh_Visual() Process GTK event loop Process Apply button callback applying operations, refreshing display and clearing operations[] vector Not allowing the operations list to be process until after the merge step is the be correct ordering. This also prevents the new operation from flashing up in the operations list and then immediately disappearing if merged. In the case of adding the first operation, delaying enabling the Undo and Apply buttons is enough as the buttons were previously disabled preventing the operation being applied before the merge. In the case of adding further operations, processing of the GTK event loop must also be delayed until after the merge to prevent the operations being applied before the merge. Although that window of opportunity would only be microseconds. Bug #699452 - Crash when applying operations before pending operations fully displayed
2013-05-17 10:01:42 -06:00
show_operationslist() ;
2004-09-19 14:24:53 -06:00
}
void Win_GParted::unmount_partition( bool * succes, Glib::ustring * error )
{
std::vector<Glib::ustring> errors, failed_mountpoints, mountpoints = GParted_Core::get_all_mountpoints() ;
Glib::ustring dummy ;
*succes = true ;
for ( unsigned int t = 0 ; t < selected_partition_ptr->get_mountpoints().size() ; t++ )
if ( std::count( mountpoints.begin(),
mountpoints.end(),
selected_partition_ptr->get_mountpoints()[t] ) <= 1 )
{
Glib::ustring cmd = "umount -v \"" + selected_partition_ptr->get_mountpoints()[t] + "\"";
if ( Utils::execute_command( cmd, dummy, *error ) )
{
*succes = false ;
errors.push_back( "# " + cmd + "\n" + *error );
}
}
else
failed_mountpoints.push_back( selected_partition_ptr->get_mountpoints()[t] );
if ( *succes && failed_mountpoints .size() )
{
*succes = false ;
*error = _("The partition could not be unmounted from the following mount points:") ;
*error += "\n\n<i>" + Glib::build_path( "\n", failed_mountpoints ) + "</i>\n\n" ;
*error += _("Most likely other partitions are also mounted on these mount points. You are advised to unmount them manually.") ;
}
else
*error = "<i>" + Glib::build_path( "\n", errors ) + "</i>" ;
}
void Win_GParted::toggle_busy_state()
{
g_assert( selected_partition_ptr != NULL ); // Bug: Partition callback without a selected partition
g_assert( valid_display_partition_ptr( selected_partition_ptr ) ); // Bug: Not pointing at a valid display partition object
int operation_count = partition_in_operation_queue_count( *selected_partition_ptr );
bool success = false ;
Glib::ustring cmd;
Glib::ustring output;
Glib::ustring error;
if ( operation_count > 0 )
{
//Note that this situation will only occur when trying to swapon a partition
// or activate the Volume Group of a Physical Volume. This is because
// GParted does not permit queueing operations on partitions that are
// currently active (i.e., swap enabled, mounted or active VG). Hence
// this situation will not occur for the swapoff, unmount or deactivate VG
// actions that this method handles.
/*TO TRANSLATORS: Singular case looks like 1 operation is currently pending for partition /dev/sdd8. */
Glib::ustring tmp_msg =
String::ucompose( ngettext( "%1 operation is currently pending for partition %2"
, "%1 operations are currently pending for partition %2"
, operation_count
)
, operation_count
, selected_partition_ptr->get_path()
) ;
Gtk::MessageDialog dialog( *this
, tmp_msg
, false
, Gtk::MESSAGE_INFO
, Gtk::BUTTONS_OK
, true
) ;
if ( selected_partition_ptr->filesystem == FS_LINUX_SWAP )
{
tmp_msg = _( "The swapon action cannot be performed if an operation is pending for the partition." ) ;
tmp_msg += "\n" ;
tmp_msg += _( "Use the Edit menu to undo, clear, or apply operations before using swapon with this partition." ) ;
}
else if ( selected_partition_ptr->filesystem == FS_LVM2_PV )
{
tmp_msg = _( "The activate Volume Group action cannot be performed if an operation is pending for the partition." ) ;
tmp_msg += "\n" ;
tmp_msg += _( "Use the Edit menu to undo, clear, or apply operations before using activate Volume Group with this partition." ) ;
}
dialog .set_secondary_text( tmp_msg ) ;
dialog .run() ;
return ;
}
if ( selected_partition_ptr->filesystem == FS_LINUX_SWAP )
{
show_pulsebar(
String::ucompose(
selected_partition_ptr->busy ? _("Deactivating swap on %1") : _("Activating swap on %1"),
selected_partition_ptr->get_path() ) );
if ( selected_partition_ptr->busy )
cmd = "swapoff -v " + selected_partition_ptr->get_path();
else
cmd = "swapon -v " + selected_partition_ptr->get_path();
success = ! Utils::execute_command( cmd, output, error );
hide_pulsebar();
if ( ! success )
{
Gtk::MessageDialog dialog(
*this,
selected_partition_ptr->busy ? _("Could not deactivate swap") : _("Could not activate swap"),
false,
Gtk::MESSAGE_ERROR,
Gtk::BUTTONS_OK,
true ) ;
dialog.set_secondary_text( "# " + cmd + "\n" + error );
dialog.run() ;
}
}
else if ( selected_partition_ptr->filesystem == FS_LVM2_PV )
{
show_pulsebar(
String::ucompose(
selected_partition_ptr->busy ? _("Deactivating Volume Group %1")
: _("Activating Volume Group %1"),
// VGNAME from mount point
selected_partition_ptr->get_mountpoint() ) );
if ( selected_partition_ptr->busy )
// VGNAME from mount point
cmd = "lvm vgchange -a n " + selected_partition_ptr->get_mountpoint();
else
cmd = "lvm vgchange -a y " + selected_partition_ptr->get_mountpoint();
success = ! Utils::execute_command( cmd, output, error );
hide_pulsebar();
if ( ! success )
{
Gtk::MessageDialog dialog(
*this,
selected_partition_ptr->busy ? _("Could not deactivate Volume Group")
: _("Could not activate Volume Group"),
false,
Gtk::MESSAGE_ERROR,
Gtk::BUTTONS_OK,
true ) ;
dialog.set_secondary_text( "# " + cmd + "\n" + error );
dialog.run() ;
}
}
else if ( selected_partition_ptr->busy )
{
show_pulsebar( String::ucompose( _("Unmounting %1"), selected_partition_ptr->get_path() ) );
unmount_partition( &success, &error );
hide_pulsebar();
if ( ! success )
{
Gtk::MessageDialog dialog( *this,
String::ucompose( _("Could not unmount %1"),
selected_partition_ptr->get_path() ),
false,
Gtk::MESSAGE_ERROR,
Gtk::BUTTONS_OK,
true );
dialog .set_secondary_text( error, true ) ;
dialog.run() ;
}
}
menu_gparted_refresh_devices() ;
}
void Win_GParted::activate_mount_partition( unsigned int index )
{
g_assert( selected_partition_ptr != NULL ); // Bug: Partition callback without a selected partition
g_assert( valid_display_partition_ptr( selected_partition_ptr ) ); // Bug: Not pointing at a valid display partition object
int operation_count = partition_in_operation_queue_count( *selected_partition_ptr );
if ( operation_count > 0 )
{
/*TO TRANSLATORS: Plural case looks like 4 operations are currently pending for partition /dev/sdd8. */
Glib::ustring tmp_msg =
String::ucompose( ngettext( "%1 operation is currently pending for partition %2"
, "%1 operations are currently pending for partition %2"
, operation_count
)
, operation_count
, selected_partition_ptr->get_path()
) ;
Gtk::MessageDialog dialog( *this
, tmp_msg
, false
, Gtk::MESSAGE_INFO
, Gtk::BUTTONS_OK
, true
) ;
tmp_msg = _( "The mount action cannot be performed if an operation is pending for the partition." ) ;
tmp_msg += "\n" ;
tmp_msg += _( "Use the Edit menu to undo, clear, or apply operations before using mount with this partition." ) ;
dialog .set_secondary_text( tmp_msg ) ;
dialog .run() ;
return ;
}
bool success = false ;
Glib::ustring cmd;
Glib::ustring output;
Glib::ustring error;
Glib::ustring message;
show_pulsebar( String::ucompose( _("mounting %1 on %2"),
selected_partition_ptr->get_path(),
selected_partition_ptr->get_mountpoints()[index] ) );
// First try mounting letting mount (libblkid) determine the file system type.
// Do this because GParted uses libparted first and blkid second and when there
// are multiple signatures GParted may report a different result to blkid alone.
cmd = "mount -v " + selected_partition_ptr->get_path() +
" \"" + selected_partition_ptr->get_mountpoints()[index] + "\"";
success = ! Utils::execute_command( cmd, output, error );
if ( ! success )
{
message = "# " + cmd + "\n" + error;
Glib::ustring type = Utils::get_filesystem_kernel_name( selected_partition_ptr->filesystem );
if ( ! type.empty() )
{
// Second try mounting specifying the GParted determined file
// system type.
cmd = "mount -v -t " + type + " " + selected_partition_ptr->get_path() +
" \"" + selected_partition_ptr->get_mountpoints()[index] + "\"";
success = ! Utils::execute_command( cmd, output, error );
if ( ! success )
message += "\n# " + cmd + "\n" + error;
}
}
hide_pulsebar();
if ( ! success )
{
Gtk::MessageDialog dialog( *this,
String::ucompose( _("Could not mount %1 on %2"),
selected_partition_ptr->get_path(),
selected_partition_ptr->get_mountpoints()[index] ),
false,
Gtk::MESSAGE_ERROR,
Gtk::BUTTONS_OK,
true );
dialog.set_secondary_text( message );
dialog.run() ;
}
menu_gparted_refresh_devices() ;
}
void Win_GParted::activate_disklabel()
{
//If there are active mounted partitions on the device then warn
// the user that all partitions must be unactive before creating
// a new partition table
int active_count = active_partitions_on_device_count( devices[ current_device ] ) ;
if ( active_count > 0 )
{
Glib::ustring tmp_msg =
String::ucompose( /*TO TRANSLATORS: Singular case looks like 1 partition is currently active on device /dev/sda */
ngettext( "%1 partition is currently active on device %2"
/*TO TRANSLATORS: Plural case looks like 3 partitions are currently active on device /dev/sda */
, "%1 partitions are currently active on device %2"
, active_count
)
, active_count
, devices[ current_device ] .get_path()
) ;
Gtk::MessageDialog dialog( *this
, tmp_msg
, false
, Gtk::MESSAGE_INFO
, Gtk::BUTTONS_OK
, true
) ;
tmp_msg = _( "A new partition table cannot be created when there are active partitions." ) ;
tmp_msg += " " ;
tmp_msg += _( "Active partitions are those that are in use, such as a mounted file system, or enabled swap space." ) ;
tmp_msg += "\n" ;
tmp_msg += _( "Use Partition menu options, such as unmount or swapoff, to deactivate all partitions on this device before creating a new partition table." ) ;
dialog .set_secondary_text( tmp_msg ) ;
dialog .run() ;
return ;
}
//If there are pending operations then warn the user that these
// operations must either be applied or cleared before creating
// a new partition table.
if ( operations .size() )
{
Glib::ustring tmp_msg =
String::ucompose( ngettext( "%1 operation is currently pending"
, "%1 operations are currently pending"
, operations .size()
)
, operations .size()
) ;
Gtk::MessageDialog dialog( *this
, tmp_msg
, false
, Gtk::MESSAGE_INFO
, Gtk::BUTTONS_OK
, true
) ;
tmp_msg = _( "A new partition table cannot be created when there are pending operations." ) ;
tmp_msg += "\n" ;
tmp_msg += _( "Use the Edit menu to either clear or apply all operations before creating a new partition table." ) ;
dialog .set_secondary_text( tmp_msg ) ;
dialog .run() ;
return ;
}
//Display dialog for creating a new partition table.
Dialog_Disklabel dialog( devices[ current_device ] ) ;
dialog .set_transient_for( *this );
if ( dialog .run() == Gtk::RESPONSE_APPLY )
{
if ( ! gparted_core.set_disklabel( devices[current_device], dialog.Get_Disklabel() ) )
{
Gtk::MessageDialog dialog( *this,
_("Error while creating partition table"),
true,
Gtk::MESSAGE_ERROR,
Gtk::BUTTONS_OK,
true ) ;
dialog .run() ;
}
dialog .hide() ;
menu_gparted_refresh_devices() ;
}
}
//Runs when the Device->Attempt Rescue Data is clicked
void Win_GParted::activate_attempt_rescue_data()
{
if(Glib::find_program_in_path( "gpart" ) .empty()) //Gpart must be installed to continue
{
Gtk::MessageDialog errorDialog(*this, "", true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
errorDialog.set_message(_("Command gpart was not found"));
errorDialog.set_secondary_text(_("This feature uses gpart. Please install gpart and try again."));
errorDialog.run();
return;
}
//Dialog information
Glib::ustring sec_text = _( "A full disk scan is needed to find file systems." ) ;
sec_text += "\n" ;
sec_text +=_("The scan might take a very long time.");
sec_text += "\n" ;
sec_text += _("After the scan you can mount any discovered file systems and copy the data to other media.") ;
sec_text += "\n" ;
sec_text += _("Do you want to continue?");
Gtk::MessageDialog messageDialog(*this, "", true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_OK_CANCEL, true);
/*TO TRANSLATORS: looks like Search for file systems on /deb/sdb */
messageDialog.set_message(String::ucompose(_("Search for file systems on %1"), devices[ current_device ] .get_path()));
messageDialog.set_secondary_text(sec_text);
if(messageDialog.run()!=Gtk::RESPONSE_OK)
{
return;
}
messageDialog.hide();
/*TO TRANSLATORS: looks like Searching for file systems on /deb/sdb */
show_pulsebar(String::ucompose( _("Searching for file systems on %1"), devices[ current_device ] .get_path()));
gpart_output="";
gparted_core.guess_partition_table(devices[ current_device ], gpart_output);
hide_pulsebar();
Dialog_Rescue_Data dialog;
dialog .set_transient_for( *this );
//Reads the output of gpart
dialog.init_partitions(&devices[ current_device ], this->gpart_output);
if(dialog.get_partitions().size()==0) //No partitions found
{
//Dialog information
Gtk::MessageDialog errorDialog(*this, "", true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
/*TO TRANSLATORS: looks like No file systems found on /deb/sdb */
errorDialog.set_message(String::ucompose(_("No file systems found on %1"), devices[ current_device ] .get_path()));
errorDialog.set_secondary_text(_("The disk scan by gpart did not find any recognizable file systems on this disk."));
errorDialog.run();
return;
}
dialog.run();
dialog.hide();
Glib::ustring commandUmount= "umount /tmp/gparted-roview*";
Utils::execute_command(commandUmount);
menu_gparted_refresh_devices() ;
}
void Win_GParted::activate_manage_flags()
{
g_assert( selected_partition_ptr != NULL ); // Bug: Partition callback without a selected partition
g_assert( valid_display_partition_ptr( selected_partition_ptr ) ); // Bug: Not pointing at a valid display partition object
get_window() ->set_cursor( Gdk::Cursor( Gdk::WATCH ) ) ;
while ( Gtk::Main::events_pending() )
Gtk::Main::iteration() ;
DialogManageFlags dialog( *selected_partition_ptr, gparted_core.get_available_flags( *selected_partition_ptr ) );
dialog .set_transient_for( *this ) ;
dialog .signal_get_flags .connect(
sigc::mem_fun( &gparted_core, &GParted_Core::get_available_flags ) ) ;
dialog .signal_toggle_flag .connect(
sigc::mem_fun( &gparted_core, &GParted_Core::toggle_flag ) ) ;
get_window() ->set_cursor() ;
dialog .run() ;
dialog .hide() ;
if ( dialog .any_change )
menu_gparted_refresh_devices() ;
}
void Win_GParted::activate_check()
{
g_assert( selected_partition_ptr != NULL ); // Bug: Partition callback without a selected partition
g_assert( valid_display_partition_ptr( selected_partition_ptr ) ); // Bug: Not pointing at a valid display partition object
Operation * operation = new OperationCheck( devices[current_device], *selected_partition_ptr );
operation ->icon = render_icon( Gtk::Stock::EXECUTE, Gtk::ICON_SIZE_MENU );
Add_Operation( operation ) ;
// Verify if the two operations can be merged
for ( unsigned int t = 0 ; t < operations .size() - 1 ; t++ )
{
if ( operations[ t ] ->type == OPERATION_CHECK )
{
if( Merge_Operations( t, operations .size() -1 ) )
break;
}
}
Only allow Undo and Apply after merging operations (#699452) It was possible to make GParted crash by adding a label, check or new UUID operation and then applying the operation before the view of pending operations had finished fully opening. The operation would be successfully applied but GParted would crash afterwards. The fault was that Add_Operation() still enabled the Undo and Apply buttons and processed the GTK event loop before merging the list of pending operations. Faulty code flow went like this: activate_*() Add_Operation() Add operation to the operations[] vector Enable Undo and Apply buttons Refresh_Visual() Process GTK event loop Process Apply button callback applying operations, refreshing display and clearing operations[] vector Merge operations in the operations[] vector << Core dump here >> Merge_Operations() Refresh_Visual() This faulty code flow came about when merging of operations was added and it didn't appreciate that the operations[] vector could have been processed and cleared by Add_Operations() before the merge step. Relevant commit: b10349ae37df06b7bf7de91ea93a3913535ade3a Merge overlapping operations (#438573) Fragment of code in the label operation case: 2454 void Win_GParted::activate_label_partition() 2455 { ... 2472 Add_Operation( operation ) ; 2473 2474 // Verify if the two operations can be merged 2475 for ( unsigned int t = 0 ; t < operations .size() - 1 ; t++ ) 2476 { 2477 if ( operations[ t ] ->type == OPERATION_LABEL_PARTITION ) 2478 { 2479 if ( Merge_Operations( t, operations .size() - 1 ) ) 2480 break; 2481 } 2482 } Commentary in the crashing label operation case: 2472 The pending operation was already applied when Add_Operation() returned resulting in the operations[] vector being cleared setting its size to 0. 2475 The return type of operations.size() is an unsigned integral, so the upper limit of the for loop is t < 0UL - 1. Assuming a 32-bit machine that's t < 4294967295. 2477 operations[] vector is access from out of bounds offset 0 upwards until unallocated memory is accessed resulting in a core dump. Fix this by not enabling the Undo and Apply buttons and processing the GTK event loop until after merging of operations has been performed. Fixed code flow goes like this: activate_*() Add_Operation() Add operation to the operations[] vector Merge operations in the operations[] vector Merge_Operations() show_operationslist() Enable Undo and Apply buttons Refresh_Visual() Process GTK event loop Process Apply button callback applying operations, refreshing display and clearing operations[] vector Not allowing the operations list to be process until after the merge step is the be correct ordering. This also prevents the new operation from flashing up in the operations list and then immediately disappearing if merged. In the case of adding the first operation, delaying enabling the Undo and Apply buttons is enough as the buttons were previously disabled preventing the operation being applied before the merge. In the case of adding further operations, processing of the GTK event loop must also be delayed until after the merge to prevent the operations being applied before the merge. Although that window of opportunity would only be microseconds. Bug #699452 - Crash when applying operations before pending operations fully displayed
2013-05-17 10:01:42 -06:00
show_operationslist() ;
}
void Win_GParted::activate_label_filesystem()
{
g_assert( selected_partition_ptr != NULL ); // Bug: Partition callback without a selected partition
g_assert( valid_display_partition_ptr( selected_partition_ptr ) ); // Bug: Not pointing at a valid display partition object
Dialog_FileSystem_Label dialog( *selected_partition_ptr );
dialog .set_transient_for( *this );
if ( dialog .run() == Gtk::RESPONSE_OK
&& dialog.get_new_label() != selected_partition_ptr->get_filesystem_label() )
{
dialog .hide() ;
// Make a duplicate of the selected partition (used in UNDO)
Partition part_temp = *selected_partition_ptr;
part_temp.set_filesystem_label( dialog.get_new_label() );
Operation * operation = new OperationLabelFileSystem( devices[current_device],
*selected_partition_ptr, part_temp );
operation ->icon = render_icon( Gtk::Stock::EXECUTE, Gtk::ICON_SIZE_MENU );
Add_Operation( operation ) ;
// Verify if the two operations can be merged
for ( unsigned int t = 0 ; t < operations .size() - 1 ; t++ )
{
if ( operations[t]->type == OPERATION_LABEL_FILESYSTEM )
{
if( Merge_Operations( t, operations .size() -1 ) )
break;
}
}
Only allow Undo and Apply after merging operations (#699452) It was possible to make GParted crash by adding a label, check or new UUID operation and then applying the operation before the view of pending operations had finished fully opening. The operation would be successfully applied but GParted would crash afterwards. The fault was that Add_Operation() still enabled the Undo and Apply buttons and processed the GTK event loop before merging the list of pending operations. Faulty code flow went like this: activate_*() Add_Operation() Add operation to the operations[] vector Enable Undo and Apply buttons Refresh_Visual() Process GTK event loop Process Apply button callback applying operations, refreshing display and clearing operations[] vector Merge operations in the operations[] vector << Core dump here >> Merge_Operations() Refresh_Visual() This faulty code flow came about when merging of operations was added and it didn't appreciate that the operations[] vector could have been processed and cleared by Add_Operations() before the merge step. Relevant commit: b10349ae37df06b7bf7de91ea93a3913535ade3a Merge overlapping operations (#438573) Fragment of code in the label operation case: 2454 void Win_GParted::activate_label_partition() 2455 { ... 2472 Add_Operation( operation ) ; 2473 2474 // Verify if the two operations can be merged 2475 for ( unsigned int t = 0 ; t < operations .size() - 1 ; t++ ) 2476 { 2477 if ( operations[ t ] ->type == OPERATION_LABEL_PARTITION ) 2478 { 2479 if ( Merge_Operations( t, operations .size() - 1 ) ) 2480 break; 2481 } 2482 } Commentary in the crashing label operation case: 2472 The pending operation was already applied when Add_Operation() returned resulting in the operations[] vector being cleared setting its size to 0. 2475 The return type of operations.size() is an unsigned integral, so the upper limit of the for loop is t < 0UL - 1. Assuming a 32-bit machine that's t < 4294967295. 2477 operations[] vector is access from out of bounds offset 0 upwards until unallocated memory is accessed resulting in a core dump. Fix this by not enabling the Undo and Apply buttons and processing the GTK event loop until after merging of operations has been performed. Fixed code flow goes like this: activate_*() Add_Operation() Add operation to the operations[] vector Merge operations in the operations[] vector Merge_Operations() show_operationslist() Enable Undo and Apply buttons Refresh_Visual() Process GTK event loop Process Apply button callback applying operations, refreshing display and clearing operations[] vector Not allowing the operations list to be process until after the merge step is the be correct ordering. This also prevents the new operation from flashing up in the operations list and then immediately disappearing if merged. In the case of adding the first operation, delaying enabling the Undo and Apply buttons is enough as the buttons were previously disabled preventing the operation being applied before the merge. In the case of adding further operations, processing of the GTK event loop must also be delayed until after the merge to prevent the operations being applied before the merge. Although that window of opportunity would only be microseconds. Bug #699452 - Crash when applying operations before pending operations fully displayed
2013-05-17 10:01:42 -06:00
show_operationslist() ;
}
}
void Win_GParted::activate_name_partition()
{
g_assert( selected_partition_ptr != NULL ); // Bug: Partition callback without a selected partition
g_assert( valid_display_partition_ptr( selected_partition_ptr ) ); // Bug: Not pointing at a valid display partition object
Dialog_Partition_Name dialog( *selected_partition_ptr,
devices[current_device].get_max_partition_name_length() );
dialog.set_transient_for( *this );
if ( dialog.run() == Gtk::RESPONSE_OK
&& dialog.get_new_name() != selected_partition_ptr->name )
{
dialog.hide();
// Make a duplicate of the selected partition (used in UNDO)
Partition part_temp = *selected_partition_ptr;
part_temp.name = dialog.get_new_name();
Operation * operation = new OperationNamePartition( devices[current_device],
*selected_partition_ptr, part_temp );
operation->icon = render_icon( Gtk::Stock::EXECUTE, Gtk::ICON_SIZE_MENU );
Add_Operation( operation );
// Verify if the two operations can be merged
for ( unsigned int t = 0 ; t < operations.size() - 1 ; t++ )
{
if ( operations[t]->type == OPERATION_NAME_PARTITION )
{
if( Merge_Operations( t, operations.size() - 1 ) )
break;
}
}
show_operationslist();
}
}
void Win_GParted::activate_change_uuid()
{
g_assert( selected_partition_ptr != NULL ); // Bug: Partition callback without a selected partition
g_assert( valid_display_partition_ptr( selected_partition_ptr ) ); // Bug: Not pointing at a valid display partition object
const FileSystem * filesystem_object = gparted_core.get_filesystem_object( selected_partition_ptr->filesystem );
if ( filesystem_object->get_custom_text( CTEXT_CHANGE_UUID_WARNING ) != "" )
{
int i ;
Gtk::MessageDialog dialog( *this,
filesystem_object->get_custom_text( CTEXT_CHANGE_UUID_WARNING, 0 ),
false,
Gtk::MESSAGE_WARNING,
Gtk::BUTTONS_OK,
true );
Glib::ustring tmp_msg = "" ;
for ( i = 1 ; filesystem_object->get_custom_text( CTEXT_CHANGE_UUID_WARNING, i ) != "" ; i++ )
{
if ( i > 1 )
tmp_msg += "\n\n" ;
tmp_msg += filesystem_object->get_custom_text( CTEXT_CHANGE_UUID_WARNING, i );
}
dialog .set_secondary_text( tmp_msg ) ;
dialog .run() ;
}
// Make a duplicate of the selected partition (used in UNDO)
Partition part_temp = *selected_partition_ptr;
if ( part_temp .filesystem == GParted::FS_NTFS )
//Explicitly ask for half, so that the user will be aware of it
//Also, keep this kind of policy out of the NTFS code.
part_temp .uuid = UUID_RANDOM_NTFS_HALF ;
else
part_temp .uuid = UUID_RANDOM ;
Operation * operation = new OperationChangeUUID( devices[current_device],
*selected_partition_ptr, part_temp );
operation ->icon = render_icon( Gtk::Stock::EXECUTE, Gtk::ICON_SIZE_MENU );
Add_Operation( operation ) ;
// Verify if the two operations can be merged
for ( unsigned int t = 0 ; t < operations .size() - 1 ; t++ )
{
if ( operations[ t ] ->type == OPERATION_CHANGE_UUID )
{
if( Merge_Operations( t, operations .size() -1 ) )
break;
}
}
Only allow Undo and Apply after merging operations (#699452) It was possible to make GParted crash by adding a label, check or new UUID operation and then applying the operation before the view of pending operations had finished fully opening. The operation would be successfully applied but GParted would crash afterwards. The fault was that Add_Operation() still enabled the Undo and Apply buttons and processed the GTK event loop before merging the list of pending operations. Faulty code flow went like this: activate_*() Add_Operation() Add operation to the operations[] vector Enable Undo and Apply buttons Refresh_Visual() Process GTK event loop Process Apply button callback applying operations, refreshing display and clearing operations[] vector Merge operations in the operations[] vector << Core dump here >> Merge_Operations() Refresh_Visual() This faulty code flow came about when merging of operations was added and it didn't appreciate that the operations[] vector could have been processed and cleared by Add_Operations() before the merge step. Relevant commit: b10349ae37df06b7bf7de91ea93a3913535ade3a Merge overlapping operations (#438573) Fragment of code in the label operation case: 2454 void Win_GParted::activate_label_partition() 2455 { ... 2472 Add_Operation( operation ) ; 2473 2474 // Verify if the two operations can be merged 2475 for ( unsigned int t = 0 ; t < operations .size() - 1 ; t++ ) 2476 { 2477 if ( operations[ t ] ->type == OPERATION_LABEL_PARTITION ) 2478 { 2479 if ( Merge_Operations( t, operations .size() - 1 ) ) 2480 break; 2481 } 2482 } Commentary in the crashing label operation case: 2472 The pending operation was already applied when Add_Operation() returned resulting in the operations[] vector being cleared setting its size to 0. 2475 The return type of operations.size() is an unsigned integral, so the upper limit of the for loop is t < 0UL - 1. Assuming a 32-bit machine that's t < 4294967295. 2477 operations[] vector is access from out of bounds offset 0 upwards until unallocated memory is accessed resulting in a core dump. Fix this by not enabling the Undo and Apply buttons and processing the GTK event loop until after merging of operations has been performed. Fixed code flow goes like this: activate_*() Add_Operation() Add operation to the operations[] vector Merge operations in the operations[] vector Merge_Operations() show_operationslist() Enable Undo and Apply buttons Refresh_Visual() Process GTK event loop Process Apply button callback applying operations, refreshing display and clearing operations[] vector Not allowing the operations list to be process until after the merge step is the be correct ordering. This also prevents the new operation from flashing up in the operations list and then immediately disappearing if merged. In the case of adding the first operation, delaying enabling the Undo and Apply buttons is enough as the buttons were previously disabled preventing the operation being applied before the merge. In the case of adding further operations, processing of the GTK event loop must also be delayed until after the merge to prevent the operations being applied before the merge. Although that window of opportunity would only be microseconds. Bug #699452 - Crash when applying operations before pending operations fully displayed
2013-05-17 10:01:42 -06:00
show_operationslist() ;
}
void Win_GParted::activate_undo()
2004-09-19 14:24:53 -06:00
{
//when undoing a creation it's safe to decrease the newcount by one
if ( operations .back() ->type == OPERATION_CREATE )
2004-10-06 09:32:40 -06:00
new_count-- ;
remove_operation() ;
Refresh_Visual();
if ( ! operations .size() )
close_operationslist() ;
//FIXME: A slight flicker may be introduced by this extra display refresh.
//An extra display refresh seems to prevent the disk area visual disk from
// disappearing when there enough operations to require a scrollbar
// (about 4+ operations with default window size) and a user clicks on undo.
// See also Win_GParted::Add_operation().
drawingarea_visualdisk .queue_draw() ;
2004-09-19 14:24:53 -06:00
}
void Win_GParted::remove_operation( int index, bool remove_all )
{
if ( remove_all )
{
for ( unsigned int t = 0 ; t < operations .size() ; t++ )
delete operations[ t ] ;
operations .clear() ;
}
else if ( index == -1 && operations .size() > 0 )
{
delete operations .back() ;
operations .pop_back() ;
}
else if ( index > -1 && index < static_cast<int>( operations .size() ) )
{
delete operations[ index ] ;
operations .erase( operations .begin() + index ) ;
}
}
2004-09-19 14:24:53 -06:00
int Win_GParted::partition_in_operation_queue_count( const Partition & partition )
{
int operation_count = 0 ;
for ( unsigned int t = 0 ; t < operations .size() ; t++ )
{
if ( partition .get_path() == operations[ t ] ->partition_original .get_path() )
operation_count++ ;
}
return operation_count ;
}
int Win_GParted::active_partitions_on_device_count( const Device & device )
{
int active_count = 0 ;
//Count the active partitions on the device
for ( unsigned int k=0; k < device .partitions .size(); k++ )
{
//Count the active primary partitions
if ( device .partitions[ k ] .busy
&& device .partitions[ k ] .type != TYPE_EXTENDED
&& device .partitions[ k ] .type != TYPE_UNALLOCATED
)
active_count++ ;
//Count the active logical partitions
if ( device .partitions[ k ] .busy
&& device .partitions[ k ] .type == TYPE_EXTENDED
)
{
for ( unsigned int j=0; j < device .partitions[ k ] .logicals .size(); j++ )
{
if ( device .partitions[ k ] .logicals [ j ] .busy
&& device .partitions[ k ] .logicals [ j ] .type != TYPE_UNALLOCATED
)
active_count++ ;
}
}
}
return active_count ;
}
void Win_GParted::activate_apply()
2004-09-19 14:24:53 -06:00
{
Gtk::MessageDialog dialog( *this,
_("Are you sure you want to apply the pending operations?"),
false,
Gtk::MESSAGE_WARNING,
Gtk::BUTTONS_NONE,
true );
Glib::ustring temp;
temp = _( "Editing partitions has the potential to cause LOSS of DATA.") ;
temp += "\n" ;
temp += _( "You are advised to backup your data before proceeding." ) ;
dialog .set_secondary_text( temp ) ;
dialog .set_title( _( "Apply operations to device" ) );
dialog .add_button( Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL );
dialog .add_button( Gtk::Stock::APPLY, Gtk::RESPONSE_OK );
dialog .show_all_children() ;
if ( dialog.run() == Gtk::RESPONSE_OK )
2004-09-19 14:24:53 -06:00
{
dialog .hide() ; //hide confirmationdialog
Dialog_Progress dialog_progress( operations ) ;
dialog_progress .set_transient_for( *this ) ;
dialog_progress .signal_apply_operation .connect(
sigc::mem_fun(gparted_core, &GParted_Core::apply_operation_to_disk) ) ;
dialog_progress .signal_get_libparted_version .connect(
sigc::mem_fun(gparted_core, &GParted_Core::get_libparted_version) ) ;
int response ;
do
{
response = dialog_progress .run() ;
}
while ( response == Gtk::RESPONSE_CANCEL || response == Gtk::RESPONSE_OK ) ;
dialog_progress .hide() ;
2004-09-19 14:24:53 -06:00
//wipe operations...
remove_operation( -1, true ) ;
hbox_operations .clear() ;
close_operationslist() ;
//reset new_count to 1
new_count = 1 ;
2004-09-19 14:24:53 -06:00
//reread devices and their layouts...
menu_gparted_refresh_devices() ;
2004-09-19 14:24:53 -06:00
}
}
bool Win_GParted::remove_non_empty_lvm2_pv_dialog( const OperationType optype )
{
Glib::ustring tmp_msg ;
switch ( optype )
{
case OPERATION_DELETE:
tmp_msg = String::ucompose( _( "You are deleting non-empty LVM2 Physical Volume %1" ),
selected_partition_ptr->get_path() );
break ;
case OPERATION_FORMAT:
tmp_msg = String::ucompose( _( "You are formatting over non-empty LVM2 Physical Volume %1" ),
selected_partition_ptr->get_path() );
break ;
case OPERATION_COPY:
tmp_msg = String::ucompose( _( "You are pasting over non-empty LVM2 Physical Volume %1" ),
selected_partition_ptr->get_path() );
break ;
default:
break ;
}
Gtk::MessageDialog dialog( *this, tmp_msg,
false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE, true ) ;
tmp_msg = _( "Deleting or overwriting the Physical Volume is irrecoverable and will destroy or damage the "
" Volume Group." ) ;
tmp_msg += "\n\n" ;
tmp_msg += _( "To avoid destroying or damaging the Volume Group, you are advised to cancel and use external "
"LVM commands to free the Physical Volume before attempting this operation." ) ;
tmp_msg += "\n\n" ;
tmp_msg += _( "Do you want to continue to forcibly delete the Physical Volume?" ) ;
LVM2_PV_Info lvm2_pv_info ;
Glib::ustring vgname = lvm2_pv_info.get_vg_name( selected_partition_ptr->get_path() );
std::vector<Glib::ustring> members ;
if ( ! vgname .empty() )
members = lvm2_pv_info .get_vg_members( vgname ) ;
//Single copy of each string for translation purposes
const Glib::ustring vgname_label = _( "Volume Group:" ) ;
const Glib::ustring members_label = _( "Members:" ) ;
#ifndef HAVE_GET_MESSAGE_AREA
//Basic method of displaying VG members by appending it to the secondary text in the dialog.
tmp_msg += "\n____________________\n\n" ;
tmp_msg += "<b>" ;
tmp_msg += vgname_label ;
tmp_msg += "</b> " ;
tmp_msg += vgname ;
tmp_msg += "\n" ;
tmp_msg += "<b>" ;
tmp_msg += members_label ;
tmp_msg += "</b>" ;
if ( ! members .empty() )
{
tmp_msg += " " ;
tmp_msg += members [0] ;
for ( unsigned int i = 1 ; i < members .size() ; i ++ )
{
tmp_msg += " " ;
tmp_msg += members [i] ;
}
}
#endif /* ! HAVE_GET_MESSAGE_AREA */
dialog .set_secondary_text( tmp_msg, true ) ;
#ifdef HAVE_GET_MESSAGE_AREA
//Nicely formatted method of displaying VG members by using a table below the secondary text
// in the dialog. Uses Gtk::MessageDialog::get_message_area() which was new in gtkmm-2.22
// released September 2010.
Gtk::Box * msg_area = dialog .get_message_area() ;
Gtk::HSeparator * hsep( manage( new Gtk::HSeparator() ) ) ;
msg_area ->pack_start( * hsep ) ;
Gtk::Table * table( manage( new Gtk::Table() ) ) ;
table ->set_border_width( 0 ) ;
table ->set_col_spacings( 10 ) ;
msg_area ->pack_start( * table ) ;
int top = 0, bottom = 1 ;
//Volume Group
table ->attach( * Utils::mk_label( "<b>" + Glib::ustring( vgname_label ) + "</b>" ),
0, 1, top, bottom, Gtk::FILL ) ;
table ->attach( * Utils::mk_label( vgname, true, false, true ),
1, 2, top++, bottom++, Gtk::FILL ) ;
//Members
table ->attach( * Utils::mk_label( "<b>" + Glib::ustring( members_label ) + "</b>",
true, false, false, 0.0 /* ALIGN_TOP */ ),
0, 1, top, bottom, Gtk::FILL ) ;
Glib::ustring members_str = "" ;
if ( ! members .empty() )
{
for ( unsigned int i = 0 ; i < members .size() ; i ++ )
{
if ( i > 0 )
members_str += "\n" ;
members_str += members[i] ;
}
}
table ->attach( * Utils::mk_label( members_str, true, false, true, 0.0 /* ALIGN_TOP */ ),
1, 2, top++, bottom++, Gtk::FILL ) ;
#endif /* HAVE_GET_MESSAGE_AREA */
dialog .add_button( Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL );
dialog .add_button( Gtk::Stock::DELETE, Gtk::RESPONSE_OK );
dialog .show_all() ;
if ( dialog .run() == Gtk::RESPONSE_OK )
return true ;
return false ;
}
2004-09-19 14:24:53 -06:00
} // GParted