GParted_Core.cc 141 KB
Newer Older
Curtis Gedak's avatar
Curtis Gedak committed
1
/* Copyright (C) 2004 Bart 'plors' Hakvoort
2
 * Copyright (C) 2008, 2009, 2010, 2011, 2012 Curtis Gedak
3 4 5 6 7 8 9 10 11
 *
 *  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
12
 *  GNU General Public License for more details.
13 14
 *
 *  You should have received a copy of the GNU General Public License
15
 *  along with this program; if not, see <http://www.gnu.org/licenses/>.
16 17 18
 */
 
#include "../include/Win_GParted.h"
19
#include "../include/GParted_Core.h"
20
#include "../include/BlockSpecial.h"
21
#include "../include/DMRaid.h"
Curtis Gedak's avatar
Curtis Gedak committed
22
#include "../include/FS_Info.h"
23
#include "../include/LVM2_PV_Info.h"
24
#include "../include/LUKS_Info.h"
25
#include "../include/Mount_Info.h"
26
#include "../include/Operation.h"
27
#include "../include/OperationCopy.h"
28
#include "../include/Partition.h"
29
#include "../include/PartitionLUKS.h"
30
#include "../include/PartitionVector.h"
31
#include "../include/Proc_Partitions_Info.h"
32
#include "../include/SWRaid_Info.h"
33

34
#include "../include/btrfs.h"
35
#include "../include/exfat.h"
Bart Hakvoort's avatar
Bart Hakvoort committed
36
#include "../include/ext2.h"
37
#include "../include/f2fs.h"
Bart Hakvoort's avatar
Bart Hakvoort committed
38 39
#include "../include/fat16.h"
#include "../include/linux_swap.h"
40
#include "../include/lvm2_pv.h"
41
#include "../include/luks.h"
Bart Hakvoort's avatar
Bart Hakvoort committed
42
#include "../include/reiserfs.h"
Mike Fleetwood's avatar
Mike Fleetwood committed
43
#include "../include/nilfs2.h"
Bart Hakvoort's avatar
Bart Hakvoort committed
44 45 46 47 48 49 50
#include "../include/ntfs.h"
#include "../include/xfs.h"
#include "../include/jfs.h"
#include "../include/hfs.h"
#include "../include/hfsplus.h"
#include "../include/reiser4.h"
#include "../include/ufs.h"
51
#include "../include/Copy_Blocks.h"
52

53
#include <cerrno>
54 55
#include <cstring>
#include <sys/types.h>
56
#include <stdlib.h>
57
#include <unistd.h>
58
#include <gtkmm/messagedialog.h>
Phillip Susi's avatar
Phillip Susi committed
59
#include <gtkmm/main.h>
60

61
std::vector<Glib::ustring> libparted_messages ; //see ped_exception_handler()
62

