/* Create a tar archive.
Copyright (C) 1985, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
- 2003, 2004, 2005 Free Software Foundation, Inc.
+ 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
Written by John Gilmore, on 1985-08-25.
while (i);
}
+
+static bool
+to_chars (int negative, uintmax_t value, size_t valsize,
+ uintmax_t (*substitute) (int *),
+ char *where, size_t size, const char *type);
+
+static bool
+to_chars_subst (int negative, int gnu_format, uintmax_t value, size_t valsize,
+ uintmax_t (*substitute) (int *),
+ char *where, size_t size, const char *type)
+{
+ uintmax_t maxval = (gnu_format
+ ? MAX_VAL_WITH_DIGITS (size - 1, LG_256)
+ : MAX_VAL_WITH_DIGITS (size - 1, LG_8));
+ char valbuf[UINTMAX_STRSIZE_BOUND + 1];
+ char maxbuf[UINTMAX_STRSIZE_BOUND];
+ char minbuf[UINTMAX_STRSIZE_BOUND + 1];
+ char const *minval_string;
+ char const *maxval_string = STRINGIFY_BIGINT (maxval, maxbuf);
+ char const *value_string;
+
+ if (gnu_format)
+ {
+ uintmax_t m = maxval + 1 ? maxval + 1 : maxval / 2 + 1;
+ char *p = STRINGIFY_BIGINT (m, minbuf + 1);
+ *--p = '-';
+ minval_string = p;
+ }
+ else
+ minval_string = "0";
+
+ if (negative)
+ {
+ char *p = STRINGIFY_BIGINT (- value, valbuf + 1);
+ *--p = '-';
+ value_string = p;
+ }
+ else
+ value_string = STRINGIFY_BIGINT (value, valbuf);
+
+ if (substitute)
+ {
+ int negsub;
+ uintmax_t sub = substitute (&negsub) & maxval;
+ /* NOTE: This is one of the few places where GNU_FORMAT differs from
+ OLDGNU_FORMAT. The actual differences are:
+
+ 1. In OLDGNU_FORMAT all strings in a tar header end in \0
+ 2. Incremental archives use oldgnu_header.
+
+ Apart from this they are completely identical. */
+ uintmax_t s = (negsub &= archive_format == GNU_FORMAT) ? - sub : sub;
+ char subbuf[UINTMAX_STRSIZE_BOUND + 1];
+ char *sub_string = STRINGIFY_BIGINT (s, subbuf + 1);
+ if (negsub)
+ *--sub_string = '-';
+ WARN ((0, 0, _("value %s out of %s range %s..%s; substituting %s"),
+ value_string, type, minval_string, maxval_string,
+ sub_string));
+ return to_chars (negsub, s, valsize, 0, where, size, type);
+ }
+ else
+ ERROR ((0, 0, _("value %s out of %s range %s..%s"),
+ value_string, type, minval_string, maxval_string));
+ return false;
+}
+
/* Convert NEGATIVE VALUE (which was originally of size VALSIZE) to
external form, using SUBSTITUTE (...) if VALUE won't fit. Output
to buffer WHERE with size SIZE. NEGATIVE is 1 iff VALUE was
SUBSTITUTE the address of an 0-or-1 flag recording whether the
substitute value is negative. */
-static void
+static bool
to_chars (int negative, uintmax_t value, size_t valsize,
uintmax_t (*substitute) (int *),
char *where, size_t size, const char *type)
{
- int base256_allowed = (archive_format == GNU_FORMAT
- || archive_format == OLDGNU_FORMAT);
+ int gnu_format = (archive_format == GNU_FORMAT
+ || archive_format == OLDGNU_FORMAT);
/* Generate the POSIX octal representation if the number fits. */
if (! negative && value <= MAX_VAL_WITH_DIGITS (size - 1, LG_8))
{
where[size - 1] = '\0';
to_octal (value, where, size - 1);
+ return true;
}
-
- /* Otherwise, generate the base-256 representation if we are
- generating an old or new GNU format and if the number fits. */
- else if (((negative ? -1 - value : value)
- <= MAX_VAL_WITH_DIGITS (size - 1, LG_256))
- && base256_allowed)
- {
- where[0] = negative ? -1 : 1 << (LG_256 - 1);
- to_base256 (negative, value, where + 1, size - 1);
- }
-
- /* Otherwise, if the number is negative, and if it would not cause
- ambiguity on this host by confusing positive with negative
- values, then generate the POSIX octal representation of the value
- modulo 2**(field bits). The resulting tar file is
- machine-dependent, since it depends on the host word size. Yuck!
- But this is the traditional behavior. */
- else if (negative && valsize * CHAR_BIT <= (size - 1) * LG_8)
- {
- static int warned_once;
- if (! warned_once)
- {
- warned_once = 1;
- WARN ((0, 0, _("Generating negative octal headers")));
- }
- where[size - 1] = '\0';
- to_octal (value & MAX_VAL_WITH_DIGITS (valsize * CHAR_BIT, 1),
- where, size - 1);
- }
-
- /* Otherwise, output a substitute value if possible (with a
- warning), and an error message if not. */
- else
+ else if (gnu_format)
{
- uintmax_t maxval = (base256_allowed
- ? MAX_VAL_WITH_DIGITS (size - 1, LG_256)
- : MAX_VAL_WITH_DIGITS (size - 1, LG_8));
- char valbuf[UINTMAX_STRSIZE_BOUND + 1];
- char maxbuf[UINTMAX_STRSIZE_BOUND];
- char minbuf[UINTMAX_STRSIZE_BOUND + 1];
- char const *minval_string;
- char const *maxval_string = STRINGIFY_BIGINT (maxval, maxbuf);
- char const *value_string;
-
- if (base256_allowed)
- {
- uintmax_t m = maxval + 1 ? maxval + 1 : maxval / 2 + 1;
- char *p = STRINGIFY_BIGINT (m, minbuf + 1);
- *--p = '-';
- minval_string = p;
- }
- else
- minval_string = "0";
+ /* Try to cope with the number by using traditional GNU format
+ methods */
- if (negative)
+ /* Generate the base-256 representation if the number fits. */
+ if (((negative ? -1 - value : value)
+ <= MAX_VAL_WITH_DIGITS (size - 1, LG_256)))
{
- char *p = STRINGIFY_BIGINT (- value, valbuf + 1);
- *--p = '-';
- value_string = p;
+ where[0] = negative ? -1 : 1 << (LG_256 - 1);
+ to_base256 (negative, value, where + 1, size - 1);
+ return true;
}
- else
- value_string = STRINGIFY_BIGINT (value, valbuf);
- if (substitute)
+ /* Otherwise, if the number is negative, and if it would not cause
+ ambiguity on this host by confusing positive with negative
+ values, then generate the POSIX octal representation of the value
+ modulo 2**(field bits). The resulting tar file is
+ machine-dependent, since it depends on the host word size. Yuck!
+ But this is the traditional behavior. */
+ else if (negative && valsize * CHAR_BIT <= (size - 1) * LG_8)
{
- int negsub;
- uintmax_t sub = substitute (&negsub) & maxval;
- /* FIXME: This is the only place where GNU_FORMAT differs from
- OLDGNU_FORMAT. Apart from this they are completely identical. */
- uintmax_t s = (negsub &= archive_format == GNU_FORMAT) ? - sub : sub;
- char subbuf[UINTMAX_STRSIZE_BOUND + 1];
- char *sub_string = STRINGIFY_BIGINT (s, subbuf + 1);
- if (negsub)
- *--sub_string = '-';
- WARN ((0, 0, _("value %s out of %s range %s..%s; substituting %s"),
- value_string, type, minval_string, maxval_string,
- sub_string));
- to_chars (negsub, s, valsize, 0, where, size, type);
+ static int warned_once;
+ if (! warned_once)
+ {
+ warned_once = 1;
+ WARN ((0, 0, _("Generating negative octal headers")));
+ }
+ where[size - 1] = '\0';
+ to_octal (value & MAX_VAL_WITH_DIGITS (valsize * CHAR_BIT, 1),
+ where, size - 1);
+ return true;
}
- else
- ERROR ((0, 0, _("value %s out of %s range %s..%s"),
- value_string, type, minval_string, maxval_string));
+ /* Otherwise fall back to substitution, if possible: */
}
+ else
+ substitute = NULL; /* No substitution for formats, other than GNU */
+
+ return to_chars_subst (negative, gnu_format, value, valsize, substitute,
+ where, size, type);
}
static uintmax_t
return r;
}
-void
+bool
gid_to_chars (gid_t v, char *p, size_t s)
{
- to_chars (v < 0, (uintmax_t) v, sizeof v, gid_substitute, p, s, "gid_t");
+ return to_chars (v < 0, (uintmax_t) v, sizeof v, gid_substitute, p, s, "gid_t");
}
-void
+bool
major_to_chars (major_t v, char *p, size_t s)
{
- to_chars (v < 0, (uintmax_t) v, sizeof v, 0, p, s, "major_t");
+ return to_chars (v < 0, (uintmax_t) v, sizeof v, 0, p, s, "major_t");
}
-void
+bool
minor_to_chars (minor_t v, char *p, size_t s)
{
- to_chars (v < 0, (uintmax_t) v, sizeof v, 0, p, s, "minor_t");
+ return to_chars (v < 0, (uintmax_t) v, sizeof v, 0, p, s, "minor_t");
}
-void
+bool
mode_to_chars (mode_t v, char *p, size_t s)
{
/* In the common case where the internal and external mode bits are the same,
&& S_IROTH == TOREAD && S_IWOTH == TOWRITE && S_IXOTH == TOEXEC
&& archive_format != POSIX_FORMAT
&& archive_format != USTAR_FORMAT
- && archive_format != GNU_FORMAT)
+ && archive_format != GNU_FORMAT
+ && archive_format != OLDGNU_FORMAT)
{
negative = v < 0;
u = v;
| (v & S_IWOTH ? TOWRITE : 0)
| (v & S_IXOTH ? TOEXEC : 0));
}
- to_chars (negative, u, sizeof v, 0, p, s, "mode_t");
+ return to_chars (negative, u, sizeof v, 0, p, s, "mode_t");
}
-void
+bool
off_to_chars (off_t v, char *p, size_t s)
{
- to_chars (v < 0, (uintmax_t) v, sizeof v, 0, p, s, "off_t");
+ return to_chars (v < 0, (uintmax_t) v, sizeof v, 0, p, s, "off_t");
}
-void
+bool
size_to_chars (size_t v, char *p, size_t s)
{
- to_chars (0, (uintmax_t) v, sizeof v, 0, p, s, "size_t");
+ return to_chars (0, (uintmax_t) v, sizeof v, 0, p, s, "size_t");
}
-void
+bool
time_to_chars (time_t v, char *p, size_t s)
{
- to_chars (v < 0, (uintmax_t) v, sizeof v, 0, p, s, "time_t");
+ return to_chars (v < 0, (uintmax_t) v, sizeof v, 0, p, s, "time_t");
}
static uintmax_t
return r;
}
-void
+bool
uid_to_chars (uid_t v, char *p, size_t s)
{
- to_chars (v < 0, (uintmax_t) v, sizeof v, uid_substitute, p, s, "uid_t");
+ return to_chars (v < 0, (uintmax_t) v, sizeof v, uid_substitute, p, s, "uid_t");
}
-void
+bool
uintmax_to_chars (uintmax_t v, char *p, size_t s)
{
- to_chars (0, v, sizeof v, 0, p, s, "uintmax_t");
+ return to_chars (0, v, sizeof v, 0, p, s, "uintmax_t");
}
void
file_dumpable_p (struct tar_stat_info *st)
{
if (dev_null_output)
- return totals_option && sparse_option && sparse_file_p (st);
+ return totals_option && sparse_option && ST_IS_SPARSE (st->stat);
return !(st->archive_file_size == 0
&& (st->stat.st_mode & MODE_R) == MODE_R);
}
size_t i;
if (length > PREFIX_FIELD_SIZE)
- length = PREFIX_FIELD_SIZE+2;
+ length = PREFIX_FIELD_SIZE + 1;
for (i = length - 1; i > 0; i--)
if (ISSLASH (name[i]))
break;
if (mode_option)
st->stat.st_mode =
((st->stat.st_mode & ~MODE_ALL)
- | mode_adjust (st->stat.st_mode, mode_option, initial_umask));
+ | mode_adjust (st->stat.st_mode, S_ISDIR (st->stat.st_mode) != 0,
+ initial_umask, mode_option, NULL));
/* Paul Eggert tried the trivial test ($WRITER cf a b; $READER tvf a)
for a few tars and came up with the following interoperability
xheader_store ("uid", st, NULL);
uid = 0;
}
- UID_TO_CHARS (uid, header->header.uid);
+ if (!UID_TO_CHARS (uid, header->header.uid))
+ return NULL;
}
{
xheader_store ("gid", st, NULL);
gid = 0;
}
- GID_TO_CHARS (gid, header->header.gid);
+ if (!GID_TO_CHARS (gid, header->header.gid))
+ return NULL;
}
{
xheader_store ("size", st, NULL);
size = 0;
}
- OFF_TO_CHARS (size, header->header.size);
+ if (!OFF_TO_CHARS (size, header->header.size))
+ return NULL;
}
{
- struct timespec mtime = st->mtime;
+ struct timespec mtime = set_mtime_option ? mtime_option : st->mtime;
if (archive_format == POSIX_FORMAT)
{
if (MAX_OCTAL_VAL (header->header.mtime) < mtime.tv_sec
|| mtime.tv_nsec != 0)
- xheader_store ("mtime", st, NULL);
+ xheader_store ("mtime", st, &mtime);
if (MAX_OCTAL_VAL (header->header.mtime) < mtime.tv_sec)
mtime.tv_sec = 0;
}
- TIME_TO_CHARS (mtime.tv_sec, header->header.mtime);
+ if (!TIME_TO_CHARS (mtime.tv_sec, header->header.mtime))
+ return NULL;
}
/* FIXME */
xheader_store ("devmajor", st, NULL);
devmajor = 0;
}
- MAJOR_TO_CHARS (devmajor, header->header.devmajor);
+ if (!MAJOR_TO_CHARS (devmajor, header->header.devmajor))
+ return NULL;
if (archive_format == POSIX_FORMAT
&& MAX_OCTAL_VAL (header->header.devminor) < devminor)
xheader_store ("devminor", st, NULL);
devminor = 0;
}
- MINOR_TO_CHARS (devminor, header->header.devminor);
+ if (!MINOR_TO_CHARS (devminor, header->header.devminor))
+ return NULL;
}
else if (archive_format != GNU_FORMAT && archive_format != OLDGNU_FORMAT)
{
- MAJOR_TO_CHARS (0, header->header.devmajor);
- MINOR_TO_CHARS (0, header->header.devminor);
+ if (!(MAJOR_TO_CHARS (0, header->header.devmajor)
+ && MINOR_TO_CHARS (0, header->header.devminor)))
+ return NULL;
}
if (archive_format == POSIX_FORMAT)
size_left),
quotearg_colon (st->orig_file_name),
STRINGIFY_BIGINT (size_left, buf)));
- if (! ignore_failed_read_option)
- exit_status = TAREXIT_FAILURE;
+ if (! ignore_failed_read_option)
+ exit_status = TAREXIT_DIFFERS;
pad_archive (size_left - (bufsize-count));
return dump_status_short;
}
void
create_archive (void)
{
- char *p;
+ const char *p;
open_archive (ACCESS_WRITE);
xheader_write_global ();
st->stat.st_size = 0;
blk = start_header (st);
if (!blk)
- return true;
+ return false;
tar_copy_str (blk->header.linkname, link_name, NAME_FIELD_SIZE);
blk->header.typeflag = LNKTYPE;
exit_status to failure, a clear diagnostic has been issued. */
static void
-dump_file0 (struct tar_stat_info *st, char const *p,
+dump_file0 (struct tar_stat_info *st, const char *p,
int top_level, dev_t parent_device)
{
union block *header;
char type;
+ off_t original_size;
struct timespec original_ctime;
struct timespec restore_times[2];
off_t block_ordinal = -1;
assign_string (&st->file_name,
safer_name_suffix (p, false, absolute_names_option));
+ transform_name (&st->file_name);
+
if (deref_stat (dereference_option, p, &st->stat) != 0)
{
stat_diag (p);
return;
}
- st->archive_file_size = st->stat.st_size;
+ st->archive_file_size = original_size = st->stat.st_size;
st->atime = restore_times[0] = get_stat_atime (&st->stat);
st->mtime = restore_times[1] = get_stat_mtime (&st->stat);
st->ctime = original_ctime = get_stat_ctime (&st->stat);
{
enum dump_status status;
- if (fd != -1 && sparse_option && sparse_file_p (st))
+ if (fd != -1 && sparse_option && ST_IS_SPARSE (st->stat))
{
status = sparse_dump_file (fd, st);
if (status == dump_status_not_implemented)
abort ();
}
+ file_count_links (st);
+
ok = status == dump_status_ok;
}
if (ok)
{
- if (timespec_cmp (get_stat_ctime (&final_stat), original_ctime) != 0)
- WARN ((0, 0, _("%s: file changed as we read it"),
- quotearg_colon (p)));
+ if (timespec_cmp (get_stat_ctime (&final_stat), original_ctime) != 0
+ || original_size < final_stat.st_size)
+ {
+ WARN ((0, 0, _("%s: file changed as we read it"),
+ quotearg_colon (p)));
+ if (exit_status == TAREXIT_SUCCESS)
+ exit_status = TAREXIT_DIFFERS;
+ }
else if (atime_preserve_option == replace_atime_preserve
&& set_file_atime (fd, p, restore_times) != 0)
utime_error (p);
}
void
-dump_file (char *p, int top_level, dev_t parent_device)
+dump_file (const char *p, int top_level, dev_t parent_device)
{
struct tar_stat_info st;
tar_stat_init (&st);