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:

    409096f739
    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)
This commit is contained in:
Mike Fleetwood 2014-04-14 15:21:55 +01:00 committed by Curtis Gedak
parent d47783eff8
commit 4b63e46a4e
2 changed files with 81 additions and 24 deletions

View File

@ -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,

View File

@ -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,30 +964,34 @@ void GParted_Core::read_mountpoints_from_file(
node = fs_info .get_path_by_label( label ) ;
if ( ! node .empty() )
{
Glib::ustring mountpoint = p->mnt_dir ;
//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
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 ) ;
}
}
}
add_node_and_mountpoint( map, node, mountpoint ) ;
}
endmntent( fp ) ;
}
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 ) ;
}
}
}
void GParted_Core::read_mountpoints_from_file_swaps(
const Glib::ustring & filename,
std::map< Glib::ustring, std::vector<Glib::ustring> > & map )
@ -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()