Provide a way to explicitly set mtime for extended header ustar blocks.
* src/tar.c (struct textual_date): ts is a copy of the structure, not a pointer to it. Date is a copy as well, hence the `const' is taken away. (get_date_or_file): Return 0/1 depending on success/failure. Copy timestamp to the `ts' member. Store a copy of the string in `date'. (report_textual_dates): Report only if verbose_option is set, but always free the list. (expand_pax_option): New function. (parse_opt): Preprocess the argument to xheader_set_option with expand_pax_option. (decode_options): Call report_textual_dates unconditionally. * src/xheader.c (exthdr_mtime_option, exthdr_mtime) (globexthdr_mtime_option, globexthdr_mtime): New statics. (xheader_set_keyword_equal): handle exthdr.mtime and globexthdr.mtime. (xheader_write): Override `t' argument if a corresponding exthdr.mtime or globexthdr.mtime option is set. * NEWS: Update * doc/tar.texi: Document the changes.
This commit is contained in:
34
NEWS
34
NEWS
@@ -1,4 +1,4 @@
|
||||
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>
|
||||
|
||||
|
||||
@@ -53,11 +53,43 @@ and sets the exit code to 1, which means "some files differ".
|
||||
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.
|
||||
|
||||
|
||||
version 1.22 - Sergey Poznyakoff, 2009-03-05
|
||||
|
||||
46
doc/tar.texi
46
doc/tar.texi
@@ -3055,8 +3055,8 @@ This option does not affect extraction from archives.
|
||||
|
||||
@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.
|
||||
@@ -5400,7 +5400,7 @@ Name of the file owner group.
|
||||
@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.
|
||||
|
||||
@@ -9409,6 +9409,13 @@ will use the following default value:
|
||||
%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
|
||||
@@ -9438,6 +9445,13 @@ where @samp{$TMPDIR} represents the value of the @var{TMPDIR}
|
||||
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
|
||||
@@ -9467,6 +9481,32 @@ the group name will be forced to a new value for all files
|
||||
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
|
||||
|
||||
|
||||
89
src/tar.c
89
src/tar.c
@@ -1002,12 +1002,12 @@ set_stat_signal (const char *name)
|
||||
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)
|
||||
{
|
||||
@@ -1030,17 +1030,19 @@ get_date_or_file (struct tar_args *args, const char *option,
|
||||
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
|
||||
@@ -1050,10 +1052,14 @@ report_textual_dates (struct tar_args *args)
|
||||
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;
|
||||
}
|
||||
@@ -1272,6 +1278,60 @@ tar_help_filter (int key, const char *text, void *input)
|
||||
obstack_free (&stk, NULL);
|
||||
return s;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
static error_t
|
||||
parse_opt (int key, char *arg, struct argp_state *state)
|
||||
@@ -1840,8 +1900,12 @@ 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:
|
||||
@@ -2440,8 +2504,7 @@ decode_options (int argc, char **argv)
|
||||
|
||||
checkpoint_finish_compile ();
|
||||
|
||||
if (verbose_option)
|
||||
report_textual_dates (&args);
|
||||
report_textual_dates (&args);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -96,9 +96,15 @@ static struct keyword_list *global_header_override_list;
|
||||
/* 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)
|
||||
{
|
||||
@@ -156,6 +162,21 @@ xheader_set_single_keyword (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)
|
||||
{
|
||||
@@ -186,6 +207,10 @@ 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))
|
||||
@@ -371,6 +396,18 @@ xheader_write (char type, char *name, time_t t, struct xheader *xhdr)
|
||||
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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user