Pass devid when resizing btrfs file systems (#723842)

GParted doesn't specify the devid when resizing a btrfs file system, so
the kernel defaults to resizing devid 1.  On a multi-device btrfs this
may not be the same partition which GParted is resizing.  This will
result in file system truncation and corruption.  Shrinking the wrong
partition example:

    1)  Create a btrfs file system spanning 2 partitions:
            # mkfs.btrfs /dev/sdb1 /dev/sdb2
            # btrfs filesystem show /dev/sdb1
            Label: none  uuid: 41654265-9840-45c4-aca1-55989da358d6
                    Total devices 2 FS bytes used 112.00KiB
                    devid    1 size 2.00GiB used 437.50MiB path /dev/sdb1
                    devid    2 size 2.00GiB used 417.50MiB path /dev/sdb2

    2)  Resize /dev/sdb2 down to 1 GiB using GParted.  This command was
        run:
            btrfs filesystem resize 1048576K /tmp/gparted-ddyGRh
        which resized devid 1 (/dev/sdb1) to 1 GiB:
            # btrfs filesystem show /dev/sdb1
            Label: none  uuid: 41654265-9840-45c4-aca1-55989da358d6
                    Total devices 2 FS bytes used 256.00KiB
                    devid    1 size 1.00GiB used 437.50MiB path /dev/sdb1
                    devid    2 size 2.00GiB used 417.50MiB path /dev/sdb2
        but GParted instead resized /dev/sdb2 to 1 GiB:
            # sfdisk -s /dev/sdb1
            2097152
            # sfdisk -s /dev/sdb2
            1048576

Even on a single device btrfs devid 1 may no longer exist if the file
system has had the initial device removed from it.  Example:

    1)  Create a single btrfs file system, add a second device and
        remove the first:
            # mkfs.btrfs /dev/sdb1
            # mount /dev/sdb1 /mnt/1
            # btrfs device add /dev/sdb2 /mnt/1
            # btrfs device remove /dev/sdb1 /mnt/1
            # umount /mnt/1
            # btrfs filesystem show /dev/sdb2
            Label: none  uuid: 2cbf3ac3-1344-472a-a0c7-1476d23bdc9f
                    Total devices 1 FS bytes used 256.00KiB
                    devid    2 size 2.00GiB used 480.00MiB path /dev/sdb2

    2)  Again resize /dev/sdb2 down to 1 GiB using GParted.  This
        command was run:
            btrfs filesystem resize 1048576K /tmp/gparted-ddyGRh
        but it failed with:
            ERROR: unable to resize 'tmp/gparted-lEyGaY' - No such device
        A more informative error message was written to syslog:
            # tail -1 /var/log/messages
            Mar 12 14:15:01 localhost kernel: btrfs: resizer unable to find device 1

This is with Linux kernel 3.13.5 on Fedora 20, circa March 2014.

Fix by specifying the devid when resizing (part of) a btrfs file system.
Example command specifying devid 2:

    btrfs filesystem resize 2:1048576K /tmp/1

This will always work because it is the kernel which interprets the
devid colon size parameter and has always done so since btrfs was first
added to the kernel in version 2.6.32 [1].

Reference:
[1] linux v2.6.32 fs/btrfs/ioctl.c btrfs_ioctl_resize()
    https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/fs/btrfs/ioctl.c?id=v2.6.32#n578

Bug #723842 - GParted resizes the wrong filesystem (does not pass the
              devid to btrfs filesystem resize)
This commit is contained in:
Mike Fleetwood 2014-04-11 11:27:18 +01:00 committed by Curtis Gedak
parent 287526681d
commit 0e980a47a2
1 changed files with 13 additions and 3 deletions

View File

@ -291,6 +291,16 @@ bool btrfs::write_label( const Partition & partition, OperationDetail & operatio
bool btrfs::resize( const Partition & partition_new, OperationDetail & operationdetail, bool fill_partition )
{
bool success = true ;
Glib::ustring path = partition_new .get_path() ;
BTRFS_Device btrfs_dev = get_cache_entry( path ) ;
if ( btrfs_dev .devid == -1 )
{
operationdetail .add_child( OperationDetail(
String::ucompose( _("Failed to find devid for path %1"), path ), STATUS_ERROR ) ) ;
return false ;
}
Glib::ustring devid_str = Utils::num_to_str( btrfs_dev .devid ) ;
Glib::ustring mount_point ;
if ( ! partition_new .busy )
@ -298,7 +308,7 @@ bool btrfs::resize( const Partition & partition_new, OperationDetail & operation
mount_point = mk_temp_dir( "", operationdetail ) ;
if ( mount_point .empty() )
return false ;
success &= ! execute_command( "mount -v -t btrfs " + partition_new .get_path() + " " + mount_point,
success &= ! execute_command( "mount -v -t btrfs " + path + " " + mount_point,
operationdetail, true ) ;
}
else
@ -314,9 +324,9 @@ bool btrfs::resize( const Partition & partition_new, OperationDetail & operation
size = "max" ;
Glib::ustring cmd ;
if ( btrfs_found )
cmd = "btrfs filesystem resize " + size + " " + mount_point ;
cmd = "btrfs filesystem resize " + devid_str + ":" + size + " " + mount_point ;
else
cmd = "btrfsctl -r " + size + " " + mount_point ;
cmd = "btrfsctl -r " + devid_str + ":" + size + " " + mount_point ;
exit_status = execute_command( cmd, operationdetail, false ) ;
bool resize_succeeded = ( exit_status == 0 ) ;
if ( resize_to_same_size_fails )