63 64
namespace GParted
{
65

66 67 68
const std::time_t SETTLE_DEVICE_PROBE_MAX_WAIT_SECONDS = 1;
const std::time_t SETTLE_DEVICE_APPLY_MAX_WAIT_SECONDS = 10;

69 70
static bool udevadm_found = false;
static bool udevsettle_found = false;
71 72
static bool hdparm_found = false;

73
GParted_Core::GParted_Core() 
74
{
75
	thread_status_message = "" ;
76

77
	ped_exception_set_handler( ped_exception_handler ) ; 
78

79
	//get valid flags ...
80 81 82
	for ( PedPartitionFlag flag = ped_partition_flag_next( static_cast<PedPartitionFlag>( NULL ) ) ;
	      flag ;
	      flag = ped_partition_flag_next( flag ) )
83
		flags .push_back( flag ) ;
84
	
85 86
	//throw libpartedversion to the stdout to see which version is actually used.
	std::cout << "======================" << std::endl ;
87
	std::cout << "libparted : " << ped_get_version() << std::endl ;
88
	std::cout << "======================" << std::endl ;
89

90 91
	find_supported_core();

92
	//initialize file system list
93 94 95
	init_filesystems() ;

	//Determine file system support capabilities for the first time
96
	find_supported_filesystems() ;
97 98
}

99 100
void GParted_Core::find_supported_core()
{
101 102
	udevadm_found = ! Glib::find_program_in_path( "udevadm" ).empty();
	udevsettle_found = ! Glib::find_program_in_path( "udevsettle" ).empty();
103 104 105
	hdparm_found = ! Glib::find_program_in_path( "hdparm" ).empty();
}

106 107 108
void GParted_Core::find_supported_filesystems()
{
	std::map< FILESYSTEM, FileSystem * >::iterator f ;
Mike Fleetwood's avatar
Mike Fleetwood committed
109

110 111 112 113
	// Iteration of std::map is ordered according to operator< of the key.  Hence the
	// FILESYSTEMS vector is constructed in FILESYSTEM enum order: FS_UNALLOCATED,
	// FS_UNKNOWN, FS_CLEARED, FS_BTRFS, ..., FS_LINUX_SWRAID, LINUX_SWSUSPEND which
	// ultimately controls the default order of file systems in menus and dialogs.
114
	FILESYSTEMS .clear() ;
115

116 117 118 119
	for ( f = FILESYSTEM_MAP .begin() ; f != FILESYSTEM_MAP .end() ; f++ ) {
		if ( f ->second )
			FILESYSTEMS .push_back( f ->second ->get_filesystem_support() ) ;
		else {
120
			FS fs_notsupp( f->first );
121 122 123
			FILESYSTEMS .push_back( fs_notsupp ) ;
		}
	}
124
}
125

126 127 128 129 130 131
void GParted_Core::set_user_devices( const std::vector<Glib::ustring> & user_devices ) 
{
	this ->device_paths = user_devices ;
	this ->probe_devices = ! user_devices .size() ;
}
	
132
void GParted_Core::set_devices( std::vector<Device> & devices )
133
{
Phillip Susi's avatar
Phillip Susi committed
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
	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;
150
	devices .clear() ;
151
	Device temp_device ;
152 153 154
	BlockSpecial::clear_cache();            // MUST BE FIRST.  Cache of name to major, minor
	                                        // numbers incrementally loaded when BlockSpecial
	                                        // objects are created in the following caches.
155
	Proc_Partitions_Info::load_cache();     // SHOULD BE SECOND.  Caches /proc/partitions and
156
	                                        // pre-populates BlockSpecial cache.
157
	FS_Info::load_cache();
158
	DMRaid dmraid( true ) ;    //Refresh cache of dmraid device information
159
	LVM2_PV_Info::clear_cache();            // Cache automatically loaded if and when needed
160
	btrfs::clear_cache();                   // Cache incrementally loaded if and when needed
161
	SWRaid_Info::load_cache();
162
	LUKS_Info::clear_cache();               // Cache automatically loaded if and when needed
163
	Mount_Info::load_cache();
164

165 166
	//only probe if no devices were specified as arguments..
	if ( probe_devices )
167
	{
168
		device_paths .clear() ;
169

170 171
		//FIXME:  When libparted bug 194 is fixed, remove code to read:
		//           /proc/partitions
172 173 174
		//        This was a problem with no floppy drive yet BIOS indicated one existed.
		//        http://parted.alioth.debian.org/cgi-bin/trac.cgi/ticket/194
		//
175
		//try to find all available devices if devices exist in /proc/partitions
176
		std::vector<Glib::ustring> temp_devices = Proc_Partitions_Info::get_device_paths();
177
		if ( ! temp_devices .empty() )
178
		{
179 180
			//Try to find all devices in /proc/partitions
			for (unsigned int k=0; k < temp_devices .size(); k++)
181
			{
182 183 184
				/*TO TRANSLATORS: looks like Scanning /dev/sda */
				set_thread_status_message( String::ucompose ( _("Scanning %1"), temp_devices[ k ] ) ) ;
				ped_device_get( temp_devices[ k ] .c_str() ) ;
185
			}
186 187 188 189 190 191 192

			//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( String::ucompose ( _("Scanning %1"), dmraid_devices[k] ) ) ;
193
#ifndef USE_LIBPARTED_DMRAID
194
					dmraid .create_dev_map_entries( dmraid_devices[k] ) ;
195
					settle_device( SETTLE_DEVICE_PROBE_MAX_WAIT_SECONDS );
196
#endif
197 198 199
					ped_device_get( dmraid_devices[k] .c_str() ) ;
				}
			}
200 201 202
		}
		else
		{
203
			//No devices found in /proc/partitions so use libparted to probe devices
204 205
			ped_device_probe_all();
		}
206

207
		PedDevice* lp_device = ped_device_get_next( NULL ) ;
208 209
		while ( lp_device ) 
		{
210 211 212
			/* TO TRANSLATORS: looks like   Confirming /dev/sda */
			set_thread_status_message( String::ucompose( _("Confirming %1"), lp_device->path ) );

213
			//only add this device if we can read the first sector (which means it's a real device)
214 215 216
			if ( useable_device( lp_device ) )
				device_paths.push_back( lp_device->path );

217 218
			lp_device = ped_device_get_next( lp_device ) ;
		}
219

220 221
		std::sort( device_paths .begin(), device_paths .end() ) ;
	}
