gparted/include/Win_GParted.h

318 lines
9.6 KiB
C
Raw Normal View History

/* Copyright (C) 2004 Bart
* Copyright (C) 2008, 2009, 2010 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
*/
#ifndef GPARTED_WIN_GPARTED_H
#define GPARTED_WIN_GPARTED_H
2004-09-19 14:24:53 -06:00
#include "Device.h"
#include "DrawingAreaVisualDisk.h"
#include "Partition.h"
#include "PartitionVector.h"
#include "TreeView_Detail.h"
#include "GParted_Core.h"
#include "HBoxOperations.h"
2004-09-19 14:24:53 -06:00
#include <gtkmm/paned.h>
#include <gtkmm/toolbar.h>
#include <gtkmm/separatortoolitem.h>
#include <gtkmm/checkmenuitem.h>
2004-09-19 14:24:53 -06:00
#include <gtkmm/menubar.h>
#include <gtkmm/statusbar.h>
#include <gtkmm/combobox.h>
#include <gtkmm/progressbar.h>
#include <gtkmm/window.h>
2004-09-19 14:24:53 -06:00
namespace GParted
{
enum MergeType
{
MERGE_LAST_WITH_PREV = 0,
MERGE_LAST_WITH_ANY = 1,
MERGE_ALL_ADJACENT = 2
};
2004-09-19 14:24:53 -06:00
class Win_GParted : public Gtk::Window
{
public:
Win_GParted( const std::vector<Glib::ustring> & user_devices ) ;
~Win_GParted();
2004-09-19 14:24:53 -06:00
private:
Win_GParted( const Win_GParted & src ); // Not implemented copy constructor
Win_GParted & operator=( const Win_GParted & rhs ); // Not implemented copy assignment operator
void init_menubar() ;
void init_toolbar() ;
void init_partition_menu() ;
Gtk::Menu * create_format_menu() ;
void create_format_menu_add_item(FSType fstype, bool activate);
void init_device_info() ;
void init_hpaned_main() ;
void add_custom_css();
2004-09-19 14:24:53 -06:00
void refresh_combo_devices() ;
void show_pulsebar( const Glib::ustring & status_message ) ;
void hide_pulsebar();
void Fill_Label_Device_Info( bool clear = false );
2004-09-19 14:24:53 -06:00
Fix snap-to-alignment of operations creating partitions (#779339) Using the default MiB alignment, creating an MSDOS logical partition between two other existing logical partitions fails with this error dialog: (-) <b>An error occurred while applying the operations</b> See the details for more information. <b>IMPORTANT</b> If you want support, you need to provide the saved details! See http://gparted.org/save-details.htm for more information. [ OK ] and these operation details: + libparted messages - Unable to satisfy all constraints on the partition. This bug was introduced by this commit included in GParted 0.23.0: 90e3ed68fc60c1395c2f3c20ed0b4493037753fb Shallow copy Device object into Operation object (#750168) The commit message claimed that the deep copied Partition objects inside the Device inside the Operation object are never accessed. This turned out not to be true. Win_GParted::Add_Operation() uses them as part of snap_to_alignment() which updates requested partition boundaries to account for alignment requirements and the space needed for EBR (Extended Boot Record) preceding logical partitions. In this case the new logical partition was trying to be created over the top of the EBR for the following logical partition because snap_to_alignment() wasn't aware of its existence. Fix by making Add_Operation() and snap_to_alignment() refer to the current device, as displayed in the UI, rather than the shallow copy included in the Operation object. Hopefully now it is true that the not copied vector of Partition objects in the Device object in each Operation object are never accessed. Bug 779339 - enforce at least 1 MiB "free space following"
2017-04-21 01:30:39 -06:00
void Add_Operation( const Device & device, Operation * operation );
bool merge_two_operations( unsigned int first, unsigned int second );
void merge_operations( MergeType mergetype );
void Refresh_Visual();
bool valid_display_partition_ptr( const Partition * partition_ptr );
bool Quit_Check_Operations();
void set_valid_operations() ;
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 show_operationslist() ;
2004-09-19 14:24:53 -06:00
//convenience functions
void toggle_item( bool state, int menu_item, int toolbar_item = -1 )
{
if (menu_item >= 0 && partitionmenu_items.count(menu_item))
partitionmenu_items[menu_item]->set_sensitive(state);
if ( toolbar_item >= 0 && toolbar_item < toolbar_main .get_n_items() )
toolbar_main .get_nth_item( toolbar_item ) ->set_sensitive( state ) ;
}
void allow_new( bool state ) {
toggle_item( state, MENU_NEW, TOOLBAR_NEW ) ; }
void allow_delete( bool state ) {
toggle_item( state, MENU_DEL, TOOLBAR_DEL ) ; }
void allow_resize( bool state ) {
toggle_item( state, MENU_RESIZE_MOVE, TOOLBAR_RESIZE_MOVE ) ; }
void allow_copy( bool state ) {
toggle_item( state, MENU_COPY, TOOLBAR_COPY ) ; }
void allow_paste( bool state ) {
toggle_item( state, MENU_PASTE, TOOLBAR_PASTE ) ; }
void allow_format( bool state ) {
toggle_item( state, MENU_FORMAT ) ; }
void allow_toggle_crypt_busy_state( bool state ) {
toggle_item( state, MENU_TOGGLE_CRYPT_BUSY ); }
void allow_toggle_fs_busy_state( bool state ) {
toggle_item( state, MENU_TOGGLE_FS_BUSY ); }
void allow_manage_flags( bool state ) {
toggle_item( state, MENU_FLAGS ) ; }
void allow_check( bool state ) {
toggle_item( state, MENU_CHECK ) ; }
void allow_label_filesystem( bool state ) {
toggle_item(state, MENU_LABEL_FILESYSTEM); }
void allow_name_partition( bool state ) {
toggle_item( state, MENU_NAME_PARTITION ); }
void allow_change_uuid( bool state ) {
toggle_item( state, MENU_CHANGE_UUID ) ; }
void allow_info( bool state ) {
toggle_item( state, MENU_INFO ) ; }
void allow_undo_clear_apply( bool state )
{
toggle_item( state, -1, TOOLBAR_UNDO ) ;
static_cast<Gtk::CheckMenuItem *>(mainmenu_items[MENU_UNDO_OPERATION])
->set_sensitive( state ) ;
static_cast<Gtk::CheckMenuItem *>(mainmenu_items[MENU_CLEAR_OPERATIONS])
->set_sensitive( state ) ;
toggle_item( state, -1, TOOLBAR_APPLY ) ;
static_cast<Gtk::CheckMenuItem *>(mainmenu_items[MENU_APPLY_OPERATIONS])
->set_sensitive( state ) ;
}
Refactor Win_GParted::unmount_partition() (#775932) The primary reason to refactor unmount_partition() is to pass the Partition object to be unmounted, rather than use member variable selected_partition_ptr so that it doesn't have to handle the differences between encrypted and non-encrypted Partition objects. The calling function can deal with that instead. Then there were lots of small reasons to change almost every other line too: * Return success or failure rather than updating a passed pointer with the result. Leftover from when the function used to be a separate thread: commit 52a2a9b00a32996921ace055e71d0e09fb33c5fe Reduce threading (#685740) * Pass updated error string by reference rather than by pointer. Likely another leftover. * Stop borrowing the updated error string as a local variable for the error output from the umount command. Use new umount_error local variable instead. Was bad practice making the code harder to understand. * Rename failed_mountpoints to skipped_mountpoints to better reflect that it contains the mount points not attempted to be unmounted because two or more file systems are mounted at that point. * Rename errors to umount_errors to better reflect it contains the errors from each failed umount command. * Document the reason for mount points being skipped. * Update the skipped mount points message to state definitely why they could not be unmounted rather than stating most likely. * Simplify logic processing the error cases and return value. * Made static because it no longer accesses any class members. * Remove out dated "//threads.." comment from the header. Another leftover from when the function use to be a separate thread. Bug 775932 - Refactor mostly applying of operations
2016-09-10 12:34:10 -06:00
static bool unmount_partition( const Partition & partition, Glib::ustring & error );
2004-09-19 14:24:53 -06:00
//signal handlers
void open_operationslist() ;
void close_operationslist() ;
void clear_operationslist() ;
void combo_devices_changed();
void radio_devices_changed( unsigned int item ) ;
bool on_delete_event( GdkEventAny* ) ;
void on_show() ;
Make GParted exit when closed before the initial load completes (#771816) If the GParted main window is closed before the initial device load completed gpartedbin never exits. The main window closes but the process sits there idle forever. Subsequently running GParted reports this error: # gparted The process gpartedbin is already running. Only one gpartedbin process is permitted. If the user is running GParted from a desktop menu they will never see this error so they will never know why GParted won't start any more. More technically, it is if the main window is closed before the Win_GParted::on_show() callback completes. I assume the Gtk main loop doesn't setup the normal quit handling until the on_show() callback finishes drawing the main window for the first time. Following this hint [1], move the initial device load from the on_show() callback to immediately after it completes by using a run once idle callback setup in on_show(). This looks exactly the same to the user except now gpartedbin exits when the main window is closed during the initial device load. Note that GParted finished the device load before exiting. This is exactly the same as happens when exiting during subsequent device refreshes. [1] How to know when a Gtk Window is fully shown? http://stackoverflow.com/questions/14663212/how-to-know-when-a-gtk-window-is-fully-shown "If you want to know when your main window appears the first time, it is far easier (and saner) add a g_idle_add after your show_all call." Bug 771816 - GParted never exits if the main window is closed before the initial device load completes
2016-09-21 06:49:02 -06:00
static gboolean initial_device_refresh( gpointer data );
void menu_gparted_refresh_devices();
void menu_gparted_features();
void menu_gparted_quit();
void menu_view_harddisk_info();
void menu_view_operations();
void show_disklabel_unrecognized(const Glib::ustring& device_name);
Prevent online resizing of file systems mounted read-only (#10) Resizing a file system mounted read-only fails. Example: # mkfs.btrfs /dev/sdb1 # mount -o ro /dev/sdb1 /mnt/1 In GParted try to resize partition sdb1. The operation fails like this: Grow /dev/sdb1 from 512.00 MiB to 1.00 GiB (ERROR) * calibrate /dev/sdb1 (SUCCESS) * grow partition from 512.00 MiB to 1.00 GiB (SUCCESS) * grow filesystem to fill the partition (ERROR) * btrfs filesystem resize 1:max '/mnt/1' (ERROR) Resize '/mnt/1' of '1:max' ERROR: unable to resize '/mnt/1': Read-only file system See GitLab issue for the testing results of attempting to online resize all supporting file system while mounted read-only. No file system allows online resizing while mounted read-only, except for reiserfs. Issue #10 - Gparted fails to resize btrfs partition that is mounted read-only https://gitlab.gnome.org/GNOME/gparted/issues/10 Fix by preventing online resizing of *all* file systems mounted read-only, including reiserfs. Instead of displaying the resize dialog in this case, display an information dialog explaining why the partition can't be resized. This is similar to what happens when attempting to create a new partition on a disk without a partition table. The new dialog looks like: (!) Unable to resize read-only file system /dev/sdb1 The file system can not be resized while it is mounted read-only. Either unmount the file system or remount it read-write. [ OK ] Closes #10 - Gparted fails to resize btrfs partition that is mounted read-only
2018-09-17 10:20:03 -06:00
void show_resize_readonly( const Glib::ustring & path );
void show_help(const Glib::ustring & filename, const Glib::ustring & link_id);
void menu_help_contents();
void menu_help_about();
2004-09-19 14:24:53 -06:00
void on_partition_selected( const Partition * partition_ptr, bool src_is_treeview );
void on_partition_activated() ;
void on_partition_popup_menu( unsigned int button, unsigned int time ) ;
bool max_amount_prim_reached() ;
2004-09-19 14:24:53 -06:00
void activate_resize();
Ask for LUKS passphrase for resize operation as required (#59) When composing a resize operation on an open encryption mapping, use the existing LUKS password dialog to prompt for the passphrase, if and only if 'cryptsetup resize' will prompt and GParted doesn't already have a password. 'cryptsetup resize' will prompt for a LUKS passphrase when the passphrase was stored in the kernel keyring service, key_loc == KEYLOC_KeyRing. See the previous commit "Capture LUKS mapping master encryption key location (#59)" for more details. As commented in the code GParted specifically doesn't support the case where the LUKS passphrase is changed while GParted is running and it knew the old passphrase. When resizing an open encryption mapping GParted will just pass the old out of date passphrase it knows and the resize will fail like this: # cryptsetup status sdb2_crypt | egrep 'type|key location' type: LUKS2 key location: keyring # dmsetup table --target crypt sdb2_crypt: 0 491520 crypt aes-xts-plain64 :64:logon:cryptsetup:3d040240-97ba-4559-af98-72c3be500498-d0 0 8:18 32768 # echo -n oldpassword | cryptsetup -v --size 491520 resize sdb2_crypt No key available with this passphrase. Command failed with code -2 (no permission or bad passphrase). # echo $? 2 To work around this either close and restart GParted or close and reopen the encryption mapping. The former because GParted doesn't save passwords across a restart so will prompt and the latter because GParted will use the wrong old passphrase to try to open the mapping and then prompt for the correct passphrase until successfully opened. Closes #59 - Resize of LUKS2 encrypted file system fails with "Nothing to read on input"
2021-03-26 15:16:06 -06:00
bool ask_for_password_for_encrypted_resize_as_required(const Partition& partition);
void activate_copy();
void activate_paste();
void activate_new();
void activate_delete();
void activate_info();
void activate_format( FSType new_fs );
bool open_encrypted_partition( const Partition & partition, const char * password, Glib::ustring & message );
void toggle_crypt_busy_state();
bool check_toggle_busy_allowed( const Glib::ustring & disallowed_msg );
void show_toggle_failure_dialog( const Glib::ustring & failure_summary,
const Glib::ustring & marked_up_error );
void toggle_fs_busy_state();
void activate_mount_partition( unsigned int index ) ;
void activate_disklabel() ;
void activate_attempt_rescue_data();
void activate_manage_flags() ;
void activate_check() ;
void activate_change_uuid() ;
void activate_label_filesystem();
void activate_name_partition();
void activate_undo();
void remove_operation( int index = -1, bool remove_all = false ) ;
int partition_in_operation_queue_count( const Partition & partition ) ;
int active_partitions_on_device_count( const Device & device ) ;
void activate_apply();
bool remove_non_empty_lvm2_pv_dialog( const OperationType optype ) ;
2004-09-19 14:24:53 -06:00
//private variables
unsigned int current_device ;
std::vector<Device> devices;
Device m_display_device; // Copy of devices[current_device] with pending operations
// operations applied to partitions for displaying in the UI.
const Partition * selected_partition_ptr; // Pointer to the selected partition. (Alias to element
// in Win_GParted::m_display_device.partitions[] vector).
const Partition * copied_partition; // NULL or copy of source partition object.
std::vector<Operation *> operations;
2004-09-19 14:24:53 -06:00
//gui stuff
Gtk::Paned hpaned_main;
Gtk::Paned vpaned_main;
Gtk::Box vbox_main;
Gtk::Box vbox_info;
Gtk::Box hbox_toolbar;
2004-09-19 14:24:53 -06:00
Gtk::Toolbar toolbar_main;
Gtk::MenuBar menubar_main;
Gtk::ComboBox combo_devices ;
Gtk::Menu menu_partition, *menu ;
2004-09-19 14:24:53 -06:00
Gtk::ToolButton *toolbutton;
Gtk::Statusbar statusbar;
Gtk::Image *image ;
Gtk::ScrolledWindow *scrollwindow;
Gtk::ProgressBar pulsebar ;
Gtk::TreeRow treerow;
2004-09-19 14:24:53 -06:00
DrawingAreaVisualDisk drawingarea_visualdisk ;
2004-09-19 14:24:53 -06:00
TreeView_Detail treeview_detail;
HBoxOperations hbox_operations ;
//device combo
Glib::RefPtr<Gtk::ListStore> liststore_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
sigc::connection combo_devices_changed_connection;
struct treeview_devices_Columns : public Gtk::TreeModelColumnRecord
{
Gtk::TreeModelColumn< Glib::RefPtr<Gdk::Pixbuf> > icon ;
Gtk::TreeModelColumn<Glib::ustring> device ;
Gtk::TreeModelColumn<Glib::ustring> size ;
treeview_devices_Columns()
{
add( icon ) ;
add( device ) ;
add( size ) ;
}
};
treeview_devices_Columns treeview_devices_columns ;
2004-09-19 14:24:53 -06:00
// Indices for toolbar
int
TOOLBAR_NEW,
TOOLBAR_DEL,
TOOLBAR_RESIZE_MOVE,
TOOLBAR_COPY,
TOOLBAR_PASTE,
TOOLBAR_UNDO,
TOOLBAR_APPLY;
enum MainMenu_Items
{
MENU_DEVICES = 0,
MENU_EDIT,
MENU_UNDO_OPERATION,
MENU_CLEAR_OPERATIONS,
MENU_APPLY_OPERATIONS,
MENU_VIEW,
MENU_DEVICE_INFORMATION,
MENU_PENDING_OPERATIONS,
MENU_DEVICE,
MENU_PARTITION
};
enum PartitionMenu_Items
{
MENU_NEW = 0,
MENU_DEL,
MENU_RESIZE_MOVE,
MENU_COPY,
MENU_PASTE,
MENU_FORMAT,
MENU_TOGGLE_CRYPT_BUSY,
MENU_TOGGLE_FS_BUSY,
MENU_MOUNT,
MENU_NAME_PARTITION,
MENU_FLAGS,
MENU_CHECK,
MENU_LABEL_FILESYSTEM,
MENU_CHANGE_UUID,
MENU_INFO
};
std::map<int, Gtk::MenuItem*> mainmenu_items;
std::map<int, Gtk::MenuItem*> partitionmenu_items;
2004-09-19 14:24:53 -06:00
//usefull variables which are used by many different functions...
unsigned short new_count;//new_count keeps track of the new created partitions
bool OPERATIONSLIST_OPEN ;
GParted_Core gparted_core ;
std::vector<Gtk::Label *> device_info ;
//stuff for progress overview and pulsebar
bool pulsebar_pulse();
sigc::connection pulsetimer;
2004-09-19 14:24:53 -06:00
};
} //GParted
#endif /* GPARTED_WIN_GPARTED_H */