[gparted] Fallback to reading mount command output instead of /etc/mtab (#723842)



commit 4b63e46a4ed7655a876875fc7d9bea01b3e91871
Author: Mike Fleetwood <mike fleetwood googlemail com>
Date:   Mon Apr 14 15:21:55 2014 +0100

    Fallback to reading mount command output instead of /etc/mtab (#723842)
    
    With linux 3.5 and later, the device used to mount a btrfs file system
    is updated in /proc/mounts when the previous mounting device is removed
    from the file system.  Most recent distributions make /etc/mtab a
    symbolic link to /proc/mounts.  However some still have /etc/mtab as a
    plain file only updated by mount and umount, thus showing the old device
    name which is no longer part of the file system.
    
    On Ubuntu 13.10, which has /etc/mtab as a plain file managed by mount
    and umount:
    
        # mkfs.btrfs /dev/sdb1
        # mount /dev/sdb1 /mnt/1
        # btrfs device add /dev/sdb2 /mnt/1
        # btrfs device delete /dev/sdb1 /mnt/1
        # sync
        # btrfs filesystem show /dev/sdb1
        # btrfs filesystem show /dev/sdb2
        Label: none  uuid: e47775a6-e5ad-4fb4-9ea4-1570aa5b4009
                Total devices 2 FS bytes used 28.00KB
                devid    2 size 2.00GB used 272.00MB path /dev/sdb2
    
        # fgrep btrfs /proc/mounts
        /dev/sdb2 /mnt/1 btrfs rw,relatime,space_cache 0 0
        # ls -l /etc/mtab
        -rw-r--r-- 1 root root 842 Apr 15 19:41 /etc/mtab
        # fgrep btrfs /etc/mtab
        /dev/sdb1 /mnt/1 btrfs rw 0 0
    
    This causes GParted to report /dev/sdb1 as busy and mounted at /mnt/1
    when it is no longer mounted.  This effects recent releases of Ubuntu,
    13.04, 13.10 and 14.04.
    
    Either /etc/mtab is a symlink and is identical to /proc/mounts or
    /etc/mtab is a plain file with wrong information.  Fix by not reading
    mounted file systems from /etc/mtab.
    
    However old distributions only contain 'rootfs' and '/dev/root' device
    names for the / (root) file system with '/dev/root' being a block device
    rather than a symlink to the true device.  For example from CentOS 5.x:
    
        # fgrep ' / ' /proc/mounts
        rootfs / rootfs rw 0 0
        /dev/root / ext3 rw,data=ordered 0 0
        # ls -l /dev/root
        brw------- 1 root root 8, 3 Jun  4  2013 /dev/root
    
    This prevents identification, and therefore busy detection, of the
    device containing the / (root) file system.  Used to read /etc/mtab to
    get the root file system device name.
    
        # fgrep ' / ' /etc/mtab
        /dev/sda3 / ext3 rw 0 0
        # ls -l /dev/sda3
        brw-r----- 1 root disk 8, 3 Jun  4  2013 /dev/sda3
    
    As per commit:
    
        409096f739118af95e3bff4484fdedb7885c97a2
        improved scanning for root mountpoint (/) ...
    
    but, as discussed above, this contains an out of date device name after
    the mounting device has been dynamically removed from a multi-device
    btrfs, thus identifying the wrong device as busy.  Instead fall back to
    reading mounted file systems from the output of the mount command, but
    only when required.
    
        # mount | fgrep ' / '
        /dev/sda3 on / type ext3 (rw)
    
    Bug #723842 - GParted resizes the wrong filesystem (does not pass the
                  devid to btrfs filesystem resize)

 include/GParted_Core.h |   11 ++++--
 src/GParted_Core.cc    |   92 ++++++++++++++++++++++++++++++++++++++----------
 2 files changed, 80 insertions(+), 23 deletions(-)
---
diff --git a/include/GParted_Core.h b/include/GParted_Core.h
index ded7835..b51271b 100644
--- a/include/GParted_Core.h
+++ b/include/GParted_Core.h
@@ -70,11 +70,14 @@ private:
        static void init_maps() ;
        void set_thread_status_message( Glib::ustring msg ) ;
        static void read_mountpoints_from_file( const Glib::ustring & filename,
-                                               std::map< Glib::ustring,
-                                               std::vector<Glib::ustring> > & map ) ;
+                                               std::map< Glib::ustring, std::vector<Glib::ustring> > & map ) 
;
+       static void add_node_and_mountpoint( std::map< Glib::ustring, std::vector<Glib::ustring> > & map,
+                                            Glib::ustring & node,
+                                            Glib::ustring & mountpoint ) ;
        static void read_mountpoints_from_file_swaps( const Glib::ustring & filename,
-                                                     std::map< Glib::ustring,
-                                                     std::vector<Glib::ustring> > & map ) ;
+                                                     std::map< Glib::ustring, std::vector<Glib::ustring> > & 
map ) ;
+       static bool have_rootfs_dev( std::map< Glib::ustring, std::vector<Glib::ustring> > & map ) ;
+       static void read_mountpoints_from_mount_command( std::map< Glib::ustring, std::vector<Glib::ustring> 
& map ) ;
        Glib::ustring get_partition_path( PedPartition * lp_partition ) ;
        void set_device_partitions( Device & device, PedDevice* lp_device, PedDisk* lp_disk ) ;
        GParted::FILESYSTEM get_filesystem( PedDevice* lp_device, PedPartition* lp_partition,
diff --git a/src/GParted_Core.cc b/src/GParted_Core.cc
index 755e9ba..16839c0 100644
--- a/src/GParted_Core.cc
+++ b/src/GParted_Core.cc
@@ -909,7 +909,20 @@ void GParted_Core::init_maps()
 
        read_mountpoints_from_file( "/proc/mounts", mount_info ) ;
        read_mountpoints_from_file_swaps( "/proc/swaps", mount_info ) ;
-       read_mountpoints_from_file( "/etc/mtab", mount_info ) ;
+
+       if ( ! have_rootfs_dev( mount_info ) )
+               //Old distributions only contain 'rootfs' and '/dev/root' device names for
+               //  the / (root) file system in /proc/mounts with '/dev/root' being a
+               //  block device rather than a symlink to the true device.  This prevents
+               //  identification, and therefore busy detection, of the device containing
+               //  the / (root) file system.  Used to read /etc/mtab to get the root file
+               //  system device name, but this contains an out of date device name after
+               //  the mounting device has been dynamically removed from a multi-device
+               //  btrfs, thus identifying the wrong device as busy.  Instead fall back
+               //  to reading mounted file systems from the output of the mount command,
+               //  but only when required.
+               read_mountpoints_from_mount_command( mount_info ) ;
+
        read_mountpoints_from_file( "/etc/fstab", fstab_info ) ;
        
        //sort the mount points and remove duplicates.. (no need to do this for fstab_info)
@@ -940,6 +953,7 @@ void GParted_Core::read_mountpoints_from_file(
        while ( (p = getmntent(fp)) != NULL )
        {
                Glib::ustring node = p->mnt_fsname ;
+               Glib::ustring mountpoint = p->mnt_dir ;
 
                Glib::ustring uuid = Utils::regexp_label( node, "^UUID=(.*)" ) ;
                if ( ! uuid .empty() )
@@ -950,28 +964,32 @@ void GParted_Core::read_mountpoints_from_file(
                        node = fs_info .get_path_by_label( label ) ;
 
                if ( ! node .empty() )
-               {
-                       Glib::ustring mountpoint = p->mnt_dir ;
+                       add_node_and_mountpoint( map, node, mountpoint ) ;
+       }
 
-                       //Only add node path(s) if mount point exists
-                       if ( file_test( mountpoint, Glib::FILE_TEST_EXISTS ) )
-                       {
-                               map[ node ] .push_back( mountpoint ) ;
+       endmntent( fp ) ;
+}
 
-                               //If node is a symbolic link (e.g., /dev/root)
-                               //  then find real path and add entry
-                               if ( file_test( node, Glib::FILE_TEST_IS_SYMLINK ) )
-                               {
-                                       char c_str[4096+1] ;
-                                       //FIXME: it seems realpath is very unsafe to use (manpage)...
-                                       if ( realpath( node .c_str(), c_str ) != NULL )
-                                               map[ c_str ] .push_back( mountpoint ) ;
-                               }
-                       }
+void GParted_Core::add_node_and_mountpoint(
+       std::map< Glib::ustring, std::vector<Glib::ustring> > & map,
+       Glib::ustring & node,
+       Glib::ustring & mountpoint )
+{
+       //Only add node path(s) if mount point exists
+       if ( file_test( mountpoint, Glib::FILE_TEST_EXISTS ) )
+       {
+               map[ node ] .push_back( mountpoint ) ;
+
+               //If node is a symbolic link (e.g., /dev/root)
+               //  then find real path and add entry too
+               if ( file_test( node, Glib::FILE_TEST_IS_SYMLINK ) )
+               {
+                       char c_str[4096+1] ;
+                       //FIXME: it seems realpath is very unsafe to use (manpage)...
+                       if ( realpath( node .c_str(), c_str ) != NULL )
+                               map[ c_str ] .push_back( mountpoint ) ;
                }
        }
-
-       endmntent( fp ) ;
 }
 
 void GParted_Core::read_mountpoints_from_file_swaps(
@@ -994,6 +1012,42 @@ void GParted_Core::read_mountpoints_from_file_swaps(
        }
 }
 
+//Return true only if the map contains a device name for the / (root) file system other
+//  than 'rootfs' and '/dev/root'
+bool GParted_Core::have_rootfs_dev( std::map< Glib::ustring, std::vector<Glib::ustring> > & map )
+{
+       std::map< Glib::ustring, std::vector<Glib::ustring> >::iterator iter_mp ;
+       for ( iter_mp = mount_info .begin() ; iter_mp != mount_info .end() ; iter_mp ++ )
+       {
+               if ( ! iter_mp ->second .empty() && iter_mp ->second[ 0 ] == "/" )
+               {
+                       if ( iter_mp ->first != "rootfs" && iter_mp ->first != "/dev/root" )
+                               return true ;
+               }
+       }
+       return false ;
+}
+
+void GParted_Core::read_mountpoints_from_mount_command(
+       std::map< Glib::ustring, std::vector<Glib::ustring> > & map )
+{
+       Glib::ustring output ;
+       Glib::ustring error ;
+       if ( ! Utils::execute_command( "mount", output, error, true ) )
+       {
+               std::vector<Glib::ustring> lines ;
+               Utils::split( output, lines, "\n") ;
+               for ( unsigned int i = 0 ; i < lines .size() ; i ++ )
+               {
+                       //Process line like "/dev/sda3 on / type ext4 (rw)"
+                       Glib::ustring node = Utils::regexp_label( lines[ i ], "^([^[:blank:]]+) on " ) ;
+                       Glib::ustring mountpoint = Utils::regexp_label( lines[ i ], "^[^[:blank:]]+ on 
([^[:blank:]]+) " ) ;
+                       if ( ! node .empty() )
+                               add_node_and_mountpoint( map, node, mountpoint ) ;
+               }
+       }
+}
+
 Glib::ustring GParted_Core::get_partition_path( PedPartition * lp_partition )
 {
        char * lp_path;  //we have to free the result of ped_partition_get_path()


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