[gparted] Detect Linux SWRaid members by querying mdadm (#756829)



commit 5f02bcf463ed61989e7f44926e7258d0ffcb2461
Author: Mike Fleetwood <mike fleetwood googlemail com>
Date:   Tue Oct 20 20:52:19 2015 +0100

    Detect Linux SWRaid members by querying mdadm (#756829)
    
    Detection of Linux SWRaid members currently fails in a number of cases:
    
    1)  Arrays which use metadata type 0.90 or 1.0 store the super block at
        the end of the partition.  So file system signatures in at least
        linear and mirrored arrays occur at the same offsets in the
        underlying partitions.  As libparted only recognises file systems
        this is what is detected, rather than an SWRaid member.
    
        # mdadm -E -s -v
        ARRAY /dev/md/1  level=raid1 metadata=1.0 num-devices=2 UUID=15224a42:c25bbcd9:15db6000:4e5fe53a 
name=chimney:1
           devices=/dev/sda1,/dev/sdb1
        ...
        # wipefs /dev/sda1
        offset               type
        ----------------------------------------------------------------
        0x438                ext4   [filesystem]
                             LABEL: chimney-boot
                             UUID:  10ab5f7d-7d8a-4171-8b6a-5e973b402501
    
        0x1fffe000           linux_raid_member   [raid]
                             LABEL: chimney:1
                             UUID:  15224a42-c25b-bcd9-15db-60004e5fe53a
    
        # parted /dev/sda print
        Model: ATA VBOX HARDDISK (scsi)
        Disk /dev/sda: 34.4GB
        Sector size (logical/physical): 512B/512B
        Partition Table: msdos
    
        Number  Start   End     Size    Type      File system  Flags
         1      1049kB  538MB   537MB   primary   ext4         boot, raid
        ...
    
    2)  Again with metadata type 0.90 or 1.0 arrays blkid may report the
        contained file system instead of an SWRaid member.  Have a single
        example of this configuration with a mirrored array containing the
        /boot file system.  Blkid reports one member as ext4 and the other as
        SWRaid!
    
        # blkid | egrep 'sd[ab]1'
        /dev/sda1: UUID="10ab5f7d-7d8a-4171-8b6a-5e973b402501" TYPE="ext4" LABEL="chimney-boot"
        /dev/sdb1: UUID="15224a42-c25b-bcd9-15db-60004e5fe53a" 
UUID_SUB="0a095e45-9360-1b17-0ad1-1fe369e22b98" LABEL="chimney:1" TYPE="linux_raid_member"
    
        Bypassing the blkid cache gets the correct result.
    
        # blkid -c /dev/null /dev/sda1
        /dev/sda1: UUID="15224a42-c25b-bcd9-15db-60004e5fe53a" 
