summaryrefslogtreecommitdiff
path: root/src/incremen.c
diff options
context:
space:
mode:
authorPaul Eggert <eggert@cs.ucla.edu>2012-12-22 20:41:23 -0800
committerPaul Eggert <eggert@cs.ucla.edu>2012-12-22 20:41:52 -0800
commitdf7b55a8f6354e30e8da62eec7f706df033d0c81 (patch)
treed63dcea973fdeb2922b9905a551221232315eb07 /src/incremen.c
parentd8ac237663db9817a688af6711320ad66f1d9e94 (diff)
downloadtar-df7b55a8f6354e30e8da62eec7f706df033d0c81.tar.gz
Fix some problems with negative and out-of-range integers.
Original problem reported for HP-UX LVM v2.2 by Michael White in <http://lists.gnu.org/archive/html/bug-tar/2012-10/msg00000.html>. This patch fixes some other gotchas that I noticed. * gnulib.modules: Add extern-inline. * src/common.h: Use _GL_INLINE_HEADER_BEGIN, _GL_INLINE_HEADER_END. (COMMON_INLINE, max, min): New macros. (represent_uintmax, valid_timespec): New inline functions. (SYSINT_BUFSIZE): New constant. (sysinttostr, strtosysint, decode_timespec): New decls. * src/create.c (start_private_header): Silently bring the time_t value into range; it is now the caller's responsibility to deal with any overflow error. Use uid 0 and gid 0 rather than the user's uid/gid, since the faked header isn't "owned" by the user and the uid/gid could in theory be out of range. Leave major and minor zeroed. (FILL): Remove. (write_gnu_long_link): Let start_private_header zero things out. * src/create.c (write_gnu_long_link, write_extended): * src/xheader.c (xheader_write_global): Use start_time, not current time; no point hammering on the clock. * src/compare.c (diff_multivol): Check that offset, size are in range. * src/incremen.c (read_incr_db_01, write_directory_file_entry): Allow negative time_t, dev_t, and ino_t. * src/list.c (max): Remove (moved to common.h). (read_header): Check that size is in range. (from_header): Return intmax_t, not uintmax_t, to allow negative. All callers changed. At compile time, check assumptions about intmax_t and uintmax_t. Use bool for booleans. Avoid overflow hassles on picky hosts. (mode_from_header): Last arg is now bool *, not unsigned *. All callers changed. (simple_print_header): Do not assume UID, GID fit in 'long'. * src/list.c (from_header): * src/xheader.c (out_of_range_header): Arg is now a plain minimum value, not minus minval converted to uintmax_t. All callers changed. * src/misc.c (COMMON_INLINE): New macro. (sysinttostr, strtosysint, decode_timespec): New functions. * src/sparse.c (oldgnu_add_sparse, oldgnu_fixup_header) (star_fixup_header): Check for offset overflow. (decode_num): Clear errno before calling strtoumax. * src/tar.c (expand_pax_option): Don't discard nanoseconds. * src/xheader.c (assign_time_option): Allow negative time_t. (decode_record): Simplify, since out-of-range string is guaranteed to produce a value exceeding len_max. (xheader_read): Last arg is off_t, not size_t. Caller should diagnose negative arg, as needed. Check that it's in range. (enum decode_time_status): Remove. (_decode_time): Remove, folding into decode_time. (decode_time): Return bool, not enum decode_time_status. Rely on decode_timespec to do most of the work. (code_signed_num): New function. (code_num): Use it. (decode_signed_num): New function. (decode_num): Use it. (gid_coder, gid_decoder, uid_coder, uid_decoder, sparse_map_decoder) (sparse_map_decoder): Code and decode negative values. (sparse_map_decoder): Improve check for out-of-range values. * tests/time01.at: New file. * tests/Makefile.am (TESTSUITE_AT): Add it. * tests/testsuite.at: Include it.
Diffstat (limited to 'src/incremen.c')
-rw-r--r--src/incremen.c112
1 files changed, 37 insertions, 75 deletions
diff --git a/src/incremen.c b/src/incremen.c
index d5bc1e41..540afc64 100644
--- a/src/incremen.c
+++ b/src/incremen.c
@@ -447,7 +447,7 @@ procdir (const char *name_buffer, struct tar_stat_info *st,
struct stat *stat_data = &st->stat;
bool nfs = NFS_FILE_STAT (*stat_data);
bool perhaps_renamed = false;
-
+
if ((directory = find_directory (name_buffer)) != NULL)
{
if (DIR_IS_INITED (directory))
@@ -520,7 +520,7 @@ procdir (const char *name_buffer, struct tar_stat_info *st,
stat_data->st_ino);
directory = note_directory (name_buffer,
- get_stat_mtime(stat_data),
+ get_stat_mtime (stat_data),
stat_data->st_dev,
stat_data->st_ino,
nfs,
@@ -573,7 +573,7 @@ procdir (const char *name_buffer, struct tar_stat_info *st,
}
perhaps_renamed = false;
}
-
+
else if (flag & PD_FORCE_CHILDREN)
{
directory->children = PD_CHILDREN(flag);
@@ -946,8 +946,6 @@ read_incr_db_01 (int version, const char *initbuf)
{
int n;
uintmax_t u;
- time_t sec;
- long int nsec;
char *buf = NULL;
size_t bufsize = 0;
char *ebuf;
@@ -969,21 +967,15 @@ read_incr_db_01 (int version, const char *initbuf)
bufsize = strlen (buf) + 1;
}
- sec = TYPE_MINIMUM (time_t);
- nsec = -1;
- errno = 0;
- u = strtoumax (buf, &ebuf, 10);
- if (!errno && TYPE_MAXIMUM (time_t) < u)
- errno = ERANGE;
- if (errno || buf == ebuf)
+ newer_mtime_option = decode_timespec (buf, &ebuf, false);
+
+ if (! valid_timespec (newer_mtime_option))
ERROR ((0, errno, "%s:%ld: %s",
quotearg_colon (listed_incremental_option),
lineno,
_("Invalid time stamp")));
else
{
- sec = u;
-
if (version == 1 && *ebuf)
{
char const *buf_ns = ebuf + 1;
@@ -997,20 +989,13 @@ read_incr_db_01 (int version, const char *initbuf)
quotearg_colon (listed_incremental_option),
lineno,
_("Invalid time stamp")));
- sec = TYPE_MINIMUM (time_t);
+ newer_mtime_option.tv_sec = TYPE_MINIMUM (time_t);
+ newer_mtime_option.tv_nsec = -1;
}
else
- nsec = u;
- }
- else
- {
- /* pre-1 incremental format does not contain nanoseconds */
- nsec = 0;
+ newer_mtime_option.tv_nsec = u;
}
}
- newer_mtime_option.tv_sec = sec;
- newer_mtime_option.tv_nsec = nsec;
-
while (0 < (n = getline (&buf, &bufsize, listed_incremental_stream)))
{
@@ -1027,20 +1012,12 @@ read_incr_db_01 (int version, const char *initbuf)
if (version == 1)
{
- errno = 0;
- u = strtoumax (strp, &ebuf, 10);
- if (!errno && TYPE_MAXIMUM (time_t) < u)
- errno = ERANGE;
- if (errno || strp == ebuf || *ebuf != ' ')
- {
- ERROR ((0, errno, "%s:%ld: %s",
- quotearg_colon (listed_incremental_option), lineno,
- _("Invalid modification time (seconds)")));
- sec = (time_t) -1;
- }
- else
- sec = u;
+ mtime = decode_timespec (strp, &ebuf, false);
strp = ebuf;
+ if (!valid_timespec (mtime) || *strp != ' ')
+ ERROR ((0, errno, "%s:%ld: %s",
+ quotearg_colon (listed_incremental_option), lineno,
+ _("Invalid modification time")));
errno = 0;
u = strtoumax (strp, &ebuf, 10);
@@ -1051,46 +1028,30 @@ read_incr_db_01 (int version, const char *initbuf)
ERROR ((0, errno, "%s:%ld: %s",
quotearg_colon (listed_incremental_option), lineno,
_("Invalid modification time (nanoseconds)")));
- nsec = -1;
+ mtime.tv_nsec = -1;
}
else
- nsec = u;
- mtime.tv_sec = sec;
- mtime.tv_nsec = nsec;
+ mtime.tv_nsec = u;
strp = ebuf;
}
else
- memset (&mtime, 0, sizeof mtime);
+ mtime.tv_sec = mtime.tv_nsec = 0;
- errno = 0;
- u = strtoumax (strp, &ebuf, 10);
- if (!errno && TYPE_MAXIMUM (dev_t) < u)
- errno = ERANGE;
- if (errno || strp == ebuf || *ebuf != ' ')
- {
- ERROR ((0, errno, "%s:%ld: %s",
- quotearg_colon (listed_incremental_option), lineno,
- _("Invalid device number")));
- dev = (dev_t) -1;
- }
- else
- dev = u;
+ dev = strtosysint (strp, &ebuf,
+ TYPE_MINIMUM (dev_t), TYPE_MAXIMUM (dev_t));
strp = ebuf;
+ if (errno || *strp != ' ')
+ ERROR ((0, errno, "%s:%ld: %s",
+ quotearg_colon (listed_incremental_option), lineno,
+ _("Invalid device number")));
- errno = 0;
- u = strtoumax (strp, &ebuf, 10);
- if (!errno && TYPE_MAXIMUM (ino_t) < u)
- errno = ERANGE;
- if (errno || strp == ebuf || *ebuf != ' ')
- {
- ERROR ((0, errno, "%s:%ld: %s",
- quotearg_colon (listed_incremental_option), lineno,
- _("Invalid inode number")));
- ino = (ino_t) -1;
- }
- else
- ino = u;
+ ino = strtosysint (strp, &ebuf,
+ TYPE_MINIMUM (ino_t), TYPE_MAXIMUM (ino_t));
strp = ebuf;
+ if (errno || *strp != ' ')
+ ERROR ((0, errno, "%s:%ld: %s",
+ quotearg_colon (listed_incremental_option), lineno,
+ _("Invalid inode number")));
strp++;
unquote_string (strp);
@@ -1391,20 +1352,21 @@ write_directory_file_entry (void *entry, void *data)
if (DIR_IS_FOUND (directory))
{
- char buf[UINTMAX_STRSIZE_BOUND];
+ char buf[max (SYSINT_BUFSIZE, INT_BUFSIZE_BOUND (intmax_t))];
char const *s;
s = DIR_IS_NFS (directory) ? "1" : "0";
fwrite (s, 2, 1, fp);
- s = (TYPE_SIGNED (time_t)
- ? imaxtostr (directory->mtime.tv_sec, buf)
- : umaxtostr (directory->mtime.tv_sec, buf));
+ s = sysinttostr (directory->mtime.tv_sec, TYPE_MINIMUM (time_t),
+ TYPE_MAXIMUM (time_t), buf);
fwrite (s, strlen (s) + 1, 1, fp);
- s = umaxtostr (directory->mtime.tv_nsec, buf);
+ s = imaxtostr (directory->mtime.tv_nsec, buf);
fwrite (s, strlen (s) + 1, 1, fp);
- s = umaxtostr (directory->device_number, buf);
+ s = sysinttostr (directory->device_number,
+ TYPE_MINIMUM (dev_t), TYPE_MAXIMUM (dev_t), buf);
fwrite (s, strlen (s) + 1, 1, fp);
- s = umaxtostr (directory->inode_number, buf);
+ s = sysinttostr (directory->inode_number,
+ TYPE_MINIMUM (ino_t), TYPE_MAXIMUM (ino_t), buf);
fwrite (s, strlen (s) + 1, 1, fp);
fwrite (directory->name, strlen (directory->name) + 1, 1, fp);