[gparted] Detect busy status of multi-device btrfs file systems (#723842)



commit 76e64f2905bdbe37c307bc9fab055f44914dc7b2
Author: Mike Fleetwood <mike fleetwood googlemail com>
Date:   Mon Feb 17 22:39:34 2014 +0000

    Detect busy status of multi-device btrfs file systems (#723842)
    
    Busy detection of file systems works by checking if the device is
    mounted (appears in the mount_info map).  For a multi-device btrfs file
    system this will only report one of the devices as busy, not all of
    them.
    
        # btrfs filesystem show /dev/sdb1
        Label: none  uuid: 36eb51a2-2927-4c92-820f-b2f0b5cdae50
                Total devices 2 FS bytes used 156.00KB
                devid    2 size 2.00GB used 512.00MB path /dev/sdb2
                devid    1 size 2.00GB used 240.75MB path /dev/sdb1
        # mount /dev/sdb1 /mnt/1
        # grep btrfs /proc/mounts
        /dev/sdb1 /mnt/1 btrfs rw,seclabel,relatime,ssd,space_cache 0 0
    
    GParted will only report /dev/sdb1 as busy, but not /dev/sdb2.
    
    Add btrfs specific is_busy() method which reports the device as busy if
    any of the devices in the btrfs file system are mounted.  This uses a
    cache which maps device membership in all btrfs file systems.  The cache
    is cleared on GParted refresh and incrementally populated as each btrfs
    partition is checked for busy status.
    
    WARNING:
    Removal of the mounting device from a btrfs file system makes it
    impossible to determine whether the file system is mounted or not for
    linux <= 3.4.  This is because /proc/mounts continues to show the old
    device which is no longer a member of the file system.
    
        # btrfs device delete /dev/sdb1 /mnt/1
        # sync
        # grep btrfs /proc/mounts
        /dev/sdb1 /mnt/1 btrfs rw,seclabel,relatime,ssd,space_cache 0 0
        # btrfs filesystem show /dev/sdb1
        # btrfs filesystem show /dev/sdb2
        Label: none  uuid: 36eb51a2-2927-4c92-820f-b2f0b5cdae50
                Total devices 1 FS bytes used 28.00KB
                devid    2 size 2.00GB used 1.02GB path /dev/sdb2
    
    Fixed in linux 3.5 by commit:
        Btrfs: implement ->show_devname
        
https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=9c5085c147989d48dfe74194b48affc23f376650
    
    Bug #723842 - GParted resizes the wrong filesystem (does not pass the
                  devid to btrfs filesystem resize)

 include/btrfs.h     |    4 +++
 src/GParted_Core.cc |    3 +-
 src/btrfs.cc        |   74 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 79 insertions(+), 2 deletions(-)
---
diff --git a/include/btrfs.h b/include/btrfs.h
index 6cf7744..f0828ee 100644
--- a/include/btrfs.h
+++ b/include/btrfs.h
@@ -28,6 +28,7 @@ class btrfs : public FileSystem
 public:
        FS get_filesystem_support() ;
        void set_used_sectors( Partition & partition ) ;
+       bool is_busy( const Glib::ustring & path ) ;
        void read_label( Partition & partition ) ;
        bool write_label( const Partition & partition, OperationDetail & operationdetail ) ;
        void read_uuid( Partition & partition ) ;
@@ -35,7 +36,10 @@ public:
        bool resize( const Partition & partition_new, OperationDetail & operationdetail, bool fill_partition 
= false ) ;
        bool check_repair( const Partition & partition, OperationDetail & operationdetail ) ;
 
+       static void clear_cache() ;
+
 private:
+       static const std::vector<Glib::ustring> get_cache_entry( const Glib::ustring & path ) ;
        static Byte_Value btrfs_size_to_num( Glib::ustring str, Byte_Value ptn_bytes, bool scale_up ) ;
        static gdouble btrfs_size_max_delta( Glib::ustring str ) ;
        static gdouble btrfs_size_to_gdouble( Glib::ustring str ) ;
diff --git a/src/GParted_Core.cc b/src/GParted_Core.cc
index 3b3d092..a2b4ebb 100644
--- a/src/GParted_Core.cc
+++ b/src/GParted_Core.cc
@@ -179,7 +179,8 @@ void GParted_Core::set_devices_thread( std::vector<Device> * pdevices )
        FS_Info fs_info( true ) ;  //Refresh cache of file system information
        DMRaid dmraid( true ) ;    //Refresh cache of dmraid device information
        LVM2_PV_Info lvm2_pv_info( true ) ;     //Refresh cache of LVM2 PV information
-       
+       btrfs::clear_cache() ;
+
        init_maps() ;
        
        //only probe if no devices were specified as arguments..
diff --git a/src/btrfs.cc b/src/btrfs.cc
index 788a484..0700026 100644
--- a/src/btrfs.cc
+++ b/src/btrfs.cc
@@ -17,6 +17,7 @@
 
 
 #include "../include/btrfs.h"
+#include "../include/GParted_Core.h"
 
 #include <ctype.h>
 
@@ -26,12 +27,21 @@ namespace GParted
 bool btrfs_found = false ;
 bool resize_to_same_size_fails = true ;
 
+//Cache of all devices in each btrfs file system by device
+//  E.g. For a single device btrfs on /dev/sda2 and a three device btrfs
+//       on /dev/sd[bcd]1 the cache would be:
+//  btrfs_device_cache["/dev/sda2"] = ["/dev/sda2"]
+//  btrfs_device_cache["/dev/sdb1"] = ["/dev/sdd1", "/dev/sdc1", "/dev/sdb1"]
+//  btrfs_device_cache["/dev/sdc1"] = ["/dev/sdd1", "/dev/sdc1", "/dev/sdb1"]
+//  btrfs_device_cache["/dev/sdd1"] = ["/dev/sdd1", "/dev/sdc1", "/dev/sdb1"]
+std::map< Glib::ustring, std::vector<Glib::ustring> > btrfs_device_cache ;
+
 FS btrfs::get_filesystem_support()
 {
        FS fs ;
        fs .filesystem = GParted::FS_BTRFS ;
 
-       fs .busy = FS::GPARTED ;
+       fs .busy = FS::EXTERNAL ;
 
        if ( ! Glib::find_program_in_path( "mkfs.btrfs" ) .empty() )
        {
@@ -121,6 +131,23 @@ FS btrfs::get_filesystem_support()
        return fs ;
 }
 
+bool btrfs::is_busy( const Glib::ustring & path )
+{
+       //A btrfs file system is busy if any of the member devices are mounted.
+       //  WARNING:
+       //  Removal of the mounting device from a btrfs file system makes it impossible to
+       //  determine whether the file system is mounted or not for linux <= 3.4.  This is
+       //  because /proc/mounts continues to show the old device which is no longer a
+       //  member of the file system.  Fixed in linux 3.5 by commit:
+       //      Btrfs: implement ->show_devname
+       //      
https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=9c5085c147989d48dfe74194b48affc23f376650
+       std::vector<Glib::ustring> entry = get_cache_entry( path ) ;
+       for ( unsigned int i = 0 ; i < entry .size() ; i ++ )
+               if ( GParted_Core::is_dev_mounted( entry[ i ] ) )
+                       return true ;
+       return false ;
+}
+
 bool btrfs::create( const Partition & new_partition, OperationDetail & operationdetail )
 {
        return (! execute_command( "mkfs.btrfs -L \"" + new_partition .get_label() + "\" " + new_partition 
.get_path(), operationdetail ) );
@@ -311,8 +338,53 @@ void btrfs::read_uuid( Partition & partition )
        }
 }
 
+void btrfs::clear_cache()
+{
+       btrfs_device_cache .clear() ;
+}
+
 //Private methods
 
+//Return btrfs device cache entry, incrementally loading cache as required
+const std::vector<Glib::ustring> btrfs::get_cache_entry( const Glib::ustring & path )
+{
+       std::vector<Glib::ustring> entry = btrfs_device_cache[ path ] ;
+
+       if ( ! entry .empty() )
+               return entry ;
+
+       int exit_status ;
+       Glib::ustring output, error ;
+       if ( btrfs_found )
+               exit_status = Utils::execute_command( "btrfs filesystem show " + path, output, error, true ) ;
+       else
+               exit_status = Utils::execute_command( "btrfs-show " + path, output, error, true ) ;
+       if ( ! exit_status )
+       {
+               //Extract path for each devid from output like this:
+               //  Label: none  uuid: 36eb51a2-2927-4c92-820f-b2f0b5cdae50
+               //          Total devices 2 FS bytes used 156.00KB
+               //          devid    2 size 2.00GB used 512.00MB path /dev/sdb2
+               //          devid    1 size 2.00GB used 240.75MB path /dev/sdb1
+               Glib::ustring::size_type offset = 0 ;
+               Glib::ustring::size_type index ;
+               while ( ( index = output .find( "devid ", offset ) ) != Glib::ustring::npos )
+               {
+                       Glib::ustring devid_path = Utils::regexp_label( output .substr( index ),
+                                                                       "devid .* path (/dev/[[:graph:]]+)" ) 
;
+                       if ( ! devid_path .empty() )
+                       {
+                               entry .push_back( devid_path ) ;
+                       }
+                       offset = index + 5 ;  //Next find starts immediately after current "devid"
+               }
+       }
+       //Add cache entries for all found devices
+       for ( unsigned int i = 0 ; i < entry .size() ; i ++ )
+               btrfs_device_cache[ entry[ i ] ] = entry ;
+       return entry ;
+}
+
 //Return the value of a btrfs tool formatted size, including reversing
 //  changes in certain cases caused by using binary prefix multipliers
 //  and rounding to two decimal places of precision.  E.g. "2.00GB".


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