222 223 224 225
	else
	{
		//Device paths were passed in on the command line.

226 227 228 229 230 231 232 233
		// 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() );

234 235
		for ( unsigned int t = 0 ; t < device_paths .size() ; t++ ) 
		{
236 237 238 239
			set_thread_status_message( String::ucompose( _("Confirming %1"), device_paths[t] ) );

#ifndef USE_LIBPARTED_DMRAID
			// Ensure that dmraid device entries are created
240 241 242 243
			if ( dmraid .is_dmraid_supported() &&
			     dmraid .is_dmraid_device( device_paths[t] ) )
			{
				dmraid .create_dev_map_entries( dmraid .get_dmraid_name( device_paths [t] ) ) ;
244
				settle_device( SETTLE_DEVICE_PROBE_MAX_WAIT_SECONDS );
245
			}
246 247 248
#endif

			PedDevice* lp_device = ped_device_get( device_paths[t].c_str() );
249
			if ( lp_device == NULL || ! useable_device( lp_device ) )
250
			{
251
				// Remove this disk device which isn't useable
252
				device_paths.erase( device_paths.begin() + t-- );
253
			}
254 255
		}
	}
256

257
	for ( unsigned int t = 0 ; t < device_paths .size() ; t++ ) 
Curtis Gedak's avatar
Curtis Gedak committed
258
	{
259 260
		/*TO TRANSLATORS: looks like Searching /dev/sda partitions */ 
		set_thread_status_message( String::ucompose ( _("Searching %1 partitions"), device_paths[ t ] ) ) ;
261 262
		PedDevice* lp_device = NULL ;
		PedDisk* lp_disk = NULL ;
263
		if ( get_device_and_disk( device_paths[ t ], lp_device, lp_disk, false, true ) )
264
		{
265
			temp_device .Reset() ;
266

267
			//device info..
268
			temp_device.set_path( device_paths[t] );
269

270
			temp_device .model 	=	lp_device ->model ;
271
			temp_device .length 	=	lp_device ->length ;
272
			temp_device .sector_size	=	lp_device ->sector_size ;
273 274 275
			temp_device .heads 	=	lp_device ->bios_geom .heads ;
			temp_device .sectors 	=	lp_device ->bios_geom .sectors ;
			temp_device .cylinders	=	lp_device ->bios_geom .cylinders ;
276
			temp_device .cylsize 	=	temp_device .heads * temp_device .sectors ; 
277
			set_device_serial_number( temp_device );
278
		
279
			//make sure cylsize is at least 1 MiB
280 281
			if ( temp_device .cylsize < (MEBIBYTE / temp_device .sector_size) )
				temp_device .cylsize = (MEBIBYTE / temp_device .sector_size) ;
282 283

			std::vector<Glib::ustring> messages;
284
			FILESYSTEM fstype = detect_filesystem( lp_device, NULL, messages );
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
			// 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();

				temp_device.disktype = "none";
				temp_device.max_prims = 1;
				set_device_one_partition( temp_device, lp_device, fstype, messages );
			}
301
			// Partitioned drive (excluding "loop"), as recognised by libparted
302 303
			else if ( lp_disk && lp_disk->type && lp_disk->type->name &&
			          strcmp( lp_disk->type->name, "loop" ) != 0         )
304
			{
305 306
				temp_device .disktype =	lp_disk ->type ->name ;
				temp_device .max_prims = ped_disk_get_max_primary_partition_count( lp_disk ) ;
307

308
				// Determine if partition naming is supported.
309 310 311 312 313
				if ( ped_disk_type_check_feature( lp_disk->type, PED_DISK_TYPE_PARTITION_NAME ) )
				{
					temp_device.enable_partition_naming(
							Utils::get_max_partition_name_length( temp_device.disktype ) );
				}
314

315
				set_device_partitions( temp_device, lp_device, lp_disk ) ;
316

317
				if ( temp_device .highest_busy )
318
				{
319
					temp_device.readonly = ! commit_to_os( lp_disk, SETTLE_DEVICE_PROBE_MAX_WAIT_SECONDS );
320 321 322 323
					//Clear libparted messages.  Typically these are:
					//  The kernel was unable to re-read the partition table...
					libparted_messages .clear() ;
				}
324
			}
325 326 327 328 329 330 331 332 333 334 335
			// 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         )
			{
				temp_device.disktype = lp_disk->type->name;
				temp_device.max_prims = 1;

				// Create virtual partition covering the whole disk device
				// with unknown contents.
336 337 338 339 340 341 342 343 344 345 346 347
				Partition * partition_temp = new Partition();
				partition_temp->Set( temp_device.get_path(),
				                     lp_device->path,
				                     1,
				                     TYPE_PRIMARY,
				                     true,
				                     FS_UNKNOWN,
				                     0LL,
				                     temp_device.length - 1LL,
				                     temp_device.sector_size,
				                     false,
				                     false );
348
				// Place unknown file system message in this partition.
349
				partition_temp->append_messages( messages );
350
				temp_device.partitions.push_back_adopt( partition_temp );
351 352
			}
			// Unrecognised, unpartitioned drive.
353 354
			else
			{
355 356 357 358 359 360 361 362
				temp_device.disktype =
					/* TO TRANSLATORS:  unrecognized
					 * means that the partition table for this
					 * disk device is unknown or not recognized.
					 */
					_("unrecognized") ;
				temp_device.max_prims = 1;

363 364 365 366 367 368 369
				Partition * partition_temp = new Partition();
				partition_temp->Set_Unallocated( temp_device.get_path(),
				                                 true,
				                                 0LL,
				                                 temp_device.length - 1LL,
				                                 temp_device.sector_size,
				                                 false );
370
				// Place libparted messages in this unallocated partition
371
				partition_temp->append_messages( libparted_messages );
372
				libparted_messages.clear();
373
				temp_device.partitions.push_back_adopt( partition_temp );
374
			}
375
					
376 377
			devices .push_back( temp_device ) ;
			
378
			destroy_device_and_disk( lp_device, lp_disk) ;
379 380
		}
	}
