[gparted] Implement rollback of failed partition resize/move steps (#791875)



commit 0f16703bbbae9c7d8d54d46af4a7b3a7e725f17f
Author: Mike Fleetwood <mike fleetwood googlemail com>
Date:   Fri Dec 8 16:46:31 2017 +0000

    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

 include/GParted_Core.h |    5 +++--
 src/GParted_Core.cc    |   37 ++++++++++++++++++++++++++++++++++---
 2 files changed, 37 insertions(+), 5 deletions(-)
---
diff --git a/include/GParted_Core.h b/include/GParted_Core.h
index b805e95..6a46647 100644
--- a/include/GParted_Core.h
+++ b/include/GParted_Core.h
@@ -144,8 +144,9 @@ private:
                           const Partition & partition_new,
                           OperationDetail & operationdetail );
        bool resize_move_partition( const Partition & partition_old,
-                                   const Partition & partition_new,
-                                   OperationDetail & operationdetail ) ;
+                                   const Partition & partition_new,
+                                   OperationDetail & operationdetail,
+                                   bool rollback_on_fail = false );
        bool resize_move_partition_implement( const Partition & partition_old,
                                              const Partition & partition_new,
                                              Sector & new_start,
diff --git a/src/GParted_Core.cc b/src/GParted_Core.cc
index 6a84201..27f178a 100644
--- a/src/GParted_Core.cc
+++ b/src/GParted_Core.cc
@@ -2640,8 +2640,9 @@ 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 )
+                                          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
@@ -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;
 }
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]