diff --git a/src/lxc/utils.c b/src/lxc/utils.c index f6b6584e27..887afdd03c 100644 --- a/src/lxc/utils.c +++ b/src/lxc/utils.c @@ -47,6 +47,7 @@ #include "log.h" #include "lxclock.h" #include "namespace.h" +#include "parse.h" #include "utils.h" #ifndef PR_SET_MM @@ -2448,3 +2449,69 @@ uint64_t lxc_getpagesize(void) return pgsz; } + +int parse_byte_size_string(const char *s, int64_t *converted) +{ + int ret, suffix_len; + long long int conv; + int64_t mltpl, overflow; + char *end; + char dup[LXC_NUMSTRLEN64 + 2]; + char suffix[3]; + + if (!s || !strcmp(s, "")) + return -EINVAL; + + end = stpncpy(dup, s, sizeof(dup)); + if (*end != '\0') + return -EINVAL; + + if (isdigit(*(end - 1))) + suffix_len = 0; + else if (isalpha(*(end - 1))) + suffix_len = 1; + else + return -EINVAL; + + if ((end - 2) == dup && !isdigit(*(end - 2))) + return -EINVAL; + + if (isalpha(*(end - 2))) { + if (suffix_len == 1) + suffix_len++; + else + return -EINVAL; + } + + if (suffix_len > 0) { + memcpy(suffix, end - suffix_len, suffix_len); + *(suffix + suffix_len) = '\0'; + *(end - suffix_len) = '\0'; + } + dup[lxc_char_right_gc(dup, strlen(dup))] = '\0'; + + ret = lxc_safe_long_long(dup, &conv); + if (ret < 0) + return -ret; + + if (suffix_len != 2) { + *converted = conv; + return 0; + } + + if (!strcmp(suffix, "kB")) + mltpl = 1024; + else if (!strcmp(suffix, "MB")) + mltpl = 1024 * 1024; + else if (!strcmp(suffix, "GB")) + mltpl = 1024 * 1024 * 1024; + else + return -EINVAL; + + overflow = conv * mltpl; + if (conv != 0 && (overflow / conv) != mltpl) + return -ERANGE; + + *converted = overflow; + return 0; +} diff --git a/src/lxc/utils.h b/src/lxc/utils.h index 315d87768f..62c2d3c726 100644 --- a/src/lxc/utils.h +++ b/src/lxc/utils.h @@ -412,6 +412,8 @@ extern int lxc_safe_int(const char *numstr, int *converted); extern int lxc_safe_long(const char *numstr, long int *converted); extern int lxc_safe_long_long(const char *numstr, long long int *converted); extern int lxc_safe_ulong(const char *numstr, unsigned long *converted); +/* Handles B, kb, MB, GB. Detects overflows and reports -ERANGE. */ +extern int parse_byte_size_string(const char *s, int64_t *converted); /* Switch to a new uid and gid. */ int lxc_switch_uid_gid(uid_t uid, gid_t gid); diff --git a/src/tests/lxc-test-utils.c b/src/tests/lxc-test-utils.c index aba7706abc..4c3c17a783 100644 --- a/src/tests/lxc-test-utils.c +++ b/src/tests/lxc-test-utils.c @@ -380,6 +380,88 @@ void test_lxc_string_in_array(void) lxc_test_assert_abort(lxc_string_in_array("XYZ", (const char *[]){"BERTA", "ARQWE(9", "C8Zhkd", "7U", "XYZ", "UOIZ9", "=)()", NULL})); } +void test_parse_byte_size_string(void) +{ + int ret; + int64_t n; + + ret = parse_byte_size_string("0", &n); + if (ret < 0) + exit(EXIT_FAILURE); + if (n != 0) + exit(EXIT_FAILURE); + + ret = parse_byte_size_string("1", &n); + if (ret < 0) + exit(EXIT_FAILURE); + if (n != 1) + exit(EXIT_FAILURE); + + ret = parse_byte_size_string("1 ", &n); + if (ret == 0) + exit(EXIT_FAILURE); + + ret = parse_byte_size_string("1B", &n); + if (ret < 0) + exit(EXIT_FAILURE); + if (n != 1) + exit(EXIT_FAILURE); + + ret = parse_byte_size_string("1kB", &n); + if (ret < 0) + exit(EXIT_FAILURE); + if (n != 1024) + exit(EXIT_FAILURE); + + ret = parse_byte_size_string("1MB", &n); + if (ret < 0) + exit(EXIT_FAILURE); + if (n != 1048576) + exit(EXIT_FAILURE); + + ret = parse_byte_size_string("1GB", &n); + if (ret < 0) + exit(EXIT_FAILURE); + if (n != 1073741824) + exit(EXIT_FAILURE); + + ret = parse_byte_size_string("1TB", &n); + if (ret == 0) + exit(EXIT_FAILURE); + + ret = parse_byte_size_string("1 B", &n); + if (ret < 0) + exit(EXIT_FAILURE); + if (n != 1) + exit(EXIT_FAILURE); + + ret = parse_byte_size_string("1 kB", &n); + if (ret < 0) + exit(EXIT_FAILURE); + if (n != 1024) + exit(EXIT_FAILURE); + + ret = parse_byte_size_string("1 MB", &n); + if (ret < 0) + exit(EXIT_FAILURE); + if (n != 1048576) + exit(EXIT_FAILURE); + + ret = parse_byte_size_string("1 GB", &n); + if (ret < 0) + exit(EXIT_FAILURE); + if (n != 1073741824) + exit(EXIT_FAILURE); + + ret = parse_byte_size_string("1 TB", &n); + if (ret == 0) + exit(EXIT_FAILURE); + + ret = parse_byte_size_string("asdf", &n); + if (ret == 0) + exit(EXIT_FAILURE); +} + int main(int argc, char *argv[]) { test_lxc_string_replace(); @@ -389,6 +471,7 @@ int main(int argc, char *argv[]) test_lxc_safe_uint(); test_lxc_safe_int(); test_lxc_safe_long(); + test_parse_byte_size_string(); exit(EXIT_SUCCESS); }