381

382
	set_thread_status_message("") ;
Phillip Susi's avatar
Phillip Susi committed
383
	g_idle_add( (GSourceFunc)_mainquit, NULL );
384 385
}

386 387 388
// runs gpart on the specified parameter
void GParted_Core::guess_partition_table(const Device & device, Glib::ustring &buff)
{
389 390 391
	Glib::ustring error;
	Glib::ustring cmd = "gpart -s " + Utils::num_to_str( device.sector_size ) + " " + device.get_path();
	Utils::execute_command( cmd, buff, error, true );
392 393
}

394 395 396 397 398 399 400 401 402 403 404
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 ;
}

405
bool GParted_Core::snap_to_cylinder( const Device & device, Partition & partition, Glib::ustring & error ) 
406
{
407
	Sector diff = 0;
408

409 410 411 412
	//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;
413

414 415 416
	if ( partition.type == TYPE_LOGICAL ||
	     partition.sector_start == device .sectors
	   )
417
	{
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
		//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  )
	{
437
		if ( diff < ( device .cylsize / 2 ) || less_than_half_cylinder )
438 439 440 441 442 443 444 445
			partition .sector_start -= diff ;
		else
			partition .sector_start += (device .cylsize - diff ) ;
	}

	diff = (partition .sector_end +1) % device .cylsize ;
	if ( diff )
	{
446
		if ( diff < ( device .cylsize / 2 ) && ! less_than_half_cylinder )
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470
			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)
		   )
471
		{
472 473 474
			//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 ;
475
		}
476 477
	}

