[gparted] Clear btrfs file system super block mirror copies too (#705426)



commit 246e05559d91865dd135690a62d56144f375c26b
Author: Mike Fleetwood <mike fleetwood googlemail com>
Date:   Wed Aug 14 08:21:58 2013 +0100

    Clear btrfs file system super block mirror copies too (#705426)
    
    With recent btrfs-progs, GParted failed to format a btrfs file system
    over the top of an existing one.  Make btrfs failed with this error:
    
        # mkfs.btrfs /dev/sdb1
        ...
        /dev/sdb1 appears to contain an existing filesystem (btrfs).
        Use the -f option to force overwrite.
    
    With this commit to btrfs-progs on 2013-02-19, mkfs.btrfs checks for
    existing file system signatures, including all mirror copies of btrfs
    super blocks, before writing to the partition.
    
        
http://git.kernel.org/cgit/linux/kernel/git/mason/btrfs-progs.git/commit/?id=2a2d8e1962e8b6cda7b0a7584f6d2fb95d442cb6
        btrfs-progs: require mkfs -f force option to overwrite filesystem or partition table
    
    Make GParted clear all the mirror copies of the btrfs file system super
    blocks as erase_filesystem_signatures() is intended to prevent detection
    of old signatures.  This also avoids having to determine if the -f
    option to mkfs.btrfs is available before trying to use it.
    
    Closes Bug #705426 - Formatting Existing BTRFS Partition as BTRFS Fails
                         Because mkfs.btrfs Is Not Run with "-f"

 include/Utils.h     |    3 +
 src/GParted_Core.cc |  151 +++++++++++++++++++++++++++++----------------------
 src/Utils.cc        |   12 ++++
 3 files changed, 102 insertions(+), 64 deletions(-)
---
diff --git a/include/Utils.h b/include/Utils.h
index 77a5d32..c1e98d9 100644
--- a/include/Utils.h
+++ b/include/Utils.h
@@ -50,6 +50,7 @@ const Byte_Value KIBIBYTE=1024;
 const Byte_Value MEBIBYTE=(KIBIBYTE * KIBIBYTE);
 const Byte_Value GIBIBYTE=(MEBIBYTE * KIBIBYTE);
 const Byte_Value TEBIBYTE=(GIBIBYTE * KIBIBYTE);
+const Byte_Value PEBIBYTE=(TEBIBYTE * KIBIBYTE);
 
 const Glib::ustring UUID_RANDOM = _("(New UUID - will be randomly generated)") ;
 const Glib::ustring UUID_RANDOM_NTFS_HALF = _("(Half new UUID - will be randomly generated)") ;
@@ -193,6 +194,8 @@ public:
        static int get_mounted_filesystem_usage( const Glib::ustring & mountpoint,
                                                 Byte_Value & fs_size, Byte_Value & fs_free,
                                                 Glib::ustring error_message ) ;
+       static Byte_Value floor_size( Byte_Value value, Byte_Value rounding_size ) ;
+       static Byte_Value ceil_size( Byte_Value value, Byte_Value rounding_size ) ;
 
 private:
        static bool get_kernel_version( int & major_ver, int & minor_ver, int & patch_ver ) ;
diff --git a/src/GParted_Core.cc b/src/GParted_Core.cc
index 625265f..d534f14 100644
--- a/src/GParted_Core.cc
+++ b/src/GParted_Core.cc
@@ -3104,7 +3104,7 @@ bool GParted_Core::filesystem_resize_disallowed( const Partition & partition )
 
 bool GParted_Core::erase_filesystem_signatures( const Partition & partition, OperationDetail & 
operationdetail )
 {
-       bool overall_success ;
+       bool overall_success = true ;
        operationdetail .add_child( OperationDetail(
                        String::ucompose( _("clear old file system signatures in %1"),
                                          partition .get_path() ) ) ) ;
@@ -3131,84 +3131,107 @@ bool GParted_Core::erase_filesystem_signatures( const Partition & partition, Ope
                }
        }
 
-       //Single copy of string for translation purposes
-       /*TO TRANSLATORS: looks like  wrote 68.00 KiB of zeros at byte offset 0 */
-       const Glib::ustring wrote_zeros_msg = _("wrote %1 of zeros at byte offset %2") ;
-
-       //Erase all file system primary super blocks including their signatures.
-       //  Overwrite the first 68 KiB with zeros (or larger if if the sector size is
-       //  larger than 4 KiB.  This covers the location all supported file super blocks
-       //  which range offset 0 for vfat, ntfs and xfs all the way up to offset 64 KiB
-       //  for btrfs, reiserfs and reiser4.  This is likely to also include future file
-       //  system super blocks too).
+       //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 68 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 additional super
+       //  blocks and signatures.  Overwrite the btrfs super block mirror copies and the
+       //  nilfs2 secondary super block.
+       //
+       //  Btrfs super blocks are located at: 64 KiB, 64 MiB, 256 GiB and 1 PiB.
+       //  https://btrfs.wiki.kernel.org/index.php/On-disk_Format#Superblock
+       //
+       //  Nilfs2 secondary super block is located at 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)
+       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           , 68LL * 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
+               {  -4LL * KIBIBYTE, 4LL * KIBIBYTE,  4LL * KIBIBYTE }   //Nilfs2 secondary super block
+       } ;
+       for ( unsigned int i = 0 ; overall_success && i < sizeof( ranges ) / sizeof( ranges[0] ) ; i ++ )
        {
-               OperationDetail & od = operationdetail .get_last_child() ;
-               od .add_child( OperationDetail( _("clear primary signatures") ) ) ;
+               //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 ;
 
-               Byte_Value total_size = std::min( 64LL * KIBIBYTE + bufsize, partition .get_byte_length() ) ;
-               Byte_Value written = 0LL ;
-               bool zero_success = false ;
-               if ( device_is_open && buf )
+               //Compute range to be erased taking into minimum desired rounding requirements and
+               //  negative offsets.  Range may become larger, but not smaller than requested.
+               if ( ranges[i] .offset >= 0LL )
                {
-                       while ( written < total_size )
-                       {
-                               zero_success = ped_geometry_write( & lp_partition ->geom, buf,
-                                                                  written / lp_device ->sector_size,
-                                                                  bufsize / lp_device ->sector_size ) ;
-                               if ( ! zero_success )
-                                       break ;
-                               written += bufsize ;
-                       }
+                       byte_offset = Utils::floor_size( ranges[i] .offset, rounding_size ) ;
+                       byte_len    = Utils::ceil_size( ranges[i] .offset + ranges[i] .length, rounding_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, rounding_size )
+                                     - byte_offset ;
                }
 
-               if ( zero_success )
+               //Limit range to partition size.
+               if ( byte_offset + byte_len <= 0LL )
                {
-                       od .get_last_child() .add_child( OperationDetail(
-                                       String::ucompose( wrote_zeros_msg,
-                                                         Utils::format_size( written, 1 ),
-                                                         0LL ),
-                                       STATUS_NONE, FONT_ITALIC ) ) ;
-                       od .get_last_child() .set_status( STATUS_SUCCES ) ;
+                       //Byte range entirely before partition start.  Programmer error!
+                       continue;
                }
-               else
+               else if ( byte_offset < 0 )
                {
-                       od .get_last_child() .set_status( STATUS_ERROR ) ;
+                       //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 ;
                }
-               overall_success = zero_success ;
-       }
 
-       //Also erase any nilfs2 secondary super block at the end of the partition to
-       //  prevent erroneous detection by libparted using the signature in the secondary
-       //  super block.  Overwrite the last full 4 KiB block with zeros (or larger if the
-       //  sector size is larger and possibly earlier depending on the sector alignment).
-       //  Ref: nilfs-utils-2.1.4/include/nilfs2_fs.h
-       //  #define NILFS_SB2_OFFSET_BYTES(devsize) ((((devsize) >> 12) - 1) << 12)
-       if ( overall_success )
-       {
                OperationDetail & od = operationdetail .get_last_child() ;
-               od .add_child( OperationDetail( _("clear secondary signatures") ) ) ;
-
-               Byte_Value byte_offset = ( ( partition .get_byte_length() >> 12 ) - 1 ) << 12 ;
+               Byte_Value written = 0LL ;
                bool zero_success = false ;
                if ( device_is_open && buf )
                {
-                       zero_success = ped_geometry_write( & lp_partition ->geom, buf,
-                                                          byte_offset / lp_device ->sector_size,
-                                                          bufsize / lp_device ->sector_size ) ;
-               }
+                       od .add_child( OperationDetail(
+                                       /*TO TRANSLATORS: looks like  write 68.00 KiB of zeros at byte offset 
0 */
+                                       String::ucompose( "write %1 of zeros at byte offset %2",
+                                                         Utils::format_size( byte_len, 1 ),
+                                                         byte_offset ) ) ) ;
 
-               if ( zero_success )
-               {
-                       od .get_last_child() .add_child( OperationDetail(
-                                       String::ucompose( wrote_zeros_msg,
-                                                         Utils::format_size( bufsize, 1 ),
-                                                         byte_offset ),
-                                       STATUS_NONE, FONT_ITALIC ) ) ;
-                       od .get_last_child() .set_status( STATUS_SUCCES ) ;
-               }
-               else
-               {
-                       od .get_last_child() .set_status( STATUS_ERROR ) ;
+                       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_geometry_write( & lp_partition ->geom, buf,
+                                                                  ( byte_offset + written ) / lp_device 
->sector_size,
+                                                                  amount / lp_device ->sector_size ) ;
+                               if ( ! zero_success )
+                                       break ;
+                               written += amount ;
+                       }
+
+                       od .get_last_child() .set_status( zero_success ? STATUS_SUCCES : STATUS_ERROR ) ;
                }
                overall_success &= zero_success ;
        }
diff --git a/src/Utils.cc b/src/Utils.cc
index 0968e3d..e7fcd78 100644
--- a/src/Utils.cc
+++ b/src/Utils.cc
@@ -669,6 +669,18 @@ int Utils::get_mounted_filesystem_usage( const Glib::ustring & mountpoint,
        return ret ;
 }
 
+//Round down to multiple of rounding_size
+Byte_Value Utils::floor_size( Byte_Value value, Byte_Value rounding_size )
+{
+       return value / rounding_size * rounding_size ;
+}
+
+//Round up to multiple of rounding_size
+Byte_Value Utils::ceil_size( Byte_Value value, Byte_Value rounding_size )
+{
+       return ( value + rounding_size - 1LL ) / rounding_size * rounding_size ;
+}
+
 //private functions ...
 
 //Read kernel version, reporting success or failure


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