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
<https://wiki.debian.org/ReproducibleBuilds> for more information.

Patch submitted by Jeremy Bobbio and
Daniel Kahn Gillmor <dkg@fifthhorseman.net>

* 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.
This commit is contained in:
Jeremy Bobbio
2016-03-24 07:26:27 +02:00
committed by Sergey Poznyakoff
parent 8d31493c99
commit 13d04fe6ae
9 changed files with 120 additions and 11 deletions
+12 -5
View File
@@ -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. */
+18 -1
View File
@@ -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
+1 -1
View File
@@ -195,7 +195,7 @@ read_and (void (*do_something) (void))
decode_header (current_header, &current_stat_info,
&current_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
+20 -3
View File
@@ -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.