diff options
Diffstat (limited to 'contrib/libarchive/tar')
| -rw-r--r-- | contrib/libarchive/tar/bsdtar.1 | 74 | ||||
| -rw-r--r-- | contrib/libarchive/tar/bsdtar.c | 35 | ||||
| -rw-r--r-- | contrib/libarchive/tar/bsdtar.h | 12 | ||||
| -rw-r--r-- | contrib/libarchive/tar/cmdline.c | 2 | ||||
| -rw-r--r-- | contrib/libarchive/tar/subst.c | 1 | ||||
| -rw-r--r-- | contrib/libarchive/tar/test/test_list_item.c | 63 | ||||
| -rw-r--r-- | contrib/libarchive/tar/test/test_list_item.tar.uu | 169 | ||||
| -rw-r--r-- | contrib/libarchive/tar/test/test_option_C_mtree.c | 4 | ||||
| -rw-r--r-- | contrib/libarchive/tar/test/test_option_P_upper.c (renamed from contrib/libarchive/tar/test/test_option_P.c) | 0 | ||||
| -rw-r--r-- | contrib/libarchive/tar/test/test_option_ignore_zeros.c | 4 | ||||
| -rw-r--r-- | contrib/libarchive/tar/test/test_option_mtime.c | 82 | ||||
| -rw-r--r-- | contrib/libarchive/tar/test/test_option_s.c | 14 | ||||
| -rw-r--r-- | contrib/libarchive/tar/test/test_stdio.c | 48 | ||||
| -rw-r--r-- | contrib/libarchive/tar/util.c | 57 | ||||
| -rw-r--r-- | contrib/libarchive/tar/write.c | 4 |
15 files changed, 520 insertions, 49 deletions
diff --git a/contrib/libarchive/tar/bsdtar.1 b/contrib/libarchive/tar/bsdtar.1 index 7a7b0d5d9fb1..0062789e27e9 100644 --- a/contrib/libarchive/tar/bsdtar.1 +++ b/contrib/libarchive/tar/bsdtar.1 @@ -32,19 +32,22 @@ .Nm creates and manipulates streaming archive files. This implementation can extract from tar, pax, cpio, zip, jar, ar, xar, -rpm, 7-zip, and ISO 9660 cdrom images and can create tar, pax, cpio, ar, zip, -7-zip, and shar archives. +rar, rpm, 7-zip, and ISO 9660 cdrom images and can create tar, pax, +cpio, ar, zip, 7-zip, and shar archives. .Pp The first synopsis form shows a .Dq bundled option word. This usage is provided for compatibility with historical implementations. -See COMPATIBILITY below for details. +See +.Sx COMPATIBILITY +below for details. .Pp The other synopsis forms show the preferred usage. The first option to .Nm is a mode indicator from the following list: +.Pp .Bl -tag -compact -width indent .It Fl c Create a new archive containing the specified items. @@ -109,16 +112,22 @@ all operating modes. The specified archive is opened and the entries in it will be appended to the current archive. As a simple example, +.Pp .Dl Nm Fl c Fl f Pa - Pa newfile Cm @ Ns Pa original.tar +.Pp writes a new archive to standard output containing a file .Pa newfile and all of the entries from .Pa original.tar . In contrast, +.Pp .Dl Nm Fl c Fl f Pa - Pa newfile Pa original.tar +.Pp creates a new archive with only two entries. Similarly, +.Pp .Dl Nm Fl czf Pa - Fl Fl format Cm pax Cm @ Ns Pa - +.Pp reads an archive from standard input (whose format will be determined automatically) and converts it into a gzip-compressed pax-format archive on stdout. @@ -130,19 +139,29 @@ can be used to convert archives from one format to another. Use the archive suffix to decide a set of the format and the compressions. As a simple example, +.Pp .Dl Nm Fl a Fl cf Pa archive.tgz source.c source.h +.Pp creates a new archive with restricted pax format and gzip compression, +.Pp .Dl Nm Fl a Fl cf Pa archive.tar.bz2.uu source.c source.h +.Pp creates a new archive with restricted pax format and bzip2 compression and uuencode compression, +.Pp .Dl Nm Fl a Fl cf Pa archive.zip source.c source.h +.Pp creates a new archive with zip format, +.Pp .Dl Nm Fl a Fl jcf Pa archive.tgz source.c source.h +.Pp ignores the .Dq -j option, and creates a new archive with restricted pax format and gzip compression, +.Pp .Dl Nm Fl a Fl jcf Pa archive.xxx source.c source.h +.Pp if it is unknown suffix or no suffix, creates a new archive with restricted pax format and bzip2 compression. .It Fl Fl acls @@ -177,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 @@ -295,7 +319,9 @@ The .Fl Fl include option is especially useful when filtering archives. For example, the command +.Pp .Dl Nm Fl c Fl f Pa new.tar Fl Fl include='*foo*' Cm @ Ns Pa old.tgz +.Pp creates a new archive .Pa new.tar containing only the entries from @@ -393,7 +419,11 @@ and the default behavior in c, r, and u modes or if .Nm is run in x mode as root. Currently supported only for pax formats -(including "pax restricted", the default tar format for bsdtar.) +.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 @@ -450,7 +480,7 @@ and the default behavior if .Nm is run as non-root in x mode. .It Fl Fl no-mac-metadata -(x mode only) +(c, r, u and x mode only) Mac OS X specific. Do not archive or extract ACLs and extended file attributes using @@ -556,6 +586,7 @@ keywords and values. These are passed to the modules that handle particular formats to control how those formats will behave. Each option has one of the following forms: +.Pp .Bl -tag -compact -width indent .It Ar key=value The key will be set to the specified value in every module that supports it. @@ -579,6 +610,7 @@ and for extract and list modes in .Xr archive_read_set_options 3 . .Pp Examples of supported options: +.Pp .Bl -tag -compact -width indent .It Cm iso9660:joliet Support Joliet extensions. @@ -738,6 +770,7 @@ This option is comparable to the .Fl i , Fl Fl ignore-zeros option of GNU tar. .El +.Pp If a provided option is not supported by any module, that is a fatal error. .It Fl P , Fl Fl absolute-paths @@ -910,7 +943,9 @@ This flag also causes .Nm to remove intervening directory symlinks instead of reporting an error. -See the SECURITY section below for more details. +See the +.Sx SECURITY +section below for more details. .It Fl Fl uid Ar id Use the provided user id number and ignore the user name from the archive. @@ -1048,24 +1083,31 @@ that contains two files .Ar source.c and .Ar source.h : +.Pp .Dl Nm Fl czf Pa file.tar.gz Pa source.c Pa source.h .Pp To view a detailed table of contents for this archive: +.Pp .Dl Nm Fl tvf Pa file.tar.gz .Pp To extract all entries from the archive on the default tape drive: +.Pp .Dl Nm Fl x .Pp To examine the contents of an ISO 9660 cdrom image: +.Pp .Dl Nm Fl tf Pa image.iso .Pp To move file hierarchies, invoke .Nm as +.Pp .Dl Nm Fl cf Pa - Fl C Pa srcdir \&. | Nm Fl xpf Pa - Fl C Pa destdir +.Pp or more traditionally +.Pp .Dl cd srcdir \&; Nm Fl cf Pa - \&. | ( cd destdir \&; Nm Fl xpf Pa - ) .Pp In create mode, the list of files and directories to be archived @@ -1074,7 +1116,9 @@ can also include directory change instructions of the form and archive inclusions of the form .Cm @ Ns Pa archive-file . For example, the command line +.Pp .Dl Nm Fl c Fl f Pa new.tar Pa foo1 Cm @ Ns Pa old.tgz Cm -C Ns Pa /tmp Pa foo2 +.Pp will create a new archive .Pa new.tar . .Nm @@ -1122,9 +1166,13 @@ For example, you can generate mtree output which only contains and .Cm uid keywords: +.Pp .Dl Nm Fl cf Pa file.tar Fl Fl format=mtree Fl Fl options='!all,type,time,uid' Pa dir +.Pp or you can set the compression level used by gzip or xz compression: +.Pp .Dl Nm Fl czf Pa file.tar Fl Fl options='compression-level=9' . +.Pp For more details, see the explanation of the .Fn archive_read_set_options and @@ -1142,7 +1190,9 @@ Arguments follow as separate words. The order of the arguments must match the order of the corresponding characters in the bundled command word. For example, +.Pp .Dl Nm Cm tbf 32 Pa file.tar +.Pp specifies three flags .Cm t , .Cm b , @@ -1240,10 +1290,13 @@ is specified, .Nm will refuse to extract the entry. .El +.Pp To protect yourself, you should be wary of any archives that come from untrusted sources. You should examine the contents of an archive with +.Pp .Dl Nm Fl tf Pa filename +.Pp before extraction. You should use the .Fl k @@ -1294,7 +1347,7 @@ command appeared in Seventh Edition Unix, which was released in January, 1979. There have been numerous other implementations, many of which extended the file format. John Gilmore's -.Nm pdtar +.Sy pdtar public-domain implementation (circa November, 1987) was quite influential, and formed the basis of GNU tar. GNU tar was included as the standard system tar @@ -1347,8 +1400,11 @@ although they still extract it correctly. The compression and decompression is implemented internally, so there may be insignificant differences between the compressed output generated by +.Pp .Dl Nm Fl czf Pa - file +.Pp and that generated by +.Pp .Dl Nm Fl cf Pa - file | Nm gzip .Pp The default should be to read and write archives to the standard I/O paths, @@ -1393,5 +1449,5 @@ There is not yet any support for multi-volume archives. Converting between dissimilar archive formats (such as tar and cpio) using the .Cm @ Ns Pa - convention can cause hard link information to be lost. -(This is a consequence of the incompatible ways that different archive -formats store hardlink information.) +This is a consequence of the incompatible ways that different archive +formats store hardlink information. diff --git a/contrib/libarchive/tar/bsdtar.c b/contrib/libarchive/tar/bsdtar.c index 439093ded76a..53ac135f0129 100644 --- a/contrib/libarchive/tar/bsdtar.c +++ b/contrib/libarchive/tar/bsdtar.c @@ -7,6 +7,9 @@ #include "bsdtar_platform.h" +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif #ifdef HAVE_SYS_PARAM_H #include <sys/param.h> #endif @@ -101,7 +104,7 @@ need_report(void) static __LA_NORETURN void long_help(void); static void only_mode(struct bsdtar *, const char *opt, const char *valid); -static void set_mode(struct bsdtar *, char opt); +static void set_mode(struct bsdtar *, int opt); static __LA_NORETURN void version(void); /* A basic set of security flags to request from libarchive. */ @@ -136,13 +139,14 @@ main(int argc, char **argv) { struct bsdtar *bsdtar, bsdtar_storage; int opt, t; - char compression, compression2; + int compression, compression2; const char *compression_name, *compression2_name; const char *compress_program; char *tptr, *uptr; char possible_help_request; char buff[16]; long l; + time_t now; /* * Use a pointer for consistency, but stack-allocated storage @@ -157,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. */ @@ -331,7 +336,7 @@ main(int argc, char **argv) if (archive_match_exclude_pattern( bsdtar->matching, bsdtar->argument) != ARCHIVE_OK) lafe_errc(1, 0, - "Couldn't exclude %s\n", bsdtar->argument); + "Couldn't exclude %s", bsdtar->argument); break; case OPTION_EXCLUDE_VCS: /* GNU tar */ for(t=0; vcs_files[t]; t++) { @@ -339,7 +344,7 @@ main(int argc, char **argv) bsdtar->matching, vcs_files[t]) != ARCHIVE_OK) lafe_errc(1, 0, "Couldn't " - "exclude %s\n", vcs_files[t]); + "exclude %s", vcs_files[t]); } break; case OPTION_FFLAGS: @@ -673,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 @@ -740,6 +755,8 @@ main(int argc, char **argv) bsdtar->strip_components = (int)l; break; case 'T': /* GNU tar */ + if (bsdtar->names_from_file) + lafe_errc(1, 0, "Multiple --files-from/-T options are not supported"); bsdtar->names_from_file = bsdtar->argument; break; case 't': /* SUSv2 */ @@ -935,7 +952,7 @@ main(int argc, char **argv) switch (compression) { case 'J': case 'j': case 'y': case 'Z': case 'z': strcpy(buff, "-?"); - buff[1] = compression; + buff[1] = (char)compression; break; default: strcpy(buff, "--"); @@ -959,6 +976,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 @@ -1005,7 +1026,7 @@ main(int argc, char **argv) } static void -set_mode(struct bsdtar *bsdtar, char opt) +set_mode(struct bsdtar *bsdtar, int opt) { if (bsdtar->mode != '\0' && bsdtar->mode != opt) lafe_errc(1, 0, @@ -1063,6 +1084,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" diff --git a/contrib/libarchive/tar/bsdtar.h b/contrib/libarchive/tar/bsdtar.h index 42520c88f2bc..45dfeed7dce3 100644 --- a/contrib/libarchive/tar/bsdtar.h +++ b/contrib/libarchive/tar/bsdtar.h @@ -42,10 +42,13 @@ struct bsdtar { int uid; /* --uid */ const char *uname; /* --uname */ const char *passphrase; /* --passphrase */ - char mode; /* Program mode: 'c', 't', 'r', 'u', 'x' */ + int mode; /* Program mode: 'c', 't', 'r', 'u', 'x' */ 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 */ @@ -114,7 +117,7 @@ struct bsdtar { /* Fake short equivalents for long options that otherwise lack them. */ enum { - OPTION_ACLS = 1, + OPTION_ACLS = 256, OPTION_B64ENCODE, OPTION_CHECK_LINKS, OPTION_CHROOT, @@ -175,14 +178,17 @@ 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 *, const char *fmt, ...) __LA_PRINTF(2, 3); +void safe_fprintf(FILE * restrict, const char * restrict fmt, ...) __LA_PRINTF(2, 3); void set_chdir(struct bsdtar *, const char *newdir); const char *tar_i64toa(int64_t); void tar_mode_c(struct bsdtar *bsdtar); diff --git a/contrib/libarchive/tar/cmdline.c b/contrib/libarchive/tar/cmdline.c index 35cb1cb43bf1..c766c1a52dbb 100644 --- a/contrib/libarchive/tar/cmdline.c +++ b/contrib/libarchive/tar/cmdline.c @@ -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 }, diff --git a/contrib/libarchive/tar/subst.c b/contrib/libarchive/tar/subst.c index 0194cc84bd2d..5546b5f93b12 100644 --- a/contrib/libarchive/tar/subst.c +++ b/contrib/libarchive/tar/subst.c @@ -229,6 +229,7 @@ apply_substitution(struct bsdtar *bsdtar, const char *name, char **result, if (rule->from_begin && *result) { realloc_strcat(result, name); + if (buffer) buffer[0] = 0; realloc_strcat(&buffer, *result); name = buffer; (*result)[0] = 0; diff --git a/contrib/libarchive/tar/test/test_list_item.c b/contrib/libarchive/tar/test/test_list_item.c new file mode 100644 index 000000000000..fd131c475275 --- /dev/null +++ b/contrib/libarchive/tar/test/test_list_item.c @@ -0,0 +1,63 @@ +/*-SPDX-License-Identifier: BSD-2-Clause + * Copyright (c) 2024 Tarsnap Backup Inc. + * All rights reserved. + */ +#include "test.h" + +#if defined(_WIN32) && !defined(__CYGWIN__) +#include <winbase.h> +#endif + +/* These lists of files come from 'test_list_archive.tar.uu', which includes + * the script which generated it. */ + +static const char *tf_out = +"f\n" +"hl\n" +"sl\n" +"d/\n" +"d/f\n" +"fake-username\n" +"fake-groupname\n" +"f\n"; + +#if defined(_WIN32) && !defined(__CYGWIN__) +static const char *tvf_out = +"-rw-r--r-- 0 1000 1000 0 Jan 01 1980 f\n" +"hrw-r--r-- 0 1000 1000 0 Jan 01 1980 hl link to f\n" +"lrwxr-xr-x 0 1000 1000 0 Jan 01 1980 sl -> f\n" +"drwxrwxrwx 0 1000 1000 0 Jan 01 1980 d/\n" +"-r-------- 0 1000 1000 0 Jan 01 1980 d/f\n" +"-rw-r--r-- 0 long-fake-uname 1000 0 Jan 01 1980 fake-username\n" +"-rw-r--r-- 0 1000 long-fake-gname 0 Jan 01 1980 fake-groupname\n" +"-rw-r--r-- 0 1000 1000 0 Jan 01 1980 f\n"; +#else +static const char *tvf_out = +"-rw-r--r-- 0 1000 1000 0 Jan 1 1980 f\n" +"hrw-r--r-- 0 1000 1000 0 Jan 1 1980 hl link to f\n" +"lrwxr-xr-x 0 1000 1000 0 Jan 1 1980 sl -> f\n" +"drwxrwxrwx 0 1000 1000 0 Jan 1 1980 d/\n" +"-r-------- 0 1000 1000 0 Jan 1 1980 d/f\n" +"-rw-r--r-- 0 long-fake-uname 1000 0 Jan 1 1980 fake-username\n" +"-rw-r--r-- 0 1000 long-fake-gname 0 Jan 1 1980 fake-groupname\n" +"-rw-r--r-- 0 1000 1000 0 Jan 1 1980 f\n"; +#endif + +DEFINE_TEST(test_list_item) +{ + extract_reference_file("test_list_item.tar"); + + /* Run 'tf' and check output. */ + assertEqualInt(0, + systemf("%s tf test_list_item.tar >tf.out 2>tf.err", testprog)); + failure("'t' mode should write results to stdout"); + assertTextFileContents(tf_out, "tf.out"); + assertEmptyFile("tf.err"); + + /* Run 'tvf' and check output. */ + assertEqualInt(0, + systemf("%s tvf test_list_item.tar >tvf.out 2>tvf.err", testprog)); + failure("'t' mode with 'v' should write more results to stdout"); + assertTextFileContents(tvf_out, "tvf.out"); + assertEmptyFile("tvf.err"); +} diff --git a/contrib/libarchive/tar/test/test_list_item.tar.uu b/contrib/libarchive/tar/test/test_list_item.tar.uu new file mode 100644 index 000000000000..f0e174793438 --- /dev/null +++ b/contrib/libarchive/tar/test/test_list_item.tar.uu @@ -0,0 +1,169 @@ +begin 644 test_list_item.tar +M9@`````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````#`P,#8T-"``,#`Q-S4P(``P,#$W-3`@`#`P,#`P,#`P,#`P +M(#`R,C8S-C$S,C`Q(#`P-S0P-P`@,``````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````````````!U<W1A<@`P,``````` +M```````````````````````````````````````````````````````````` +M```````````````````P,#`P,#`@`#`P,#`P,"`````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````!H;``````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````,#`P-C0T(``P,#$W-3`@`#`P +M,3<U,"``,#`P,#`P,#`P,#`@,#(R-C,V,3,R,#$@,#`W-S,T`"`Q9@`````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````'5S=&%R`#`P```````````````````````````````````````````` +M`````````````````````````````````````````#`P,#`P,"``,#`P,#`P +M(``````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````````'-L```````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````P +M,#`W-34@`#`P,3<U,"``,#`Q-S4P(``P,#`P,#`P,#`P,"`P,C(V,S8Q,S(P +M,2`P,#<W-3,`(#)F```````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````=7-T87(`,#`````````````````````` +M```````````````````````````````````````````````````````````` +M````,#`P,#`P(``P,#`P,#`@```````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````9"\````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````#`P,#<W-R``,#`Q-S4P(``P,#$W-3`@`#`P,#`P +M,#`P,#`P(#`R,C8S-C$S,C`Q(#`P-S4P,``@-0`````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````````````````````!U<W1A<@`P +M,``````````````````````````````````````````````````````````` +M```````````````````````````P,#`P,#`@`#`P,#`P,"`````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````!D+V8````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````````````,#`P-#`P(``P,#$W +M-3`@`#`P,3<U,"``,#`P,#`P,#`P,#`@,#(R-C,V,3,R,#$@,#`W-C(P`"`P +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````'5S=&%R`#`P```````````````````````````````````` +M`````````````````````````````````````````````````#`P,#`P,"`` +M,#`P,#`P(``````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````````````````&9A:V4M +M=7-E<FYA;64````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````P,#`V-#0@`#`P,3<U,"``,#`Q-S4P(``P,#`P,#`P,#`P,"`P,C(V +M,S8Q,S(P,2`P,30U-S0`(#`````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````=7-T87(`,#!L;VYG+69A:V4M +M=6YA;64````````````````````````````````````````````````````` +M````````````,#`P,#`P(``P,#`P,#`@```````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````9F%K92UG<F]U<&YA;64````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````#`P,#8T-"``,#`Q-S4P(``P,#$W-3`@ +M`#`P,#`P,#`P,#`P(#`R,C8S-C$S,C`Q(#`Q-#<S-``@,``````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````````````````````````````!U +M<W1A<@`P,```````````````````````````````````````````;&]N9RUF +M86ME+6=N86UE```````````````````````P,#`P,#`@`#`P,#`P,"`````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````````!F```````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````````````````````,#`P-C0T +M(``P,#$W-3`@`#`P,3<U,"``,#`P,#`P,#`P,#`@,#(R-C,V,3,R,#$@,#`W +M-#`W`"`P```````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````'5S=&%R`#`P```````````````````````````` +M`````````````````````````````````````````````````````````#`P +M,#`P,"``,#`P,#`P(``````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +C```````````````````````````````````````````````` +` +end +#!/bin/sh + +set -e + +mkdir test-list-item +cd test-list-item + +# Create files +touch f +ln f hl +ln -s f sl + +mkdir d +chmod 777 d +touch d/f +chmod 400 d/f + +touch fake-username +touch fake-groupname + +# Set all the dates to 1980 +touch -d "1980-01-01T00:00:01" -h -a -m ./* +touch -d "1980-01-01T00:00:01" -h -a -m d/* + +# Archive +tar -cf test_list_item.tar \ + --uid 1000 --gid 1000 \ + f hl sl d + +# Append to archive, with fake username and groupname +tar --append -f test_list_item.tar \ + --uid 1000 --gid 1000 \ + --uname long-fake-uname \ + fake-username +tar --append -f test_list_item.tar \ + --uid 1000 --gid 1000 \ + --gname long-fake-gname \ + fake-groupname +# Add f again, to print with the expanded field lengths +tar --append -f test_list_item.tar \ + --uid 1000 --gid 1000 \ + f + +# uuencode for the test suite +uuencode -o test_list_item.tar.uu test_list_item.tar test_list_item.tar + +# Append this script, and overwrite file in git +cat ../"$0" >> test_list_item.tar.uu +mv test_list_item.tar.uu ../ + +# Don't delete the temporary directory; leave that up to the developer to +# delete manually when they want to. diff --git a/contrib/libarchive/tar/test/test_option_C_mtree.c b/contrib/libarchive/tar/test/test_option_C_mtree.c index b6ab159941d8..6cbac38fcac5 100644 --- a/contrib/libarchive/tar/test/test_option_C_mtree.c +++ b/contrib/libarchive/tar/test/test_option_C_mtree.c @@ -17,7 +17,7 @@ DEFINE_TEST(test_option_C_mtree) p0 = NULL; char *content = "./foo type=file uname=root gname=root mode=0755\n"; char *filename = "output.tar"; -#if defined(_WIN32) && !defined(CYGWIN) +#if defined(_WIN32) && !defined(__CYGWIN__) char *p; #endif @@ -32,7 +32,7 @@ DEFINE_TEST(test_option_C_mtree) assertMakeDir("bar", 0775); assertMakeFile("bar/foo", 0777, "abc"); -#if defined(_WIN32) && !defined(CYGWIN) +#if defined(_WIN32) && !defined(__CYGWIN__) p = absolute_path; while(*p != '\0') { if (*p == '/') diff --git a/contrib/libarchive/tar/test/test_option_P.c b/contrib/libarchive/tar/test/test_option_P_upper.c index 953d9f58991c..953d9f58991c 100644 --- a/contrib/libarchive/tar/test/test_option_P.c +++ b/contrib/libarchive/tar/test/test_option_P_upper.c diff --git a/contrib/libarchive/tar/test/test_option_ignore_zeros.c b/contrib/libarchive/tar/test/test_option_ignore_zeros.c index 31e0dd940460..453e8a0907e6 100644 --- a/contrib/libarchive/tar/test/test_option_ignore_zeros.c +++ b/contrib/libarchive/tar/test/test_option_ignore_zeros.c @@ -76,8 +76,7 @@ DEFINE_TEST(test_option_ignore_zeros_mode_c) // bsdtar.exe: b: Can't translate uname '(null)' to UTF-8 skipping("TODO: figure out why this test fails on github workflows with MSVC"); return; -#endif - +#else if (make_files()) return; @@ -97,6 +96,7 @@ DEFINE_TEST(test_option_ignore_zeros_mode_c) assertEqualFile("out/a", "in/a"); assertEqualFile("out/b", "in/b"); assertEqualFile("out/c", "in/c"); +#endif } static void diff --git a/contrib/libarchive/tar/test/test_option_mtime.c b/contrib/libarchive/tar/test/test_option_mtime.c new file mode 100644 index 000000000000..75cdb8390bc0 --- /dev/null +++ b/contrib/libarchive/tar/test/test_option_mtime.c @@ -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(".."); +} diff --git a/contrib/libarchive/tar/test/test_option_s.c b/contrib/libarchive/tar/test/test_option_s.c index 1a36280c111f..125e971d3304 100644 --- a/contrib/libarchive/tar/test/test_option_s.c +++ b/contrib/libarchive/tar/test/test_option_s.c @@ -70,7 +70,7 @@ DEFINE_TEST(test_option_s) assertFileContents("bar", 3, "test4/in/d1/baz"); /* - * Test 4b: Multiple substitutions behavior with option b). + * Test 4b: Multiple substitutions behavior with option b. */ assertMakeDir("test4b", 0755); systemf("%s -cf test4b.tar in/d1/foo in/d1/bar", @@ -81,6 +81,18 @@ DEFINE_TEST(test_option_s) assertFileContents("bar", 3, "test4b/in/d1/baz"); /* + * Test 4bb: Multiple substitutions with option b + * (libarchive/libarchive#2414 GitHub issue regression test). + */ + assertMakeDir("test4bb", 0755); + systemf("%s -cf test4bb.tar in/d1/foo in/d1/bar", + testprog); + systemf("%s -xf test4bb.tar -s /oo/ar/ -s }ar}az}b -s :az:end:b -C test4bb", + testprog); + assertFileContents("foo", 3, "test4bb/in/d1/fend"); + assertFileContents("bar", 3, "test4bb/in/d1/bend"); + + /* * Test 5: Name-switching substitutions when extracting archive. */ assertMakeDir("test5", 0755); diff --git a/contrib/libarchive/tar/test/test_stdio.c b/contrib/libarchive/tar/test/test_stdio.c index 0c27c59a2af6..239f3815fb71 100644 --- a/contrib/libarchive/tar/test/test_stdio.c +++ b/contrib/libarchive/tar/test/test_stdio.c @@ -6,6 +6,28 @@ */ #include "test.h" +/* + * This data would fit onto one line, but it's easier to understand when it's + * on mulitple lines (and thus matches the output files). + */ +static const char *cvf_err = +"a f\n" +"a l\n"; + +static const char *tf_out = +"f\n" +"l\n"; + +static const char *xvf_err = +"x f\n" +"x l\n"; + +/* + * This string should appear in the verbose listing regardless of platform, + * locale, username, or groupname. + */ +static const char * tvf_contains = "l link to f"; + DEFINE_TEST(test_stdio) { FILE *filelist; @@ -47,35 +69,43 @@ DEFINE_TEST(test_stdio) failure("'cv' writes filenames to stderr, nothing to stdout (SUSv2)\n" "Note that GNU tar writes the file list to stdout by default."); assertEmptyFile("cvf.out"); - /* TODO: Verify cvf.err has file list in SUSv2-prescribed format. */ + assertTextFileContents(cvf_err, "cvf.err"); /* 'cvf -' should generate file list on stderr, archive on stdout. */ r = systemf("%s cvf - f l >cvf-.out 2>cvf-.err", testprog); assertEqualInt(r, 0); failure("cvf - should write archive to stdout"); - /* TODO: Verify cvf-.out has archive. */ failure("cvf - should write file list to stderr (SUSv2)"); - /* TODO: Verify cvf-.err has verbose file list. */ + assertEqualFile("cvf.err", "cvf-.err"); + /* Check that stdout from 'cvf -' was a valid archive. */ + r = systemf("%s tf cvf-.out >cvf-tf.out 2>cvf-tf.err", testprog); + assertEqualInt(r, 0); + assertEmptyFile("cvf-tf.err"); + assertTextFileContents(tf_out, "cvf-tf.out"); /* 'tf' should generate file list on stdout, empty stderr. */ r = systemf("%s tf archive >tf.out 2>tf.err", testprog); assertEqualInt(r, 0); assertEmptyFile("tf.err"); failure("'t' mode should write results to stdout"); - /* TODO: Verify tf.out has file list. */ + assertTextFileContents(tf_out, "tf.out"); /* 'tvf' should generate file list on stdout, empty stderr. */ r = systemf("%s tvf archive >tvf.out 2>tvf.err", testprog); assertEqualInt(r, 0); assertEmptyFile("tvf.err"); failure("'tv' mode should write results to stdout"); - /* TODO: Verify tvf.out has file list. */ + /* Check that it contains a string only found in the verbose listing. */ + p = slurpfile(&s, "%s", "tvf.out"); + assert(strstr(p, tvf_contains) != NULL); + free(p); /* 'tvf -' uses stdin, file list on stdout, empty stderr. */ r = systemf("%s tvf - < archive >tvf-.out 2>tvf-.err", testprog); assertEqualInt(r, 0); assertEmptyFile("tvf-.err"); - /* TODO: Verify tvf-.out has file list. */ + failure("'tvf-' mode should write the same results as 'tvf'"); + assertEqualFile("tvf.out", "tvf-.out"); /* Basic 'xf' should generate no output on stdout or stderr. */ r = systemf("%s xf archive >xf.out 2>xf.err", testprog); @@ -87,7 +117,7 @@ DEFINE_TEST(test_stdio) r = systemf("%s xvf archive >xvf.out 2>xvf.err", testprog); assertEqualInt(r, 0); assertEmptyFile("xvf.out"); - /* TODO: Verify xvf.err */ + assertTextFileContents(xvf_err, "xvf.err"); /* 'xvOf' should generate list on stderr, file contents on stdout. */ r = systemf("%s xvOf archive >xvOf.out 2>xvOf.err", testprog); @@ -96,12 +126,12 @@ DEFINE_TEST(test_stdio) p = slurpfile(&s, "xvOf.out"); assertEqualInt((int)s, 3); assertEqualMem(p, "abc", 3); - /* TODO: Verify xvf.err */ + assertEqualFile("xvf.err", "xvOf.err"); free(p); /* 'xvf -' should generate list on stderr, empty stdout. */ r = systemf("%s xvf - < archive >xvf-.out 2>xvf-.err", testprog); assertEqualInt(r, 0); assertEmptyFile("xvf-.out"); - /* TODO: Verify xvf-.err */ + assertEqualFile("xvf.err", "xvf-.err"); } diff --git a/contrib/libarchive/tar/util.c b/contrib/libarchive/tar/util.c index a152f3506994..c99f67797562 100644 --- a/contrib/libarchive/tar/util.c +++ b/contrib/libarchive/tar/util.c @@ -67,26 +67,26 @@ static const char *strip_components(const char *path, int elements); * malloc()), partly out of expedience (we have to call vsnprintf() * before malloc() anyway to find out how big a buffer we need; we may * as well point that first call at a small local buffer in case it - * works), but mostly for safety (so we can use this to print messages - * about out-of-memory conditions). + * works). */ void -safe_fprintf(FILE *f, const char *fmt, ...) +safe_fprintf(FILE * restrict f, const char * restrict fmt, ...) { char fmtbuff_stack[256]; /* Place to format the printf() string. */ char outbuff[256]; /* Buffer for outgoing characters. */ char *fmtbuff_heap; /* If fmtbuff_stack is too small, we use malloc */ char *fmtbuff; /* Pointer to fmtbuff_stack or fmtbuff_heap. */ - int fmtbuff_length; + size_t fmtbuff_length; int length, n; va_list ap; const char *p; - unsigned i; + size_t i; wchar_t wc; char try_wc; /* Use a stack-allocated buffer if we can, for speed and safety. */ + memset(fmtbuff_stack, '\0', sizeof(fmtbuff_stack)); fmtbuff_heap = NULL; fmtbuff_length = sizeof(fmtbuff_stack); fmtbuff = fmtbuff_stack; @@ -96,17 +96,21 @@ safe_fprintf(FILE *f, const char *fmt, ...) length = vsnprintf(fmtbuff, fmtbuff_length, fmt, ap); va_end(ap); + /* If vsnprintf will always fail, stop early. */ + if (length < 0 && errno == EOVERFLOW) + return; + /* If the result was too large, allocate a buffer on the heap. */ - while (length < 0 || length >= fmtbuff_length) { - if (length >= fmtbuff_length) - fmtbuff_length = length+1; + while (length < 0 || (size_t)length >= fmtbuff_length) { + if (length >= 0 && (size_t)length >= fmtbuff_length) + fmtbuff_length = (size_t)length + 1; else if (fmtbuff_length < 8192) fmtbuff_length *= 2; else if (fmtbuff_length < 1000000) fmtbuff_length += fmtbuff_length / 4; else { - length = fmtbuff_length; - fmtbuff_heap[length-1] = '\0'; + fmtbuff[fmtbuff_length - 1] = '\0'; + length = (int)strlen(fmtbuff); break; } free(fmtbuff_heap); @@ -121,8 +125,9 @@ safe_fprintf(FILE *f, const char *fmt, ...) } else { /* Leave fmtbuff pointing to the truncated * string in fmtbuff_stack. */ + fmtbuff_stack[sizeof(fmtbuff_stack) - 1] = '\0'; fmtbuff = fmtbuff_stack; - length = sizeof(fmtbuff_stack) - 1; + length = (int)strlen(fmtbuff); break; } } @@ -153,13 +158,13 @@ safe_fprintf(FILE *f, const char *fmt, ...) } else { /* Not printable, format the bytes. */ while (n-- > 0) - i += (unsigned)bsdtar_expand_char( + i += bsdtar_expand_char( outbuff, sizeof(outbuff), i, *p++); } } else { /* After any conversion failure, don't bother * trying to convert the rest. */ - i += (unsigned)bsdtar_expand_char(outbuff, sizeof(outbuff), i, *p++); + i += bsdtar_expand_char(outbuff, sizeof(outbuff), i, *p++); try_wc = 0; } @@ -202,7 +207,8 @@ bsdtar_expand_char(char *buff, size_t buffsize, size_t offset, char c) case '\v': buff[i++] = 'v'; break; case '\\': buff[i++] = '\\'; break; default: - snprintf(buff + i, buffsize - i, "%03o", 0xFF & (int)c); + snprintf(buff + i, buffsize - i, "%03o", + 0xFF & (unsigned int)c); i += 3; } } @@ -328,7 +334,7 @@ do_chdir(struct bsdtar *bsdtar) return; if (chdir(bsdtar->pending_chdir) != 0) { - lafe_errc(1, 0, "could not chdir to '%s'\n", + lafe_errc(1, 0, "could not chdir to '%s'", bsdtar->pending_chdir); } free(bsdtar->pending_chdir); @@ -562,6 +568,20 @@ edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry) } /* + * 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 * following simple utility function. @@ -684,7 +704,7 @@ list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry) } if (!now) time(&now); - fprintf(out, "%s %d ", + fprintf(out, "%s %u ", archive_entry_strmode(entry), archive_entry_nlink(entry)); @@ -748,7 +768,10 @@ list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry) #else ltime = localtime(&tim); #endif - strftime(tmp, sizeof(tmp), fmt, ltime); + if (ltime) + strftime(tmp, sizeof(tmp), fmt, ltime); + else + sprintf(tmp, "-- -- ----"); fprintf(out, " %s ", tmp); safe_fprintf(out, "%s", archive_entry_pathname(entry)); diff --git a/contrib/libarchive/tar/write.c b/contrib/libarchive/tar/write.c index f1838d6b5dea..21984e980ebd 100644 --- a/contrib/libarchive/tar/write.c +++ b/contrib/libarchive/tar/write.c @@ -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 "); |
