Systemd/src/basic/parse-util.c
Lennart Poettering 2d49a208f8 parse-util: really refuse parsing negative values as positive ones, even on x86-32
strtoull() doesn't make it particularly easy to detect passed-in
negative numbers, as it silently converts them to positive ones without
generating any error. Since we are not interested in negative values we
should hence explicitly filter them out by looking at the string
directly and returning ERANGE if we see a leading "-".

Fixes: #1829
2015-11-10 17:36:52 +01:00

493 lines
13 KiB
C

/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "alloc-util.h"
#include "extract-word.h"
#include "parse-util.h"
#include "string-util.h"
#include "util.h"
int parse_boolean(const char *v) {
assert(v);
if (streq(v, "1") || strcaseeq(v, "yes") || strcaseeq(v, "y") || strcaseeq(v, "true") || strcaseeq(v, "t") || strcaseeq(v, "on"))
return 1;
else if (streq(v, "0") || strcaseeq(v, "no") || strcaseeq(v, "n") || strcaseeq(v, "false") || strcaseeq(v, "f") || strcaseeq(v, "off"))
return 0;
return -EINVAL;
}
int parse_pid(const char *s, pid_t* ret_pid) {
unsigned long ul = 0;
pid_t pid;
int r;
assert(s);
assert(ret_pid);
r = safe_atolu(s, &ul);
if (r < 0)
return r;
pid = (pid_t) ul;
if ((unsigned long) pid != ul)
return -ERANGE;
if (pid <= 0)
return -ERANGE;
*ret_pid = pid;
return 0;
}
int parse_mode(const char *s, mode_t *ret) {
char *x;
long l;
assert(s);
assert(ret);
s += strspn(s, WHITESPACE);
if (s[0] == '-')
return -ERANGE;
errno = 0;
l = strtol(s, &x, 8);
if (errno != 0)
return -errno;
if (!x || x == s || *x)
return -EINVAL;
if (l < 0 || l > 07777)
return -ERANGE;
*ret = (mode_t) l;
return 0;
}
int parse_ifindex(const char *s, int *ret) {
int ifi, r;
r = safe_atoi(s, &ifi);
if (r < 0)
return r;
if (ifi <= 0)
return -EINVAL;
*ret = ifi;
return 0;
}
int parse_size(const char *t, uint64_t base, uint64_t *size) {
/* Soo, sometimes we want to parse IEC binary suffixes, and
* sometimes SI decimal suffixes. This function can parse
* both. Which one is the right way depends on the
* context. Wikipedia suggests that SI is customary for
* hardware metrics and network speeds, while IEC is
* customary for most data sizes used by software and volatile
* (RAM) memory. Hence be careful which one you pick!
*
* In either case we use just K, M, G as suffix, and not Ki,
* Mi, Gi or so (as IEC would suggest). That's because that's
* frickin' ugly. But this means you really need to make sure
* to document which base you are parsing when you use this
* call. */
struct table {
const char *suffix;
unsigned long long factor;
};
static const struct table iec[] = {
{ "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
{ "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
{ "T", 1024ULL*1024ULL*1024ULL*1024ULL },
{ "G", 1024ULL*1024ULL*1024ULL },
{ "M", 1024ULL*1024ULL },
{ "K", 1024ULL },
{ "B", 1ULL },
{ "", 1ULL },
};
static const struct table si[] = {
{ "E", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL*1000ULL },
{ "P", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL },
{ "T", 1000ULL*1000ULL*1000ULL*1000ULL },
{ "G", 1000ULL*1000ULL*1000ULL },
{ "M", 1000ULL*1000ULL },
{ "K", 1000ULL },
{ "B", 1ULL },
{ "", 1ULL },
};
const struct table *table;
const char *p;
unsigned long long r = 0;
unsigned n_entries, start_pos = 0;
assert(t);
assert(base == 1000 || base == 1024);
assert(size);
if (base == 1000) {
table = si;
n_entries = ELEMENTSOF(si);
} else {
table = iec;
n_entries = ELEMENTSOF(iec);
}
p = t;
do {
unsigned long long l, tmp;
double frac = 0;
char *e;
unsigned i;
p += strspn(p, WHITESPACE);
errno = 0;
l = strtoull(p, &e, 10);
if (errno != 0)
return -errno;
if (e == p)
return -EINVAL;
if (*p == '-')
return -ERANGE;
if (*e == '.') {
e++;
/* strtoull() itself would accept space/+/- */
if (*e >= '0' && *e <= '9') {
unsigned long long l2;
char *e2;
l2 = strtoull(e, &e2, 10);
if (errno != 0)
return -errno;
/* Ignore failure. E.g. 10.M is valid */
frac = l2;
for (; e < e2; e++)
frac /= 10;
}
}
e += strspn(e, WHITESPACE);
for (i = start_pos; i < n_entries; i++)
if (startswith(e, table[i].suffix))
break;
if (i >= n_entries)
return -EINVAL;
if (l + (frac > 0) > ULLONG_MAX / table[i].factor)
return -ERANGE;
tmp = l * table[i].factor + (unsigned long long) (frac * table[i].factor);
if (tmp > ULLONG_MAX - r)
return -ERANGE;
r += tmp;
if ((unsigned long long) (uint64_t) r != r)
return -ERANGE;
p = e + strlen(table[i].suffix);
start_pos = i + 1;
} while (*p);
*size = r;
return 0;
}
int parse_range(const char *t, unsigned *lower, unsigned *upper) {
_cleanup_free_ char *word = NULL;
unsigned l, u;
int r;
assert(lower);
assert(upper);
/* Extract the lower bound. */
r = extract_first_word(&t, &word, "-", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return r;
if (r == 0)
return -EINVAL;
r = safe_atou(word, &l);
if (r < 0)
return r;
/* Check for the upper bound and extract it if needed */
if (!t)
/* Single number with no dashes. */
u = l;
else if (!*t)
/* Trailing dash is an error. */
return -EINVAL;
else {
r = safe_atou(t, &u);
if (r < 0)
return r;
}
*lower = l;
*upper = u;
return 0;
}
char *format_bytes(char *buf, size_t l, uint64_t t) {
unsigned i;
/* This only does IEC units so far */
static const struct {
const char *suffix;
uint64_t factor;
} table[] = {
{ "E", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
{ "P", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
{ "T", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
{ "G", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
{ "M", UINT64_C(1024)*UINT64_C(1024) },
{ "K", UINT64_C(1024) },
};
if (t == (uint64_t) -1)
return NULL;
for (i = 0; i < ELEMENTSOF(table); i++) {
if (t >= table[i].factor) {
snprintf(buf, l,
"%" PRIu64 ".%" PRIu64 "%s",
t / table[i].factor,
((t*UINT64_C(10)) / table[i].factor) % UINT64_C(10),
table[i].suffix);
goto finish;
}
}
snprintf(buf, l, "%" PRIu64 "B", t);
finish:
buf[l-1] = 0;
return buf;
}
int safe_atou(const char *s, unsigned *ret_u) {
char *x = NULL;
unsigned long l;
assert(s);
assert(ret_u);
/* strtoul() is happy to parse negative values, and silently
* converts them to unsigned values without generating an
* error. We want a clean error, hence let's look for the "-"
* prefix on our own, and generate an error. But let's do so
* only after strtoul() validated that the string is clean
* otherwise, so that we return EINVAL preferably over
* ERANGE. */
s += strspn(s, WHITESPACE);
errno = 0;
l = strtoul(s, &x, 0);
if (errno != 0)
return -errno;
if (!x || x == s || *x)
return -EINVAL;
if (s[0] == '-')
return -ERANGE;
if ((unsigned long) (unsigned) l != l)
return -ERANGE;
*ret_u = (unsigned) l;
return 0;
}
int safe_atoi(const char *s, int *ret_i) {
char *x = NULL;
long l;
assert(s);
assert(ret_i);
errno = 0;
l = strtol(s, &x, 0);
if (errno != 0)
return -errno;
if (!x || x == s || *x)
return -EINVAL;
if ((long) (int) l != l)
return -ERANGE;
*ret_i = (int) l;
return 0;
}
int safe_atollu(const char *s, long long unsigned *ret_llu) {
char *x = NULL;
unsigned long long l;
assert(s);
assert(ret_llu);
s += strspn(s, WHITESPACE);
errno = 0;
l = strtoull(s, &x, 0);
if (errno != 0)
return -errno;
if (!x || x == s || *x)
return -EINVAL;
if (*s == '-')
return -ERANGE;
*ret_llu = l;
return 0;
}
int safe_atolli(const char *s, long long int *ret_lli) {
char *x = NULL;
long long l;
assert(s);
assert(ret_lli);
errno = 0;
l = strtoll(s, &x, 0);
if (errno != 0)
return -errno;
if (!x || x == s || *x)
return -EINVAL;
*ret_lli = l;
return 0;
}
int safe_atou8(const char *s, uint8_t *ret) {
char *x = NULL;
unsigned long l;
assert(s);
assert(ret);
s += strspn(s, WHITESPACE);
errno = 0;
l = strtoul(s, &x, 0);
if (errno != 0)
return -errno;
if (!x || x == s || *x)
return -EINVAL;
if (s[0] == '-')
return -ERANGE;
if ((unsigned long) (uint8_t) l != l)
return -ERANGE;
*ret = (uint8_t) l;
return 0;
}
int safe_atou16(const char *s, uint16_t *ret) {
char *x = NULL;
unsigned long l;
assert(s);
assert(ret);
s += strspn(s, WHITESPACE);
errno = 0;
l = strtoul(s, &x, 0);
if (errno != 0)
return -errno;
if (!x || x == s || *x)
return -EINVAL;
if (s[0] == '-')
return -ERANGE;
if ((unsigned long) (uint16_t) l != l)
return -ERANGE;
*ret = (uint16_t) l;
return 0;
}
int safe_atoi16(const char *s, int16_t *ret) {
char *x = NULL;
long l;
assert(s);
assert(ret);
errno = 0;
l = strtol(s, &x, 0);
if (errno != 0)
return -errno;
if (!x || x == s || *x)
return -EINVAL;
if ((long) (int16_t) l != l)
return -ERANGE;
*ret = (int16_t) l;
return 0;
}
int safe_atod(const char *s, double *ret_d) {
char *x = NULL;
double d = 0;
locale_t loc;
assert(s);
assert(ret_d);
loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
if (loc == (locale_t) 0)
return -errno;
errno = 0;
d = strtod_l(s, &x, loc);
if (errno != 0) {
freelocale(loc);
return -errno;
}
if (!x || x == s || *x) {
freelocale(loc);
return -EINVAL;
}
freelocale(loc);
*ret_d = (double) d;
return 0;
}