diff options
Diffstat (limited to 'tar')
| -rw-r--r-- | tar/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | tar/bsdtar.1 | 70 | ||||
| -rw-r--r-- | tar/bsdtar.c | 42 | ||||
| -rw-r--r-- | tar/bsdtar.h | 12 | ||||
| -rw-r--r-- | tar/bsdtar_platform.h | 8 | ||||
| -rw-r--r-- | tar/bsdtar_windows.h | 6 | ||||
| -rw-r--r-- | tar/cmdline.c | 5 | ||||
| -rw-r--r-- | tar/creation_set.c | 2 | ||||
| -rw-r--r-- | tar/read.c | 153 | ||||
| -rw-r--r-- | tar/subst.c | 106 | ||||
| -rw-r--r-- | tar/test/CMakeLists.txt | 9 | ||||
| -rw-r--r-- | tar/test/main.c | 154 | ||||
| -rw-r--r-- | tar/test/test.h | 29 | ||||
| -rw-r--r-- | tar/test/test_extract.tar.lz4.uu | 8 | ||||
| -rw-r--r-- | tar/test/test_extract_tar_lz4.c | 48 | ||||
| -rw-r--r-- | tar/test/test_leading_slash.c | 49 | ||||
| -rw-r--r-- | tar/test/test_leading_slash.tar.uu | 60 | ||||
| -rw-r--r-- | tar/test/test_option_X_upper.c | 14 | ||||
| -rw-r--r-- | tar/test/test_option_b.c | 19 | ||||
| -rw-r--r-- | tar/test/test_option_lz4.c | 74 | ||||
| -rw-r--r-- | tar/test/test_option_passphrase.c | 43 | ||||
| -rw-r--r-- | tar/test/test_option_passphrase.zip.uu | 12 | ||||
| -rw-r--r-- | tar/test/test_option_s.c | 23 | ||||
| -rw-r--r-- | tar/test/test_version.c | 5 | ||||
| -rw-r--r-- | tar/util.c | 312 | ||||
| -rw-r--r-- | tar/write.c | 40 |
26 files changed, 1005 insertions, 301 deletions
diff --git a/tar/CMakeLists.txt b/tar/CMakeLists.txt index 46ce58b02e2e9..6434791f788f5 100644 --- a/tar/CMakeLists.txt +++ b/tar/CMakeLists.txt @@ -20,6 +20,8 @@ IF(ENABLE_TAR) ../libarchive_fe/lafe_platform.h ../libarchive_fe/line_reader.c ../libarchive_fe/line_reader.h + ../libarchive_fe/passphrase.c + ../libarchive_fe/passphrase.h ) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../libarchive_fe) IF(WIN32 AND NOT CYGWIN) @@ -39,7 +41,6 @@ IF(ENABLE_TAR) SET_TARGET_PROPERTIES(bsdtar PROPERTIES COMPILE_DEFINITIONS LIBARCHIVE_STATIC) ENDIF(ENABLE_TAR_SHARED) - GET_TARGET_PROPERTY(BSDTAR bsdtar LOCATION) # Installation rules INSTALL(TARGETS bsdtar RUNTIME DESTINATION bin) diff --git a/tar/bsdtar.1 b/tar/bsdtar.1 index 7bb6a6084d54e..9eadaaf885b1b 100644 --- a/tar/bsdtar.1 +++ b/tar/bsdtar.1 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd November 1, 2012 +.Dd September 16, 2014 .Dt TAR 1 .Os .Sh NAME @@ -184,6 +184,10 @@ 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 clear-nochange-fflags +(x mode only) +Before removing file system objects to replace them, clear platform-specific +file flags that might prevent removal. .It Fl Fl disable-copyfile Mac OS X specific. Disable the use of @@ -256,6 +260,10 @@ Show usage. .It Fl Fl hfsCompression (x mode only) Mac OS X specific(v10.6 or later). Compress extracted regular files with HFS+ compression. +.It Fl Fl ignore-zeros +An alias of +.Fl Fl options Cm read_concatenated_archives +for compatibility with GNU tar. .It Fl Fl include Ar pattern Process only files or directories that match the specified pattern. Note that exclusions specified with @@ -314,6 +322,11 @@ Issue a warning message unless all links to each file are archived. Compress the resulting archive with .Xr lrzip 1 . In extract or list modes, this option is ignored. +.It Fl Fl lz4 +(c mode only) +Compress the archive with lz4-compatible compression before writing it. +In input mode, this option is ignored; lz4 compression is recognized +automatically on input. .It Fl Fl lzma (c mode only) Compress the resulting archive with the original LZMA algorithm. Use of this option is discouraged and new archives should be created with @@ -494,6 +507,20 @@ Supported values are bzip2, gzip, lzo (ultra fast), and zpaq (best, extremely slow). .It Cm lrzip:compression-level A decimal integer from 1 to 9 specifying the lrzip compression level. +.It Cm lz4:compression-level +A decimal integer from 1 to 9 specifying the lzop compression level. +.It Cm lz4:stream-checksum +Enable stream checksum. This is by default, use +.Cm lz4:!stream-checksum +to disable. +.It Cm lz4:block-checksum +Enable block checksum (Disabled by default). +.It Cm lz4:block-size +A decimal integer from 4 to 7 specifying the lz4 compression block size +(7 is set by default). +.It Cm lz4:block-dependence +Use the previous block of the block being compressed for +a compression dictionary to improve compression ratio. .It Cm lzop:compression-level A decimal integer from 1 to 9 specifying the lzop compression level. .It Cm xz:compression-level @@ -524,6 +551,21 @@ Use .Ar type as compression method. Supported values are store (uncompressed) and deflate (gzip algorithm). +.It Cm zip:encryption +Enable encryption using traditional zip encryption. +.It Cm zip:encryption Ns = Ns Ar type +Use +.Ar type +as encryption type. +Supported values are zipcrypt (traditional zip encryption), +aes128 (WinZip AES-128 encryption) and aes256 (WinZip AES-256 encryption). +.It Cm read_concatenated_archives +Ignore zeroed blocks in the archive, which occurs when multiple tar archives +have been concatenated together. Without this option, only the contents of +the first concatenated archive would be read. This option is comparable to +the +.Fl i , Fl Fl ignore-zeros +option of GNU tar. .El If a provided option is not supported by any module, that is a fatal error. @@ -549,6 +591,13 @@ is being run by root and can be overridden by also specifying .Fl Fl no-same-owner and .Fl Fl no-same-permissions . +.It Fl Fl passphrase Ar passphrase +The +.Pa passphrase +is used to extract or create an encrypted archive. +Currently, zip is the only supported format that supports encryption. +You shouldn't use this option unless you realize how insecure +use of this option is. .It Fl Fl posix (c, r, u mode only) Synonym for @@ -689,9 +738,9 @@ In list mode, .Nm will produce output similar to that of .Xr ls 1 . -Additional +An additional .Fl v -options will provide additional detail. +option will also provide ls-like details in create and extract mode. .It Fl Fl version Print version of .Nm @@ -823,11 +872,13 @@ An input file in format can be used to create an output archive with arbitrary ownership, permissions, or names that differ from existing data on disk: .Pp -.Dl $ cat input.mtree -.Dl #mtree -.Dl usr/bin uid=0 gid=0 mode=0755 type=dir -.Dl usr/bin/ls uid=0 gid=0 mode=0755 type=file content=myls -.Dl $ tar -cvf output.tar @input.mtree +.Bd -literal -offset indent +$ cat input.mtree +#mtree +usr/bin uid=0 gid=0 mode=0755 type=dir +usr/bin/ls uid=0 gid=0 mode=0755 type=file content=myls +$ tar -cvf output.tar @input.mtree +.Ed .Pp The .Fl Fl newer @@ -1115,8 +1166,7 @@ option is specified. There needs to be better support for file selection on both create and extract. .Pp -There is not yet any support for multi-volume archives or for archiving -sparse files. +There is not yet any support for multi-volume archives. .Pp Converting between dissimilar archive formats (such as tar and cpio) using the .Cm @ Ns Pa - diff --git a/tar/bsdtar.c b/tar/bsdtar.c index 47267579f0c4d..93bf60a94da84 100644 --- a/tar/bsdtar.c +++ b/tar/bsdtar.c @@ -178,21 +178,8 @@ main(int argc, char **argv) } #endif - - /* Need lafe_progname before calling lafe_warnc. */ - if (*argv == NULL) - lafe_progname = "bsdtar"; - else { -#if defined(_WIN32) && !defined(__CYGWIN__) - lafe_progname = strrchr(*argv, '\\'); - if (strrchr(*argv, '/') > lafe_progname) -#endif - lafe_progname = strrchr(*argv, '/'); - if (lafe_progname != NULL) - lafe_progname++; - else - lafe_progname = *argv; - } + /* Set lafe_progname before calling lafe_warnc. */ + lafe_setprogname(*argv, "bsdtar"); #if HAVE_SETLOCALE if (setlocale(LC_ALL, "") == NULL) @@ -303,6 +290,10 @@ main(int argc, char **argv) case OPTION_CHROOT: /* NetBSD */ bsdtar->option_chroot = 1; break; + case OPTION_CLEAR_NOCHANGE_FFLAGS: + bsdtar->extract_flags |= + ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS; + break; case OPTION_DISABLE_COPYFILE: /* Mac OS X */ bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_MAC_COPYFILE; break; @@ -352,6 +343,9 @@ main(int argc, char **argv) bsdtar->extract_flags |= ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED; break; + case OPTION_IGNORE_ZEROS: + bsdtar->option_ignore_zeros = 1; + break; case 'I': /* GNU tar */ /* * TODO: Allow 'names' to come from an archive, @@ -407,6 +401,7 @@ main(int argc, char **argv) bsdtar->option_warn_links = 1; break; case OPTION_LRZIP: + case OPTION_LZ4: case OPTION_LZIP: /* GNU tar beginning with 1.23 */ case OPTION_LZMA: /* GNU tar beginning with 1.20 */ case OPTION_LZOP: /* GNU tar beginning with 1.21 */ @@ -417,6 +412,7 @@ main(int argc, char **argv) compression = opt; switch (opt) { case OPTION_LRZIP: compression_name = "lrzip"; break; + case OPTION_LZ4: compression_name = "lz4"; break; case OPTION_LZIP: compression_name = "lzip"; break; case OPTION_LZMA: compression_name = "lzma"; break; case OPTION_LZOP: compression_name = "lzop"; break; @@ -480,6 +476,10 @@ main(int argc, char **argv) bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_FFLAGS; bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_MAC_METADATA; break; + case OPTION_NO_XATTR: /* Issue #131 */ + bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_XATTR; + bsdtar->readdisk_flags |= ARCHIVE_READDISK_NO_XATTR; + break; case OPTION_NULL: /* GNU tar */ bsdtar->option_null++; break; @@ -557,6 +557,9 @@ main(int argc, char **argv) bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS; bsdtar->extract_flags |= ARCHIVE_EXTRACT_MAC_METADATA; break; + case OPTION_PASSPHRASE: + bsdtar->passphrase = bsdtar->argument; + break; case OPTION_POSIX: /* GNU tar */ cset_set_format(bsdtar->cset, "pax"); break; @@ -712,6 +715,8 @@ main(int argc, char **argv) only_mode(bsdtar, "--nopreserveHFSCompression", "x"); if (bsdtar->readdisk_flags & ARCHIVE_READDISK_HONOR_NODUMP) only_mode(bsdtar, "--nodump", "cru"); + if (bsdtar->readdisk_flags & ARCHIVE_READDISK_NO_XATTR) + only_mode(bsdtar, "--no-xattr", "crux"); if (option_o > 0) { switch (bsdtar->mode) { case 'c': @@ -810,6 +815,7 @@ main(int argc, char **argv) cleanup_substitution(bsdtar); #endif cset_free(bsdtar->cset); + passphrase_free(bsdtar->ppbuff); if (bsdtar->return_value != 0) lafe_warnc(0, @@ -844,7 +850,7 @@ usage(void) { const char *p; - p = lafe_progname; + p = lafe_getprogname(); fprintf(stderr, "Usage:\n"); fprintf(stderr, " List: %s -tf <archive-filename>\n", p); @@ -859,7 +865,7 @@ version(void) { printf("bsdtar %s - %s\n", BSDTAR_VERSION_STRING, - archive_version_string()); + archive_version_details()); exit(0); } @@ -904,7 +910,7 @@ long_help(void) const char *prog; const char *p; - prog = lafe_progname; + prog = lafe_getprogname(); fflush(stderr); diff --git a/tar/bsdtar.h b/tar/bsdtar.h index 637e1b9d4feee..4b84ba18ab467 100644 --- a/tar/bsdtar.h +++ b/tar/bsdtar.h @@ -57,12 +57,14 @@ struct bsdtar { const char *gname; /* --gname */ int uid; /* --uid */ const char *uname; /* --uname */ + const char *passphrase; /* --passphrase */ char mode; /* Program mode: 'c', 't', 'r', 'u', 'x' */ char symlink_mode; /* H or L, per BSD conventions */ char option_absolute_paths; /* -P */ char option_chroot; /* --chroot */ char option_fast_read; /* --fast-read */ const char *option_options; /* --options */ + char option_ignore_zeros; /* --ignore-zeros */ char option_interactive; /* -w */ char option_no_owner; /* -o */ char option_no_subdirs; /* -n */ @@ -109,6 +111,7 @@ struct bsdtar { struct name_cache *uname_cache; /* for write.c */ struct siginfo_data *siginfo; /* for siginfo.c */ struct substitution *substitution; /* for subst.c */ + char *ppbuff; /* for util.c */ }; /* Fake short equivalents for long options that otherwise lack them. */ @@ -116,6 +119,7 @@ enum { OPTION_B64ENCODE = 1, OPTION_CHECK_LINKS, OPTION_CHROOT, + OPTION_CLEAR_NOCHANGE_FFLAGS, OPTION_DISABLE_COPYFILE, OPTION_EXCLUDE, OPTION_FORMAT, @@ -124,9 +128,11 @@ enum { OPTION_GRZIP, OPTION_HELP, OPTION_HFS_COMPRESSION, + OPTION_IGNORE_ZEROS, OPTION_INCLUDE, OPTION_KEEP_NEWER_FILES, OPTION_LRZIP, + OPTION_LZ4, OPTION_LZIP, OPTION_LZMA, OPTION_LZOP, @@ -138,6 +144,7 @@ enum { OPTION_NOPRESERVE_HFS_COMPRESSION, OPTION_NO_SAME_OWNER, OPTION_NO_SAME_PERMISSIONS, + OPTION_NO_XATTR, OPTION_NULL, OPTION_NUMERIC_OWNER, OPTION_OLDER_CTIME, @@ -146,6 +153,7 @@ enum { OPTION_OLDER_MTIME_THAN, OPTION_ONE_FILE_SYSTEM, OPTION_OPTIONS, + OPTION_PASSPHRASE, OPTION_POSIX, OPTION_SAME_OWNER, OPTION_STRIP_COMPONENTS, @@ -191,3 +199,7 @@ void cset_set_format(struct creation_set *, const char *); int cset_write_add_filters(struct creation_set *, struct archive *, const void **); +const char * passphrase_callback(struct archive *, void *); +void passphrase_free(char *); +void list_item_verbose(struct bsdtar *, FILE *, + struct archive_entry *); diff --git a/tar/bsdtar_platform.h b/tar/bsdtar_platform.h index 45228f504f849..e73f9828b3b0b 100644 --- a/tar/bsdtar_platform.h +++ b/tar/bsdtar_platform.h @@ -42,6 +42,10 @@ #include "config.h" #endif +#if defined(_WIN32) && !defined(__CYGWIN__) +#include "bsdtar_windows.h" +#endif + /* Get a real definition for __FBSDID if we can */ #if HAVE_SYS_CDEFS_H #include <sys/cdefs.h> @@ -125,8 +129,4 @@ #define __LA_DEAD #endif -#if defined(_WIN32) && !defined(__CYGWIN__) -#include "bsdtar_windows.h" -#endif - #endif /* !BSDTAR_PLATFORM_H_INCLUDED */ diff --git a/tar/bsdtar_windows.h b/tar/bsdtar_windows.h index f0611d79abdcd..308ad1107befe 100644 --- a/tar/bsdtar_windows.h +++ b/tar/bsdtar_windows.h @@ -29,12 +29,16 @@ #define BSDTAR_WINDOWS_H 1 #include <direct.h> #include <windows.h> +#include <io.h> +#include <fcntl.h> #ifndef PRId64 #define PRId64 "I64" #endif #define geteuid() 0 +#ifndef __WATCOMC__ + #ifndef S_IFIFO #define S_IFIFO 0010000 /* pipe */ #endif @@ -57,4 +61,6 @@ int __tar_chdir(const char *); #define S_ISBLK(a) (0) #endif +#endif + #endif /* BSDTAR_WINDOWS_H */ diff --git a/tar/cmdline.c b/tar/cmdline.c index 4444b80c55802..fd0712a0dd6cb 100644 --- a/tar/cmdline.c +++ b/tar/cmdline.c @@ -74,6 +74,7 @@ static const struct bsdtar_option { { "cd", 1, 'C' }, { "check-links", 0, OPTION_CHECK_LINKS }, { "chroot", 0, OPTION_CHROOT }, + { "clear-nochange-fflags", 0, OPTION_CLEAR_NOCHANGE_FFLAGS }, { "compress", 0, 'Z' }, { "confirmation", 0, 'w' }, { "create", 0, 'c' }, @@ -94,6 +95,7 @@ static const struct bsdtar_option { { "gzip", 0, 'z' }, { "help", 0, OPTION_HELP }, { "hfsCompression", 0, OPTION_HFS_COMPRESSION }, + { "ignore-zeros", 0, OPTION_IGNORE_ZEROS }, { "include", 1, OPTION_INCLUDE }, { "insecure", 0, 'P' }, { "interactive", 0, 'w' }, @@ -101,6 +103,7 @@ static const struct bsdtar_option { { "keep-old-files", 0, 'k' }, { "list", 0, 't' }, { "lrzip", 0, OPTION_LRZIP }, + { "lz4", 0, OPTION_LZ4 }, { "lzip", 0, OPTION_LZIP }, { "lzma", 0, OPTION_LZMA }, { "lzop", 0, OPTION_LZOP }, @@ -114,6 +117,7 @@ static const struct bsdtar_option { { "no-recursion", 0, 'n' }, { "no-same-owner", 0, OPTION_NO_SAME_OWNER }, { "no-same-permissions", 0, OPTION_NO_SAME_PERMISSIONS }, + { "no-xattr", 0, OPTION_NO_XATTR }, { "nodump", 0, OPTION_NODUMP }, { "nopreserveHFSCompression",0, OPTION_NOPRESERVE_HFS_COMPRESSION }, { "norecurse", 0, 'n' }, @@ -127,6 +131,7 @@ static const struct bsdtar_option { { "older-than", 1, OPTION_OLDER_CTIME_THAN }, { "one-file-system", 0, OPTION_ONE_FILE_SYSTEM }, { "options", 1, OPTION_OPTIONS }, + { "passphrase", 1, OPTION_PASSPHRASE }, { "posix", 0, OPTION_POSIX }, { "preserve-permissions", 0, 'p' }, { "read-full-blocks", 0, 'B' }, diff --git a/tar/creation_set.c b/tar/creation_set.c index 3d7764d46dbac..87d561b351933 100644 --- a/tar/creation_set.c +++ b/tar/creation_set.c @@ -75,6 +75,7 @@ get_filter_code(const char *suffix) { ".grz", "grzip" }, { ".lrz", "lrzip" }, { ".lz", "lzip" }, + { ".lz4", "lz4" }, { ".lzo", "lzop" }, { ".lzma", "lzma" }, { ".uu", "uuencode" }, @@ -97,6 +98,7 @@ get_format_code(const char *suffix) { ".mtree", "mtree" }, { ".shar", "shar" }, { ".tar", "paxr" }, + { ".warc", "warc" }, { ".xar", "xar" }, { ".zip", "zip" }, { NULL, NULL } diff --git a/tar/read.c b/tar/read.c index e2bacad9573c2..e94cb3da8ac70 100644 --- a/tar/read.c +++ b/tar/read.c @@ -39,9 +39,19 @@ __FBSDID("$FreeBSD: src/usr.bin/tar/read.c,v 1.40 2008/08/21 06:41:14 kientzle E #ifdef HAVE_ERRNO_H #include <errno.h> #endif + +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif + #ifdef HAVE_GRP_H #include <grp.h> #endif + +#ifdef HAVE_IO_H +#include <io.h> +#endif + #ifdef HAVE_LIMITS_H #include <limits.h> #endif @@ -74,8 +84,6 @@ struct progress_data { struct archive_entry *entry; }; -static void list_item_verbose(struct bsdtar *, FILE *, - struct archive_entry *); static void read_archive(struct bsdtar *bsdtar, char mode, struct archive *); static int unmatched_inclusions_warn(struct archive *matching, const char *); @@ -112,7 +120,7 @@ tar_mode_x(struct bsdtar *bsdtar) static void progress_func(void *cookie) { - struct progress_data *progress_data = cookie; + struct progress_data *progress_data = (struct progress_data *)cookie; struct bsdtar *bsdtar = progress_data->bsdtar; struct archive *a = progress_data->archive; struct archive_entry *entry = progress_data->entry; @@ -182,7 +190,7 @@ read_archive(struct bsdtar *bsdtar, char mode, struct archive *writer) if (reader_options != NULL) { char *p; /* Set default read options. */ - p = malloc(sizeof(IGNORE_WRONG_MODULE_NAME) + p = (char *)malloc(sizeof(IGNORE_WRONG_MODULE_NAME) + strlen(reader_options) + 1); if (p == NULL) lafe_errc(1, errno, "Out of memory"); @@ -201,6 +209,17 @@ read_archive(struct bsdtar *bsdtar, char mode, struct archive *writer) } if (ARCHIVE_OK != archive_read_set_options(a, bsdtar->option_options)) lafe_errc(1, 0, "%s", archive_error_string(a)); + if (bsdtar->option_ignore_zeros) + if (archive_read_set_options(a, + "read_concatenated_archives") != ARCHIVE_OK) + lafe_errc(1, 0, "%s", archive_error_string(a)); + if (bsdtar->passphrase != NULL) + r = archive_read_add_passphrase(a, bsdtar->passphrase); + else + r = archive_read_set_passphrase_callback(a, bsdtar, + &passphrase_callback); + if (r != ARCHIVE_OK) + lafe_errc(1, 0, "%s", archive_error_string(a)); if (archive_read_open_filename(a, bsdtar->filename, bsdtar->bytes_per_block)) lafe_errc(1, 0, "Error opening archive: %s", @@ -226,8 +245,15 @@ read_archive(struct bsdtar *bsdtar, char mode, struct archive *writer) #endif } +#if defined(_WIN32) && !defined(__CYGWIN__) + if (mode == 'x' && bsdtar->option_stdout) { + _setmode(1, _O_BINARY); + } +#endif + for (;;) { /* Support --fast-read option */ + const char *p; if (bsdtar->option_fast_read && archive_match_path_unmatched_inclusions(bsdtar->matching) == 0) break; @@ -247,6 +273,12 @@ read_archive(struct bsdtar *bsdtar, char mode, struct archive *writer) } if (r == ARCHIVE_FATAL) break; + p = archive_entry_pathname(entry); + if (p == NULL || p[0] == '\0') { + lafe_warnc(0, "Archive entry has empty or unreadable filename ... skipping."); + bsdtar->return_value = 1; + continue; + } if (bsdtar->uid >= 0) { archive_entry_set_uid(entry, bsdtar->uid); @@ -318,11 +350,14 @@ read_archive(struct bsdtar *bsdtar, char mode, struct archive *writer) !yes("extract '%s'", archive_entry_pathname(entry))) continue; - /* - * Format here is from SUSv2, including the - * deferred '\n'. - */ - if (bsdtar->verbose) { + if (bsdtar->verbose > 1) { + /* GNU tar uses -tv format with -xvv */ + safe_fprintf(stderr, "x "); + list_item_verbose(bsdtar, stderr, entry); + fflush(stderr); + } else if (bsdtar->verbose > 0) { + /* Format follows SUSv2, including the + * deferred '\n'. */ safe_fprintf(stderr, "x %s", archive_entry_pathname(entry)); fflush(stderr); @@ -366,106 +401,6 @@ read_archive(struct bsdtar *bsdtar, char mode, struct archive *writer) } -/* - * Display information about the current file. - * - * The format here roughly duplicates the output of 'ls -l'. - * This is based on SUSv2, where 'tar tv' is documented as - * listing additional information in an "unspecified format," - * and 'pax -l' is documented as using the same format as 'ls -l'. - */ -static void -list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry) -{ - char tmp[100]; - size_t w; - const char *p; - const char *fmt; - time_t tim; - static time_t now; - - /* - * We avoid collecting the entire list in memory at once by - * listing things as we see them. However, that also means we can't - * just pre-compute the field widths. Instead, we start with guesses - * and just widen them as necessary. These numbers are completely - * arbitrary. - */ - if (!bsdtar->u_width) { - bsdtar->u_width = 6; - bsdtar->gs_width = 13; - } - if (!now) - time(&now); - fprintf(out, "%s %d ", - archive_entry_strmode(entry), - archive_entry_nlink(entry)); - - /* Use uname if it's present, else uid. */ - p = archive_entry_uname(entry); - if ((p == NULL) || (*p == '\0')) { - sprintf(tmp, "%lu ", - (unsigned long)archive_entry_uid(entry)); - p = tmp; - } - w = strlen(p); - if (w > bsdtar->u_width) - bsdtar->u_width = w; - fprintf(out, "%-*s ", (int)bsdtar->u_width, p); - - /* Use gname if it's present, else gid. */ - p = archive_entry_gname(entry); - if (p != NULL && p[0] != '\0') { - fprintf(out, "%s", p); - w = strlen(p); - } else { - sprintf(tmp, "%lu", - (unsigned long)archive_entry_gid(entry)); - w = strlen(tmp); - fprintf(out, "%s", tmp); - } - - /* - * Print device number or file size, right-aligned so as to make - * total width of group and devnum/filesize fields be gs_width. - * If gs_width is too small, grow it. - */ - if (archive_entry_filetype(entry) == AE_IFCHR - || archive_entry_filetype(entry) == AE_IFBLK) { - sprintf(tmp, "%lu,%lu", - (unsigned long)archive_entry_rdevmajor(entry), - (unsigned long)archive_entry_rdevminor(entry)); - } else { - strcpy(tmp, tar_i64toa(archive_entry_size(entry))); - } - if (w + strlen(tmp) >= bsdtar->gs_width) - bsdtar->gs_width = w+strlen(tmp)+1; - fprintf(out, "%*s", (int)(bsdtar->gs_width - w), tmp); - - /* Format the time using 'ls -l' conventions. */ - tim = archive_entry_mtime(entry); -#define HALF_YEAR (time_t)365 * 86400 / 2 -#if defined(_WIN32) && !defined(__CYGWIN__) -#define DAY_FMT "%d" /* Windows' strftime function does not support %e format. */ -#else -#define DAY_FMT "%e" /* Day number without leading zeros */ -#endif - if (tim < now - HALF_YEAR || tim > now + HALF_YEAR) - fmt = bsdtar->day_first ? DAY_FMT " %b %Y" : "%b " DAY_FMT " %Y"; - else - fmt = bsdtar->day_first ? DAY_FMT " %b %H:%M" : "%b " DAY_FMT " %H:%M"; - strftime(tmp, sizeof(tmp), fmt, localtime(&tim)); - fprintf(out, " %s ", tmp); - safe_fprintf(out, "%s", archive_entry_pathname(entry)); - - /* Extra information for links. */ - if (archive_entry_hardlink(entry)) /* Hard link */ - safe_fprintf(out, " link to %s", - archive_entry_hardlink(entry)); - else if (archive_entry_symlink(entry)) /* Symbolic link */ - safe_fprintf(out, " -> %s", archive_entry_symlink(entry)); -} - static int unmatched_inclusions_warn(struct archive *matching, const char *msg) { diff --git a/tar/subst.c b/tar/subst.c index fd6f8e222c6a7..4710e06a623a8 100644 --- a/tar/subst.c +++ b/tar/subst.c @@ -236,64 +236,66 @@ apply_substitution(struct bsdtar *bsdtar, const char *name, char **result, continue; } - if (regexec(&rule->re, name, 10, matches, 0)) - continue; - - got_match = 1; - print_match |= rule->print; - realloc_strncat(result, name, matches[0].rm_so); - - for (i = 0, j = 0; rule->result[i] != '\0'; ++i) { - if (rule->result[i] == '~') { - realloc_strncat(result, rule->result + j, i - j); - realloc_strncat(result, - name + matches[0].rm_so, - matches[0].rm_eo - matches[0].rm_so); - j = i + 1; - continue; - } - if (rule->result[i] != '\\') - continue; - - ++i; - c = rule->result[i]; - switch (c) { - case '~': - case '\\': - realloc_strncat(result, rule->result + j, i - j - 1); - j = i; + while (1) { + if (regexec(&rule->re, name, 10, matches, 0)) break; - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - realloc_strncat(result, rule->result + j, i - j - 1); - if ((size_t)(c - '0') > (size_t)(rule->re.re_nsub)) { - free(*result); - *result = NULL; - return -1; + + got_match = 1; + print_match |= rule->print; + realloc_strncat(result, name, matches[0].rm_so); + + for (i = 0, j = 0; rule->result[i] != '\0'; ++i) { + if (rule->result[i] == '~') { + realloc_strncat(result, rule->result + j, i - j); + realloc_strncat(result, + name + matches[0].rm_so, + matches[0].rm_eo - matches[0].rm_so); + j = i + 1; + continue; + } + if (rule->result[i] != '\\') + continue; + + ++i; + c = rule->result[i]; + switch (c) { + case '~': + case '\\': + realloc_strncat(result, rule->result + j, i - j - 1); + j = i; + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + realloc_strncat(result, rule->result + j, i - j - 1); + if ((size_t)(c - '0') > (size_t)(rule->re.re_nsub)) { + free(*result); + *result = NULL; + return -1; + } + realloc_strncat(result, name + matches[c - '0'].rm_so, matches[c - '0'].rm_eo - matches[c - '0'].rm_so); + j = i + 1; + break; + default: + /* Just continue; */ + break; } - realloc_strncat(result, name + matches[c - '0'].rm_so, matches[c - '0'].rm_eo - matches[c - '0'].rm_so); - j = i + 1; - break; - default: - /* Just continue; */ - break; - } - } + } - realloc_strcat(result, rule->result + j); + realloc_strcat(result, rule->result + j); - name += matches[0].rm_eo; + name += matches[0].rm_eo; - if (!rule->global) - break; + if (!rule->global) + break; + } } if (got_match) diff --git a/tar/test/CMakeLists.txt b/tar/test/CMakeLists.txt index 98f49e29298b6..9648e4892a3c0 100644 --- a/tar/test/CMakeLists.txt +++ b/tar/test/CMakeLists.txt @@ -18,11 +18,13 @@ IF(ENABLE_TAR AND ENABLE_TEST) test_extract_tar_gz.c test_extract_tar_lrz.c test_extract_tar_lz.c + test_extract_tar_lz4.c test_extract_tar_lzma.c test_extract_tar_lzo.c test_extract_tar_xz.c test_format_newc.c test_help.c + test_leading_slash.c test_option_C_upper.c test_option_H_upper.c test_option_L_upper.c @@ -40,12 +42,14 @@ IF(ENABLE_TAR AND ENABLE_TEST) test_option_k.c test_option_keep_newer_files.c test_option_lrzip.c + test_option_lz4.c test_option_lzma.c test_option_lzop.c test_option_n.c test_option_newer_than.c test_option_nodump.c test_option_older_than.c + test_option_passphrase.c test_option_q.c test_option_r.c test_option_s.c @@ -90,11 +94,12 @@ IF(ENABLE_TAR AND ENABLE_TEST) INCLUDE(${CMAKE_CURRENT_BINARY_DIR}/list.h) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) - INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/test_utils) + INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/test_utils) # Experimental new test handling ADD_CUSTOM_TARGET(run_bsdtar_test - COMMAND bsdtar_test -p ${BSDTAR} -r ${CMAKE_CURRENT_SOURCE_DIR}) + COMMAND bsdtar_test -p $<TARGET_FILE:bsdtar> + -r ${CMAKE_CURRENT_SOURCE_DIR}) ADD_DEPENDENCIES(run_bsdtar_test bsdtar) ADD_DEPENDENCIES(run_all_tests run_bsdtar_test) diff --git a/tar/test/main.c b/tar/test/main.c index f2dcaf4ae83b5..90801a98bc8fd 100644 --- a/tar/test/main.c +++ b/tar/test/main.c @@ -130,6 +130,16 @@ __FBSDID("$FreeBSD: src/usr.bin/tar/test/main.c,v 1.6 2008/11/05 06:40:53 kientz # include <crtdbg.h> #endif +/* Path to working directory for current test */ +const char *testworkdir; +#ifdef PROGRAM +/* Pathname of exe to be tested. */ +const char *testprogfile; +/* Name of exe to use in printf-formatted command strings. */ +/* On Windows, this includes leading/trailing quotes. */ +const char *testprog; +#endif + #if defined(_WIN32) && !defined(__CYGWIN__) static void *GetFunctionKernel32(const char *); static int my_CreateSymbolicLinkA(const char *, const char *, int); @@ -194,7 +204,7 @@ my_GetFileInformationByName(const char *path, BY_HANDLE_FILE_INFORMATION *bhfi) } #endif -#if defined(HAVE__CrtSetReportMode) +#if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__) static void invalid_parameter_handler(const wchar_t * expression, const wchar_t * function, const wchar_t * file, @@ -565,10 +575,10 @@ static void strdump(const char *e, const char *p, int ewidth, int utf8) while (*p != '\0') { unsigned int c = 0xff & *p++; switch (c) { - case '\a': printf("\a"); break; - case '\b': printf("\b"); break; - case '\n': printf("\n"); break; - case '\r': printf("\r"); break; + case '\a': logprintf("\\a"); break; + case '\b': logprintf("\\b"); break; + case '\n': logprintf("\\n"); break; + case '\r': logprintf("\\r"); break; default: if (c >= 32 && c < 127) logprintf("%c", c); @@ -771,6 +781,34 @@ assertion_equal_mem(const char *file, int line, return (0); } +/* Verify that a block of memory is filled with the specified byte. */ +int +assertion_memory_filled_with(const char *file, int line, + const void *_v1, const char *vd, + size_t l, const char *ld, + char b, const char *bd, void *extra) +{ + const char *v1 = (const char *)_v1; + size_t c = 0; + size_t i; + (void)ld; /* UNUSED */ + + assertion_count(file, line); + + for (i = 0; i < l; ++i) { + if (v1[i] == b) { + ++c; + } + } + if (c == l) + return (1); + + failure_start(file, line, "%s (size %d) not filled with %s", vd, (int)l, bd); + logprintf(" Only %d bytes were correct\n", (int)c); + failure_finish(extra); + return (0); +} + /* Verify that the named file exists and is empty. */ int assertion_empty_file(const char *filename, int line, const char *f1) @@ -1061,7 +1099,8 @@ assertion_file_contains_lines_any_order(const char *file, int line, free(expected); return (0); } - for (j = 0, p = buff; p < buff + buff_size; p += 1 + strlen(p)) { + for (j = 0, p = buff; p < buff + buff_size; + p += 1 + strlen(p)) { if (*p != '\0') { actual[j] = p; ++j; @@ -1913,6 +1952,18 @@ canGzip(void) * Can this platform run the lrzip program? */ int +canRunCommand(const char *cmd) +{ + static int tested = 0, value = 0; + if (!tested) { + tested = 1; + if (systemf("%s %s", cmd, redirectArgs) == 0) + value = 1; + } + return (value); +} + +int canLrzip(void) { static int tested = 0, value = 0; @@ -1925,6 +1976,21 @@ canLrzip(void) } /* + * Can this platform run the lz4 program? + */ +int +canLz4(void) +{ + static int tested = 0, value = 0; + if (!tested) { + tested = 1; + if (systemf("lz4 -V %s", redirectArgs) == 0) + value = 1; + } + return (value); +} + +/* * Can this platform run the lzip program? */ int @@ -2136,8 +2202,31 @@ slurpfile(size_t * sizep, const char *fmt, ...) return (p); } +/* + * Slurp a file into memory for ease of comparison and testing. + * Returns size of file in 'sizep' if non-NULL, null-terminates + * data in memory for ease of use. + */ +void +dumpfile(const char *filename, void *data, size_t len) +{ + ssize_t bytes_written; + FILE *f; + + f = fopen(filename, "wb"); + if (f == NULL) { + logprintf("Can't open file %s for writing\n", filename); + return; + } + bytes_written = fwrite(data, 1, len, f); + if (bytes_written < (ssize_t)len) + logprintf("Can't write file %s\n", filename); + fclose(f); +} + /* Read a uuencoded file from the reference directory, decode, and * write the result into the current directory. */ +#define VALID_UUDECODE(c) (c >= 32 && c <= 96) #define UUDECODE(c) (((c) - 0x20) & 0x3f) void extract_reference_file(const char *name) @@ -2161,7 +2250,6 @@ extract_reference_file(const char *name) break; } /* Now, decode the rest and write it. */ - /* Not a lot of error checking here; the input better be right. */ out = fopen(name, "wb"); while (fgets(buff, sizeof(buff), in) != NULL) { char *p = buff; @@ -2175,17 +2263,21 @@ extract_reference_file(const char *name) int n = 0; /* Write out 1-3 bytes from that. */ if (bytes > 0) { + assert(VALID_UUDECODE(p[0])); + assert(VALID_UUDECODE(p[1])); n = UUDECODE(*p++) << 18; n |= UUDECODE(*p++) << 12; fputc(n >> 16, out); --bytes; } if (bytes > 0) { + assert(VALID_UUDECODE(p[0])); n |= UUDECODE(*p++) << 6; fputc((n >> 8) & 0xFF, out); --bytes; } if (bytes > 0) { + assert(VALID_UUDECODE(p[0])); n |= UUDECODE(*p++); fputc(n & 0xFF, out); --bytes; @@ -2196,6 +2288,32 @@ extract_reference_file(const char *name) fclose(in); } +void +copy_reference_file(const char *name) +{ + char buff[1024]; + FILE *in, *out; + size_t rbytes; + + sprintf(buff, "%s/%s", refdir, name); + in = fopen(buff, "rb"); + failure("Couldn't open reference file %s", buff); + assert(in != NULL); + if (in == NULL) + return; + /* Now, decode the rest and write it. */ + /* Not a lot of error checking here; the input better be right. */ + out = fopen(name, "wb"); + while ((rbytes = fread(buff, 1, sizeof(buff), in)) > 0) { + if (fwrite(buff, 1, rbytes, out) != rbytes) { + logprintf("Error: fwrite\n"); + break; + } + } + fclose(out); + fclose(in); +} + int is_LargeInode(const char *file) { @@ -2217,6 +2335,14 @@ is_LargeInode(const char *file) return (ino > 0xffffffff); #endif } + +void +extract_reference_files(const char **names) +{ + while (names && *names) + extract_reference_file(*names++); +} + /* * * TEST management @@ -2246,7 +2372,7 @@ struct test_list_t tests[] = { * Summarize repeated failures in the just-completed test. */ static void -test_summarize(int failed) +test_summarize(int failed, int skips_num) { unsigned int i; @@ -2256,7 +2382,7 @@ test_summarize(int failed) fflush(stdout); break; case VERBOSITY_PASSFAIL: - printf(failed ? "FAIL\n" : "ok\n"); + printf(failed ? "FAIL\n" : skips_num ? "ok (S)\n" : "ok\n"); break; } @@ -2281,13 +2407,14 @@ test_run(int i, const char *tmpdir) char workdir[1024]; char logfilename[64]; int failures_before = failures; + int skips_before = skips; int oldumask; switch (verbosity) { case VERBOSITY_SUMMARY_ONLY: /* No per-test reports at all */ break; case VERBOSITY_PASSFAIL: /* rest of line will include ok/FAIL marker */ - printf("%3d: %-50s", i, tests[i].name); + printf("%3d: %-64s", i, tests[i].name); fflush(stdout); break; default: /* Title of test, details will follow */ @@ -2337,7 +2464,7 @@ test_run(int i, const char *tmpdir) } /* Report per-test summaries. */ tests[i].failures = failures - failures_before; - test_summarize(tests[i].failures); + test_summarize(tests[i].failures, skips - skips_before); /* Close the per-test log file. */ fclose(logfile); logfile = NULL; @@ -2480,6 +2607,7 @@ get_refdir(const char *d) failure: printf("Unable to locate known reference file %s\n", KNOWNREF); printf(" Checked following directories:\n%s\n", tried); + printf("Use -r option to specify full path to reference directory\n"); #if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG) DebugBreak(); #endif @@ -2516,7 +2644,7 @@ main(int argc, char **argv) while (pwd[strlen(pwd) - 1] == '\n') pwd[strlen(pwd) - 1] = '\0'; -#if defined(HAVE__CrtSetReportMode) +#if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__) /* To stop to run the default invalid parameter handler. */ _set_invalid_parameter_handler(invalid_parameter_handler); /* Disable annoying assertion message box. */ @@ -2563,7 +2691,7 @@ main(int argc, char **argv) exit(1); } memmove(testprogdir + strlen(pwd) + 1, testprogdir, - strlen(testprogdir)); + strlen(testprogdir) + 1); memcpy(testprogdir, pwd, strlen(pwd)); testprogdir[strlen(pwd)] = '/'; } diff --git a/tar/test/test.h b/tar/test/test.h index a0a9bb6f600c2..704a137ed3fb8 100644 --- a/tar/test/test.h +++ b/tar/test/test.h @@ -66,6 +66,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <ctype.h> #include <time.h> #ifdef HAVE_UNISTD_H #include <unistd.h> @@ -91,7 +92,7 @@ #endif /* Visual Studio */ -#ifdef _MSC_VER +#if defined(_MSC_VER) && _MSC_VER < 1900 #define snprintf sprintf_s #endif @@ -144,6 +145,9 @@ /* As above, but raw blocks of bytes. */ #define assertEqualMem(v1, v2, l) \ assertion_equal_mem(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (l), #l, NULL) +/* Assert that memory is full of a specified byte */ +#define assertMemoryFilledWith(v1, l, b) \ + assertion_memory_filled_with(__FILE__, __LINE__, (v1), #v1, (l), #l, (b), #b, NULL) /* Assert two files are the same. */ #define assertEqualFile(f1, f2) \ assertion_equal_file(__FILE__, __LINE__, (f1), (f2)) @@ -227,6 +231,7 @@ int assertion_empty_file(const char *, int, const char *); int assertion_equal_file(const char *, int, const char *, const char *); int assertion_equal_int(const char *, int, long long, const char *, long long, const char *, void *); int assertion_equal_mem(const char *, int, const void *, const char *, const void *, const char *, size_t, const char *, void *); +int assertion_memory_filled_with(const char *, int, const void *, const char *, size_t, const char *, char, const char *, void *); int assertion_equal_string(const char *, int, const char *v1, const char *, const char *v2, const char *, void *, int); int assertion_equal_wstring(const char *, int, const wchar_t *v1, const char *, const wchar_t *v2, const char *, void *); int assertion_file_atime(const char *, int, const char *, long, long); @@ -277,9 +282,15 @@ int canGrzip(void); /* Return true if this platform can run the "gzip" program. */ int canGzip(void); +/* Return true if this platform can run the specified command. */ +int canRunCommand(const char *); + /* Return true if this platform can run the "lrzip" program. */ int canLrzip(void); +/* Return true if this platform can run the "lz4" program. */ +int canLz4(void); + /* Return true if this platform can run the "lzip" program. */ int canLzip(void); @@ -302,21 +313,31 @@ int is_LargeInode(const char *); /* Supports printf-style args: slurpfile(NULL, "%s/myfile", refdir); */ char *slurpfile(size_t *, const char *fmt, ...); +/* Dump block of bytes to a file. */ +void dumpfile(const char *filename, void *, size_t); + /* Extracts named reference file to the current directory. */ void extract_reference_file(const char *); +/* Copies named reference file to the current directory. */ +void copy_reference_file(const char *); + +/* Extracts a list of files to the current directory. + * List must be NULL terminated. + */ +void extract_reference_files(const char **); /* Path to working directory for current test */ -const char *testworkdir; +extern const char *testworkdir; /* * Special interfaces for program test harness. */ /* Pathname of exe to be tested. */ -const char *testprogfile; +extern const char *testprogfile; /* Name of exe to use in printf-formatted command strings. */ /* On Windows, this includes leading/trailing quotes. */ -const char *testprog; +extern const char *testprog; #ifdef USE_DMALLOC #include <dmalloc.h> diff --git a/tar/test/test_extract.tar.lz4.uu b/tar/test/test_extract.tar.lz4.uu new file mode 100644 index 0000000000000..a10ac0275ad72 --- /dev/null +++ b/tar/test/test_extract.tar.lz4.uu @@ -0,0 +1,8 @@ +begin 644 test_extract.tar.lz4 +M!")-&&1PN:L```!O9FEL93$``0!+Z#`P,#8V-"``,#`Q-S4P"``#`@#_"3(S +M(#$R,#,R-S0P,C,T(#`Q,3,V-0`@,)<`2P("`+)U<W1A<@`P,&-U91$`#P(` +M!`\@``T"RP``W0```@`?($8`!`\"`'[!8V]N=&5N=',@;V8@#`(O+@JD`'X/ +M`@#_2@#T`1\R80%,#P`$&B$T-0`$+S<P``3_9!\R``3_W0\"`/___^M0```` +*````````J.?`=``` +` +end diff --git a/tar/test/test_extract_tar_lz4.c b/tar/test/test_extract_tar_lz4.c new file mode 100644 index 0000000000000..150d57d71551a --- /dev/null +++ b/tar/test/test_extract_tar_lz4.c @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 2012,2014 Michihiro NAKAJIMA + * 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. + */ +#include "test.h" +__FBSDID("$FreeBSD$"); + +DEFINE_TEST(test_extract_tar_lz4) +{ + const char *reffile = "test_extract.tar.lz4"; + int f; + + extract_reference_file(reffile); + f = systemf("%s -tf %s >test.out 2>test.err", testprog, reffile); + if (f == 0 || canLz4()) { + assertEqualInt(0, systemf("%s -xf %s >test.out 2>test.err", + testprog, reffile)); + + assertFileExists("file1"); + assertTextFileContents("contents of file1.\n", "file1"); + assertFileExists("file2"); + assertTextFileContents("contents of file2.\n", "file2"); + assertEmptyFile("test.out"); + assertEmptyFile("test.err"); + } else { + skipping("It seems lz4 is not supported on this platform"); + } +} diff --git a/tar/test/test_leading_slash.c b/tar/test/test_leading_slash.c new file mode 100644 index 0000000000000..a8921ebcbae86 --- /dev/null +++ b/tar/test/test_leading_slash.c @@ -0,0 +1,49 @@ +/*- + * Copyright (c) 2003-2014 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. + */ +#include "test.h" +__FBSDID("$FreeBSD$"); + +DEFINE_TEST(test_leading_slash) +{ + const char *reffile = "test_leading_slash.tar"; + char *errfile; + size_t errfile_size; + const char *expected_errmsg = "Removing leading '/' from member names"; + + extract_reference_file(reffile); + assertEqualInt(0, systemf("%s -xf %s >test.out 2>test.err", testprog, reffile)); + assertFileExists("foo/file"); + assertTextFileContents("foo\x0a", "foo/file"); + assertTextFileContents("foo\x0a", "foo/hardlink"); + assertIsHardlink("foo/file", "foo/hardlink"); + assertEmptyFile("test.out"); + + /* Verify the error output contains the expected text somewhere in it */ + if (assertFileExists("test.err")) { + errfile = slurpfile(&errfile_size, "test.err"); + assert(strstr(errfile, expected_errmsg) != NULL); + } +} + diff --git a/tar/test/test_leading_slash.tar.uu b/tar/test/test_leading_slash.tar.uu new file mode 100644 index 0000000000000..eeb27c3bbf405 --- /dev/null +++ b/tar/test/test_leading_slash.tar.uu @@ -0,0 +1,60 @@ +begin 644 test_leading_slash.tar +M+V9O;R]F:6QE```````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````#`P,#8T-"``,#`P,#`P(``P,#`P,#`@`#`P,#`P,#`P,#`T +M(#$R-#$V,S4U-34V(#`Q,C8V-P`@,``````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````````````!U<W1A<@`P,')O;W0` +M````````````````````````````````````=VAE96P````````````````` +M```````````````````P,#`P,#`@`#`P,#`P,"`````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````!F;V\*```````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````````"]F;V\O:&%R9&QI +M;FL````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````P +M,#`V-#0@`#`P,#`P,"``,#`P,#`P(``P,#`P,#`P,#`P,"`Q,C0Q-C,U-34U +M-B`P,34R-#,`(#$O9F]O+V9I;&4````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````=7-T87(`,#!R;V]T```````````````` +M`````````````````````'=H965L```````````````````````````````` +M````,#`P,#`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```````````````````````````````````````````````````````````` +H```````````````````````````````````````````````````````` +` +end diff --git a/tar/test/test_option_X_upper.c b/tar/test/test_option_X_upper.c index 1aa21fc90a670..4916af2970e81 100644 --- a/tar/test/test_option_X_upper.c +++ b/tar/test/test_option_X_upper.c @@ -142,4 +142,18 @@ DEFINE_TEST(test_option_X_upper) assertEmptyFile("test.out"); assertEmptyFile("test.err"); assertChdir(".."); + + /* Test 8: with empty exclusions file */ + assertMakeDir("test8", 0755); + assertChdir("test8"); + assertMakeFile("exclusions", 0644, ""); + assertEqualInt(0, + systemf("%s -xf ../archive.tar -X exclusions >test.out 2>test.err", testprog)); + assertFileContents("file1", 5, "file1"); + assertFileContents("file2", 5, "file2"); + assertFileContents("file3a", 6, "file3a"); + assertFileContents("file4a", 6, "file4a"); + assertEmptyFile("test.out"); + assertEmptyFile("test.err"); + assertChdir(".."); } diff --git a/tar/test/test_option_b.c b/tar/test/test_option_b.c index be2ae65c75a80..81f50be8355eb 100644 --- a/tar/test/test_option_b.c +++ b/tar/test/test_option_b.c @@ -25,18 +25,25 @@ #include "test.h" __FBSDID("$FreeBSD$"); +#define USTAR_OPT " --format=ustar" + DEFINE_TEST(test_option_b) { + char *testprog_ustar; + assertMakeFile("file1", 0644, "file1"); if (systemf("cat file1 > test_cat.out 2> test_cat.err") != 0) { skipping("Platform doesn't have cat"); return; } + testprog_ustar = malloc(strlen(testprog) + sizeof(USTAR_OPT) + 1); + strcpy(testprog_ustar, testprog); + strcat(testprog_ustar, USTAR_OPT); /* * Bsdtar does not pad if the output is going directly to a disk file. */ - assertEqualInt(0, systemf("%s -cf archive1.tar file1 >test1.out 2>test1.err", testprog)); + assertEqualInt(0, systemf("%s -cf archive1.tar file1 >test1.out 2>test1.err", testprog_ustar)); failure("bsdtar does not pad archives written directly to regular files"); assertFileSize("archive1.tar", 2048); assertEmptyFile("test1.out"); @@ -46,24 +53,24 @@ DEFINE_TEST(test_option_b) * Bsdtar does pad to the block size if the output is going to a socket. */ /* Default is -b 20 */ - assertEqualInt(0, systemf("%s -cf - file1 2>test2.err | cat >archive2.tar ", testprog)); + assertEqualInt(0, systemf("%s -cf - file1 2>test2.err | cat >archive2.tar ", testprog_ustar)); failure("bsdtar does pad archives written to pipes"); assertFileSize("archive2.tar", 10240); assertEmptyFile("test2.err"); - assertEqualInt(0, systemf("%s -cf - -b 20 file1 2>test3.err | cat >archive3.tar ", testprog)); + assertEqualInt(0, systemf("%s -cf - -b 20 file1 2>test3.err | cat >archive3.tar ", testprog_ustar)); assertFileSize("archive3.tar", 10240); assertEmptyFile("test3.err"); - assertEqualInt(0, systemf("%s -cf - -b 10 file1 2>test4.err | cat >archive4.tar ", testprog)); + assertEqualInt(0, systemf("%s -cf - -b 10 file1 2>test4.err | cat >archive4.tar ", testprog_ustar)); assertFileSize("archive4.tar", 5120); assertEmptyFile("test4.err"); - assertEqualInt(0, systemf("%s -cf - -b 1 file1 2>test5.err | cat >archive5.tar ", testprog)); + assertEqualInt(0, systemf("%s -cf - -b 1 file1 2>test5.err | cat >archive5.tar ", testprog_ustar)); assertFileSize("archive5.tar", 2048); assertEmptyFile("test5.err"); - assertEqualInt(0, systemf("%s -cf - -b 8192 file1 2>test6.err | cat >archive6.tar ", testprog)); + assertEqualInt(0, systemf("%s -cf - -b 8192 file1 2>test6.err | cat >archive6.tar ", testprog_ustar)); assertFileSize("archive6.tar", 4194304); assertEmptyFile("test6.err"); diff --git a/tar/test/test_option_lz4.c b/tar/test/test_option_lz4.c new file mode 100644 index 0000000000000..5dc945216300b --- /dev/null +++ b/tar/test/test_option_lz4.c @@ -0,0 +1,74 @@ +/*- + * Copyright (c) 2014 Michihiro NAKAJIMA + * 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. + */ +#include "test.h" +__FBSDID("$FreeBSD$"); + +DEFINE_TEST(test_option_lz4) +{ + char *p; + int r; + size_t s; + + /* Create a file. */ + assertMakeFile("f", 0644, "a"); + + /* Archive it with lz4 compression. */ + r = systemf("%s -cf - --lz4 f >archive.out 2>archive.err", + testprog); + p = slurpfile(&s, "archive.err"); + p[s] = '\0'; + if (r != 0) { + if (strstr(p, "Unsupported compression") != NULL) { + skipping("This version of bsdtar was compiled " + "without lz4 support"); + return; + } + /* POSIX permits different handling of the spawnp + * system call used to launch the subsidiary + * program: */ + /* Some systems fail immediately to spawn the new process. */ + if (strstr(p, "Can't launch") != NULL && !canLz4()) { + skipping("This version of bsdtar uses an external lz4 program " + "but no such program is available on this system."); + return; + } + /* Some systems successfully spawn the new process, + * but fail to exec a program within that process. + * This results in failure at the first attempt to + * write. */ + if (strstr(p, "Can't write") != NULL && !canLz4()) { + skipping("This version of bsdtar uses an external lz4 program " + "but no such program is available on this system."); + return; + } + failure("--lz4 option is broken: %s", p); + assertEqualInt(r, 0); + return; + } + /* Check that the archive file has an lz4 signature. */ + p = slurpfile(&s, "archive.out"); + assert(s > 2); + assertEqualMem(p, "\x04\x22\x4d\x18", 4); +} diff --git a/tar/test/test_option_passphrase.c b/tar/test/test_option_passphrase.c new file mode 100644 index 0000000000000..337292c95bfcb --- /dev/null +++ b/tar/test/test_option_passphrase.c @@ -0,0 +1,43 @@ +/*- + * Copyright (c) 2014 Michihiro NAKAJIMA + * 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. + */ +#include "test.h" +__FBSDID("$FreeBSD$"); + +DEFINE_TEST(test_option_passphrase) +{ + const char *reffile = "test_option_passphrase.zip"; + + extract_reference_file(reffile); + failure("--passphrase option is broken"); + assertEqualInt(0, systemf( + "%s --passphrase pass1 -xf %s >test.out 2>test.err", + testprog, reffile)); + assertFileExists("file1"); + assertTextFileContents("contents of file1.\n", "file1"); + assertFileExists("file2"); + assertTextFileContents("contents of file2.\n", "file2"); + assertEmptyFile("test.out"); + assertEmptyFile("test.err"); +} diff --git a/tar/test/test_option_passphrase.zip.uu b/tar/test/test_option_passphrase.zip.uu new file mode 100644 index 0000000000000..021ae8585fe86 --- /dev/null +++ b/tar/test/test_option_passphrase.zip.uu @@ -0,0 +1,12 @@ +begin 644 test_option_passphrase.zip +M4$L#!`H`"0```#B91$7D$C4,'P```!,````%`!P`9FEL93%55`D``VS'+U0" +MQR]4=7@+``$$]0$```04````BHPD*"^*I04=XKI\_FQ*TE+#),TD7TTKSP/7 +MR6R35%!+!PCD$C4,'P```!,```!02P,$"@`)````09E$1;VL<PX?````$P`` +M``4`'`!F:6QE,E54"0`#><<O5`+'+U1U>`L``03U`0``!!0```!D#6Z\@CI8 +MV1GIJO5TISQF^I:7.;Y3<-G3$YOCL(C_4$L'"+VL<PX?````$P```%!+`0(> +M`PH`"0```#B91$7D$C4,'P```!,````%`!@```````$```"D@0````!F:6QE +M,554!0`#;,<O5'5X"P`!!/4!```$%````%!+`0(>`PH`"0```$&91$6]K',. +M'P```!,````%`!@```````$```"D@6X```!F:6QE,E54!0`#><<O5'5X"P`! +@!/4!```$%````%!+!08``````@`"`)8```#<```````` +` +end diff --git a/tar/test/test_option_s.c b/tar/test/test_option_s.c index a1f8697e32b2c..ee8332f34fd58 100644 --- a/tar/test/test_option_s.c +++ b/tar/test/test_option_s.c @@ -82,9 +82,9 @@ DEFINE_TEST(test_option_s) */ assertMakeDir("test4", 0755); systemf("%s -cf test4.tar in/d1/foo in/d1/bar", - testprog, testprog); + testprog); systemf("%s -xf test4.tar -s /foo/bar/ -s }bar}baz} -C test4", - testprog, testprog); + testprog); assertFileContents("foo", 3, "test4/in/d1/bar"); assertFileContents("bar", 3, "test4/in/d1/baz"); @@ -258,4 +258,23 @@ DEFINE_TEST(test_option_s) assertFileContents("foo", 3, "test13a/in/d1/hardlink2"); assertIsHardlink("test13a/in/d1/foo", "test13a/in/d1/hardlink2"); /* TODO: See above; expand this test to verify renames at creation. */ + + /* + * Test 14: Global substitutions when extracting archive. + */ + /* Global substitution. */ + assertMakeDir("test14", 0755); + systemf("%s -cf test14.tar in/d1/foo in/d1/bar", + testprog); + systemf("%s -xf test14.tar -s /o/z/g -s /bar/baz/ -C test14", + testprog); + assertFileContents("foo", 3, "test14/in/d1/fzz"); + assertFileContents("bar", 3, "test14/in/d1/baz"); + /* Singular substitution. */ + systemf("%s -cf test14.tar in/d1/foo in/d1/bar", + testprog); + systemf("%s -xf test14.tar -s /o/z/ -s /bar/baz/ -C test14", + testprog); + assertFileContents("foo", 3, "test14/in/d1/fzo"); + assertFileContents("bar", 3, "test14/in/d1/baz"); } diff --git a/tar/test/test_version.c b/tar/test/test_version.c index 42472d1bc264c..5474261d207af 100644 --- a/tar/test/test_version.c +++ b/tar/test/test_version.c @@ -87,6 +87,11 @@ DEFINE_TEST(test_version) /* Skip a single trailing a,b,c, or d. */ if (*q == 'a' || *q == 'b' || *q == 'c' || *q == 'd') ++q; + /* Skip arbitrary third-party version numbers. */ + while (s > 0 && (*q == ' ' || *q == '/' || *q == '.' || isalnum(*q))) { + ++q; + --s; + } /* All terminated by end-of-line. */ assert(s >= 1); /* Skip an optional CR character (e.g., Windows) */ diff --git a/tar/util.c b/tar/util.c index b1b9b93d0e936..9ff22f2b61546 100644 --- a/tar/util.c +++ b/tar/util.c @@ -61,6 +61,7 @@ __FBSDID("$FreeBSD: src/usr.bin/tar/util.c,v 1.23 2008/12/15 06:00:25 kientzle E #include "bsdtar.h" #include "err.h" +#include "passphrase.h" static size_t bsdtar_expand_char(char *, size_t, char); static const char *strip_components(const char *path, int elements); @@ -372,20 +373,107 @@ strip_components(const char *p, int elements) } } +static void +warn_strip_leading_char(struct bsdtar *bsdtar, const char *c) +{ + if (!bsdtar->warned_lead_slash) { + lafe_warnc(0, + "Removing leading '%c' from member names", + c[0]); + bsdtar->warned_lead_slash = 1; + } +} + +static void +warn_strip_drive_letter(struct bsdtar *bsdtar) +{ + if (!bsdtar->warned_lead_slash) { + lafe_warnc(0, + "Removing leading drive letter from " + "member names"); + bsdtar->warned_lead_slash = 1; + } +} + +/* + * Convert absolute path to non-absolute path by skipping leading + * absolute path prefixes. + */ +static const char* +strip_absolute_path(struct bsdtar *bsdtar, const char *p) +{ + const char *rp; + + /* Remove leading "//./" or "//?/" or "//?/UNC/" + * (absolute path prefixes used by Windows API) */ + if ((p[0] == '/' || p[0] == '\\') && + (p[1] == '/' || p[1] == '\\') && + (p[2] == '.' || p[2] == '?') && + (p[3] == '/' || p[3] == '\\')) + { + if (p[2] == '?' && + (p[4] == 'U' || p[4] == 'u') && + (p[5] == 'N' || p[5] == 'n') && + (p[6] == 'C' || p[6] == 'c') && + (p[7] == '/' || p[7] == '\\')) + p += 8; + else + p += 4; + warn_strip_drive_letter(bsdtar); + } + + /* Remove multiple leading slashes and Windows drive letters. */ + do { + rp = p; + if (((p[0] >= 'a' && p[0] <= 'z') || + (p[0] >= 'A' && p[0] <= 'Z')) && + p[1] == ':') { + p += 2; + warn_strip_drive_letter(bsdtar); + } + + /* Remove leading "/../", "/./", "//", etc. */ + while (p[0] == '/' || p[0] == '\\') { + if (p[1] == '.' && + p[2] == '.' && + (p[3] == '/' || p[3] == '\\')) { + p += 3; /* Remove "/..", leave "/" for next pass. */ + } else if (p[1] == '.' && + (p[2] == '/' || p[2] == '\\')) { + p += 2; /* Remove "/.", leave "/" for next pass. */ + } else + p += 1; /* Remove "/". */ + warn_strip_leading_char(bsdtar, rp); + } + } while (rp != p); + + return (p); +} + /* * Handle --strip-components and any future path-rewriting options. * Returns non-zero if the pathname should not be extracted. * + * Note: The rewrites are applied uniformly to pathnames and hardlink + * names but not to symlink bodies. This is deliberate: Symlink + * bodies are not necessarily filenames. Even when they are, they + * need to be interpreted relative to the directory containing them, + * so simple rewrites like this are rarely appropriate. + * * TODO: Support pax-style regex path rewrites. */ int edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry) { const char *name = archive_entry_pathname(entry); + const char *original_name = name; + const char *hardlinkname = archive_entry_hardlink(entry); + const char *original_hardlinkname = hardlinkname; #if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H) char *subst_name; int r; + /* Apply user-specified substitution to pathname. */ r = apply_substitution(bsdtar, name, &subst_name, 0, 0); if (r == -1) { lafe_warnc(0, "Invalid substitution, skipping entry"); @@ -399,10 +487,12 @@ edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry) } else free(subst_name); name = archive_entry_pathname(entry); + original_name = name; } - if (archive_entry_hardlink(entry)) { - r = apply_substitution(bsdtar, archive_entry_hardlink(entry), &subst_name, 0, 1); + /* Apply user-specified substitution to hardlink target. */ + if (hardlinkname != NULL) { + r = apply_substitution(bsdtar, hardlinkname, &subst_name, 0, 1); if (r == -1) { lafe_warnc(0, "Invalid substitution, skipping entry"); return 1; @@ -411,7 +501,11 @@ edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry) archive_entry_copy_hardlink(entry, subst_name); free(subst_name); } + hardlinkname = archive_entry_hardlink(entry); + original_hardlinkname = hardlinkname; } + + /* Apply user-specified substitution to symlink body. */ if (archive_entry_symlink(entry) != NULL) { r = apply_substitution(bsdtar, archive_entry_symlink(entry), &subst_name, 1, 0); if (r == -1) { @@ -427,94 +521,41 @@ edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry) /* Strip leading dir names as per --strip-components option. */ if (bsdtar->strip_components > 0) { - const char *linkname = archive_entry_hardlink(entry); - name = strip_components(name, bsdtar->strip_components); if (name == NULL) return (1); - if (linkname != NULL) { - linkname = strip_components(linkname, + if (hardlinkname != NULL) { + hardlinkname = strip_components(hardlinkname, bsdtar->strip_components); - if (linkname == NULL) + if (hardlinkname == NULL) return (1); - archive_entry_copy_hardlink(entry, linkname); } } - /* By default, don't write or restore absolute pathnames. */ if (!bsdtar->option_absolute_paths) { - const char *rp, *p = name; - int slashonly = 1; - - /* Remove leading "//./" or "//?/" or "//?/UNC/" - * (absolute path prefixes used by Windows API) */ - if ((p[0] == '/' || p[0] == '\\') && - (p[1] == '/' || p[1] == '\\') && - (p[2] == '.' || p[2] == '?') && - (p[3] == '/' || p[3] == '\\')) - { - if (p[2] == '?' && - (p[4] == 'U' || p[4] == 'u') && - (p[5] == 'N' || p[5] == 'n') && - (p[6] == 'C' || p[6] == 'c') && - (p[7] == '/' || p[7] == '\\')) - p += 8; - else - p += 4; - slashonly = 0; - } - do { - rp = p; - /* Remove leading drive letter from archives created - * on Windows. */ - if (((p[0] >= 'a' && p[0] <= 'z') || - (p[0] >= 'A' && p[0] <= 'Z')) && - p[1] == ':') { - p += 2; - slashonly = 0; - } - /* Remove leading "/../", "//", etc. */ - while (p[0] == '/' || p[0] == '\\') { - if (p[1] == '.' && p[2] == '.' && - (p[3] == '/' || p[3] == '\\')) { - p += 3; /* Remove "/..", leave "/" - * for next pass. */ - slashonly = 0; - } else - p += 1; /* Remove "/". */ - } - } while (rp != p); - - if (p != name && !bsdtar->warned_lead_slash) { - /* Generate a warning the first time this happens. */ - if (slashonly) - lafe_warnc(0, - "Removing leading '%c' from member names", - name[0]); - else - lafe_warnc(0, - "Removing leading drive letter from " - "member names"); - bsdtar->warned_lead_slash = 1; - } - - /* Special case: Stripping everything yields ".". */ - if (*p == '\0') + /* By default, don't write or restore absolute pathnames. */ + name = strip_absolute_path(bsdtar, name); + if (*name == '\0') name = "."; - else - name = p; + + if (hardlinkname != NULL) { + hardlinkname = strip_absolute_path(bsdtar, hardlinkname); + if (*hardlinkname == '\0') + return (1); + } } else { /* Strip redundant leading '/' characters. */ while (name[0] == '/' && name[1] == '/') name++; } - /* Safely replace name in archive_entry. */ - if (name != archive_entry_pathname(entry)) { - char *q = strdup(name); - archive_entry_copy_pathname(entry, q); - free(q); + /* Replace name in archive_entry. */ + if (name != original_name) { + archive_entry_copy_pathname(entry, name); + } + if (hardlinkname != original_hardlinkname) { + archive_entry_copy_hardlink(entry, hardlinkname); } return (0); } @@ -581,3 +622,128 @@ pathcmp(const char *a, const char *b) /* They're really different, return the correct sign. */ return (*(const unsigned char *)a - *(const unsigned char *)b); } + +#define PPBUFF_SIZE 1024 +const char * +passphrase_callback(struct archive *a, void *_client_data) +{ + struct bsdtar *bsdtar = (struct bsdtar *)_client_data; + (void)a; /* UNUSED */ + + if (bsdtar->ppbuff == NULL) { + bsdtar->ppbuff = malloc(PPBUFF_SIZE); + if (bsdtar->ppbuff == NULL) + lafe_errc(1, errno, "Out of memory"); + } + return lafe_readpassphrase("Enter passphrase:", + bsdtar->ppbuff, PPBUFF_SIZE); +} + +void +passphrase_free(char *ppbuff) +{ + if (ppbuff != NULL) { + memset(ppbuff, 0, PPBUFF_SIZE); + free(ppbuff); + } +} + +/* + * Display information about the current file. + * + * The format here roughly duplicates the output of 'ls -l'. + * This is based on SUSv2, where 'tar tv' is documented as + * listing additional information in an "unspecified format," + * and 'pax -l' is documented as using the same format as 'ls -l'. + */ +void +list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry) +{ + char tmp[100]; + size_t w; + const char *p; + const char *fmt; + time_t tim; + static time_t now; + + /* + * We avoid collecting the entire list in memory at once by + * listing things as we see them. However, that also means we can't + * just pre-compute the field widths. Instead, we start with guesses + * and just widen them as necessary. These numbers are completely + * arbitrary. + */ + if (!bsdtar->u_width) { + bsdtar->u_width = 6; + bsdtar->gs_width = 13; + } + if (!now) + time(&now); + fprintf(out, "%s %d ", + archive_entry_strmode(entry), + archive_entry_nlink(entry)); + + /* Use uname if it's present, else uid. */ + p = archive_entry_uname(entry); + if ((p == NULL) || (*p == '\0')) { + sprintf(tmp, "%lu ", + (unsigned long)archive_entry_uid(entry)); + p = tmp; + } + w = strlen(p); + if (w > bsdtar->u_width) + bsdtar->u_width = w; + fprintf(out, "%-*s ", (int)bsdtar->u_width, p); + + /* Use gname if it's present, else gid. */ + p = archive_entry_gname(entry); + if (p != NULL && p[0] != '\0') { + fprintf(out, "%s", p); + w = strlen(p); + } else { + sprintf(tmp, "%lu", + (unsigned long)archive_entry_gid(entry)); + w = strlen(tmp); + fprintf(out, "%s", tmp); + } + + /* + * Print device number or file size, right-aligned so as to make + * total width of group and devnum/filesize fields be gs_width. + * If gs_width is too small, grow it. + */ + if (archive_entry_filetype(entry) == AE_IFCHR + || archive_entry_filetype(entry) == AE_IFBLK) { + sprintf(tmp, "%lu,%lu", + (unsigned long)archive_entry_rdevmajor(entry), + (unsigned long)archive_entry_rdevminor(entry)); + } else { + strcpy(tmp, tar_i64toa(archive_entry_size(entry))); + } + if (w + strlen(tmp) >= bsdtar->gs_width) + bsdtar->gs_width = w+strlen(tmp)+1; + fprintf(out, "%*s", (int)(bsdtar->gs_width - w), tmp); + + /* Format the time using 'ls -l' conventions. */ + tim = archive_entry_mtime(entry); +#define HALF_YEAR (time_t)365 * 86400 / 2 +#if defined(_WIN32) && !defined(__CYGWIN__) +#define DAY_FMT "%d" /* Windows' strftime function does not support %e format. */ +#else +#define DAY_FMT "%e" /* Day number without leading zeros */ +#endif + if (tim < now - HALF_YEAR || tim > now + HALF_YEAR) + fmt = bsdtar->day_first ? DAY_FMT " %b %Y" : "%b " DAY_FMT " %Y"; + else + fmt = bsdtar->day_first ? DAY_FMT " %b %H:%M" : "%b " DAY_FMT " %H:%M"; + strftime(tmp, sizeof(tmp), fmt, localtime(&tim)); + fprintf(out, " %s ", tmp); + safe_fprintf(out, "%s", archive_entry_pathname(entry)); + + /* Extra information for links. */ + if (archive_entry_hardlink(entry)) /* Hard link */ + safe_fprintf(out, " link to %s", + archive_entry_hardlink(entry)); + else if (archive_entry_symlink(entry)) /* Symbolic link */ + safe_fprintf(out, " -> %s", archive_entry_symlink(entry)); +} diff --git a/tar/write.c b/tar/write.c index 40d2fb0a9f52c..53b63834c419a 100644 --- a/tar/write.c +++ b/tar/write.c @@ -236,6 +236,13 @@ tar_mode_c(struct bsdtar *bsdtar) } set_writer_options(bsdtar, a); + if (bsdtar->passphrase != NULL) + r = archive_write_set_passphrase(a, bsdtar->passphrase); + else + r = archive_write_set_passphrase_callback(a, bsdtar, + &passphrase_callback); + if (r != ARCHIVE_OK) + lafe_errc(1, 0, "%s", archive_error_string(a)); if (ARCHIVE_OK != archive_write_open_filename(a, bsdtar->filename)) lafe_errc(1, 0, "%s", archive_error_string(a)); write_archive(a, bsdtar); @@ -647,7 +654,15 @@ append_archive_filename(struct bsdtar *bsdtar, struct archive *a, ina = archive_read_new(); archive_read_support_format_all(ina); archive_read_support_filter_all(ina); - set_reader_options(bsdtar, a); + set_reader_options(bsdtar, ina); + archive_read_set_options(ina, "mtree:checkfs"); + if (bsdtar->passphrase != NULL) + rc = archive_read_add_passphrase(a, bsdtar->passphrase); + else + rc = archive_read_set_passphrase_callback(ina, bsdtar, + &passphrase_callback); + if (rc != ARCHIVE_OK) + lafe_errc(1, 0, "%s", archive_error_string(a)); if (archive_read_open_filename(ina, filename, bsdtar->bytes_per_block)) { lafe_warnc(0, "%s", archive_error_string(ina)); @@ -679,7 +694,10 @@ append_archive(struct bsdtar *bsdtar, struct archive *a, struct archive *ina) if (bsdtar->option_interactive && !yes("copy '%s'", archive_entry_pathname(in_entry))) continue; - if (bsdtar->verbose) + if (bsdtar->verbose > 1) { + safe_fprintf(stderr, "a "); + list_item_verbose(bsdtar, stderr, in_entry); + } else if (bsdtar->verbose > 0) safe_fprintf(stderr, "a %s", archive_entry_pathname(in_entry)); if (need_report()) @@ -899,11 +917,15 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) if (edit_pathname(bsdtar, entry)) continue; - /* Display entry as we process it. - * This format is required by SUSv2. */ - if (bsdtar->verbose) + /* Display entry as we process it. */ + if (bsdtar->verbose > 1) { + safe_fprintf(stderr, "a "); + list_item_verbose(bsdtar, stderr, entry); + } else if (bsdtar->verbose > 0) { + /* This format is required by SUSv2. */ safe_fprintf(stderr, "a %s", archive_entry_pathname(entry)); + } /* Non-regular files get archived with zero size. */ if (archive_entry_filetype(entry) != AE_IFREG) @@ -947,11 +969,15 @@ write_entry(struct bsdtar *bsdtar, struct archive *a, e = archive_write_header(a, entry); if (e != ARCHIVE_OK) { - if (!bsdtar->verbose) + if (bsdtar->verbose > 1) { + safe_fprintf(stderr, "a "); + list_item_verbose(bsdtar, stderr, entry); + lafe_warnc(0, ": %s", archive_error_string(a)); + } else if (bsdtar->verbose > 0) { lafe_warnc(0, "%s: %s", archive_entry_pathname(entry), archive_error_string(a)); - else + } else fprintf(stderr, ": %s", archive_error_string(a)); } |
