Read partition names from /proc/partitions too (#131)

GParted already always reads /proc/partitions for whole disk device
names no matter whether it uses whole disk devices named on the command
line, from /proc/partitions or from libparted.  As /proc/partitions
lists all the block devices that the kernel knows about, and therefore
all the possible ones blkid could probe, so use it to provide partition
names and device to partition mapping.  See code comments for more
details about the assumptions the /proc/partition parsing code makes and
the fact that these are confirmed by examining the Linux kernel source.

This commit just adds debugging to print the existing vector of
validated devices GParted shows in the UI and the vector with all
partitions added, ready for but not yet passed to blkid.
    # ./gpartedbin
    ...
    DEBUG: device_paths=["/dev/sda","/dev/sdb"]
    DEBUG: device_and_partition_paths=["/dev/sda","/dev/sda1","/dev/sda2","/dev/sdb","/dev/sdb1"]

Also demonstrating that this continues to support named devices,
including file system image files [1].
    # truncate -s 256M /tmp/ext4.img
    # mkfs.ext4 /tmp/ext4.img
    # ./gpartedbin /dev/sda /tmp/ext4.img
    ...
    DEBUG: device_paths=["/dev/sda","/tmp/ext4.img"]
    DEBUG: device_and_partition_paths=["/dev/sda","/dev/sda1","/dev/sda2","/tmp/ext4.img"]

[1] e8f0504b13
    Make sure that FS_Info cache is loaded for all named paths (#787181)

Closes #131 - GParted hangs when non-named device is hung
This commit is contained in:
Mike Fleetwood 2021-01-23 20:56:51 +00:00 committed by Curtis Gedak
parent 52930f30ae
commit 884cd5a352
3 changed files with 127 additions and 3 deletions

View File

@ -24,9 +24,13 @@
#ifndef GPARTED_PROC_PARTITIONS_INFO_H #ifndef GPARTED_PROC_PARTITIONS_INFO_H
#define GPARTED_PROC_PARTITIONS_INFO_H #define GPARTED_PROC_PARTITIONS_INFO_H
#include "BlockSpecial.h"
#include <glibmm/ustring.h> #include <glibmm/ustring.h>
#include <vector> #include <vector>
namespace GParted namespace GParted
{ {
@ -35,13 +39,18 @@ class Proc_Partitions_Info
public: public:
static void load_cache(); static void load_cache();
static const std::vector<Glib::ustring> & get_device_paths(); static const std::vector<Glib::ustring> & get_device_paths();
static std::vector<Glib::ustring> get_device_and_partition_paths_for(
const std::vector<Glib::ustring>& device_paths);
private: private:
static void initialize_if_required(); static void initialize_if_required();
static void load_proc_partitions_info_cache(); static void load_proc_partitions_info_cache();
static bool is_whole_disk_device_name(const Glib::ustring& name); static bool is_whole_disk_device_name(const Glib::ustring& name);
static std::vector<Glib::ustring> get_partition_paths_for(const Glib::ustring& name);
static bool is_partition_of_device(const Glib::ustring& partname, const Glib::ustring& devname);
static bool proc_partitions_info_cache_initialized ; static bool proc_partitions_info_cache_initialized ;
static std::vector<BlockSpecial> all_entries_cache;
static std::vector<Glib::ustring> device_paths_cache ; static std::vector<Glib::ustring> device_paths_cache ;
}; };

View File

@ -247,6 +247,18 @@ void GParted_Core::set_devices_thread( std::vector<Device> * pdevices )
} }
} }
std::cout << "DEBUG: device_paths=[";
for (unsigned int i = 0; i < device_paths.size(); i++)
std::cout << (i>0?",":"") << "\"" << device_paths[i] << "\"";
std::cout << "]" << std::endl;
const std::vector<Glib::ustring>& device_and_partition_paths =
Proc_Partitions_Info::get_device_and_partition_paths_for(device_paths);
std::cout << "DEBUG: device_and_partition_paths=[";
for (unsigned int i = 0; i < device_and_partition_paths.size(); i++)
std::cout << (i>0?",":"") << "\"" << device_and_partition_paths[i] << "\"";
std::cout << "]" << std::endl;
// Initialise and load caches needed for content discovery. // Initialise and load caches needed for content discovery.
FS_Info::load_cache_for_paths(device_paths); FS_Info::load_cache_for_paths(device_paths);
Mount_Info::load_cache(); Mount_Info::load_cache();

View File

