From: Karel Zak Date: Mon, 11 Jul 2022 14:02:30 +0200 Subject: [PATCH 10/24] libfdisk: make scripts portable between different sector sizes Fixes: https://github.com/util-linux/util-linux/issues/1744 Signed-off-by: Karel Zak --- libfdisk/src/script.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/libfdisk/src/script.c b/libfdisk/src/script.c index d93b981..b620eda 100644 --- a/libfdisk/src/script.c +++ b/libfdisk/src/script.c @@ -63,6 +63,8 @@ struct fdisk_script { size_t nlines; struct fdisk_label *label; + unsigned long sector_size; /* as defined by script */ + unsigned int json : 1, /* JSON output */ force_label : 1; /* label: specified */ }; @@ -806,6 +808,19 @@ static int parse_line_header(struct fdisk_script *dp, char *s) return -EINVAL; /* unknown label name */ dp->force_label = 1; + } else if (strcmp(name, "sector-size") == 0) { + uint64_t x = 0; + + if (ul_strtou64(value, &x, 10) != 0) + return -EINVAL; + if (x > ULONG_MAX || x % 512) + return -ERANGE; + dp->sector_size = (unsigned long) x; + + if (dp->cxt && dp->sector_size && dp->cxt->sector_size + && dp->sector_size != dp->cxt->sector_size) + fdisk_warnx(dp->cxt, _("The script and device sector size differ; the sizes will be recalculated to match the device.")); + } else if (strcmp(name, "unit") == 0) { if (strcmp(value, "sectors") != 0) return -EINVAL; /* only "sectors" supported */ @@ -966,6 +981,27 @@ static int skip_optional_sign(char **str) return 0; } +static int recount_script2device_sectors(struct fdisk_script *dp, uint64_t *num) +{ + if (!dp->cxt || + !dp->sector_size || + !dp->cxt->sector_size) + return 0; + + if (dp->sector_size > dp->cxt->sector_size) + *num *= (dp->sector_size / dp->cxt->sector_size); + + else if (dp->sector_size < dp->cxt->sector_size) { + uint64_t x = dp->cxt->sector_size / dp->sector_size; + + if (*num % x) + return -EINVAL; + *num /= x; + } + + return 0; +} + static int parse_start_value(struct fdisk_script *dp, struct fdisk_partition *pa, char **str) { char *tk; @@ -997,7 +1033,14 @@ static int parse_start_value(struct fdisk_script *dp, struct fdisk_partition *pa goto done; } num /= dp->cxt->sector_size; + } else { + rc = recount_script2device_sectors(dp, &num); + if (rc) { + fdisk_warnx(dp->cxt, _("Can't recalculate partition start to the device sectors")); + goto done; + } } + fdisk_partition_set_start(pa, num); pa->movestart = sign == '-' ? FDISK_MOVE_DOWN : @@ -1046,8 +1089,15 @@ static int parse_size_value(struct fdisk_script *dp, struct fdisk_partition *pa, goto done; } num /= dp->cxt->sector_size; - } else /* specified as number of sectors */ + } else { + /* specified as number of sectors */ fdisk_partition_size_explicit(pa, 1); + rc = recount_script2device_sectors(dp, &num); + if (rc) { + fdisk_warnx(dp->cxt, _("Can't recalculate partition size to the device sectors")); + goto done; + } + } fdisk_partition_set_size(pa, num); pa->resize = sign == '-' ? FDISK_RESIZE_REDUCE : @@ -1492,6 +1542,25 @@ int fdisk_apply_script_headers(struct fdisk_context *cxt, struct fdisk_script *d DBG(SCRIPT, ul_debugobj(dp, "applying script headers")); fdisk_set_script(cxt, dp); + if (dp->sector_size && dp->cxt->sector_size != dp->sector_size) { + /* + * Ignore last and first LBA if device sector size mismatch + * with sector size in script. It would be possible to + * recalculate it, but for GPT it will not work in some cases + * as these offsets are calculated by relative number of + * sectors. It's better to use library defaults than try + * to be smart ... + */ + if (fdisk_script_get_header(dp, "first-lba")) { + fdisk_script_set_header(dp, "first-lba", NULL); + fdisk_info(dp->cxt, _("Ingnore \"first-lba\" header due to sector size mismatch.")); + } + if (fdisk_script_get_header(dp, "last-lba")) { + fdisk_script_set_header(dp, "last-lba", NULL); + fdisk_info(dp->cxt, _("Ingnore \"last-lba\" header due to sector size mismatch.")); + } + } + str = fdisk_script_get_header(dp, "grain"); if (str) { uintmax_t sz;