size_t nlink;
char name[1];
};
+
+struct exclude_tag
+{
+ const char *name;
+ size_t length;
+ struct exclude_tag *next;
+};
+
+static struct exclude_tag *exclude_tags;
+
+void
+add_exclude_tag (const char *name)
+{
+ struct exclude_tag *tag = xmalloc (sizeof tag[0]);
+ tag->next = exclude_tags;
+ tag->name = name;
+ tag->length = strlen (name);
+ exclude_tags = tag;
+}
+
+static bool
+check_exclude_tags (char *dirname)
+{
+ static char *tagname;
+ static size_t tagsize;
+ struct exclude_tag *tag;
+ size_t dlen = strlen (dirname);
+ char *nptr = NULL;
+ char *ret = NULL;
+
+ for (tag = exclude_tags; tag; tag = tag->next)
+ {
+ size_t size = dlen + tag->length + 1;
+ if (size > tagsize)
+ {
+ tagsize = size;
+ tagname = xrealloc (tagname, tagsize);
+ }
+
+ if (!nptr)
+ {
+ strcpy (tagname, dirname);
+ nptr = tagname + dlen;
+ }
+ strcpy (nptr, tag->name);
+ if (access (tagname, F_OK) == 0)
+ {
+ if (verbose_option)
+ WARN ((0, 0,
+ _("%s: contains a cache directory tag %s; not dumped"),
+ quotearg_colon (dirname),
+ quotearg_n (1, tag->name)));
+ return true;
+ }
+ }
+
+ return false;
+}
+
\f
/* The maximum uintmax_t value that can be represented with DIGITS digits,
assuming that each digit is BITS_PER_DIGIT wide. */
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;
}
else
minval_string = "0";
-
+
if (negative)
{
char *p = STRINGIFY_BIGINT (- value, valbuf + 1);
}
else
value_string = STRINGIFY_BIGINT (value, valbuf);
-
+
if (substitute)
{
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. */
+ /* 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);
}
else
substitute = NULL; /* No substitution for formats, other than GNU */
-
+
return to_chars_subst (negative, gnu_format, value, valsize, substitute,
where, size, type);
}
&& 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;
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);
}
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
}
{
- 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;
}
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;
}
return dump_status_ok;
}
+\f
/* Look in directory DIRNAME for a cache directory tag file
with the magic name "CACHEDIR.TAG" and a standard header,
as described at:
static char tagname[] = "CACHEDIR.TAG";
char *tagpath;
int fd;
- int tag_present = false;
+ bool tag_present = false;
tagpath = xmalloc (strlen (dirname) + strlen (tagname) + 1);
strcpy (tagpath, dirname);
quotearg_colon (st->orig_file_name)));
return;
}
-
- if (exclude_caches_option
- && check_cache_directory(st->orig_file_name))
- {
- if (verbose_option)
- WARN ((0, 0,
- _("%s: contains a cache directory tag; not dumped"),
- quotearg_colon (st->orig_file_name)));
- return;
- }
-
+
{
char const *entry;
size_t entry_len;
return false;
}
- ensure_slash (&st->orig_file_name);
- ensure_slash (&st->file_name);
-
dump_dir0 (directory, st, top_level, parent_device);
free (directory);
{
union block *header;
char type;
+ off_t original_size;
struct timespec original_ctime;
struct timespec restore_times[2];
off_t block_ordinal = -1;
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);
if (is_dir)
{
+ ensure_slash (&st->orig_file_name);
+ ensure_slash (&st->file_name);
+
+ if (exclude_caches_option
+ && check_cache_directory (st->orig_file_name))
+ {
+ if (verbose_option)
+ WARN ((0, 0,
+ _("%s: contains a cache directory tag; not dumped"),
+ quotearg_colon (st->orig_file_name)));
+ return;
+ }
+
+ if (check_exclude_tags (st->orig_file_name))
+ return;
+
ok = dump_dir (fd, st, top_level, parent_device);
/* dump_dir consumes FD if successful. */
{
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)
}
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);