allow specifying larger /tmp tmpfs mounts

Extract the size string-parsing and overflow-checking wrapper into util.c.

Test: New unit tests.
Change-Id: I31ba2f1a77217a2f13cda078e5e6a80104fbcd32
Signed-off-by: Martin Pelikán <mpel@google.com>
diff --git a/util.c b/util.c
index d91d068..a96c71f 100644
--- a/util.c
+++ b/util.c
@@ -7,6 +7,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <string.h>
 #include <sys/stat.h>
@@ -157,6 +158,56 @@
 	return value;
 }
 
+/*
+ * parse_size, specified as a string with a decimal number in bytes,
+ * possibly with one 1-character suffix like "10K" or "6G".
+ * Assumes both pointers are non-NULL.
+ *
+ * Returns 0 on success, negative errno on failure.
+ * Only writes to result on success.
+ */
+int parse_size(size_t *result, const char *sizespec)
+{
+	const char prefixes[] = "KMGTPE";
+	size_t i, multiplier = 1, nsize, size = 0;
+	unsigned long long parsed;
+	const size_t len = strlen(sizespec);
+	char *end;
+
+	if (len == 0 || sizespec[0] == '-')
+		return -EINVAL;
+
+	for (i = 0; i < sizeof(prefixes); ++i) {
+		if (sizespec[len - 1] == prefixes[i]) {
+#if __WORDSIZE == 32
+			if (i >= 3)
+				return -ERANGE;
+#endif
+			multiplier = 1024;
+			while (i-- > 0)
+				multiplier *= 1024;
+			break;
+		}
+	}
+
+	/* We only need size_t but strtoul(3) is too small on IL32P64. */
+	parsed = strtoull(sizespec, &end, 10);
+	if (parsed == ULLONG_MAX)
+		return -errno;
+	if (parsed >= SIZE_MAX)
+		return -ERANGE;
+	if ((multiplier != 1 && end != sizespec + len - 1) ||
+	    (multiplier == 1 && end != sizespec + len))
+		return -EINVAL;
+	size = (size_t)parsed;
+
+	nsize = size * multiplier;
+	if (nsize / multiplier != size)
+		return -ERANGE;
+	*result = nsize;
+	return 0;
+}
+
 char *strip(char *s)
 {
 	char *end;