From 63e092548a9b87c0be0f0b286c883e1f3d52294c Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Wed, 7 Oct 2009 21:08:29 +0300 Subject: [PATCH] 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. --- NEWS | 34 +++++++++++++++++++- doc/tar.texi | 46 ++++++++++++++++++++++++-- src/tar.c | 89 +++++++++++++++++++++++++++++++++++++++++++-------- src/xheader.c | 37 +++++++++++++++++++++ 4 files changed, 189 insertions(+), 17 deletions(-) diff --git a/NEWS b/NEWS index bf02a24..7624d80 100644 --- a/NEWS +++ b/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 @@ -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 diff --git a/doc/tar.texi b/doc/tar.texi index e609368..1102e32 100644 --- a/doc/tar.texi +++ b/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 diff --git a/src/tar.c b/src/tar.c index 0c59352..a639974 100644 --- a/src/tar.c +++ b/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); } diff --git a/src/xheader.c b/src/xheader.c index e8fd6a2..5eabdfb 100644 --- a/src/xheader.c +++ b/src/xheader.c @@ -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; -- 2.45.2