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.
This commit is contained in:
@@ -12,6 +12,7 @@ configmake
|
||||
dirname
|
||||
error
|
||||
exclude
|
||||
extern-inline
|
||||
exitfail
|
||||
fchmodat
|
||||
fchownat
|
||||
|
||||
42
src/common.h
42
src/common.h
@@ -70,6 +70,11 @@
|
||||
#define LG_8 3
|
||||
#define LG_64 6
|
||||
#define LG_256 8
|
||||
|
||||
_GL_INLINE_HEADER_BEGIN
|
||||
#ifndef COMMON_INLINE
|
||||
# define COMMON_INLINE _GL_INLINE
|
||||
#endif
|
||||
|
||||
/* Information gleaned from the command line. */
|
||||
|
||||
@@ -588,6 +593,8 @@ void skip_member (void);
|
||||
|
||||
/* Module misc.c. */
|
||||
|
||||
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define max(a, b) ((a) < (b) ? (b) : (a))
|
||||
void assign_string (char **dest, const char *src);
|
||||
int unquote_string (char *str);
|
||||
char *zap_slashes (char *name);
|
||||
@@ -600,11 +607,42 @@ namebuf_t namebuf_create (const char *dir);
|
||||
void namebuf_free (namebuf_t buf);
|
||||
char *namebuf_name (namebuf_t buf, const char *name);
|
||||
|
||||
/* Represent N using a signed integer I such that (uintmax_t) I == N.
|
||||
With a good optimizing compiler, this is equivalent to (intmax_t) i
|
||||
and requires zero machine instructions. */
|
||||
#if ! (UINTMAX_MAX / 2 <= INTMAX_MAX)
|
||||
# error "represent_uintmax returns intmax_t to represent uintmax_t"
|
||||
#endif
|
||||
COMMON_INLINE intmax_t
|
||||
represent_uintmax (uintmax_t n)
|
||||
{
|
||||
if (n <= INTMAX_MAX)
|
||||
return n;
|
||||
else
|
||||
{
|
||||
/* Avoid signed integer overflow on picky platforms. */
|
||||
intmax_t nd = n - INTMAX_MIN;
|
||||
return nd + INTMAX_MIN;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
void code_ns_fraction (int ns, char *p);
|
||||
char const *code_timespec (struct timespec ts, char *sbuf);
|
||||
enum { BILLION = 1000000000, LOG10_BILLION = 9 };
|
||||
enum { TIMESPEC_STRSIZE_BOUND =
|
||||
UINTMAX_STRSIZE_BOUND + LOG10_BILLION + sizeof "-." - 1 };
|
||||
struct timespec decode_timespec (char const *, char **, bool);
|
||||
|
||||
/* Return true if T does not represent an out-of-range or invalid value. */
|
||||
COMMON_INLINE bool
|
||||
valid_timespec (struct timespec t)
|
||||
{
|
||||
return 0 <= t.tv_nsec;
|
||||
}
|
||||
|
||||
bool must_be_dot_or_slash (char const *);
|
||||
|
||||
@@ -730,7 +768,7 @@ void xheader_decode (struct tar_stat_info *stat);
|
||||
void xheader_decode_global (struct xheader *xhdr);
|
||||
void xheader_store (char const *keyword, struct tar_stat_info *st,
|
||||
void const *data);
|
||||
void xheader_read (struct xheader *xhdr, union block *header, size_t size);
|
||||
void xheader_read (struct xheader *xhdr, union block *header, off_t size);
|
||||
void xheader_write (char type, char *name, time_t t, struct xheader *xhdr);
|
||||
void xheader_write_global (struct xheader *xhdr);
|
||||
void xheader_finish (struct xheader *hdr);
|
||||
@@ -858,3 +896,5 @@ void finish_deferred_unlinks (void);
|
||||
|
||||
/* Module exit.c */
|
||||
extern void (*fatal_exit_hook) (void);
|
||||
|
||||
_GL_INLINE_HEADER_END
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/* Diff files from a tar archive.
|
||||
|
||||
Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
|
||||
2003, 2004, 2005, 2006, 2007, 2009, 2010 Free Software Foundation, Inc.
|
||||
2003, 2004, 2005, 2006, 2007, 2009, 2010, 2012 Free Software
|
||||
Foundation, Inc.
|
||||
|
||||
Written by John Gilmore, on 1987-04-30.
|
||||
|
||||
@@ -414,7 +415,9 @@ diff_multivol (void)
|
||||
}
|
||||
|
||||
offset = OFF_FROM_HEADER (current_header->oldgnu_header.offset);
|
||||
if (stat_data.st_size != current_stat_info.stat.st_size + offset)
|
||||
if (offset < 0
|
||||
|| INT_ADD_OVERFLOW (current_stat_info.stat.st_size, offset)
|
||||
|| stat_data.st_size != current_stat_info.stat.st_size + offset)
|
||||
{
|
||||
report_difference (¤t_stat_info, _("Size differs"));
|
||||
skip_member ();
|
||||
|
||||
24
src/create.c
24
src/create.c
@@ -512,12 +512,11 @@ start_private_header (const char *name, size_t size, time_t t)
|
||||
tar_name_copy_str (header->header.name, name, NAME_FIELD_SIZE);
|
||||
OFF_TO_CHARS (size, header->header.size);
|
||||
|
||||
TIME_TO_CHARS (t, header->header.mtime);
|
||||
TIME_TO_CHARS (t < 0 ? 0 : min (t, MAX_OCTAL_VAL (header->header.mtime)),
|
||||
header->header.mtime);
|
||||
MODE_TO_CHARS (S_IFREG|S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, header->header.mode);
|
||||
UID_TO_CHARS (getuid (), header->header.uid);
|
||||
GID_TO_CHARS (getgid (), header->header.gid);
|
||||
MAJOR_TO_CHARS (0, header->header.devmajor);
|
||||
MINOR_TO_CHARS (0, header->header.devminor);
|
||||
UID_TO_CHARS (0, header->header.uid);
|
||||
GID_TO_CHARS (0, header->header.gid);
|
||||
strncpy (header->header.magic, TMAGIC, TMAGLEN);
|
||||
strncpy (header->header.version, TVERSION, TVERSLEN);
|
||||
return header;
|
||||
@@ -535,11 +534,6 @@ write_short_name (struct tar_stat_info *st)
|
||||
return header;
|
||||
}
|
||||
|
||||
#define FILL(field,byte) do { \
|
||||
memset(field, byte, sizeof(field)-1); \
|
||||
(field)[sizeof(field)-1] = 0; \
|
||||
} while (0)
|
||||
|
||||
/* Write a GNUTYPE_LONGLINK or GNUTYPE_LONGNAME block. */
|
||||
static void
|
||||
write_gnu_long_link (struct tar_stat_info *st, const char *p, char type)
|
||||
@@ -549,13 +543,7 @@ write_gnu_long_link (struct tar_stat_info *st, const char *p, char type)
|
||||
union block *header;
|
||||
char *tmpname;
|
||||
|
||||
header = start_private_header ("././@LongLink", size, time (NULL));
|
||||
FILL (header->header.mtime, '0');
|
||||
FILL (header->header.mode, '0');
|
||||
FILL (header->header.uid, '0');
|
||||
FILL (header->header.gid, '0');
|
||||
FILL (header->header.devmajor, 0);
|
||||
FILL (header->header.devminor, 0);
|
||||
header = start_private_header ("././@LongLink", size, start_time.tv_sec);
|
||||
uid_to_uname (0, &tmpname);
|
||||
UNAME_TO_CHARS (tmpname, header->header.uname);
|
||||
free (tmpname);
|
||||
@@ -712,7 +700,7 @@ write_extended (bool global, struct tar_stat_info *st, union block *old_header)
|
||||
{
|
||||
type = XGLTYPE;
|
||||
p = xheader_ghdr_name ();
|
||||
time (&t);
|
||||
t = start_time.tv_sec;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
112
src/incremen.c
112
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);
|
||||
|
||||
127
src/list.c
127
src/list.c
@@ -26,8 +26,6 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define max(a, b) ((a) < (b) ? (b) : (a))
|
||||
|
||||
union block *current_header; /* points to current archive header */
|
||||
enum archive_format current_format; /* recognized format */
|
||||
union block *recent_long_name; /* recent long name header and contents */
|
||||
@@ -47,11 +45,11 @@ static union block *recent_global_header; /* Recent global header block */
|
||||
static gid_t gid_from_header (const char *buf, size_t size);
|
||||
static major_t major_from_header (const char *buf, size_t size);
|
||||
static minor_t minor_from_header (const char *buf, size_t size);
|
||||
static mode_t mode_from_header (const char *buf, size_t size, unsigned *hbits);
|
||||
static mode_t mode_from_header (const char *buf, size_t size, bool *hbits);
|
||||
static time_t time_from_header (const char *buf, size_t size);
|
||||
static uid_t uid_from_header (const char *buf, size_t size);
|
||||
static uintmax_t from_header (const char *, size_t, const char *,
|
||||
uintmax_t, uintmax_t, bool, bool);
|
||||
static intmax_t from_header (const char *, size_t, const char *,
|
||||
intmax_t, uintmax_t, bool, bool);
|
||||
|
||||
/* Base 64 digits; see Internet RFC 2045 Table 1. */
|
||||
static char const base_64_digits[64] =
|
||||
@@ -318,7 +316,7 @@ tar_checksum (union block *header, bool silent)
|
||||
int unsigned_sum = 0; /* the POSIX one :-) */
|
||||
int signed_sum = 0; /* the Sun one :-( */
|
||||
int recorded_sum;
|
||||
uintmax_t parsed_sum;
|
||||
int parsed_sum;
|
||||
char *p;
|
||||
|
||||
p = header->buffer;
|
||||
@@ -343,9 +341,8 @@ tar_checksum (union block *header, bool silent)
|
||||
|
||||
parsed_sum = from_header (header->header.chksum,
|
||||
sizeof header->header.chksum, 0,
|
||||
(uintmax_t) 0,
|
||||
(uintmax_t) TYPE_MAXIMUM (int), true, silent);
|
||||
if (parsed_sum == (uintmax_t) -1)
|
||||
0, INT_MAX, true, silent);
|
||||
if (parsed_sum < 0)
|
||||
return HEADER_FAILURE;
|
||||
|
||||
recorded_sum = parsed_sum;
|
||||
@@ -408,7 +405,11 @@ read_header (union block **return_block, struct tar_stat_info *info,
|
||||
if (header->header.typeflag == LNKTYPE)
|
||||
info->stat.st_size = 0; /* links 0 size on tape */
|
||||
else
|
||||
info->stat.st_size = OFF_FROM_HEADER (header->header.size);
|
||||
{
|
||||
info->stat.st_size = OFF_FROM_HEADER (header->header.size);
|
||||
if (info->stat.st_size < 0)
|
||||
return HEADER_FAILURE;
|
||||
}
|
||||
|
||||
if (header->header.typeflag == GNUTYPE_LONGNAME
|
||||
|| header->header.typeflag == GNUTYPE_LONGLINK
|
||||
@@ -573,7 +574,7 @@ decode_header (union block *header, struct tar_stat_info *stat_info,
|
||||
enum archive_format *format_pointer, int do_user_group)
|
||||
{
|
||||
enum archive_format format;
|
||||
unsigned hbits; /* high bits of the file mode. */
|
||||
bool hbits;
|
||||
mode_t mode = MODE_FROM_HEADER (header->header.mode, &hbits);
|
||||
|
||||
if (strcmp (header->header.magic, TMAGIC) == 0)
|
||||
@@ -680,20 +681,30 @@ decode_header (union block *header, struct tar_stat_info *stat_info,
|
||||
|
||||
|
||||
/* Convert buffer at WHERE0 of size DIGS from external format to
|
||||
uintmax_t. DIGS must be positive. If TYPE is nonnull, the data
|
||||
are of type TYPE. The buffer must represent a value in the range
|
||||
-MINUS_MINVAL through MAXVAL. If OCTAL_ONLY, allow only octal
|
||||
intmax_t. DIGS must be positive. If TYPE is nonnull, the data are
|
||||
of type TYPE. The buffer must represent a value in the range
|
||||
MINVAL through MAXVAL; if the mathematically correct result V would
|
||||
be greater than INTMAX_MAX, return a negative integer V such that
|
||||
(uintmax_t) V yields the correct result. If OCTAL_ONLY, allow only octal
|
||||
numbers instead of the other GNU extensions. Return -1 on error,
|
||||
diagnosing the error if TYPE is nonnull and if !SILENT. */
|
||||
static uintmax_t
|
||||
#if ! (INTMAX_MAX <= UINTMAX_MAX && - (INTMAX_MIN + 1) <= UINTMAX_MAX)
|
||||
# error "from_header internally represents intmax_t as uintmax_t + sign"
|
||||
#endif
|
||||
#if ! (UINTMAX_MAX / 2 <= INTMAX_MAX)
|
||||
# error "from_header returns intmax_t to represent uintmax_t"
|
||||
#endif
|
||||
static intmax_t
|
||||
from_header (char const *where0, size_t digs, char const *type,
|
||||
uintmax_t minus_minval, uintmax_t maxval,
|
||||
intmax_t minval, uintmax_t maxval,
|
||||
bool octal_only, bool silent)
|
||||
{
|
||||
uintmax_t value;
|
||||
uintmax_t uminval = minval;
|
||||
uintmax_t minus_minval = - uminval;
|
||||
char const *where = where0;
|
||||
char const *lim = where + digs;
|
||||
int negative = 0;
|
||||
bool negative = false;
|
||||
|
||||
/* Accommodate buggy tar of unknown vintage, which outputs leading
|
||||
NUL if the previous field overflows. */
|
||||
@@ -721,14 +732,14 @@ from_header (char const *where0, size_t digs, char const *type,
|
||||
if (ISODIGIT (*where))
|
||||
{
|
||||
char const *where1 = where;
|
||||
uintmax_t overflow = 0;
|
||||
bool overflow = false;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
value += *where++ - '0';
|
||||
if (where == lim || ! ISODIGIT (*where))
|
||||
break;
|
||||
overflow |= value ^ (value << LG_8 >> LG_8);
|
||||
overflow |= value != (value << LG_8 >> LG_8);
|
||||
value <<= LG_8;
|
||||
}
|
||||
|
||||
@@ -752,7 +763,7 @@ from_header (char const *where0, size_t digs, char const *type,
|
||||
if (where == lim || ! ISODIGIT (*where))
|
||||
break;
|
||||
digit = *where - '0';
|
||||
overflow |= value ^ (value << LG_8 >> LG_8);
|
||||
overflow |= value != (value << LG_8 >> LG_8);
|
||||
value <<= LG_8;
|
||||
}
|
||||
value++;
|
||||
@@ -765,7 +776,7 @@ from_header (char const *where0, size_t digs, char const *type,
|
||||
/* TRANSLATORS: Second %s is a type name (gid_t,uid_t,etc.) */
|
||||
_("Archive octal value %.*s is out of %s range; assuming two's complement"),
|
||||
(int) (where - where1), where1, type));
|
||||
negative = 1;
|
||||
negative = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -845,7 +856,7 @@ from_header (char const *where0, size_t digs, char const *type,
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
negative = signbit;
|
||||
negative = signbit != 0;
|
||||
if (negative)
|
||||
value = -value;
|
||||
}
|
||||
@@ -877,7 +888,7 @@ from_header (char const *where0, size_t digs, char const *type,
|
||||
}
|
||||
|
||||
if (value <= (negative ? minus_minval : maxval))
|
||||
return negative ? -value : value;
|
||||
return represent_uintmax (negative ? -value : value);
|
||||
|
||||
if (type && !silent)
|
||||
{
|
||||
@@ -903,8 +914,7 @@ static gid_t
|
||||
gid_from_header (const char *p, size_t s)
|
||||
{
|
||||
return from_header (p, s, "gid_t",
|
||||
- (uintmax_t) TYPE_MINIMUM (gid_t),
|
||||
(uintmax_t) TYPE_MAXIMUM (gid_t),
|
||||
TYPE_MINIMUM (gid_t), TYPE_MAXIMUM (gid_t),
|
||||
false, false);
|
||||
}
|
||||
|
||||
@@ -912,26 +922,26 @@ static major_t
|
||||
major_from_header (const char *p, size_t s)
|
||||
{
|
||||
return from_header (p, s, "major_t",
|
||||
- (uintmax_t) TYPE_MINIMUM (major_t),
|
||||
(uintmax_t) TYPE_MAXIMUM (major_t), false, false);
|
||||
TYPE_MINIMUM (major_t), TYPE_MAXIMUM (major_t),
|
||||
false, false);
|
||||
}
|
||||
|
||||
static minor_t
|
||||
minor_from_header (const char *p, size_t s)
|
||||
{
|
||||
return from_header (p, s, "minor_t",
|
||||
- (uintmax_t) TYPE_MINIMUM (minor_t),
|
||||
(uintmax_t) TYPE_MAXIMUM (minor_t), false, false);
|
||||
TYPE_MINIMUM (minor_t), TYPE_MAXIMUM (minor_t),
|
||||
false, false);
|
||||
}
|
||||
|
||||
/* Convert P to the file mode, as understood by tar.
|
||||
Store unrecognized mode bits (from 10th up) in HBITS. */
|
||||
Set *HBITS if there are any unrecognized bits. */
|
||||
static mode_t
|
||||
mode_from_header (const char *p, size_t s, unsigned *hbits)
|
||||
mode_from_header (const char *p, size_t s, bool *hbits)
|
||||
{
|
||||
unsigned u = from_header (p, s, "mode_t",
|
||||
- (uintmax_t) TYPE_MINIMUM (mode_t),
|
||||
TYPE_MAXIMUM (uintmax_t), false, false);
|
||||
intmax_t u = from_header (p, s, "mode_t",
|
||||
INTMAX_MIN, UINTMAX_MAX,
|
||||
false, false);
|
||||
mode_t mode = ((u & TSUID ? S_ISUID : 0)
|
||||
| (u & TSGID ? S_ISGID : 0)
|
||||
| (u & TSVTX ? S_ISVTX : 0)
|
||||
@@ -944,7 +954,7 @@ mode_from_header (const char *p, size_t s, unsigned *hbits)
|
||||
| (u & TOREAD ? S_IROTH : 0)
|
||||
| (u & TOWRITE ? S_IWOTH : 0)
|
||||
| (u & TOEXEC ? S_IXOTH : 0));
|
||||
*hbits = mode ^ u;
|
||||
*hbits = (u & ~07777) != 0;
|
||||
return mode;
|
||||
}
|
||||
|
||||
@@ -953,31 +963,31 @@ off_from_header (const char *p, size_t s)
|
||||
{
|
||||
/* Negative offsets are not allowed in tar files, so invoke
|
||||
from_header with minimum value 0, not TYPE_MINIMUM (off_t). */
|
||||
return from_header (p, s, "off_t", (uintmax_t) 0,
|
||||
(uintmax_t) TYPE_MAXIMUM (off_t), false, false);
|
||||
return from_header (p, s, "off_t",
|
||||
0, TYPE_MAXIMUM (off_t),
|
||||
false, false);
|
||||
}
|
||||
|
||||
static time_t
|
||||
time_from_header (const char *p, size_t s)
|
||||
{
|
||||
return from_header (p, s, "time_t",
|
||||
- (uintmax_t) TYPE_MINIMUM (time_t),
|
||||
(uintmax_t) TYPE_MAXIMUM (time_t), false, false);
|
||||
TYPE_MINIMUM (time_t), TYPE_MAXIMUM (time_t),
|
||||
false, false);
|
||||
}
|
||||
|
||||
static uid_t
|
||||
uid_from_header (const char *p, size_t s)
|
||||
{
|
||||
return from_header (p, s, "uid_t",
|
||||
- (uintmax_t) TYPE_MINIMUM (uid_t),
|
||||
(uintmax_t) TYPE_MAXIMUM (uid_t), false, false);
|
||||
TYPE_MINIMUM (uid_t), TYPE_MAXIMUM (uid_t),
|
||||
false, false);
|
||||
}
|
||||
|
||||
uintmax_t
|
||||
uintmax_from_header (const char *p, size_t s)
|
||||
{
|
||||
return from_header (p, s, "uintmax_t", (uintmax_t) 0,
|
||||
TYPE_MAXIMUM (uintmax_t), false, false);
|
||||
return from_header (p, s, "uintmax_t", 0, UINTMAX_MAX, false, false);
|
||||
}
|
||||
|
||||
|
||||
@@ -1073,7 +1083,8 @@ simple_print_header (struct tar_stat_info *st, union block *blk,
|
||||
char *temp_name;
|
||||
|
||||
/* These hold formatted ints. */
|
||||
char uform[UINTMAX_STRSIZE_BOUND], gform[UINTMAX_STRSIZE_BOUND];
|
||||
char uform[max (INT_BUFSIZE_BOUND (intmax_t), UINTMAX_STRSIZE_BOUND)];
|
||||
char gform[sizeof uform];
|
||||
char *user, *group;
|
||||
char size[2 * UINTMAX_STRSIZE_BOUND];
|
||||
/* holds formatted size or major,minor */
|
||||
@@ -1183,17 +1194,11 @@ simple_print_header (struct tar_stat_info *st, union block *blk,
|
||||
ids that are too large to fit in a uid_t. */
|
||||
uintmax_t u = from_header (blk->header.uid,
|
||||
sizeof blk->header.uid, 0,
|
||||
(uintmax_t) 0,
|
||||
(uintmax_t) TYPE_MAXIMUM (uintmax_t),
|
||||
0, UINTMAX_MAX,
|
||||
false, false);
|
||||
if (u != -1)
|
||||
user = STRINGIFY_BIGINT (u, uform);
|
||||
else
|
||||
{
|
||||
sprintf (uform, "%ld",
|
||||
(long) UID_FROM_HEADER (blk->header.uid));
|
||||
user = uform;
|
||||
}
|
||||
user = (u != -1
|
||||
? STRINGIFY_BIGINT (u, uform)
|
||||
: imaxtostr (UID_FROM_HEADER (blk->header.uid), uform));
|
||||
}
|
||||
|
||||
if (st->gname
|
||||
@@ -1208,17 +1213,11 @@ simple_print_header (struct tar_stat_info *st, union block *blk,
|
||||
ids that are too large to fit in a gid_t. */
|
||||
uintmax_t g = from_header (blk->header.gid,
|
||||
sizeof blk->header.gid, 0,
|
||||
(uintmax_t) 0,
|
||||
(uintmax_t) TYPE_MAXIMUM (uintmax_t),
|
||||
0, UINTMAX_MAX,
|
||||
false, false);
|
||||
if (g != -1)
|
||||
group = STRINGIFY_BIGINT (g, gform);
|
||||
else
|
||||
{
|
||||
sprintf (gform, "%ld",
|
||||
(long) GID_FROM_HEADER (blk->header.gid));
|
||||
group = gform;
|
||||
}
|
||||
group = (g != -1
|
||||
? STRINGIFY_BIGINT (g, gform)
|
||||
: imaxtostr (GID_FROM_HEADER (blk->header.gid), gform));
|
||||
}
|
||||
|
||||
/* Format the file size or major/minor device numbers. */
|
||||
|
||||
151
src/misc.c
151
src/misc.c
@@ -1,7 +1,7 @@
|
||||
/* Miscellaneous functions, not really specific to GNU tar.
|
||||
|
||||
Copyright (C) 1988, 1992, 1994, 1995, 1996, 1997, 1999, 2000, 2001,
|
||||
2003, 2004, 2005, 2006, 2007, 2009, 2010 Free Software Foundation, Inc.
|
||||
2003, 2004, 2005, 2006, 2007, 2009, 2010, 2012 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
@@ -17,6 +17,7 @@
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
|
||||
|
||||
#define COMMON_INLINE _GL_EXTERN_INLINE
|
||||
#include <system.h>
|
||||
#include <rmt.h>
|
||||
#include "common.h"
|
||||
@@ -325,6 +326,76 @@ replace_prefix (char **pname, const char *samp, size_t slen,
|
||||
|
||||
/* Handling numbers. */
|
||||
|
||||
/* Convert VALUE, which is converted from a system integer type whose
|
||||
minimum value is MINVAL and maximum MINVAL, to an decimal
|
||||
integer string. Use the storage in BUF and return a pointer to the
|
||||
converted string. If VALUE is converted from a negative integer in
|
||||
the range MINVAL .. -1, represent it with a string representation
|
||||
of the negative integer, using leading '-'. */
|
||||
#if ! (INTMAX_MAX <= UINTMAX_MAX / 2)
|
||||
# error "strtosysint accepts uintmax_t to represent intmax_t"
|
||||
#endif
|
||||
char *
|
||||
sysinttostr (uintmax_t value, intmax_t minval, uintmax_t maxval,
|
||||
char buf[SYSINT_BUFSIZE])
|
||||
{
|
||||
if (value <= maxval)
|
||||
return umaxtostr (value, buf);
|
||||
else
|
||||
{
|
||||
intmax_t i = value - minval;
|
||||
return imaxtostr (i + minval, 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,
|
||||
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.
|
||||
|
||||
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. */
|
||||
#if ! (INTMAX_MAX <= UINTMAX_MAX)
|
||||
# error "strtosysint accepts uintmax_t to represent nonnegative intmax_t"
|
||||
#endif
|
||||
intmax_t
|
||||
strtosysint (char const *arg, char **arglim, intmax_t minval, uintmax_t maxval)
|
||||
{
|
||||
errno = 0;
|
||||
if (maxval <= INTMAX_MAX)
|
||||
{
|
||||
if (ISDIGIT (arg[*arg == '-']))
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ISDIGIT (*arg))
|
||||
{
|
||||
uintmax_t i = strtoumax (arg, arglim, 10);
|
||||
if (i <= maxval)
|
||||
return represent_uintmax (i);
|
||||
errno = ERANGE;
|
||||
return maxval;
|
||||
}
|
||||
}
|
||||
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Output fraction and trailing digits appropriate for a nanoseconds
|
||||
count equal to NS, but don't output unnecessary '.' or trailing
|
||||
zeros. */
|
||||
@@ -381,6 +452,84 @@ code_timespec (struct timespec t, char sbuf[TIMESPEC_STRSIZE_BOUND])
|
||||
code_ns_fraction (ns, sbuf + UINTMAX_STRSIZE_BOUND);
|
||||
return np;
|
||||
}
|
||||
|
||||
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 (! ISDIGIT (arg[negative]))
|
||||
errno = EINVAL;
|
||||
else
|
||||
{
|
||||
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 == '.')
|
||||
{
|
||||
int digits = 0;
|
||||
bool trailing_nonzero = false;
|
||||
|
||||
while (ISDIGIT (*++p))
|
||||
if (digits < LOG10_BILLION)
|
||||
digits++, ns = 10 * ns + (*p - '0');
|
||||
else
|
||||
trailing_nonzero |= *p != '0';
|
||||
|
||||
while (digits < LOG10_BILLION)
|
||||
digits++, ns *= 10;
|
||||
|
||||
if (negative)
|
||||
{
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (errno == ERANGE)
|
||||
ns = -1;
|
||||
}
|
||||
|
||||
*arg_lim = (char *) p;
|
||||
r.tv_sec = s;
|
||||
r.tv_nsec = ns;
|
||||
return r;
|
||||
}
|
||||
|
||||
/* File handling. */
|
||||
|
||||
|
||||
17
src/sparse.c
17
src/sparse.c
@@ -629,8 +629,8 @@ oldgnu_add_sparse (struct tar_sparse_file *file, struct sparse *s)
|
||||
return add_finish;
|
||||
sp.offset = OFF_FROM_HEADER (s->offset);
|
||||
sp.numbytes = OFF_FROM_HEADER (s->numbytes);
|
||||
if (sp.offset < 0
|
||||
|| sp.offset + sp.numbytes < 0
|
||||
if (sp.offset < 0 || sp.numbytes < 0
|
||||
|| INT_ADD_OVERFLOW (sp.offset, sp.numbytes)
|
||||
|| file->stat_info->stat.st_size < sp.offset + sp.numbytes
|
||||
|| file->stat_info->archive_file_size < 0)
|
||||
return add_fail;
|
||||
@@ -644,10 +644,10 @@ oldgnu_fixup_header (struct tar_sparse_file *file)
|
||||
{
|
||||
/* NOTE! st_size was initialized from the header
|
||||
which actually contains archived size. The following fixes it */
|
||||
off_t realsize = OFF_FROM_HEADER (current_header->oldgnu_header.realsize);
|
||||
file->stat_info->archive_file_size = file->stat_info->stat.st_size;
|
||||
file->stat_info->stat.st_size =
|
||||
OFF_FROM_HEADER (current_header->oldgnu_header.realsize);
|
||||
return true;
|
||||
file->stat_info->stat.st_size = max (0, realsize);
|
||||
return 0 <= realsize;
|
||||
}
|
||||
|
||||
/* Convert old GNU format sparse data to internal representation */
|
||||
@@ -768,10 +768,10 @@ star_fixup_header (struct tar_sparse_file *file)
|
||||
{
|
||||
/* NOTE! st_size was initialized from the header
|
||||
which actually contains archived size. The following fixes it */
|
||||
off_t realsize = OFF_FROM_HEADER (current_header->star_in_header.realsize);
|
||||
file->stat_info->archive_file_size = file->stat_info->stat.st_size;
|
||||
file->stat_info->stat.st_size =
|
||||
OFF_FROM_HEADER (current_header->star_in_header.realsize);
|
||||
return true;
|
||||
file->stat_info->stat.st_size = max (0, realsize);
|
||||
return 0 <= realsize;
|
||||
}
|
||||
|
||||
/* Convert STAR format sparse data to internal representation */
|
||||
@@ -1080,6 +1080,7 @@ decode_num (uintmax_t *num, char const *arg, uintmax_t maxval)
|
||||
if (!ISDIGIT (*arg))
|
||||
return false;
|
||||
|
||||
errno = 0;
|
||||
u = strtoumax (arg, &arg_lim, 10);
|
||||
|
||||
if (! (u <= maxval && errno != ERANGE) || *arg_lim)
|
||||
|
||||
@@ -1383,8 +1383,8 @@ expand_pax_option (struct tar_args *targs, const char *arg)
|
||||
tmp[len-2] = 0;
|
||||
if (get_date_or_file (targs, "--pax-option", tmp, &ts) == 0)
|
||||
{
|
||||
char buf[UINTMAX_STRSIZE_BOUND], *s;
|
||||
s = umaxtostr (ts.tv_sec, buf);
|
||||
char buf[TIMESPEC_STRSIZE_BOUND];
|
||||
char const *s = code_timespec (ts, buf);
|
||||
obstack_grow (&stk, s, strlen (s));
|
||||
}
|
||||
else
|
||||
|
||||
228
src/xheader.c
228
src/xheader.c
@@ -167,14 +167,13 @@ xheader_set_single_keyword (char *kw)
|
||||
static void
|
||||
assign_time_option (char **sval, time_t *tval, const char *input)
|
||||
{
|
||||
uintmax_t u;
|
||||
char *p;
|
||||
time_t t = u = strtoumax (input, &p, 10);
|
||||
if (t != u || *p || errno == ERANGE)
|
||||
struct timespec t = decode_timespec (input, &p, false);
|
||||
if (! valid_timespec (t) || *p)
|
||||
ERROR ((0, 0, _("Time stamp is out of allowed range")));
|
||||
else
|
||||
{
|
||||
*tval = t;
|
||||
*tval = t.tv_sec;
|
||||
assign_string (sval, input);
|
||||
}
|
||||
}
|
||||
@@ -452,7 +451,8 @@ xheader_write_global (struct xheader *xhdr)
|
||||
char *name;
|
||||
|
||||
xheader_finish (xhdr);
|
||||
xheader_write (XGLTYPE, name = xheader_ghdr_name (), time (NULL), xhdr);
|
||||
name = xheader_ghdr_name ();
|
||||
xheader_write (XGLTYPE, name, start_time.tv_sec, xhdr);
|
||||
free (name);
|
||||
}
|
||||
}
|
||||
@@ -652,7 +652,6 @@ decode_record (struct xheader *xhdr,
|
||||
{
|
||||
char *start = *ptr;
|
||||
char *p = start;
|
||||
uintmax_t u;
|
||||
size_t len;
|
||||
char *len_lim;
|
||||
char const *keyword;
|
||||
@@ -669,13 +668,7 @@ decode_record (struct xheader *xhdr,
|
||||
return false;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
len = u = strtoumax (p, &len_lim, 10);
|
||||
if (len != u || errno == ERANGE)
|
||||
{
|
||||
ERROR ((0, 0, _("Extended header length is out of allowed range")));
|
||||
return false;
|
||||
}
|
||||
len = strtoumax (p, &len_lim, 10);
|
||||
|
||||
if (len_max < len)
|
||||
{
|
||||
@@ -817,10 +810,16 @@ xheader_store (char const *keyword, struct tar_stat_info *st,
|
||||
}
|
||||
|
||||
void
|
||||
xheader_read (struct xheader *xhdr, union block *p, size_t size)
|
||||
xheader_read (struct xheader *xhdr, union block *p, off_t size)
|
||||
{
|
||||
size_t j = 0;
|
||||
|
||||
if (size < 0)
|
||||
size = 0; /* Already diagnosed. */
|
||||
|
||||
if (SIZE_MAX - BLOCKSIZE <= size)
|
||||
xalloc_die ();
|
||||
|
||||
size += BLOCKSIZE;
|
||||
xhdr->size = size;
|
||||
xhdr->buffer = xmalloc (size + 1);
|
||||
@@ -1031,14 +1030,12 @@ xheader_string_end (struct xheader *xhdr, char const *keyword)
|
||||
|
||||
static void
|
||||
out_of_range_header (char const *keyword, char const *value,
|
||||
uintmax_t minus_minval, uintmax_t maxval)
|
||||
intmax_t minval, uintmax_t maxval)
|
||||
{
|
||||
char minval_buf[UINTMAX_STRSIZE_BOUND + 1];
|
||||
char minval_buf[INT_BUFSIZE_BOUND (intmax_t)];
|
||||
char maxval_buf[UINTMAX_STRSIZE_BOUND];
|
||||
char *minval_string = umaxtostr (minus_minval, minval_buf + 1);
|
||||
char *minval_string = imaxtostr (minval, minval_buf);
|
||||
char *maxval_string = umaxtostr (maxval, maxval_buf);
|
||||
if (minus_minval)
|
||||
*--minval_string = '-';
|
||||
|
||||
/* TRANSLATORS: The first %s is the pax extended header keyword
|
||||
(atime, gid, etc.). */
|
||||
@@ -1081,138 +1078,74 @@ code_time (struct timespec t, char const *keyword, struct xheader *xhdr)
|
||||
xheader_print (xhdr, keyword, code_timespec (t, buf));
|
||||
}
|
||||
|
||||
enum decode_time_status
|
||||
{
|
||||
decode_time_success,
|
||||
decode_time_range,
|
||||
decode_time_bad_header
|
||||
};
|
||||
|
||||
static enum decode_time_status
|
||||
_decode_time (struct timespec *ts, char const *arg, char const *keyword)
|
||||
{
|
||||
time_t s;
|
||||
unsigned long int ns = 0;
|
||||
char *p;
|
||||
char *arg_lim;
|
||||
bool negative = *arg == '-';
|
||||
|
||||
errno = 0;
|
||||
|
||||
if (ISDIGIT (arg[negative]))
|
||||
{
|
||||
if (negative)
|
||||
{
|
||||
intmax_t i = strtoimax (arg, &arg_lim, 10);
|
||||
if (TYPE_SIGNED (time_t) ? i < TYPE_MINIMUM (time_t) : i < 0)
|
||||
return decode_time_range;
|
||||
s = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
uintmax_t i = strtoumax (arg, &arg_lim, 10);
|
||||
if (TYPE_MAXIMUM (time_t) < i)
|
||||
return decode_time_range;
|
||||
s = i;
|
||||
}
|
||||
|
||||
p = arg_lim;
|
||||
|
||||
if (errno == ERANGE)
|
||||
return decode_time_range;
|
||||
|
||||
if (*p == '.')
|
||||
{
|
||||
int digits = 0;
|
||||
bool trailing_nonzero = false;
|
||||
|
||||
while (ISDIGIT (*++p))
|
||||
if (digits < LOG10_BILLION)
|
||||
{
|
||||
ns = 10 * ns + (*p - '0');
|
||||
digits++;
|
||||
}
|
||||
else
|
||||
trailing_nonzero |= *p != '0';
|
||||
|
||||
while (digits++ < LOG10_BILLION)
|
||||
ns *= 10;
|
||||
|
||||
if (negative)
|
||||
{
|
||||
/* 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))
|
||||
return decode_time_range;
|
||||
s--;
|
||||
ns = BILLION - ns;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! *p)
|
||||
{
|
||||
ts->tv_sec = s;
|
||||
ts->tv_nsec = ns;
|
||||
return decode_time_success;
|
||||
}
|
||||
}
|
||||
|
||||
return decode_time_bad_header;
|
||||
}
|
||||
|
||||
static bool
|
||||
decode_time (struct timespec *ts, char const *arg, char const *keyword)
|
||||
{
|
||||
switch (_decode_time (ts, arg, keyword))
|
||||
char *arg_lim;
|
||||
struct timespec t = decode_timespec (arg, &arg_lim, true);
|
||||
|
||||
if (! valid_timespec (t))
|
||||
{
|
||||
case decode_time_success:
|
||||
return true;
|
||||
case decode_time_bad_header:
|
||||
ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"),
|
||||
keyword, arg));
|
||||
return false;
|
||||
case decode_time_range:
|
||||
out_of_range_header (keyword, arg, - (uintmax_t) TYPE_MINIMUM (time_t),
|
||||
TYPE_MAXIMUM (time_t));
|
||||
if (arg < arg_lim && !*arg_lim)
|
||||
out_of_range_header (keyword, arg, TYPE_MINIMUM (time_t),
|
||||
TYPE_MAXIMUM (time_t));
|
||||
else
|
||||
ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"),
|
||||
keyword, arg));
|
||||
return false;
|
||||
}
|
||||
|
||||
*ts = t;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
code_signed_num (uintmax_t value, char const *keyword,
|
||||
intmax_t minval, uintmax_t maxval, struct xheader *xhdr)
|
||||
{
|
||||
char sbuf[SYSINT_BUFSIZE];
|
||||
xheader_print (xhdr, keyword, sysinttostr (value, minval, maxval, sbuf));
|
||||
}
|
||||
|
||||
static void
|
||||
code_num (uintmax_t value, char const *keyword, struct xheader *xhdr)
|
||||
{
|
||||
char sbuf[UINTMAX_STRSIZE_BOUND];
|
||||
xheader_print (xhdr, keyword, umaxtostr (value, sbuf));
|
||||
code_signed_num (value, keyword, 0, UINTMAX_MAX, xhdr);
|
||||
}
|
||||
|
||||
static bool
|
||||
decode_signed_num (intmax_t *num, char const *arg,
|
||||
intmax_t minval, uintmax_t maxval,
|
||||
char const *keyword)
|
||||
{
|
||||
char *arg_lim;
|
||||
intmax_t u = strtosysint (arg, &arg_lim, minval, maxval);
|
||||
|
||||
if (errno == EINVAL || *arg_lim)
|
||||
{
|
||||
ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"),
|
||||
keyword, arg));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (errno == ERANGE)
|
||||
{
|
||||
out_of_range_header (keyword, arg, minval, maxval);
|
||||
return false;
|
||||
}
|
||||
|
||||
*num = u;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
decode_num (uintmax_t *num, char const *arg, uintmax_t maxval,
|
||||
char const *keyword)
|
||||
{
|
||||
uintmax_t u;
|
||||
char *arg_lim;
|
||||
|
||||
if (! (ISDIGIT (*arg)
|
||||
&& (errno = 0, u = strtoumax (arg, &arg_lim, 10), !*arg_lim)))
|
||||
{
|
||||
ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"),
|
||||
keyword, arg));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! (u <= maxval && errno != ERANGE))
|
||||
{
|
||||
out_of_range_header (keyword, arg, 0, maxval);
|
||||
return false;
|
||||
}
|
||||
|
||||
*num = u;
|
||||
intmax_t i;
|
||||
if (! decode_signed_num (&i, arg, 0, maxval, keyword))
|
||||
return false;
|
||||
*num = i;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1254,7 +1187,8 @@ static void
|
||||
gid_coder (struct tar_stat_info const *st, char const *keyword,
|
||||
struct xheader *xhdr, void const *data __attribute__ ((unused)))
|
||||
{
|
||||
code_num (st->stat.st_gid, keyword, xhdr);
|
||||
code_signed_num (st->stat.st_gid, keyword,
|
||||
TYPE_MINIMUM (gid_t), TYPE_MAXIMUM (gid_t), xhdr);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1263,8 +1197,9 @@ gid_decoder (struct tar_stat_info *st,
|
||||
char const *arg,
|
||||
size_t size __attribute__((unused)))
|
||||
{
|
||||
uintmax_t u;
|
||||
if (decode_num (&u, arg, TYPE_MAXIMUM (gid_t), keyword))
|
||||
intmax_t u;
|
||||
if (decode_signed_num (&u, arg, TYPE_MINIMUM (gid_t),
|
||||
TYPE_MAXIMUM (gid_t), keyword))
|
||||
st->stat.st_gid = u;
|
||||
}
|
||||
|
||||
@@ -1377,7 +1312,8 @@ static void
|
||||
uid_coder (struct tar_stat_info const *st, char const *keyword,
|
||||
struct xheader *xhdr, void const *data __attribute__ ((unused)))
|
||||
{
|
||||
code_num (st->stat.st_uid, keyword, xhdr);
|
||||
code_signed_num (st->stat.st_uid, keyword,
|
||||
TYPE_MINIMUM (uid_t), TYPE_MAXIMUM (uid_t), xhdr);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1386,8 +1322,9 @@ uid_decoder (struct tar_stat_info *st,
|
||||
char const *arg,
|
||||
size_t size __attribute__((unused)))
|
||||
{
|
||||
uintmax_t u;
|
||||
if (decode_num (&u, arg, TYPE_MAXIMUM (uid_t), keyword))
|
||||
intmax_t u;
|
||||
if (decode_signed_num (&u, arg, TYPE_MINIMUM (uid_t),
|
||||
TYPE_MAXIMUM (uid_t), keyword))
|
||||
st->stat.st_uid = u;
|
||||
}
|
||||
|
||||
@@ -1509,7 +1446,7 @@ sparse_map_decoder (struct tar_stat_info *st,
|
||||
st->sparse_map_avail = 0;
|
||||
while (1)
|
||||
{
|
||||
uintmax_t u;
|
||||
intmax_t u;
|
||||
char *delim;
|
||||
struct sp_array e;
|
||||
|
||||
@@ -1521,11 +1458,16 @@ sparse_map_decoder (struct tar_stat_info *st,
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
u = strtoumax (arg, &delim, 10);
|
||||
u = strtoimax (arg, &delim, 10);
|
||||
if (TYPE_MAXIMUM (off_t) < u)
|
||||
{
|
||||
u = TYPE_MAXIMUM (off_t);
|
||||
errno = ERANGE;
|
||||
}
|
||||
if (offset)
|
||||
{
|
||||
e.offset = u;
|
||||
if (!(u == e.offset && errno != ERANGE))
|
||||
if (errno == ERANGE)
|
||||
{
|
||||
out_of_range_header (keyword, arg, 0, TYPE_MAXIMUM (off_t));
|
||||
return;
|
||||
@@ -1534,7 +1476,7 @@ sparse_map_decoder (struct tar_stat_info *st,
|
||||
else
|
||||
{
|
||||
e.numbytes = u;
|
||||
if (!(u == e.numbytes && errno != ERANGE))
|
||||
if (errno == ERANGE)
|
||||
{
|
||||
out_of_range_header (keyword, arg, 0, TYPE_MAXIMUM (off_t));
|
||||
return;
|
||||
|
||||
@@ -157,6 +157,7 @@ TESTSUITE_AT = \
|
||||
spmvp00.at\
|
||||
spmvp01.at\
|
||||
spmvp10.at\
|
||||
time01.at\
|
||||
truncate.at\
|
||||
update.at\
|
||||
update01.at\
|
||||
|
||||
@@ -284,6 +284,8 @@ m4_include([lustar01.at])
|
||||
m4_include([lustar02.at])
|
||||
m4_include([lustar03.at])
|
||||
|
||||
m4_include([time01.at])
|
||||
|
||||
m4_include([multiv01.at])
|
||||
m4_include([multiv02.at])
|
||||
m4_include([multiv03.at])
|
||||
@@ -357,4 +359,3 @@ m4_include([star/ustar-big-2g.at])
|
||||
m4_include([star/ustar-big-8g.at])
|
||||
|
||||
m4_include([star/pax-big-10g.at])
|
||||
|
||||
|
||||
70
tests/time01.at
Normal file
70
tests/time01.at
Normal file
@@ -0,0 +1,70 @@
|
||||
# Test time stamps for GNU tar. -*- Autotest -*-
|
||||
#
|
||||
# Copyright 2012 Free Software Foundation, Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# This program 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# written by Paul Eggert
|
||||
|
||||
AT_SETUP([time: tricky time stamps])
|
||||
AT_KEYWORDS([time time01])
|
||||
|
||||
AT_TAR_CHECK([
|
||||
export TZ=UTC0
|
||||
mkdir dir
|
||||
|
||||
# Test files with time stamps that are near common sources of error,
|
||||
# typically near powers of 2 (for seconds) or near 0, 1970, or 9999 (years).
|
||||
# Use GNU-style @ notation for very large time stamps, since they
|
||||
# typically don't render into years correctly due to int overflow.
|
||||
for s in \
|
||||
@-9223372036854775809 @-9223372036854775808 @-9223372036854775807 \
|
||||
0000-01-01T00:00:00 0000-01-01T00:00:01 \
|
||||
0000-01-02T00:00:00 \
|
||||
1697-10-17T11:03:27 1697-10-17T11:03:28 1697-10-17T11:03:29 \
|
||||
1833-11-24T17:31:43 1833-11-24T17:31:44 1833-11-24T17:31:45 \
|
||||
1901-12-13T20:45:51 1901-12-13T20:45:52 1901-12-13T20:45:53 \
|
||||
1901-12-14T20:45:51 \
|
||||
1969-12-31T23:59:58 1969-12-31T23:59:59 \
|
||||
1970-01-01T00:00:00 1970-01-01T00:00:01 \
|
||||
2038-01-18T03:14:07 \
|
||||
2038-01-19T03:14:07 2038-01-19T03:14:08 \
|
||||
2106-02-07T06:28:15 2106-02-07T06:28:16 \
|
||||
2242-03-16T12:56:31 2242-03-16T12:56:32 \
|
||||
9999-12-31T23:59:58 9999-12-31T23:59:59 \
|
||||
@9223372036854775807 @9223372036854775808
|
||||
do
|
||||
# Skip a time stamp $s if it's out of range for this platform,
|
||||
# of if it uses a notation that this platform does not recognize.
|
||||
touch -d $s dir/f$s >/dev/null 2>&1 || continue
|
||||
|
||||
# Likewise for $s.1. If $s is the most negative time stamp and
|
||||
# time stamps are signed, then $s.1 is out of range.
|
||||
touch -d $s.1 dir/f$s.$ns >/dev/null 2>&1 || continue
|
||||
|
||||
for frac in 01 001 00001 000001 0000001 00000001 000000001 0000000001 \
|
||||
9 99 999 99999 999999 9999999 99999999 999999999 9999999999
|
||||
do
|
||||
touch -d $s.$frac dir/f$s.$frac
|
||||
done
|
||||
done
|
||||
|
||||
tar -c -f archive.tar dir
|
||||
tar -d -f archive.tar dir
|
||||
],
|
||||
[0],
|
||||
[], [], [], [],
|
||||
[pax])
|
||||
|
||||
AT_CLEANUP
|
||||
Reference in New Issue
Block a user