@@ -743,7 +743,27 @@ void free_string_array(char **nodes)
free(nodes);
}
-unsigned long long ustrtoull(const char *cp, unsigned int base)
+/*
+ * Determines if ustrtoull() consumes a size-type delimiter
+ * from the provided string.
+ */
+int size_delimiter_match(const char *size)
+{
+ char *suffix = NULL, *usuffix = NULL;
+ strtoull(size, &suffix, 10);
+ ustrtoull(size, &usuffix, 10);
+ return suffix != usuffix;
+}
+
+/*
+ * Like strtoull(), but automatically scales the conversion
+ * result by size-type units, and only returns a pointer to
+ * the size unit in the string if requested by the caller.
+ *
+ * Sets errno to ERANGE if strtoull() found no digits or
+ * encountered an overflow, and returns 0 in both cases.
+ */
+unsigned long long ustrtoull(const char *cp, char **endptr, unsigned int base)
{
errno = 0;
char *endp = NULL;
@@ -756,7 +776,8 @@ unsigned long long ustrtoull(const char *cp, unsigned int base)
if (cp == endp || (result == ULLONG_MAX && errno == ERANGE)) {
errno = ERANGE;
- return 0;
+ result = 0;
+ goto out;
}
switch (*endp) {
@@ -774,8 +795,15 @@ unsigned long long ustrtoull(const char *cp, unsigned int base)
endp += 3;
else
endp += 2;
+ } else {
+ endp += 1;
}
}
+
+out:
+ if (endptr)
+ *endptr = endp;
+
return result;
}
@@ -1196,7 +1224,7 @@ long long get_output_size(struct img_type *img, bool strict)
return -ENOENT;
}
- bytes = ustrtoull(output_size_str, 0);
+ bytes = ustrtoull(output_size_str, NULL, 0);
if (errno || bytes <= 0) {
ERROR("decompressed-size argument %s: ustrtoull failed",
output_size_str);
@@ -1214,7 +1242,7 @@ long long get_output_size(struct img_type *img, bool strict)
return -ENOENT;
}
- bytes = ustrtoull(output_size_str, 0);
+ bytes = ustrtoull(output_size_str, NULL, 0);
if (errno || bytes <= 0) {
ERROR("decrypted-size argument %s: ustrtoull failed",
output_size_str);
@@ -319,7 +319,7 @@ static void lua_string_to_img(struct img_type *img, const char *key,
strncpy(seek_str, value,
sizeof(seek_str));
/* convert the offset handling multiplicative suffixes */
- img->seek = ustrtoull(seek_str, 0);
+ img->seek = ustrtoull(seek_str, NULL, 0);
if (errno){
ERROR("offset argument: ustrtoull failed");
}
@@ -329,7 +329,7 @@ static int delta_retrieve_attributes(struct img_type *img, struct hnd_priv *priv
if (!strcmp(srcsize, "detect"))
priv->detectsrcsize = true;
else
- priv->srcsize = ustrtoull(srcsize, 10);
+ priv->srcsize = ustrtoull(srcsize, NULL, 10);
}
char *zckloglevel = dict_get_value(&img->properties, "zckloglevel");
@@ -74,6 +74,7 @@ struct partition_data {
char fstype[SWUPDATE_GENERAL_STRING_SIZE];
char dostype[SWUPDATE_GENERAL_STRING_SIZE];
char partuuid[UUID_STR_LEN];
+ int explicit_size;
LIST_ENTRY(partition_data) next;
};
LIST_HEAD(listparts, partition_data);
@@ -305,10 +306,13 @@ static int diskpart_set_partition(struct fdisk_partition *pa,
ret |= -EINVAL;
if (strlen(part->name))
ret |= fdisk_partition_set_name(pa, part->name);
- if (part->size != LIBFDISK_INIT_UNDEF(part->size))
+ if (part->size != LIBFDISK_INIT_UNDEF(part->size)) {
ret |= fdisk_partition_set_size(pa, part->size / sector_size);
- else
+ if (part->explicit_size)
+ ret |= fdisk_partition_size_explicit(pa, part->explicit_size);
+ } else {
ret |= fdisk_partition_end_follow_default(pa, 1);
+ }
if (parttype)
ret |= fdisk_partition_set_type(pa, parttype);
@@ -543,7 +547,7 @@ static int diskpart_fill_table(struct fdisk_context *cxt, struct diskpart_table
parttype = fdisk_label_get_parttype_from_string(lb, GPT_DEFAULT_ENTRY_TYPE);
}
} else {
- parttype = fdisk_label_get_parttype_from_code(lb, ustrtoull(part->type, 16));
+ parttype = fdisk_label_get_parttype_from_code(lb, ustrtoull(part->type, NULL, 16));
}
ret = diskpart_set_partition(newpa, part, sector_size, parttype, oldtb->parent);
if (ret) {
@@ -578,7 +582,7 @@ static int diskpart_fill_table(struct fdisk_context *cxt, struct diskpart_table
newpa = fdisk_new_partition();
- parttype = fdisk_label_get_parttype_from_code(lb, ustrtoull(part->dostype, 16));
+ parttype = fdisk_label_get_parttype_from_code(lb, ustrtoull(part->dostype, NULL, 16));
if (!parttype) {
ERROR("I cannot add hybrid partition %zu(%s) invalid dostype: %s",
part->partno, part->name, part->dostype);
@@ -854,10 +858,12 @@ static int diskpart(struct img_type *img,
equal++;
switch (i) {
case PART_SIZE:
- part->size = ustrtoull(equal, 10);
+ part->size = ustrtoull(equal, NULL, 10);
+ if (!size_delimiter_match(equal))
+ part->explicit_size = 1;
break;
case PART_START:
- part->start = ustrtoull(equal, 10);
+ part->start = ustrtoull(equal, NULL, 10);
break;
case PART_TYPE:
strncpy(part->type, equal, sizeof(part->type));
@@ -228,7 +228,8 @@ void set_version_range(const char *minversion,
const char *maxversion,
const char *current);
-unsigned long long ustrtoull(const char *cp, unsigned int base);
+int size_delimiter_match(const char *size);
+unsigned long long ustrtoull(const char *cp, char **endptr, unsigned int base);
const char* get_tmpdir(void);
const char* get_tmpdirscripts(void);
@@ -69,7 +69,7 @@ static void sw_append_stream(struct img_type *img, const char *key,
strlcpy(seek_str, value,
sizeof(seek_str));
/* convert the offset handling multiplicative suffixes */
- img->seek = ustrtoull(seek_str, 0);
+ img->seek = ustrtoull(seek_str, NULL, 0);
if (errno){
ERROR("offset argument: ustrtoull failed");
}
@@ -406,7 +406,7 @@ static int parse_common_attributes(parsertype p, void *elem, struct img_type *im
get_hash_value(p, elem, image->sha256);
/* convert the offset handling multiplicative suffixes */
- image->seek = ustrtoull(seek_str, 0);
+ image->seek = ustrtoull(seek_str, NULL, 0);
if (errno){
ERROR("offset argument: ustrtoull failed");
return -1;
@@ -25,7 +25,7 @@ void suricatta_channel_settings(void *elem, channel_data_t *chan)
GET_FIELD_STRING_RESET(LIBCFG_PARSER, elem, "max-download-speed", tmp);
if (strlen(tmp))
- chan->max_download_speed = (unsigned int)ustrtoull(tmp, 10);
+ chan->max_download_speed = (unsigned int)ustrtoull(tmp, NULL, 10);
GET_FIELD_STRING_RESET(LIBCFG_PARSER, elem, "retrywait", tmp);
if (strlen(tmp))
@@ -669,7 +669,7 @@ server_op_res_t server_start(char *fname, int argc, char *argv[])
break;
case 'n':
channel_data_defaults.max_download_speed =
- (unsigned int)ustrtoull(optarg, 10);
+ (unsigned int)ustrtoull(optarg, NULL, 10);
break;
case '?':
@@ -1848,7 +1848,7 @@ server_op_res_t server_start(char *fname, int argc, char *argv[])
break;
case 'n':
channel_data_defaults.max_download_speed =
- (unsigned int)ustrtoull(optarg, 10);
+ (unsigned int)ustrtoull(optarg, NULL, 10);
break;
/* Ignore not recognized options, they can be already parsed by the caller */
case '?':
@@ -26,6 +26,7 @@ tests-$(CONFIG_SIGNED_IMAGES) += test_verify
endif
tests-$(CONFIG_SURICATTA_HAWKBIT) += test_json
tests-$(CONFIG_SURICATTA_HAWKBIT) += test_server_hawkbit
+tests-y += test_util
ccflags-y += -I$(src)/../
new file mode 100644
@@ -0,0 +1,52 @@
+// SPDX-FileCopyrightText: 2022 Kyle Russell <bkylerussell@gmail.com>
+//
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include "util.h"
+
+static int util_setup(void **state)
+{
+ (void)state;
+ return 0;
+}
+
+static int util_teardown(void **state)
+{
+ (void)state;
+ return 0;
+}
+
+static void test_util_size_delimiter_match(void **state)
+{
+ (void)state;
+ assert_int_equal(size_delimiter_match("1024G, some fancy things"), 1);
+ assert_int_equal(size_delimiter_match("2048KiB"), 1);
+ assert_int_equal(size_delimiter_match("1073741824"), 0);
+}
+
+static void test_util_ustrtoull(void **state)
+{
+ (void)state;
+ char *suffix = NULL;
+ uint64_t size = ustrtoull("1024M, some fancy things", &suffix, 10);
+ assert_int_equal(errno, 0);
+ assert_int_equal(size, 1073741824);
+ assert_string_equal(suffix, ", some fancy things");
+}
+
+int main(void)
+{
+ int error_count = 0;
+ const struct CMUnitTest util_tests[] = {
+ cmocka_unit_test(test_util_ustrtoull),
+ cmocka_unit_test(test_util_size_delimiter_match)
+ };
+ error_count += cmocka_run_group_tests_name("util", util_tests,
+ util_setup, util_teardown);
+ return error_count;
+}
When passing in a partition size in units of sectors, fdisk expects you to enable explicit sizing on the partition; otherwise, fdisk assumes you want the partition aligned to the nearest grain (typically 1MB), which may corrupt a partition table containing already explicitly sized partitions. This matches the behavior of other libfdisk clients in util-linux (see excerpt from libfdisk's parse_line_nameval() below): libfdisk/src/script.c: if (pow) /* specified as <num><suffix> */ num /= dp->cxt->sector_size; else /* specified as number of sectors */ fdisk_partition_size_explicit(pa, 1); To achieve this, we allow ustrtoull() to return a char* to the suffix of the converted string, if one exists. If strtoull() points to a different suffix in the string than ustrtoull(), we assume ustrtoull() consumed a user provided size-type delimiter. If no size-type delimiter match is found, explicit sizing is not enabled and fdisk aligns the partition to the nearest grain. Signed-off-by: Kyle Russell <bkylerussell@gmail.com> --- core/util.c | 36 ++++++++++++++++++++++--- corelib/lua_interface.c | 2 +- handlers/delta_handler.c | 2 +- handlers/diskpart_handler.c | 18 ++++++++----- include/util.h | 3 ++- parser/parse_external.c | 2 +- parser/parser.c | 2 +- suricatta/common.c | 2 +- suricatta/server_general.c | 2 +- suricatta/server_hawkbit.c | 2 +- test/Makefile | 1 + test/test_util.c | 52 +++++++++++++++++++++++++++++++++++++ 12 files changed, 106 insertions(+), 18 deletions(-) create mode 100644 test/test_util.c