478
	//Calculate difference offset from Mebibyte boundary
479
	diff = Sector(partition .sector_start % ( MEBIBYTE / partition .sector_size ));
480 481

	//Align start sector only if permitted to change start sector
482 483 484 485 486 487 488 489
	if ( diff && (   (! partition .strict_start)
	              || (   partition .strict_start
	                  && (   partition .status == STAT_NEW
	                      || partition .status == STAT_COPY
	                     )
	                 )
	             )
	   )
490
	{
491 492
		partition .sector_start += ( (MEBIBYTE / partition .sector_size) - diff) ;

493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527
		//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 )
		{
			//Locate the extended partition that contains the logical partitions.
			int index_extended = -1 ;
			for ( unsigned int t = 0 ; t < device .partitions .size() ; t++ )
			{
				if ( device .partitions[ t ] .type == TYPE_EXTENDED )
					index_extended = t ;
			}

			//If there is logical partition that starts less than 2 sectors
			//  from the start of this partition, then reserve a mebibyte for the EBR.
			if ( index_extended != -1 )
			{
				for ( unsigned int t = 0; t < device .partitions[ index_extended ] .logicals .size(); t++ )
				{
					if (   ( device .partitions[ index_extended ] .logicals[ t ] .type == TYPE_LOGICAL )
					    && ( (  (  device .partitions[ index_extended ] .logicals[ t ] .sector_start )
					          - ( partition .sector_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) ;
					}
				}
			}
		}
	}

528 529 530 531 532
	//Align end sector
	diff = (partition .sector_end + 1) % ( MEBIBYTE / partition .sector_size);
	if ( diff )
		partition .sector_end -= diff ;

533 534 535 536 537 538 539
	//If this is a logical partition not at end of drive then check to see if space is
	//  required for a following logical partition Extended Boot Record
	if ( partition .type == TYPE_LOGICAL )
	{
		//Locate the extended partition that contains the logical partitions.
		int index_extended = -1 ;
		for ( unsigned int t = 0 ; t < device .partitions .size() ; t++ )
540
		{
541 542
			if ( device .partitions[ t ] .type == TYPE_EXTENDED )
				index_extended = t ;
543
		}
544

545 546
		//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.
547
		if ( index_extended != -1 )
548
		{
549 550 551 552 553
			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 )
554 555 556
				           //Unless specifically told otherwise, the Linux kernel considers extended
				           //  boot records to be two sectors long, in order to "leave room for LILO".
				         < 2
557 558
				       )
				   )
559
					partition .sector_end -= ( MEBIBYTE / partition .sector_size ) ;
560
			}
561
		}
562 563 564 565 566 567 568

		//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 ) ;
569
	}
570