UUID_SUB="d0460f90-d11a-e80a-ee1c-3d104dae7e5d" LABEL="chimney:1" TYPE="linux_raid_member"
    
        However this can't be used because if a user has a floppy configured
        in the BIOS but no floppy attached, GParted will wait for minutes as
        the kernel tries to access non-existent hardware on behalf of the
        blkid query.  See commit:
            18f863151c82934fe0a980853cc3deb1e439bec2
            Fix long scan problem when BIOS floppy setting incorrect
    
    3)  Old versions of blkid don't recognise SWRaid members at all so always
        report the file system when found.  Occurs with blkid v1.0 on
        RedHat / CentOS 5.
    
    The only way I can see how to fix all these cases is to use the mdadm
    command to query the configured arrays.  Then use this information for
    first choice when detecting partition content, making the order: SWRaid
    members, libparted, blkid and internal.
    
    GParted shell wrapper already creates temporary blank udev rules to
    prevent Linux Software RAID arrays being automatically started when
    GParted refreshes its device information[1].  However an administrator
    could manually stop or start arrays or change their configuration
    between refreshes so GParted must load this information every refresh.
    On my desktop with 4 internal hard drives and 3 testing Linux Software
    RAID arrays, running mdadm adds between 0.20 and 0.30 seconds to the
    device refresh time.
    
    [1] a255abf3432ad106fac9c766f0816ada20be8e42
        Prevent GParted starting stopped Linux Software RAID arrays (#709640)
    
    Bug 756829 - SWRaid member detection enhancements

 include/Makefile.am   |    1 +
 include/SWRaid_Info.h |   49 +++++++++++++++++++
 po/POTFILES.in        |    1 +
 src/GParted_Core.cc   |   29 ++++++++----
 src/Makefile.am       |    1 +
 src/SWRaid_Info.cc    |  124 +++++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 195 insertions(+), 10 deletions(-)
---
diff --git a/include/Makefile.am b/include/Makefile.am
index 4b00f1c..4c308d9 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -38,6 +38,7 @@ EXTRA_DIST = \
        Partition.h                     \
        PipeCapture.h                   \
        Proc_Partitions_Info.h          \
+       SWRaid_Info.h                   \
        TreeView_Detail.h               \
        Utils.h                         \
        Win_GParted.h                   \
diff --git a/include/SWRaid_Info.h b/include/SWRaid_Info.h
new file mode 100644
index 0000000..e59275e
--- /dev/null
+++ b/include/SWRaid_Info.h
@@ -0,0 +1,49 @@
+/* Copyright (C) 2015 Mike Fleetwood
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/* SWRaid_Info
+ *
+ * Cache of information about Linux Software RAID arrays so that the
+ * mdadm command only needs to be executed once per refresh.
+ */
+
+#ifndef GPARTED_SWRAID_INFO_H
+#define GPARTED_SWRAID_INFO_H
+
+#include <glibmm/ustring.h>
+#include <vector>
+
+namespace GParted
+{
+
+class SWRaid_Info
+{
+public:
+       static void load_cache();
+       static bool is_member( const Glib::ustring & member_path );
+
+private:
+       static void set_command_found();
+       static void load_swraid_info_cache();
+
+       static bool mdadm_found;
+       static std::vector<Glib::ustring> swraid_info_cache;
+};
+
+}//GParted
+
+#endif /* GPARTED_SWRAID_INFO_H */
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 38bf0f6..29741cf 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -31,6 +31,7 @@ src/OperationLabelFileSystem.cc
 src/OperationNamePartition.cc
 src/OperationResizeMove.cc
 src/Partition.cc
+src/SWRaid_Info.cc
 src/TreeView_Detail.cc
 src/Utils.cc
 src/Win_GParted.cc
diff --git a/src/GParted_Core.cc b/src/GParted_Core.cc
index ee97efa..59c7c15 100644
--- a/src/GParted_Core.cc
+++ b/src/GParted_Core.cc
@@ -29,6 +29,7 @@
 #include "../include/OperationLabelFileSystem.h"
 #include "../include/OperationNamePartition.h"
 #include "../include/Proc_Partitions_Info.h"
+#include "../include/SWRaid_Info.h"
 
 #include "../include/btrfs.h"
 #include "../include/exfat.h"
@@ -165,6 +166,7 @@ void GParted_Core::set_devices_thread( std::vector<Device> * pdevices )
        DMRaid dmraid( true ) ;    //Refresh cache of dmraid device information
        LVM2_PV_Info::clear_cache();            // Cache automatically loaded if and when needed
        btrfs::clear_cache();                   // Cache incrementally loaded if and when needed
+       SWRaid_Info::load_cache();
 
        init_maps() ;
        
@@ -1516,10 +1518,21 @@ FILESYSTEM GParted_Core::detect_filesystem( PedDevice * lp_device, PedPartition
        static Glib::ustring luks_unsupported = _("Linux Unified Key Setup encryption is not yet supported.");
 
        if ( lp_partition )
-       {
+               // Will query partition using methods: (Q1) SWRaid, (Q2) libparted,
+               // (Q3) blkid, (Q4) internal
                path = get_partition_path( lp_partition );
+       else
+               // Will query whole disk device using methods: (Q1) SWRaid, (Q3) blkid,
+               // (Q4) internal
+               path = lp_device->path;
 
-               // Standard libparted file system detection
+       // (Q1) Linux Software RAID member detection
+       if ( SWRaid_Info::is_member( path ) )
+               return FS_LINUX_SWRAID;
+
+       if ( lp_partition )
+       {
+               // (Q2) Standard libparted file system detection
                if ( lp_partition->fs_type )
                {
                        fsname = lp_partition->fs_type->name;
@@ -1531,14 +1544,10 @@ FILESYSTEM GParted_Core::detect_filesystem( PedDevice * lp_device, PedPartition
                                fsname = temp;
                }
        }
-       else
-       {
-               // Querying whole disk device instead of libparted PedPartition
-               path = lp_device->path;
-       }
 
-       //FS_Info (blkid) file system detection because current libparted (v2.2) does not
-       //  appear to detect file systems for sector sizes other than 512 bytes.
+       // (Q3) FS_Info (blkid) file system detection
+       // Originally just because libparted v2.2 didn't appear to detect file system for
+       // sector sizes other than 512 bytes.  Now to also detect what libparted doesn't.
        if ( fsname.empty() )
        {
                //TODO: blkid does not return anything for an "extended" partition.  Need to handle this 
somehow
@@ -1609,7 +1618,7 @@ FILESYSTEM GParted_Core::detect_filesystem( PedDevice * lp_device, PedPartition
                        return FS_ZFS;
        }
 
-       // Fallback to GParted simple internal file system detection
+       // (Q4) Fallback to GParted simple internal file system detection
        FILESYSTEM fstype = detect_filesystem_internal( lp_device, lp_partition );
        if ( fstype == FS_LUKS )
                messages.push_back( luks_unsupported );
diff --git a/src/Makefile.am b/src/Makefile.am
index cd11213..0d9cd36 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -49,6 +49,7 @@ gpartedbin_SOURCES = \
        Partition.cc                    \
        PipeCapture.cc                  \
        Proc_Partitions_Info.cc         \
+       SWRaid_Info.cc                  \
        TreeView_Detail.cc              \
        Utils.cc                        \
        Win_GParted.cc                  \
diff --git a/src/SWRaid_Info.cc b/src/SWRaid_Info.cc
new file mode 100644
index 0000000..5a36791
--- /dev/null
+++ b/src/SWRaid_Info.cc
@@ -0,0 +1,124 @@
+/* Copyright (C) 2015 Mike Fleetwood
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "../include/SWRaid_Info.h"
+
+#include "../include/Utils.h"
+
+#include <glibmm/ustring.h>
+
+namespace GParted
+{
+
+// Data model:
+// mdadm_found       - Is the "mdadm" command available?
+// swraid_info_cache - Vector of /dev entries of members in Linux Software RAID arrays.
+//                     E.g.
+//                     ["/dev/sda1", "/dev/sda2", ...]
+
+// Initialise static data elements
+bool SWRaid_Info::mdadm_found = false;
+std::vector<Glib::ustring> SWRaid_Info::swraid_info_cache;
+
+void SWRaid_Info::load_cache()
+{
+       set_command_found();
+       load_swraid_info_cache();
+}
+
+bool SWRaid_Info::is_member( const Glib::ustring & member_path )
+{
+       for ( unsigned int i = 0 ; i < swraid_info_cache.size() ; i ++ )
+               if ( member_path == swraid_info_cache[i] )
+                       return true;
+
+       return false;
+}
+
+// Private methods
+
+void SWRaid_Info::set_command_found()
+{
+       mdadm_found = ! Glib::find_program_in_path( "mdadm" ).empty();
+}
+
+void SWRaid_Info::load_swraid_info_cache()
+{
+       Glib::ustring output, error;
+
+       swraid_info_cache.clear();
+
+       if ( ! mdadm_found )
+               return;
+
+       // Load SWRaid members into the cache.
+       Glib::ustring cmd = "mdadm --examine --scan --verbose";
+       if ( ! Utils::execute_command( cmd, output, error, true ) )
+       {
+               // Extract member /dev entries from Linux Software RAID arrays only,
+               // excluding IMSM and DDF arrays.  Example output:
+               //     ARRAY metadata=imsm UUID=...
+               //        devices=/dev/sdd,/dev/sdc,/dev/md/imsm0
+               //     ARRAY /dev/md/MyRaid container=...
+               //
+               //     ARRAY /dev/md/1  level=raid1 metadata=1.0 num-devices=2 UUID=...
+               //        devices=/dev/sda1,/dev/sdb1
+               //     ARRAY /dev/md5 level=raid1 num-devices=2 UUID=...
+               //        devices=/dev/sda6,/dev/sdb6
+               std::vector<Glib::ustring> lines;
+               Utils::split( output, lines, "\n" );
+               enum LINE_TYPE
+               {
+                       LINE_TYPE_OTHER   = 0,
+                       LINE_TYPE_ARRAY   = 1,
+                       LINE_TYPE_DEVICES = 2
+               };
+               LINE_TYPE line_type = LINE_TYPE_OTHER;
+               for ( unsigned int i = 0 ; i < lines.size() ; i ++ )
+               {
+                       Glib::ustring metadata_type;
+                       if ( lines[i].substr( 0, 6 ) == "ARRAY " )
+                       {
+                               line_type = LINE_TYPE_ARRAY;
+                               Glib::ustring metadata_type = Utils::regexp_label( lines[i],
+                                                                                  "metadata=([[:graph:]]+)" 
);
+                               // Mdadm with these flags doesn't seem to print the
+                               // metadata tag for 0.90 version arrays.  Accept no tag
+                               // (or empty version) as well as "0.90".
+                               if ( metadata_type != ""    && metadata_type != "0.90" &&
+                                    metadata_type != "1.0" && metadata_type != "1.1"  &&
+                                    metadata_type != "1.2"                               )
+                                       // Skip mdadm reported non-Linux Software RAID arrays
+                                       line_type = LINE_TYPE_OTHER;
+                       }
+                       else if ( line_type == LINE_TYPE_ARRAY                       &&
+                                 lines[i].find( "devices=" ) != Glib::ustring::npos    )
+                       {
+                               line_type = LINE_TYPE_DEVICES;
+                               Glib::ustring devices_str = Utils::regexp_label( lines[i],
+                                                                                "devices=([[:graph:]]+)" );
+                               std::vector<Glib::ustring> devices;
+                               Utils::split( devices_str, devices, "," );
+                               for ( unsigned int j = 0 ; j < devices.size() ; j ++ )
+                                       swraid_info_cache.push_back( devices[j] );
+                       }
+                       else
+                               line_type = LINE_TYPE_OTHER;
+               }
+       }
+}
+
+} //GParted


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