dirname
error
exclude
+extern-inline
exitfail
fchmodat
fchownat
#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
\f
/* Information gleaned from the command line. */
/* 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);
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 *);
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);
/* Module exit.c */
extern void (*fatal_exit_hook) (void);
+
+_GL_INLINE_HEADER_END
/* 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.
}
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 ();
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;
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)
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);
{
type = XGLTYPE;
p = xheader_ghdr_name ();
- time (&t);
+ t = start_time.tv_sec;
}
else
{
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))
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,
}
perhaps_renamed = false;
}
-
+
else if (flag & PD_FORCE_CHILDREN)
{
directory->children = PD_CHILDREN(flag);
{
int n;
uintmax_t u;
- time_t sec;
- long int nsec;
char *buf = NULL;
size_t bufsize = 0;
char *ebuf;
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;
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)))
{
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);
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);
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);
#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 */
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] =
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;
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;
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
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)
/* 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. */
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;
}
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++;
/* 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;
}
}
return -1;
}
}
- negative = signbit;
+ negative = signbit != 0;
if (negative)
value = -value;
}
}
if (value <= (negative ? minus_minval : maxval))
- return negative ? -value : value;
+ return represent_uintmax (negative ? -value : value);
if (type && !silent)
{
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);
}
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)
| (u & TOREAD ? S_IROTH : 0)
| (u & TOWRITE ? S_IWOTH : 0)
| (u & TOEXEC ? S_IXOTH : 0));
- *hbits = mode ^ u;
+ *hbits = (u & ~07777) != 0;
return mode;
}
{
/* 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);
}
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 */
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
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. */
/* 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
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"
\f
/* 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. */
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;
+}
\f
/* File handling. */
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;
{
/* 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 */
{
/* 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 */
if (!ISDIGIT (*arg))
return false;
+ errno = 0;
u = strtoumax (arg, &arg_lim, 10);
if (! (u <= maxval && errno != ERANGE) || *arg_lim)
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
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);
}
}
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);
}
}
{
char *start = *ptr;
char *p = start;
- uintmax_t u;
size_t len;
char *len_lim;
char const *keyword;
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)
{
}
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);
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.). */
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)
+static bool
+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 == '-';
+ struct timespec t = decode_timespec (arg, &arg_lim, true);
- errno = 0;
-
- if (ISDIGIT (arg[negative]))
+ if (! valid_timespec (t))
{
- 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;
- }
+ if (arg < arg_lim && !*arg_lim)
+ out_of_range_header (keyword, arg, TYPE_MINIMUM (time_t),
+ TYPE_MAXIMUM (time_t));
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;
- }
+ ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"),
+ keyword, arg));
+ return false;
}
- return decode_time_bad_header;
+ *ts = t;
+ return true;
}
-static bool
-decode_time (struct timespec *ts, char const *arg, char const *keyword)
+static void
+code_signed_num (uintmax_t value, char const *keyword,
+ intmax_t minval, uintmax_t maxval, struct xheader *xhdr)
{
- switch (_decode_time (ts, arg, keyword))
- {
- 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));
- return false;
- }
- return true;
+ 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_num (uintmax_t *num, char const *arg, uintmax_t maxval,
- char const *keyword)
+decode_signed_num (intmax_t *num, char const *arg,
+ intmax_t minval, uintmax_t maxval,
+ char const *keyword)
{
- uintmax_t u;
char *arg_lim;
+ intmax_t u = strtosysint (arg, &arg_lim, minval, maxval);
- if (! (ISDIGIT (*arg)
- && (errno = 0, u = strtoumax (arg, &arg_lim, 10), !*arg_lim)))
+ if (errno == EINVAL || *arg_lim)
{
ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"),
keyword, arg));
return false;
}
- if (! (u <= maxval && errno != ERANGE))
+ if (errno == ERANGE)
{
- out_of_range_header (keyword, arg, 0, maxval);
+ out_of_range_header (keyword, arg, minval, maxval);
return false;
}
return true;
}
+static bool
+decode_num (uintmax_t *num, char const *arg, uintmax_t maxval,
+ char const *keyword)
+{
+ intmax_t i;
+ if (! decode_signed_num (&i, arg, 0, maxval, keyword))
+ return false;
+ *num = i;
+ return true;
+}
+
static void
dummy_coder (struct tar_stat_info const *st __attribute__ ((unused)),
char const *keyword __attribute__ ((unused)),
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
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;
}
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
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;
}
st->sparse_map_avail = 0;
while (1)
{
- uintmax_t u;
+ intmax_t u;
char *delim;
struct sp_array e;
}
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;
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;
spmvp00.at\
spmvp01.at\
spmvp10.at\
+ time01.at\
truncate.at\
update.at\
update01.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])
m4_include([star/ustar-big-8g.at])
m4_include([star/pax-big-10g.at])
-
--- /dev/null
+# 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