/* List a tar archive, with support routines for reading a tar archive.
Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1998, 1999, 2000,
- 2001, 2003, 2004 Free Software Foundation, Inc.
+ 2001, 2003, 2004, 2005 Free Software Foundation, Inc.
Written by John Gilmore, on 1985-08-26.
/* Define to non-zero for forcing old ctime format instead of ISO format. */
#undef USE_OLD_CTIME
-#include "system.h"
+#include <system.h>
#include <quotearg.h>
#include "common.h"
size_t recent_long_link_blocks; /* likewise, for long link */
static uintmax_t from_header (const char *, size_t, const char *,
- uintmax_t, uintmax_t);
+ uintmax_t, uintmax_t, bool, bool);
/* Base 64 digits; see Internet RFC 2045 Table 1. */
static char const base_64_digits[64] =
base64_init ();
name_gather ();
- open_archive (ACCESS_READ);
+ open_archive (ACCESS_READ);
do
{
prev_status = status;
assign_string (&save_name, 0);
}
+/* Check header checksum */
+/* The standard BSD tar sources create the checksum by adding up the
+ bytes in the header as type char. I think the type char was unsigned
+ on the PDP-11, but it's signed on the Next and Sun. It looks like the
+ sources to BSD tar were never changed to compute the checksum
+ correctly, so both the Sun and Next add the bytes of the header as
+ signed chars. This doesn't cause a problem until you get a file with
+ a name containing characters with the high bit set. So tar_checksum
+ computes two checksums -- signed and unsigned. */
+
+enum read_header
+tar_checksum (union block *header, bool silent)
+{
+ size_t i;
+ int unsigned_sum = 0; /* the POSIX one :-) */
+ int signed_sum = 0; /* the Sun one :-( */
+ int recorded_sum;
+ uintmax_t parsed_sum;
+ char *p;
+
+ p = header->buffer;
+ for (i = sizeof *header; i-- != 0;)
+ {
+ unsigned_sum += (unsigned char) *p;
+ signed_sum += (signed char) (*p++);
+ }
+
+ if (unsigned_sum == 0)
+ return HEADER_ZERO_BLOCK;
+
+ /* Adjust checksum to count the "chksum" field as blanks. */
+
+ for (i = sizeof header->header.chksum; i-- != 0;)
+ {
+ unsigned_sum -= (unsigned char) header->header.chksum[i];
+ signed_sum -= (signed char) (header->header.chksum[i]);
+ }
+ unsigned_sum += ' ' * sizeof header->header.chksum;
+ signed_sum += ' ' * sizeof header->header.chksum;
+
+ 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)
+ return HEADER_FAILURE;
+
+ recorded_sum = parsed_sum;
+
+ if (unsigned_sum != recorded_sum && signed_sum != recorded_sum)
+ return HEADER_FAILURE;
+
+ return HEADER_SUCCESS;
+}
+
/* Read a block that's supposed to be a header block. Return its
address in "current_header", and if it is good, the file's size in
current_stat_info.stat.st_size.
You must always set_next_block_after(current_header) to skip past
the header which this routine reads. */
-/* The standard BSD tar sources create the checksum by adding up the
- bytes in the header as type char. I think the type char was unsigned
- on the PDP-11, but it's signed on the Next and Sun. It looks like the
- sources to BSD tar were never changed to compute the checksum
- correctly, so both the Sun and Next add the bytes of the header as
- signed chars. This doesn't cause a problem until you get a file with
- a name containing characters with the high bit set. So read_header
- computes two checksums -- signed and unsigned. */
-
enum read_header
read_header (bool raw_extended_headers)
{
- size_t i;
- int unsigned_sum; /* the POSIX one :-) */
- int signed_sum; /* the Sun one :-( */
- int recorded_sum;
- uintmax_t parsed_sum;
- char *p;
union block *header;
union block *header_copy;
char *bp;
while (1)
{
+ enum read_header status;
+
header = find_next_block ();
current_header = header;
if (!header)
return HEADER_END_OF_FILE;
- unsigned_sum = 0;
- signed_sum = 0;
- p = header->buffer;
- for (i = sizeof *header; i-- != 0;)
- {
- unsigned_sum += (unsigned char) *p;
- signed_sum += (signed char) (*p++);
- }
-
- if (unsigned_sum == 0)
- return HEADER_ZERO_BLOCK;
-
- /* Adjust checksum to count the "chksum" field as blanks. */
-
- for (i = sizeof header->header.chksum; i-- != 0;)
- {
- unsigned_sum -= (unsigned char) header->header.chksum[i];
- signed_sum -= (signed char) (header->header.chksum[i]);
- }
- unsigned_sum += ' ' * sizeof header->header.chksum;
- signed_sum += ' ' * sizeof header->header.chksum;
-
- parsed_sum = from_header (header->header.chksum,
- sizeof header->header.chksum, 0,
- (uintmax_t) 0,
- (uintmax_t) TYPE_MAXIMUM (int));
- if (parsed_sum == (uintmax_t) -1)
- return HEADER_FAILURE;
-
- recorded_sum = parsed_sum;
-
- if (unsigned_sum != recorded_sum && signed_sum != recorded_sum)
- return HEADER_FAILURE;
+ if ((status = tar_checksum (header, false)) != HEADER_SUCCESS)
+ return status;
/* Good block. Decode file size and return. */
|| header->header.typeflag == GNUTYPE_LONGLINK)
{
size_t name_size = current_stat_info.stat.st_size;
- size = name_size - name_size % BLOCKSIZE + 2 * BLOCKSIZE;
+ size_t n = name_size % BLOCKSIZE;
+ size = name_size + BLOCKSIZE;
+ if (n)
+ size += BLOCKSIZE - n;
+
if (name_size != current_stat_info.stat.st_size
|| size < name_size)
xalloc_die ();
stat_info->stat.st_mode = MODE_FROM_HEADER (header->header.mode);
stat_info->stat.st_mtime = TIME_FROM_HEADER (header->header.mtime);
- assign_string (&stat_info->uname, header->header.uname);
- assign_string (&stat_info->gname, header->header.gname);
+ assign_string (&stat_info->uname,
+ header->header.uname[0] ? header->header.uname : NULL);
+ assign_string (&stat_info->gname,
+ header->header.gname[0] ? header->header.gname : NULL);
stat_info->devmajor = MAJOR_FROM_HEADER (header->header.devmajor);
stat_info->devminor = MINOR_FROM_HEADER (header->header.devminor);
sparse_fixup_header (stat_info);
stat_info->is_sparse = true;
}
+ else
+ stat_info->is_sparse = false;
}
/* Convert buffer at WHERE0 of size DIGS from external format to
- uintmax_t. The data is of type TYPE. The buffer must represent a
- value in the range -MINUS_MINVAL through MAXVAL. DIGS must be
- positive. Return -1 on error, diagnosing the error if TYPE is
- nonzero. */
+ 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
+ numbers instead of the other GNU extensions. Return -1 on error,
+ diagnosing the error if TYPE is nonnull and if !SILENT. */
static uintmax_t
from_header (char const *where0, size_t digs, char const *type,
- uintmax_t minus_minval, uintmax_t maxval)
+ uintmax_t minus_minval, uintmax_t maxval,
+ bool octal_only, bool silent)
{
uintmax_t value;
char const *where = where0;
{
if (where == lim)
{
- if (type)
+ if (type && !silent)
ERROR ((0, 0,
+ /* TRANSLATORS: %s is type of the value (gid_t, uid_t, etc.) */
_("Blanks in header where numeric %s value expected"),
type));
return -1;
if (!overflow && value <= minus_minval)
{
- WARN ((0, 0,
- _("Archive octal value %.*s is out of %s range; assuming two's complement"),
- (int) (where - where1), where1, type));
+ if (!silent)
+ WARN ((0, 0,
+ /* 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;
}
}
if (overflow)
{
- if (type)
+ if (type && !silent)
ERROR ((0, 0,
+ /* TRANSLATORS: Second %s is a type name (gid_t,uid_t,etc.) */
_("Archive octal value %.*s is out of %s range"),
(int) (where - where1), where1, type));
return -1;
}
}
+ else if (octal_only)
+ {
+ /* Suppress the following extensions. */
+ }
else if (*where == '-' || *where == '+')
{
/* Parse base-64 output produced only by tar test versions
1.13.6 (1999-08-11) through 1.13.11 (1999-08-23).
Support for this will be withdrawn in future releases. */
int dig;
- static int warned_once;
- if (! warned_once)
+ if (!silent)
{
- warned_once = 1;
- WARN ((0, 0,
- _("Archive contains obsolescent base-64 headers")));
+ static bool warned_once;
+ if (! warned_once)
+ {
+ warned_once = true;
+ WARN ((0, 0, _("Archive contains obsolescent base-64 headers")));
+ }
}
negative = *where++ == '-';
while (where != lim
char *string = alloca (digs + 1);
memcpy (string, where0, digs);
string[digs] = '\0';
- if (type)
+ if (type && !silent)
ERROR ((0, 0,
_("Archive signed base-64 string %s is out of %s range"),
quote (string), type));
break;
if (((value << LG_256 >> LG_256) | topbits) != value)
{
- if (type)
+ if (type && !silent)
ERROR ((0, 0,
_("Archive base-256 value is out of %s range"),
type));
while (where0 != lim && ! lim[-1])
lim--;
quotearg_buffer (buf, sizeof buf, where0, lim - where, o);
- ERROR ((0, 0,
- _("Archive contains %.*s where numeric %s value expected"),
- (int) sizeof buf, buf, type));
+ if (!silent)
+ ERROR ((0, 0,
+ /* TRANSLATORS: Second %s is a type name (gid_t,uid_t,etc.) */
+ _("Archive contains %.*s where numeric %s value expected"),
+ (int) sizeof buf, buf, type));
}
return -1;
if (value <= (negative ? minus_minval : maxval))
return negative ? -value : value;
- if (type)
+ if (type && !silent)
{
char minval_buf[UINTMAX_STRSIZE_BOUND + 1];
char maxval_buf[UINTMAX_STRSIZE_BOUND];
*--value_string = '-';
if (minus_minval)
*--minval_string = '-';
- ERROR ((0, 0, _("Archive value %s is out of %s range %s.%s"),
+ /* TRANSLATORS: Second %s is type name (gid_t,uid_t,etc.) */
+ ERROR ((0, 0, _("Archive value %s is out of %s range %s..%s"),
value_string, type,
minval_string, STRINGIFY_BIGINT (maxval, maxval_buf)));
}
{
return from_header (p, s, "gid_t",
- (uintmax_t) TYPE_MINIMUM (gid_t),
- (uintmax_t) TYPE_MAXIMUM (gid_t));
+ (uintmax_t) TYPE_MAXIMUM (gid_t),
+ false, false);
}
major_t
{
return from_header (p, s, "major_t",
- (uintmax_t) TYPE_MINIMUM (major_t),
- (uintmax_t) TYPE_MAXIMUM (major_t));
+ (uintmax_t) TYPE_MAXIMUM (major_t), false, false);
}
minor_t
{
return from_header (p, s, "minor_t",
- (uintmax_t) TYPE_MINIMUM (minor_t),
- (uintmax_t) TYPE_MAXIMUM (minor_t));
+ (uintmax_t) TYPE_MAXIMUM (minor_t), false, false);
}
mode_t
/* Do not complain about unrecognized mode bits. */
unsigned u = from_header (p, s, "mode_t",
- (uintmax_t) TYPE_MINIMUM (mode_t),
- TYPE_MAXIMUM (uintmax_t));
+ TYPE_MAXIMUM (uintmax_t), false, false);
return ((u & TSUID ? S_ISUID : 0)
| (u & TSGID ? S_ISGID : 0)
| (u & TSVTX ? S_ISVTX : 0)
/* 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));
+ (uintmax_t) TYPE_MAXIMUM (off_t), false, false);
}
size_t
size_from_header (const char *p, size_t s)
{
return from_header (p, s, "size_t", (uintmax_t) 0,
- (uintmax_t) TYPE_MAXIMUM (size_t));
+ (uintmax_t) TYPE_MAXIMUM (size_t), false, false);
}
time_t
{
return from_header (p, s, "time_t",
- (uintmax_t) TYPE_MINIMUM (time_t),
- (uintmax_t) TYPE_MAXIMUM (time_t));
+ (uintmax_t) TYPE_MAXIMUM (time_t), false, false);
}
uid_t
{
return from_header (p, s, "uid_t",
- (uintmax_t) TYPE_MINIMUM (uid_t),
- (uintmax_t) TYPE_MAXIMUM (uid_t));
+ (uintmax_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));
+ TYPE_MAXIMUM (uintmax_t), false, false);
}
case GNUTYPE_LONGNAME:
case GNUTYPE_LONGLINK:
modes[0] = 'L';
- ERROR ((0, 0, _("Visible longname error")));
+ ERROR ((0, 0, _("Unexpected long name header")));
break;
case GNUTYPE_SPARSE:
uintmax_t u = from_header (current_header->header.uid,
sizeof current_header->header.uid, 0,
(uintmax_t) 0,
- (uintmax_t) TYPE_MAXIMUM (uintmax_t));
+ (uintmax_t) TYPE_MAXIMUM (uintmax_t),
+ false, false);
if (u != -1)
user = STRINGIFY_BIGINT (u, uform);
else
uintmax_t g = from_header (current_header->header.gid,
sizeof current_header->header.gid, 0,
(uintmax_t) 0,
- (uintmax_t) TYPE_MAXIMUM (uintmax_t));
+ (uintmax_t) TYPE_MAXIMUM (uintmax_t),
+ false, false);
if (g != -1)
group = STRINGIFY_BIGINT (g, gform);
else
else
seekable_archive = false;
}
-
+
while (size > 0)
{
x = find_next_block ();
{
char save_typeflag = current_header->header.typeflag;
set_next_block_after (current_header);
-
+
assign_string (&save_name, current_stat_info.file_name);
if (current_stat_info.is_sparse)