[parted-devel] [PATCH] libparted: fix reading partition start sector from kernel

Petr Uzel petr.uzel at suse.cz
Fri Nov 11 10:22:18 UTC 2011


The HDIO_GETGEO ioctl() does not work for querying the kernel about
partition start sector offset, because the 'start' field of struct
hd_geometry is represented as unsigned long. On 32bit systems, this
imposes a limit of 2TiB, assuming the device uses 512 bytes logical
sectors.  Therefore, the start field in struct hd_geometry is wrong if
the partition starts beyond 2 TiB limit.

Work around this limitation by reading the partition starting offset
from sysfs, while still falling back to HDIO_GETGEO ioctl if the sysfs
file is not available.

Another advantage of this approach is that now we can get the starting
sector of a partition even if the device does not support HDIO_GETGEO
ioctl(), which is the case with e.g. partitionable loop devices.

* libparted/arch/linux.c (_sysfs_ull_entry_from_part): New function.
(_kernel_get_partition_start_sector): New function.
(_disk_sync_part_table): Do not use HDIO_GETGEO ioctl, but call
_kernel_get_partition_start_sector().

Addresses: http://osdir.com/ml/bug-parted-gnu/2011-10/msg00016.html
Signed-off-by: Petr Uzel <petr.uzel at suse.cz>
---
 libparted/arch/linux.c |   69 +++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 65 insertions(+), 4 deletions(-)

diff --git a/libparted/arch/linux.c b/libparted/arch/linux.c
index ab3d904..9e4dd45 100644
--- a/libparted/arch/linux.c
+++ b/libparted/arch/linux.c
@@ -2450,6 +2450,67 @@ _sysfs_int_entry_from_dev(PedDevice const* dev, const char *entry, int *val)
         return ok;
 }
 
+
+/* Read the unsigned long long from /sys/block/DEV_BASE/PART_BASE/ENTRY
+   and set *VAL to that value, where DEV_BASE is the last component of path to
+   block device corresponding to PART and PART_BASE is the sysfs name of PART.
+   Upon success, return true. Otherwise, return false. */
+static bool
+_sysfs_ull_entry_from_part(PedPartition const* part, const char *entry, unsigned long long *val)
+{
+        char        path[128];
+        char        *part_name = linux_partition_get_path(part);
+        if (!part_name)
+            return false;
+
+        int r = snprintf(path, sizeof(path), "/sys/block/%s/%s/%s",
+                last_component(part->disk->dev->path),
+                last_component(part_name), entry);
+        free(part_name);
+        if (r < 0 || r >= sizeof(path))
+                return false;
+
+        FILE *fp = fopen(path, "r");
+        if (!fp)
+                return false;
+
+        bool ok = fscanf(fp, "%llu", val) == 1;
+        fclose(fp);
+
+        return ok;
+}
+
+
+/* Get the starting sector of a partition PART within a block device and
+ * store the result to *VAL. First check sysfs and then use HDIO_GETGEO
+ * ioctl as fallback. Upon success, return true. Otherwise, return false. */
+static bool
+_kernel_get_partition_start_sector(PedPartition const *part, unsigned long long *val)
+{
+        PED_ASSERT(part);
+        PED_ASSERT(val);
+
+        char *dev_name = linux_partition_get_path (part);
+        if (!dev_name)
+                return false;
+
+        int ok = _sysfs_ull_entry_from_part (part, "start", val);
+        if (!ok) {
+                struct hd_geometry geom;
+                int fd = open (dev_name, O_RDONLY);
+                if (fd != -1 && ioctl (fd, HDIO_GETGEO, &geom)) {
+                        *val = geom.start;
+                        ok = true;
+        }
+        if (fd != -1)
+                close (fd);
+        }
+
+        free (dev_name);
+        return ok;
+}
+
+
 /* Return the maximum number of partitions that the loopback device can hold.
    First, check the loop-module-exported max_part parameter (since linux-3.0).
    If that is not available, fall back to checking ext_range, which seems to
@@ -2570,16 +2631,16 @@ _disk_sync_part_table (PedDisk* disk)
                 const PedPartition *part = ped_disk_get_partition (disk, i);
                 if (part) {
                         if (!ok[i - 1] && errnums[i - 1] == EBUSY) {
-                                struct hd_geometry geom;
                                 unsigned long long length = 0;
+                                unsigned long long start = 0;
                                 /* get start and length of existing partition */
                                 char *dev_name = _device_get_part_path (disk->dev, i);
                                 if (!dev_name)
                                         goto cleanup;
                                 int fd = open (dev_name, O_RDONLY);
                                 if (fd == -1
-				    || ioctl (fd, HDIO_GETGEO, &geom)
-				    || ioctl (fd, BLKGETSIZE64, &length)) {
+                                   || !_kernel_get_partition_start_sector(part, &start)
+                                   || ioctl (fd, BLKGETSIZE64, &length)) {
                                         ped_exception_throw (
                                                              PED_EXCEPTION_BUG,
                                                              PED_EXCEPTION_CANCEL,
@@ -2593,7 +2654,7 @@ _disk_sync_part_table (PedDisk* disk)
                                 free (dev_name);
                                 length /= disk->dev->sector_size;
                                 close (fd);
-                                if (geom.start == part->geom.start
+                                if (start == part->geom.start
 				    && length == part->geom.length)
                                         ok[i - 1] = 1;
                                 /* If the new partition is unchanged and the
-- 
1.7.7




More information about the parted-devel mailing list