[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