libfdisk: make scripts portable between different sector sizes

Fixes: https://github.com/util-linux/util-linux/issues/1744
Signed-off-by: Karel Zak <kzak@redhat.com>
This commit is contained in:
Karel Zak 2022-07-11 14:02:30 +02:00 committed by su-fang
parent 599e3fd306
commit ff1eb8a42b
1 changed files with 70 additions and 1 deletions

View File

@ -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: <name> 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;