Implement rollback of failed partition resize/move steps (#791875)
Even after implementing a fix for bug 790418 "Unable to inform the kernel of the change message may lead to corrupted partition table" GParted/libparted can still encounter errors informing the kernel of the new partition layout. This has been seen with GParted on CentOS 7 with libparted 3.1. In such a case the partition has been successfully written to the disk but just informing the kernel failed. This is a problem because when a partition is being moved in advance of a file system move step, failure to inform the kernel leaves the partition boundaries not matching the on disk limits of the file system. For a move to the left this leaves the partition reported as unknown, apparently losing the user's data. For example start with a 512 MiB partition containing an XFS file system. This is recognised by blkid and parted, hence also by GParted. # blkid /dev/sdb1 /dev/sdb1: UUID=... TYPE="xfs" PARTUUID="37965980-01" # parted /dev/sdb unit s print Model: ATA VBOX HARDDISK (scsi) Disk /dev/sdb: 16777216s Sector size (logical/physical): 512B/512B Partition Table: msdos Disk Flags: Number Start End Size Type File system Flags 1 1048576s 2097151s 1048576s primary xfs Now move the partition 100 MiB to the left and have it fail to inform the kernel after the first partition change step. Operation details: Move /dev/sdb1 to the left (ERROR) * calibrate /dev/sdb1 (SUCCESS) * check file system on /dev/sdb1 for errors and (if poss...(SUCCESS) * grow partition from 512.00 MiB to 612.00 MiB (ERROR) old start: 1048576 old end: 2097151 old size: 1048576 (512.00 MiB) requested start: 843776 requested end: 2097151 requested size: 1253376 (612.00 MiB) * libparted messages (ERROR) Error informing the kernel about modifications to partition /dev/sdb1 -- Device or resource busy. This means Linux won't know about any changes you made to /dev/sdb1 until you reboot -- so you shouldn't mount it or use it in any way before rebooting. Failed to add partition 1 (resource temporarily unavailable) Now because the start of the partition is 100 MiB before the start of the file system, the file system is no longer recognised, and apparently the user's data has been lost. # blkid /dev/sdb1 /dev/sdb1: PARTUUID="37965980-01" # parted /dev/sdb unit s print ... Number Start End Size Type File system Flags 1 843776s 2097151s 1253376s primary It doesn't matter why updating the partition failed, even if it was because of an error writing to the disk. Rollback of the change to the partition should be attempted. The worst case scenario is that rollback of the change fails, which is the equivalent to how the code worked before this patch set. However in other cases where the partition boundaries are being updated after a file system move or shrink step then the partition should be updated to match the new location of the file system itself. And no rollback is wanted. If the failure was only informing the kernel then in fact the partition has actually been updated on disk after all. So each partition resize/move step needs examining on a case by case basis to decide if rolling back the change to the partition is wanted or not. This patch only adds partition change rollback into resize_move_partition(). Rollback remains disabled until all cases are examined in the following patch. Bug 791875 - Rollback specific failed partition change steps
This commit is contained in:
parent
93ccc79e3a
commit
0f16703bbb
|
@ -145,7 +145,8 @@ private:
|
|||
OperationDetail & operationdetail );
|
||||
bool resize_move_partition( const Partition & partition_old,
|
||||
const Partition & partition_new,
|
||||
OperationDetail & operationdetail ) ;
|
||||
OperationDetail & operationdetail,
|
||||
bool rollback_on_fail = false );
|
||||
bool resize_move_partition_implement( const Partition & partition_old,
|
||||
const Partition & partition_new,
|
||||
Sector & new_start,
|
||||
|
|
|
@ -2641,7 +2641,8 @@ bool GParted_Core::resize_plain( const Partition & partition_old,
|
|||
|
||||
bool GParted_Core::resize_move_partition( const Partition & partition_old,
|
||||
const Partition & partition_new,
|
||||
OperationDetail & operationdetail )
|
||||
OperationDetail & operationdetail,
|
||||
bool rollback_on_fail )
|
||||
{
|
||||
if ( partition_new.type == TYPE_UNPARTITIONED )
|
||||
// Trying to resize/move a non-partitioned whole disk device is a
|
||||
|
@ -2770,8 +2771,38 @@ bool GParted_Core::resize_move_partition( const Partition & partition_old,
|
|||
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") ) );
|
||||
|
||||
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_new, *partition_restore,
|
||||
new_start, new_end );
|
||||
|
||||
operationdetail.get_last_child().add_child(
|
||||
OperationDetail(
|
||||
String::ucompose( _("original start: %1"), partition_restore->sector_start ) + "\n" +
|
||||
String::ucompose( _("original end: %1"), partition_restore->sector_end ) + "\n" +
|
||||
String::ucompose( _("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;
|
||||
|
||||
operationdetail.get_last_child().set_success_and_capture_errors( rollback_success );
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue