From 71b36d17f019c9e2cf218351520d5b55a6c2d479 Mon Sep 17 00:00:00 2001 From: "Jason J. Herne" Date: Wed, 29 Apr 2020 13:32:10 -0400 Subject: [PATCH] zipl: Fix NVMe partition and base device detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Copying the SCSI scheme wasn't good enough due to how major/minor numbers are assigned by the blkext driver. We now query the relevant information via sysfs. Signed-off-by: Jason J. Herne Reviewed-by: Peter Oberparleiter Signed-off-by: Jan Höppner --- zipl/src/disk.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 93 insertions(+), 2 deletions(-) diff --git a/zipl/src/disk.c b/zipl/src/disk.c index 8abf668f..5baeaeb1 100644 --- a/zipl/src/disk.c +++ b/zipl/src/disk.c @@ -89,6 +89,88 @@ disk_determine_dasd_type(struct disk_info *data, return 0; } +static int blkext_get_partnum(dev_t dev) +{ + char path[PATH_MAX], *buf; + int dev_major, dev_minor, partnum = -1; + + dev_major = major(dev); + dev_minor = minor(dev); + snprintf(path, PATH_MAX, "/sys/dev/block/%d:%d/partition", + dev_major, dev_minor); + + if (misc_read_special_file(path, &buf, NULL, 1)) { + error_text("Could not read from path '%s'", path); + return -1; + } + + partnum = atoi(buf); + free(buf); + if (partnum < 0) { + error_text("Bad partition number in '%s'", path); + return -1; + } + + return partnum; +} + +static int blkext_is_base_device(dev_t dev) +{ + int dev_major, dev_minor; + char path[PATH_MAX]; + struct stat stats; + + dev_major = major(dev); + dev_minor = minor(dev); + + snprintf(path, PATH_MAX, "/sys/dev/block/%d:%d/partition", + dev_major, dev_minor); + return (stat(path, &stats)); +} + +static int blkext_get_base_dev(dev_t dev, dev_t *base_dev) +{ + int base_major, base_minor; + char dev_path[PATH_MAX], base_path[PATH_MAX]; + char *temp_path, *buf; + + misc_asprintf(&temp_path, "/sys/dev/block/%d:%d", major(dev), minor(dev)); + if (!realpath(temp_path, dev_path)) { + error_reason(strerror(errno)); + error_text("Could not resolve link %s", temp_path); + free(temp_path); + return -1; + } + free(temp_path); + + misc_asprintf(&temp_path, "%s/..", dev_path); + if (!realpath(temp_path, base_path)) { + error_reason(strerror(errno)); + error_text("Could not resolve path %s", temp_path); + free(temp_path); + return -1; + } + free(temp_path); + + misc_asprintf(&temp_path, "%s/dev", base_path); + if (misc_read_special_file(temp_path, &buf, NULL, 1)) { + error_text("Could not read from path '%s'", temp_path); + free(temp_path); + return -1; + } + free(temp_path); + + if (sscanf(buf, "%i:%i", &base_major, &base_minor) != 2) { + error_text("Could not parse major:minor from string '%s'", buf); + free(buf); + return -1; + } + + free(buf); + *base_dev = makedev(base_major, base_minor); + return 0; +} + /* Return non-zero for ECKD type. */ int disk_is_eckd(disk_type_t type) @@ -409,8 +491,17 @@ disk_get_info(const char* device, struct job_target_data* target, } else if (strcmp(data->drv_name, "blkext") == 0) { data->devno = -1; data->type = disk_type_scsi; - data->partnum = stats.st_rdev & SCSI_PARTN_MASK; - data->device = stats.st_rdev & ~SCSI_PARTN_MASK; + + if (blkext_is_base_device(stats.st_rdev)) { + data->device = stats.st_rdev; + data->partnum = 0; + } else { + if (blkext_get_base_dev(stats.st_rdev, &data->device)) + goto out_close; + data->partnum = blkext_get_partnum(stats.st_rdev); + if (data->partnum == -1) + goto out_close; + } } else { /* Driver name is unknown */ error_reason("Unsupported device driver '%s'", data->drv_name);