@ -22,14 +22,25 @@
#include <vector> #include <vector>
#include <fstream> #include <fstream>
#include <stdio.h> #include <stdio.h>
#include <ctype.h>
namespace GParted namespace GParted
{ {
//Initialize static data elements //Initialize static data elements
bool Proc_Partitions_Info::proc_partitions_info_cache_initialized = false ; bool Proc_Partitions_Info::proc_partitions_info_cache_initialized = false ;
// Cache of all entries from /proc/partitions.
// E.g. [BlockSpecial("/dev/sda"), BlockSpecial("/dev/sda1"), BlockSpecial("/dev/sda2"), ...]
std::vector<BlockSpecial> Proc_Partitions_Info::all_entries_cache;
// Cache of device names read from /proc/partitions that GParted will present by default
// as partitionable.
// E.g. ["/dev/sda", "/dev/sdb", "/dev/md1"]
std::vector<Glib::ustring> Proc_Partitions_Info::device_paths_cache ; std::vector<Glib::ustring> Proc_Partitions_Info::device_paths_cache ;
void Proc_Partitions_Info::load_cache() void Proc_Partitions_Info::load_cache()
{ {
load_proc_partitions_info_cache(); load_proc_partitions_info_cache();
@ -42,6 +53,23 @@ const std::vector<Glib::ustring> & Proc_Partitions_Info::get_device_paths()
return device_paths_cache; return device_paths_cache;
} }
// E.g. ["/dev/sda", "/tmp/fs.img"] -> ["/dev/sda", "/dev/sda1", "/dev/sda2", "/tmp/fs.img"]
std::vector<Glib::ustring> Proc_Partitions_Info::get_device_and_partition_paths_for(
const std::vector<Glib::ustring>& device_paths)
{
initialize_if_required();
std::vector<Glib::ustring> all_paths;
for (unsigned int i = 0; i < device_paths.size(); i++)
{
all_paths.push_back(device_paths[i]);
const std::vector<Glib::ustring>& part_paths = get_partition_paths_for(device_paths[i]);
all_paths.insert(all_paths.end(), part_paths.begin(), part_paths.end());
}
return all_paths;
}
// Private Methods // Private Methods
void Proc_Partitions_Info::initialize_if_required() void Proc_Partitions_Info::initialize_if_required()
@ -53,9 +81,11 @@ void Proc_Partitions_Info::initialize_if_required()
} }
} }
void Proc_Partitions_Info::load_proc_partitions_info_cache() void Proc_Partitions_Info::load_proc_partitions_info_cache()
{ {
device_paths_cache .clear() ; all_entries_cache.clear();
device_paths_cache.clear();
std::ifstream proc_partitions( "/proc/partitions" ) ; std::ifstream proc_partitions( "/proc/partitions" ) ;
if ( proc_partitions ) if ( proc_partitions )
@ -69,13 +99,15 @@ void Proc_Partitions_Info::load_proc_partitions_info_cache()
if ( name == "" ) if ( name == "" )
continue; continue;
// Pre-populate BlockSpecial cache to avoid stat() call for all // Pre-populate BlockSpecial cache to avoid stat() call and save
// entries from /proc/partitions. // all entries from /proc/partitions for device to partition
// mapping.
unsigned long maj; unsigned long maj;
unsigned long min; unsigned long min;
if ( sscanf( line.c_str(), "%lu %lu", &maj, &min ) != 2 ) if ( sscanf( line.c_str(), "%lu %lu", &maj, &min ) != 2 )
continue; continue;
BlockSpecial::register_block_special( "/dev/" + name, maj, min ); BlockSpecial::register_block_special( "/dev/" + name, maj, min );
all_entries_cache.push_back(BlockSpecial("/dev/" + name));
// Save recognised whole disk device names for later returning as // Save recognised whole disk device names for later returning as
// the default GParted partitionable devices. // the default GParted partitionable devices.
@ -126,4 +158,75 @@ bool Proc_Partitions_Info::is_whole_disk_device_name(const Glib::ustring& name)
} }
// Return all partitions for a whole disk device.
// E.g. get_partitions_for("/dev/sda") -> ["/dev/sda1", "/dev/sda2"]
std::vector<Glib::ustring> Proc_Partitions_Info::get_partition_paths_for(const Glib::ustring& device)
{
// Assumes that entries from /proc/partitions are:
// 1. Always ordered with whole device followed by any partitions for that device.
// 2. Partition names are the whole device names appended with optional "p" and
// partition number.
// This is confirmed by checking Linux kernel block/genhd.c:show_partition()
// function which prints the content of /proc/partitions.
// https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/block/genhd.c?h=v5.10.12#n1175
std::vector<Glib::ustring> partitions;
BlockSpecial bs = BlockSpecial(device);
// Find matching whole disk device entry from /proc/partitions cache.
unsigned int i = 0;
bool found_device = false;
for (; i < all_entries_cache.size(); i++)
{
if (bs == all_entries_cache[i])
{
found_device = true;
break;
}
}
if (! found_device)
return partitions; // Empty vector
// Find following partition entries from /proc/partitions cache.
for (i++; i < all_entries_cache.size(); i++)
{
if (is_partition_of_device(all_entries_cache[i].m_name, bs.m_name))
partitions.push_back(all_entries_cache[i].m_name);
else
// No more partitions for this whole disk device.
break;
}
return partitions;
}
// True if and only if devname is a leading substring of partname and the remainder
// consists of optional "p" and digits.
// E.g. ("/dev/sda1", "/dev/sda") -> true ("/dev/md1p2", "/dev/md1") -> true
bool Proc_Partitions_Info::is_partition_of_device(const Glib::ustring& partname, const Glib::ustring& devname)
{
// Linux kernel function block/genhd.c:disk_name() formats device and partition
// names written to /proc/partitions.
// https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/block/genhd.c?h=v5.10.12#n73
size_t devname_len = devname.length();
if (partname.compare(0, devname_len, devname) != 0)
return false;
size_t i = devname_len;
if (partname.length() <= i)
return false;
if (partname[i] == 'p')
i ++;
if (partname.length() <= i)
return false;
for (i++; i < partname.length(); i++)
{
if (! isdigit(partname[i]))
return false;
}
return true;
}
} //GParted } //GParted