New option: --set-mtime-command
* NEWS: Document new option. * src/common.h (COMMAND_MTIME): New constant. * src/create.c (set_mtime_command) (set_mtime_format): New globals. (sys_exec_setmtime_script): New prototype. * src/system.c (start_header): Handle COMMAND_MTIME. * src/tar.c (sys_exec_setmtime_script): New function.
This commit is contained in:
21
NEWS
21
NEWS
@@ -1,10 +1,29 @@
|
||||
GNU tar NEWS - User visible changes. 2023-07-24
|
||||
GNU tar NEWS - User visible changes. 2023-08-01
|
||||
Please send GNU tar bug reports to <bug-tar@gnu.org>
|
||||
|
||||
version TBD
|
||||
|
||||
* New manual section "Reproducibility", for reproducible tarballs.
|
||||
|
||||
* New options: --set-mtime-command and --set-mtime-format
|
||||
|
||||
Both options are valid when archiving files.
|
||||
|
||||
** --set-mtime-command=COMMAND
|
||||
|
||||
For each FILE being archived, run "COMMAND FILE", parse its
|
||||
output as time string and set mtime value of the archive member
|
||||
from the result.
|
||||
|
||||
Unless --set-mtime-format is also used, the output is parsed
|
||||
as argument to --mtime option (see GNU tar manual, chapter 4
|
||||
"Date input formats".
|
||||
|
||||
** --set-mtime-format=FMT
|
||||
|
||||
Defines output format for the COMMAND set by the above option. If
|
||||
used, command output will be parsed using strptime(3).
|
||||
|
||||
|
||||
version 1.35 - Sergey Poznyakoff, 2023-07-18
|
||||
|
||||
|
||||
13
src/common.h
13
src/common.h
@@ -219,6 +219,7 @@ enum set_mtime_option_mode
|
||||
USE_FILE_MTIME,
|
||||
FORCE_MTIME,
|
||||
CLAMP_MTIME,
|
||||
COMMAND_MTIME,
|
||||
};
|
||||
|
||||
/* Override actual mtime if set to FORCE_MTIME or CLAMP_MTIME */
|
||||
@@ -226,6 +227,13 @@ 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;
|
||||
|
||||
/* Command to use to set mtime when archiving. */
|
||||
GLOBAL char *set_mtime_command;
|
||||
|
||||
/* Format (as per strptime(3)) of the output of the above command. If
|
||||
not set, parse_datetime will be used. */
|
||||
GLOBAL char *set_mtime_format;
|
||||
|
||||
/* Return true if mtime_option or newer_mtime_option is initialized. */
|
||||
#define TIME_OPTION_INITIALIZED(opt) (0 <= (opt).tv_nsec)
|
||||
|
||||
@@ -923,6 +931,11 @@ void sys_exec_checkpoint_script (const char *script_name,
|
||||
const char *archive_name,
|
||||
int checkpoint_number);
|
||||
bool mtioseek (bool count_files, off_t count);
|
||||
int sys_exec_setmtime_script (const char *script_name,
|
||||
int dirfd,
|
||||
const char *file_name,
|
||||
const char *fmt,
|
||||
struct timespec *ts);
|
||||
|
||||
/* Module compare.c */
|
||||
void report_difference (struct tar_stat_info *st, const char *message, ...)
|
||||
|
||||
@@ -843,6 +843,15 @@ start_header (struct tar_stat_info *st)
|
||||
mtime = timespec_cmp (st->mtime, mtime_option) > 0
|
||||
? mtime_option : st->mtime;
|
||||
break;
|
||||
|
||||
case COMMAND_MTIME:
|
||||
if (sys_exec_setmtime_script (set_mtime_command,
|
||||
chdir_fd,
|
||||
st->orig_file_name,
|
||||
set_mtime_format,
|
||||
&mtime))
|
||||
mtime = st->mtime;
|
||||
break;
|
||||
}
|
||||
|
||||
if (archive_format == POSIX_FORMAT)
|
||||
|
||||
176
src/system.c
176
src/system.c
@@ -23,6 +23,8 @@
|
||||
#include <rmt.h>
|
||||
#include <signal.h>
|
||||
#include <wordsplit.h>
|
||||
#include <poll.h>
|
||||
#include <parse-datetime.h>
|
||||
|
||||
static _Noreturn void
|
||||
xexec (const char *cmd)
|
||||
@@ -149,6 +151,15 @@ sys_child_open_for_uncompress (void)
|
||||
FATAL_ERROR ((0, 0, _("Cannot use compressed or remote archives")));
|
||||
}
|
||||
|
||||
int
|
||||
sys_exec_setmtime_script (const char *script_name,
|
||||
int dirfd,
|
||||
const char *file_name,
|
||||
const char *fmt,
|
||||
struct timespec *ts)
|
||||
{
|
||||
FATAL_ERROR ((0, 0, _("--set-mtime-command not implemented on this platform")));
|
||||
}
|
||||
#else
|
||||
|
||||
extern union block *record_start; /* FIXME */
|
||||
@@ -183,11 +194,12 @@ sys_file_is_archive (struct tar_stat_info *p)
|
||||
&& p->stat.st_ino == archive_stat.st_ino);
|
||||
}
|
||||
|
||||
static char const dev_null[] = "/dev/null";
|
||||
|
||||
/* Detect if outputting to "/dev/null". */
|
||||
void
|
||||
sys_detect_dev_null_output (void)
|
||||
{
|
||||
static char const dev_null[] = "/dev/null";
|
||||
static struct stat dev_null_stat;
|
||||
|
||||
dev_null_output = (strcmp (archive_name_array[0], dev_null) == 0
|
||||
@@ -916,4 +928,166 @@ sys_exec_checkpoint_script (const char *script_name,
|
||||
xexec (script_name);
|
||||
}
|
||||
|
||||
int
|
||||
sys_exec_setmtime_script (const char *script_name,
|
||||
int dirfd,
|
||||
const char *file_name,
|
||||
const char *fmt,
|
||||
struct timespec *ts)
|
||||
{
|
||||
pid_t pid;
|
||||
int p[2];
|
||||
int stop = 0;
|
||||
struct pollfd pfd;
|
||||
|
||||
char *buffer = NULL;
|
||||
size_t buflen = 0;
|
||||
size_t bufsize = 0;
|
||||
char *cp;
|
||||
int rc = 0;
|
||||
|
||||
if (pipe (p))
|
||||
FATAL_ERROR ((0, errno, _("pipe failed")));
|
||||
|
||||
if ((pid = xfork ()) == 0)
|
||||
{
|
||||
char *command = xmalloc (strlen (script_name) + strlen (file_name) + 2);
|
||||
|
||||
strcpy (command, script_name);
|
||||
strcat (command, " ");
|
||||
strcat (command, file_name);
|
||||
|
||||
if (dirfd != AT_FDCWD)
|
||||
{
|
||||
if (fchdir (dirfd))
|
||||
FATAL_ERROR ((0, errno, _("chdir failed")));
|
||||
}
|
||||
|
||||
close (0);
|
||||
close (1);
|
||||
|
||||
if (open (dev_null, O_RDONLY) == -1)
|
||||
open_error (dev_null);
|
||||
|
||||
if (dup2 (p[1], 1) == -1)
|
||||
FATAL_ERROR ((0, errno, _("dup2 failed")));
|
||||
close (p[0]);
|
||||
|
||||
priv_set_restore_linkdir ();
|
||||
xexec (command);
|
||||
}
|
||||
close (p[1]);
|
||||
|
||||
pfd.fd = p[0];
|
||||
pfd.events = POLLIN;
|
||||
|
||||
while (1)
|
||||
{
|
||||
int n = poll (&pfd, 1, -1);
|
||||
if (n == -1)
|
||||
{
|
||||
if (errno != EINTR)
|
||||
{
|
||||
ERROR ((0, errno, _("poll failed")));
|
||||
stop = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (n == 0)
|
||||
break;
|
||||
if (pfd.revents & POLLIN)
|
||||
{
|
||||
if (buflen == bufsize)
|
||||
{
|
||||
if (bufsize == 0)
|
||||
bufsize = BUFSIZ;
|
||||
buffer = x2nrealloc (buffer, &bufsize, 1);
|
||||
}
|
||||
n = read (pfd.fd, buffer + buflen, bufsize - buflen);
|
||||
if (n == -1)
|
||||
{
|
||||
ERROR ((0, errno, _("error reading output of %s"), script_name));
|
||||
stop = 1;
|
||||
break;
|
||||
}
|
||||
if (n == 0)
|
||||
break;
|
||||
buflen += n;
|
||||
}
|
||||
else if (pfd.revents & POLLHUP)
|
||||
break;
|
||||
}
|
||||
close (pfd.fd);
|
||||
|
||||
if (stop)
|
||||
kill (SIGKILL, pid);
|
||||
|
||||
sys_wait_for_child (pid, false);
|
||||
|
||||
if (stop)
|
||||
{
|
||||
free (buffer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (buflen == 0)
|
||||
{
|
||||
ERROR ((0, 0, _("empty output from \"%s %s\""), script_name, file_name));
|
||||
return -1;
|
||||
}
|
||||
|
||||
cp = memchr (buffer, '\n', buflen);
|
||||
if (cp)
|
||||
*cp = 0;
|
||||
else
|
||||
{
|
||||
if (buflen == bufsize)
|
||||
buffer = x2nrealloc (buffer, &bufsize, 1);
|
||||
buffer[buflen] = 0;
|
||||
}
|
||||
|
||||
if (fmt)
|
||||
{
|
||||
struct tm tm;
|
||||
time_t t;
|
||||
cp = strptime (buffer, fmt, &tm);
|
||||
if (cp == NULL)
|
||||
{
|
||||
ERROR ((0, 0, _("output from \"%s %s\" does not satisfy format string: %s"),
|
||||
script_name, file_name, buffer));
|
||||
rc = -1;
|
||||
}
|
||||
else if (*cp != 0)
|
||||
{
|
||||
WARN ((0, 0, _("unconsumed output from \"%s %s\": %s"),
|
||||
script_name, file_name, cp));
|
||||
rc = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
t = mktime (&tm);
|
||||
if (t == (time_t) -1)
|
||||
{
|
||||
ERROR ((0, errno, _("mktime failed")));
|
||||
rc = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ts->tv_sec = t;
|
||||
ts->tv_nsec = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (! parse_datetime (ts, buffer, NULL))
|
||||
{
|
||||
ERROR ((0, 0, _("unparsable output from \"%s %s\": %s"),
|
||||
script_name, file_name, buffer));
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
free (buffer);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* not MSDOS */
|
||||
|
||||
26
src/tar.c
26
src/tar.c
@@ -350,6 +350,8 @@ enum
|
||||
XATTR_EXCLUDE,
|
||||
XATTR_INCLUDE,
|
||||
ZSTD_OPTION,
|
||||
SET_MTIME_COMMAND_OPTION,
|
||||
SET_MTIME_FORMAT_OPTION,
|
||||
};
|
||||
|
||||
static char const doc[] = N_("\
|
||||
@@ -563,6 +565,11 @@ static struct argp_option options[] = {
|
||||
N_("set mtime for added files from DATE-OR-FILE"), GRID_FATTR },
|
||||
{"clamp-mtime", CLAMP_MTIME_OPTION, 0, 0,
|
||||
N_("only set time when the file is more recent than what was given with --mtime"), GRID_FATTR },
|
||||
{"set-mtime-command", SET_MTIME_COMMAND_OPTION, N_("COMMAND"), 0,
|
||||
N_("use output of the COMMAND to set mtime of the stored archive members"),
|
||||
GRID_FATTR },
|
||||
{"set-mtime-format", SET_MTIME_FORMAT_OPTION, N_("FORMAT"), 0,
|
||||
N_("set output format (in the sense of strptime(3)) of the --set-mtime-command command"), GRID_FATTR },
|
||||
{"mode", MODE_OPTION, N_("CHANGES"), 0,
|
||||
N_("force (symbolic) mode CHANGES for added files"), GRID_FATTR },
|
||||
{"atime-preserve", ATIME_PRESERVE_OPTION,
|
||||
@@ -1706,6 +1713,14 @@ parse_opt (int key, char *arg, struct argp_state *state)
|
||||
sparse_option = true;
|
||||
break;
|
||||
|
||||
case SET_MTIME_COMMAND_OPTION:
|
||||
set_mtime_command = arg;
|
||||
break;
|
||||
|
||||
case SET_MTIME_FORMAT_OPTION:
|
||||
set_mtime_format = arg;
|
||||
break;
|
||||
|
||||
case SPARSE_VERSION_OPTION:
|
||||
sparse_option = true;
|
||||
{
|
||||
@@ -2525,7 +2540,16 @@ decode_options (int argc, char **argv)
|
||||
USAGE_ERROR ((0, 0, _("Cannot concatenate compressed archives")));
|
||||
}
|
||||
|
||||
if (set_mtime_option == CLAMP_MTIME)
|
||||
if (set_mtime_command)
|
||||
{
|
||||
if (set_mtime_option != USE_FILE_MTIME)
|
||||
{
|
||||
USAGE_ERROR ((0, 0,
|
||||
_("--mtime conflicts with --set-mtime-command")));
|
||||
}
|
||||
set_mtime_option = COMMAND_MTIME;
|
||||
}
|
||||
else if (set_mtime_option == CLAMP_MTIME)
|
||||
{
|
||||
if (!TIME_OPTION_INITIALIZED (mtime_option))
|
||||
USAGE_ERROR ((0, 0,
|
||||
|
||||
Reference in New Issue
Block a user