From: Jeremy Bobbio Date: Thu, 24 Mar 2016 05:11:28 +0000 (+0200) Subject: New option --clamp-mtime X-Git-Tag: release_1_29~8 X-Git-Url: https://git.gag.com/?a=commitdiff_plain;h=13d04fe6ae5a343415299359944382f7a6d37816;p=debian%2Ftar New option --clamp-mtime The new `--clamp-mtime` option will change the behavior of `--mtime` to only use the time specified if the file mtime is newer than the given time. The `--clamp-mtime` option can only be used together with `--mtime`. Typical use case is to make builds reproducible: to loose less information, it's better to keep the original date of an archive, except for files modified during the build process. In that case, using a reference (and thus reproducible) timestamps for the latter is good enough. See for more information. Patch submitted by Jeremy Bobbio and Daniel Kahn Gillmor * doc/tar.1: Document --clamp-mtime * doc/tar.texi: Likewise. * src/common.h (set_mtime_option_mode): New enum (set_mtime_option): Change type to enum set_mtime_option_mode. (NEWER_OPTION_INITIALIZED): Rename to NEWER_OPTION_INITIALIZED. * src/create.c (start_header): Set mtime depending on set_mtime_option. * src/tar.c (options,parse_opt): New option --clamp-mtime (decode_options): Initialize mtime_option * tests/time02.at: New testcase. * tests/Makefile.am: Add new testcase * tests/testsuite.at: Likewise. --- diff --git a/doc/tar.1 b/doc/tar.1 index 26a354a3..b72f28e5 100644 --- a/doc/tar.1 +++ b/doc/tar.1 @@ -13,7 +13,7 @@ .\" .\" You should have received a copy of the GNU General Public License .\" along with this program. If not, see . -.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 @@ -1051,6 +1051,9 @@ Display progress messages every \fIN\fRth record (default 10). \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 diff --git a/doc/tar.texi b/doc/tar.texi index 0d537cca..e5b920ec 100644 --- a/doc/tar.texi +++ b/doc/tar.texi @@ -2585,6 +2585,11 @@ complies to UNIX98, was introduced with version 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 @@ -2985,6 +2990,11 @@ either a textual date representation (@pxref{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. @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 @@ -5445,6 +5455,17 @@ tar: Option --mtime: Treating date 'yesterday' as 2006-06-20 @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 diff --git a/src/common.h b/src/common.h index 09e42e1f..2337b0e2 100644 --- a/src/common.h +++ b/src/common.h @@ -211,13 +211,20 @@ GLOBAL bool multi_volume_option; 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. */ diff --git a/src/create.c b/src/create.c index 58a530de..3a0f2dce 100644 --- a/src/create.c +++ b/src/create.c @@ -823,7 +823,24 @@ start_header (struct tar_stat_info *st) } { - 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 diff --git a/src/list.c b/src/list.c index 4cd99631..4e36bd4f 100644 --- a/src/list.c +++ b/src/list.c @@ -195,7 +195,7 @@ read_and (void (*do_something) (void)) 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 diff --git a/src/tar.c b/src/tar.c index 60786786..cc494bb7 100644 --- a/src/tar.c +++ b/src/tar.c @@ -276,6 +276,7 @@ enum CHECK_DEVICE_OPTION, CHECKPOINT_OPTION, CHECKPOINT_ACTION_OPTION, + CLAMP_MTIME_OPTION, DELAY_DIRECTORY_RESTORE_OPTION, HARD_DEREFERENCE_OPTION, DELETE_OPTION, @@ -514,6 +515,8 @@ static struct argp_option options[] = { 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, @@ -1364,6 +1367,10 @@ parse_opt (int key, char *arg, struct argp_state *state) set_subcommand_option (CREATE_SUBCOMMAND); break; + case CLAMP_MTIME_OPTION: + set_mtime_option = CLAMP_MTIME; + break; + case 'd': set_subcommand_option (DIFF_SUBCOMMAND); break; @@ -1505,7 +1512,8 @@ parse_opt (int key, char *arg, struct argp_state *state) 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': @@ -1521,7 +1529,7 @@ parse_opt (int key, char *arg, struct argp_state *state) /* 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" @@ -2236,6 +2244,8 @@ decode_options (int argc, char **argv) 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; @@ -2397,7 +2407,7 @@ decode_options (int argc, char **argv) _("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); @@ -2464,6 +2474,13 @@ decode_options (int argc, char **argv) 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. diff --git a/tests/Makefile.am b/tests/Makefile.am index 02a3d790..53365826 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -218,6 +218,7 @@ TESTSUITE_AT = \ spmvp01.at\ spmvp10.at\ time01.at\ + time02.at\ truncate.at\ update.at\ update01.at\ diff --git a/tests/testsuite.at b/tests/testsuite.at index 0b37fa30..127f3a0e 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -358,6 +358,7 @@ m4_include([lustar03.at]) m4_include([old.at]) m4_include([time01.at]) +m4_include([time02.at]) AT_BANNER([Multivolume archives]) m4_include([multiv01.at]) diff --git a/tests/time02.at b/tests/time02.at new file mode 100644 index 00000000..c55b8ce1 --- /dev/null +++ b/tests/time02.at @@ -0,0 +1,42 @@ +# 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 . + +# 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