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"
This commit is contained in:
Mike Fleetwood 2013-08-14 08:21:58 +01:00 committed by Curtis Gedak
parent 51845b7799
commit 246e05559d
3 changed files with 105 additions and 67 deletions

View File

@ -50,6 +50,7 @@ const Byte_Value KIBIBYTE=1024;
const Byte_Value MEBIBYTE=(KIBIBYTE * KIBIBYTE); const Byte_Value MEBIBYTE=(KIBIBYTE * KIBIBYTE);
const Byte_Value GIBIBYTE=(MEBIBYTE * KIBIBYTE); const Byte_Value GIBIBYTE=(MEBIBYTE * KIBIBYTE);
const Byte_Value TEBIBYTE=(GIBIBYTE * 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 = _("(New UUID - will be randomly generated)") ;
const Glib::ustring UUID_RANDOM_NTFS_HALF = _("(Half 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, static int get_mounted_filesystem_usage( const Glib::ustring & mountpoint,
Byte_Value & fs_size, Byte_Value & fs_free, Byte_Value & fs_size, Byte_Value & fs_free,
Glib::ustring error_message ) ; 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: private:
static bool get_kernel_version( int & major_ver, int & minor_ver, int & patch_ver ) ; static bool get_kernel_version( int & major_ver, int & minor_ver, int & patch_ver ) ;

View File

@ -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 GParted_Core::erase_filesystem_signatures( const Partition & partition, OperationDetail & operationdetail )
{ {
bool overall_success ; bool overall_success = true ;
operationdetail .add_child( OperationDetail( operationdetail .add_child( OperationDetail(
String::ucompose( _("clear old file system signatures in %1"), String::ucompose( _("clear old file system signatures in %1"),
partition .get_path() ) ) ) ; partition .get_path() ) ) ) ;
@ -3131,84 +3131,107 @@ bool GParted_Core::erase_filesystem_signatures( const Partition & partition, Ope
} }
} }
//Single copy of string for translation purposes //Erase all file system super blocks, including their signatures. The specified
/*TO TRANSLATORS: looks like wrote 68.00 KiB of zeros at byte offset 0 */ // byte ranges are converted to whole sectors (as disks fundamentally only read
const Glib::ustring wrote_zeros_msg = _("wrote %1 of zeros at byte offset %2") ; // 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
//Erase all file system primary super blocks including their signatures. // code overwrites whole sectors and it embeds more knowledge that is necessary.
// 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 // First byte range from offset 0 of length 68 KiB covers the primary super block
// which range offset 0 for vfat, ntfs and xfs all the way up to offset 64 KiB // of all currently supported file systems and is also likely to include future
// for btrfs, reiserfs and reiser4. This is likely to also include future file // file system super blocks too. Only a few file systems have additional super
// system super blocks too). // 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() ; //Rounding is performed in multiples of the sector size because writes are in whole sectors.
od .add_child( OperationDetail( _("clear primary signatures") ) ) ; 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() ) ; //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 )
{
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 ;
}
//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 ; Byte_Value written = 0LL ;
bool zero_success = false ; bool zero_success = false ;
if ( device_is_open && buf ) if ( device_is_open && buf )
{ {
while ( written < total_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 ) ) ) ;
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, zero_success = ped_geometry_write( & lp_partition ->geom, buf,
written / lp_device ->sector_size, ( byte_offset + written ) / lp_device ->sector_size,
bufsize / lp_device ->sector_size ) ; amount / lp_device ->sector_size ) ;
if ( ! zero_success ) if ( ! zero_success )
break ; break ;
written += bufsize ; written += amount ;
} }
}
if ( zero_success ) od .get_last_child() .set_status( zero_success ? STATUS_SUCCES : STATUS_ERROR ) ;
{
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 ) ;
}
else
{
od .get_last_child() .set_status( STATUS_ERROR ) ;
}
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 ;
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 ) ;
}
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 ) ;
} }
overall_success &= zero_success ; overall_success &= zero_success ;
} }

View File

@ -669,6 +669,18 @@ int Utils::get_mounted_filesystem_usage( const Glib::ustring & mountpoint,
return ret ; 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 ... //private functions ...
//Read kernel version, reporting success or failure //Read kernel version, reporting success or failure