mirror of
https://github.com/libarchive/libarchive.git
synced 2026-01-18 17:11:25 +01:00
bsdtar: Support --mtime and --clamp-mtime (#2601)
Hi, This PR adds support for setting a forced mtime on all written files (`--mtime` and `--clamp-mtime`) in bsdtar. The end goal will be to support all functionalities in <https://reproducible-builds.org/docs/archives/#full-example>, namely `--sort` and disabling other attributes (atime, ctime, etc.). Fixes #971. ## History - [v1](https://github.com/zhaofengli/libarchive/tree/forced-mtime-v1): Added `archive_read_disk_set_forced_mtime` in libarchive. As a result, it was only applied when reading from the filesystem and not from other archives. - [v2](https://github.com/zhaofengli/libarchive/tree/forced-mtime-v2): Refactored to apply the forced mtime in `archive_write`. - v3 (current): Reduced libarchive change to exposing `archive_parse_date`, moved clamping logic into bsdtar. --------- Signed-off-by: Zhaofeng Li <hello@zhaofeng.li> Co-authored-by: Dustin L. Howett <dustin@howett.net>
This commit is contained in:
2
COPYING
2
COPYING
@@ -20,7 +20,7 @@ the actual statements in the files are controlling.
|
||||
libarchive/mtree.5
|
||||
|
||||
* The following source files are in the public domain:
|
||||
libarchive/archive_getdate.c
|
||||
libarchive/archive_parse_date.c
|
||||
|
||||
* The following source files are triple-licensed with the ability to choose
|
||||
from CC0 1.0 Universal, OpenSSL or Apache 2.0 licenses:
|
||||
|
||||
@@ -118,8 +118,6 @@ libarchive_la_SOURCES= \
|
||||
libarchive/archive_entry_stat.c \
|
||||
libarchive/archive_entry_strmode.c \
|
||||
libarchive/archive_entry_xattr.c \
|
||||
libarchive/archive_getdate.c \
|
||||
libarchive/archive_getdate.h \
|
||||
libarchive/archive_hmac.c \
|
||||
libarchive/archive_hmac_private.h \
|
||||
libarchive/archive_match.c \
|
||||
@@ -129,6 +127,7 @@ libarchive_la_SOURCES= \
|
||||
libarchive/archive_options_private.h \
|
||||
libarchive/archive_pack_dev.h \
|
||||
libarchive/archive_pack_dev.c \
|
||||
libarchive/archive_parse_date.c \
|
||||
libarchive/archive_pathmatch.c \
|
||||
libarchive/archive_pathmatch.h \
|
||||
libarchive/archive_platform.h \
|
||||
@@ -379,10 +378,10 @@ libarchive_test_SOURCES= \
|
||||
libarchive/test/test_archive_clear_error.c \
|
||||
libarchive/test/test_archive_cmdline.c \
|
||||
libarchive/test/test_archive_digest.c \
|
||||
libarchive/test/test_archive_getdate.c \
|
||||
libarchive/test/test_archive_match_owner.c \
|
||||
libarchive/test/test_archive_match_path.c \
|
||||
libarchive/test/test_archive_match_time.c \
|
||||
libarchive/test/test_archive_parse_date.c \
|
||||
libarchive/test/test_archive_pathmatch.c \
|
||||
libarchive/test/test_archive_read.c \
|
||||
libarchive/test/test_archive_read_add_passphrase.c \
|
||||
@@ -1178,6 +1177,7 @@ bsdtar_test_SOURCES= \
|
||||
tar/test/test_option_lz4.c \
|
||||
tar/test/test_option_lzma.c \
|
||||
tar/test/test_option_lzop.c \
|
||||
tar/test/test_option_mtime.c \
|
||||
tar/test/test_option_n.c \
|
||||
tar/test/test_option_newer_than.c \
|
||||
tar/test/test_option_nodump.c \
|
||||
|
||||
@@ -37,11 +37,11 @@ libarchive_src_files := libarchive/archive_acl.c \
|
||||
libarchive/archive_entry_stat.c \
|
||||
libarchive/archive_entry_strmode.c \
|
||||
libarchive/archive_entry_xattr.c \
|
||||
libarchive/archive_getdate.c \
|
||||
libarchive/archive_hmac.c \
|
||||
libarchive/archive_match.c \
|
||||
libarchive/archive_options.c \
|
||||
libarchive/archive_pack_dev.c \
|
||||
libarchive/archive_parse_date.c \
|
||||
libarchive/archive_pathmatch.c \
|
||||
libarchive/archive_ppmd7.c \
|
||||
libarchive/archive_random.c \
|
||||
|
||||
@@ -38,8 +38,6 @@ SET(libarchive_SOURCES
|
||||
archive_entry_stat.c
|
||||
archive_entry_strmode.c
|
||||
archive_entry_xattr.c
|
||||
archive_getdate.c
|
||||
archive_getdate.h
|
||||
archive_hmac.c
|
||||
archive_hmac_private.h
|
||||
archive_match.c
|
||||
@@ -49,6 +47,7 @@ SET(libarchive_SOURCES
|
||||
archive_options_private.h
|
||||
archive_pack_dev.h
|
||||
archive_pack_dev.c
|
||||
archive_parse_date.c
|
||||
archive_pathmatch.c
|
||||
archive_pathmatch.h
|
||||
archive_platform.h
|
||||
|
||||
@@ -1128,6 +1128,10 @@ __LA_DECL int archive_compression(struct archive *)
|
||||
__LA_DEPRECATED;
|
||||
#endif
|
||||
|
||||
/* Parses a date string relative to the current time.
|
||||
* NOTE: This is not intended for general date parsing, and the resulting timestamp should only be used for libarchive. */
|
||||
__LA_DECL time_t archive_parse_date(time_t now, const char *datestr);
|
||||
|
||||
__LA_DECL int archive_errno(struct archive *);
|
||||
__LA_DECL const char *archive_error_string(struct archive *);
|
||||
__LA_DECL const char *archive_format_name(struct archive *);
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2015 Tim Kientzle
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef ARCHIVE_GETDATE_H_INCLUDED
|
||||
#define ARCHIVE_GETDATE_H_INCLUDED
|
||||
|
||||
#ifndef __LIBARCHIVE_BUILD
|
||||
#error This header is only to be used internally to libarchive.
|
||||
#endif
|
||||
|
||||
#include <time.h>
|
||||
|
||||
time_t __archive_get_date(time_t now, const char *);
|
||||
|
||||
#endif
|
||||
@@ -39,7 +39,6 @@
|
||||
#include "archive.h"
|
||||
#include "archive_private.h"
|
||||
#include "archive_entry.h"
|
||||
#include "archive_getdate.h"
|
||||
#include "archive_pathmatch.h"
|
||||
#include "archive_rb.h"
|
||||
#include "archive_string.h"
|
||||
@@ -188,7 +187,7 @@ static int time_excluded(struct archive_match *,
|
||||
struct archive_entry *);
|
||||
static int validate_time_flag(struct archive *, int, const char *);
|
||||
|
||||
#define get_date __archive_get_date
|
||||
#define get_date archive_parse_date
|
||||
|
||||
static const struct archive_rb_tree_ops rb_ops_mbs = {
|
||||
cmp_node_mbs, cmp_key_mbs
|
||||
|
||||
@@ -35,8 +35,7 @@
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#define __LIBARCHIVE_BUILD 1
|
||||
#include "archive_getdate.h"
|
||||
#include "archive.h"
|
||||
|
||||
/* Basic time units. */
|
||||
#define EPOCH 1970
|
||||
@@ -937,7 +936,7 @@ difftm (struct tm *a, struct tm *b)
|
||||
* TODO: tokens[] array should be dynamically sized.
|
||||
*/
|
||||
time_t
|
||||
__archive_get_date(time_t now, const char *p)
|
||||
archive_parse_date(time_t now, const char *p)
|
||||
{
|
||||
struct token tokens[256];
|
||||
struct gdstate _gds;
|
||||
@@ -21,10 +21,10 @@ IF(ENABLE_TEST)
|
||||
test_archive_clear_error.c
|
||||
test_archive_cmdline.c
|
||||
test_archive_digest.c
|
||||
test_archive_getdate.c
|
||||
test_archive_match_owner.c
|
||||
test_archive_match_path.c
|
||||
test_archive_match_time.c
|
||||
test_archive_parse_date.c
|
||||
test_archive_pathmatch.c
|
||||
test_archive_read.c
|
||||
test_archive_read_add_passphrase.c
|
||||
|
||||
@@ -25,8 +25,7 @@
|
||||
|
||||
#include "test.h"
|
||||
|
||||
#define __LIBARCHIVE_BUILD 1
|
||||
#include "archive_getdate.h"
|
||||
#define parse_date archive_parse_date
|
||||
|
||||
static void
|
||||
test_newer_time(void)
|
||||
@@ -102,27 +101,27 @@ test_newer_time_str(void)
|
||||
|
||||
/* Test1: Allow newer time. */
|
||||
archive_entry_copy_pathname(ae, "file1");
|
||||
t = __archive_get_date(now, "1980/2/1 0:0:0 UTC");
|
||||
t = parse_date(now, "1980/2/1 0:0:0 UTC");
|
||||
archive_entry_set_mtime(ae, t, 0);
|
||||
archive_entry_set_ctime(ae, t, 0);
|
||||
failure("Both Its mtime and ctime should be excluded");
|
||||
assertEqualInt(1, archive_match_time_excluded(m, ae));
|
||||
assertEqualInt(1, archive_match_excluded(m, ae));
|
||||
t = __archive_get_date(now, "1980/1/1 0:0:0 UTC");
|
||||
t = parse_date(now, "1980/1/1 0:0:0 UTC");
|
||||
archive_entry_set_mtime(ae, t, 0);
|
||||
archive_entry_set_ctime(ae, t, 0);
|
||||
failure("Both Its mtime and ctime should be excluded");
|
||||
assertEqualInt(1, archive_match_time_excluded(m, ae));
|
||||
assertEqualInt(1, archive_match_excluded(m, ae));
|
||||
|
||||
t = __archive_get_date(now, "1980/2/1 0:0:1 UTC");
|
||||
t = parse_date(now, "1980/2/1 0:0:1 UTC");
|
||||
archive_entry_set_mtime(ae, t, 0);
|
||||
archive_entry_set_ctime(ae, t, 0);
|
||||
failure("Both Its mtime and ctime should not be excluded");
|
||||
assertEqualInt(0, archive_match_time_excluded(m, ae));
|
||||
assertEqualInt(0, archive_match_excluded(m, ae));
|
||||
|
||||
t = __archive_get_date(now, "1980/2/1 0:0:0 UTC");
|
||||
t = parse_date(now, "1980/2/1 0:0:0 UTC");
|
||||
archive_entry_set_mtime(ae, t, 1);
|
||||
archive_entry_set_ctime(ae, t, 0);
|
||||
failure("Its mtime should be excluded");
|
||||
@@ -143,20 +142,20 @@ test_newer_time_str(void)
|
||||
"1980/2/1 0:0:0 UTC"));
|
||||
|
||||
archive_entry_copy_pathname(ae, "file1");
|
||||
t = __archive_get_date(now, "1980/2/1 0:0:0 UTC");
|
||||
t = parse_date(now, "1980/2/1 0:0:0 UTC");
|
||||
archive_entry_set_mtime(ae, t, 0);
|
||||
archive_entry_set_ctime(ae, t, 0);
|
||||
failure("Both Its mtime and ctime should not be excluded");
|
||||
assertEqualInt(0, archive_match_time_excluded(m, ae));
|
||||
assertEqualInt(0, archive_match_excluded(m, ae));
|
||||
t = __archive_get_date(now, "1980/1/1 0:0:0 UTC");
|
||||
t = parse_date(now, "1980/1/1 0:0:0 UTC");
|
||||
archive_entry_set_mtime(ae, t, 0);
|
||||
archive_entry_set_ctime(ae, t, 0);
|
||||
failure("Both Its mtime and ctime should be excluded");
|
||||
assertEqualInt(1, archive_match_time_excluded(m, ae));
|
||||
assertEqualInt(1, archive_match_excluded(m, ae));
|
||||
|
||||
t = __archive_get_date(now, "1980/2/1 0:0:1 UTC");
|
||||
t = parse_date(now, "1980/2/1 0:0:1 UTC");
|
||||
archive_entry_set_mtime(ae, t, 0);
|
||||
archive_entry_set_ctime(ae, t, 0);
|
||||
failure("Both Its mtime and ctime should not be excluded");
|
||||
@@ -190,27 +189,27 @@ test_newer_time_str_w(void)
|
||||
|
||||
/* Test1: Allow newer time. */
|
||||
archive_entry_copy_pathname(ae, "file1");
|
||||
t = __archive_get_date(now, "1980/2/1 0:0:0 UTC");
|
||||
t = parse_date(now, "1980/2/1 0:0:0 UTC");
|
||||
archive_entry_set_mtime(ae, t, 0);
|
||||
archive_entry_set_ctime(ae, t, 0);
|
||||
failure("Both Its mtime and ctime should be excluded");
|
||||
assertEqualInt(1, archive_match_time_excluded(m, ae));
|
||||
assertEqualInt(1, archive_match_excluded(m, ae));
|
||||
t = __archive_get_date(now, "1980/1/1 0:0:0 UTC");
|
||||
t = parse_date(now, "1980/1/1 0:0:0 UTC");
|
||||
archive_entry_set_mtime(ae, t, 0);
|
||||
archive_entry_set_ctime(ae, t, 0);
|
||||
failure("Both Its mtime and ctime should be excluded");
|
||||
assertEqualInt(1, archive_match_time_excluded(m, ae));
|
||||
assertEqualInt(1, archive_match_excluded(m, ae));
|
||||
|
||||
t = __archive_get_date(now, "1980/2/1 0:0:1 UTC");
|
||||
t = parse_date(now, "1980/2/1 0:0:1 UTC");
|
||||
archive_entry_set_mtime(ae, t, 0);
|
||||
archive_entry_set_ctime(ae, t, 0);
|
||||
failure("Both Its mtime and ctime should not be excluded");
|
||||
assertEqualInt(0, archive_match_time_excluded(m, ae));
|
||||
assertEqualInt(0, archive_match_excluded(m, ae));
|
||||
|
||||
t = __archive_get_date(now, "1980/2/1 0:0:0 UTC");
|
||||
t = parse_date(now, "1980/2/1 0:0:0 UTC");
|
||||
archive_entry_set_mtime(ae, t, 1);
|
||||
archive_entry_set_ctime(ae, t, 0);
|
||||
failure("Its mtime should be excluded");
|
||||
@@ -231,20 +230,20 @@ test_newer_time_str_w(void)
|
||||
L"1980/2/1 0:0:0 UTC"));
|
||||
|
||||
archive_entry_copy_pathname(ae, "file1");
|
||||
t = __archive_get_date(now, "1980/2/1 0:0:0 UTC");
|
||||
t = parse_date(now, "1980/2/1 0:0:0 UTC");
|
||||
archive_entry_set_mtime(ae, t, 0);
|
||||
archive_entry_set_ctime(ae, t, 0);
|
||||
failure("Both Its mtime and ctime should not be excluded");
|
||||
assertEqualInt(0, archive_match_time_excluded(m, ae));
|
||||
assertEqualInt(0, archive_match_excluded(m, ae));
|
||||
t = __archive_get_date(now, "1980/1/1 0:0:0 UTC");
|
||||
t = parse_date(now, "1980/1/1 0:0:0 UTC");
|
||||
archive_entry_set_mtime(ae, t, 0);
|
||||
archive_entry_set_ctime(ae, t, 0);
|
||||
failure("Both Its mtime and ctime should be excluded");
|
||||
assertEqualInt(1, archive_match_time_excluded(m, ae));
|
||||
assertEqualInt(1, archive_match_excluded(m, ae));
|
||||
|
||||
t = __archive_get_date(now, "1980/2/1 0:0:1 UTC");
|
||||
t = parse_date(now, "1980/2/1 0:0:1 UTC");
|
||||
archive_entry_set_mtime(ae, t, 0);
|
||||
archive_entry_set_ctime(ae, t, 0);
|
||||
failure("Both Its mtime and ctime should not be excluded");
|
||||
@@ -569,37 +568,37 @@ test_older_time_str(void)
|
||||
ARCHIVE_MATCH_OLDER, "1980/2/1 0:0:0 UTC"));
|
||||
|
||||
archive_entry_copy_pathname(ae, "file1");
|
||||
t = __archive_get_date(now, "1980/2/1 0:0:0 UTC");
|
||||
t = parse_date(now, "1980/2/1 0:0:0 UTC");
|
||||
archive_entry_set_mtime(ae, t, 0);
|
||||
archive_entry_set_ctime(ae, t, 0);
|
||||
failure("Both Its mtime and ctime should be excluded");
|
||||
assertEqualInt(1, archive_match_time_excluded(m, ae));
|
||||
assertEqualInt(1, archive_match_excluded(m, ae));
|
||||
t = __archive_get_date(now, "1980/1/1 0:0:0 UTC");
|
||||
t = parse_date(now, "1980/1/1 0:0:0 UTC");
|
||||
archive_entry_set_mtime(ae, t, 0);
|
||||
archive_entry_set_ctime(ae, t, 0);
|
||||
failure("Both Its mtime and ctime should not be excluded");
|
||||
assertEqualInt(0, archive_match_time_excluded(m, ae));
|
||||
assertEqualInt(0, archive_match_excluded(m, ae));
|
||||
|
||||
t = __archive_get_date(now, "1980/3/1 0:0:0 UTC");
|
||||
t = parse_date(now, "1980/3/1 0:0:0 UTC");
|
||||
archive_entry_set_mtime(ae, t, 0);
|
||||
archive_entry_set_ctime(ae, t, 0);
|
||||
failure("Both Its mtime and ctime should be excluded");
|
||||
assertEqualInt(1, archive_match_time_excluded(m, ae));
|
||||
assertEqualInt(1, archive_match_excluded(m, ae));
|
||||
|
||||
t = __archive_get_date(now, "1980/3/1 0:0:0 UTC");
|
||||
t = parse_date(now, "1980/3/1 0:0:0 UTC");
|
||||
archive_entry_set_mtime(ae, t, 0);
|
||||
t = __archive_get_date(now, "1980/1/1 0:0:0 UTC");
|
||||
t = parse_date(now, "1980/1/1 0:0:0 UTC");
|
||||
archive_entry_set_ctime(ae, t, 0);
|
||||
failure("Its mtime should be excluded");
|
||||
assertEqualInt(1, archive_match_time_excluded(m, ae));
|
||||
assertEqualInt(1, archive_match_excluded(m, ae));
|
||||
|
||||
t = __archive_get_date(now, "1980/1/1 0:0:0 UTC");
|
||||
t = parse_date(now, "1980/1/1 0:0:0 UTC");
|
||||
archive_entry_set_mtime(ae, t, 0);
|
||||
t = __archive_get_date(now, "1980/3/1 0:0:0 UTC");
|
||||
t = parse_date(now, "1980/3/1 0:0:0 UTC");
|
||||
archive_entry_set_ctime(ae, t, 0);
|
||||
failure("Its ctime should be excluded");
|
||||
assertEqualInt(1, archive_match_time_excluded(m, ae));
|
||||
@@ -612,20 +611,20 @@ test_older_time_str(void)
|
||||
"1980/2/1 0:0:0 UTC"));
|
||||
|
||||
archive_entry_copy_pathname(ae, "file1");
|
||||
t = __archive_get_date(now, "1980/2/1 0:0:0 UTC");
|
||||
t = parse_date(now, "1980/2/1 0:0:0 UTC");
|
||||
archive_entry_set_mtime(ae, t, 0);
|
||||
archive_entry_set_ctime(ae, t, 0);
|
||||
failure("Both Its mtime and ctime should not be excluded");
|
||||
assertEqualInt(0, archive_match_time_excluded(m, ae));
|
||||
assertEqualInt(0, archive_match_excluded(m, ae));
|
||||
t = __archive_get_date(now, "1980/1/1 0:0:0 UTC");
|
||||
t = parse_date(now, "1980/1/1 0:0:0 UTC");
|
||||
archive_entry_set_mtime(ae, t, 0);
|
||||
archive_entry_set_ctime(ae, t, 0);
|
||||
failure("Both Its mtime and ctime should not be excluded");
|
||||
assertEqualInt(0, archive_match_time_excluded(m, ae));
|
||||
assertEqualInt(0, archive_match_excluded(m, ae));
|
||||
|
||||
t = __archive_get_date(now, "1980/3/1 0:0:0 UTC");
|
||||
t = parse_date(now, "1980/3/1 0:0:0 UTC");
|
||||
archive_entry_set_mtime(ae, t, 0);
|
||||
archive_entry_set_ctime(ae, t, 0);
|
||||
failure("Both Its mtime and ctime should be excluded");
|
||||
@@ -659,37 +658,37 @@ test_older_time_str_w(void)
|
||||
ARCHIVE_MATCH_OLDER, L"1980/2/1 0:0:0 UTC"));
|
||||
|
||||
archive_entry_copy_pathname(ae, "file1");
|
||||
t = __archive_get_date(now, "1980/2/1 0:0:0 UTC");
|
||||
t = parse_date(now, "1980/2/1 0:0:0 UTC");
|
||||
archive_entry_set_mtime(ae, t, 0);
|
||||
archive_entry_set_ctime(ae, t, 0);
|
||||
failure("Both Its mtime and ctime should be excluded");
|
||||
assertEqualInt(1, archive_match_time_excluded(m, ae));
|
||||
assertEqualInt(1, archive_match_excluded(m, ae));
|
||||
t = __archive_get_date(now, "1980/1/1 0:0:0 UTC");
|
||||
t = parse_date(now, "1980/1/1 0:0:0 UTC");
|
||||
archive_entry_set_mtime(ae, t, 0);
|
||||
archive_entry_set_ctime(ae, t, 0);
|
||||
failure("Both Its mtime and ctime should not be excluded");
|
||||
assertEqualInt(0, archive_match_time_excluded(m, ae));
|
||||
assertEqualInt(0, archive_match_excluded(m, ae));
|
||||
|
||||
t = __archive_get_date(now, "1980/3/1 0:0:0 UTC");
|
||||
t = parse_date(now, "1980/3/1 0:0:0 UTC");
|
||||
archive_entry_set_mtime(ae, t, 0);
|
||||
archive_entry_set_ctime(ae, t, 0);
|
||||
failure("Both Its mtime and ctime should be excluded");
|
||||
assertEqualInt(1, archive_match_time_excluded(m, ae));
|
||||
assertEqualInt(1, archive_match_excluded(m, ae));
|
||||
|
||||
t = __archive_get_date(now, "1980/3/1 0:0:0 UTC");
|
||||
t = parse_date(now, "1980/3/1 0:0:0 UTC");
|
||||
archive_entry_set_mtime(ae, t, 0);
|
||||
t = __archive_get_date(now, "1980/1/1 0:0:0 UTC");
|
||||
t = parse_date(now, "1980/1/1 0:0:0 UTC");
|
||||
archive_entry_set_ctime(ae, t, 0);
|
||||
failure("Its mtime should be excluded");
|
||||
assertEqualInt(1, archive_match_time_excluded(m, ae));
|
||||
assertEqualInt(1, archive_match_excluded(m, ae));
|
||||
|
||||
t = __archive_get_date(now, "1980/1/1 0:0:0 UTC");
|
||||
t = parse_date(now, "1980/1/1 0:0:0 UTC");
|
||||
archive_entry_set_mtime(ae, t, 0);
|
||||
t = __archive_get_date(now, "1980/3/1 0:0:0 UTC");
|
||||
t = parse_date(now, "1980/3/1 0:0:0 UTC");
|
||||
archive_entry_set_ctime(ae, t, 0);
|
||||
failure("Its ctime should be excluded");
|
||||
assertEqualInt(1, archive_match_time_excluded(m, ae));
|
||||
@@ -702,20 +701,20 @@ test_older_time_str_w(void)
|
||||
L"1980/2/1 0:0:0 UTC"));
|
||||
|
||||
archive_entry_copy_pathname(ae, "file1");
|
||||
t = __archive_get_date(now, "1980/2/1 0:0:0 UTC");
|
||||
t = parse_date(now, "1980/2/1 0:0:0 UTC");
|
||||
archive_entry_set_mtime(ae, t, 0);
|
||||
archive_entry_set_ctime(ae, t, 0);
|
||||
failure("Both Its mtime and ctime should not be excluded");
|
||||
assertEqualInt(0, archive_match_time_excluded(m, ae));
|
||||
assertEqualInt(0, archive_match_excluded(m, ae));
|
||||
t = __archive_get_date(now, "1980/1/1 0:0:0 UTC");
|
||||
t = parse_date(now, "1980/1/1 0:0:0 UTC");
|
||||
archive_entry_set_mtime(ae, t, 0);
|
||||
archive_entry_set_ctime(ae, t, 0);
|
||||
failure("Both Its mtime and ctime should not be excluded");
|
||||
assertEqualInt(0, archive_match_time_excluded(m, ae));
|
||||
assertEqualInt(0, archive_match_excluded(m, ae));
|
||||
|
||||
t = __archive_get_date(now, "1980/3/1 0:0:0 UTC");
|
||||
t = parse_date(now, "1980/3/1 0:0:0 UTC");
|
||||
archive_entry_set_mtime(ae, t, 0);
|
||||
archive_entry_set_ctime(ae, t, 0);
|
||||
failure("Both Its mtime and ctime should be excluded");
|
||||
|
||||
@@ -26,16 +26,13 @@
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#define __LIBARCHIVE_BUILD 1
|
||||
#include "archive_getdate.h"
|
||||
|
||||
/*
|
||||
* Verify that the getdate() function works.
|
||||
* Verify that the archive_parse_date() function works.
|
||||
*/
|
||||
|
||||
#define get_date __archive_get_date
|
||||
#define get_date archive_parse_date
|
||||
|
||||
DEFINE_TEST(test_archive_getdate)
|
||||
DEFINE_TEST(test_archive_parse_date)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
|
||||
@@ -196,6 +196,11 @@ but before extracting entries from the archive.
|
||||
to the current directory after processing any
|
||||
.Fl C
|
||||
options and before extracting any files.
|
||||
.It Fl Fl clamp-mtime
|
||||
(use with
|
||||
.Fl Fl mtime )
|
||||
Only set the modification time if the file is newer than the date specified in
|
||||
.Fl Fl mtime .
|
||||
.It Fl Fl clear-nochange-fflags
|
||||
(x mode only)
|
||||
Before removing file system objects to replace them, clear platform-specific
|
||||
@@ -416,6 +421,9 @@ is run in x mode as root.
|
||||
Currently supported only for pax formats
|
||||
.Po including "pax restricted", the default tar format for
|
||||
.Nm bsdtar Pc
|
||||
.It Fl Fl mtime Ar date
|
||||
(c, r, u modes only)
|
||||
Set the modification times of added files to the specified date.
|
||||
.It Fl n , Fl Fl norecurse , Fl Fl no-recursion
|
||||
Do not operate recursively on the content of directories.
|
||||
.It Fl Fl newer Ar date
|
||||
|
||||
18
tar/bsdtar.c
18
tar/bsdtar.c
@@ -146,6 +146,7 @@ main(int argc, char **argv)
|
||||
char possible_help_request;
|
||||
char buff[16];
|
||||
long l;
|
||||
time_t now;
|
||||
|
||||
/*
|
||||
* Use a pointer for consistency, but stack-allocated storage
|
||||
@@ -160,6 +161,7 @@ main(int argc, char **argv)
|
||||
compression = compression2 = '\0';
|
||||
compression_name = compression2_name = NULL;
|
||||
compress_program = NULL;
|
||||
time(&now);
|
||||
|
||||
#if defined(HAVE_SIGACTION)
|
||||
{ /* Set up signal handling. */
|
||||
@@ -676,6 +678,16 @@ main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OPTION_MTIME: /* GNU tar */
|
||||
bsdtar->has_mtime = 1;
|
||||
bsdtar->mtime = archive_parse_date(now, bsdtar->argument);
|
||||
if (bsdtar->mtime == (time_t)-1) {
|
||||
lafe_errc(1, 0, "Invalid argument to --mtime (bad date string)");
|
||||
}
|
||||
break;
|
||||
case OPTION_CLAMP_MTIME: /* GNU tar */
|
||||
bsdtar->clamp_mtime = 1;
|
||||
break;
|
||||
#if 0
|
||||
/*
|
||||
* The common BSD -P option is not necessary, since
|
||||
@@ -962,6 +974,10 @@ main(int argc, char **argv)
|
||||
only_mode(bsdtar, buff, "cru");
|
||||
}
|
||||
|
||||
if (!bsdtar->has_mtime && bsdtar->clamp_mtime)
|
||||
lafe_errc(1, 0,
|
||||
"--clamp-mtime is not valid without --mtime <date>");
|
||||
|
||||
/*
|
||||
* When creating an archive from a directory tree, the directory
|
||||
* walking code will already avoid entering directories when
|
||||
@@ -1066,6 +1082,8 @@ static const char *long_help_msg =
|
||||
" -z, -j, -J, --lzma Compress archive with gzip/bzip2/xz/lzma\n"
|
||||
" --format {ustar|pax|cpio|shar} Select archive format\n"
|
||||
" --exclude <pattern> Skip files that match pattern\n"
|
||||
" --mtime <date> Set modification times for added files\n"
|
||||
" --clamp-mtime Only set modification times for files newer than --mtime\n"
|
||||
" -C <dir> Change to <dir> before processing remaining files\n"
|
||||
" @<archive> Add entries from <archive> to output\n"
|
||||
"List: %p -t [options] [<patterns>]\n"
|
||||
|
||||
@@ -46,6 +46,9 @@ struct bsdtar {
|
||||
char symlink_mode; /* H or L, per BSD conventions */
|
||||
const char *option_options; /* --options */
|
||||
char day_first; /* show day before month in -tv output */
|
||||
char has_mtime; /* --mtime exists (0 or 1) */
|
||||
char clamp_mtime; /* --clamp-mtime (0 or 1)*/
|
||||
time_t mtime; /* --mtime */
|
||||
struct creation_set *cset;
|
||||
|
||||
/* Option parser state */
|
||||
@@ -175,11 +178,14 @@ enum {
|
||||
OPTION_VERSION,
|
||||
OPTION_XATTRS,
|
||||
OPTION_ZSTD,
|
||||
OPTION_MTIME,
|
||||
OPTION_CLAMP_MTIME,
|
||||
};
|
||||
|
||||
int bsdtar_getopt(struct bsdtar *);
|
||||
void do_chdir(struct bsdtar *);
|
||||
int edit_pathname(struct bsdtar *, struct archive_entry *);
|
||||
void edit_mtime(struct bsdtar *, struct archive_entry *);
|
||||
int need_report(void);
|
||||
int pathcmp(const char *a, const char *b);
|
||||
void safe_fprintf(FILE * restrict, const char * restrict fmt, ...) __LA_PRINTF(2, 3);
|
||||
|
||||
@@ -57,6 +57,7 @@ static const struct bsdtar_option {
|
||||
{ "cd", 1, 'C' },
|
||||
{ "check-links", 0, OPTION_CHECK_LINKS },
|
||||
{ "chroot", 0, OPTION_CHROOT },
|
||||
{ "clamp-mtime", 0, OPTION_CLAMP_MTIME },
|
||||
{ "clear-nochange-fflags", 0, OPTION_CLEAR_NOCHANGE_FFLAGS },
|
||||
{ "compress", 0, 'Z' },
|
||||
{ "confirmation", 0, 'w' },
|
||||
@@ -95,6 +96,7 @@ static const struct bsdtar_option {
|
||||
{ "lzop", 0, OPTION_LZOP },
|
||||
{ "mac-metadata", 0, OPTION_MAC_METADATA },
|
||||
{ "modification-time", 0, 'm' },
|
||||
{ "mtime", 1, OPTION_MTIME },
|
||||
{ "newer", 1, OPTION_NEWER_CTIME },
|
||||
{ "newer-ctime", 1, OPTION_NEWER_CTIME },
|
||||
{ "newer-ctime-than", 1, OPTION_NEWER_CTIME_THAN },
|
||||
|
||||
@@ -58,6 +58,7 @@ IF(ENABLE_TAR AND ENABLE_TEST)
|
||||
test_option_lz4.c
|
||||
test_option_lzma.c
|
||||
test_option_lzop.c
|
||||
test_option_mtime.c
|
||||
test_option_n.c
|
||||
test_option_newer_than.c
|
||||
test_option_nodump.c
|
||||
|
||||
82
tar/test/test_option_mtime.c
Normal file
82
tar/test/test_option_mtime.c
Normal file
@@ -0,0 +1,82 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2025 Zhaofeng Li
|
||||
* All rights reserved.
|
||||
*/
|
||||
#include "test.h"
|
||||
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
/* system() on Windows runs its arguments through CMD.EXE, which has
|
||||
* notoriously unfriendly quoting rules. The current best documented way around
|
||||
* them is to wrap your *entire commandline* in sacrificial quotes.
|
||||
*
|
||||
* See CMD.EXE /? for more information. Excerpted here:
|
||||
* | Otherwise, old behavior is to see if the first character is
|
||||
* | a quote character and if so, strip the leading character and
|
||||
* | remove the last quote character on the command line, preserving
|
||||
* | any text after the last quote character.
|
||||
*
|
||||
* Since this test makes heavy use of systemf() with quoted arguments inside
|
||||
* the commandline, this macro is unfortunately an easier workaround.
|
||||
*/
|
||||
#define systemf(command, ...) systemf("\"" command "\"", __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
DEFINE_TEST(test_option_mtime)
|
||||
{
|
||||
/* Create three files with different mtimes. */
|
||||
assertMakeDir("in", 0755);
|
||||
assertChdir("in");
|
||||
assertMakeFile("new_mtime", 0666, "new");
|
||||
assertUtimes("new_mtime", 100000, 0, 100000, 0);
|
||||
assertMakeFile("mid_mtime", 0666, "mid");
|
||||
assertUtimes("mid_mtime", 10000, 0, 10000, 0);
|
||||
assertMakeFile("old_mtime", 0666, "old");
|
||||
// assertion_utimes silently discards 0 :(
|
||||
assertUtimes("old_mtime", 1, 0, 1, 0);
|
||||
|
||||
/* Archive with --mtime 86400 */
|
||||
assertEqualInt(0,
|
||||
systemf("%s --format pax -cf ../noclamp.tar "
|
||||
"--mtime \"1970/1/2 0:0:0 UTC\" .",
|
||||
testprog));
|
||||
assertChdir("..");
|
||||
|
||||
assertMakeDir("out.noclamp", 0755);
|
||||
assertChdir("out.noclamp");
|
||||
assertEqualInt(0, systemf("%s xf ../noclamp.tar", testprog));
|
||||
assertFileMtime("new_mtime", 86400, 0);
|
||||
assertFileMtime("mid_mtime", 86400, 0);
|
||||
assertFileMtime("old_mtime", 86400, 0);
|
||||
assertChdir("..");
|
||||
|
||||
/* Archive with --mtime 86400 --clamp-mtime */
|
||||
assertChdir("in");
|
||||
assertEqualInt(0,
|
||||
systemf("%s --format pax -cf ../clamp.tar "
|
||||
"--mtime \"1970/1/2 0:0:0 UTC\" --clamp-mtime .",
|
||||
testprog));
|
||||
assertChdir("..");
|
||||
|
||||
assertMakeDir("out.clamp", 0755);
|
||||
assertChdir("out.clamp");
|
||||
assertEqualInt(0, systemf("%s xf ../clamp.tar", testprog));
|
||||
assertFileMtime("new_mtime", 86400, 0);
|
||||
assertFileMtime("mid_mtime", 10000, 0);
|
||||
assertFileMtime("old_mtime", 1, 0);
|
||||
assertChdir("..");
|
||||
|
||||
/* Archive-to-archive copy with --mtime 0 */
|
||||
assertEqualInt(0,
|
||||
systemf("%s --format pax -cf ./archive2archive.tar "
|
||||
"--mtime \"1970/1/1 0:0:0 UTC\" @noclamp.tar",
|
||||
testprog));
|
||||
assertMakeDir("out.archive2archive", 0755);
|
||||
assertChdir("out.archive2archive");
|
||||
assertEqualInt(0, systemf("%s xf ../archive2archive.tar", testprog));
|
||||
assertFileMtime("new_mtime", 0, 0);
|
||||
assertFileMtime("mid_mtime", 0, 0);
|
||||
assertFileMtime("old_mtime", 0, 0);
|
||||
assertChdir("..");
|
||||
}
|
||||
14
tar/util.c
14
tar/util.c
@@ -562,6 +562,20 @@ edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Apply --mtime and --clamp-mtime options.
|
||||
*/
|
||||
void
|
||||
edit_mtime(struct bsdtar *bsdtar, struct archive_entry *entry)
|
||||
{
|
||||
if (!bsdtar->has_mtime)
|
||||
return;
|
||||
|
||||
__LA_TIME_T entry_mtime = archive_entry_mtime(entry);
|
||||
if (!bsdtar->clamp_mtime || entry_mtime > bsdtar->mtime)
|
||||
archive_entry_set_mtime(entry, bsdtar->mtime, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* It would be nice to just use printf() for formatting large numbers,
|
||||
* but the compatibility problems are quite a headache. Hence the
|
||||
|
||||
@@ -682,6 +682,7 @@ append_archive(struct bsdtar *bsdtar, struct archive *a, struct archive *ina)
|
||||
if ((bsdtar->flags & OPTFLAG_INTERACTIVE) &&
|
||||
!yes("copy '%s'", archive_entry_pathname(in_entry)))
|
||||
continue;
|
||||
edit_mtime(bsdtar, in_entry);
|
||||
if (bsdtar->verbose > 1) {
|
||||
safe_fprintf(stderr, "a ");
|
||||
list_item_verbose(bsdtar, stderr, in_entry);
|
||||
@@ -908,6 +909,9 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path)
|
||||
if (edit_pathname(bsdtar, entry))
|
||||
continue;
|
||||
|
||||
/* Rewrite the mtime. */
|
||||
edit_mtime(bsdtar, entry);
|
||||
|
||||
/* Display entry as we process it. */
|
||||
if (bsdtar->verbose > 1) {
|
||||
safe_fprintf(stderr, "a ");
|
||||
|
||||
Reference in New Issue
Block a user