571 572 573 574 575 576 577 578 579 580
	//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
			       )
581 582 583 584 585 586
			    && (   //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
			       )
587 588 589 590 591 592 593
			    && ( device .partitions[ t ] .sector_start > partition .sector_start )
			    && ( device .partitions[ t ] .sector_start <= partition .sector_end )
			   )
				partition .sector_end -= ( MEBIBYTE / partition .sector_size );
		}
	}

594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613
	//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 ;
			}
		}
	}

614 615 616 617 618
	//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 )
619 620
	   )
	{
621
		partition .sector_end -= ( MEBIBYTE / partition .sector_size ) ;
622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644
	}

	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 )
	{
645 646 647
		error = String::ucompose(
				/* TO TRANSLATORS:  looks like   A partition cannot have a length of -1 sectors */
				_("A partition cannot have a length of %1 sectors"),
648 649 650 651
				partition .get_sector_length() ) ;
		return false ;
	}

652 653 654 655 656
	//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.
657 658 659
	if ( partition .get_sector_length() < partition .sectors_used )
	{
		error = String::ucompose(
660
				/* TO TRANSLATORS: looks like   A partition with used sectors (2048) greater than its length (1536) is not valid */
Bruce Cowan's avatar
Bruce Cowan committed
661
				_("A partition with used sectors (%1) greater than its length (%2) is not valid"),
662
				partition .sectors_used,
663
				partition .get_sector_length() ) ;
664
		return false ;
665 666
	}

667 668 669 670 671
	//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 ;
672 673
}

674 675
bool GParted_Core::apply_operation_to_disk( Operation * operation )
{
676
	bool success = false;
677
	libparted_messages .clear() ;
678

679 680 681 682 683 684 685 686 687 688 689
	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.

		case OPERATION_DELETE:
690 691 692 693 694
			success =    calibrate_partition( operation->get_partition_original(),
			                                  operation->operation_detail )
			          && remove_filesystem( operation->get_partition_original(),
			                                operation->operation_detail )
			          && Delete( operation->get_partition_original(), operation->operation_detail );
695 696 697
			break;

		case OPERATION_CHECK:
698 699 700
			success =    calibrate_partition( operation->get_partition_original(),
			                                  operation->operation_detail )
			          && check_repair_filesystem( operation->get_partition_original(),
701
			                                      operation->operation_detail )
702 703
			          && maximize_filesystem( operation->get_partition_original(),
			                                  operation->operation_detail );
704 705 706 707
			break;

		case OPERATION_CREATE:
			// The partition doesn't exist yet so there's nothing to calibrate.
708
			success = create( operation->get_partition_new(), operation->operation_detail );
709 710 711
			break;

		case OPERATION_RESIZE_MOVE:
712 713
			success = calibrate_partition( operation->get_partition_original(),
			                               operation->operation_detail );
714 715 716
			if ( ! success )
				break;

717 718 719 720
			// 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.
721
			operation->get_partition_new().set_path( operation->get_partition_original().get_path() );
722

723 724
			success = resize_move( operation->get_partition_original(),
			                       operation->get_partition_new(),
725 726 727 728
			                       operation->operation_detail );
			break;

		case OPERATION_FORMAT:
729
			success = calibrate_partition( operation->get_partition_new(), operation->operation_detail );
730 731 732
			if ( ! success )
				break;

733 734
			// Replace the original partition object's path from the
			// calibration in case the path is "Copy of ..." from the
735 736
			// partition having been newly created by a paste into unallocated
			// space earlier in the sequence of operations now being applied.
737
			operation->get_partition_original().set_path( operation->get_partition_new().get_path() );
738

739 740 741
			success =    remove_filesystem( operation->get_partition_original(),
			                                operation->operation_detail )
			          && format( operation->get_partition_new(), operation->operation_detail );
742 743 744
			break;

		case OPERATION_COPY:
745
		{
746 747
			//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
748

749 750 751
			OperationCopy * copy_op = static_cast<OperationCopy*>( operation );
			success =    calibrate_partition( copy_op->get_partition_copied(),
			                                  copy_op->operation_detail )
752 753
			             // Only calibrate the destination when pasting into an existing
			             // partition, rather than when creating a new partition.
754 755
                                  && ( copy_op->get_partition_original().type == TYPE_UNALLOCATED                     ||
			               calibrate_partition( copy_op->get_partition_new(), copy_op->operation_detail )    );
756
			if ( ! success )
757
				break;
758

759 760 761 762 763
			success =    remove_filesystem( copy_op->get_partition_original(), copy_op->operation_detail )
			          && copy( copy_op->get_partition_copied(),
			                   copy_op->get_partition_new(),
			                   copy_op->get_partition_copied().get_byte_length(),
			                   copy_op->operation_detail );
764
			break;
765
		}
766 767

		case OPERATION_LABEL_FILESYSTEM:
768 769
			success =    calibrate_partition( operation->get_partition_new(), operation->operation_detail )
			          && label_filesystem( operation->get_partition_new(), operation->operation_detail );
770 771 772
			break;

		case OPERATION_NAME_PARTITION:
773 774
			success =    calibrate_partition( operation->get_partition_new(), operation->operation_detail )
			          && name_partition( operation->get_partition_new(), operation->operation_detail );
775 776 777
			break;

		case OPERATION_CHANGE_UUID:
778 779
			success =    calibrate_partition( operation->get_partition_new(), operation->operation_detail )
			          && change_uuid( operation->get_partition_new(), operation->operation_detail );
780 781
			break;
	}
