浏览代码

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.
Sergey Poznyakoff 15 年之前
父节点
当前提交
63e092548a
共有 4 个文件被更改,包括 189 次插入17 次删除
  1. 33 1
      NEWS
  2. 43 3
      doc/tar.texi
  3. 76 13
      src/tar.c
  4. 37 0
      src/xheader.c

+ 33 - 1
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>
 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
 If the --warning=no-file-removed option is given, no warning
 is issued and the exit code remains 0.
 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
 * Bugfixes
 ** Fix handling of hard link targets by -c --transform.
 ** Fix handling of hard link targets by -c --transform.
 ** Fix hard links recognition with -c --remove-files.
 ** Fix hard links recognition with -c --remove-files.
 ** Fix restoring files from backup (debian bug #508199).
 ** Fix restoring files from backup (debian bug #508199).
 ** Correctly restore modes and permissions on existing directories.
 ** 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
 version 1.22 - Sergey Poznyakoff, 2009-03-05

+ 43 - 3
doc/tar.texi

@@ -3055,8 +3055,8 @@ This option does not affect extraction from archives.
 
 
 @opsummary{pax-option}
 @opsummary{pax-option}
 @item --pax-option=@var{keyword-list}
 @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
 extended header keywords.  @var{Keyword-list} is a comma-separated
 list of keyword options.  @xref{PAX keywords}, for a detailed
 list of keyword options.  @xref{PAX keywords}, for a detailed
 discussion.
 discussion.
@@ -5400,7 +5400,7 @@ Name of the file owner group.
 @vrindex TAR_ATIME, to-command environment
 @vrindex TAR_ATIME, to-command environment
 @item TAR_ATIME
 @item TAR_ATIME
 Time of last access. It is a decimal number, representing seconds
 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
 precision, the nanoseconds are appended to the timestamp after a
 decimal point.
 decimal point.
 
 
@@ -9409,6 +9409,13 @@ will use the following default value:
 %d/PaxHeaders.%p/%f
 %d/PaxHeaders.%p/%f
 @end smallexample
 @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}
 @item globexthdr.name=@var{string}
 This keyword allows user control over the name that is written into
 This keyword allows user control over the name that is written into
 the ustar header blocks for global extended header records.  The name
 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}
 environment variable.  If @var{TMPDIR} is not set, @command{tar}
 uses @samp{/tmp}.
 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}
 @item @var{keyword}=@var{value}
 When used with one of archive-creation commands, these keyword/value pairs
 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
 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.
 stored in the archive.
 @end table
 @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
 @node Checksumming
 @subsection Checksumming Problems
 @subsection Checksumming Problems
 
 

+ 76 - 13
src/tar.c

@@ -1002,12 +1002,12 @@ set_stat_signal (const char *name)
 struct textual_date
 struct textual_date
 {
 {
   struct textual_date *next;
   struct textual_date *next;
-  struct timespec *ts;
+  struct timespec ts;
   const char *option;
   const char *option;
-  const char *date;
+  char *date;
 };
 };
 
 
-static void
+static int
 get_date_or_file (struct tar_args *args, const char *option,
 get_date_or_file (struct tar_args *args, const char *option,
 		  const char *str, struct timespec *ts)
 		  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"),
 	  WARN ((0, 0, _("Substituting %s for unknown date format %s"),
 		 tartime (*ts, false), quote (str)));
 		 tartime (*ts, false), quote (str)));
 	  ts->tv_nsec = 0;
 	  ts->tv_nsec = 0;
+	  return 1;
 	}
 	}
       else
       else
 	{
 	{
 	  struct textual_date *p = xmalloc (sizeof (*p));
 	  struct textual_date *p = xmalloc (sizeof (*p));
-	  p->ts = ts;
+	  p->ts = *ts;
 	  p->option = option;
 	  p->option = option;
-	  p->date = str;
+	  p->date = xstrdup (str);
 	  p->next = args->textual_date;
 	  p->next = args->textual_date;
 	  args->textual_date = p;
 	  args->textual_date = p;
 	}
 	}
     }
     }
+  return 0;
 }
 }
 
 
 static void
 static void
@@ -1050,10 +1052,14 @@ report_textual_dates (struct tar_args *args)
   for (p = args->textual_date; p; )
   for (p = args->textual_date; p; )
     {
     {
       struct textual_date *next = p->next;
       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);
       free (p);
       p = next;
       p = next;
     }
     }
@@ -1272,6 +1278,60 @@ tar_help_filter (int key, const char *text, void *input)
   obstack_free (&stk, NULL);
   obstack_free (&stk, NULL);
   return s;
   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
 static error_t
 parse_opt (int key, char *arg, struct argp_state *state)
 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;
       break;
       
       
     case PAX_OPTION:
     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;
       break;
       
       
     case POSIX_OPTION:
     case POSIX_OPTION:
@@ -2440,8 +2504,7 @@ decode_options (int argc, char **argv)
 
 
   checkpoint_finish_compile ();
   checkpoint_finish_compile ();
   
   
-  if (verbose_option)
-    report_textual_dates (&args);
+  report_textual_dates (&args);
 }
 }
 
 
 
 

+ 37 - 0
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 */
 /* Template for the name field of an 'x' type header */
 static char *exthdr_name;
 static char *exthdr_name;
 
 
+static char *exthdr_mtime_option;
+static time_t exthdr_mtime;
+
 /* Template for the name field of a 'g' type header */
 /* Template for the name field of a 'g' type header */
 static char *globexthdr_name;
 static char *globexthdr_name;
 
 
+static char *globexthdr_mtime_option;
+static time_t globexthdr_mtime;
+
 bool
 bool
 xheader_keyword_deleted_p (const char *kw)
 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));
   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
 static void
 xheader_set_keyword_equal (char *kw, char *eq)
 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);
     assign_string (&exthdr_name, p);
   else if (strcmp (kw, "globexthdr.name") == 0)
   else if (strcmp (kw, "globexthdr.name") == 0)
     assign_string (&globexthdr_name, p);
     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
   else
     {
     {
       if (xheader_protected_keyword_p (kw))
       if (xheader_protected_keyword_p (kw))
@@ -371,6 +396,18 @@ xheader_write (char type, char *name, time_t t, struct xheader *xhdr)
   char *p;
   char *p;
 
 
   size = xhdr->size;
   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 = start_private_header (name, size, t);
   header->header.typeflag = type;
   header->header.typeflag = type;