-GNU tar NEWS - User visible changes. 2009-09-08
+GNU tar NEWS - User visible changes. 2009-10-07
Please send GNU tar bug reports to <bug-tar@gnu.org>
\f
If the --warning=no-file-removed option is given, no warning
is issued and the exit code remains 0.
+* Modification times of PAX extended headers.
+
+Modification times in the ustar header blocks for the
+extended headers are set to the mtimes of the corresponding archive
+members. This can be overridden by the
+
+ --pax-opion='exthdr.mtime=STRING'
+
+command line option. The STRING is either the number of seconds since
+the Epoch or a `Time reference' (see below).
+
+Modification times in the ustar header blocks for the global
+extended headers are set to the time when tar was invoked.
+
+This can be overridden by the
+
+ --pax-opion='globexthdr.mtime=STRING'
+
+command line option. The STRING is either the number of seconds since
+the Epoch or a `Time reference' (see below).
+
+* Time references in --pax-option argument.
+
+Any value from the --pax-option argument that is enclosed in a pair
+of curly braces. In that case, the string between the braces is
+understood either as a textual time representation, as described in
+chapter 7, "Date input formats", of the Tar manual, or as a name of
+the existing file, starting with `/' or `.'. In the latter
+case, the value is replaced with the modification time of that file.
+
* Bugfixes
** Fix handling of hard link targets by -c --transform.
** Fix hard links recognition with -c --remove-files.
** Fix restoring files from backup (debian bug #508199).
** Correctly restore modes and permissions on existing directories.
+** The --remove-files option removes the files only if they were
+succesfully stored in the archive.
\f
version 1.22 - Sergey Poznyakoff, 2009-03-05
@opsummary{pax-option}
@item --pax-option=@var{keyword-list}
-This option is meaningful only with @acronym{POSIX.1-2001} archives
-(@pxref{posix}). It modifies the way @command{tar} handles the
+This option enables creation of the archive in @acronym{POSIX.1-2001}
+format (@pxref{posix}) and modifies the way @command{tar} handles the
extended header keywords. @var{Keyword-list} is a comma-separated
list of keyword options. @xref{PAX keywords}, for a detailed
discussion.
@vrindex TAR_ATIME, to-command environment
@item TAR_ATIME
Time of last access. It is a decimal number, representing seconds
-since the epoch. If the archive provides times with nanosecond
+since the Epoch. If the archive provides times with nanosecond
precision, the nanoseconds are appended to the timestamp after a
decimal point.
%d/PaxHeaders.%p/%f
@end smallexample
+@item exthdr.mtime=@var{value}
+
+This keyword defines the value of the @samp{mtime} field that
+is written into the ustar header blocks for the extended headers.
+By default, the @samp{mtime} field is set to the modification time
+of the archive member described by that extended headers.
+
@item globexthdr.name=@var{string}
This keyword allows user control over the name that is written into
the ustar header blocks for global extended header records. The name
environment variable. If @var{TMPDIR} is not set, @command{tar}
uses @samp{/tmp}.
+@item exthdr.mtime=@var{value}
+
+This keyword defines the value of the @samp{mtime} field that
+is written into the ustar header blocks for the global extended headers.
+By default, the @samp{mtime} field is set to the time when
+@command{tar} was invoked.
+
@item @var{keyword}=@var{value}
When used with one of archive-creation commands, these keyword/value pairs
will be included at the beginning of the archive in a global extended
stored in the archive.
@end table
+In any of the forms described above, the @var{value} may be
+a string enclosed in curly braces. In that case, the string
+between the braces is understood either as a textual time
+representation, as described in @ref{Date input formats}, or a name of
+the existing file, starting with @samp{/} or @samp{.}. In the latter
+case, the modification time of that file is used.
+
+For example, to set all modification times to the current date, you
+use the following option:
+
+@smallexample
+--pax-option='mtime:=@{now@}'
+@end smallexample
+
+Note quoting of the option's argument.
+
+@cindex archives, binary equivalent
+@cindex binary equivalent archives, creating
+As another example, here is the option that ensures that any two
+archives created using it, will be binary equivalent if they have the
+same contents:
+
+@smallexample
+--pax-option=exthdr.name=%d/PaxHeaders/%f,atime:=0
+@end smallexample
+
@node Checksumming
@subsection Checksumming Problems
struct textual_date
{
struct textual_date *next;
- struct timespec *ts;
+ struct timespec ts;
const char *option;
- const char *date;
+ char *date;
};
-static void
+static int
get_date_or_file (struct tar_args *args, const char *option,
const char *str, struct timespec *ts)
{
WARN ((0, 0, _("Substituting %s for unknown date format %s"),
tartime (*ts, false), quote (str)));
ts->tv_nsec = 0;
+ return 1;
}
else
{
struct textual_date *p = xmalloc (sizeof (*p));
- p->ts = ts;
+ p->ts = *ts;
p->option = option;
- p->date = str;
+ p->date = xstrdup (str);
p->next = args->textual_date;
args->textual_date = p;
}
}
+ return 0;
}
static void
for (p = args->textual_date; p; )
{
struct textual_date *next = p->next;
- char const *treated_as = tartime (*p->ts, true);
- if (strcmp (p->date, treated_as) != 0)
- WARN ((0, 0, _("Option %s: Treating date `%s' as %s"),
- p->option, p->date, treated_as));
+ if (verbose_option)
+ {
+ char const *treated_as = tartime (p->ts, true);
+ if (strcmp (p->date, treated_as) != 0)
+ WARN ((0, 0, _("Option %s: Treating date `%s' as %s"),
+ p->option, p->date, treated_as));
+ }
+ free (p->date);
free (p);
p = next;
}
obstack_free (&stk, NULL);
return s;
}
+\f
+static char *
+expand_pax_option (struct tar_args *targs, const char *arg)
+{
+ struct obstack stk;
+ char *res;
+
+ obstack_init (&stk);
+ while (*arg)
+ {
+ size_t seglen = strcspn (arg, ",");
+ char *p = memchr (arg, '=', seglen);
+ if (p)
+ {
+ size_t len = p - arg + 1;
+ obstack_grow (&stk, arg, len);
+ len = seglen - len;
+ for (++p; *p && isspace ((unsigned char) *p); p++)
+ len--;
+ if (*p == '{' && p[len-1] == '}')
+ {
+ struct timespec ts;
+ char *tmp = xmalloc (len);
+ memcpy (tmp, p + 1, len-2);
+ 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);
+ obstack_grow (&stk, s, strlen (s));
+ }
+ else
+ obstack_grow (&stk, p, len);
+ free (tmp);
+ }
+ else
+ obstack_grow (&stk, p, len);
+ }
+ else
+ obstack_grow (&stk, arg, seglen);
+
+ arg += seglen;
+ if (*arg)
+ {
+ obstack_1grow (&stk, *arg);
+ arg++;
+ }
+ }
+ obstack_1grow (&stk, 0);
+ res = xstrdup (obstack_finish (&stk));
+ obstack_free (&stk, NULL);
+ return res;
+}
+
\f
static error_t
parse_opt (int key, char *arg, struct argp_state *state)
break;
case PAX_OPTION:
- args->pax_option = true;
- xheader_set_option (arg);
+ {
+ char *tmp = expand_pax_option (args, arg);
+ args->pax_option = true;
+ xheader_set_option (tmp);
+ free (tmp);
+ }
break;
case POSIX_OPTION:
checkpoint_finish_compile ();
- if (verbose_option)
- report_textual_dates (&args);
+ report_textual_dates (&args);
}
\f
/* Template for the name field of an 'x' type header */
static char *exthdr_name;
+static char *exthdr_mtime_option;
+static time_t exthdr_mtime;
+
/* Template for the name field of a 'g' type header */
static char *globexthdr_name;
+static char *globexthdr_mtime_option;
+static time_t globexthdr_mtime;
+
bool
xheader_keyword_deleted_p (const char *kw)
{
USAGE_ERROR ((0, 0, _("Keyword %s is unknown or not yet implemented"), 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)
+ ERROR ((0, 0, _("Time stamp is out of allowed range")));
+ else
+ {
+ *tval = t;
+ assign_string (sval, input);
+ }
+}
+
static void
xheader_set_keyword_equal (char *kw, char *eq)
{
assign_string (&exthdr_name, p);
else if (strcmp (kw, "globexthdr.name") == 0)
assign_string (&globexthdr_name, p);
+ else if (strcmp (kw, "exthdr.mtime") == 0)
+ assign_time_option (&exthdr_mtime_option, &exthdr_mtime, p);
+ else if (strcmp (kw, "globexthdr.mtime") == 0)
+ assign_time_option (&globexthdr_mtime_option, &globexthdr_mtime, p);
else
{
if (xheader_protected_keyword_p (kw))
char *p;
size = xhdr->size;
+ switch (type)
+ {
+ case XGLTYPE:
+ if (globexthdr_mtime_option)
+ t = globexthdr_mtime;
+ break;
+
+ case XHDTYPE:
+ if (exthdr_mtime_option)
+ t = exthdr_mtime;
+ break;
+ }
header = start_private_header (name, size, t);
header->header.typeflag = type;