782

783
	if ( libparted_messages .size() > 0 )
784
	{
785
		operation ->operation_detail .add_child( OperationDetail( _("libparted messages"), STATUS_INFO ) ) ;
786 787

		for ( unsigned int t = 0 ; t < libparted_messages .size() ; t++ )
788
			operation ->operation_detail .get_last_child() .add_child(
789 790 791
				OperationDetail( libparted_messages[ t ], STATUS_NONE, FONT_ITALIC ) ) ;
	}

792
	return success;
793 794
}

795 796 797 798
bool GParted_Core::set_disklabel( const Device & device, const Glib::ustring & disklabel )
{
	Glib::ustring device_path = device.get_path();

799 800 801 802 803 804 805 806 807 808 809 810 811 812 813
	// 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

814 815 816 817 818 819 820 821 822 823 824 825
	// 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_Unallocated( device_path,
	                                true,
	                                0LL,
	                                device.length - 1LL,
	                                device.sector_size,
	                                false );
	erase_filesystem_signatures( temp_partition, dummy_od );
826 827 828 829

	return new_disklabel( device_path, disklabel );
}

830 831
bool GParted_Core::new_disklabel( const Glib::ustring & device_path, const Glib::ustring & disklabel,
                                  bool recreate_dmraid_devs )
832 833
{
	bool return_value = false ;
834

835 836
	PedDevice* lp_device = NULL ;
	PedDisk* lp_disk = NULL ;
837
	if ( get_device( device_path, lp_device ) )
838 839 840 841 842 843 844 845
	{
		PedDiskType *type = NULL ;
		type = ped_disk_type_get( disklabel .c_str() ) ;
		
		if ( type )
		{
			lp_disk = ped_disk_new_fresh( lp_device, type );
		
846
			return_value = commit( lp_disk ) ;
847 848
		}
		
849
		destroy_device_and_disk( lp_device, lp_disk ) ;
850
	}
851

852
#ifndef USE_LIBPARTED_DMRAID
853 854
	//delete and recreate disk entries if dmraid
	DMRaid dmraid ;
855
	if ( recreate_dmraid_devs && return_value && dmraid.is_dmraid_device( device_path ) )
856 857 858 859
	{
		dmraid .purge_dev_map_entries( device_path ) ;
		dmraid .create_dev_map_entries( device_path ) ;
	}
860
#endif
861

862 863
	return return_value ;	
}
864

