4535 lines
161 KiB
C++
4535 lines
161 KiB
C++
/* Copyright (C) 2004 Bart 'plors' Hakvoort
|
|
* Copyright (C) 2008, 2009, 2010, 2011, 2012 Curtis Gedak
|
|
*
|
|
* 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "Win_GParted.h"
|
|
#include "GParted_Core.h"
|
|
#include "CopyBlocks.h"
|
|
#include "BlockSpecial.h"
|
|
#include "DMRaid.h"
|
|
#include "FileSystem.h"
|
|
#include "FS_Info.h"
|
|
#include "LVM2_PV_Info.h"
|
|
#include "LUKS_Info.h"
|
|
#include "Mount_Info.h"
|
|
#include "Operation.h"
|
|
#include "OperationCopy.h"
|
|
#include "Partition.h"
|
|
#include "PartitionLUKS.h"
|
|
#include "PartitionVector.h"
|
|
#include "Proc_Partitions_Info.h"
|
|
#include "SWRaid_Info.h"
|
|
#include "Utils.h"
|
|
#include "../config.h"
|
|
|
|
#include "btrfs.h"
|
|
#include "exfat.h"
|
|
#include "ext2.h"
|
|
#include "f2fs.h"
|
|
#include "fat16.h"
|
|
#include "linux_swap.h"
|
|
#include "lvm2_pv.h"
|
|
#include "luks.h"
|
|
#include "reiserfs.h"
|
|
#include "minix.h"
|
|
#include "nilfs2.h"
|
|
#include "ntfs.h"
|
|
#include "xfs.h"
|
|
#include "jfs.h"
|
|
#include "hfs.h"
|
|
#include "hfsplus.h"
|
|
#include "reiser4.h"
|
|
#include "udf.h"
|
|
|
|
#include <parted/parted.h>
|
|
#include <cerrno>
|
|
#include <cstring>
|
|
#include <sys/types.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <glibmm/miscutils.h>
|
|
#include <glibmm/fileutils.h>
|
|
#include <glibmm/shell.h>
|
|
#include <gtkmm/messagedialog.h>
|
|
#include <gtkmm/main.h>
|
|
|
|
std::vector<Glib::ustring> libparted_messages ; //see ped_exception_handler()
|
|
|
|
namespace GParted
|
|
{
|
|
|
|
const std::time_t SETTLE_DEVICE_PROBE_MAX_WAIT_SECONDS = 1;
|
|
const std::time_t SETTLE_DEVICE_APPLY_MAX_WAIT_SECONDS = 10;
|
|
|
|
static bool udevadm_found = false;
|
|
static bool udevsettle_found = false;
|
|
static bool hdparm_found = false;
|
|
|
|
static const Glib::ustring GPARTED_BUG( _("GParted Bug") );
|
|
|
|
GParted_Core::GParted_Core()
|
|
{
|
|
thread_status_message = "" ;
|
|
|
|
ped_exception_set_handler( ped_exception_handler ) ;
|
|
|
|
//get valid flags ...
|
|
for ( PedPartitionFlag flag = ped_partition_flag_next( static_cast<PedPartitionFlag>( NULL ) ) ;
|
|
flag ;
|
|
flag = ped_partition_flag_next( flag ) )
|
|
flags .push_back( flag ) ;
|
|
|
|
find_supported_core();
|
|
|
|
//initialize file system list
|
|
init_filesystems() ;
|
|
|
|
//Determine file system support capabilities for the first time
|
|
find_supported_filesystems() ;
|
|
}
|
|
|
|
|
|
Glib::ustring GParted_Core::get_version_and_config_string()
|
|
{
|
|
Glib::ustring str = Glib::ustring("GParted ") + VERSION + "\n";
|
|
|
|
str += "configuration";
|
|
bool added_config_flag = false;
|
|
#ifdef USE_LIBPARTED_DMRAID
|
|
str += " --enable-libparted-dmraid";
|
|
added_config_flag = true;
|
|
#endif
|
|
#ifdef ENABLE_ONLINE_RESIZE
|
|
str += " --enable-online-resize";
|
|
added_config_flag = true;
|
|
#endif
|
|
if (! added_config_flag)
|
|
str += " (none)";
|
|
str += "\n";
|
|
|
|
str += Glib::ustring("libparted ") + ped_get_version();
|
|
|
|
return str;
|
|
}
|
|
|
|
|
|
void GParted_Core::find_supported_core()
|
|
{
|
|
udevadm_found = ! Glib::find_program_in_path( "udevadm" ).empty();
|
|
udevsettle_found = ! Glib::find_program_in_path( "udevsettle" ).empty();
|
|
hdparm_found = ! Glib::find_program_in_path( "hdparm" ).empty();
|
|
}
|
|
|
|
void GParted_Core::find_supported_filesystems()
|
|
{
|
|
std::map< FSType, FileSystem * >::iterator f;
|
|
|
|
// Iteration of std::map is ordered according to operator< of the key. Hence the
|
|
// FILESYSTEMS vector is constructed in FSType enum order: FS_UNKNOWN, FS_BTRFS,
|
|
// ..., FS_XFS, ... . This ultimately controls the default order of the file
|
|
// systems in the menus and dialogs.
|
|
FILESYSTEMS .clear() ;
|
|
|
|
for ( f = FILESYSTEM_MAP .begin() ; f != FILESYSTEM_MAP .end() ; f++ ) {
|
|
if ( f ->second )
|
|
{
|
|
FILESYSTEMS .push_back( f ->second ->get_filesystem_support() ) ;
|
|
}
|
|
else
|
|
{
|
|
// For basic supported file systems create the supported action
|
|
// set.
|
|
FS fs_basicsupp( f->first );
|
|
fs_basicsupp.move = FS::GPARTED;
|
|
fs_basicsupp.copy = FS::GPARTED;
|
|
FILESYSTEMS.push_back( fs_basicsupp );
|
|
}
|
|
}
|
|
}
|
|
|
|
void GParted_Core::set_user_devices( const std::vector<Glib::ustring> & user_devices )
|
|
{
|
|
this ->device_paths = user_devices ;
|
|
this ->probe_devices = ! user_devices .size() ;
|
|
}
|
|
|
|
void GParted_Core::set_devices( std::vector<Device> & devices )
|
|
{
|
|
Glib::Thread::create( sigc::bind(
|
|
sigc::mem_fun( *this, &GParted_Core::set_devices_thread ),
|
|
&devices),
|
|
false );
|
|
Gtk::Main::run();
|
|
}
|
|
|
|
static bool _mainquit( void *dummy )
|
|
{
|
|
Gtk::Main::quit();
|
|
return false;
|
|
}
|
|
|
|
void GParted_Core::set_devices_thread( std::vector<Device> * pdevices )
|
|
{
|
|
std::vector<Device> &devices = *pdevices;
|
|
devices .clear() ;
|
|
BlockSpecial::clear_cache(); // MUST BE FIRST. Cache of name to major, minor
|
|
// numbers incrementally loaded when BlockSpecial
|
|
// objects are created in the following caches.
|
|
Proc_Partitions_Info::load_cache(); // SHOULD BE SECOND. Caches /proc/partitions and
|
|
// pre-populates BlockSpecial cache.
|
|
FS_Info::load_cache(); // SHOULD BE THRID. Caches file system details
|
|
// from blkid output.
|
|
DMRaid dmraid( true ) ; //Refresh cache of dmraid device information
|
|
LVM2_PV_Info::clear_cache(); // Cache automatically loaded if and when needed
|
|
btrfs::clear_cache(); // Cache incrementally loaded if and when needed
|
|
SWRaid_Info::load_cache();
|
|
LUKS_Info::clear_cache(); // Cache automatically loaded if and when needed
|
|
Mount_Info::load_cache();
|
|
|
|
//only probe if no devices were specified as arguments..
|
|
if ( probe_devices )
|
|
{
|
|
device_paths .clear() ;
|
|
|
|
//FIXME: When libparted bug 194 is fixed, remove code to read:
|
|
// /proc/partitions
|
|
// This was a problem with no floppy drive yet BIOS indicated one existed.
|
|
// http://parted.alioth.debian.org/cgi-bin/trac.cgi/ticket/194
|
|
//
|
|
//try to find all available devices if devices exist in /proc/partitions
|
|
std::vector<Glib::ustring> temp_devices = Proc_Partitions_Info::get_device_paths();
|
|
if ( ! temp_devices .empty() )
|
|
{
|
|
//Try to find all devices in /proc/partitions
|
|
for (unsigned int k=0; k < temp_devices .size(); k++)
|
|
{
|
|
/*TO TRANSLATORS: looks like Scanning /dev/sda */
|
|
set_thread_status_message( Glib::ustring::compose( _("Scanning %1"), temp_devices[ k ] ) ) ;
|
|
ped_device_get( temp_devices[ k ] .c_str() ) ;
|
|
}
|
|
|
|
//Try to find all dmraid devices
|
|
if (dmraid .is_dmraid_supported() ) {
|
|
std::vector<Glib::ustring> dmraid_devices ;
|
|
dmraid .get_devices( dmraid_devices ) ;
|
|
for ( unsigned int k=0; k < dmraid_devices .size(); k++ ) {
|
|
set_thread_status_message( Glib::ustring::compose( _("Scanning %1"), dmraid_devices[k] ) ) ;
|
|
#ifndef USE_LIBPARTED_DMRAID
|
|
dmraid .create_dev_map_entries( dmraid_devices[k] ) ;
|
|
settle_device( SETTLE_DEVICE_PROBE_MAX_WAIT_SECONDS );
|
|
#endif
|
|
ped_device_get( dmraid_devices[k] .c_str() ) ;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//No devices found in /proc/partitions so use libparted to probe devices
|
|
ped_device_probe_all();
|
|
}
|
|
|
|
PedDevice* lp_device = ped_device_get_next( NULL ) ;
|
|
while ( lp_device )
|
|
{
|
|
/* TO TRANSLATORS: looks like Confirming /dev/sda */
|
|
set_thread_status_message( Glib::ustring::compose( _("Confirming %1"), lp_device->path ) );
|
|
|
|
//only add this device if we can read the first sector (which means it's a real device)
|
|
if ( useable_device( lp_device ) )
|
|
device_paths.push_back( lp_device->path );
|
|
|
|
lp_device = ped_device_get_next( lp_device ) ;
|
|
}
|
|
|
|
std::sort( device_paths .begin(), device_paths .end() ) ;
|
|
}
|
|
else
|
|
{
|
|
//Device paths were passed in on the command line.
|
|
|
|
// Sort name device paths and remove duplicates. Avoids repeated scanning
|
|
// the same device and showing it multiple times in the UI.
|
|
// Reference:
|
|
// What's the most efficient way to erase duplicates and sort a vector?
|
|
// http://stackoverflow.com/questions/1041620/whats-the-most-efficient-way-to-erase-duplicates-and-sort-a-vector
|
|
std::sort( device_paths.begin(), device_paths.end() );
|
|
device_paths.erase( std::unique( device_paths.begin(), device_paths.end() ), device_paths.end() );
|
|
|
|
for ( unsigned int t = 0 ; t < device_paths .size() ; t++ )
|
|
{
|
|
set_thread_status_message( Glib::ustring::compose( _("Confirming %1"), device_paths[t] ) );
|
|
|
|
#ifndef USE_LIBPARTED_DMRAID
|
|
// Ensure that dmraid device entries are created
|
|
if ( dmraid .is_dmraid_supported() &&
|
|
dmraid .is_dmraid_device( device_paths[t] ) )
|
|
{
|
|
dmraid .create_dev_map_entries( dmraid .get_dmraid_name( device_paths [t] ) ) ;
|
|
settle_device( SETTLE_DEVICE_PROBE_MAX_WAIT_SECONDS );
|
|
}
|
|
#endif
|
|
|
|
PedDevice* lp_device = ped_device_get( device_paths[t].c_str() );
|
|
if ( lp_device == NULL || ! useable_device( lp_device ) )
|
|
{
|
|
// Remove this disk device which isn't useable
|
|
device_paths.erase( device_paths.begin() + t-- );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Ensure all named paths have FS_Info blkid cache entries specifically so that
|
|
// command line named file system image files, which blkid can't otherwise know
|
|
// about, can be identified.
|
|
FS_Info::load_cache_for_paths( device_paths );
|
|
|
|
for ( unsigned int t = 0 ; t < device_paths .size() ; t++ )
|
|
{
|
|
/*TO TRANSLATORS: looks like Searching /dev/sda partitions */
|
|
set_thread_status_message( Glib::ustring::compose( _("Searching %1 partitions"), device_paths[ t ] ) ) ;
|
|
Device temp_device;
|
|
set_device_from_disk( temp_device, device_paths[t] );
|
|
devices.push_back( temp_device );
|
|
}
|
|
|
|
set_thread_status_message("") ;
|
|
g_idle_add( (GSourceFunc)_mainquit, NULL );
|
|
}
|
|
|
|
// runs gpart on the specified parameter
|
|
void GParted_Core::guess_partition_table(const Device & device, Glib::ustring &buff)
|
|
{
|
|
Glib::ustring error;
|
|
Glib::ustring cmd = "gpart -s " + Utils::num_to_str( device.sector_size ) +
|
|
" " + Glib::shell_quote( device.get_path() );
|
|
Utils::execute_command( cmd, buff, error, true );
|
|
}
|
|
|
|
void GParted_Core::set_thread_status_message( Glib::ustring msg )
|
|
{
|
|
//Remember to clear status message when finished with thread.
|
|
thread_status_message = msg ;
|
|
}
|
|
|
|
Glib::ustring GParted_Core::get_thread_status_message( )
|
|
{
|
|
return thread_status_message ;
|
|
}
|
|
|
|
bool GParted_Core::snap_to_cylinder( const Device & device, Partition & partition, Glib::ustring & error )
|
|
{
|
|
Sector diff = 0;
|
|
|
|
//Determine if partition size is less than half a disk cylinder
|
|
bool less_than_half_cylinder = false;
|
|
if ( ( partition .sector_end - partition .sector_start ) < ( device .cylsize / 2 ) )
|
|
less_than_half_cylinder = true;
|
|
|
|
if ( partition.type == TYPE_LOGICAL ||
|
|
partition.sector_start == device .sectors
|
|
)
|
|
{
|
|
//Must account the relative offset between:
|
|
// (A) the Extended Boot Record sector and the next track of the
|
|
// logical partition (usually 63 sectors), and
|
|
// (B) the Master Boot Record sector and the next track of the first
|
|
// primary partition
|
|
diff = (partition .sector_start - device .sectors) % device .cylsize ;
|
|
}
|
|
else if ( partition.sector_start == 34 )
|
|
{
|
|
// (C) the GUID Partition Table (GPT) and the start of the data
|
|
// partition at sector 34
|
|
diff = (partition .sector_start - 34 ) % device .cylsize ;
|
|
}
|
|
else
|
|
{
|
|
diff = partition .sector_start % device .cylsize ;
|
|
}
|
|
if ( diff && ! partition .strict_start )
|
|
{
|
|
if ( diff < ( device .cylsize / 2 ) || less_than_half_cylinder )
|
|
partition .sector_start -= diff ;
|
|
else
|
|
partition .sector_start += (device .cylsize - diff ) ;
|
|
}
|
|
|
|
diff = (partition .sector_end +1) % device .cylsize ;
|
|
if ( diff )
|
|
{
|
|
if ( diff < ( device .cylsize / 2 ) && ! less_than_half_cylinder )
|
|
partition .sector_end -= diff ;
|
|
else
|
|
partition .sector_end += (device .cylsize - diff ) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
bool GParted_Core::snap_to_mebibyte( const Device & device, Partition & partition, Glib::ustring & error )
|
|
{
|
|
Sector diff = 0;
|
|
if ( partition .sector_start < 2 || partition .type == TYPE_LOGICAL )
|
|
{
|
|
//Must account the relative offset between:
|
|
// (A) the Master Boot Record sector and the first primary/extended partition, and
|
|
// (B) the Extended Boot Record sector and the logical partition
|
|
|
|
//If strict_start is set then do not adjust sector start.
|
|
//If this partition is not simply queued for a reformat then
|
|
// add space minimum to force alignment to next mebibyte.
|
|
if ( (! partition .strict_start)
|
|
&& (partition .free_space_before == 0)
|
|
&& ( partition .status != STAT_FORMATTED)
|
|
)
|
|
{
|
|
//Unless specifically told otherwise, the Linux kernel considers extended
|
|
// boot records to be two sectors long, in order to "leave room for LILO".
|
|
partition .sector_start += 2 ;
|
|
}
|
|
}
|
|
|
|
//Calculate difference offset from Mebibyte boundary
|
|
diff = Sector(partition .sector_start % ( MEBIBYTE / partition .sector_size ));
|
|
|
|
//Align start sector only if permitted to change start sector
|
|
if ( diff && ( (! partition .strict_start)
|
|
|| ( partition .strict_start
|
|
&& ( partition .status == STAT_NEW
|
|
|| partition .status == STAT_COPY
|
|
)
|
|
)
|
|
)
|
|
)
|
|
{
|
|
partition .sector_start += ( (MEBIBYTE / partition .sector_size) - diff) ;
|
|
|
|
//If this is an extended partition then check to see if sufficient space is
|
|
// available for any following logical partition Extended Boot Record
|
|
if ( partition .type == TYPE_EXTENDED )
|
|
{
|
|
//If there is logical partition that starts less than 2 sectors
|
|
// from the start of this partition, then reserve a mebibyte for the EBR.
|
|
int index_extended = find_extended_partition( device.partitions );
|
|
if ( index_extended >= 0 )
|
|
{
|
|
for ( unsigned int t = 0; t < device .partitions[ index_extended ] .logicals .size(); t++ )
|
|
{
|
|
if ( ( device .partitions[ index_extended ] .logicals[ t ] .type == TYPE_LOGICAL )
|
|
&& ( ( ( device .partitions[ index_extended ] .logicals[ t ] .sector_start )
|
|
- ( partition .sector_start )
|
|
)
|
|
//Unless specifically told otherwise, the Linux kernel considers extended
|
|
// boot records to be two sectors long, in order to "leave room for LILO".
|
|
< 2
|
|
)
|
|
)
|
|
{
|
|
partition .sector_start -= (MEBIBYTE / partition .sector_size) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Align end sector
|
|
diff = (partition .sector_end + 1) % ( MEBIBYTE / partition .sector_size);
|
|
if ( diff )
|
|
partition .sector_end -= diff ;
|
|
|
|
//If this is a logical partition not at end of drive then check to see if space is
|
|
// required for a following logical partition Extended Boot Record
|
|
if ( partition .type == TYPE_LOGICAL )
|
|
{
|
|
//If there is a following logical partition that starts less than 2 sectors from
|
|
// the end of this partition, then reserve at least a mebibyte for the EBR.
|
|
int index_extended = find_extended_partition( device.partitions );
|
|
if ( index_extended >= 0 )
|
|
{
|
|
for ( unsigned int t = 0; t < device .partitions[ index_extended ] .logicals .size(); t++ )
|
|
{
|
|
if ( ( device .partitions[ index_extended ] .logicals[ t ] .type == TYPE_LOGICAL )
|
|
&& ( device .partitions[ index_extended ] .logicals[ t ] .sector_start > partition .sector_end )
|
|
&& ( ( device .partitions[ index_extended ] .logicals[ t ] .sector_start - partition .sector_end )
|
|
//Unless specifically told otherwise, the Linux kernel considers extended
|
|
// boot records to be two sectors long, in order to "leave room for LILO".
|
|
< 2
|
|
)
|
|
)
|
|
partition .sector_end -= ( MEBIBYTE / partition .sector_size ) ;
|
|
}
|
|
}
|
|
|
|
//If the logical partition end is beyond the end of the extended partition
|
|
// then reduce logical partition end by a mebibyte to address the overlap.
|
|
if ( ( index_extended != -1 )
|
|
&& ( partition .sector_end > device .partitions[ index_extended ] .sector_end )
|
|
)
|
|
partition .sector_end -= ( MEBIBYTE / partition .sector_size ) ;
|
|
}
|
|
|
|
//If this is a primary or an extended partition and the partition overlaps
|
|
// the start of the next primary or extended partition then subtract a
|
|
// mebibyte from the end of the partition to address the overlap.
|
|
if ( partition .type == TYPE_PRIMARY || partition .type == TYPE_EXTENDED )
|
|
{
|
|
for ( unsigned int t = 0 ; t < device .partitions .size() ; t++ )
|
|
{
|
|
if ( ( device .partitions[ t ] .type == TYPE_PRIMARY
|
|
|| device .partitions[ t ] .type == TYPE_EXTENDED
|
|
)
|
|
&& ( //For a change to an existing partition, (e.g., move or resize)
|
|
// skip comparing to original partition and
|
|
// only compare to other existing partitions
|
|
partition .status == STAT_REAL
|
|
&& partition .partition_number != device. partitions[ t ] .partition_number
|
|
)
|
|
&& ( device .partitions[ t ] .sector_start > partition .sector_start )
|
|
&& ( device .partitions[ t ] .sector_start <= partition .sector_end )
|
|
)
|
|
partition .sector_end -= ( MEBIBYTE / partition .sector_size );
|
|
}
|
|
}
|
|
|
|
//If this is an extended partition then check to see if the end of the
|
|
// extended partition encompasses the end of the last logical partition.
|
|
if ( partition .type == TYPE_EXTENDED )
|
|
{
|
|
//If there is logical partition that has an end sector beyond the
|
|
// end of the extended partition, then set the extended partition
|
|
// end sector to be the same as the end of the logical partition.
|
|
for ( unsigned int t = 0; t < partition .logicals .size(); t++ )
|
|
{
|
|
if ( ( partition .logicals[ t ] .type == TYPE_LOGICAL )
|
|
&& ( ( partition .logicals[ t ] .sector_end )
|
|
> ( partition .sector_end )
|
|
)
|
|
)
|
|
{
|
|
partition .sector_end = partition .logicals[ t ] .sector_end ;
|
|
}
|
|
}
|
|
}
|
|
|
|
//If this is a GPT partition table and the partition ends less than 34 sectors
|
|
// from the end of the device, then reserve at least a mebibyte for the
|
|
// backup partition table
|
|
if ( device .disktype == "gpt"
|
|
&& ( ( device .length - partition .sector_end ) < 34 )
|
|
)
|
|
{
|
|
partition .sector_end -= ( MEBIBYTE / partition .sector_size ) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
bool GParted_Core::snap_to_alignment( const Device & device, Partition & partition, Glib::ustring & error )
|
|
{
|
|
bool rc = true ;
|
|
|
|
if ( partition .alignment == ALIGN_CYLINDER )
|
|
rc = snap_to_cylinder( device, partition, error ) ;
|
|
else if ( partition .alignment == ALIGN_MEBIBYTE )
|
|
rc = snap_to_mebibyte( device, partition, error ) ;
|
|
|
|
//Ensure that partition start and end are not beyond the ends of the disk device
|
|
if ( partition .sector_start < 0 )
|
|
partition .sector_start = 0 ;
|
|
if ( partition .sector_end > device .length )
|
|
partition .sector_end = device .length - 1 ;
|
|
|
|
//do some basic checks on the partition
|
|
if ( partition .get_sector_length() <= 0 )
|
|
{
|
|
error = Glib::ustring::compose(
|
|
/* TO TRANSLATORS: looks like A partition cannot have a length of -1 sectors */
|
|
_("A partition cannot have a length of %1 sectors"),
|
|
partition .get_sector_length() ) ;
|
|
return false ;
|
|
}
|
|
|
|
//FIXME: I think that this if condition should be impossible because Partition::set_sector_usage(),
|
|
// and ::set_used() and ::Set_Unused() before that, don't allow setting file usage figures to be
|
|
// larger than the partition size. A btrfs file system spanning muiltiple partitions will have
|
|
// usage figures larger than any single partition but the figures will won't be set because of
|
|
// the above reasoning. Confirm condition is impossible and consider removing this code.
|
|
if ( partition .get_sector_length() < partition .sectors_used )
|
|
{
|
|
error = Glib::ustring::compose(
|
|
/* TO TRANSLATORS: looks like A partition with used sectors (2048) greater than its length (1536) is not valid */
|
|
_("A partition with used sectors (%1) greater than its length (%2) is not valid"),
|
|
partition .sectors_used,
|
|
partition .get_sector_length() ) ;
|
|
return false ;
|
|
}
|
|
|
|
//FIXME: it would be perfect if we could check for overlapping with adjacent partitions as well,
|
|
//however, finding the adjacent partitions is not as easy as it seems and at this moment all the dialogs
|
|
//already perform these checks. A perfect 'fixme-later' ;)
|
|
|
|
return rc ;
|
|
}
|
|
|
|
bool GParted_Core::apply_operation_to_disk( Operation * operation )
|
|
{
|
|
bool success = false;
|
|
libparted_messages .clear() ;
|
|
operation->operation_detail.signal_capture_errors.connect(
|
|
sigc::mem_fun( *this, &GParted_Core::capture_libparted_messages ) );
|
|
|
|
switch ( operation->type )
|
|
{
|
|
// Call calibrate_partition() first for each operation to ensure the
|
|
// correct partition path name and boundary is known before performing the
|
|
// actual modifications. (See comments in calibrate_partition() for
|
|
// reasons why this is necessary). Calibrate the most relevant partition
|
|
// object(s), either partition_original, partition_new or partition_copy,
|
|
// as required. Calibrate also displays details of the partition being
|
|
// modified in the operation results to inform the user.
|
|
//
|
|
// Win_GParted::set_valid_operations() determines which operations are
|
|
// allowed on file systems depending on whether each is busy mounted or
|
|
// not. For encrypted file systems allowed operations also depends on
|
|
// whether the encryption mapping is open or not. Therefore each
|
|
// operation must leave the status of the file system and any encryption
|
|
// mapping in the same state which it found it, ready for the next
|
|
// operation.
|
|
|
|
case OPERATION_DELETE:
|
|
success = calibrate_partition( operation->get_partition_original(),
|
|
operation->operation_detail )
|
|
&& remove_filesystem( operation->get_partition_original(),
|
|
operation->operation_detail )
|
|
&& delete_partition( operation->get_partition_original(),
|
|
operation->operation_detail );
|
|
break;
|
|
|
|
case OPERATION_CHECK:
|
|
success = calibrate_partition( operation->get_partition_original(),
|
|
operation->operation_detail )
|
|
&& check_repair_filesystem( operation->get_partition_original().get_filesystem_partition(),
|
|
operation->operation_detail )
|
|
&& check_repair_maximize( operation->get_partition_original(),
|
|
operation->operation_detail );
|
|
break;
|
|
|
|
case OPERATION_CREATE:
|
|
// The partition doesn't exist yet so there's nothing to calibrate.
|
|
success = create( operation->get_partition_new(), operation->operation_detail );
|
|
break;
|
|
|
|
case OPERATION_RESIZE_MOVE:
|
|
success = calibrate_partition( operation->get_partition_original(),
|
|
operation->operation_detail );
|
|
if ( ! success )
|
|
break;
|
|
|
|
// Replace the new partition object's path from the calibration in
|
|
// case the path is "Copy of ..." from the partition having been
|
|
// newly created by a paste into unallocated space earlier in the
|
|
// sequence of operations now being applied.
|
|
operation->get_partition_new().set_path( operation->get_partition_original().get_path() );
|
|
|
|
success = resize_move( operation->get_partition_original(),
|
|
operation->get_partition_new(),
|
|
operation->operation_detail );
|
|
break;
|
|
|
|
case OPERATION_FORMAT:
|
|
success = calibrate_partition( operation->get_partition_new(), operation->operation_detail );
|
|
if ( ! success )
|
|
break;
|
|
|
|
// Replace the original partition object's path from the
|
|
// calibration in case the path is "Copy of ..." from the
|
|
// partition having been newly created by a paste into unallocated
|
|
// space earlier in the sequence of operations now being applied.
|
|
operation->get_partition_original().set_path( operation->get_partition_new().get_path() );
|
|
|
|
success = remove_filesystem( operation->get_partition_original().get_filesystem_partition(),
|
|
operation->operation_detail )
|
|
&& format( operation->get_partition_new().get_filesystem_partition(),
|
|
operation->operation_detail );
|
|
break;
|
|
|
|
case OPERATION_COPY:
|
|
{
|
|
//FIXME: in case of a new partition we should make sure the new partition is >= the source partition...
|
|
//i think it's best to do this in the dialog_paste
|
|
|
|
OperationCopy * copy_op = static_cast<OperationCopy*>( operation );
|
|
success = calibrate_partition( copy_op->get_partition_copied(),
|
|
copy_op->operation_detail )
|
|
// Only calibrate the destination when pasting into an existing
|
|
// partition, rather than when creating a new partition.
|
|
&& ( copy_op->get_partition_original().type == TYPE_UNALLOCATED ||
|
|
calibrate_partition( copy_op->get_partition_new(), copy_op->operation_detail ) );
|
|
if ( ! success )
|
|
break;
|
|
|
|
success = remove_filesystem( copy_op->get_partition_original().get_filesystem_partition(),
|
|
copy_op->operation_detail )
|
|
&& copy( copy_op->get_partition_copied(),
|
|
copy_op->get_partition_new(),
|
|
copy_op->operation_detail );
|
|
break;
|
|
}
|
|
|
|
case OPERATION_LABEL_FILESYSTEM:
|
|
success = calibrate_partition( operation->get_partition_new(), operation->operation_detail )
|
|
&& label_filesystem( operation->get_partition_new().get_filesystem_partition(),
|
|
operation->operation_detail );
|
|
break;
|
|
|
|
case OPERATION_NAME_PARTITION:
|
|
success = calibrate_partition( operation->get_partition_new(), operation->operation_detail )
|
|
&& name_partition( operation->get_partition_new(), operation->operation_detail );
|
|
break;
|
|
|
|
case OPERATION_CHANGE_UUID:
|
|
success = calibrate_partition( operation->get_partition_new(), operation->operation_detail )
|
|
&& change_filesystem_uuid( operation->get_partition_new().get_filesystem_partition(),
|
|
operation->operation_detail );
|
|
break;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
bool GParted_Core::set_disklabel( const Device & device, const Glib::ustring & disklabel )
|
|
{
|
|
Glib::ustring device_path = device.get_path();
|
|
|
|
// FIXME: Should call file system specific removal actions
|
|
// (to remove LVM2 PVs before deleting the partitions).
|
|
|
|
#ifdef ENABLE_LOOP_DELETE_OLD_PTNS_WORKAROUND
|
|
// When creating a "loop" table with libparted 2.0 to 3.0 inclusive, it doesn't
|
|
// inform the kernel to delete old partitions so as a consequence blkid's cache
|
|
// becomes stale and it won't report a file system subsequently created on the
|
|
// whole disk device. Create a GPT first to use that code in libparted to delete
|
|
// any old partitions. Fixed in parted 3.1 by commit:
|
|
// f5c909c0cd50ed52a48dae6d35907dc08b137e88
|
|
// libparted: remove has_partitions check to allow loopback partitions
|
|
if ( disklabel == "loop" )
|
|
new_disklabel( device_path, "gpt", false );
|
|
#endif
|
|
|
|
// Ensure that any previous whole disk device file system can't be recognised by
|
|
// libparted in preference to the "loop" partition table signature, or by blkid in
|
|
// preference to any partition table about to be written.
|
|
OperationDetail dummy_od;
|
|
Partition temp_partition;
|
|
temp_partition.set_unpartitioned( device_path,
|
|
"",
|
|
FS_UNALLOCATED,
|
|
device.length,
|
|
device.sector_size,
|
|
false );
|
|
erase_filesystem_signatures( temp_partition, dummy_od );
|
|
|
|
return new_disklabel( device_path, disklabel );
|
|
}
|
|
|
|
bool GParted_Core::new_disklabel( const Glib::ustring & device_path, const Glib::ustring & disklabel,
|
|
bool recreate_dmraid_devs )
|
|
{
|
|
bool return_value = false ;
|
|
|
|
PedDevice* lp_device = NULL ;
|
|
PedDisk* lp_disk = NULL ;
|
|
if ( get_device( device_path, lp_device ) )
|
|
{
|
|
PedDiskType *type = NULL ;
|
|
type = ped_disk_type_get( disklabel .c_str() ) ;
|
|
|
|
if ( type )
|
|
{
|
|
lp_disk = ped_disk_new_fresh( lp_device, type );
|
|
|
|
return_value = commit( lp_disk ) ;
|
|
}
|
|
|
|
destroy_device_and_disk( lp_device, lp_disk ) ;
|
|
}
|
|
|
|
#ifndef USE_LIBPARTED_DMRAID
|
|
//delete and recreate disk entries if dmraid
|
|
DMRaid dmraid ;
|
|
if ( recreate_dmraid_devs && return_value && dmraid.is_dmraid_device( device_path ) )
|
|
{
|
|
dmraid .purge_dev_map_entries( device_path ) ;
|
|
dmraid .create_dev_map_entries( device_path ) ;
|
|
}
|
|
#endif
|
|
|
|
return return_value ;
|
|
}
|
|
|
|
bool GParted_Core::toggle_flag( const Partition & partition, const Glib::ustring & flag, bool state )
|
|
{
|
|
bool succes = false ;
|
|
PedDevice* lp_device = NULL ;
|
|
PedDisk* lp_disk = NULL ;
|
|
if ( get_device_and_disk( partition .device_path, lp_device, lp_disk ) )
|
|
{
|
|
PedPartition* lp_partition = get_lp_partition( lp_disk, partition );
|
|
if ( lp_partition )
|
|
{
|
|
PedPartitionFlag lp_flag = ped_partition_flag_get_by_name( flag .c_str() ) ;
|
|
|
|
if ( lp_flag > 0 && ped_partition_set_flag( lp_partition, lp_flag, state ) )
|
|
succes = commit( lp_disk ) ;
|
|
}
|
|
|
|
destroy_device_and_disk( lp_device, lp_disk ) ;
|
|
}
|
|
|
|
return succes ;
|
|
}
|
|
|
|
const std::vector<FS> & GParted_Core::get_filesystems() const
|
|
{
|
|
return FILESYSTEMS ;
|
|
}
|
|
|
|
// Return supported capabilities of the file system type or, if not found, not supported
|
|
// capabilities set.
|
|
const FS & GParted_Core::get_fs( FSType filesystem ) const
|
|
{
|
|
for ( unsigned int t = 0 ; t < FILESYSTEMS .size() ; t++ )
|
|
{
|
|
if ( FILESYSTEMS[ t ] .filesystem == filesystem )
|
|
return FILESYSTEMS[ t ] ;
|
|
}
|
|
|
|
static FS fs_notsupp( FS_UNSUPPORTED );
|
|
return fs_notsupp;
|
|
}
|
|
|
|
//Return all libparted's partition table types in it's preferred ordering,
|
|
// alphabetic except with "loop" last.
|
|
// Ref: parted >= 1.8 ./libparted/libparted.c init_disk_types()
|
|
std::vector<Glib::ustring> GParted_Core::get_disklabeltypes()
|
|
{
|
|
std::vector<Glib::ustring> disklabeltypes ;
|
|
|
|
PedDiskType *disk_type ;
|
|
for ( disk_type = ped_disk_type_get_next( NULL ) ; disk_type ; disk_type = ped_disk_type_get_next( disk_type ) )
|
|
disklabeltypes .push_back( disk_type->name ) ;
|
|
|
|
return disklabeltypes ;
|
|
}
|
|
|
|
std::map<Glib::ustring, bool> GParted_Core::get_available_flags( const Partition & partition )
|
|
{
|
|
std::map<Glib::ustring, bool> flag_info ;
|
|
|
|
PedDevice* lp_device = NULL ;
|
|
PedDisk* lp_disk = NULL ;
|
|
if ( get_device_and_disk( partition .device_path, lp_device, lp_disk ) )
|
|
{
|
|
PedPartition* lp_partition = get_lp_partition( lp_disk, partition );
|
|
if ( lp_partition )
|
|
{
|
|
for ( unsigned int t = 0 ; t < flags .size() ; t++ )
|
|
if ( ped_partition_is_flag_available( lp_partition, flags[ t ] ) )
|
|
flag_info[ ped_partition_flag_get_name( flags[ t ] ) ] =
|
|
ped_partition_get_flag( lp_partition, flags[ t ] ) ;
|
|
}
|
|
|
|
destroy_device_and_disk( lp_device, lp_disk ) ;
|
|
}
|
|
|
|
return flag_info ;
|
|
}
|
|
|
|
|
|
//private functions...
|
|
|
|
Glib::ustring GParted_Core::get_partition_path( PedPartition * lp_partition )
|
|
{
|
|
char * lp_path; //we have to free the result of ped_partition_get_path()
|
|
Glib::ustring partition_path = "Partition path not found";
|
|
|
|
lp_path = ped_partition_get_path(lp_partition);
|
|
if ( lp_path != NULL )
|
|
{
|
|
partition_path = lp_path;
|
|
free(lp_path);
|
|
}
|
|
|
|
#ifndef USE_LIBPARTED_DMRAID
|
|
//Ensure partition path name is compatible with dmraid
|
|
DMRaid dmraid; //Use cache of dmraid device information
|
|
if ( dmraid .is_dmraid_supported()
|
|
&& dmraid .is_dmraid_device( partition_path )
|
|
)
|
|
{
|
|
partition_path = dmraid .make_path_dmraid_compatible(partition_path);
|
|
}
|
|
#endif
|
|
|
|
return partition_path ;
|
|
}
|
|
|
|
void GParted_Core::set_device_from_disk( Device & device, const Glib::ustring & device_path )
|
|
{
|
|
PedDevice* lp_device = NULL;
|
|
PedDisk* lp_disk = NULL;
|
|
if ( get_device( device_path, lp_device, true ) )
|
|
{
|
|
device.Reset();
|
|
|
|
// Device info ...
|
|
device.set_path( device_path );
|
|
device.model = lp_device->model;
|
|
device.length = lp_device->length;
|
|
device.sector_size = lp_device->sector_size;
|
|
device.heads = lp_device->bios_geom.heads;
|
|
device.sectors = lp_device->bios_geom.sectors;
|
|
device.cylinders = lp_device->bios_geom.cylinders;
|
|
device.cylsize = device.heads * device.sectors;
|
|
set_device_serial_number( device );
|
|
|
|
// Make sure cylsize is at least 1 MiB
|
|
if ( device.cylsize < (MEBIBYTE / device.sector_size) )
|
|
device.cylsize = MEBIBYTE / device.sector_size;
|
|
|
|
std::vector<Glib::ustring> messages;
|
|
FSType fstype = detect_filesystem( lp_device, NULL, messages );
|
|
// FS_Info (blkid) recognised file system signature on whole disk device.
|
|
// Need to detect before libparted reported partitioning to avoid bug in
|
|
// libparted 1.9.0 to 2.3 inclusive which recognised FAT file systems as
|
|
// MSDOS partition tables. Fixed in parted 2.4 by commit:
|
|
// 616a2a1659d89ff90f9834016a451da8722df509
|
|
// libparted: avoid regression when processing a whole-disk FAT partition
|
|
if ( fstype != FS_UNKNOWN )
|
|
{
|
|
// Clear the possible "unrecognised disk label" message
|
|
libparted_messages.clear();
|
|
|
|
device.disktype = "none";
|
|
device.max_prims = 1;
|
|
set_device_one_partition( device, lp_device, fstype, messages );
|
|
}
|
|
// Partitioned drive
|
|
else if ( get_disk( lp_device, lp_disk, false ) )
|
|
{
|
|
// Partitioned drive (excluding "loop"), as recognised by libparted
|
|
if ( lp_disk && lp_disk->type && lp_disk->type->name &&
|
|
strcmp( lp_disk->type->name, "loop" ) != 0 )
|
|
{
|
|
device.disktype = lp_disk->type->name;
|
|
device.max_prims = ped_disk_get_max_primary_partition_count( lp_disk );
|
|
|
|
// Determine if partition naming is supported.
|
|
if ( ped_disk_type_check_feature( lp_disk->type, PED_DISK_TYPE_PARTITION_NAME ) )
|
|
{
|
|
device.enable_partition_naming(
|
|
Utils::get_max_partition_name_length( device.disktype ) );
|
|
}
|
|
|
|
set_device_partitions( device, lp_device, lp_disk );
|
|
|
|
if ( device.highest_busy )
|
|
{
|
|
device.readonly = ! commit_to_os( lp_disk, SETTLE_DEVICE_PROBE_MAX_WAIT_SECONDS );
|
|
// Clear libparted messages. Typically these are:
|
|
// The kernel was unable to re-read the partition table...
|
|
libparted_messages.clear();
|
|
}
|
|
}
|
|
// Drive just containing libparted "loop" signature and nothing
|
|
// else. (Actually any drive reported by libparted as "loop" but
|
|
// not recognised by blkid on the whole disk device).
|
|
else if ( lp_disk && lp_disk->type && lp_disk->type->name &&
|
|
strcmp( lp_disk->type->name, "loop" ) == 0 )
|
|
{
|
|
device.disktype = lp_disk->type->name;
|
|
device.max_prims = 1;
|
|
|
|
// Create virtual partition covering the whole disk device
|
|
// with unknown contents.
|
|
Partition * partition_temp = new Partition();
|
|
partition_temp->set_unpartitioned( device.get_path(),
|
|
lp_device->path,
|
|
FS_UNKNOWN,
|
|
device.length,
|
|
device.sector_size,
|
|
false );
|
|
// Place unknown file system message in this partition.
|
|
partition_temp->append_messages( messages );
|
|
device.partitions.push_back_adopt( partition_temp );
|
|
}
|
|
// Unrecognised, unpartitioned drive.
|
|
else
|
|
{
|
|
device.disktype =
|
|
/* TO TRANSLATORS: unrecognized
|
|
* means that the partition table for this disk
|
|
* device is unknown or not recognized.
|
|
*/
|
|
_("unrecognized");
|
|
device.max_prims = 1;
|
|
|
|
Partition * partition_temp = new Partition();
|
|
partition_temp->set_unpartitioned( device.get_path(),
|
|
"", // Overridden with "unallocated"
|
|
FS_UNALLOCATED,
|
|
device.length,
|
|
device.sector_size,
|
|
false );
|
|
// Place libparted messages in this unallocated partition
|
|
partition_temp->append_messages( libparted_messages );
|
|
libparted_messages.clear();
|
|
device.partitions.push_back_adopt( partition_temp );
|
|
}
|
|
}
|
|
|
|
destroy_device_and_disk( lp_device, lp_disk);
|
|
}
|
|
}
|
|
|
|
void GParted_Core::set_device_serial_number( Device & device )
|
|
{
|
|
if ( ! hdparm_found )
|
|
// Serial number left blank when the hdparm command is not installed.
|
|
return;
|
|
|
|
Glib::ustring output;
|
|
Glib::ustring error;
|
|
Utils::execute_command( "hdparm -I " + Glib::shell_quote( device.get_path() ), output, error, true );
|
|
if ( ! error.empty() )
|
|
{
|
|
// hdparm reported an error message to stderr. Assume it's a device
|
|
// without a hard drive serial number.
|
|
//
|
|
// Using hdparm -I to query Linux software RAID arrays and BIOS fake RAID
|
|
// arrays, both devices without their own hard drive serial numbers,
|
|
// produce this error:
|
|
// HDIO_DRIVE_CMD(identify) failed: Inappropriate ioctl for device
|
|
//
|
|
// And querying USB flash drives, also a device type without their own
|
|
// hard drive serial numbers, generates this error:
|
|
// SG_IO: bad/missing sense data, sb[]: 70 00 05 00 00 00 00 0a ...
|
|
device.serial_number = "none";
|
|
}
|
|
else
|
|
{
|
|
Glib::ustring serial_number = Utils::trim( Utils::regexp_label( output,
|
|
"^[[:blank:]]*Serial Number:[[:blank:]]*(.*)[[:blank:]]*$" ) );
|
|
if ( ! serial_number.empty() )
|
|
device.serial_number = serial_number;
|
|
}
|
|
// Otherwise serial number left blank when not found in the hdparm output.
|
|
}
|
|
|
|
/**
|
|
* Fills the device.partitions member of device by scanning
|
|
* all partitions
|
|
*/
|
|
void GParted_Core::set_device_partitions( Device & device, PedDevice* lp_device, PedDisk* lp_disk )
|
|
{
|
|
int EXT_INDEX = -1 ;
|
|
#ifndef USE_LIBPARTED_DMRAID
|
|
DMRaid dmraid ; //Use cache of dmraid device information
|
|
#endif
|
|
|
|
//clear partitions
|
|
device .partitions .clear() ;
|
|
|
|
PedPartition* lp_partition = ped_disk_next_partition( lp_disk, NULL ) ;
|
|
while ( lp_partition )
|
|
{
|
|
libparted_messages .clear() ;
|
|
Partition * partition_temp = NULL;
|
|
bool partition_is_busy = false ;
|
|
FSType filesystem;
|
|
std::vector<Glib::ustring> detect_messages;
|
|
|
|
//Retrieve partition path
|
|
Glib::ustring partition_path = get_partition_path( lp_partition );
|
|
|
|
// NOTE: lp_partition->type bit field
|
|
// lp_partition->type is a bit field using enumerated names for each bit.
|
|
// GParted is only interested in partitions with these single bits set:
|
|
// PED_PARTITION_NORMAL, PED_PARTITION_LOGICAL and PED_PARTITION_EXTENDED.
|
|
// Partitions, ranges of blocks, with other bits set representing free
|
|
// space and disk label meta-data, PED_PARTITION_FREESPACE and
|
|
// PED_PARTITION_METADATA bits respectively, are ignored and GParted
|
|
// creates its own unallocated partitions and accounts for partition
|
|
// tables.
|
|
// References:
|
|
// * struct PedPartition and type PedPartitionType
|
|
// https://www.gnu.org/software/parted/api/struct__PedPartition.html
|
|
// * enum _PedPartitionType
|
|
// http://git.savannah.gnu.org/cgit/parted.git/tree/include/parted/disk.in.h?id=v3.2#n45
|
|
switch ( lp_partition ->type )
|
|
{
|
|
case PED_PARTITION_NORMAL:
|
|
case PED_PARTITION_LOGICAL:
|
|
filesystem = detect_filesystem( lp_device, lp_partition, detect_messages );
|
|
#ifndef USE_LIBPARTED_DMRAID
|
|
//Handle dmraid devices differently because the minor number might not
|
|
// match the last number of the partition filename as shown by "ls -l /dev/mapper"
|
|
if ( dmraid .is_dmraid_device( device .get_path() ) )
|
|
{
|
|
//Try device_name + partition_number
|
|
Glib::ustring dmraid_path = device .get_path() + Utils::num_to_str( lp_partition ->num ) ;
|
|
partition_is_busy = is_busy( filesystem, dmraid_path ) ;
|
|
|
|
//Try device_name + p + partition_number
|
|
dmraid_path = device .get_path() + "p" + Utils::num_to_str( lp_partition ->num ) ;
|
|
partition_is_busy |= is_busy( filesystem, dmraid_path ) ;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
partition_is_busy = is_busy( filesystem, partition_path ) ;
|
|
}
|
|
|
|
if ( filesystem == FS_LUKS )
|
|
partition_temp = new PartitionLUKS();
|
|
else
|
|
partition_temp = new Partition();
|
|
partition_temp->Set( device .get_path(),
|
|
partition_path,
|
|
lp_partition->num,
|
|
( lp_partition->type == PED_PARTITION_NORMAL ) ? TYPE_PRIMARY
|
|
: TYPE_LOGICAL,
|
|
filesystem,
|
|
lp_partition->geom.start,
|
|
lp_partition->geom.end,
|
|
device.sector_size,
|
|
( lp_partition->type == PED_PARTITION_LOGICAL ),
|
|
partition_is_busy );
|
|
partition_temp->append_messages( detect_messages );
|
|
|
|
set_flags( *partition_temp, lp_partition );
|
|
|
|
if ( filesystem == FS_LUKS )
|
|
set_luks_partition( *dynamic_cast<PartitionLUKS *>( partition_temp ) );
|
|
|
|
if ( partition_temp->busy && partition_temp->partition_number > device.highest_busy )
|
|
device.highest_busy = partition_temp->partition_number;
|
|
break ;
|
|
|
|
case PED_PARTITION_EXTENDED:
|
|
partition_temp = new Partition();
|
|
partition_temp->Set( device.get_path(),
|
|
partition_path,
|
|
lp_partition->num,
|
|
TYPE_EXTENDED,
|
|
FS_EXTENDED,
|
|
lp_partition->geom.start,
|
|
lp_partition->geom.end,
|
|
device.sector_size,
|
|
false,
|
|
false );
|
|
|
|
set_flags( *partition_temp, lp_partition );
|
|
|
|
EXT_INDEX = device .partitions .size() ;
|
|
break ;
|
|
|
|
default:
|
|
// Ignore libparted reported partitions with other type
|
|
// bits set.
|
|
break;
|
|
}
|
|
|
|
// Only for libparted reported partition types that we care about: NORMAL,
|
|
// LOGICAL, EXTENDED
|
|
if ( partition_temp != NULL )
|
|
{
|
|
set_partition_label_and_uuid( *partition_temp );
|
|
set_mountpoints( *partition_temp );
|
|
set_used_sectors( *partition_temp, lp_disk );
|
|
|
|
// Retrieve partition name
|
|
if ( device.partition_naming_supported() )
|
|
partition_temp->name = Glib::ustring( ped_partition_get_name( lp_partition ) );
|
|
|
|
partition_temp->append_messages( libparted_messages );
|
|
|
|
if ( ! partition_temp->inside_extended )
|
|
device.partitions.push_back_adopt( partition_temp );
|
|
else
|
|
device.partitions[EXT_INDEX].logicals.push_back_adopt( partition_temp );
|
|
}
|
|
|
|
//next partition (if any)
|
|
lp_partition = ped_disk_next_partition( lp_disk, lp_partition ) ;
|
|
}
|
|
|
|
if ( EXT_INDEX > -1 )
|
|
{
|
|
insert_unallocated( device .get_path(),
|
|
device .partitions[ EXT_INDEX ] .logicals,
|
|
device .partitions[ EXT_INDEX ] .sector_start,
|
|
device .partitions[ EXT_INDEX ] .sector_end,
|
|
device .sector_size,
|
|
true ) ;
|
|
|
|
//Set busy status of extended partition if and only if
|
|
// there is at least one busy logical partition.
|
|
for ( unsigned int t = 0 ; t < device .partitions[ EXT_INDEX ] .logicals .size() ; t ++ )
|
|
{
|
|
if ( device .partitions[ EXT_INDEX ] .logicals[ t ] .busy )
|
|
{
|
|
device .partitions[ EXT_INDEX ] .busy = true ;
|
|
break ;
|
|
}
|
|
}
|
|
}
|
|
|
|
insert_unallocated( device .get_path(), device .partitions, 0, device .length -1, device .sector_size, false ) ;
|
|
}
|
|
|
|
// Create one Partition object spanning the Device after identifying the file system
|
|
// on the whole disk device. Much simplified equivalent of set_device_partitions().
|
|
void GParted_Core::set_device_one_partition( Device & device, PedDevice * lp_device, FSType fstype,
|
|
std::vector<Glib::ustring> & messages )
|
|
{
|
|
device.partitions.clear();
|
|
|
|
Glib::ustring path = lp_device->path;
|
|
bool partition_is_busy = is_busy( fstype, path );
|
|
|
|
Partition * partition_temp = NULL;
|
|
if ( fstype == FS_LUKS )
|
|
partition_temp = new PartitionLUKS();
|
|
else
|
|
partition_temp = new Partition();
|
|
partition_temp->set_unpartitioned( device.get_path(),
|
|
path,
|
|
fstype,
|
|
device.length,
|
|
device.sector_size,
|
|
partition_is_busy );
|
|
|
|
partition_temp->append_messages( messages );
|
|
|
|
if ( fstype == FS_LUKS )
|
|
set_luks_partition( *dynamic_cast<PartitionLUKS *>( partition_temp ) );
|
|
|
|
if ( partition_temp->busy )
|
|
device.highest_busy = 1;
|
|
|
|
set_partition_label_and_uuid( *partition_temp );
|
|
set_mountpoints( *partition_temp );
|
|
set_used_sectors( *partition_temp, NULL );
|
|
|
|
device.partitions.push_back_adopt( partition_temp );
|
|
}
|
|
|
|
void GParted_Core::set_luks_partition( PartitionLUKS & partition )
|
|
{
|
|
LUKS_Mapping mapping = LUKS_Info::get_cache_entry( partition.get_path() );
|
|
if ( mapping.name.empty() )
|
|
// No LUKS mapping found so no device file with which to query the
|
|
// encrypted file system. Assume no open dm-crypt mapping exists.
|
|
// Details of encrypted file system left blank.
|
|
return;
|
|
|
|
Glib::ustring mapping_path = DEV_MAPPER_PATH + mapping.name;
|
|
PedDevice* lp_device = NULL;
|
|
std::vector<Glib::ustring> detect_messages;
|
|
FSType fstype = FS_UNKNOWN;
|
|
if ( get_device( mapping_path, lp_device ) )
|
|
{
|
|
fstype = detect_filesystem( lp_device, NULL, detect_messages );
|
|
PedDisk* lp_disk = NULL;
|
|
destroy_device_and_disk( lp_device, lp_disk );
|
|
}
|
|
bool fs_busy = is_busy( fstype, mapping_path );
|
|
|
|
partition.set_luks( mapping_path,
|
|
fstype,
|
|
mapping.offset / partition.sector_size,
|
|
mapping.length / partition.sector_size,
|
|
partition.sector_size,
|
|
fs_busy );
|
|
|
|
Partition & encrypted = partition.get_encrypted();
|
|
encrypted.append_messages( detect_messages );
|
|
|
|
set_partition_label_and_uuid( encrypted );
|
|
set_mountpoints( encrypted );
|
|
set_used_sectors( encrypted, NULL );
|
|
}
|
|
|
|
void GParted_Core::set_partition_label_and_uuid( Partition & partition )
|
|
{
|
|
Glib::ustring partition_path = partition.get_path();
|
|
|
|
// For SWRaid members only get the label and UUID from SWRaid_Info. Never use
|
|
// values from FS_Info to avoid showing incorrect information in cases where blkid
|
|
// reports the wrong values.
|
|
if ( partition.filesystem == FS_LINUX_SWRAID )
|
|
{
|
|
Glib::ustring label = SWRaid_Info::get_label( partition_path );
|
|
if ( ! label.empty() )
|
|
partition.set_filesystem_label( label );
|
|
|
|
partition.uuid = SWRaid_Info::get_uuid( partition_path );
|
|
return;
|
|
}
|
|
|
|
// Retrieve file system label. Use file system specific method first because it
|
|
// has been that way since (#662537) to display Unicode labels correctly.
|
|
// (#786502) adds support for reading Unicode labels through the FS_Info cache.
|
|
// Either method should produce the same labels however the FS specific command is
|
|
// run in the users locale where as blkid is run in the C locale. Shouldn't
|
|
// matter but who knows for sure!
|
|
read_label( partition );
|
|
if ( ! partition.filesystem_label_known() )
|
|
{
|
|
bool label_found = false;
|
|
Glib::ustring label = FS_Info::get_label( partition_path, label_found );
|
|
if ( label_found )
|
|
partition.set_filesystem_label( label );
|
|
}
|
|
|
|
// Retrieve file system UUID. Use cached method first in an effort to speed up
|
|
// device scanning.
|
|
partition.uuid = FS_Info::get_uuid( partition_path );
|
|
if ( partition.uuid.empty() )
|
|
{
|
|
read_uuid( partition );
|
|
}
|
|
}
|
|
|
|
// GParted simple internal file system signature detection. Use sparingly. Only when
|
|
// (old versions of) blkid and libparted don't recognise a signature.
|
|
FSType GParted_Core::detect_filesystem_internal( PedDevice * lp_device, PedPartition * lp_partition )
|
|
{
|
|
char magic1[16]; // Big enough for largest signatures[].sig1 or sig2
|
|
char magic2[16];
|
|
FSType fstype = FS_UNKNOWN;
|
|
|
|
char * buf = static_cast<char *>( malloc( lp_device->sector_size ) );
|
|
if ( ! buf )
|
|
return FS_UNKNOWN;
|
|
|
|
if ( ! ped_device_open( lp_device ) )
|
|
{
|
|
free( buf );
|
|
return FS_UNKNOWN;
|
|
}
|
|
|
|
struct {
|
|
Byte_Value offset1;
|
|
const char * sig1;
|
|
Byte_Value offset2;
|
|
const char * sig2;
|
|
FSType fstype;
|
|
} signatures[] = {
|
|
//offset1, sig1 , offset2, sig2 , fstype
|
|
{ 65536LL, "ReIsEr4" , 0LL, NULL , FS_REISER4 },
|
|
{ 512LL, "LABELONE" , 536LL, "LVM2", FS_LVM2_PV },
|
|
{ 0LL, "LUKS\xBA\xBE" , 0LL, NULL , FS_LUKS },
|
|
{ 65600LL, "_BHRfS_M" , 0LL, NULL , FS_BTRFS },
|
|
{ 3LL, "-FVE-FS-" , 0LL, NULL , FS_BITLOCKER },
|
|
{ 1030LL, "\x34\x34" , 0LL, NULL , FS_NILFS2 },
|
|
{ 0LL, "\x52\x56\xBE\x1B", 0LL, NULL , FS_GRUB2_CORE_IMG },
|
|
{ 0LL, "\x52\x56\xBE\x6F", 0LL, NULL , FS_GRUB2_CORE_IMG },
|
|
{ 0LL, "\x52\xE8\x28\x01", 0LL, NULL , FS_GRUB2_CORE_IMG },
|
|
{ 0LL, "\x52\xBF\xF4\x81", 0LL, NULL , FS_GRUB2_CORE_IMG },
|
|
{ 0LL, "\x52\x56\xBE\x63", 0LL, NULL , FS_GRUB2_CORE_IMG },
|
|
{ 0LL, "\x52\x56\xBE\x56", 0LL, NULL , FS_GRUB2_CORE_IMG },
|
|
{ 24LL, "\x01\x00" , 32LL, "NXSB", FS_APFS }
|
|
};
|
|
// For simple BitLocker recognition consider validation of BIOS Parameter block
|
|
// fields unnecessary.
|
|
// * Detecting BitLocker
|
|
// http://blogs.msdn.com/b/si_team/archive/2006/10/26/detecting-bitlocker.aspx
|
|
//
|
|
// Recognise GRUB2 core.img just by any of the possible first 4 bytes of x86 CPU
|
|
// instructions it starts with.
|
|
// * bootinfoscript v0.77 line 1990 [GRUB2 core.img possible staring 4 bytes]
|
|
// https://github.com/arvidjaar/bootinfoscript/blob/009f509d59e2f0d39b8d44692e2a81720f5af7b6/bootinfoscript#L1990
|
|
//
|
|
// Simple APFS recognition based on matching the following fields in the
|
|
// superblock:
|
|
// 1) Object type is OBJECT_TYPE_NX_SUPERBLOCK, lower 16-bits of the object type
|
|
// field is 0x0001 stored as little endian bytes 0x01, 0x00.
|
|
// WARNING: The magic signatures are defined as NUL terminated strings so the
|
|
// below code only does a 1-byte match for 0x01, rather than a 2-byte match
|
|
// for 0x01, 0x00.
|
|
// 2) 4 byte magic "NXSB".
|
|
// * Apple File System Reference
|
|
// https://developer.apple.com/support/apple-file-system/Apple-File-System-Reference.pdf
|
|
|
|
for ( unsigned int i = 0 ; i < sizeof( signatures ) / sizeof( signatures[0] ) ; i ++ )
|
|
{
|
|
const size_t len1 = std::min( ( signatures[i].sig1 == NULL ) ? 0U : strlen( signatures[i].sig1 ),
|
|
sizeof( magic1 ) );
|
|
const size_t len2 = std::min( ( signatures[i].sig2 == NULL ) ? 0U : strlen( signatures[i].sig2 ),
|
|
sizeof( magic2 ) );
|
|
// NOTE: From this point onwards signatures[].sig1 and .sig2 are treated
|
|
// as character buffers of known lengths len1 and len2, not NUL terminated
|
|
// strings.
|
|
if ( len1 == 0UL || ( signatures[i].sig2 != NULL && len2 == 0UL ) )
|
|
continue; // Don't allow 0 length signatures to match
|
|
|
|
Sector start = 0LL;
|
|
if ( lp_partition )
|
|
start = lp_partition->geom.start;
|
|
start += signatures[i].offset1 / lp_device->sector_size;
|
|
|
|
memset( buf, 0, lp_device->sector_size );
|
|
if ( ped_device_read( lp_device, buf, start, 1 ) != 0 )
|
|
{
|
|
memcpy( magic1, buf + signatures[i].offset1 % lp_device->sector_size, len1 );
|
|
|
|
// WARNING: This assumes offset2 is in the same sector as offset1
|
|
if ( signatures[i].sig2 != NULL )
|
|
{
|
|
memcpy( magic2, buf + signatures[i].offset2 % lp_device->sector_size, len2 );
|
|
}
|
|
|
|
if ( memcmp( magic1, signatures[i].sig1, len1 ) == 0 &&
|
|
( signatures[i].sig2 == NULL ||
|
|
memcmp( magic2, signatures[i].sig2, len2 ) == 0 ) )
|
|
{
|
|
fstype = signatures[i].fstype;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
ped_device_close( lp_device );
|
|
free( buf );
|
|
|
|
return fstype;
|
|
}
|
|
|
|
FSType GParted_Core::detect_filesystem( PedDevice * lp_device, PedPartition * lp_partition,
|
|
std::vector<Glib::ustring> & messages )
|
|
{
|
|
Glib::ustring fsname = "";
|
|
Glib::ustring path;
|
|
|
|
if ( lp_partition )
|
|
// Will query partition using methods: (Q1) SWRaid, (Q2) blkid,
|
|
// (Q3) libparted, (Q4) internal
|
|
path = get_partition_path( lp_partition );
|
|
else
|
|
// Will query whole disk device using methods: (Q1) SWRaid, (Q2) blkid,
|
|
// (Q4) internal
|
|
path = lp_device->path;
|
|
|
|
// (Q1) Linux Software RAID member detection
|
|
if ( SWRaid_Info::is_member( path ) )
|
|
return FS_LINUX_SWRAID;
|
|
|
|
// (Q2) FS_Info (blkid) file system detection
|
|
// Blkid detects more signatures and generally has less limitations so use before
|
|
// libparted detection, but it doesn't report anything for extended partitions.
|
|
fsname = FS_Info::get_fs_type( path );
|
|
|
|
// (Q3) Libparted file system detection
|
|
// Only used when blkid didn't report anything and only on partitions, not whole
|
|
// disk devices. (Doesn't detect anything on non-512 byte sector devices before
|
|
// libparted 3.2).
|
|
if ( fsname.empty() && lp_partition && lp_partition->fs_type )
|
|
fsname = lp_partition->fs_type->name;
|
|
|
|
if ( ! fsname.empty() )
|
|
{
|
|
if ( fsname == "extended" )
|
|
return FS_EXTENDED;
|
|
else if ( fsname == "btrfs" )
|
|
return FS_BTRFS;
|
|
else if ( fsname == "exfat" )
|
|
return FS_EXFAT;
|
|
else if ( fsname == "ext2" )
|
|
return FS_EXT2;
|
|
else if ( fsname == "ext3" )
|
|
return FS_EXT3;
|
|
else if ( fsname == "ext4" ||
|
|
fsname == "ext4dev" )
|
|
return FS_EXT4;
|
|
else if ( fsname == "linux-swap" ||
|
|
fsname == "linux-swap(v1)" ||
|
|
fsname == "linux-swap(new)" ||
|
|
fsname == "linux-swap(v0)" ||
|
|
fsname == "linux-swap(old)" ||
|
|
fsname == "swap" )
|
|
return FS_LINUX_SWAP;
|
|
else if ( fsname == "crypto_LUKS" )
|
|
return FS_LUKS;
|
|
else if ( fsname == "LVM2_member" )
|
|
return FS_LVM2_PV;
|
|
else if ( fsname == "f2fs" )
|
|
return FS_F2FS;
|
|
else if ( fsname == "fat16" )
|
|
return FS_FAT16;
|
|
else if ( fsname == "fat32" )
|
|
return FS_FAT32;
|
|
else if ( fsname == "minix" )
|
|
return FS_MINIX;
|
|
else if ( fsname == "nilfs2" )
|
|
return FS_NILFS2;
|
|
else if ( fsname == "ntfs" )
|
|
return FS_NTFS;
|
|
else if ( fsname == "reiserfs" )
|
|
return FS_REISERFS;
|
|
else if ( fsname == "xfs" )
|
|
return FS_XFS;
|
|
else if ( fsname == "jfs" )
|
|
return FS_JFS;
|
|
else if ( fsname == "hfs" )
|
|
return FS_HFS;
|
|
else if ( fsname == "hfs+" ||
|
|
fsname == "hfsx" ||
|
|
fsname == "hfsplus" )
|
|
return FS_HFSPLUS;
|
|
else if ( fsname == "udf" )
|
|
return FS_UDF;
|
|
else if ( fsname == "ufs" )
|
|
return FS_UFS;
|
|
else if ( fsname == "apfs" )
|
|
return FS_APFS;
|
|
else if ( fsname == "BitLocker" )
|
|
return FS_BITLOCKER;
|
|
else if ( fsname == "iso9660" )
|
|
return FS_ISO9660;
|
|
else if ( fsname == "linux_raid_member" )
|
|
return FS_LINUX_SWRAID ;
|
|
else if ( fsname == "swsusp" ||
|
|
fsname == "swsuspend" )
|
|
return FS_LINUX_SWSUSPEND ;
|
|
else if ( fsname == "ReFS" )
|
|
return FS_REFS;
|
|
else if ( fsname == "zfs_member" )
|
|
return FS_ZFS;
|
|
}
|
|
|
|
// (Q4) Fallback to GParted simple internal file system detection
|
|
FSType fstype = detect_filesystem_internal( lp_device, lp_partition );
|
|
if ( fstype != FS_UNKNOWN )
|
|
return fstype;
|
|
|
|
//no file system found....
|
|
Glib::ustring temp = _( "Unable to detect file system! Possible reasons are:" ) ;
|
|
temp += "\n- ";
|
|
temp += _( "The file system is damaged" ) ;
|
|
temp += "\n- " ;
|
|
temp += _( "The file system is unknown to GParted" ) ;
|
|
temp += "\n- ";
|
|
temp += _( "There is no file system available (unformatted)" ) ;
|
|
temp += "\n- ";
|
|
/* TO TRANSLATORS: looks like The device entry /dev/sda5 is missing */
|
|
temp += Glib::ustring::compose( _("The device entry %1 is missing"), path );
|
|
|
|
messages .push_back( temp ) ;
|
|
|
|
return FS_UNKNOWN;
|
|
}
|
|
|
|
void GParted_Core::read_label( Partition & partition )
|
|
{
|
|
FileSystem* p_filesystem = NULL;
|
|
switch ( get_fs( partition.filesystem ).read_label )
|
|
{
|
|
case FS::EXTERNAL:
|
|
p_filesystem = get_filesystem_object( partition.filesystem );
|
|
if ( p_filesystem )
|
|
p_filesystem->read_label( partition );
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void GParted_Core::read_uuid( Partition & partition )
|
|
{
|
|
FileSystem* p_filesystem = NULL;
|
|
switch ( get_fs( partition.filesystem ).read_uuid )
|
|
{
|
|
case FS::EXTERNAL:
|
|
p_filesystem = get_filesystem_object( partition.filesystem );
|
|
if ( p_filesystem )
|
|
p_filesystem->read_uuid( partition );
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void GParted_Core::insert_unallocated( const Glib::ustring & device_path,
|
|
PartitionVector & partitions,
|
|
Sector start,
|
|
Sector end,
|
|
Byte_Value sector_size,
|
|
bool inside_extended )
|
|
{
|
|
//if there are no partitions at all..
|
|
if ( partitions .empty() )
|
|
{
|
|
Partition * partition_temp = new Partition();
|
|
partition_temp->Set_Unallocated( device_path, start, end, sector_size, inside_extended );
|
|
partitions.push_back_adopt( partition_temp );
|
|
return ;
|
|
}
|
|
|
|
//start <---> first partition start
|
|
if ( (partitions .front() .sector_start - start) > (MEBIBYTE / sector_size) )
|
|
{
|
|
Sector temp_end = partitions.front().sector_start - 1;
|
|
Partition * partition_temp = new Partition();
|
|
partition_temp->Set_Unallocated( device_path, start, temp_end, sector_size, inside_extended );
|
|
partitions.insert_adopt( partitions.begin(), partition_temp );
|
|
}
|
|
|
|
//look for gaps in between
|
|
for ( unsigned int t =0 ; t < partitions .size() -1 ; t++ )
|
|
{
|
|
if ( ( ( partitions[ t + 1 ] .sector_start - partitions[ t ] .sector_end - 1 ) > (MEBIBYTE / sector_size) )
|
|
|| ( ( partitions[ t + 1 ] .type != TYPE_LOGICAL ) // Only show exactly 1 MiB if following partition is not logical.
|
|
&& ( ( partitions[ t + 1 ] .sector_start - partitions[ t ] .sector_end - 1 ) == (MEBIBYTE / sector_size) )
|
|
)
|
|
)
|
|
{
|
|
Sector temp_start = partitions[t].sector_end + 1;
|
|
Sector temp_end = partitions[t+1].sector_start - 1;
|
|
Partition * partition_temp = new Partition();
|
|
partition_temp->Set_Unallocated( device_path, temp_start, temp_end,
|
|
sector_size, inside_extended );
|
|
partitions.insert_adopt( partitions.begin() + ++t, partition_temp );
|
|
}
|
|
}
|
|
|
|
//last partition end <---> end
|
|
if ( (end - partitions .back() .sector_end) >= (MEBIBYTE / sector_size) )
|
|
{
|
|
Sector temp_start = partitions.back().sector_end + 1;
|
|
Partition * partition_temp = new Partition();
|
|
partition_temp->Set_Unallocated( device_path, temp_start, end, sector_size, inside_extended );
|
|
partitions.push_back_adopt( partition_temp );
|
|
}
|
|
}
|
|
|
|
void GParted_Core::set_mountpoints( Partition & partition )
|
|
{
|
|
#ifndef USE_LIBPARTED_DMRAID
|
|
DMRaid dmraid ; //Use cache of dmraid device information
|
|
#endif
|
|
|
|
if ( partition.filesystem == FS_LVM2_PV )
|
|
{
|
|
Glib::ustring vgname = LVM2_PV_Info::get_vg_name( partition.get_path() );
|
|
if ( ! vgname.empty() )
|
|
partition.add_mountpoint( vgname );
|
|
}
|
|
else if ( partition.filesystem == FS_LINUX_SWRAID )
|
|
{
|
|
Glib::ustring array_path = SWRaid_Info::get_array( partition.get_path() );
|
|
if ( ! array_path.empty() )
|
|
partition.add_mountpoint( array_path );
|
|
}
|
|
else if ( partition.filesystem == FS_LUKS )
|
|
{
|
|
LUKS_Mapping mapping = LUKS_Info::get_cache_entry( partition.get_path() );
|
|
if ( ! mapping.name.empty() )
|
|
partition.add_mountpoint( DEV_MAPPER_PATH + mapping.name );
|
|
}
|
|
// Swap spaces don't have mount points so don't bother trying to add them.
|
|
else if ( partition.filesystem != FS_LINUX_SWAP )
|
|
{
|
|
if ( partition.busy )
|
|
{
|
|
#ifndef USE_LIBPARTED_DMRAID
|
|
// Handle dmraid devices differently because there may be more
|
|
// than one partition name.
|
|
// E.g., there might be names with and/or without a 'p' between
|
|
// the device name and partition number.
|
|
if ( dmraid.is_dmraid_device( partition.device_path ) )
|
|
{
|
|
// Try device_name + partition_number
|
|
Glib::ustring dmraid_path = partition.device_path +
|
|
Utils::num_to_str( partition.partition_number );
|
|
if ( set_mountpoints_helper( partition, dmraid_path ) )
|
|
return;
|
|
|
|
// Try device_name + p + partition_number
|
|
dmraid_path = partition.device_path + "p" +
|
|
Utils::num_to_str( partition.partition_number );
|
|
if ( set_mountpoints_helper( partition, dmraid_path ) )
|
|
return;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
// Normal device, not DMRaid device
|
|
if ( set_mountpoints_helper( partition, partition.get_path() ) )
|
|
return;
|
|
}
|
|
|
|
if ( partition.get_mountpoints().empty() )
|
|
partition.push_back_message( _("Unable to find mount point") );
|
|
}
|
|
else // Not busy file system
|
|
{
|
|
partition.add_mountpoints( Mount_Info::get_fstab_mountpoints( partition.get_path() ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
bool GParted_Core::set_mountpoints_helper( Partition & partition, const Glib::ustring & path )
|
|
{
|
|
Glib::ustring search_path ;
|
|
if ( partition .filesystem == FS_BTRFS )
|
|
search_path = btrfs::get_mount_device( path ) ;
|
|
else
|
|
search_path = path ;
|
|
|
|
const std::vector<Glib::ustring> & mountpoints = Mount_Info::get_mounted_mountpoints( search_path );
|
|
if ( mountpoints.size() )
|
|
{
|
|
partition.add_mountpoints( mountpoints );
|
|
partition.fs_readonly = Mount_Info::is_dev_mounted_readonly( search_path );
|
|
return true ;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//Report whether the partition is busy (mounted/active)
|
|
bool GParted_Core::is_busy( FSType fstype, const Glib::ustring & path )
|
|
{
|
|
FileSystem * p_filesystem = NULL ;
|
|
bool busy = false ;
|
|
|
|
if ( supported_filesystem( fstype ) )
|
|
{
|
|
switch ( get_fs( fstype ) .busy )
|
|
{
|
|
case FS::GPARTED:
|
|
//Search GParted internal mounted partitions map
|
|
busy = Mount_Info::is_dev_mounted( path );
|
|
break ;
|
|
|
|
case FS::EXTERNAL:
|
|
//Call file system specific method
|
|
p_filesystem = get_filesystem_object( fstype ) ;
|
|
if ( p_filesystem )
|
|
busy = p_filesystem -> is_busy( path ) ;
|
|
break;
|
|
|
|
default:
|
|
break ;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Still search GParted internal mounted partitions map in case an
|
|
// unknown file system is mounted
|
|
busy = Mount_Info::is_dev_mounted( path );
|
|
|
|
//Custom checks for recognised but other not-supported file system types
|
|
busy |= ( fstype == FS_LINUX_SWRAID && SWRaid_Info::is_member_active( path ) );
|
|
}
|
|
|
|
return busy ;
|
|
}
|
|
|
|
void GParted_Core::set_used_sectors( Partition & partition, PedDisk* lp_disk )
|
|
{
|
|
if ( supported_filesystem( partition.filesystem ) )
|
|
{
|
|
FileSystem* p_filesystem = NULL;
|
|
if ( partition.busy )
|
|
{
|
|
switch( get_fs( partition.filesystem ).online_read )
|
|
{
|
|
case FS::EXTERNAL:
|
|
p_filesystem = get_filesystem_object( partition.filesystem );
|
|
if ( p_filesystem )
|
|
p_filesystem->set_used_sectors( partition );
|
|
break;
|
|
case FS::GPARTED:
|
|
mounted_fs_set_used_sectors( partition );
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else // Not busy file system
|
|
{
|
|
switch( get_fs( partition.filesystem ).read )
|
|
{
|
|
case FS::EXTERNAL:
|
|
p_filesystem = get_filesystem_object( partition.filesystem );
|
|
if ( p_filesystem )
|
|
p_filesystem->set_used_sectors( partition );
|
|
break;
|
|
#ifdef HAVE_LIBPARTED_FS_RESIZE
|
|
case FS::LIBPARTED:
|
|
if ( lp_disk )
|
|
LP_set_used_sectors( partition, lp_disk );
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
Sector unallocated;
|
|
// Only confirm that the above code succeeded in setting the sector usage
|
|
// values for this base Partition object, hence the explicit call to the
|
|
// base Partition class sector_usage_known() method. For LUKS this avoids
|
|
// calling derived PartitionLUKS class sector_usage_known() which also
|
|
// checks for known sector usage in the encrypted file system. But that
|
|
// wasn't set by the above code so in the case of luks/unknown would
|
|
// produce a false positive.
|
|
if ( ! partition.Partition::sector_usage_known() )
|
|
{
|
|
Glib::ustring temp = _("Unable to read the contents of this file system!");
|
|
temp += "\n";
|
|
temp += _("Because of this some operations may be unavailable.");
|
|
if ( ! Utils::get_filesystem_software( partition.filesystem ).empty() )
|
|
{
|
|
temp += "\n";
|
|
temp += _("The cause might be a missing software package.");
|
|
temp += "\n";
|
|
/*TO TRANSLATORS: looks like The following list of software packages is required for NTFS file system support: ntfsprogs. */
|
|
temp += Glib::ustring::compose( _("The following list of software packages is required for %1 file system support: %2."),
|
|
Utils::get_filesystem_string( partition.filesystem ),
|
|
Utils::get_filesystem_software( partition.filesystem )
|
|
);
|
|
}
|
|
partition.push_back_message( temp );
|
|
}
|
|
else if ( ( unallocated = partition.get_sectors_unallocated() ) > 0 )
|
|
{
|
|
/* TO TRANSLATORS: looks like 1.28GiB of unallocated space within the partition. */
|
|
Glib::ustring temp = Glib::ustring::compose( _("%1 of unallocated space within the partition."),
|
|
Utils::format_size( unallocated, partition.sector_size ) );
|
|
FS fs = get_fs( partition.filesystem );
|
|
if ( fs.check != FS::NONE && fs.grow != FS::NONE )
|
|
{
|
|
temp += "\n";
|
|
/* TO TRANSLATORS: To grow the file system to fill the partition, select the partition and choose the menu item:
|
|
* means that the user can perform a check of the partition which will
|
|
* also grow the file system to fill the partition.
|
|
*/
|
|
temp += _("To grow the file system to fill the partition, select the partition and choose the menu item:");
|
|
temp += "\n";
|
|
temp += _("Partition --> Check.");
|
|
}
|
|
partition.push_back_message( temp );
|
|
}
|
|
|
|
if ( filesystem_resize_disallowed( partition ) )
|
|
{
|
|
Glib::ustring temp = get_filesystem_object( partition.filesystem )
|
|
->get_custom_text( CTEXT_RESIZE_DISALLOWED_WARNING );
|
|
if ( ! temp.empty() )
|
|
partition.push_back_message( temp );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Set usage of mounted but unsupported file systems.
|
|
if ( partition.busy )
|
|
mounted_fs_set_used_sectors(partition);
|
|
}
|
|
}
|
|
|
|
void GParted_Core::mounted_fs_set_used_sectors( Partition & partition )
|
|
{
|
|
if (partition.get_mountpoints().size() > 0 && Mount_Info::is_dev_mounted(partition.get_path()))
|
|
{
|
|
Byte_Value fs_size ;
|
|
Byte_Value fs_free ;
|
|
Glib::ustring error_message ;
|
|
if ( Utils::get_mounted_filesystem_usage( partition .get_mountpoint(),
|
|
fs_size, fs_free, error_message ) == 0 )
|
|
partition .set_sector_usage( fs_size / partition .sector_size,
|
|
fs_free / partition .sector_size ) ;
|
|
else
|
|
partition.push_back_message( error_message );
|
|
}
|
|
}
|
|
|
|
#ifdef HAVE_LIBPARTED_FS_RESIZE
|
|
void GParted_Core::LP_set_used_sectors( Partition & partition, PedDisk* lp_disk )
|
|
{
|
|
PedFileSystem *fs = NULL;
|
|
PedConstraint *constraint = NULL;
|
|
|
|
if ( lp_disk )
|
|
{
|
|
PedPartition* lp_partition = ped_disk_get_partition_by_sector( lp_disk, partition .get_sector() ) ;
|
|
|
|
if ( lp_partition )
|
|
{
|
|
fs = ped_file_system_open( & lp_partition ->geom );
|
|
|
|
if ( fs )
|
|
{
|
|
constraint = ped_file_system_get_resize_constraint( fs ) ;
|
|
if ( constraint )
|
|
{
|
|
partition .set_sector_usage( fs ->geom ->length,
|
|
fs ->geom ->length - constraint ->min_size ) ;
|
|
|
|
ped_constraint_destroy( constraint );
|
|
}
|
|
|
|
ped_file_system_close( fs ) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void GParted_Core::set_flags( Partition & partition, PedPartition* lp_partition )
|
|
{
|
|
for ( unsigned int t = 0 ; t < flags .size() ; t++ )
|
|
if ( ped_partition_is_flag_available( lp_partition, flags[ t ] ) &&
|
|
ped_partition_get_flag( lp_partition, flags[ t ] ) )
|
|
partition .flags .push_back( ped_partition_flag_get_name( flags[ t ] ) ) ;
|
|
}
|
|
|
|
bool GParted_Core::create( Partition & new_partition, OperationDetail & operationdetail )
|
|
{
|
|
bool success;
|
|
if ( new_partition.type == TYPE_EXTENDED )
|
|
{
|
|
success = create_partition( new_partition, operationdetail );
|
|
}
|
|
else
|
|
{
|
|
FS_Limits fs_limits = get_filesystem_limits( new_partition.filesystem, new_partition );
|
|
success = create_partition( new_partition, operationdetail,
|
|
fs_limits.min_size / new_partition.sector_size );
|
|
}
|
|
if ( ! success )
|
|
return false;
|
|
|
|
if ( ! new_partition.name.empty() )
|
|
{
|
|
if ( ! name_partition( new_partition, operationdetail ) )
|
|
return false;
|
|
}
|
|
|
|
if ( new_partition.type == TYPE_EXTENDED ||
|
|
new_partition.filesystem == FS_UNFORMATTED )
|
|
return true;
|
|
else if ( new_partition.filesystem == FS_CLEARED )
|
|
return erase_filesystem_signatures( new_partition, operationdetail );
|
|
else
|
|
return erase_filesystem_signatures( new_partition, operationdetail )
|
|
&& set_partition_type( new_partition, operationdetail )
|
|
&& create_filesystem( new_partition, operationdetail );
|
|
|
|
return false;
|
|
}
|
|
|
|
bool GParted_Core::create_partition( Partition & new_partition, OperationDetail & operationdetail, Sector min_size )
|
|
{
|
|
operationdetail .add_child( OperationDetail( _("create empty partition") ) ) ;
|
|
|
|
new_partition .partition_number = 0 ;
|
|
PedDevice* lp_device = NULL ;
|
|
PedDisk* lp_disk = NULL ;
|
|
if ( get_device_and_disk( new_partition .device_path, lp_device, lp_disk ) )
|
|
{
|
|
PedPartitionType type;
|
|
PedConstraint *constraint = NULL ;
|
|
PedFileSystemType* fs_type = NULL ;
|
|
|
|
//create new partition
|
|
switch ( new_partition .type )
|
|
{
|
|
case TYPE_PRIMARY:
|
|
type = PED_PARTITION_NORMAL ;
|
|
break ;
|
|
case TYPE_LOGICAL:
|
|
type = PED_PARTITION_LOGICAL ;
|
|
break ;
|
|
case TYPE_EXTENDED:
|
|
type = PED_PARTITION_EXTENDED ;
|
|
break ;
|
|
|
|
default :
|
|
type = PED_PARTITION_FREESPACE;
|
|
}
|
|
|
|
if (new_partition.type != TYPE_EXTENDED)
|
|
fs_type = ped_file_system_type_get( "ext2" ) ;
|
|
|
|
PedPartition* lp_partition = ped_partition_new( lp_disk,
|
|
type,
|
|
fs_type,
|
|
new_partition .sector_start,
|
|
new_partition .sector_end ) ;
|
|
|
|
if ( lp_partition )
|
|
{
|
|
if ( new_partition .alignment == ALIGN_STRICT
|
|
|| new_partition .alignment == ALIGN_MEBIBYTE
|
|
)
|
|
{
|
|
PedGeometry *geom = ped_geometry_new( lp_device,
|
|
new_partition .sector_start,
|
|
new_partition .get_sector_length() ) ;
|
|
|
|
if ( geom )
|
|
{
|
|
constraint = ped_constraint_exact( geom ) ;
|
|
ped_geometry_destroy( geom );
|
|
}
|
|
}
|
|
else
|
|
constraint = ped_constraint_any( lp_device );
|
|
|
|
if ( constraint )
|
|
{
|
|
if ( min_size > 0
|
|
&& new_partition .filesystem != FS_XFS // Permit copying to smaller xfs partition
|
|
)
|
|
constraint ->min_size = min_size ;
|
|
|
|
if ( ped_disk_add_partition( lp_disk, lp_partition, constraint ) && commit( lp_disk ) )
|
|
{
|
|
new_partition.set_path( get_partition_path( lp_partition ) );
|
|
|
|
new_partition .partition_number = lp_partition ->num ;
|
|
new_partition .sector_start = lp_partition ->geom .start ;
|
|
new_partition .sector_end = lp_partition ->geom .end ;
|
|
|
|
operationdetail .get_last_child() .add_child( OperationDetail(
|
|
/* TO TRANSLATORS: looks like path: /dev/sda1 (partition)
|
|
* This is showing the name and the fact
|
|
* that it is a partition within a device.
|
|
*/
|
|
Glib::ustring::compose( _("path: %1 (%2)"),
|
|
new_partition.get_path(), _("partition") ) + "\n" +
|
|
Glib::ustring::compose( _("start: %1"), new_partition .sector_start ) + "\n" +
|
|
Glib::ustring::compose( _("end: %1"), new_partition .sector_end ) + "\n" +
|
|
Glib::ustring::compose( _("size: %1 (%2)"),
|
|
new_partition .get_sector_length(),
|
|
Utils::format_size( new_partition .get_sector_length(), new_partition .sector_size ) ),
|
|
STATUS_NONE,
|
|
FONT_ITALIC ) ) ;
|
|
}
|
|
|
|
ped_constraint_destroy( constraint );
|
|
}
|
|
}
|
|
|
|
destroy_device_and_disk( lp_device, lp_disk ) ;
|
|
}
|
|
|
|
bool succes = new_partition .partition_number > 0 ;
|
|
|
|
#ifndef USE_LIBPARTED_DMRAID
|
|
//create dev map entries if dmraid
|
|
DMRaid dmraid ;
|
|
if ( succes && dmraid .is_dmraid_device( new_partition .device_path ) )
|
|
succes = dmraid .create_dev_map_entries( new_partition, operationdetail .get_last_child() ) ;
|
|
#endif
|
|
|
|
operationdetail.get_last_child().set_success_and_capture_errors( succes );
|
|
return succes ;
|
|
}
|
|
|
|
bool GParted_Core::create_filesystem( const Partition & partition, OperationDetail & operationdetail )
|
|
{
|
|
if ( partition.filesystem == FS_LUKS && partition.busy )
|
|
{
|
|
operationdetail.add_child( OperationDetail(
|
|
GPARTED_BUG + ": " + _("partition contains open LUKS encryption for a create file system only step"),
|
|
STATUS_ERROR, FONT_ITALIC ) );
|
|
return false;
|
|
}
|
|
|
|
operationdetail .add_child( OperationDetail( Glib::ustring::compose(
|
|
/*TO TRANSLATORS: looks like create new ext3 file system */
|
|
_("create new %1 file system"),
|
|
Utils::get_filesystem_string( partition .filesystem ) ) ) ) ;
|
|
|
|
bool succes = false ;
|
|
FileSystem* p_filesystem = NULL ;
|
|
switch ( get_fs( partition .filesystem ) .create )
|
|
{
|
|
case FS::NONE:
|
|
break ;
|
|
case FS::GPARTED:
|
|
break ;
|
|
case FS::LIBPARTED:
|
|
break ;
|
|
case FS::EXTERNAL:
|
|
succes = ( p_filesystem = get_filesystem_object( partition .filesystem ) ) &&
|
|
p_filesystem ->create( partition, operationdetail .get_last_child() ) ;
|
|
|
|
break ;
|
|
|
|
default:
|
|
break ;
|
|
}
|
|
|
|
operationdetail.get_last_child().set_success_and_capture_errors( succes );
|
|
return succes ;
|
|
}
|
|
|
|
bool GParted_Core::format( const Partition & partition, OperationDetail & operationdetail )
|
|
{
|
|
if ( partition.filesystem == FS_LUKS && partition.busy )
|
|
{
|
|
operationdetail.add_child( OperationDetail(
|
|
GPARTED_BUG + ": " + _("partition contains open LUKS encryption for a format file system only step"),
|
|
STATUS_ERROR, FONT_ITALIC ) );
|
|
return false;
|
|
}
|
|
|
|
if ( partition .filesystem == FS_CLEARED )
|
|
return erase_filesystem_signatures( partition, operationdetail ) ;
|
|
else
|
|
return erase_filesystem_signatures( partition, operationdetail )
|
|
&& set_partition_type( partition, operationdetail )
|
|
&& create_filesystem( partition, operationdetail ) ;
|
|
}
|
|
|
|
bool GParted_Core::delete_partition( const Partition & partition, OperationDetail & operationdetail )
|
|
{
|
|
operationdetail .add_child( OperationDetail( _("delete partition") ) ) ;
|
|
|
|
bool succes = false ;
|
|
PedDevice* lp_device = NULL ;
|
|
PedDisk* lp_disk = NULL ;
|
|
if ( get_device_and_disk( partition .device_path, lp_device, lp_disk ) )
|
|
{
|
|
PedPartition* lp_partition = get_lp_partition( lp_disk, partition );
|
|
|
|
succes = ped_disk_delete_partition( lp_disk, lp_partition ) && commit( lp_disk ) ;
|
|
|
|
destroy_device_and_disk( lp_device, lp_disk ) ;
|
|
}
|
|
|
|
#ifndef USE_LIBPARTED_DMRAID
|
|
//delete partition dev mapper entry, and delete and recreate all other affected dev mapper entries if dmraid
|
|
DMRaid dmraid ;
|
|
if ( succes && dmraid .is_dmraid_device( partition .device_path ) )
|
|
{
|
|
PedDevice* lp_device = NULL ;
|
|
PedDisk* lp_disk = NULL ;
|
|
//Open disk handle before and close after to prevent application crash.
|
|
if ( get_device_and_disk( partition .device_path, lp_device, lp_disk ) )
|
|
{
|
|
if ( ! dmraid .delete_affected_dev_map_entries( partition, operationdetail .get_last_child() ) )
|
|
succes = false ; //comand failed
|
|
|
|
if ( ! dmraid .create_dev_map_entries( partition, operationdetail .get_last_child() ) )
|
|
succes = false ; //command failed
|
|
|
|
destroy_device_and_disk( lp_device, lp_disk ) ;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
operationdetail.get_last_child().set_success_and_capture_errors( succes );
|
|
return succes ;
|
|
}
|
|
|
|
bool GParted_Core::remove_filesystem( const Partition & partition, OperationDetail & operationdetail )
|
|
{
|
|
if ( partition.filesystem == FS_LUKS && partition.busy )
|
|
{
|
|
operationdetail.add_child( OperationDetail(
|
|
GPARTED_BUG + ": " + _("partition contains open LUKS encryption for a delete file system only step"),
|
|
STATUS_ERROR, FONT_ITALIC ) );
|
|
return false;
|
|
}
|
|
|
|
bool success = true ;
|
|
FileSystem* p_filesystem = NULL ;
|
|
|
|
switch ( get_fs( partition .filesystem ) .remove )
|
|
{
|
|
case FS::EXTERNAL:
|
|
//Run file system specific remove method to delete the file system. Most
|
|
// file systems should NOT implement a remove() method as it will prevent
|
|
// recovery from accidental partition deletion.
|
|
operationdetail .add_child( OperationDetail( Glib::ustring::compose(
|
|
_("delete %1 file system"),
|
|
Utils::get_filesystem_string( partition .filesystem ) ) ) ) ;
|
|
success = ( p_filesystem = get_filesystem_object( partition .filesystem ) ) &&
|
|
p_filesystem ->remove( partition, operationdetail .get_last_child() ) ;
|
|
operationdetail.get_last_child().set_success_and_capture_errors( success );
|
|
break ;
|
|
|
|
default:
|
|
break ;
|
|
}
|
|
return success ;
|
|
}
|
|
|
|
bool GParted_Core::label_filesystem( const Partition & partition, OperationDetail & operationdetail )
|
|
{
|
|
if ( partition.filesystem == FS_LUKS && partition.busy )
|
|
{
|
|
operationdetail.add_child( OperationDetail(
|
|
GPARTED_BUG + ": " + _("partition contains open LUKS encryption for a label file system only step"),
|
|
STATUS_ERROR, FONT_ITALIC ) );
|
|
return false;
|
|
}
|
|
|
|
if( partition.get_filesystem_label().empty() ) {
|
|
operationdetail.add_child( OperationDetail(
|
|
Glib::ustring::compose( _("Clear file system label on %1"), partition.get_path() ) ) );
|
|
} else {
|
|
operationdetail.add_child( OperationDetail(
|
|
Glib::ustring::compose( _("Set file system label to \"%1\" on %2"),
|
|
partition.get_filesystem_label(), partition.get_path() ) ) );
|
|
}
|
|
|
|
bool succes = false ;
|
|
FileSystem* p_filesystem = NULL ;
|
|
switch ( get_fs( partition.filesystem ).write_label )
|
|
{
|
|
case FS::EXTERNAL:
|
|
succes = ( p_filesystem = get_filesystem_object( partition.filesystem ) )
|
|
&& p_filesystem->write_label( partition, operationdetail.get_last_child() );
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
operationdetail.get_last_child().set_success_and_capture_errors( succes );
|
|
return succes ;
|
|
}
|
|
|
|
bool GParted_Core::name_partition( const Partition & partition, OperationDetail & operationdetail )
|
|
{
|
|
if ( partition.name.empty() )
|
|
operationdetail.add_child( OperationDetail(
|
|
Glib::ustring::compose( _("Clear partition name on %1"), partition.get_path() ) ) );
|
|
else
|
|
operationdetail.add_child( OperationDetail(
|
|
Glib::ustring::compose( _("Set partition name to \"%1\" on %2"),
|
|
partition.name, partition.get_path() ) ) );
|
|
|
|
bool success = false;
|
|
PedDevice *lp_device = NULL;
|
|
PedDisk *lp_disk = NULL;
|
|
if ( get_device_and_disk( partition.device_path, lp_device, lp_disk ) )
|
|
{
|
|
PedPartition *lp_partition = ped_disk_get_partition_by_sector( lp_disk, partition.get_sector() );
|
|
if ( lp_partition )
|
|
{
|
|
success = ped_partition_set_name( lp_partition, partition.name.c_str() )
|
|
&& commit( lp_disk );
|
|
}
|
|
}
|
|
|
|
operationdetail.get_last_child().set_success_and_capture_errors( success );
|
|
return success;
|
|
}
|
|
|
|
bool GParted_Core::change_filesystem_uuid( const Partition & partition, OperationDetail & operationdetail )
|
|
{
|
|
if ( partition.filesystem == FS_LUKS && partition.busy )
|
|
{
|
|
operationdetail.add_child( OperationDetail(
|
|
GPARTED_BUG + ": " + _("partition contains open LUKS encryption for a change file system UUID only step"),
|
|
STATUS_ERROR, FONT_ITALIC ) );
|
|
return false;
|
|
}
|
|
|
|
if ( partition .uuid == UUID_RANDOM_NTFS_HALF ) {
|
|
operationdetail .add_child( OperationDetail( Glib::ustring::compose(
|
|
_("Set half of the UUID on %1 to a new, random value"),
|
|
partition .get_path()
|
|
) ) ) ;
|
|
} else {
|
|
operationdetail .add_child( OperationDetail( Glib::ustring::compose(
|
|
_("Set UUID on %1 to a new, random value"),
|
|
partition .get_path()
|
|
) ) ) ;
|
|
}
|
|
|
|
bool succes = false ;
|
|
FileSystem* p_filesystem = NULL ;
|
|
switch ( get_fs( partition.filesystem ).write_uuid )
|
|
{
|
|
case FS::EXTERNAL:
|
|
succes = ( p_filesystem = get_filesystem_object( partition.filesystem ) )
|
|
&& p_filesystem->write_uuid( partition, operationdetail.get_last_child() );
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
operationdetail.get_last_child().set_success_and_capture_errors( succes );
|
|
return succes ;
|
|
}
|
|
|
|
bool GParted_Core::resize_move( const Partition & partition_old,
|
|
Partition & partition_new,
|
|
OperationDetail & operationdetail )
|
|
{
|
|
if ( (partition_new .alignment == ALIGN_STRICT)
|
|
|| (partition_new .alignment == ALIGN_MEBIBYTE)
|
|
|| partition_new .strict_start
|
|
|| calculate_exact_geom( partition_old, partition_new, operationdetail )
|
|
)
|
|
{
|
|
if ( partition_old .type == TYPE_EXTENDED )
|
|
return resize_move_partition( partition_old, partition_new, operationdetail, false );
|
|
|
|
if ( partition_new .sector_start == partition_old .sector_start )
|
|
return resize( partition_old, partition_new, operationdetail ) ;
|
|
|
|
if ( partition_new .get_sector_length() == partition_old .get_sector_length() )
|
|
{
|
|
return move( partition_old, partition_new, operationdetail );
|
|
}
|
|
Partition * temp = NULL;
|
|
if ( partition_new .get_sector_length() > partition_old .get_sector_length() )
|
|
{
|
|
//first move, then grow. Since old.length < new.length and new.start is valid, temp is valid.
|
|
temp = partition_new.clone();
|
|
temp->sector_end = temp->sector_start + partition_old.get_sector_length() - 1;
|
|
}
|
|
else // ( partition_new.get_sector_length() < partition_old.get_sector_length() )
|
|
{
|
|
//first shrink, then move. Since new.length < old.length and old.start is valid, temp is valid.
|
|
temp = partition_old.clone();
|
|
temp->sector_end = partition_old.sector_start + partition_new.get_sector_length() - 1;
|
|
}
|
|
|
|
PartitionAlignment previous_alignment = temp->alignment;
|
|
temp->alignment = ALIGN_STRICT;
|
|
bool success = resize_move( partition_old, *temp, operationdetail );
|
|
temp->alignment = previous_alignment;
|
|
if ( success )
|
|
success = resize_move( *temp, partition_new, operationdetail );
|
|
|
|
delete temp;
|
|
temp = NULL;
|
|
|
|
return success;
|
|
}
|
|
|
|
return false ;
|
|
}
|
|
|
|
bool GParted_Core::move( const Partition & partition_old,
|
|
const Partition & partition_new,
|
|
OperationDetail & operationdetail )
|
|
{
|
|
if ( partition_old .get_sector_length() != partition_new .get_sector_length() )
|
|
{
|
|
operationdetail .add_child( OperationDetail(
|
|
/* TO TRANSLATORS:
|
|
* means that GParted has encountered a programming bug and tried
|
|
* to change the size of a partition when performing a move only
|
|
* step which is not permitted to change the partition size.
|
|
*/
|
|
GPARTED_BUG + ": " + _("size of the partition is changing for a move only step"),
|
|
STATUS_ERROR, FONT_ITALIC ) );
|
|
return false ;
|
|
}
|
|
|
|
if ( ! check_repair_filesystem( partition_old, operationdetail ) )
|
|
return false;
|
|
|
|
// NOTE:
|
|
// Logical partitions are preceded by meta data. To prevent this meta data from
|
|
// being overwritten we first expand the partition to encompass all of the space
|
|
// involved in the move. In this way we prevent overwriting the meta data for
|
|
// this partition when we move this partition to the left. We also prevent
|
|
// overwriting the meta data of a following partition when we move this partition
|
|
// to the right.
|
|
Partition * partition_all_space = partition_old.clone();
|
|
partition_all_space->alignment = ALIGN_STRICT;
|
|
if ( partition_new.sector_start < partition_all_space->sector_start )
|
|
partition_all_space->sector_start = partition_new.sector_start;
|
|
if ( partition_new.sector_end > partition_all_space->sector_end )
|
|
partition_all_space->sector_end = partition_new.sector_end;
|
|
|
|
// Make old partition all encompassing and if move file system fails then return
|
|
// partition table to original state
|
|
bool success = false;
|
|
if ( resize_move_partition( partition_old, *partition_all_space, operationdetail, true ) )
|
|
{
|
|
// Note move of file system is from old values to new values, not from the
|
|
// all encompassing values.
|
|
if ( ! move_filesystem( partition_old, partition_new, operationdetail ) )
|
|
{
|
|
operationdetail.add_child( OperationDetail( _("rollback last change to the partition") ) );
|
|
|
|
Partition * partition_restore = partition_old.clone();
|
|
partition_restore->alignment = ALIGN_STRICT; // Ensure that old partition boundaries are not modified
|
|
if ( resize_move_partition( *partition_all_space, *partition_restore,
|
|
operationdetail.get_last_child(), false ) )
|
|
{
|
|
operationdetail.get_last_child().set_success_and_capture_errors( true );
|
|
check_repair_filesystem( partition_old, operationdetail );
|
|
}
|
|
else
|
|
{
|
|
operationdetail.get_last_child().set_success_and_capture_errors( false );
|
|
}
|
|
|
|
delete partition_restore;
|
|
partition_restore = NULL;
|
|
}
|
|
else
|
|
{
|
|
success = true;
|
|
}
|
|
}
|
|
|
|
// Make new partition from all encompassing partition
|
|
if ( success )
|
|
{
|
|
success = resize_move_partition( *partition_all_space, partition_new, operationdetail, false )
|
|
&& update_bootsector( partition_new, operationdetail );
|
|
}
|
|
|
|
delete partition_all_space;
|
|
partition_all_space = NULL;
|
|
|
|
if ( ! success )
|
|
return false;
|
|
|
|
if ( partition_new.filesystem == FS_LINUX_SWAP )
|
|
// linux-swap is recreated, not moved
|
|
return recreate_linux_swap_filesystem( partition_new, operationdetail );
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GParted_Core::move_filesystem( const Partition & partition_old,
|
|
const Partition & partition_new,
|
|
OperationDetail & operationdetail )
|
|
{
|
|
if ( partition_new .sector_start < partition_old .sector_start )
|
|
operationdetail .add_child( OperationDetail( _("move file system to the left") ) ) ;
|
|
else if ( partition_new .sector_start > partition_old .sector_start )
|
|
operationdetail .add_child( OperationDetail( _("move file system to the right") ) ) ;
|
|
else
|
|
{
|
|
operationdetail .add_child( OperationDetail( _("move file system") ) ) ;
|
|
operationdetail .get_last_child() .add_child(
|
|
OperationDetail( _("new and old file system have the same position. Hence skipping this operation"),
|
|
STATUS_NONE,
|
|
FONT_ITALIC ) ) ;
|
|
|
|
operationdetail.get_last_child().set_success_and_capture_errors( true );
|
|
return true ;
|
|
}
|
|
|
|
bool succes = false ;
|
|
FileSystem* p_filesystem = NULL ;
|
|
Sector total_done = 0;
|
|
switch ( get_fs( partition_old .filesystem ) .move )
|
|
{
|
|
case FS::NONE:
|
|
break ;
|
|
case FS::GPARTED:
|
|
succes = false ;
|
|
if ( partition_new .test_overlap( partition_old ) )
|
|
{
|
|
succes = copy_filesystem_internal( partition_old,
|
|
partition_new,
|
|
operationdetail.get_last_child(),
|
|
total_done,
|
|
true );
|
|
|
|
operationdetail.get_last_child().get_last_child()
|
|
.set_success_and_capture_errors( succes );
|
|
if ( ! succes )
|
|
{
|
|
rollback_move_filesystem( partition_old,
|
|
partition_new,
|
|
operationdetail.get_last_child(),
|
|
total_done );
|
|
}
|
|
}
|
|
else
|
|
succes = copy_filesystem_internal( partition_old,
|
|
partition_new,
|
|
operationdetail.get_last_child(),
|
|
total_done,
|
|
true );
|
|
|
|
break ;
|
|
case FS::LIBPARTED:
|
|
break ;
|
|
case FS::EXTERNAL:
|
|
succes = ( p_filesystem = get_filesystem_object( partition_new .filesystem ) ) &&
|
|
p_filesystem ->move( partition_old
|
|
, partition_new
|
|
, operationdetail .get_last_child()
|
|
) ;
|
|
break ;
|
|
|
|
default:
|
|
break ;
|
|
}
|
|
|
|
operationdetail.get_last_child().set_success_and_capture_errors( succes );
|
|
return succes ;
|
|
}
|
|
|
|
#ifdef HAVE_LIBPARTED_FS_RESIZE
|
|
bool GParted_Core::resize_move_filesystem_using_libparted( const Partition & partition_old,
|
|
const Partition & partition_new,
|
|
OperationDetail & operationdetail )
|
|
{
|
|
operationdetail .add_child( OperationDetail( _("using libparted"), STATUS_NONE ) ) ;
|
|
|
|
bool return_value = false ;
|
|
PedDevice* lp_device = NULL ;
|
|
PedDisk* lp_disk = NULL ;
|
|
if ( get_device_and_disk( partition_old .device_path, lp_device, lp_disk ) )
|
|
{
|
|
PedFileSystem * fs = NULL ;
|
|
PedGeometry * lp_geom = NULL ;
|
|
|
|
lp_geom = ped_geometry_new( lp_device,
|
|
partition_old .sector_start,
|
|
partition_old .get_sector_length() ) ;
|
|
if ( lp_geom )
|
|
{
|
|
fs = ped_file_system_open( lp_geom );
|
|
|
|
ped_geometry_destroy( lp_geom );
|
|
lp_geom = NULL;
|
|
|
|
if ( fs )
|
|
{
|
|
lp_geom = ped_geometry_new( lp_device,
|
|
partition_new .sector_start,
|
|
partition_new .get_sector_length() ) ;
|
|
if ( lp_geom )
|
|
{
|
|
// Use thread for libparted FS resize call to avoid blocking GUI
|
|
Glib::Thread::create( sigc::bind<PedFileSystem *, PedGeometry *, bool *>(
|
|
sigc::mem_fun( *this, &GParted_Core::thread_lp_ped_file_system_resize ),
|
|
fs,
|
|
lp_geom,
|
|
&return_value ),
|
|
false );
|
|
Gtk::Main::run();
|
|
|
|
if ( return_value )
|
|
commit( lp_disk ) ;
|
|
|
|
ped_geometry_destroy( lp_geom );
|
|
}
|
|
|
|
ped_file_system_close( fs );
|
|
}
|
|
}
|
|
|
|
destroy_device_and_disk( lp_device, lp_disk ) ;
|
|
}
|
|
|
|
return return_value ;
|
|
}
|
|
|
|
void GParted_Core::thread_lp_ped_file_system_resize( PedFileSystem * fs,
|
|
PedGeometry * lp_geom,
|
|
bool * return_value )
|
|
{
|
|
*return_value = ped_file_system_resize( fs, lp_geom, NULL );
|
|
g_idle_add( (GSourceFunc)_mainquit, NULL );
|
|
}
|
|
#endif
|
|
|
|
bool GParted_Core::resize( const Partition & partition_old,
|
|
const Partition & partition_new,
|
|
OperationDetail & operationdetail )
|
|
{
|
|
if ( partition_old .sector_start != partition_new .sector_start )
|
|
{
|
|
operationdetail .add_child( OperationDetail(
|
|
/* TO TRANSLATORS:
|
|
* means that GParted has encountered a programming bug and tried
|
|
* to move the start of the partition when performing a resize
|
|
* only step which is not permitted to change the start of the
|
|
* partition.
|
|
*/
|
|
GPARTED_BUG + ": " + _("start of the partition is changing for a resize only step"),
|
|
STATUS_ERROR, FONT_ITALIC ) );
|
|
return false ;
|
|
}
|
|
|
|
if ( partition_new.filesystem == FS_LUKS )
|
|
return resize_encryption( partition_old, partition_new, operationdetail );
|
|
else
|
|
return resize_plain( partition_old, partition_new, operationdetail );
|
|
}
|
|
|
|
bool GParted_Core::resize_encryption( const Partition & partition_old,
|
|
const Partition & partition_new,
|
|
OperationDetail & operationdetail )
|
|
{
|
|
if ( partition_old.filesystem != FS_LUKS )
|
|
{
|
|
operationdetail.add_child( OperationDetail(
|
|
GPARTED_BUG + ": " + _("partition does not contain LUKS encryption for a resize encryption only step"),
|
|
STATUS_ERROR, FONT_ITALIC ) );
|
|
return false;
|
|
}
|
|
|
|
Sector delta = partition_new.get_sector_length() - partition_old.get_sector_length();
|
|
|
|
if ( ! partition_old.busy && delta < 0LL )
|
|
{
|
|
operationdetail.add_child( OperationDetail(
|
|
GPARTED_BUG + ": " + _("impossible to shrink a closed LUKS encryption volume"),
|
|
STATUS_ERROR, FONT_ITALIC ) );
|
|
return false;
|
|
}
|
|
|
|
if ( ! partition_old.busy )
|
|
{
|
|
// grow closed LUKS.
|
|
// maximize_encryption() is only called to display no action needed
|
|
// operation message generated in luks::resize() for this case.
|
|
return resize_move_partition( partition_old, partition_new, operationdetail, true )
|
|
&& maximize_encryption( partition_new, operationdetail );
|
|
}
|
|
|
|
const Partition & filesystem_ptn_new = partition_new.get_filesystem_partition();
|
|
|
|
if ( filesystem_ptn_new.filesystem == FS_LINUX_SWAP )
|
|
{
|
|
// LUKS is resized, but linux-swap is recreated, not resized
|
|
if ( delta < 0LL ) // shrink
|
|
{
|
|
return shrink_encryption( partition_old, partition_new, operationdetail )
|
|
&& resize_move_partition( partition_old, partition_new, operationdetail, false )
|
|
&& recreate_linux_swap_filesystem( filesystem_ptn_new, operationdetail );
|
|
}
|
|
else if ( delta > 0LL ) // grow
|
|
{
|
|
return resize_move_partition( partition_old, partition_new, operationdetail, true )
|
|
&& maximize_encryption( partition_new, operationdetail )
|
|
&& recreate_linux_swap_filesystem( filesystem_ptn_new, operationdetail );
|
|
}
|
|
}
|
|
|
|
const Partition & filesystem_ptn_old = partition_old.get_filesystem_partition();
|
|
if ( delta < 0LL ) // shrink
|
|
{
|
|
return check_repair_filesystem( filesystem_ptn_old, operationdetail )
|
|
&& shrink_filesystem( filesystem_ptn_old, filesystem_ptn_new, operationdetail )
|
|
&& shrink_encryption( partition_old, partition_new, operationdetail )
|
|
&& resize_move_partition( partition_old, partition_new, operationdetail, false );
|
|
}
|
|
else if ( delta > 0LL ) // grow
|
|
{
|
|
return check_repair_filesystem( filesystem_ptn_old, operationdetail )
|
|
&& resize_move_partition( partition_old, partition_new, operationdetail, true )
|
|
&& maximize_encryption( partition_new, operationdetail )
|
|
&& maximize_filesystem( filesystem_ptn_new, operationdetail );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GParted_Core::resize_plain( const Partition & partition_old,
|
|
const Partition & partition_new,
|
|
OperationDetail & operationdetail )
|
|
{
|
|
if ( partition_old.filesystem == FS_LUKS && partition_old.busy )
|
|
{
|
|
operationdetail.add_child( OperationDetail(
|
|
GPARTED_BUG + ": " + _("partition contains open LUKS encryption for a resize file system only step"),
|
|
STATUS_ERROR, FONT_ITALIC ) );
|
|
return false;
|
|
}
|
|
|
|
if ( partition_new.filesystem == FS_LINUX_SWAP )
|
|
{
|
|
// linux-swap is recreated, not resized
|
|
return resize_move_partition( partition_old, partition_new, operationdetail, true )
|
|
&& recreate_linux_swap_filesystem( partition_new, operationdetail );
|
|
}
|
|
|
|
Sector delta = partition_new.get_sector_length() - partition_old.get_sector_length();
|
|
if ( delta < 0LL ) // shrink
|
|
{
|
|
return check_repair_filesystem( partition_new, operationdetail )
|
|
&& shrink_filesystem( partition_old, partition_new, operationdetail )
|
|
&& resize_move_partition( partition_old, partition_new, operationdetail, false );
|
|
}
|
|
else if ( delta > 0LL ) // grow
|
|
{
|
|
return check_repair_filesystem( partition_new, operationdetail )
|
|
&& resize_move_partition( partition_old, partition_new, operationdetail, true )
|
|
&& maximize_filesystem( partition_new, operationdetail );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GParted_Core::resize_move_partition( const Partition & partition_old,
|
|
const Partition & partition_new,
|
|
OperationDetail & operationdetail,
|
|
bool rollback_on_fail )
|
|
{
|
|
if ( partition_new.type == TYPE_UNPARTITIONED )
|
|
// Trying to resize/move a non-partitioned whole disk device is a
|
|
// successful non-operation.
|
|
return true;
|
|
|
|
//i'm not too happy with this, but i think it is the correct way from a i18n POV
|
|
enum Action
|
|
{
|
|
NONE = 0,
|
|
MOVE_RIGHT = 1,
|
|
MOVE_LEFT = 2,
|
|
GROW = 3,
|
|
SHRINK = 4,
|
|
MOVE_RIGHT_GROW = 5,
|
|
MOVE_RIGHT_SHRINK = 6,
|
|
MOVE_LEFT_GROW = 7,
|
|
MOVE_LEFT_SHRINK = 8
|
|
} ;
|
|
Action action = NONE ;
|
|
|
|
if ( partition_new .get_sector_length() > partition_old .get_sector_length() )
|
|
action = GROW ;
|
|
else if ( partition_new .get_sector_length() < partition_old .get_sector_length() )
|
|
action = SHRINK ;
|
|
|
|
if ( partition_new .sector_start > partition_old .sector_start &&
|
|
partition_new .sector_end > partition_old .sector_end )
|
|
action = action == GROW ? MOVE_RIGHT_GROW : action == SHRINK ? MOVE_RIGHT_SHRINK : MOVE_RIGHT ;
|
|
else if ( partition_new .sector_start < partition_old .sector_start &&
|
|
partition_new .sector_end < partition_old .sector_end )
|
|
action = action == GROW ? MOVE_LEFT_GROW : action == SHRINK ? MOVE_LEFT_SHRINK : MOVE_LEFT ;
|
|
|
|
Glib::ustring description ;
|
|
switch ( action )
|
|
{
|
|
case NONE :
|
|
description = _("resize/move partition") ;
|
|
break ;
|
|
case MOVE_RIGHT :
|
|
description = _("move partition to the right") ;
|
|
break ;
|
|
case MOVE_LEFT :
|
|
description = _("move partition to the left") ;
|
|
break ;
|
|
case GROW :
|
|
description = _("grow partition from %1 to %2") ;
|
|
break ;
|
|
case SHRINK :
|
|
description = _("shrink partition from %1 to %2") ;
|
|
break ;
|
|
case MOVE_RIGHT_GROW :
|
|
description = _("move partition to the right and grow it from %1 to %2") ;
|
|
break ;
|
|
case MOVE_RIGHT_SHRINK :
|
|
description = _("move partition to the right and shrink it from %1 to %2") ;
|
|
break ;
|
|
case MOVE_LEFT_GROW :
|
|
description = _("move partition to the left and grow it from %1 to %2") ;
|
|
break ;
|
|
case MOVE_LEFT_SHRINK :
|
|
description = _("move partition to the left and shrink it from %1 to %2") ;
|
|
break ;
|
|
}
|
|
|
|
if ( ! description .empty() && action != NONE && action != MOVE_LEFT && action != MOVE_RIGHT )
|
|
description = Glib::ustring::compose( description,
|
|
Utils::format_size( partition_old .get_sector_length(), partition_old .sector_size ),
|
|
Utils::format_size( partition_new .get_sector_length(), partition_new .sector_size ) ) ;
|
|
|
|
operationdetail .add_child( OperationDetail( description ) ) ;
|
|
|
|
|
|
if ( action == NONE )
|
|
{
|
|
operationdetail .get_last_child() .add_child(
|
|
OperationDetail( _("new and old partition have the same size and position. Hence skipping this operation"),
|
|
STATUS_NONE,
|
|
FONT_ITALIC ) ) ;
|
|
|
|
operationdetail.get_last_child().set_success_and_capture_errors( true );
|
|
return true ;
|
|
}
|
|
|
|
operationdetail .get_last_child() .add_child(
|
|
OperationDetail(
|
|
Glib::ustring::compose( _("old start: %1"), partition_old .sector_start ) + "\n" +
|
|
Glib::ustring::compose( _("old end: %1"), partition_old .sector_end ) + "\n" +
|
|
Glib::ustring::compose( _("old size: %1 (%2)"),
|
|
partition_old .get_sector_length(),
|
|
Utils::format_size( partition_old .get_sector_length(), partition_old .sector_size ) ),
|
|
STATUS_NONE,
|
|
FONT_ITALIC ) ) ;
|
|
|
|
//finally the actual resize/move
|
|
Sector new_start = -1;
|
|
Sector new_end = -1;
|
|
bool success = resize_move_partition_implement( partition_old, partition_new, new_start, new_end );
|
|
if ( success )
|
|
{
|
|
//Change to partition succeeded
|
|
operationdetail .get_last_child() .add_child(
|
|
OperationDetail(
|
|
Glib::ustring::compose( _("new start: %1"), new_start ) + "\n" +
|
|
Glib::ustring::compose( _("new end: %1"), new_end ) + "\n" +
|
|
Glib::ustring::compose( _("new size: %1 (%2)"),
|
|
new_end - new_start + 1,
|
|
Utils::format_size( new_end - new_start + 1, partition_new .sector_size ) ),
|
|
STATUS_NONE,
|
|
FONT_ITALIC ) ) ;
|
|
|
|
//update dev mapper entry if partition is dmraid.
|
|
success = success && update_dmraid_entry( partition_new, operationdetail );
|
|
}
|
|
else
|
|
{
|
|
//Change to partition failed
|
|
operationdetail .get_last_child() .add_child(
|
|
OperationDetail(
|
|
Glib::ustring::compose( _("requested start: %1"), partition_new .sector_start ) + "\n" +
|
|
Glib::ustring::compose( _("requested end: %1"), partition_new . sector_end ) + "\n" +
|
|
Glib::ustring::compose( _("requested size: %1 (%2)"),
|
|
partition_new .get_sector_length(),
|
|
Utils::format_size( partition_new .get_sector_length(), partition_new .sector_size ) ),
|
|
STATUS_NONE,
|
|
FONT_ITALIC )
|
|
) ;
|
|
}
|
|
operationdetail.get_last_child().set_success_and_capture_errors( success );
|
|
|
|
if ( ! success && rollback_on_fail )
|
|
{
|
|
operationdetail.add_child(
|
|
OperationDetail( _("attempt to rollback failed change to the partition") ) );
|
|
|
|
// Create a temporary partition which is the intersection of the old and
|
|
// new partitions so that the middle sector can be used to identify the
|
|
// partition needing rollback, whether or not the above failed partition
|
|
// change made it to the drive or not.
|
|
Partition *partition_intersection = partition_new.clone();
|
|
partition_intersection->sector_start = std::max( partition_old.sector_start,
|
|
partition_new.sector_start );
|
|
partition_intersection->sector_end = std::min( partition_old.sector_end,
|
|
partition_new.sector_end );
|
|
|
|
Partition *partition_restore = partition_old.clone();
|
|
// Ensure that old partition boundaries are not modified
|
|
partition_restore->alignment = ALIGN_STRICT;
|
|
|
|
bool rollback_success = resize_move_partition_implement( *partition_intersection, *partition_restore,
|
|
new_start, new_end );
|
|
|
|
operationdetail.get_last_child().add_child(
|
|
OperationDetail(
|
|
Glib::ustring::compose( _("original start: %1"), partition_restore->sector_start ) + "\n" +
|
|
Glib::ustring::compose( _("original end: %1"), partition_restore->sector_end ) + "\n" +
|
|
Glib::ustring::compose( _("original size: %1 (%2)"),
|
|
partition_restore->get_sector_length(),
|
|
Utils::format_size( partition_restore->get_sector_length(), partition_restore->sector_size ) ),
|
|
STATUS_NONE, FONT_ITALIC ) );
|
|
|
|
// Update dev mapper entry if partition is dmraid.
|
|
rollback_success = rollback_success && update_dmraid_entry( *partition_restore, operationdetail );
|
|
|
|
delete partition_restore;
|
|
partition_restore = NULL;
|
|
delete partition_intersection;
|
|
partition_intersection = NULL;
|
|
|
|
operationdetail.get_last_child().set_success_and_capture_errors( rollback_success );
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
bool GParted_Core::resize_move_partition_implement( const Partition & partition_old,
|
|
const Partition & partition_new,
|
|
Sector & new_start,
|
|
Sector & new_end )
|
|
{
|
|
bool success = false;
|
|
PedDevice *lp_device = NULL;
|
|
PedDisk *lp_disk = NULL;
|
|
if ( get_device_and_disk( partition_old.device_path, lp_device, lp_disk ) )
|
|
{
|
|
PedPartition *lp_partition = get_lp_partition( lp_disk, partition_old );
|
|
if ( lp_partition )
|
|
{
|
|
PedConstraint *constraint = NULL;
|
|
if ( partition_new.alignment == ALIGN_STRICT ||
|
|
partition_new.alignment == ALIGN_MEBIBYTE ||
|
|
partition_new.strict_start )
|
|
{
|
|
PedGeometry *geom = ped_geometry_new( lp_device,
|
|
partition_new.sector_start,
|
|
partition_new.get_sector_length() );
|
|
if ( geom )
|
|
{
|
|
constraint = ped_constraint_exact( geom );
|
|
ped_geometry_destroy( geom );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
constraint = ped_constraint_any( lp_device );
|
|
}
|
|
|
|
if ( constraint )
|
|
{
|
|
if ( ped_disk_set_partition_geom( lp_disk,
|
|
lp_partition,
|
|
constraint,
|
|
partition_new.sector_start,
|
|
partition_new.sector_end ) )
|
|
{
|
|
new_start = lp_partition->geom.start;
|
|
new_end = lp_partition->geom.end;
|
|
|
|
success = commit( lp_disk );
|
|
}
|
|
|
|
ped_constraint_destroy( constraint );
|
|
}
|
|
}
|
|
|
|
destroy_device_and_disk( lp_device, lp_disk );
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
bool GParted_Core::shrink_encryption( const Partition & partition_old,
|
|
const Partition & partition_new,
|
|
OperationDetail & operationdetail )
|
|
{
|
|
if ( ! ( partition_old.filesystem == FS_LUKS && partition_old.busy ) )
|
|
{
|
|
operationdetail.add_child( OperationDetail(
|
|
GPARTED_BUG + ": " + _("partition does not contain open LUKS encryption for a shrink encryption only step"),
|
|
STATUS_ERROR, FONT_ITALIC ) );
|
|
return false;
|
|
}
|
|
|
|
operationdetail.add_child( OperationDetail( _("shrink encryption volume") ) );
|
|
bool success = resize_filesystem_implement( partition_old, partition_new, operationdetail );
|
|
operationdetail.get_last_child().set_success_and_capture_errors( success );
|
|
return success;
|
|
}
|
|
|
|
bool GParted_Core::maximize_encryption( const Partition & partition, OperationDetail & operationdetail )
|
|
{
|
|
if ( partition.filesystem != FS_LUKS )
|
|
{
|
|
operationdetail.add_child( OperationDetail(
|
|
GPARTED_BUG + ": " + _("partition does not contain LUKS encryption for a maximize encryption only step"),
|
|
STATUS_ERROR, FONT_ITALIC ) );
|
|
return false;
|
|
}
|
|
|
|
operationdetail.add_child( OperationDetail( _("grow encryption volume to fill the partition") ) );
|
|
|
|
// Checking if growing is allowed is only relevant for the check repair operation
|
|
// to inform the user why the grow step is being skipped. For a resize/move
|
|
// operation these growing checks are merely retesting those performed to allow
|
|
// the operation to be queued in the first place. See
|
|
// Win_GParted::set_valid_operations().
|
|
if ( get_fs( partition.filesystem ).grow == FS::NONE )
|
|
{
|
|
operationdetail.get_last_child().add_child( OperationDetail(
|
|
_("growing is not available for this encryption volume"),
|
|
STATUS_NONE, FONT_ITALIC ) );
|
|
operationdetail.get_last_child().set_status( STATUS_WARNING );
|
|
return true;
|
|
}
|
|
|
|
bool success = resize_filesystem_implement( partition, partition, operationdetail );
|
|
operationdetail.get_last_child().set_success_and_capture_errors( success );
|
|
return success;
|
|
}
|
|
|
|
bool GParted_Core::shrink_filesystem( const Partition & partition_old,
|
|
const Partition & partition_new,
|
|
OperationDetail & operationdetail )
|
|
{
|
|
if ( partition_old.filesystem == FS_LUKS && partition_old.busy )
|
|
{
|
|
operationdetail.add_child( OperationDetail(
|
|
GPARTED_BUG + ": " + _("partition contains open LUKS encryption for a shrink file system only step"),
|
|
STATUS_ERROR, FONT_ITALIC ) );
|
|
return false;
|
|
}
|
|
if ( partition_new.get_sector_length() >= partition_old.get_sector_length() )
|
|
{
|
|
operationdetail.add_child( OperationDetail(
|
|
/* TO TRANSLATORS:
|
|
* means that GParted has encountered a programming bug and tried
|
|
* to grow the partition size or keep it the same when performing
|
|
* a shrink partition only step.
|
|
*/
|
|
GPARTED_BUG + ": " + _("the new partition size is larger or the same for a shrink only step"),
|
|
STATUS_ERROR, FONT_ITALIC ) );
|
|
return false;
|
|
}
|
|
|
|
operationdetail.add_child( OperationDetail( _("shrink file system") ) );
|
|
bool success = resize_filesystem_implement( partition_old, partition_new, operationdetail );
|
|
operationdetail.get_last_child().set_success_and_capture_errors( success );
|
|
return success;
|
|
}
|
|
|
|
bool GParted_Core::maximize_filesystem( const Partition & partition, OperationDetail & operationdetail )
|
|
{
|
|
if ( partition.filesystem == FS_LUKS && partition.busy )
|
|
{
|
|
operationdetail.add_child( OperationDetail(
|
|
GPARTED_BUG + ": " + _("partition contains open LUKS encryption for a maximize file system only step"),
|
|
STATUS_ERROR, FONT_ITALIC ) );
|
|
return false;
|
|
}
|
|
|
|
operationdetail .add_child( OperationDetail( _("grow file system to fill the partition") ) ) ;
|
|
|
|
// Checking if growing is available or allowed is only relevant for the check
|
|
// repair operation to inform the user why the grow step is being skipped. For a
|
|
// resize/move operation these growing checks are merely retesting those performed
|
|
// to allow the operation to be queued in the first place. See
|
|
// Win_GParted::set_valid_operations() and
|
|
// Dialog_Partition_Resize_Move::Resize_Move_Normal().
|
|
if (get_fs(partition.filesystem).grow == FS::NONE)
|
|
{
|
|
operationdetail .get_last_child() .add_child(
|
|
OperationDetail( _("growing is not available for this file system"),
|
|
STATUS_NONE,
|
|
FONT_ITALIC ) ) ;
|
|
operationdetail.get_last_child().set_status( STATUS_WARNING );
|
|
return true ;
|
|
}
|
|
else if ( filesystem_resize_disallowed( partition ) )
|
|
{
|
|
Glib::ustring msg = _("growing the file system is currently disallowed") ;
|
|
Glib::ustring custom_msg = get_filesystem_object( partition .filesystem )
|
|
->get_custom_text( CTEXT_RESIZE_DISALLOWED_WARNING ) ;
|
|
if ( ! custom_msg .empty() )
|
|
{
|
|
msg += "\n" ;
|
|
msg += custom_msg ;
|
|
}
|
|
operationdetail .get_last_child() .add_child( OperationDetail( msg, STATUS_NONE, FONT_ITALIC ) ) ;
|
|
operationdetail.get_last_child().set_status( STATUS_WARNING );
|
|
return true ;
|
|
}
|
|
|
|
bool success = resize_filesystem_implement( partition, partition, operationdetail );
|
|
operationdetail.get_last_child().set_success_and_capture_errors( success );
|
|
return success;
|
|
}
|
|
|
|
bool GParted_Core::recreate_linux_swap_filesystem( const Partition & partition, OperationDetail & operationdetail )
|
|
{
|
|
if ( partition.filesystem != FS_LINUX_SWAP )
|
|
{
|
|
operationdetail.add_child( OperationDetail(
|
|
/* TO TRANSLATORS: looks like not a linux-swap file system for a recreate linux-swap only step */
|
|
GPARTED_BUG + ": " + Glib::ustring::compose( _("not a %1 file system for a recreate %1 only step"),
|
|
Utils::get_filesystem_string( FS_LINUX_SWAP ),
|
|
Utils::get_filesystem_string( FS_LINUX_SWAP ) ),
|
|
STATUS_ERROR, FONT_ITALIC ) );
|
|
return false;
|
|
}
|
|
|
|
if ( ! erase_filesystem_signatures( partition, operationdetail ) )
|
|
return false;
|
|
|
|
operationdetail.add_child( OperationDetail(
|
|
/* TO TRANSLATORS: looks like recreate linux-swap file system */
|
|
Glib::ustring::compose( _("recreate %1 file system"),
|
|
Utils::get_filesystem_string( FS_LINUX_SWAP ) ) ) );
|
|
|
|
// Linux-swap is recreated by using the linux_swap::resize() method
|
|
bool success = resize_filesystem_implement( partition, partition, operationdetail );
|
|
operationdetail.get_last_child().set_success_and_capture_errors( success );
|
|
return success;
|
|
}
|
|
|
|
bool GParted_Core::resize_filesystem_implement( const Partition & partition_old,
|
|
const Partition & partition_new,
|
|
OperationDetail & operationdetail )
|
|
{
|
|
|
|
bool fill_partition = false;
|
|
const FS & fs_cap = get_fs( partition_new.filesystem );
|
|
FS::Support action = FS::NONE;
|
|
if ( partition_new.get_sector_length() >= partition_old.get_sector_length() )
|
|
{
|
|
// grow (always maximises the file system to fill the partition)
|
|
fill_partition = true;
|
|
action = ( partition_old.busy ) ? fs_cap.online_grow : fs_cap.grow;
|
|
}
|
|
else
|
|
{
|
|
// shrink
|
|
fill_partition = false;
|
|
action = ( partition_old.busy ) ? fs_cap.online_shrink : fs_cap.shrink;
|
|
}
|
|
bool success = false;
|
|
FileSystem* p_filesystem = NULL;
|
|
switch ( action )
|
|
{
|
|
case FS::NONE:
|
|
break;
|
|
case FS::GPARTED:
|
|
break;
|
|
#ifdef HAVE_LIBPARTED_FS_RESIZE
|
|
case FS::LIBPARTED:
|
|
success = resize_move_filesystem_using_libparted( partition_old,
|
|
partition_new,
|
|
operationdetail.get_last_child() );
|
|
break;
|
|
#endif
|
|
case FS::EXTERNAL:
|
|
success = ( p_filesystem = get_filesystem_object( partition_new.filesystem ) ) &&
|
|
p_filesystem->resize( partition_new,
|
|
operationdetail.get_last_child(),
|
|
fill_partition );
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
bool GParted_Core::copy( const Partition & partition_src,
|
|
Partition & partition_dst,
|
|
OperationDetail & operationdetail )
|
|
{
|
|
const Partition & filesystem_ptn_src = partition_src.get_filesystem_partition();
|
|
Partition & filesystem_ptn_dst = partition_dst.get_filesystem_partition();
|
|
|
|
if ( filesystem_ptn_dst.get_byte_length() < filesystem_ptn_src.get_byte_length()
|
|
&& filesystem_ptn_src.filesystem != FS_XFS // Permit copying to smaller xfs partition
|
|
)
|
|
{
|
|
operationdetail .add_child( OperationDetail(
|
|
_("the destination is smaller than the source partition"), STATUS_ERROR, FONT_ITALIC ) ) ;
|
|
|
|
return false ;
|
|
}
|
|
|
|
if ( ! check_repair_filesystem( filesystem_ptn_src, operationdetail ) )
|
|
return false;
|
|
|
|
if ( partition_dst.status == STAT_COPY )
|
|
{
|
|
// Handle situation where src sector size is smaller than dst sector size
|
|
// and an additional partial dst sector is required.
|
|
Sector min_size = ( partition_src.get_byte_length() + partition_dst.sector_size - 1 ) /
|
|
partition_dst.sector_size;
|
|
if ( ! create_partition( partition_dst, operationdetail, min_size ) )
|
|
return false;
|
|
}
|
|
|
|
// NOTE:
|
|
// Deliberately call set_partition_type() on the Partition object directly
|
|
// containing the file system, rather than the Partition object containing the
|
|
// partition. When copying into an existing open LUKS encryption mapping this
|
|
// will avoid changing partition type, where as when copying into a plain
|
|
// partition the type will be set.
|
|
bool success = set_partition_type( filesystem_ptn_dst, operationdetail )
|
|
&& copy_filesystem( filesystem_ptn_src, filesystem_ptn_dst, operationdetail )
|
|
&& update_bootsector( partition_dst, operationdetail );
|
|
if ( ! success )
|
|
return false;
|
|
|
|
if ( filesystem_ptn_dst.filesystem == FS_LINUX_SWAP )
|
|
{
|
|
// linux-swap is recreated, not copied
|
|
return recreate_linux_swap_filesystem( filesystem_ptn_dst, operationdetail );
|
|
}
|
|
else if ( filesystem_ptn_dst.get_byte_length() > filesystem_ptn_src.get_byte_length() )
|
|
{
|
|
// Copied into a bigger partition so maximise file system
|
|
return check_repair_filesystem( filesystem_ptn_dst, operationdetail )
|
|
&& maximize_filesystem( filesystem_ptn_dst, operationdetail );
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool GParted_Core::copy_filesystem( const Partition & partition_src,
|
|
Partition & partition_dst,
|
|
OperationDetail & operationdetail )
|
|
{
|
|
if ( partition_src.filesystem == FS_LUKS && partition_src.busy )
|
|
{
|
|
operationdetail.add_child( OperationDetail(
|
|
GPARTED_BUG + ": " + _("source partition contains open LUKS encryption for a file system copy only step"),
|
|
STATUS_ERROR, FONT_ITALIC ) );
|
|
return false;
|
|
}
|
|
if ( partition_dst.filesystem == FS_LUKS && partition_dst.busy )
|
|
{
|
|
operationdetail.add_child( OperationDetail(
|
|
GPARTED_BUG + ": " + _("destination partition contains open LUKS encryption for a file system copy only step"),
|
|
STATUS_ERROR, FONT_ITALIC ) );
|
|
return false;
|
|
}
|
|
|
|
operationdetail.add_child( OperationDetail(
|
|
Glib::ustring::compose( _("copy file system from %1 to %2"),
|
|
partition_src.get_path(),
|
|
partition_dst.get_path() ) ) );
|
|
|
|
bool success = false;
|
|
FileSystem* p_filesystem = NULL;
|
|
switch ( get_fs( partition_dst.filesystem ).copy )
|
|
{
|
|
case FS::GPARTED:
|
|
success = copy_filesystem_internal( partition_src,
|
|
partition_dst,
|
|
operationdetail.get_last_child(),
|
|
true );
|
|
break;
|
|
|
|
case FS::LIBPARTED:
|
|
// FIXME: see if copying through libparted has any advantages
|
|
break;
|
|
|
|
case FS::EXTERNAL:
|
|
success = ( p_filesystem = get_filesystem_object( partition_dst.filesystem ) ) &&
|
|
p_filesystem->copy( partition_src,
|
|
partition_dst,
|
|
operationdetail.get_last_child() );
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
operationdetail.get_last_child().set_success_and_capture_errors( success );
|
|
return success;
|
|
}
|
|
|
|
bool GParted_Core::copy_filesystem_internal( const Partition & partition_src,
|
|
const Partition & partition_dst,
|
|
OperationDetail & operationdetail,
|
|
bool cancel_safe )
|
|
{
|
|
Sector dummy ;
|
|
return copy_blocks( partition_src.device_path,
|
|
partition_dst.device_path,
|
|
partition_src.sector_start,
|
|
partition_dst.sector_start,
|
|
partition_src.sector_size,
|
|
partition_dst.sector_size,
|
|
partition_src.get_byte_length(),
|
|
operationdetail,
|
|
dummy,
|
|
cancel_safe );
|
|
}
|
|
|
|
bool GParted_Core::copy_filesystem_internal( const Partition & partition_src,
|
|
const Partition & partition_dst,
|
|
OperationDetail & operationdetail,
|
|
Byte_Value & total_done,
|
|
bool cancel_safe )
|
|
{
|
|
return copy_blocks( partition_src.device_path,
|
|
partition_dst.device_path,
|
|
partition_src.sector_start,
|
|
partition_dst.sector_start,
|
|
partition_src.sector_size,
|
|
partition_dst.sector_size,
|
|
partition_src.get_byte_length(),
|
|
operationdetail,
|
|
total_done,
|
|
cancel_safe );
|
|
}
|
|
|
|
bool GParted_Core::copy_blocks( const Glib::ustring & src_device,
|
|
const Glib::ustring & dst_device,
|
|
Sector src_start,
|
|
Sector dst_start,
|
|
Byte_Value src_sector_size,
|
|
Byte_Value dst_sector_size,
|
|
Byte_Value src_length,
|
|
OperationDetail & operationdetail,
|
|
Byte_Value & total_done,
|
|
bool cancel_safe )
|
|
{
|
|
operationdetail .add_child( OperationDetail( _("using internal algorithm"), STATUS_NONE ) ) ;
|
|
operationdetail .add_child( OperationDetail(
|
|
Glib::ustring::compose( /*TO TRANSLATORS: looks like copy 1.00 MiB */
|
|
_("copy %1"), Utils::format_size( src_length, 1 ) ),
|
|
STATUS_NONE ) ) ;
|
|
|
|
operationdetail .add_child( OperationDetail( _("finding optimal block size"), STATUS_NONE ) ) ;
|
|
|
|
Byte_Value benchmark_blocksize = (1 * MEBIBYTE) ;
|
|
Byte_Value benchmark_copysize = 16 * MEBIBYTE;
|
|
Byte_Value optimal_blocksize = benchmark_blocksize ;
|
|
Sector offset_read = src_start ;
|
|
Sector offset_write = dst_start ;
|
|
|
|
//Handle situation where we need to perform the copy beginning
|
|
// with the end of the partition and finishing with the start.
|
|
if ( dst_start > src_start )
|
|
{
|
|
offset_read += (src_length/src_sector_size) - (benchmark_copysize/src_sector_size);
|
|
/* Handle situation where src sector size is smaller than dst sector size and an additional partial dst sector is required. */
|
|
offset_write += ((src_length + (dst_sector_size - 1))/dst_sector_size) - (benchmark_copysize/dst_sector_size);
|
|
}
|
|
|
|
total_done = 0 ;
|
|
Byte_Value done = 0 ;
|
|
Glib::Timer timer ;
|
|
double smallest_time = 1000000 ;
|
|
bool succes = true ;
|
|
OperationDetail & benchmark_od = operationdetail.get_last_child();
|
|
|
|
//Benchmark copy times using different block sizes to determine optimal size
|
|
while (succes &&
|
|
llabs(done) + benchmark_copysize <= src_length &&
|
|
benchmark_blocksize <= benchmark_copysize )
|
|
{
|
|
benchmark_od.add_child( OperationDetail(
|
|
/*TO TRANSLATORS: looks like copy 16.00 MiB using a block size of 1.00 MiB */
|
|
Glib::ustring::compose(_("copy %1 using a block size of %2"),
|
|
Utils::format_size(benchmark_copysize, 1),
|
|
Utils::format_size(benchmark_blocksize, 1))));
|
|
|
|
timer .reset() ;
|
|
succes = CopyBlocks( src_device,
|
|
dst_device,
|
|
offset_read + (done / src_sector_size),
|
|
offset_write + (done / dst_sector_size),
|
|
benchmark_copysize,
|
|
benchmark_blocksize,
|
|
benchmark_od,
|
|
total_done,
|
|
src_length,
|
|
cancel_safe ).copy();
|
|
timer.stop() ;
|
|
|
|
benchmark_od.get_last_child().add_child( OperationDetail(
|
|
Glib::ustring::compose( _("%1 seconds"), timer .elapsed() ), STATUS_NONE, FONT_ITALIC ) ) ;
|
|
benchmark_od.get_last_child().set_success_and_capture_errors( succes );
|
|
|
|
if ( timer .elapsed() <= smallest_time )
|
|
{
|
|
smallest_time = timer .elapsed() ;
|
|
optimal_blocksize = benchmark_blocksize ;
|
|
}
|
|
benchmark_blocksize *= 2 ;
|
|
|
|
if ( ( dst_start > src_start ) )
|
|
done -= benchmark_copysize;
|
|
else
|
|
done += benchmark_copysize;
|
|
}
|
|
|
|
if ( succes )
|
|
operationdetail .get_last_child() .add_child( OperationDetail( Glib::ustring::compose(
|
|
/*TO TRANSLATORS: looks like optimal block size is 1.00 MiB */
|
|
_("optimal block size is %1"),
|
|
Utils::format_size( optimal_blocksize, 1 ) ),
|
|
STATUS_NONE ) ) ;
|
|
|
|
if ( succes && llabs( done ) < src_length )
|
|
{
|
|
Byte_Value remaining_length = src_length - llabs( done );
|
|
operationdetail.add_child( OperationDetail(
|
|
/*TO TRANSLATORS: looks like copy 16.00 MiB using a block size of 1.00 MiB */
|
|
Glib::ustring::compose( _("copy %1 using a block size of %2"),
|
|
Utils::format_size( remaining_length, 1 ),
|
|
Utils::format_size( optimal_blocksize, 1 ) ) ) );
|
|
succes = CopyBlocks( src_device,
|
|
dst_device,
|
|
src_start + ((done > 0 ? done : 0) / src_sector_size),
|
|
dst_start + ((done > 0 ? done : 0) / dst_sector_size),
|
|
remaining_length,
|
|
optimal_blocksize,
|
|
operationdetail,
|
|
total_done,
|
|
src_length,
|
|
cancel_safe ).copy();
|
|
operationdetail.get_last_child().set_success_and_capture_errors( succes );
|
|
}
|
|
|
|
operationdetail .add_child( OperationDetail(
|
|
Glib::ustring::compose( /*TO TRANSLATORS: looks like 1.00 MiB (1048576 B) copied */
|
|
_("%1 (%2 B) copied"), Utils::format_size( total_done, 1 ), total_done ),
|
|
STATUS_NONE ) ) ;
|
|
return succes ;
|
|
}
|
|
|
|
void GParted_Core::rollback_move_filesystem( const Partition & partition_src,
|
|
const Partition & partition_dst,
|
|
OperationDetail & operationdetail,
|
|
Byte_Value total_done )
|
|
{
|
|
if ( total_done > 0 )
|
|
{
|
|
//find out exactly which part of the file system was copied (and to where it was copied)..
|
|
Partition * temp_src = partition_src.clone();
|
|
Partition * temp_dst = partition_dst.clone();
|
|
bool rollback_needed = true;
|
|
|
|
if ( partition_dst .sector_start > partition_src .sector_start )
|
|
{
|
|
Sector distance = partition_dst.sector_start - partition_src.sector_start;
|
|
temp_src->sector_start = temp_src->sector_end - ( (total_done / temp_src->sector_size) - 1 ) + distance;
|
|
temp_dst->sector_start = temp_dst->sector_end - ( (total_done / temp_dst->sector_size) - 1 ) + distance;
|
|
if ( temp_src->sector_start > temp_src->sector_end )
|
|
// Nothing has been overwritten yet, so nothing to roll back
|
|
rollback_needed = false;
|
|
}
|
|
else
|
|
{
|
|
Sector distance = partition_src.sector_start - partition_dst.sector_start;
|
|
temp_src->sector_end = temp_src->sector_start + ( (total_done / temp_src->sector_size) - 1 ) - distance;
|
|
temp_dst->sector_end = temp_dst->sector_start + ( (total_done / temp_dst->sector_size) - 1 ) - distance;
|
|
if ( temp_src->sector_start > temp_src->sector_end )
|
|
// Nothing has been overwritten yet, so nothing to roll back
|
|
rollback_needed = false;
|
|
}
|
|
|
|
if ( rollback_needed )
|
|
{
|
|
operationdetail.add_child( OperationDetail( _("rollback failed file system move") ) );
|
|
|
|
//and copy it back (NOTE the reversed dst and src)
|
|
bool success = copy_filesystem_internal( *temp_dst,
|
|
*temp_src,
|
|
operationdetail.get_last_child(),
|
|
false );
|
|
|
|
operationdetail.get_last_child().set_success_and_capture_errors( success );
|
|
}
|
|
|
|
delete temp_src;
|
|
delete temp_dst;
|
|
temp_src = NULL;
|
|
temp_dst = NULL;
|
|
}
|
|
}
|
|
|
|
bool GParted_Core::check_repair_filesystem( const Partition & partition, OperationDetail & operationdetail )
|
|
{
|
|
if ( partition.filesystem == FS_LUKS && partition.busy )
|
|
{
|
|
operationdetail.add_child( OperationDetail(
|
|
GPARTED_BUG + ": " + _("partition contains open LUKS encryption for a check file system only step"),
|
|
STATUS_ERROR, FONT_ITALIC ) );
|
|
return false;
|
|
}
|
|
|
|
if ( partition.busy )
|
|
// Trying to check an online file system is a successful non-operation.
|
|
return true;
|
|
|
|
operationdetail .add_child( OperationDetail(
|
|
Glib::ustring::compose(
|
|
/* TO TRANSLATORS: looks like check file system on /dev/sda5 for errors and (if possible) fix them */
|
|
_("check file system on %1 for errors and (if possible) fix them"),
|
|
partition .get_path() ) ) ) ;
|
|
|
|
bool succes = false ;
|
|
FileSystem* p_filesystem = NULL ;
|
|
switch ( get_fs( partition .filesystem ) .check )
|
|
{
|
|
case FS::NONE:
|
|
operationdetail .get_last_child() .add_child(
|
|
OperationDetail( _("checking is not available for this file system"),
|
|
STATUS_NONE,
|
|
FONT_ITALIC ) ) ;
|
|
operationdetail.get_last_child().set_status( STATUS_WARNING );
|
|
return true ;
|
|
|
|
break ;
|
|
case FS::GPARTED:
|
|
break ;
|
|
case FS::LIBPARTED:
|
|
break ;
|
|
case FS::EXTERNAL:
|
|
succes = ( p_filesystem = get_filesystem_object( partition .filesystem ) ) &&
|
|
p_filesystem ->check_repair( partition, operationdetail .get_last_child() ) ;
|
|
|
|
break ;
|
|
|
|
default:
|
|
break ;
|
|
}
|
|
|
|
operationdetail.get_last_child().set_success_and_capture_errors( succes );
|
|
return succes ;
|
|
}
|
|
|
|
bool GParted_Core::check_repair_maximize( const Partition & partition,
|
|
OperationDetail & operationdetail )
|
|
{
|
|
if ( partition.filesystem == FS_LUKS )
|
|
{
|
|
// Pretend that the LUKS partition is closed so that
|
|
// resize_filesystem_implement() checks the offline .grow capability
|
|
// rather than .online_grow so that maximising LUKS in the context of a
|
|
// check encrypted file system operation, which doesn't want to change the
|
|
// partition boundaries, can be performed even if libparted and/or the
|
|
// kernel aren't new enough to support online partition resizing.
|
|
// luks::resize() determines the open status of the LUKS mapping by
|
|
// querying the LUKS_Info module cache so doesn't care if the partition
|
|
// busy status is wrong.
|
|
Partition * temp_offline = partition.clone();
|
|
temp_offline->busy = false;
|
|
bool success = maximize_encryption( *temp_offline, operationdetail );
|
|
delete temp_offline;
|
|
temp_offline = NULL;
|
|
if ( ! success )
|
|
return false;
|
|
|
|
if ( partition.busy )
|
|
success = maximize_filesystem( partition.get_filesystem_partition(), operationdetail );
|
|
return success;
|
|
}
|
|
else
|
|
{
|
|
return maximize_filesystem( partition, operationdetail );
|
|
}
|
|
}
|
|
|
|
bool GParted_Core::set_partition_type( const Partition & partition, OperationDetail & operationdetail )
|
|
{
|
|
if ( partition.type == TYPE_UNPARTITIONED )
|
|
// Trying to set the type of a partition on a non-partitioned whole disk
|
|
// device is a successful non-operation.
|
|
return true;
|
|
|
|
operationdetail .add_child( OperationDetail(
|
|
Glib::ustring::compose( _("set partition type on %1"), partition .get_path() ) ) ) ;
|
|
//Set partition type appropriately for the type of file system stored in the partition.
|
|
// Libparted treats every type as a file system, except LVM which it treats as a flag.
|
|
|
|
bool return_value = false ;
|
|
|
|
PedDevice* lp_device = NULL ;
|
|
PedDisk* lp_disk = NULL ;
|
|
if ( get_device_and_disk( partition .device_path, lp_device, lp_disk ) )
|
|
{
|
|
PedPartition* lp_partition = ped_disk_get_partition_by_sector( lp_disk, partition.get_sector() );
|
|
if ( lp_partition )
|
|
{
|
|
Glib::ustring fs_type = Utils::get_filesystem_string( partition.filesystem );
|
|
|
|
// Lookup libparted file system type using GParted's name, as most match
|
|
PedFileSystemType * lp_fs_type = ped_file_system_type_get( fs_type.c_str() );
|
|
|
|
// If not found, and FS is linux-swap, then try linux-swap(v1)
|
|
if ( ! lp_fs_type && fs_type == "linux-swap" )
|
|
lp_fs_type = ped_file_system_type_get( "linux-swap(v1)" );
|
|
|
|
// If not found, and FS is linux-swap, then try linux-swap(new)
|
|
if ( ! lp_fs_type && fs_type == "linux-swap" )
|
|
lp_fs_type = ped_file_system_type_get( "linux-swap(new)" );
|
|
|
|
// If not found, and FS is udf, then try ntfs.
|
|
// Actually MBR 07 IFS (Microsoft Installable File System) or
|
|
// GPT BDP (Windows Basic Data Partition).
|
|
// Ref: https://serverfault.com/a/829172
|
|
if ( ! lp_fs_type && fs_type == "udf" )
|
|
lp_fs_type = ped_file_system_type_get( "ntfs" );
|
|
|
|
// default is Linux (83)
|
|
if ( ! lp_fs_type )
|
|
lp_fs_type = ped_file_system_type_get( "ext2" );
|
|
|
|
bool supports_lvm_flag = ped_partition_is_flag_available( lp_partition, PED_PARTITION_LVM );
|
|
|
|
if ( lp_fs_type && partition.filesystem != FS_LVM2_PV )
|
|
{
|
|
// Also clear any libparted LVM flag so that it doesn't
|
|
// override the file system type
|
|
if ( ( ! supports_lvm_flag ||
|
|
ped_partition_set_flag( lp_partition, PED_PARTITION_LVM, 0 ) ) &&
|
|
ped_partition_set_system( lp_partition, lp_fs_type ) &&
|
|
commit( lp_disk ) )
|
|
{
|
|
operationdetail.get_last_child().add_child(
|
|
/* TO TRANSLATORS: looks like new partition type: ext4 */
|
|
OperationDetail( Glib::ustring::compose( _("new partition type: %1"),
|
|
lp_partition->fs_type->name ),
|
|
STATUS_NONE,
|
|
FONT_ITALIC ) );
|
|
return_value = true;
|
|
}
|
|
}
|
|
else if ( partition.filesystem == FS_LVM2_PV )
|
|
{
|
|
if ( supports_lvm_flag &&
|
|
ped_partition_set_flag( lp_partition, PED_PARTITION_LVM, 1 ) &&
|
|
commit( lp_disk ) )
|
|
{
|
|
operationdetail.get_last_child().add_child(
|
|
/* TO TRANSLATORS: looks like new partition flag: lvm */
|
|
OperationDetail( Glib::ustring::compose( _("new partition flag: %1"),
|
|
ped_partition_flag_get_name( PED_PARTITION_LVM ) ),
|
|
STATUS_NONE,
|
|
FONT_ITALIC ) );
|
|
return_value = true;
|
|
}
|
|
else if ( ! supports_lvm_flag )
|
|
{
|
|
// Skip setting the lvm flag because the partition
|
|
// table type doesn't support it. Applies to dvh
|
|
// and pc98 disk labels.
|
|
return_value = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
destroy_device_and_disk( lp_device, lp_disk ) ;
|
|
}
|
|
|
|
operationdetail.get_last_child().set_success_and_capture_errors( return_value );
|
|
return return_value ;
|
|
}
|
|
|
|
bool GParted_Core::calibrate_partition( Partition & partition, OperationDetail & operationdetail )
|
|
{
|
|
if ( partition.type == TYPE_PRIMARY || partition.type == TYPE_LOGICAL ||
|
|
partition.type == TYPE_EXTENDED || partition.type == TYPE_UNPARTITIONED )
|
|
{
|
|
Glib::ustring curr_path = partition.get_path();
|
|
operationdetail.add_child( OperationDetail( Glib::ustring::compose( _("calibrate %1"), curr_path ) ) );
|
|
|
|
bool success = false;
|
|
PedDevice* lp_device = NULL ;
|
|
PedDisk* lp_disk = NULL ;
|
|
if ( get_device( partition.device_path, lp_device ) )
|
|
{
|
|
if ( partition.type == TYPE_UNPARTITIONED )
|
|
{
|
|
// Virtual partition spanning whole disk device
|
|
success = true;
|
|
}
|
|
else if ( get_disk( lp_device, lp_disk ) )
|
|
{
|
|
// Partitioned device
|
|
PedPartition *lp_partition = get_lp_partition( lp_disk, partition );
|
|
if ( lp_partition ) // FIXME: add check to see if lp_partition->type matches partition.type..
|
|
{
|
|
if ( ! file_test( curr_path, Glib::FILE_TEST_EXISTS ) )
|
|
{
|
|
// Re-set the real partition path from libparted.
|
|
//
|
|
// When creating a copy operation by pasting into
|
|
// unallocated space the path for the partition
|
|
// object was set to "Copy of /dev/SRC" because
|
|
// the partition didn't yet exist before the
|
|
// operations were applied. Additional operations
|
|
// on that new partition also got the path set to
|
|
// "Copy of /dev/SRC". This re-sets the real path
|
|
// to "/dev/NEW". This provides the real path for
|
|
// file system specific tools used during those
|
|
// additional operations such mkfs for the format
|
|
// operation or fsck and others for the
|
|
// resize/move operation.
|
|
partition.set_path( get_partition_path( lp_partition ) );
|
|
}
|
|
|
|
// Reload the partition boundaries from libparted
|
|
// to ensure that GParted knows what the actual
|
|
// partition boundaries are before applying the
|
|
// next operation. Necessary when the previous
|
|
// operation in the sequence was a resize/move
|
|
// operation where GParted may have only estimated
|
|
// where libparted would move the boundaries to.
|
|
partition.sector_start = lp_partition->geom.start;
|
|
partition.sector_end = lp_partition->geom.end;
|
|
|
|
success = true;
|
|
}
|
|
}
|
|
// else error from get_disk() reading partition table
|
|
|
|
if ( success )
|
|
{
|
|
operationdetail .get_last_child() .add_child(
|
|
OperationDetail(
|
|
/* TO TRANSLATORS: looks like path: /dev/sda (device)
|
|
* or looks like path: /dev/sda1 (partition)
|
|
* This is showing the name and whether it
|
|
* is a whole disk device or a partition
|
|
* within a device.
|
|
*/
|
|
Glib::ustring::compose( _("path: %1 (%2)"),
|
|
partition.get_path(),
|
|
( partition.type == TYPE_UNPARTITIONED )
|
|
? _("device")
|
|
: _("partition") ) + "\n" +
|
|
Glib::ustring::compose( _("start: %1"), partition .sector_start ) + "\n" +
|
|
Glib::ustring::compose( _("end: %1"), partition .sector_end ) + "\n" +
|
|
Glib::ustring::compose( _("size: %1 (%2)"),
|
|
partition .get_sector_length(),
|
|
Utils::format_size( partition .get_sector_length(), partition .sector_size ) ),
|
|
STATUS_NONE,
|
|
FONT_ITALIC ) ) ;
|
|
|
|
if ( partition.filesystem == FS_LUKS && partition.busy )
|
|
{
|
|
const Partition & encrypted = dynamic_cast<const PartitionLUKS *>( &partition )->get_encrypted();
|
|
operationdetail.get_last_child().add_child( OperationDetail(
|
|
Glib::ustring::compose( _("encryption path: %1"), encrypted.get_path() ),
|
|
STATUS_NONE, FONT_ITALIC ) );
|
|
}
|
|
}
|
|
|
|
destroy_device_and_disk( lp_device, lp_disk ) ;
|
|
}
|
|
|
|
// (#762941) Above libparted partition querying triggers udev >= 219 to
|
|
// remove and re-add all the partition specific /dev/ entries. Wait for
|
|
// this to complete to avoid FS specific commands failing because they
|
|
// happen to run just when the needed /dev/PTN entry doesn't exist.
|
|
settle_device( SETTLE_DEVICE_APPLY_MAX_WAIT_SECONDS );
|
|
|
|
operationdetail.get_last_child().set_success_and_capture_errors( success );
|
|
return success;
|
|
}
|
|
else //nothing to calibrate...
|
|
return true ;
|
|
}
|
|
|
|
bool GParted_Core::calculate_exact_geom( const Partition & partition_old,
|
|
Partition & partition_new,
|
|
OperationDetail & operationdetail )
|
|
{
|
|
operationdetail .add_child( OperationDetail(
|
|
Glib::ustring::compose( _("calculate new size and position of %1"), partition_new .get_path() ) ) ) ;
|
|
|
|
operationdetail .get_last_child() .add_child(
|
|
OperationDetail(
|
|
Glib::ustring::compose( _("requested start: %1"), partition_new .sector_start ) + "\n" +
|
|
Glib::ustring::compose( _("requested end: %1"), partition_new .sector_end ) + "\n" +
|
|
Glib::ustring::compose( _("requested size: %1 (%2)"),
|
|
partition_new .get_sector_length(),
|
|
Utils::format_size( partition_new .get_sector_length(), partition_new .sector_size ) ),
|
|
STATUS_NONE,
|
|
FONT_ITALIC ) ) ;
|
|
|
|
bool succes = false ;
|
|
PedDevice* lp_device = NULL ;
|
|
PedDisk* lp_disk = NULL ;
|
|
if ( get_device_and_disk( partition_old .device_path, lp_device, lp_disk ) )
|
|
{
|
|
PedPartition* lp_partition = get_lp_partition( lp_disk, partition_old );
|
|
if ( lp_partition )
|
|
{
|
|
PedConstraint *constraint = NULL ;
|
|
constraint = ped_constraint_any( lp_device ) ;
|
|
|
|
if ( constraint )
|
|
{
|
|
//FIXME: if we insert a weird partitionnew geom here (e.g. start > end)
|
|
//ped_disk_set_partition_geom() will still return true (althoug an lp exception is written
|
|
//to stdout.. see if this also affect create_partition and resize_move_partition
|
|
//sended a patch to fix this to libparted list. will probably be in 1.7.2
|
|
if ( ped_disk_set_partition_geom( lp_disk,
|
|
lp_partition,
|
|
constraint,
|
|
partition_new .sector_start,
|
|
partition_new .sector_end ) )
|
|
{
|
|
partition_new .sector_start = lp_partition ->geom .start ;
|
|
partition_new .sector_end = lp_partition ->geom .end ;
|
|
succes = true ;
|
|
}
|
|
|
|
ped_constraint_destroy( constraint );
|
|
}
|
|
}
|
|
|
|
destroy_device_and_disk( lp_device, lp_disk ) ;
|
|
}
|
|
|
|
if ( succes )
|
|
{
|
|
operationdetail .get_last_child() .add_child(
|
|
OperationDetail(
|
|
Glib::ustring::compose( _("new start: %1"), partition_new .sector_start ) + "\n" +
|
|
Glib::ustring::compose( _("new end: %1"), partition_new .sector_end ) + "\n" +
|
|
Glib::ustring::compose( _("new size: %1 (%2)"),
|
|
partition_new .get_sector_length(),
|
|
Utils::format_size( partition_new .get_sector_length(), partition_new .sector_size ) ),
|
|
STATUS_NONE,
|
|
FONT_ITALIC ) ) ;
|
|
|
|
//Update dev mapper entry if partition is dmraid.
|
|
succes = succes && update_dmraid_entry( partition_new, operationdetail );
|
|
}
|
|
|
|
operationdetail.get_last_child().set_success_and_capture_errors( succes );
|
|
return succes ;
|
|
}
|
|
|
|
bool GParted_Core::update_dmraid_entry( const Partition & partition, OperationDetail & operationdetail )
|
|
{
|
|
bool success = true;
|
|
#ifndef USE_LIBPARTED_DMRAID
|
|
DMRaid dmraid;
|
|
if ( dmraid.is_dmraid_device( partition.device_path ) )
|
|
{
|
|
PedDevice *lp_device = NULL;
|
|
PedDisk *lp_disk;
|
|
// Open disk handle before and close after to prevent application crash.
|
|
if ( get_device_and_disk( partition.device_path, lp_device, lp_disk ) )
|
|
{
|
|
success = dmraid.update_dev_map_entry( partition, operationdetail.get_last_child() );
|
|
destroy_device_and_disk( lp_device, lp_disk );
|
|
}
|
|
}
|
|
#endif
|
|
return success;
|
|
}
|
|
|
|
FileSystem * GParted_Core::get_filesystem_object( FSType fstype )
|
|
{
|
|
std::map<FSType, FileSystem *>::const_iterator fs_iter = FILESYSTEM_MAP.find( fstype );
|
|
if ( fs_iter == FILESYSTEM_MAP.end() )
|
|
return NULL;
|
|
else
|
|
return fs_iter->second;
|
|
}
|
|
|
|
// Return true for file systems with an implementation class, false otherwise
|
|
bool GParted_Core::supported_filesystem( FSType fstype )
|
|
{
|
|
return get_filesystem_object( fstype ) != NULL;
|
|
}
|
|
|
|
FS_Limits GParted_Core::get_filesystem_limits( FSType fstype, const Partition & partition )
|
|
{
|
|
FileSystem *p_filesystem = get_filesystem_object( fstype );
|
|
FS_Limits fs_limits;
|
|
if ( p_filesystem != NULL )
|
|
fs_limits = p_filesystem->get_filesystem_limits( partition );
|
|
return fs_limits;
|
|
}
|
|
|
|
bool GParted_Core::filesystem_resize_disallowed( const Partition & partition )
|
|
{
|
|
if ( partition .filesystem == FS_LVM2_PV )
|
|
{
|
|
//The LVM2 PV can't be resized when it's a member of an export VG
|
|
Glib::ustring vgname = LVM2_PV_Info::get_vg_name( partition.get_path() );
|
|
if ( vgname .empty() )
|
|
return false ;
|
|
return LVM2_PV_Info::is_vg_exported( vgname );
|
|
}
|
|
return false ;
|
|
}
|
|
|
|
bool GParted_Core::erase_filesystem_signatures( const Partition & partition, OperationDetail & operationdetail )
|
|
{
|
|
if ( partition.filesystem == FS_LUKS && partition.busy )
|
|
{
|
|
operationdetail.add_child( OperationDetail(
|
|
GPARTED_BUG + ": " + _("partition contains open LUKS encryption for an erase file system signatures only step"),
|
|
STATUS_ERROR, FONT_ITALIC ) );
|
|
return false;
|
|
}
|
|
|
|
bool overall_success = false ;
|
|
operationdetail .add_child( OperationDetail(
|
|
Glib::ustring::compose( _("clear old file system signatures in %1"),
|
|
partition .get_path() ) ) ) ;
|
|
|
|
//Get device, disk & partition and open the device. Allocate buffer and fill with
|
|
// zeros. Buffer size is the greater of 4 KiB and the sector size.
|
|
PedDevice* lp_device = NULL ;
|
|
PedDisk* lp_disk = NULL ;
|
|
PedPartition* lp_partition = NULL ;
|
|
bool device_is_open = false ;
|
|
Byte_Value bufsize = 4LL * KIBIBYTE ;
|
|
char * buf = NULL ;
|
|
if ( get_device( partition.device_path, lp_device ) )
|
|
{
|
|
if ( partition.type == TYPE_UNPARTITIONED )
|
|
{
|
|
// Virtual partition spanning whole disk device
|
|
overall_success = true;
|
|
}
|
|
else if ( get_disk( lp_device, lp_disk ) )
|
|
{
|
|
// Partitioned device
|
|
lp_partition = ped_disk_get_partition_by_sector( lp_disk, partition.get_sector() );
|
|
overall_success = ( lp_partition != NULL );
|
|
}
|
|
|
|
if ( overall_success && ped_device_open( lp_device ) )
|
|
{
|
|
device_is_open = true ;
|
|
|
|
bufsize = std::max( bufsize, lp_device ->sector_size ) ;
|
|
buf = static_cast<char *>( malloc( bufsize ) ) ;
|
|
if ( buf )
|
|
memset( buf, 0, bufsize ) ;
|
|
}
|
|
overall_success &= device_is_open;
|
|
}
|
|
|
|
//Erase all file system super blocks, including their signatures. The specified
|
|
// byte ranges are converted to whole sectors (as disks fundamentally only read
|
|
// or write whole sectors) and written using ped_geometry_write(). Therefore
|
|
// don't try to surgically overwrite just the few bytes of each signature as this
|
|
// code overwrites whole sectors and it embeds more knowledge that is necessary.
|
|
//
|
|
// First byte range from offset 0 of length 512 KiB covers the primary super
|
|
// block of all currently supported file systems and is also likely to include
|
|
// future file system super blocks too. Only a few file systems have super
|
|
// blocks and signatures located elsewhere.
|
|
//
|
|
// Btrfs super blocks are located at: 64 KiB, 64 MiB, 256 GiB and 1 PiB.
|
|
// (The super block at 64 KiB will be erased by the zeroing from offset 0. Other
|
|
// super block mirror copies need to be explicitly cleared).
|
|
// https://btrfs.wiki.kernel.org/index.php/On-disk_Format#Superblock
|
|
//
|
|
// ZFS labels are 256 KiB in size and 4 copies are stored on every member device,
|
|
// 2 at the beginning and 2 at the end of the device, aligned to 256 KiB. (The
|
|
// front two labels, L0 and L1, will be erased by the zeroing from offset 0. The
|
|
// back two labels, L2 and L3, need to be explicitly cleared).
|
|
// Reference:
|
|
// ZFS On-Disk Specification
|
|
// http://maczfs.googlecode.com/files/ZFSOnDiskFormat.pdf
|
|
//
|
|
// Linux Software RAID metadata 0.90 stores it's super block at 64 KiB before the
|
|
// end of the device, aligned to 64 KiB boundary. Length 4 KiB.
|
|
// Ref: mdadm/super0.c load_super0()
|
|
// mdadm/md_p.h #define MD_NEW_SIZE_SECTORS(x) ...
|
|
//
|
|
// Linux Software RAID metadata 1.0 stores it's super block at 8 KiB before the
|
|
// end of the device, aligned to 4 KiB boundary. Length 4 KiB. (Metadata 1.1
|
|
// and 1.2 store their super blocks at 0 KiB and 4 KiB respectively so will be
|
|
// erased by the zeroing from offset 0).
|
|
// Ref: mdadm/super1.c load_super1()
|
|
// #define MAX_SB_SIZE 4096
|
|
//
|
|
// Nilfs2 secondary super block is located at the last whole 4 KiB block.
|
|
// Ref: nilfs-utils-2.1.4/include/nilfs2_fs.h
|
|
// #define NILFS_SB2_OFFSET_BYTES(devsize) ((((devsize) >> 12) - 1) << 12)
|
|
//
|
|
// NOTE:
|
|
// Most of the time partitions are aligned to whole MiBs so the writing of zeros
|
|
// at offsets -64 KiB and -8 KiB will be overwriting zeros already just written
|
|
// at offset -512 KiB, length 512 KiB. This will double write 12 KiB of zeros.
|
|
// However partitions don't have to be MiB aligned and real disk drives generally
|
|
// aren't an exact multiple of 1024^2 bytes in size either. So zeroing at offsets
|
|
// -64 KiB and -8 KiB with their smaller rounding / alignment requirements will
|
|
// write outside the -512 KiB zeroed region and needs to be kept. Because of the
|
|
// small amount of double writing it is not worth the effort to suppress it when
|
|
// not needed.
|
|
struct {
|
|
Byte_Value offset; //Negative offsets work backwards from the end of the partition
|
|
Byte_Value rounding; //Minimum desired rounding for offset
|
|
Byte_Value length;
|
|
} ranges[] = {
|
|
//offset , rounding , length
|
|
{ 0LL , 1LL , 512LL * KIBIBYTE }, // All primary super blocks
|
|
{ 64LL * MEBIBYTE, 1LL , 4LL * KIBIBYTE }, // Btrfs super block mirror copy
|
|
{ 256LL * GIBIBYTE, 1LL , 4LL * KIBIBYTE }, // Btrfs super block mirror copy
|
|
{ 1LL * PEBIBYTE, 1LL , 4LL * KIBIBYTE }, // Btrfs super block mirror copy
|
|
{ -512LL * KIBIBYTE, 256LL * KIBIBYTE, 512LL * KIBIBYTE }, // ZFS labels L2 and L3
|
|
{ -64LL * KIBIBYTE, 64LL * KIBIBYTE, 4LL * KIBIBYTE }, // SWRaid metadata 0.90 super block
|
|
{ -8LL * KIBIBYTE, 4LL * KIBIBYTE, 8LL * KIBIBYTE } // @-8K SWRaid metadata 1.0 super block
|
|
// and @-4K Nilfs2 secondary super block
|
|
} ;
|
|
for ( unsigned int i = 0 ; overall_success && i < sizeof( ranges ) / sizeof( ranges[0] ) ; i ++ )
|
|
{
|
|
//Rounding is performed in multiples of the sector size because writes are in whole sectors.
|
|
Byte_Value rounding_size = Utils::ceil_size( ranges[i].rounding, lp_device ->sector_size ) ;
|
|
Byte_Value byte_offset ;
|
|
Byte_Value byte_len ;
|
|
|
|
// Compute range to be erased. Starting position takes into account
|
|
// negative offsets from the end of the partition and minimum desired
|
|
// rounding requirements. Length is only rounded to device sector size.
|
|
// Ranges may become larger, but not smaller than requested.
|
|
if ( ranges[i] .offset >= 0LL )
|
|
{
|
|
byte_offset = Utils::floor_size( ranges[i] .offset, rounding_size ) ;
|
|
byte_len = Utils::ceil_size( ranges[i].offset + ranges[i].length, lp_device->sector_size )
|
|
- byte_offset ;
|
|
}
|
|
else //Negative offsets
|
|
{
|
|
Byte_Value notional_offset = Utils::floor_size( partition .get_byte_length() + ranges[i] .offset, ranges[i]. rounding ) ;
|
|
byte_offset = Utils::floor_size( notional_offset, rounding_size ) ;
|
|
byte_len = Utils::ceil_size( notional_offset + ranges[i].length, lp_device->sector_size )
|
|
- byte_offset ;
|
|
}
|
|
|
|
//Limit range to partition size.
|
|
if ( byte_offset + byte_len <= 0LL )
|
|
{
|
|
//Byte range entirely before partition start. Programmer error!
|
|
continue;
|
|
}
|
|
else if ( byte_offset < 0 )
|
|
{
|
|
//Byte range spans partition start. Trim to fit.
|
|
byte_len += byte_offset ;
|
|
byte_offset = 0LL ;
|
|
}
|
|
if ( byte_offset >= partition .get_byte_length() )
|
|
{
|
|
//Byte range entirely after partition end. Ignore.
|
|
continue ;
|
|
}
|
|
else if ( byte_offset + byte_len > partition .get_byte_length() )
|
|
{
|
|
//Byte range spans partition end. Trim to fit.
|
|
byte_len = partition .get_byte_length() - byte_offset ;
|
|
}
|
|
|
|
OperationDetail & od = operationdetail .get_last_child() ;
|
|
Byte_Value written = 0LL ;
|
|
bool zero_success = false ;
|
|
if ( device_is_open && buf )
|
|
{
|
|
od .add_child( OperationDetail(
|
|
/*TO TRANSLATORS: looks like write 68.00 KiB of zeros at byte offset 0 */
|
|
Glib::ustring::compose( "write %1 of zeros at byte offset %2",
|
|
Utils::format_size( byte_len, 1 ),
|
|
byte_offset ) ) ) ;
|
|
|
|
// Start sector of the whole disk device or the partition
|
|
Sector ptn_start = 0LL;
|
|
if ( lp_partition )
|
|
ptn_start = lp_partition->geom.start;
|
|
|
|
while ( written < byte_len )
|
|
{
|
|
//Write in bufsize amounts. Last write may be smaller but
|
|
// will still be a whole number of sectors.
|
|
Byte_Value amount = std::min( bufsize, byte_len - written ) ;
|
|
zero_success = ped_device_write( lp_device, buf,
|
|
ptn_start + ( byte_offset + written ) / lp_device->sector_size,
|
|
amount / lp_device->sector_size );
|
|
if ( ! zero_success )
|
|
break ;
|
|
written += amount ;
|
|
}
|
|
|
|
od.get_last_child().set_success_and_capture_errors( zero_success );
|
|
}
|
|
overall_success &= zero_success ;
|
|
}
|
|
if ( buf )
|
|
free( buf ) ;
|
|
|
|
//Linux kernel doesn't maintain buffer cache coherency between the whole disk
|
|
// device and partition devices. So even though the file system signatures
|
|
// have been overwritten on the disk via a partition device, libparted reading
|
|
// via the whole disk device will read the old data and report the file system
|
|
// as still existing.
|
|
//
|
|
// Calling ped_device_sync() to flush the cache is required when the partition is
|
|
// just being cleared. However the sync can be skipped when the wipe is being
|
|
// performed as part of writing a new file system as the partition type is also
|
|
// changed, which modified the partition table so GParted calls
|
|
// ped_disk_commit_to_os(). This instructs the kernel to remove all non-busy
|
|
// partitions on the disk and re-adds them, thus effectively flushing the cache
|
|
// of the disk.
|
|
//
|
|
// Opening the device and calling ped_device_sync() took 0.15 seconds or less on
|
|
// a 5400 RPM laptop hard drive. For now just always call ped_device_sync() as
|
|
// it doesn't add much time to the overall operation.
|
|
if ( overall_success )
|
|
{
|
|
OperationDetail & od = operationdetail .get_last_child() ;
|
|
od .add_child( OperationDetail( Glib::ustring::compose( _("flush operating system cache of %1"),
|
|
lp_device ->path ) ) ) ;
|
|
|
|
bool flush_success = false ;
|
|
if ( device_is_open )
|
|
{
|
|
flush_success = ped_device_sync( lp_device ) ;
|
|
ped_device_close( lp_device ) ;
|
|
}
|
|
od.get_last_child().set_success_and_capture_errors( flush_success );
|
|
overall_success &= flush_success ;
|
|
}
|
|
destroy_device_and_disk( lp_device, lp_disk ) ;
|
|
|
|
operationdetail.get_last_child().set_success_and_capture_errors( overall_success );
|
|
return overall_success ;
|
|
}
|
|
|
|
bool GParted_Core::update_bootsector( const Partition & partition, OperationDetail & operationdetail )
|
|
{
|
|
//only for ntfs atm...
|
|
//FIXME: this should probably be done in the fs classes...
|
|
if ( partition .filesystem == FS_NTFS )
|
|
{
|
|
//The NTFS file system stores a value in the boot record called the
|
|
// Number of Hidden Sectors. This value must match the partition start
|
|
// sector number in order for Windows to boot from the file system.
|
|
// For more details, refer to the NTFS Volume Boot Record at:
|
|
// http://www.geocities.com/thestarman3/asm/mbr/NTFSBR.htm
|
|
|
|
operationdetail .add_child( OperationDetail(
|
|
/*TO TRANSLATORS: update boot sector of ntfs file system on /dev/sdd1 */
|
|
Glib::ustring::compose( _("update boot sector of %1 file system on %2"),
|
|
Utils::get_filesystem_string( partition .filesystem ),
|
|
partition .get_path() ) ) ) ;
|
|
|
|
//convert start sector to hex string
|
|
std::stringstream ss ;
|
|
ss << std::hex << partition .sector_start ;
|
|
Glib::ustring hex = ss .str() ;
|
|
|
|
//fill with zeros and reverse...
|
|
hex .insert( 0, 8 - hex .length(), '0' ) ;
|
|
Glib::ustring reversed_hex ;
|
|
for ( int t = 6 ; t >= 0 ; t -=2 )
|
|
reversed_hex .append( hex .substr( t, 2 ) ) ;
|
|
|
|
//convert reversed hex codes into ascii characters
|
|
char buf[4] ;
|
|
for ( unsigned int k = 0; (k < 4 && k < (reversed_hex .length() / 2)); k++ )
|
|
{
|
|
Glib::ustring tmp_hex = "0x" + reversed_hex .substr( k * 2, 2 ) ;
|
|
buf[k] = (char)( std::strtol( tmp_hex .c_str(), NULL, 16 ) ) ;
|
|
}
|
|
|
|
//write new Number of Hidden Sectors value into NTFS boot sector at offset 0x1C
|
|
Glib::ustring error_message = "" ;
|
|
std::ofstream dev_file ;
|
|
dev_file .open( partition .get_path() .c_str(), std::ios::out | std::ios::binary ) ;
|
|
if ( dev_file .is_open() )
|
|
{
|
|
dev_file .seekp( 0x1C ) ;
|
|
if ( dev_file .good() )
|
|
{
|
|
dev_file .write( buf, 4 ) ;
|
|
if ( dev_file .bad() )
|
|
{
|
|
/*TO TRANSLATORS: looks like Error trying to write to boot sector in /dev/sdd1 */
|
|
error_message = Glib::ustring::compose( _("Error trying to write to boot sector in %1"), partition .get_path() ) ;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*TO TRANSLATORS: looks like Error trying to seek to position 0x1C in /dev/sdd1 */
|
|
error_message = Glib::ustring::compose( _("Error trying to seek to position 0x1c in %1"), partition .get_path() ) ;
|
|
}
|
|
dev_file .close( ) ;
|
|
}
|
|
else
|
|
{
|
|
/*TO TRANSLATORS: looks like Error trying to open /dev/sdd1 */
|
|
error_message = Glib::ustring::compose( _("Error trying to open %1"), partition .get_path() ) ;
|
|
}
|
|
|
|
//append error messages if any found
|
|
bool succes = true ;
|
|
if ( ! error_message .empty() )
|
|
{
|
|
succes = false ;
|
|
error_message += "\n" ;
|
|
/*TO TRANSLATORS: looks like Failed to set the number of hidden sectors to 05ab4f00 in the ntfs boot record. */
|
|
error_message += Glib::ustring::compose( _("Failed to set the number of hidden sectors to %1 in the NTFS boot record."), reversed_hex ) ;
|
|
error_message += "\n" ;
|
|
error_message += Glib::ustring::compose( _("You might try the following command to correct the problem:"), reversed_hex ) ;
|
|
error_message += "\n" ;
|
|
error_message += Glib::ustring::compose( "echo %1 | xxd -r -p | dd conv=notrunc of=%2 bs=1 seek=28", reversed_hex, partition .get_path() ) ;
|
|
operationdetail .get_last_child() .add_child( OperationDetail( error_message, STATUS_NONE, FONT_ITALIC ) ) ;
|
|
}
|
|
|
|
operationdetail.get_last_child().set_success_and_capture_errors( succes );
|
|
return succes ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
void GParted_Core::init_filesystems()
|
|
{
|
|
// File system support falls into 3 categories determined by their entry in
|
|
// FILESYSTEM_MAP:
|
|
// 1) Fully supported file systems have an entry pointing to the instance of
|
|
// their derived FileSystem object, which determines and implements their
|
|
// supported actions.
|
|
// supported_filesystem() -> true
|
|
// 2) Basic supported file systems have a NULL pointer entry, with
|
|
// find_supported_filesystems() creating a basic set of supported actions.
|
|
// supported_filesystem() -> false
|
|
// 3) Unsupported file systems have no entry, and no supported actions.
|
|
// supported_filesystem() -> false
|
|
FILESYSTEM_MAP[FS_UNKNOWN] = NULL;
|
|
FILESYSTEM_MAP[FS_OTHER] = NULL;
|
|
FILESYSTEM_MAP[FS_BTRFS] = new btrfs();
|
|
FILESYSTEM_MAP[FS_EXFAT] = new exfat();
|
|
FILESYSTEM_MAP[FS_EXT2] = new ext2( FS_EXT2 );
|
|
FILESYSTEM_MAP[FS_EXT3] = new ext2( FS_EXT3 );
|
|
FILESYSTEM_MAP[FS_EXT4] = new ext2( FS_EXT4 );
|
|
FILESYSTEM_MAP[FS_F2FS] = new f2fs();
|
|
FILESYSTEM_MAP[FS_FAT16] = new fat16( FS_FAT16 );
|
|
FILESYSTEM_MAP[FS_FAT32] = new fat16( FS_FAT32 );
|
|
FILESYSTEM_MAP[FS_HFS] = new hfs();
|
|
FILESYSTEM_MAP[FS_HFSPLUS] = new hfsplus();
|
|
FILESYSTEM_MAP[FS_JFS] = new jfs();
|
|
FILESYSTEM_MAP[FS_LINUX_SWAP] = new linux_swap();
|
|
FILESYSTEM_MAP[FS_LVM2_PV] = new lvm2_pv();
|
|
FILESYSTEM_MAP[FS_LUKS] = new luks();
|
|
FILESYSTEM_MAP[FS_MINIX] = new minix();
|
|
FILESYSTEM_MAP[FS_NILFS2] = new nilfs2();
|
|
FILESYSTEM_MAP[FS_NTFS] = new ntfs();
|
|
FILESYSTEM_MAP[FS_REISER4] = new reiser4();
|
|
FILESYSTEM_MAP[FS_REISERFS] = new reiserfs();
|
|
FILESYSTEM_MAP[FS_UDF] = new udf();
|
|
FILESYSTEM_MAP[FS_XFS] = new xfs();
|
|
FILESYSTEM_MAP[FS_APFS] = NULL;
|
|
FILESYSTEM_MAP[FS_BITLOCKER] = NULL;
|
|
FILESYSTEM_MAP[FS_GRUB2_CORE_IMG] = NULL;
|
|
FILESYSTEM_MAP[FS_ISO9660] = NULL;
|
|
FILESYSTEM_MAP[FS_LINUX_SWRAID] = NULL;
|
|
FILESYSTEM_MAP[FS_LINUX_SWSUSPEND] = NULL;
|
|
FILESYSTEM_MAP[FS_REFS] = NULL;
|
|
FILESYSTEM_MAP[FS_UFS] = NULL;
|
|
FILESYSTEM_MAP[FS_ZFS] = NULL;
|
|
}
|
|
|
|
void GParted_Core::fini_filesystems()
|
|
{
|
|
std::map<FSType, FileSystem *>::iterator fs_iter;
|
|
for ( fs_iter = FILESYSTEM_MAP.begin() ; fs_iter != FILESYSTEM_MAP.end() ; fs_iter ++ )
|
|
{
|
|
delete fs_iter->second;
|
|
fs_iter->second = NULL;
|
|
}
|
|
}
|
|
|
|
void GParted_Core::capture_libparted_messages( OperationDetail & operationdetail, bool success )
|
|
{
|
|
if ( libparted_messages.size() > 0 )
|
|
{
|
|
operationdetail.add_child( OperationDetail( _("libparted messages"),
|
|
success ? STATUS_INFO : STATUS_ERROR ) );
|
|
for ( unsigned int i = 0 ; i < libparted_messages.size() ; i++ )
|
|
operationdetail.get_last_child().add_child(
|
|
OperationDetail( libparted_messages[i], STATUS_NONE, FONT_ITALIC ) );
|
|
libparted_messages.clear();
|
|
}
|
|
}
|
|
|
|
bool GParted_Core::useable_device( PedDevice * lp_device )
|
|
{
|
|
g_assert( lp_device != NULL ); // Bug: Not initialised by call to ped_device_get() or ped_device_get_next()
|
|
|
|
char * buf = static_cast<char *>( malloc( lp_device->sector_size ) );
|
|
if ( ! buf )
|
|
return false;
|
|
|
|
// Must be able to read from the first sector before the disk device is considered
|
|
// useable in GParted.
|
|
bool success = false;
|
|
if ( ped_device_open( lp_device ) &&
|
|
ped_device_read( lp_device, buf, 0, 1 ) )
|
|
{
|
|
success = true;
|
|
|
|
ped_device_close( lp_device );
|
|
}
|
|
|
|
free( buf );
|
|
|
|
return success;
|
|
}
|
|
|
|
//Flush the Linux kernel caches, and therefore ensure coherency between the caches of the
|
|
// whole disk device and the partition devices.
|
|
//
|
|
// Libparted >= 2.0 works around this by calling ioctl(fd, BLKFLSBUF) to flush the cache
|
|
// when opening the whole disk device, but only for kernels before 2.6.0.
|
|
// Ref: parted v3.1-52-g1c659d5 ./libparted/arch/linux.c linux_open()
|
|
// 1657 /* With kernels < 2.6 flush cache for cache coherence issues */
|
|
// 1658 if (!_have_kern26())
|
|
// 1659 _flush_cache (dev);
|
|
//
|
|
// Libparted >= v3.1-61-gfb99ba5 works around this for all kernel versions.
|
|
// Ref: parted v3.1-61-gfb99ba5 ./libparted/arch/linux.c linux_open()
|
|
// 1640 _flush_cache (dev);
|
|
bool GParted_Core::flush_device( PedDevice * lp_device )
|
|
{
|
|
bool success = false ;
|
|
if ( ped_device_open( lp_device ) )
|
|
{
|
|
success = ped_device_sync( lp_device ) ;
|
|
ped_device_close( lp_device ) ;
|
|
}
|
|
return success ;
|
|
}
|
|
|
|
bool GParted_Core::get_device( const Glib::ustring & device_path, PedDevice *& lp_device, bool flush )
|
|
{
|
|
lp_device = ped_device_get( device_path.c_str() );
|
|
if ( lp_device )
|
|
{
|
|
if ( flush )
|
|
// Force cache coherency before going on to read the partition
|
|
// table so that libparted reading the whole disk device and the
|
|
// file system tools reading the partition devices read the same
|
|
// data.
|
|
flush_device( lp_device );
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool GParted_Core::get_disk( PedDevice *& lp_device, PedDisk *& lp_disk, bool strict )
|
|
{
|
|
if ( lp_device )
|
|
{
|
|
lp_disk = ped_disk_new( lp_device );
|
|
|
|
// if ! disk and writable it's probably a HD without disklabel.
|
|
// We return true here and deal with them in
|
|
// GParted_Core::set_device_from_disk().
|
|
if ( lp_disk || ( ! strict && ! lp_device ->read_only ) )
|
|
return true;
|
|
|
|
destroy_device_and_disk( lp_device, lp_disk );
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool GParted_Core::get_device_and_disk( const Glib::ustring & device_path,
|
|
PedDevice*& lp_device, PedDisk*& lp_disk, bool strict, bool flush )
|
|
{
|
|
if ( get_device( device_path, lp_device, flush ) )
|
|
{
|
|
return get_disk( lp_device, lp_disk, strict );
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void GParted_Core::destroy_device_and_disk( PedDevice*& lp_device, PedDisk*& lp_disk )
|
|
{
|
|
if ( lp_disk )
|
|
ped_disk_destroy( lp_disk ) ;
|
|
lp_disk = NULL ;
|
|
|
|
if ( lp_device )
|
|
ped_device_destroy( lp_device ) ;
|
|
lp_device = NULL ;
|
|
}
|
|
|
|
bool GParted_Core::commit( PedDisk* lp_disk )
|
|
{
|
|
// (#790418) Hold a file handle open across the ped_disk_commit_to_dev() and
|
|
// commit_to_os()->ped_disk_commit_to_os() calls to avoid libparted having to open
|
|
// and close the device twice itself. This avoids the kernel and udev events
|
|
// removing and re-adding partitions exactly when ped_disk_commit_to_os() is
|
|
// trying to doing the same thing.
|
|
bool opened = ped_device_open( lp_disk->dev );
|
|
|
|
bool succes = ped_disk_commit_to_dev( lp_disk ) ;
|
|
|
|
succes = commit_to_os( lp_disk, SETTLE_DEVICE_APPLY_MAX_WAIT_SECONDS ) && succes;
|
|
|
|
if ( opened )
|
|
{
|
|
ped_device_close( lp_disk->dev );
|
|
// Wait for udev rules to complete and partition device nodes to settle
|
|
// from this ped_device_close().
|
|
settle_device( SETTLE_DEVICE_APPLY_MAX_WAIT_SECONDS );
|
|
}
|
|
|
|
return succes ;
|
|
}
|
|
|
|
bool GParted_Core::commit_to_os( PedDisk* lp_disk, std::time_t timeout )
|
|
{
|
|
bool succes ;
|
|
#ifndef USE_LIBPARTED_DMRAID
|
|
DMRaid dmraid ;
|
|
if ( dmraid .is_dmraid_device( lp_disk ->dev ->path ) )
|
|
succes = true ;
|
|
else
|
|
{
|
|
#endif
|
|
succes = ped_disk_commit_to_os( lp_disk ) ;
|
|
#ifndef USE_LIBPARTED_DMRAID
|
|
}
|
|
#endif
|
|
|
|
// Wait for udev rules to complete and partition device nodes to settle from above
|
|
// ped_disk_commit_to_os() initiated kernel update of the partitions.
|
|
settle_device( timeout ) ;
|
|
|
|
return succes ;
|
|
}
|
|
|
|
void GParted_Core::settle_device( std::time_t timeout )
|
|
{
|
|
if ( udevsettle_found )
|
|
Utils::execute_command( "udevsettle --timeout=" + Utils::num_to_str( timeout ) ) ;
|
|
else if ( udevadm_found )
|
|
Utils::execute_command( "udevadm settle --timeout=" + Utils::num_to_str( timeout ) ) ;
|
|
else
|
|
sleep( timeout ) ;
|
|
}
|
|
|
|
PedPartition* GParted_Core::get_lp_partition( const PedDisk* lp_disk, const Partition & partition )
|
|
{
|
|
if ( partition.type == TYPE_EXTENDED )
|
|
return ped_disk_extended_partition( lp_disk );
|
|
return ped_disk_get_partition_by_sector( lp_disk, partition.get_sector() );
|
|
}
|
|
|
|
class PedExceptionMsg : public Gtk::MessageDialog
|
|
{
|
|
public:
|
|
PedExceptionMsg( PedException &e ) : MessageDialog( Glib::ustring(e.message), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_NONE, true )
|
|
{
|
|
switch( e.type )
|
|
{
|
|
case PED_EXCEPTION_INFORMATION:
|
|
set_title( _("Libparted Information") );
|
|
property_message_type() = Gtk::MESSAGE_INFO;
|
|
break;
|
|
case PED_EXCEPTION_WARNING:
|
|
set_title( _("Libparted Warning") );
|
|
property_message_type() = Gtk::MESSAGE_WARNING;
|
|
break;
|
|
case PED_EXCEPTION_ERROR:
|
|
set_title( _("Libparted Error") );
|
|
break;
|
|
case PED_EXCEPTION_FATAL:
|
|
set_title( _("Libparted Fatal") );
|
|
break;
|
|
case PED_EXCEPTION_BUG:
|
|
set_title( _("Libparted Bug") );
|
|
break;
|
|
case PED_EXCEPTION_NO_FEATURE:
|
|
set_title( _("Libparted Unsupported Feature") );
|
|
break;
|
|
default:
|
|
set_title( _("Libparted unknown exception") );
|
|
break;
|
|
}
|
|
if (e.options & PED_EXCEPTION_FIX)
|
|
add_button( _("Fix"), PED_EXCEPTION_FIX );
|
|
if (e.options & PED_EXCEPTION_YES)
|
|
add_button( _("Yes"), PED_EXCEPTION_YES );
|
|
if (e.options & PED_EXCEPTION_OK)
|
|
add_button( _("Ok"), PED_EXCEPTION_OK );
|
|
if (e.options & PED_EXCEPTION_RETRY)
|
|
add_button( _("Retry"), PED_EXCEPTION_RETRY );
|
|
if (e.options & PED_EXCEPTION_NO)
|
|
add_button( _("No"), PED_EXCEPTION_NO );
|
|
if (e.options & PED_EXCEPTION_CANCEL)
|
|
add_button( _("Cancel"), PED_EXCEPTION_CANCEL );
|
|
if (e.options & PED_EXCEPTION_IGNORE)
|
|
add_button( _("Ignore"), PED_EXCEPTION_IGNORE );
|
|
}
|
|
};
|
|
|
|
struct ped_exception_ctx {
|
|
PedExceptionOption ret;
|
|
PedException *e;
|
|
Glib::Mutex mutex;
|
|
Glib::Cond cond;
|
|
};
|
|
|
|
static bool _ped_exception_handler( struct ped_exception_ctx *ctx )
|
|
{
|
|
std::cerr << ctx->e->message << std::endl;
|
|
|
|
libparted_messages.push_back( ctx->e->message );
|
|
char optcount = 0;
|
|
int opt = 0;
|
|
for( char c = 0; c < 10; c++ )
|
|
if( ctx->e->options & (1 << c) ) {
|
|
opt = (1 << c);
|
|
optcount++;
|
|
}
|
|
// if only one option was given, choose it without popup
|
|
if( optcount == 1 && ctx->e->type != PED_EXCEPTION_BUG && ctx->e->type != PED_EXCEPTION_FATAL )
|
|
{
|
|
ctx->ret = (PedExceptionOption)opt;
|
|
ctx->mutex.lock();
|
|
ctx->cond.signal();
|
|
ctx->mutex.unlock();
|
|
return false;
|
|
}
|
|
PedExceptionMsg msg( *ctx->e );
|
|
msg.show_all();
|
|
ctx->ret = (PedExceptionOption)msg.run();
|
|
if (ctx->ret < 0)
|
|
ctx->ret = PED_EXCEPTION_UNHANDLED;
|
|
ctx->mutex.lock();
|
|
ctx->cond.signal();
|
|
ctx->mutex.unlock();
|
|
return false;
|
|
}
|
|
|
|
PedExceptionOption GParted_Core::ped_exception_handler( PedException * e )
|
|
{
|
|
struct ped_exception_ctx ctx;
|
|
ctx.ret = PED_EXCEPTION_UNHANDLED;
|
|
ctx.e = e;
|
|
if (Glib::Thread::self() != GParted_Core::mainthread) {
|
|
ctx.mutex.lock();
|
|
g_idle_add( (GSourceFunc)_ped_exception_handler, &ctx );
|
|
ctx.cond.wait( ctx.mutex );
|
|
ctx.mutex.unlock();
|
|
} else _ped_exception_handler( &ctx );
|
|
return ctx.ret;
|
|
}
|
|
|
|
GParted_Core::~GParted_Core()
|
|
{
|
|
// Delete file system map entries
|
|
fini_filesystems();
|
|
}
|
|
|
|
Glib::Thread *GParted_Core::mainthread;
|
|
|
|
std::map< FSType, FileSystem * > GParted_Core::FILESYSTEM_MAP;
|
|
|
|
} //GParted
|