.\"
.\" You should have received a copy of the GNU General Public License
.\" along with this program. If not, see <http://www.gnu.org/licenses/>.
-.TH TAR 1 "March 16, 2016" "TAR" "GNU TAR Manual"
+.TH TAR 1 "March 23, 2016" "TAR" "GNU TAR Manual"
.SH NAME
tar \- an archiving utility
.SH SYNOPSIS
\fB\-\-checkpoint\-action\fR=\fIACTION\fR
Run \fIACTION\fR on each checkpoint.
.TP
+\fB\-\-clamp\-mtime\fR
+Only set time when the file is more recent than what was given with \-\-mtime.
+.TP
\fB\-\-full\-time\fR
Print file time to its full resolution.
.TP
writing the archive. This allows you to directly act on archives
while saving space. @xref{gzip}.
+@opsummary{clamp-mtime}
+@item --clamp-mtime
+
+(See @option{--mtime}.)
+
@opsummary{confirmation}
@item --confirmation
name of the existing file, starting with @samp{/} or @samp{.}. In the
latter case, the modification time of that file is used. @xref{override}.
+When @command{--clamp-mtime} is also specified, files with
+modification times earlier than @var{date} will retain their actual
+modification times, and @var{date} will only be used for files whose
+modification times are later than @var{date}.
+
@opsummary{multi-volume}
@item --multi-volume
@itemx -M
@dots{}
@end smallexample
+@noindent
+When used with @option{--clamp-mtime} @GNUTAR{} will only set the
+modification date to @var{date} on files whose actual modification
+date is later than @var{date}. This is to make it easy to build
+reproducible archives given a common timestamp for generated files
+while still retaining the original timestamps of untouched files.
+
+@smallexample
+$ @kbd{tar -c -f archive.tar --clamp-mtime --mtime=@atchar{}$SOURCE_DATE_EPOCH .}
+@end smallexample
+
@item --owner=@var{user}
@opindex owner
do not get archived (also see after_date_option above). */
GLOBAL struct timespec newer_mtime_option;
-/* If true, override actual mtime (see below) */
-GLOBAL bool set_mtime_option;
-/* Value to be put in mtime header field instead of the actual mtime */
+enum set_mtime_option_mode
+{
+ USE_FILE_MTIME,
+ FORCE_MTIME,
+ CLAMP_MTIME,
+};
+
+/* Override actual mtime if set to FORCE_MTIME or CLAMP_MTIME */
+GLOBAL enum set_mtime_option_mode set_mtime_option;
+/* Value to use when forcing or clamping the mtime header field. */
GLOBAL struct timespec mtime_option;
-/* Return true if newer_mtime_option is initialized. */
-#define NEWER_OPTION_INITIALIZED(opt) (0 <= (opt).tv_nsec)
+/* Return true if mtime_option or newer_mtime_option is initialized. */
+#define TIME_OPTION_INITIALIZED(opt) (0 <= (opt).tv_nsec)
/* Return true if the struct stat ST's M time is less than
newer_mtime_option. */
}
{
- struct timespec mtime = set_mtime_option ? mtime_option : st->mtime;
+ struct timespec mtime;
+
+ switch (set_mtime_option)
+ {
+ case USE_FILE_MTIME:
+ mtime = st->mtime;
+ break;
+
+ case FORCE_MTIME:
+ mtime = mtime_option;
+ break;
+
+ case CLAMP_MTIME:
+ mtime = timespec_cmp (st->mtime, mtime_option) > 0
+ ? mtime_option : st->mtime;
+ break;
+ }
+
if (archive_format == POSIX_FORMAT)
{
if (MAX_OCTAL_VAL (header->header.mtime) < mtime.tv_sec
decode_header (current_header, ¤t_stat_info,
¤t_format, 1);
if (! name_match (current_stat_info.file_name)
- || (NEWER_OPTION_INITIALIZED (newer_mtime_option)
+ || (TIME_OPTION_INITIALIZED (newer_mtime_option)
/* FIXME: We get mtime now, and again later; this causes
duplicate diagnostics if header.mtime is bogus. */
&& ((mtime.tv_sec
CHECK_DEVICE_OPTION,
CHECKPOINT_OPTION,
CHECKPOINT_ACTION_OPTION,
+ CLAMP_MTIME_OPTION,
DELAY_DIRECTORY_RESTORE_OPTION,
HARD_DEREFERENCE_OPTION,
DELETE_OPTION,
N_("use FILE to map file owner GIDs and names"), GRID+1 },
{"mtime", MTIME_OPTION, N_("DATE-OR-FILE"), 0,
N_("set mtime for added files from DATE-OR-FILE"), GRID+1 },
+ {"clamp-mtime", CLAMP_MTIME_OPTION, 0, 0,
+ N_("only set time when the file is more recent than what was given with --mtime"), GRID+1 },
{"mode", MODE_OPTION, N_("CHANGES"), 0,
N_("force (symbolic) mode CHANGES for added files"), GRID+1 },
{"atime-preserve", ATIME_PRESERVE_OPTION,
set_subcommand_option (CREATE_SUBCOMMAND);
break;
+ case CLAMP_MTIME_OPTION:
+ set_mtime_option = CLAMP_MTIME;
+ break;
+
case 'd':
set_subcommand_option (DIFF_SUBCOMMAND);
break;
case MTIME_OPTION:
get_date_or_file (args, "--mtime", arg, &mtime_option);
- set_mtime_option = true;
+ if (set_mtime_option == USE_FILE_MTIME)
+ set_mtime_option = FORCE_MTIME;
break;
case 'n':
/* Fall through. */
case NEWER_MTIME_OPTION:
- if (NEWER_OPTION_INITIALIZED (newer_mtime_option))
+ if (TIME_OPTION_INITIALIZED (newer_mtime_option))
USAGE_ERROR ((0, 0, _("More than one threshold date")));
get_date_or_file (args,
key == NEWER_MTIME_OPTION ? "--newer-mtime"
newer_mtime_option.tv_sec = TYPE_MINIMUM (time_t);
newer_mtime_option.tv_nsec = -1;
+ mtime_option.tv_sec = TYPE_MINIMUM (time_t);
+ mtime_option.tv_nsec = -1;
recursion_option = FNM_LEADING_DIR;
unquote_option = true;
tar_sparse_major = 1;
_("Multiple archive files require '-M' option")));
if (listed_incremental_option
- && NEWER_OPTION_INITIALIZED (newer_mtime_option))
+ && TIME_OPTION_INITIALIZED (newer_mtime_option))
{
struct option_locus *listed_loc = optloc_lookup (OC_LISTED_INCREMENTAL);
struct option_locus *newer_loc = optloc_lookup (OC_NEWER);
USAGE_ERROR ((0, 0, _("Cannot concatenate compressed archives")));
}
+ if (set_mtime_option == CLAMP_MTIME)
+ {
+ if (!TIME_OPTION_INITIALIZED (mtime_option))
+ USAGE_ERROR ((0, 0,
+ _("--clamp-mtime needs a date specified using --mtime")));
+ }
+
/* It is no harm to use --pax-option on non-pax archives in archive
reading mode. It may even be useful, since it allows to override
file attributes from tar headers. Therefore I allow such usage.
spmvp01.at\
spmvp10.at\
time01.at\
+ time02.at\
truncate.at\
update.at\
update01.at\
m4_include([old.at])
m4_include([time01.at])
+m4_include([time02.at])
AT_BANNER([Multivolume archives])
m4_include([multiv01.at])
--- /dev/null
+# Test clamping mtime GNU tar. -*- Autotest -*-
+#
+# Copyright 2016 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# written by Daniel Kahn Gillmor
+
+AT_SETUP([time: clamping mtime])
+AT_KEYWORDS([time time02])
+
+AT_TAR_CHECK([
+export TZ=UTC0
+mkdir dir
+
+touch -d 2015-12-01T00:00:00 dir/a >/dev/null 2>&1 || AT_SKIP_TEST
+touch -d 2016-01-01T00:00:00 dir/b >/dev/null 2>&1 || AT_SKIP_TEST
+touch -d 2016-02-01T00:00:00 dir/c >/dev/null 2>&1 || AT_SKIP_TEST
+touch -d 2038-01-01T00:00:00 dir/d >/dev/null 2>&1 || AT_SKIP_TEST
+
+tar -c --mtime 2016-01-15T00:00:00 --clamp-mtime -f archive.tar dir
+tar -d -f archive.tar dir
+],
+[1],
+[
+dir/c: Mod time differs
+dir/d: Mod time differs
+], [], [], [],
+[pax])
+
+AT_CLEANUP