Prefer stoint to strtoul and variants
When parsing numbers prefer using strtosysint (renamed stoint) to using strtoul and its variants. This is simpler and faster and likely more reliable than relying on quirks of the system strtoul etc, and it standardizes how tar deals with parsing integers. Among other things, the C standard and POSIX don’t specify what strtol does to errno when conversions cannot be performed, and it requires strtoul to support "-" before unsigned numbers. * gnulib.modules (strtoimax, strtol, strtoumax, xstrtoimax): Remove. * src/checkpoint.c (checkpoint_compile_action, getwidth) (format_checkpoint_string): * src/incremen.c (read_incr_db_01, read_num) * src/map.c (parse_id): * src/misc.c (decode_timespec): * src/sparse.c (decode_num): * src/tar.c (parse_owner_group, parse_opt): * src/transform.c (parse_transform_expr): * src/xheader.c (decode_record, decode_signed_num) (sparse_map_decoder): Prefer stoint to strtol etc. Don’t rely on errno == EINVAL as the standards don’t guarantee it. * src/checkpoint.c (getwidth, format_checkpoint_string): Check for invalid string suffix. * src/checkpoint.c (getwidth): Return intmax_t, not long. All callers changed. * src/incremen.c (read_directory_file): It’s just a one-digit number, so just subtract '0'. * src/map.c (parse_id): Return bool not int. All callers changed. * src/misc.c (stoint): Rename from strtosysint, and add a bool * argument for reporting overflow. All callers changed. (decode_timespec): Simplify by using ckd_sub rather than checking for overflow by hand. * src/tar.c (incremental_level): Now signed char to emphasize that it can be only -1, 0, 1. All uses changed. * src/xheader.c (decode_record): Avoid giant diagnostics.
This commit is contained in:
@@ -106,9 +106,6 @@ stdopen
|
||||
strdup-posix
|
||||
strerror
|
||||
strnlen
|
||||
strtoimax
|
||||
strtol
|
||||
strtoumax
|
||||
symlinkat
|
||||
sys_stat
|
||||
timespec
|
||||
@@ -122,7 +119,6 @@ xalignalloc
|
||||
xalloc
|
||||
xalloc-die
|
||||
xgetcwd
|
||||
xstrtoimax
|
||||
xstrtoumax
|
||||
xvasprintf
|
||||
year2038-recommended
|
||||
|
||||
@@ -127,13 +127,12 @@ checkpoint_compile_action (const char *str)
|
||||
}
|
||||
else if (strncmp (str, "sleep=", 6) == 0)
|
||||
{
|
||||
char const *arg = str + 6;
|
||||
char *p;
|
||||
intmax_t sleepsec = strtoimax (str + 6, &p, 10);
|
||||
if (*p || sleepsec < 0)
|
||||
FATAL_ERROR ((0, 0, _("%s: not a valid timeout"), str));
|
||||
time_t n = ckd_add (&n, sleepsec, 0) ? TYPE_MAXIMUM (time_t) : n;
|
||||
act = alloc_action (cop_sleep);
|
||||
act->v.time = n;
|
||||
act->v.time = stoint (arg, &p, NULL, 0, TYPE_MAXIMUM (time_t));
|
||||
if ((p == arg) | *p)
|
||||
FATAL_ERROR ((0, 0, _("%s: not a valid timeout"), str));
|
||||
}
|
||||
else if (strcmp (str, "totals") == 0)
|
||||
alloc_action (cop_totals);
|
||||
@@ -176,7 +175,7 @@ static const char *checkpoint_total_format[] = {
|
||||
"D"
|
||||
};
|
||||
|
||||
static long
|
||||
static intmax_t
|
||||
getwidth (FILE *fp)
|
||||
{
|
||||
char const *columns;
|
||||
@@ -190,8 +189,9 @@ getwidth (FILE *fp)
|
||||
columns = getenv ("COLUMNS");
|
||||
if (columns)
|
||||
{
|
||||
long int col = strtol (columns, NULL, 10);
|
||||
if (0 < col)
|
||||
char *end;
|
||||
intmax_t col = stoint (columns, &end, NULL, 0, INTMAX_MAX);
|
||||
if (! (*end | !col))
|
||||
return col;
|
||||
}
|
||||
|
||||
@@ -337,7 +337,16 @@ format_checkpoint_string (FILE *fp, size_t len,
|
||||
|
||||
case '*':
|
||||
{
|
||||
long w = arg ? strtol (arg, NULL, 10) : getwidth (fp);
|
||||
intmax_t w;
|
||||
if (!arg)
|
||||
w = getwidth (fp);
|
||||
else
|
||||
{
|
||||
char *end;
|
||||
w = stoint (arg, &end, NULL, 0, INTMAX_MAX);
|
||||
if ((end == arg) | *end)
|
||||
w = 80;
|
||||
}
|
||||
for (; w > len; len++)
|
||||
fputc (' ', fp);
|
||||
}
|
||||
|
||||
@@ -190,8 +190,8 @@ extern bool keep_directory_symlink_option;
|
||||
|
||||
/* Specified file name for incremental list. */
|
||||
extern const char *listed_incremental_option;
|
||||
/* Incremental dump level */
|
||||
extern int incremental_level;
|
||||
/* Incremental dump level: either -1, 0, or 1. */
|
||||
extern signed char incremental_level;
|
||||
/* Check device numbers when doing incremental dumps. */
|
||||
extern bool check_device_option;
|
||||
|
||||
@@ -701,7 +701,7 @@ enum { UINTMAX_STRSIZE_BOUND = INT_BUFSIZE_BOUND (intmax_t) };
|
||||
enum { SYSINT_BUFSIZE =
|
||||
max (UINTMAX_STRSIZE_BOUND, INT_BUFSIZE_BOUND (intmax_t)) };
|
||||
char *sysinttostr (uintmax_t, intmax_t, uintmax_t, char buf[SYSINT_BUFSIZE]);
|
||||
intmax_t strtosysint (char const *, char **, intmax_t, uintmax_t);
|
||||
intmax_t stoint (char const *, char **, bool *, intmax_t, uintmax_t);
|
||||
char *timetostr (time_t, char buf[SYSINT_BUFSIZE]);
|
||||
void code_ns_fraction (int ns, char *p);
|
||||
enum { BILLION = 1000000000, LOG10_BILLION = 9 };
|
||||
|
||||
@@ -976,7 +976,6 @@ static void
|
||||
read_incr_db_01 (int version, const char *initbuf)
|
||||
{
|
||||
int n;
|
||||
uintmax_t u;
|
||||
char *buf = NULL;
|
||||
size_t bufsize = 0;
|
||||
char *ebuf;
|
||||
@@ -1010,21 +1009,18 @@ read_incr_db_01 (int version, const char *initbuf)
|
||||
if (version == 1 && *ebuf)
|
||||
{
|
||||
char const *buf_ns = ebuf + 1;
|
||||
errno = 0;
|
||||
u = strtoumax (buf_ns, &ebuf, 10);
|
||||
if (!errno && BILLION <= u)
|
||||
errno = ERANGE;
|
||||
if (errno || buf_ns == ebuf)
|
||||
bool overflow;
|
||||
newer_mtime_option.tv_nsec
|
||||
= stoint (buf_ns, &ebuf, &overflow, 0, BILLION - 1);
|
||||
if ((ebuf == buf_ns) | *ebuf | overflow)
|
||||
{
|
||||
ERROR ((0, errno, "%s:%ld: %s",
|
||||
ERROR ((0, 0, "%s:%ld: %s",
|
||||
quotearg_colon (listed_incremental_option),
|
||||
lineno,
|
||||
_("Invalid time stamp")));
|
||||
newer_mtime_option.tv_sec = TYPE_MINIMUM (time_t);
|
||||
newer_mtime_option.tv_nsec = -1;
|
||||
}
|
||||
else
|
||||
newer_mtime_option.tv_nsec = u;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1050,41 +1046,37 @@ read_incr_db_01 (int version, const char *initbuf)
|
||||
quotearg_colon (listed_incremental_option), lineno,
|
||||
_("Invalid modification time")));
|
||||
|
||||
errno = 0;
|
||||
u = strtoumax (strp, &ebuf, 10);
|
||||
if (!errno && BILLION <= u)
|
||||
errno = ERANGE;
|
||||
if (errno || strp == ebuf || *ebuf != ' ')
|
||||
bool overflow;
|
||||
mtime.tv_nsec = stoint (strp, &ebuf, &overflow, 0, BILLION - 1);
|
||||
if ((ebuf == strp) | (*ebuf != ' ') | overflow)
|
||||
{
|
||||
FATAL_ERROR ((0, errno, "%s:%ld: %s",
|
||||
FATAL_ERROR ((0, 0, "%s:%ld: %s",
|
||||
quotearg_colon (listed_incremental_option), lineno,
|
||||
_("Invalid modification time (nanoseconds)")));
|
||||
mtime.tv_nsec = -1;
|
||||
}
|
||||
else
|
||||
mtime.tv_nsec = u;
|
||||
strp = ebuf;
|
||||
}
|
||||
else
|
||||
mtime.tv_sec = mtime.tv_nsec = 0;
|
||||
|
||||
dev = strtosysint (strp, &ebuf,
|
||||
TYPE_MINIMUM (dev_t), TYPE_MAXIMUM (dev_t));
|
||||
strp = ebuf;
|
||||
if (errno || *strp != ' ')
|
||||
FATAL_ERROR ((0, errno, "%s:%ld: %s",
|
||||
bool overflow;
|
||||
dev = stoint (strp, &ebuf, &overflow,
|
||||
TYPE_MINIMUM (dev_t), TYPE_MAXIMUM (dev_t));
|
||||
if ((ebuf == strp) | (*ebuf != ' ') | overflow)
|
||||
FATAL_ERROR ((0, 0, "%s:%ld: %s",
|
||||
quotearg_colon (listed_incremental_option), lineno,
|
||||
_("Invalid device number")));
|
||||
strp = ebuf + 1;
|
||||
|
||||
ino = strtosysint (strp, &ebuf,
|
||||
TYPE_MINIMUM (ino_t), TYPE_MAXIMUM (ino_t));
|
||||
strp = ebuf;
|
||||
if (errno || *strp != ' ')
|
||||
FATAL_ERROR ((0, errno, "%s:%ld: %s",
|
||||
ino = stoint (strp, &ebuf, &overflow,
|
||||
TYPE_MINIMUM (ino_t), TYPE_MAXIMUM (ino_t));
|
||||
if ((ebuf == strp) | (*ebuf != ' ') | overflow)
|
||||
FATAL_ERROR ((0, 0, "%s:%ld: %s",
|
||||
quotearg_colon (listed_incremental_option), lineno,
|
||||
_("Invalid inode number")));
|
||||
strp = ebuf + 1;
|
||||
|
||||
strp++;
|
||||
unquote_string (strp);
|
||||
note_directory (strp, mtime, dev, ino, nfs, false, NULL);
|
||||
}
|
||||
@@ -1126,7 +1118,6 @@ read_num (FILE *fp, char const *fieldname,
|
||||
{
|
||||
int i;
|
||||
char buf[INT_BUFSIZE_BOUND (intmax_t)];
|
||||
int conversion_errno;
|
||||
int c = getc (fp);
|
||||
bool negative = c == '-';
|
||||
|
||||
@@ -1165,24 +1156,20 @@ read_num (FILE *fp, char const *fieldname,
|
||||
fieldname, buf, uc));
|
||||
}
|
||||
|
||||
*pval = strtosysint (buf, NULL, min_val, max_val);
|
||||
conversion_errno = errno;
|
||||
char *bufend;
|
||||
bool overflow;
|
||||
*pval = stoint (buf, &bufend, &overflow, min_val, max_val);
|
||||
|
||||
switch (conversion_errno)
|
||||
{
|
||||
case ERANGE:
|
||||
FATAL_ERROR ((0, conversion_errno,
|
||||
_("%s: byte %jd: (valid range %jd..%ju)\n\t%s %s"),
|
||||
quotearg_colon (listed_incremental_option),
|
||||
intmax (ftello (fp)), min_val, max_val, fieldname, buf));
|
||||
default:
|
||||
FATAL_ERROR ((0, conversion_errno,
|
||||
_("%s: byte %jd: %s %s"),
|
||||
quotearg_colon (listed_incremental_option),
|
||||
intmax (ftello (fp)), fieldname, buf));
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
if (buf == bufend)
|
||||
FATAL_ERROR ((0, EINVAL,
|
||||
_("%s: byte %jd: %s %s"),
|
||||
quotearg_colon (listed_incremental_option),
|
||||
intmax (ftello (fp)), fieldname, buf));
|
||||
if (overflow)
|
||||
FATAL_ERROR ((0, ERANGE,
|
||||
_("%s: byte %jd: (valid range %jd..%ju)\n\t%s %s"),
|
||||
quotearg_colon (listed_incremental_option),
|
||||
intmax (ftello (fp)), min_val, max_val, fieldname, buf));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1361,7 +1348,7 @@ read_directory_file (void)
|
||||
if (0 < getline (&buf, &bufsize, listed_incremental_stream))
|
||||
{
|
||||
char *ebuf;
|
||||
uintmax_t incremental_version;
|
||||
int incremental_version;
|
||||
|
||||
if (strncmp (buf, PACKAGE_NAME, sizeof PACKAGE_NAME - 1) == 0)
|
||||
{
|
||||
@@ -1372,7 +1359,12 @@ read_directory_file (void)
|
||||
if (!*ebuf)
|
||||
ERROR((1, 0, _("Bad incremental file format")));
|
||||
|
||||
incremental_version = strtoumax (ebuf + 1, NULL, 10);
|
||||
ebuf++;
|
||||
if (! ('0' <= *ebuf && *ebuf <= '0' + TAR_INCREMENTAL_VERSION
|
||||
&& !c_isdigit (ebuf[1])))
|
||||
ERROR ((1, 0, _("Unsupported incremental format version: %s"),
|
||||
ebuf));
|
||||
incremental_version = *ebuf - '0';
|
||||
}
|
||||
else
|
||||
incremental_version = 0;
|
||||
@@ -1389,10 +1381,8 @@ read_directory_file (void)
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR ((1, 0, _("Unsupported incremental format version: %"PRIuMAX),
|
||||
incremental_version));
|
||||
unreachable ();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (ferror (listed_incremental_stream))
|
||||
|
||||
42
src/map.c
42
src/map.c
@@ -45,28 +45,26 @@ map_compare (void const *entry1, void const *entry2)
|
||||
return map1->orig_id == map2->orig_id;
|
||||
}
|
||||
|
||||
static int
|
||||
static bool
|
||||
parse_id (uintmax_t *retval,
|
||||
char const *arg, char const *what, uintmax_t maxval,
|
||||
char const *file, unsigned line)
|
||||
{
|
||||
uintmax_t v;
|
||||
char *p;
|
||||
|
||||
errno = 0;
|
||||
v = strtoumax (arg, &p, 10);
|
||||
if (*p || errno)
|
||||
bool overflow;
|
||||
*retval = stoint (arg, &p, &overflow, 0, maxval);
|
||||
|
||||
if ((p == arg) | *p)
|
||||
{
|
||||
error (0, 0, _("%s:%u: invalid %s: %s"), file, line, what, arg);
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
if (v > maxval)
|
||||
if (overflow)
|
||||
{
|
||||
error (0, 0, _("%s:%u: %s out of range: %s"), file, line, what, arg);
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
*retval = v;
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -82,7 +80,7 @@ map_read (Hash_table **ptab, char const *file,
|
||||
int wsopt;
|
||||
unsigned line;
|
||||
int err = 0;
|
||||
|
||||
|
||||
fp = fopen (file, "r");
|
||||
if (!fp)
|
||||
open_fatal (file);
|
||||
@@ -97,7 +95,7 @@ map_read (Hash_table **ptab, char const *file,
|
||||
uintmax_t orig_id, new_id;
|
||||
char *name = NULL;
|
||||
char *colon;
|
||||
|
||||
|
||||
++line;
|
||||
if (wordsplit (buf, &ws, wsopt))
|
||||
FATAL_ERROR ((0, 0, _("%s:%u: cannot split line: %s"),
|
||||
@@ -114,7 +112,7 @@ map_read (Hash_table **ptab, char const *file,
|
||||
|
||||
if (ws.ws_wordv[0][0] == '+')
|
||||
{
|
||||
if (parse_id (&orig_id, ws.ws_wordv[0]+1, what, maxval, file, line))
|
||||
if (!parse_id (&orig_id, ws.ws_wordv[0]+1, what, maxval, file, line))
|
||||
{
|
||||
err = 1;
|
||||
continue;
|
||||
@@ -138,7 +136,7 @@ map_read (Hash_table **ptab, char const *file,
|
||||
if (colon > ws.ws_wordv[1])
|
||||
name = ws.ws_wordv[1];
|
||||
*colon++ = 0;
|
||||
if (parse_id (&new_id, colon, what, maxval, file, line))
|
||||
if (!parse_id (&new_id, colon, what, maxval, file, line))
|
||||
{
|
||||
err = 1;
|
||||
continue;
|
||||
@@ -146,7 +144,7 @@ map_read (Hash_table **ptab, char const *file,
|
||||
}
|
||||
else if (ws.ws_wordv[1][0] == '+')
|
||||
{
|
||||
if (parse_id (&new_id, ws.ws_wordv[1], what, maxval, file, line))
|
||||
if (!parse_id (&new_id, ws.ws_wordv[1], what, maxval, file, line))
|
||||
{
|
||||
err = 1;
|
||||
continue;
|
||||
@@ -169,7 +167,7 @@ map_read (Hash_table **ptab, char const *file,
|
||||
ent->orig_id = orig_id;
|
||||
ent->new_id = new_id;
|
||||
ent->new_name = name ? xstrdup (name) : NULL;
|
||||
|
||||
|
||||
if (!((*ptab
|
||||
|| (*ptab = hash_initialize (0, 0, map_hash, map_compare, 0)))
|
||||
&& hash_insert (*ptab, ent)))
|
||||
@@ -203,11 +201,11 @@ int
|
||||
owner_map_translate (uid_t uid, uid_t *new_uid, char const **new_name)
|
||||
{
|
||||
int rc = 1;
|
||||
|
||||
|
||||
if (owner_map)
|
||||
{
|
||||
struct mapentry ent, *res;
|
||||
|
||||
|
||||
ent.orig_id = uid;
|
||||
res = hash_lookup (owner_map, &ent);
|
||||
if (res)
|
||||
@@ -253,11 +251,11 @@ int
|
||||
group_map_translate (gid_t gid, gid_t *new_gid, char const **new_name)
|
||||
{
|
||||
int rc = 1;
|
||||
|
||||
|
||||
if (group_map)
|
||||
{
|
||||
struct mapentry ent, *res;
|
||||
|
||||
|
||||
ent.orig_id = gid;
|
||||
res = hash_lookup (group_map, &ent);
|
||||
if (res)
|
||||
@@ -278,6 +276,6 @@ group_map_translate (gid_t gid, gid_t *new_gid, char const **new_name)
|
||||
*new_name = group_name_option;
|
||||
rc = 0;
|
||||
}
|
||||
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
163
src/misc.c
163
src/misc.c
@@ -400,51 +400,102 @@ timetostr (time_t t, char buf[SYSINT_BUFSIZE])
|
||||
return sysinttostr (t, TYPE_MINIMUM (time_t), TYPE_MAXIMUM (time_t), buf);
|
||||
}
|
||||
|
||||
/* Convert a prefix of the string ARG to a system integer type whose
|
||||
minimum value is MINVAL and maximum MAXVAL. If MINVAL is negative,
|
||||
/* Convert a prefix of the string ARG to a system integer type.
|
||||
If ARGLIM, set *ARGLIM to point to just after the prefix.
|
||||
If OVERFLOW, set *OVERFLOW to true or false
|
||||
depending on whether the input is out of MINVAL..MAXVAL range.
|
||||
If the input is out of that range, return an extreme value.
|
||||
MINVAL must not be positive.
|
||||
|
||||
If MINVAL is negative, MAXVAL can be at most INTMAX_MAX, and
|
||||
negative integers MINVAL .. -1 are assumed to be represented using
|
||||
leading '-' in the usual way. If the represented value exceeds
|
||||
INTMAX_MAX, return a negative integer V such that (uintmax_t) V
|
||||
yields the represented value. If ARGLIM is nonnull, store into
|
||||
*ARGLIM a pointer to the first character after the prefix.
|
||||
yields the represented value.
|
||||
|
||||
On conversion error: if ARGLIM set *ARGLIM = ARG; if OVERFLOW set
|
||||
*OVERFLOW = false; then return 0.
|
||||
|
||||
This is the inverse of sysinttostr.
|
||||
|
||||
On a normal return, set errno = 0.
|
||||
On conversion error, return 0 and set errno = EINVAL.
|
||||
On overflow, return an extreme value and set errno = ERANGE. */
|
||||
Sample call to this function:
|
||||
|
||||
char *s_end;
|
||||
bool overflow;
|
||||
idx_t i = stoint (s, &s_end, &overflow, 0, IDX_MAX);
|
||||
if ((s_end == s) | *s_end | overflow)
|
||||
diagnose_invalid (s);
|
||||
|
||||
This example uses "|" instead of "||" for fewer branches at runtime,
|
||||
which tends to be more efficient on modern processors.
|
||||
|
||||
This function is named "stoint" instead of "strtoint" because
|
||||
<string.h> reserves names beginning with "str". */
|
||||
|
||||
intmax_t
|
||||
strtosysint (char const *arg, char **arglim, intmax_t minval, uintmax_t maxval)
|
||||
stoint (char const *arg, char **arglim, bool *overflow,
|
||||
intmax_t minval, uintmax_t maxval)
|
||||
{
|
||||
static_assert (INTMAX_MAX <= UINTMAX_MAX);
|
||||
char const *p = arg;
|
||||
intmax_t i;
|
||||
bool v = false;
|
||||
|
||||
errno = 0;
|
||||
if (maxval <= INTMAX_MAX)
|
||||
if (c_isdigit (*p))
|
||||
{
|
||||
if (c_isdigit (arg[*arg == '-']))
|
||||
if (minval < 0)
|
||||
{
|
||||
intmax_t i = strtoimax (arg, arglim, 10);
|
||||
intmax_t imaxval = maxval;
|
||||
if (minval <= i && i <= imaxval)
|
||||
return i;
|
||||
errno = ERANGE;
|
||||
return i < minval ? minval : maxval;
|
||||
i = *p - '0';
|
||||
|
||||
while (c_isdigit (*++p))
|
||||
{
|
||||
v |= ckd_mul (&i, i, 10);
|
||||
v |= ckd_add (&i, i, *p - '0');
|
||||
}
|
||||
|
||||
v |= maxval < i;
|
||||
if (v)
|
||||
i = maxval;
|
||||
}
|
||||
else
|
||||
{
|
||||
uintmax_t u = *p - '0';
|
||||
|
||||
while (c_isdigit (*++p))
|
||||
{
|
||||
v |= ckd_mul (&u, u, 10);
|
||||
v |= ckd_add (&u, u, *p - '0');
|
||||
}
|
||||
|
||||
v |= maxval < u;
|
||||
if (v)
|
||||
u = maxval;
|
||||
i = represent_uintmax (u);
|
||||
}
|
||||
}
|
||||
else if (minval < 0 && *p == '-' && c_isdigit (p[1]))
|
||||
{
|
||||
p++;
|
||||
i = - (*p - '0');
|
||||
|
||||
while (c_isdigit (*++p))
|
||||
{
|
||||
v |= ckd_mul (&i, i, 10);
|
||||
v |= ckd_sub (&i, i, *p - '0');
|
||||
}
|
||||
|
||||
v |= i < minval;
|
||||
if (v)
|
||||
i = minval;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c_isdigit (*arg))
|
||||
{
|
||||
uintmax_t i = strtoumax (arg, arglim, 10);
|
||||
if (i <= maxval)
|
||||
return represent_uintmax (i);
|
||||
errno = ERANGE;
|
||||
return maxval;
|
||||
}
|
||||
}
|
||||
i = 0;
|
||||
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
if (arglim)
|
||||
*arglim = (char *) p;
|
||||
if (overflow)
|
||||
*overflow = v;
|
||||
return i;
|
||||
}
|
||||
|
||||
/* Output fraction and trailing digits appropriate for a nanoseconds
|
||||
@@ -507,36 +558,13 @@ code_timespec (struct timespec t, char tsbuf[TIMESPEC_STRSIZE_BOUND])
|
||||
struct timespec
|
||||
decode_timespec (char const *arg, char **arg_lim, bool parse_fraction)
|
||||
{
|
||||
time_t s = TYPE_MINIMUM (time_t);
|
||||
int ns = -1;
|
||||
char const *p = arg;
|
||||
bool negative = *arg == '-';
|
||||
struct timespec r;
|
||||
|
||||
if (! c_isdigit (arg[negative]))
|
||||
errno = EINVAL;
|
||||
else
|
||||
bool overflow;
|
||||
time_t s = stoint (arg, arg_lim, &overflow,
|
||||
TYPE_MINIMUM (time_t), TYPE_MAXIMUM (time_t));
|
||||
char const *p = *arg_lim;
|
||||
if (p != arg)
|
||||
{
|
||||
errno = 0;
|
||||
|
||||
if (negative)
|
||||
{
|
||||
intmax_t i = strtoimax (arg, arg_lim, 10);
|
||||
if (TYPE_SIGNED (time_t) ? TYPE_MINIMUM (time_t) <= i : 0 <= i)
|
||||
s = i;
|
||||
else
|
||||
errno = ERANGE;
|
||||
}
|
||||
else
|
||||
{
|
||||
uintmax_t i = strtoumax (arg, arg_lim, 10);
|
||||
if (i <= TYPE_MAXIMUM (time_t))
|
||||
s = i;
|
||||
else
|
||||
errno = ERANGE;
|
||||
}
|
||||
|
||||
p = *arg_lim;
|
||||
ns = 0;
|
||||
|
||||
if (parse_fraction && *p == '.')
|
||||
@@ -550,36 +578,27 @@ decode_timespec (char const *arg, char **arg_lim, bool parse_fraction)
|
||||
else
|
||||
trailing_nonzero |= *p != '0';
|
||||
|
||||
*arg_lim = (char *) p;
|
||||
|
||||
while (digits < LOG10_BILLION)
|
||||
digits++, ns *= 10;
|
||||
|
||||
if (negative)
|
||||
if (*arg == '-')
|
||||
{
|
||||
/* Convert "-1.10000000000001" to s == -2, ns == 89999999.
|
||||
I.e., truncate time stamps towards minus infinity while
|
||||
converting them to internal form. */
|
||||
ns += trailing_nonzero;
|
||||
if (ns != 0)
|
||||
{
|
||||
if (s == TYPE_MINIMUM (time_t))
|
||||
ns = -1;
|
||||
else
|
||||
{
|
||||
s--;
|
||||
ns = BILLION - ns;
|
||||
}
|
||||
}
|
||||
ns = ckd_sub (&s, s, 1) ? -1 : BILLION - ns;
|
||||
}
|
||||
}
|
||||
|
||||
if (errno == ERANGE)
|
||||
if (overflow)
|
||||
ns = -1;
|
||||
}
|
||||
|
||||
*arg_lim = (char *) p;
|
||||
r.tv_sec = s;
|
||||
r.tv_nsec = ns;
|
||||
return r;
|
||||
return (struct timespec) { .tv_sec = s, .tv_nsec = ns };
|
||||
}
|
||||
|
||||
/* File handling. */
|
||||
|
||||
16
src/sparse.c
16
src/sparse.c
@@ -1249,20 +1249,10 @@ pax_dump_header (struct tar_sparse_file *file)
|
||||
static bool
|
||||
decode_num (uintmax_t *num, char const *arg, uintmax_t maxval)
|
||||
{
|
||||
uintmax_t u;
|
||||
char *arg_lim;
|
||||
|
||||
if (!c_isdigit (*arg))
|
||||
return false;
|
||||
|
||||
errno = 0;
|
||||
u = strtoumax (arg, &arg_lim, 10);
|
||||
|
||||
if (! (u <= maxval && errno != ERANGE) || *arg_lim)
|
||||
return false;
|
||||
|
||||
*num = u;
|
||||
return true;
|
||||
bool overflow;
|
||||
*num = stoint (arg, &arg_lim, &overflow, 0, maxval);
|
||||
return ! ((arg_lim == arg) | *arg_lim | overflow);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
||||
109
src/tar.c
109
src/tar.c
@@ -60,7 +60,7 @@ uintmax_t occurrence_option;
|
||||
enum old_files old_files_option;
|
||||
bool keep_directory_symlink_option;
|
||||
const char *listed_incremental_option;
|
||||
int incremental_level;
|
||||
signed char incremental_level;
|
||||
bool check_device_option;
|
||||
struct mode_change *mode_option;
|
||||
mode_t initial_umask;
|
||||
@@ -1345,50 +1345,24 @@ expand_pax_option (struct tar_args *targs, const char *arg)
|
||||
static uintmax_t
|
||||
parse_owner_group (char *arg, uintmax_t field_max, char const **name_option)
|
||||
{
|
||||
uintmax_t u = UINTMAX_MAX;
|
||||
char *end;
|
||||
char const *name = 0;
|
||||
char const *invalid_num = 0;
|
||||
char const *name = NULL;
|
||||
char const *num = arg;
|
||||
char *colon = strchr (arg, ':');
|
||||
|
||||
if (colon)
|
||||
{
|
||||
char const *num = colon + 1;
|
||||
num = colon + 1;
|
||||
*colon = '\0';
|
||||
if (*arg)
|
||||
if (arg != colon)
|
||||
name = arg;
|
||||
if (num && (! (xstrtoumax (num, &end, 10, &u, "") == LONGINT_OK
|
||||
&& u <= field_max)))
|
||||
invalid_num = num;
|
||||
}
|
||||
else
|
||||
{
|
||||
uintmax_t u1;
|
||||
switch ('0' <= *arg && *arg <= '9'
|
||||
? xstrtoumax (arg, &end, 10, &u1, "")
|
||||
: LONGINT_INVALID)
|
||||
{
|
||||
default:
|
||||
name = arg;
|
||||
break;
|
||||
|
||||
case LONGINT_OK:
|
||||
if (u1 <= field_max)
|
||||
{
|
||||
u = u1;
|
||||
break;
|
||||
}
|
||||
FALLTHROUGH;
|
||||
case LONGINT_OVERFLOW:
|
||||
invalid_num = arg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (invalid_num)
|
||||
FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (invalid_num),
|
||||
bool overflow;
|
||||
char *end;
|
||||
uintmax_t u = stoint (num, &end, &overflow, 0, field_max);
|
||||
if ((end == num) | *end | overflow)
|
||||
FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (num),
|
||||
_("Invalid owner or group ID")));
|
||||
if (name)
|
||||
if (name_option)
|
||||
*name_option = name;
|
||||
return u;
|
||||
}
|
||||
@@ -1498,14 +1472,15 @@ parse_opt (int key, char *arg, struct argp_state *state)
|
||||
|
||||
case 'b':
|
||||
{
|
||||
uintmax_t u;
|
||||
if (! (xstrtoumax (arg, 0, 10, &u, "") == LONGINT_OK
|
||||
&& !ckd_add (&blocking_factor, u, 0)
|
||||
&& 0 < blocking_factor
|
||||
&& !ckd_mul (&record_size, u, BLOCKSIZE)
|
||||
&& record_size <= min (SSIZE_MAX, SIZE_MAX)))
|
||||
bool overflow;
|
||||
char *end;
|
||||
blocking_factor = stoint (arg, &end, &overflow, 0,
|
||||
(min (IDX_MAX, min (SSIZE_MAX, SIZE_MAX))
|
||||
/ BLOCKSIZE));
|
||||
if ((end == arg) | *end | overflow | !blocking_factor)
|
||||
USAGE_ERROR ((0, 0, "%s: %s", quotearg_colon (arg),
|
||||
_("Invalid blocking factor")));
|
||||
record_size = blocking_factor * BLOCKSIZE;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1712,9 +1687,9 @@ parse_opt (int key, char *arg, struct argp_state *state)
|
||||
|
||||
case LEVEL_OPTION:
|
||||
{
|
||||
uintmax_t u;
|
||||
if (! (xstrtoumax (arg, nullptr, 10, &u, "") == LONGINT_OK
|
||||
&& ckd_add (&incremental_level, u, 0)))
|
||||
char *end;
|
||||
incremental_level = stoint (arg, &end, NULL, 0, 1);
|
||||
if ((end == arg) | *end)
|
||||
USAGE_ERROR ((0, 0, _("Invalid incremental level value")));
|
||||
}
|
||||
break;
|
||||
@@ -1832,26 +1807,11 @@ parse_opt (int key, char *arg, struct argp_state *state)
|
||||
sparse_option = true;
|
||||
{
|
||||
char *p;
|
||||
bool ok;
|
||||
switch (xstrtoimax (arg, &p, 10, &tar_sparse_major, ""))
|
||||
{
|
||||
case LONGINT_OK:
|
||||
tar_sparse_minor = 0;
|
||||
ok = 0 <= tar_sparse_major;
|
||||
break;
|
||||
|
||||
case LONGINT_INVALID_SUFFIX_CHAR:
|
||||
ok = (*p == '.'
|
||||
&& (xstrtoimax (p + 1, nullptr, 10, &tar_sparse_minor, "")
|
||||
== LONGINT_OK)
|
||||
&& 0 <= tar_sparse_minor && 0 <= tar_sparse_major);
|
||||
break;
|
||||
|
||||
default:
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
if (!ok)
|
||||
bool vmajor, vminor;
|
||||
tar_sparse_major = stoint (arg, &p, &vmajor, 0, INTMAX_MAX);
|
||||
if ((p != arg) & (*p == '.'))
|
||||
tar_sparse_minor = stoint (p + 1, &p, &vminor, 0, INTMAX_MAX);
|
||||
if ((p == arg) | *p | vmajor | vminor)
|
||||
USAGE_ERROR ((0, 0, _("Invalid sparse version value")));
|
||||
}
|
||||
break;
|
||||
@@ -1949,8 +1909,8 @@ parse_opt (int key, char *arg, struct argp_state *state)
|
||||
checkpoint_compile_action (".");
|
||||
arg++;
|
||||
}
|
||||
checkpoint_option = strtoimax (arg, &p, 0);
|
||||
if (*p || checkpoint_option <= 0)
|
||||
checkpoint_option = stoint (arg, &p, NULL, 0, INTMAX_MAX);
|
||||
if (*p | (checkpoint_option <= 0))
|
||||
FATAL_ERROR ((0, 0,
|
||||
_("invalid --checkpoint value")));
|
||||
}
|
||||
@@ -2058,10 +2018,9 @@ parse_opt (int key, char *arg, struct argp_state *state)
|
||||
occurrence_option = 1;
|
||||
else
|
||||
{
|
||||
uintmax_t u;
|
||||
if (xstrtoumax (arg, 0, 10, &u, "") == LONGINT_OK)
|
||||
occurrence_option = u;
|
||||
else
|
||||
char *end;
|
||||
occurrence_option = stoint (arg, &end, NULL, 0, UINTMAX_MAX);
|
||||
if (*end)
|
||||
FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (arg),
|
||||
_("Invalid number")));
|
||||
}
|
||||
@@ -2172,9 +2131,9 @@ parse_opt (int key, char *arg, struct argp_state *state)
|
||||
|
||||
case STRIP_COMPONENTS_OPTION:
|
||||
{
|
||||
uintmax_t u;
|
||||
if (! (xstrtoumax (arg, 0, 10, &u, "") == LONGINT_OK
|
||||
&& !ckd_add (&strip_name_components, u, 0)))
|
||||
char *end;
|
||||
strip_name_components = stoint (arg, &end, NULL, 0, SIZE_MAX);
|
||||
if (*end)
|
||||
USAGE_ERROR ((0, 0, "%s: %s", quotearg_colon (arg),
|
||||
_("Invalid number of elements")));
|
||||
}
|
||||
|
||||
@@ -250,10 +250,7 @@ parse_transform_expr (const char *expr)
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
{
|
||||
char *endp;
|
||||
intmax_t match_number = strtoimax (p, &endp, 10);
|
||||
assume (0 <= match_number);
|
||||
if (ckd_add (&tf->match_number, match_number, 0))
|
||||
tf->match_number = IDX_MAX;
|
||||
tf->match_number = stoint (p, &endp, NULL, 0, IDX_MAX);
|
||||
p = endp - 1;
|
||||
}
|
||||
break;
|
||||
@@ -302,8 +299,7 @@ parse_transform_expr (const char *expr)
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
{
|
||||
intmax_t n = strtoimax (cur, &cur, 10);
|
||||
assume (0 <= n);
|
||||
idx_t n = stoint (cur, &cur, NULL, 0, IDX_MAX);
|
||||
if (tf->regex.re_nsub < n)
|
||||
USAGE_ERROR ((0, 0, _("Invalid transform replacement:"
|
||||
" back reference out of range")));
|
||||
|
||||
@@ -608,27 +608,30 @@ decode_record (struct xheader *xhdr,
|
||||
{
|
||||
char *start = *ptr;
|
||||
char *p = start;
|
||||
size_t len;
|
||||
char *len_lim;
|
||||
char const *keyword;
|
||||
char *nextp;
|
||||
size_t len_max = xhdr->buffer + xhdr->size - start;
|
||||
idx_t len_max = xhdr->buffer + xhdr->size - start;
|
||||
|
||||
while (*p == ' ' || *p == '\t')
|
||||
p++;
|
||||
|
||||
if (! c_isdigit (*p))
|
||||
idx_t len = stoint (p, &len_lim, NULL, 0, IDX_MAX);
|
||||
|
||||
if (len_lim == p)
|
||||
{
|
||||
/* The length is missing.
|
||||
FIXME: Comment why this is diagnosed only if (*p), or change code. */
|
||||
if (*p)
|
||||
ERROR ((0, 0, _("Malformed extended header: missing length")));
|
||||
return false;
|
||||
}
|
||||
|
||||
len = strtoumax (p, &len_lim, 10);
|
||||
|
||||
if (len_max < len)
|
||||
{
|
||||
int len_len = len_lim - p;
|
||||
/* Avoid giant diagnostics, as this won't help user. */
|
||||
int len_len = min (len_lim - p, 1000);
|
||||
|
||||
ERROR ((0, 0, _("Extended header length %.*s is out of range"),
|
||||
len_len, p));
|
||||
return false;
|
||||
@@ -1087,16 +1090,17 @@ decode_signed_num (intmax_t *num, char const *arg,
|
||||
char const *keyword)
|
||||
{
|
||||
char *arg_lim;
|
||||
intmax_t u = strtosysint (arg, &arg_lim, minval, maxval);
|
||||
bool overflow;
|
||||
intmax_t u = stoint (arg, &arg_lim, &overflow, minval, maxval);
|
||||
|
||||
if (errno == EINVAL || *arg_lim)
|
||||
if ((arg_lim == arg) | *arg_lim)
|
||||
{
|
||||
ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"),
|
||||
keyword, arg));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (errno == ERANGE)
|
||||
if (overflow)
|
||||
{
|
||||
out_of_range_header (keyword, arg, minval, maxval);
|
||||
return false;
|
||||
@@ -1433,33 +1437,26 @@ sparse_map_decoder (struct tar_stat_info *st,
|
||||
char const *arg,
|
||||
MAYBE_UNUSED size_t size)
|
||||
{
|
||||
int offset = 1;
|
||||
bool offset = true;
|
||||
struct sp_array e;
|
||||
|
||||
st->sparse_map_avail = 0;
|
||||
while (1)
|
||||
while (true)
|
||||
{
|
||||
intmax_t u;
|
||||
char *delim;
|
||||
|
||||
if (!c_isdigit (*arg))
|
||||
bool overflow;
|
||||
off_t u = stoint (arg, &delim, &overflow, 0, TYPE_MAXIMUM (off_t));
|
||||
if (delim == arg)
|
||||
{
|
||||
ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"),
|
||||
keyword, arg));
|
||||
return;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
u = strtoimax (arg, &delim, 10);
|
||||
if (TYPE_MAXIMUM (off_t) < u)
|
||||
{
|
||||
u = TYPE_MAXIMUM (off_t);
|
||||
errno = ERANGE;
|
||||
}
|
||||
if (offset)
|
||||
{
|
||||
e.offset = u;
|
||||
if (errno == ERANGE)
|
||||
if (overflow)
|
||||
{
|
||||
out_of_range_header (keyword, arg, 0, TYPE_MAXIMUM (off_t));
|
||||
return;
|
||||
@@ -1468,7 +1465,7 @@ sparse_map_decoder (struct tar_stat_info *st,
|
||||
else
|
||||
{
|
||||
e.numbytes = u;
|
||||
if (errno == ERANGE)
|
||||
if (overflow)
|
||||
{
|
||||
out_of_range_header (keyword, arg, 0, TYPE_MAXIMUM (off_t));
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user