865 866 867
bool GParted_Core::toggle_flag( const Partition & partition, const Glib::ustring & flag, bool state ) 
{
	bool succes = false ;
868 869
	PedDevice* lp_device = NULL ;
	PedDisk* lp_disk = NULL ;
870
	if ( get_device_and_disk( partition .device_path, lp_device, lp_disk ) )
871
	{
872
		PedPartition* lp_partition = NULL ;
873 874 875
		if ( partition .type == GParted::TYPE_EXTENDED )
			lp_partition = ped_disk_extended_partition( lp_disk ) ;
		else
876
			lp_partition = ped_disk_get_partition_by_sector( lp_disk, partition .get_sector() ) ;
877 878 879 880 881 882
	
		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 ) )
883
				succes = commit( lp_disk ) ;
884 885
		}
	
886
		destroy_device_and_disk( lp_device, lp_disk ) ;
887 888 889 890 891 892 893 894 895 896
	}

	return succes ;
}

const std::vector<FS> & GParted_Core::get_filesystems() const 
{
	return FILESYSTEMS ;
}

897 898
// Return supported capabilities of the file system type or, if not found, not supported
// capabilities set.
899 900 901
const FS & GParted_Core::get_fs( GParted::FILESYSTEM filesystem ) const 
{
	for ( unsigned int t = 0 ; t < FILESYSTEMS .size() ; t++ )
902
	{
903 904
		if ( FILESYSTEMS[ t ] .filesystem == filesystem )
			return FILESYSTEMS[ t ] ;
905 906
	}

907
	static FS fs_notsupp( FS_UNKNOWN );
908
	return fs_notsupp;
909 910
}

911 912 913 914
//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()
915 916
{
	std::vector<Glib::ustring> disklabeltypes ;
917 918 919 920

	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 ) ;
921 922 923 924 925 926 927 928

	 return disklabeltypes ;
}

std::map<Glib::ustring, bool> GParted_Core::get_available_flags( const Partition & partition ) 
{
	std::map<Glib::ustring, bool> flag_info ;

929 930
	PedDevice* lp_device = NULL ;
	PedDisk* lp_disk = NULL ;
931
	if ( get_device_and_disk( partition .device_path, lp_device, lp_disk ) )
932
	{
933
		PedPartition* lp_partition = NULL ;
934 935 936
		if ( partition .type == GParted::TYPE_EXTENDED )
			lp_partition = ped_disk_extended_partition( lp_disk ) ;
		else
937
			lp_partition = ped_disk_get_partition_by_sector( lp_disk, partition .get_sector() ) ;
938 939 940 941 942 943 944 945 946
	
		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 ] ) ;
		}
	
947
		destroy_device_and_disk( lp_device, lp_disk ) ;
948 949 950 951
	}

	return flag_info ;
}
952 953 954 955 956
	
Glib::ustring GParted_Core::get_libparted_version() 
{
	return ped_get_version() ;
}
957 958 959

//private functions...

960 961
Glib::ustring GParted_Core::get_partition_path( PedPartition * lp_partition )
{
962
	char * lp_path;  //we have to free the result of ped_partition_get_path()
963
	Glib::ustring partition_path = "Partition path not found";
964 965

	lp_path = ped_partition_get_path(lp_partition);
966 967 968 969 970
	if ( lp_path != NULL )
	{
		partition_path = lp_path;
		free(lp_path);
	}
971

972
#ifndef USE_LIBPARTED_DMRAID
973
	//Ensure partition path name is compatible with dmraid
974
	DMRaid dmraid;   //Use cache of dmraid device information
975 976 977
	if (   dmraid .is_dmraid_supported()
	    && dmraid .is_dmraid_device( partition_path )
	   )
978 979 980
	{
		partition_path = dmraid .make_path_dmraid_compatible(partition_path);
	}
981
#endif