git: bd5e624a8614 - main - libarchive: merge from vendor branch
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 13 Dec 2022 19:45:04 UTC
The branch main has been updated by mm: URL: https://cgit.FreeBSD.org/src/commit/?id=bd5e624a861433dee76fe00a8acedc9564425332 commit bd5e624a861433dee76fe00a8acedc9564425332 Merge: 8d0ed56646f0 b5a00e61e90d Author: Martin Matuska <mm@FreeBSD.org> AuthorDate: 2022-12-13 19:21:13 +0000 Commit: Martin Matuska <mm@FreeBSD.org> CommitDate: 2022-12-13 19:21:13 +0000 libarchive: merge from vendor branch Libarchive 3.6.2 Important bug fixes: rar5 reader: fix possible garbled output with bsdtar -O (#1745) mtree reader: support reading mtree files with tabs (#1783) various small fixes for issues found by CodeQL MFC after: 2 weeks PR: 286306 (exp-run) contrib/libarchive/NEWS | 2 + contrib/libarchive/README.md | 5 ++ contrib/libarchive/cpio/test/test_option_t.c | 24 +++++- contrib/libarchive/libarchive/archive.h | 6 +- contrib/libarchive/libarchive/archive_digest.c | 16 ++-- contrib/libarchive/libarchive/archive_entry.c | 14 ++++ contrib/libarchive/libarchive/archive_entry.h | 4 +- contrib/libarchive/libarchive/archive_hmac.c | 29 ++++++++ .../libarchive/libarchive/archive_hmac_private.h | 7 ++ contrib/libarchive/libarchive/archive_platform.h | 3 +- .../libarchive/archive_read_disk_posix.c | 7 +- .../libarchive/archive_read_support_filter_lz4.c | 6 ++ .../libarchive/archive_read_support_filter_lzop.c | 2 + .../libarchive/archive_read_support_filter_xz.c | 2 + .../libarchive/archive_read_support_format_7zip.c | 8 +- .../libarchive/archive_read_support_format_cab.c | 6 +- .../archive_read_support_format_iso9660.c | 2 +- .../libarchive/archive_read_support_format_lha.c | 6 +- .../libarchive/archive_read_support_format_mtree.c | 24 ++++-- .../libarchive/archive_read_support_format_rar.c | 10 +++ .../libarchive/archive_read_support_format_rar5.c | 9 +++ .../libarchive/archive_read_support_format_tar.c | 20 ++++- .../libarchive/archive_read_support_format_xar.c | 4 + contrib/libarchive/libarchive/archive_string.c | 6 +- contrib/libarchive/libarchive/archive_write.c | 8 ++ .../libarchive/archive_write_disk_posix.c | 4 +- contrib/libarchive/libarchive/archive_write_open.3 | 1 + .../libarchive/archive_write_set_format_pax.c | 2 +- contrib/libarchive/libarchive/filter_fork_posix.c | 2 +- .../libarchive/test/test_acl_platform_nfs4.c | 4 +- .../libarchive/test/test_archive_api_feature.c | 2 +- .../libarchive/test/test_archive_match_time.c | 30 ++++++++ .../test/test_archive_string_conversion.c | 1 + .../libarchive/test/test_read_format_mtree.c | 30 ++++++++ .../libarchive/test/test_read_format_rar5.c | 43 ++++++++--- .../test/test_read_format_tar_invalid_pax_size.c | 53 +++++++++++++ .../test_read_format_tar_invalid_pax_size.tar.uu | 38 ++++++++++ .../libarchive/test/test_read_truncated_filter.c | 4 +- .../libarchive/libarchive/test/test_sparse_basic.c | 4 +- .../libarchive/libarchive/test/test_tar_large.c | 4 +- .../libarchive/test/test_write_disk_secure744.c | 2 +- .../libarchive/test/test_write_filter_b64encode.c | 8 +- .../libarchive/test/test_write_filter_bzip2.c | 12 +-- .../libarchive/test/test_write_filter_compress.c | 4 +- .../libarchive/test/test_write_filter_gzip.c | 12 +-- .../libarchive/test/test_write_filter_lrzip.c | 4 +- .../libarchive/test/test_write_filter_lz4.c | 16 ++-- .../libarchive/test/test_write_filter_lzip.c | 12 +-- .../libarchive/test/test_write_filter_lzma.c | 12 +-- .../libarchive/test/test_write_filter_lzop.c | 12 +-- .../libarchive/test/test_write_filter_uuencode.c | 8 +- .../libarchive/test/test_write_filter_xz.c | 12 +-- .../libarchive/test/test_write_filter_zstd.c | 12 +-- .../test/test_write_format_zip_compression_store.c | 23 +++++- .../libarchive/test/test_write_format_zip_file.c | 21 +++++- .../test/test_write_format_zip_file_zip64.c | 21 +++++- .../libarchive/test/test_write_format_zip_large.c | 4 +- contrib/libarchive/tar/bsdtar.1 | 2 +- contrib/libarchive/tar/subst.c | 1 + contrib/libarchive/tar/test/test_copy.c | 20 ++--- contrib/libarchive/tar/test/test_option_b.c | 8 +- contrib/libarchive/tar/util.c | 21 +++--- contrib/libarchive/test_utils/test_main.c | 87 ++++++++++++++-------- lib/libarchive/tests/Makefile | 2 + 64 files changed, 610 insertions(+), 178 deletions(-) diff --cc contrib/libarchive/README.md index d5ef70c2191d,000000000000..404076237871 mode 100644,000000..100644 --- a/contrib/libarchive/README.md +++ b/contrib/libarchive/README.md @@@ -1,227 -1,0 +1,232 @@@ +# Welcome to libarchive! + +The libarchive project develops a portable, efficient C library that +can read and write streaming archives in a variety of formats. It +also includes implementations of the common `tar`, `cpio`, and `zcat` +command-line tools that use the libarchive library. + +## Questions? Issues? + +* http://www.libarchive.org is the home for ongoing + libarchive development, including documentation, + and links to the libarchive mailing lists. +* To report an issue, use the issue tracker at + https://github.com/libarchive/libarchive/issues +* To submit an enhancement to libarchive, please + submit a pull request via GitHub: https://github.com/libarchive/libarchive/pulls + +## Contents of the Distribution + +This distribution bundle includes the following major components: + +* **libarchive**: a library for reading and writing streaming archives +* **tar**: the 'bsdtar' program is a full-featured 'tar' implementation built on libarchive +* **cpio**: the 'bsdcpio' program is a different interface to essentially the same functionality +* **cat**: the 'bsdcat' program is a simple replacement tool for zcat, bzcat, xzcat, and such +* **examples**: Some small example programs that you may find useful. +* **examples/minitar**: a compact sample demonstrating use of libarchive. +* **contrib**: Various items sent to me by third parties; please contact the authors with any questions. + +The top-level directory contains the following information files: + +* **NEWS** - highlights of recent changes +* **COPYING** - what you can do with this +* **INSTALL** - installation instructions +* **README** - this file +* **CMakeLists.txt** - input for "cmake" build tool, see INSTALL +* **configure** - configuration script, see INSTALL for details. If your copy of the source lacks a `configure` script, you can try to construct it by running the script in `build/autogen.sh` (or use `cmake`). + +The following files in the top-level directory are used by the 'configure' script: ++ +* `Makefile.am`, `aclocal.m4`, `configure.ac` - used to build this distribution, only needed by maintainers +* `Makefile.in`, `config.h.in` - templates used by configure script + +## Documentation + +In addition to the informational articles and documentation +in the online [libarchive Wiki](https://github.com/libarchive/libarchive/wiki), +the distribution also includes a number of manual pages: + + * bsdtar.1 explains the use of the bsdtar program + * bsdcpio.1 explains the use of the bsdcpio program + * bsdcat.1 explains the use of the bsdcat program + * libarchive.3 gives an overview of the library as a whole + * archive_read.3, archive_write.3, archive_write_disk.3, and + archive_read_disk.3 provide detailed calling sequences for the read + and write APIs + * archive_entry.3 details the "struct archive_entry" utility class + * archive_internals.3 provides some insight into libarchive's + internal structure and operation. + * libarchive-formats.5 documents the file formats supported by the library + * cpio.5, mtree.5, and tar.5 provide detailed information about these + popular archive formats, including hard-to-find details about + modern cpio and tar variants. + +The manual pages above are provided in the 'doc' directory in +a number of different formats. + +You should also read the copious comments in `archive.h` and the +source code for the sample programs for more details. Please let us +know about any errors or omissions you find. + +## Supported Formats + +Currently, the library automatically detects and reads the following formats: ++ + * Old V7 tar archives + * POSIX ustar + * GNU tar format (including GNU long filenames, long link names, and sparse files) + * Solaris 9 extended tar format (including ACLs) + * POSIX pax interchange format + * POSIX octet-oriented cpio + * SVR4 ASCII cpio + * Binary cpio (big-endian or little-endian) + * PWB binary cpio + * ISO9660 CD-ROM images (with optional Rockridge or Joliet extensions) + * ZIP archives (with uncompressed or "deflate" compressed entries, including support for encrypted Zip archives) + * ZIPX archives (with support for bzip2, ppmd8, lzma and xz compressed entries) + * GNU and BSD 'ar' archives + * 'mtree' format + * 7-Zip archives + * Microsoft CAB format + * LHA and LZH archives + * RAR and RAR 5.0 archives (with some limitations due to RAR's proprietary status) + * XAR archives + +The library also detects and handles any of the following before evaluating the archive: ++ + * uuencoded files + * files with RPM wrapper + * gzip compression + * bzip2 compression + * compress/LZW compression + * lzma, lzip, and xz compression + * lz4 compression + * lzop compression + * zstandard compression + +The library can create archives in any of the following formats: ++ + * POSIX ustar + * POSIX pax interchange format + * "restricted" pax format, which will create ustar archives except for + entries that require pax extensions (for long filenames, ACLs, etc). + * Old GNU tar format + * Old V7 tar format + * POSIX octet-oriented cpio + * SVR4 "newc" cpio + * Binary cpio (little-endian) + * PWB binary cpio + * shar archives + * ZIP archives (with uncompressed or "deflate" compressed entries) + * GNU and BSD 'ar' archives + * 'mtree' format + * ISO9660 format + * 7-Zip archives + * XAR archives + +When creating archives, the result can be filtered with any of the following: ++ + * uuencode + * gzip compression + * bzip2 compression + * compress/LZW compression + * lzma, lzip, and xz compression + * lz4 compression + * lzop compression + * zstandard compression + +## Notes about the Library Design + +The following notes address many of the most common +questions we are asked about libarchive: + +* This is a heavily stream-oriented system. That means that + it is optimized to read or write the archive in a single + pass from beginning to end. For example, this allows + libarchive to process archives too large to store on disk + by processing them on-the-fly as they are read from or + written to a network or tape drive. This also makes + libarchive useful for tools that need to produce + archives on-the-fly (such as webservers that provide + archived contents of a users account). + +* In-place modification and random access to the contents + of an archive are not directly supported. For some formats, + this is not an issue: For example, tar.gz archives are not + designed for random access. In some other cases, libarchive + can re-open an archive and scan it from the beginning quickly + enough to provide the needed abilities even without true + random access. Of course, some applications do require true + random access; those applications should consider alternatives + to libarchive. + +* The library is designed to be extended with new compression and + archive formats. The only requirement is that the format be + readable or writable as a stream and that each archive entry be + independent. There are articles on the libarchive Wiki explaining + how to extend libarchive. + +* On read, compression and format are always detected automatically. + +* The same API is used for all formats; it should be very + easy for software using libarchive to transparently handle + any of libarchive's archiving formats. + +* Libarchive's automatic support for decompression can be used + without archiving by explicitly selecting the "raw" and "empty" + formats. + +* I've attempted to minimize static link pollution. If you don't + explicitly invoke a particular feature (such as support for a + particular compression or format), it won't get pulled in to + statically-linked programs. In particular, if you don't explicitly + enable a particular compression or decompression support, you won't + need to link against the corresponding compression or decompression + libraries. This also reduces the size of statically-linked + binaries in environments where that matters. + +* The library is generally _thread safe_ depending on the platform: + it does not define any global variables of its own. However, some + platforms do not provide fully thread-safe versions of key C library + functions. On those platforms, libarchive will use the non-thread-safe + functions. Patches to improve this are of great interest to us. + +* In particular, libarchive's modules to read or write a directory + tree do use `chdir()` to optimize the directory traversals. This + can cause problems for programs that expect to do disk access from + multiple threads. Of course, those modules are completely + optional and you can use the rest of libarchive without them. + +* The library is _not_ thread aware, however. It does no locking + or thread management of any kind. If you create a libarchive + object and need to access it from multiple threads, you will + need to provide your own locking. + +* On read, the library accepts whatever blocks you hand it. + Your read callback is free to pass the library a byte at a time + or mmap the entire archive and give it to the library at once. + On write, the library always produces correctly-blocked output. + +* The object-style approach allows you to have multiple archive streams + open at once. bsdtar uses this in its "@archive" extension. + +* The archive itself is read/written using callback functions. + You can read an archive directly from an in-memory buffer or + write it to a socket, if you wish. There are some utility + functions to provide easy-to-use "open file," etc, capabilities. + +* The read/write APIs are designed to allow individual entries + to be read or written to any data source: You can create + a block of data in memory and add it to a tar archive without + first writing a temporary file. You can also read an entry from + an archive and write the data directly to a socket. If you want + to read/write entries to disk, there are convenience functions to + make this especially easy. + +* Note: The "pax interchange format" is a POSIX standard extended tar + format that should be used when the older _ustar_ format is not + appropriate. It has many advantages over other tar formats + (including the legacy GNU tar format) and is widely supported by + current tar implementations. + diff --cc contrib/libarchive/libarchive/test/test_read_format_tar_invalid_pax_size.c index 000000000000,0a03cb677593..0a03cb677593 mode 000000,100644..100644 --- a/contrib/libarchive/libarchive/test/test_read_format_tar_invalid_pax_size.c +++ b/contrib/libarchive/libarchive/test/test_read_format_tar_invalid_pax_size.c diff --cc contrib/libarchive/libarchive/test/test_read_format_tar_invalid_pax_size.tar.uu index 000000000000,71309eab2cb3..71309eab2cb3 mode 000000,100644..100644 --- a/contrib/libarchive/libarchive/test/test_read_format_tar_invalid_pax_size.tar.uu +++ b/contrib/libarchive/libarchive/test/test_read_format_tar_invalid_pax_size.tar.uu diff --cc contrib/libarchive/tar/util.c index 9d59e1a65b26,000000000000..231c53e2cf6b mode 100644,000000..100644 --- a/contrib/libarchive/tar/util.c +++ b/contrib/libarchive/tar/util.c @@@ -1,770 -1,0 +1,771 @@@ +/*- + * Copyright (c) 2003-2007 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 "bsdtar_platform.h" +__FBSDID("$FreeBSD$"); + +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> /* Linux doesn't define mode_t, etc. in sys/stat.h. */ +#endif +#include <ctype.h> +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_IO_H +#include <io.h> +#endif +#ifdef HAVE_STDARG_H +#include <stdarg.h> +#endif +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_WCTYPE_H +#include <wctype.h> +#else +/* If we don't have wctype, we need to hack up some version of iswprint(). */ +#define iswprint isprint +#endif + +#include "bsdtar.h" +#include "err.h" +#include "passphrase.h" + - static size_t bsdtar_expand_char(char *, size_t, char); ++static size_t bsdtar_expand_char(char *, size_t, size_t, char); +static const char *strip_components(const char *path, int elements); + +#if defined(_WIN32) && !defined(__CYGWIN__) +#define read _read +#endif + +/* TODO: Hack up a version of mbtowc for platforms with no wide + * character support at all. I think the following might suffice, + * but it needs careful testing. + * #if !HAVE_MBTOWC + * #define mbtowc(wcp, p, n) ((*wcp = *p), 1) + * #endif + */ + +/* + * Print a string, taking care with any non-printable characters. + * + * Note that we use a stack-allocated buffer to receive the formatted + * string if we can. This is partly performance (avoiding a call to + * 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). + */ + +void +safe_fprintf(FILE *f, const char *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; + int length, n; + va_list ap; + const char *p; + unsigned i; + wchar_t wc; + char try_wc; + + /* Use a stack-allocated buffer if we can, for speed and safety. */ + fmtbuff_heap = NULL; + fmtbuff_length = sizeof(fmtbuff_stack); + fmtbuff = fmtbuff_stack; + + /* Try formatting into the stack buffer. */ + va_start(ap, fmt); + length = vsnprintf(fmtbuff, fmtbuff_length, fmt, ap); + va_end(ap); + + /* 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; + 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'; + break; + } + free(fmtbuff_heap); + fmtbuff_heap = malloc(fmtbuff_length); + + /* Reformat the result into the heap buffer if we can. */ + if (fmtbuff_heap != NULL) { + fmtbuff = fmtbuff_heap; + va_start(ap, fmt); + length = vsnprintf(fmtbuff, fmtbuff_length, fmt, ap); + va_end(ap); + } else { + /* Leave fmtbuff pointing to the truncated + * string in fmtbuff_stack. */ + fmtbuff = fmtbuff_stack; + length = sizeof(fmtbuff_stack) - 1; + break; + } + } + + /* Note: mbrtowc() has a cleaner API, but mbtowc() seems a bit + * more portable, so we use that here instead. */ + if (mbtowc(NULL, NULL, 1) == -1) { /* Reset the shift state. */ + /* mbtowc() should never fail in practice, but + * handle the theoretical error anyway. */ + free(fmtbuff_heap); + return; + } + + /* Write data, expanding unprintable characters. */ + p = fmtbuff; + i = 0; + try_wc = 1; + while (*p != '\0') { + + /* Convert to wide char, test if the wide + * char is printable in the current locale. */ + if (try_wc && (n = mbtowc(&wc, p, length)) != -1) { + length -= n; + if (iswprint(wc) && wc != L'\\') { + /* Printable, copy the bytes through. */ + while (n-- > 0) + outbuff[i++] = *p++; + } else { + /* Not printable, format the bytes. */ + while (n-- > 0) + i += (unsigned)bsdtar_expand_char( - outbuff, i, *p++); ++ outbuff, sizeof(outbuff), i, *p++); + } + } else { + /* After any conversion failure, don't bother + * trying to convert the rest. */ - i += (unsigned)bsdtar_expand_char(outbuff, i, *p++); ++ i += (unsigned)bsdtar_expand_char(outbuff, sizeof(outbuff), i, *p++); + try_wc = 0; + } + + /* If our output buffer is full, dump it and keep going. */ + if (i > (sizeof(outbuff) - 128)) { + outbuff[i] = '\0'; + fprintf(f, "%s", outbuff); + i = 0; + } + } + outbuff[i] = '\0'; + fprintf(f, "%s", outbuff); + + /* If we allocated a heap-based formatting buffer, free it now. */ + free(fmtbuff_heap); +} + +/* + * Render an arbitrary sequence of bytes into printable ASCII characters. + */ +static size_t - bsdtar_expand_char(char *buff, size_t offset, char c) ++bsdtar_expand_char(char *buff, size_t buffsize, size_t offset, char c) +{ + size_t i = offset; + + if (isprint((unsigned char)c) && c != '\\') + buff[i++] = c; + else { + buff[i++] = '\\'; + switch (c) { + case '\a': buff[i++] = 'a'; break; + case '\b': buff[i++] = 'b'; break; + case '\f': buff[i++] = 'f'; break; + case '\n': buff[i++] = 'n'; break; +#if '\r' != '\n' + /* On some platforms, \n and \r are the same. */ + case '\r': buff[i++] = 'r'; break; +#endif + case '\t': buff[i++] = 't'; break; + case '\v': buff[i++] = 'v'; break; + case '\\': buff[i++] = '\\'; break; + default: - sprintf(buff + i, "%03o", 0xFF & (int)c); ++ snprintf(buff + i, buffsize - i, "%03o", 0xFF & (int)c); + i += 3; + } + } + + return (i - offset); +} + +int +yes(const char *fmt, ...) +{ + char buff[32]; + char *p; + ssize_t l; + + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, " (y/N)? "); + fflush(stderr); + + l = read(2, buff, sizeof(buff) - 1); + if (l < 0) { + fprintf(stderr, "Keyboard read failed\n"); + exit(1); + } + if (l == 0) + return (0); + buff[l] = 0; + + for (p = buff; *p != '\0'; p++) { + if (isspace((unsigned char)*p)) + continue; + switch(*p) { + case 'y': case 'Y': + return (1); + case 'n': case 'N': + return (0); + default: + return (0); + } + } + + return (0); +} + +/*- + * The logic here for -C <dir> attempts to avoid + * chdir() as long as possible. For example: + * "-C /foo -C /bar file" needs chdir("/bar") but not chdir("/foo") + * "-C /foo -C bar file" needs chdir("/foo/bar") + * "-C /foo -C bar /file1" does not need chdir() + * "-C /foo -C bar /file1 file2" needs chdir("/foo/bar") before file2 + * + * The only correct way to handle this is to record a "pending" chdir + * request and combine multiple requests intelligently until we + * need to process a non-absolute file. set_chdir() adds the new dir + * to the pending list; do_chdir() actually executes any pending chdir. + * + * This way, programs that build tar command lines don't have to worry + * about -C with non-existent directories; such requests will only + * fail if the directory must be accessed. + * + */ +void +set_chdir(struct bsdtar *bsdtar, const char *newdir) +{ +#if defined(_WIN32) && !defined(__CYGWIN__) + if (newdir[0] == '/' || newdir[0] == '\\' || + /* Detect this type, for example, "C:\" or "C:/" */ + (((newdir[0] >= 'a' && newdir[0] <= 'z') || + (newdir[0] >= 'A' && newdir[0] <= 'Z')) && + newdir[1] == ':' && (newdir[2] == '/' || newdir[2] == '\\'))) { +#else + if (newdir[0] == '/') { +#endif + /* The -C /foo -C /bar case; dump first one. */ + free(bsdtar->pending_chdir); + bsdtar->pending_chdir = NULL; + } + if (bsdtar->pending_chdir == NULL) + /* Easy case: no previously-saved dir. */ + bsdtar->pending_chdir = strdup(newdir); + else { + /* The -C /foo -C bar case; concatenate */ + char *old_pending = bsdtar->pending_chdir; + size_t old_len = strlen(old_pending); - bsdtar->pending_chdir = malloc(old_len + strlen(newdir) + 2); ++ size_t new_len = old_len + strlen(newdir) + 2; ++ bsdtar->pending_chdir = malloc(new_len); + if (old_pending[old_len - 1] == '/') + old_pending[old_len - 1] = '\0'; + if (bsdtar->pending_chdir != NULL) - sprintf(bsdtar->pending_chdir, "%s/%s", ++ snprintf(bsdtar->pending_chdir, new_len, "%s/%s", + old_pending, newdir); + free(old_pending); + } + if (bsdtar->pending_chdir == NULL) + lafe_errc(1, errno, "No memory"); +} + +void +do_chdir(struct bsdtar *bsdtar) +{ + if (bsdtar->pending_chdir == NULL) + return; + + if (chdir(bsdtar->pending_chdir) != 0) { + lafe_errc(1, 0, "could not chdir to '%s'\n", + bsdtar->pending_chdir); + } + free(bsdtar->pending_chdir); + bsdtar->pending_chdir = NULL; +} + +static const char * +strip_components(const char *p, int elements) +{ + /* Skip as many elements as necessary. */ + while (elements > 0) { + switch (*p++) { + case '/': +#if defined(_WIN32) && !defined(__CYGWIN__) + case '\\': /* Support \ path sep on Windows ONLY. */ +#endif + elements--; + break; + case '\0': + /* Path is too short, skip it. */ + return (NULL); + } + } + + /* Skip any / characters. This handles short paths that have + * additional / termination. This also handles the case where + * the logic above stops in the middle of a duplicate // + * sequence (which would otherwise get converted to an + * absolute path). */ + for (;;) { + switch (*p) { + case '/': +#if defined(_WIN32) && !defined(__CYGWIN__) + case '\\': /* Support \ path sep on Windows ONLY. */ +#endif + ++p; + break; + case '\0': + return (NULL); + default: + return (p); + } + } +} + +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"); + return 1; + } + if (r == 1) { + archive_entry_copy_pathname(entry, subst_name); + if (*subst_name == '\0') { + free(subst_name); + return -1; + } else + free(subst_name); + name = archive_entry_pathname(entry); + original_name = name; + } + + /* 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; + } + if (r == 1) { + 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) { + lafe_warnc(0, "Invalid substitution, skipping entry"); + return 1; + } + if (r == 1) { + archive_entry_copy_symlink(entry, subst_name); + free(subst_name); + } + } +#endif + + /* Strip leading dir names as per --strip-components option. */ + if (bsdtar->strip_components > 0) { + name = strip_components(name, bsdtar->strip_components); + if (name == NULL) + return (1); + + if (hardlinkname != NULL) { + hardlinkname = strip_components(hardlinkname, + bsdtar->strip_components); + if (hardlinkname == NULL) + return (1); + } + } + + if ((bsdtar->flags & OPTFLAG_ABSOLUTE_PATHS) == 0) { + /* By default, don't write or restore absolute pathnames. */ + name = strip_absolute_path(bsdtar, name); + if (*name == '\0') + name = "."; + + 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++; + } + + /* 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); +} + +/* + * 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. + */ +const char * +tar_i64toa(int64_t n0) +{ + static char buff[24]; + uint64_t n = n0 < 0 ? -n0 : n0; + char *p = buff + sizeof(buff); + + *--p = '\0'; + do { + *--p = '0' + (int)(n % 10); + } while (n /= 10); + if (n0 < 0) + *--p = '-'; + return p; +} + +/* + * Like strcmp(), but try to be a little more aware of the fact that + * we're comparing two paths. Right now, it just handles leading + * "./" and trailing '/' specially, so that "a/b/" == "./a/b" + * + * TODO: Make this better, so that "./a//b/./c/" == "a/b/c" + * TODO: After this works, push it down into libarchive. + * TODO: Publish the path normalization routines in libarchive so + * that bsdtar can normalize paths and use fast strcmp() instead + * of this. + * + * Note: This is currently only used within write.c, so should + * not handle \ path separators. + */ + +int +pathcmp(const char *a, const char *b) +{ + /* Skip leading './' */ + if (a[0] == '.' && a[1] == '/' && a[2] != '\0') + a += 2; + if (b[0] == '.' && b[1] == '/' && b[2] != '\0') + b += 2; + /* Find the first difference, or return (0) if none. */ + while (*a == *b) { + if (*a == '\0') + return (0); + a++; + b++; + } + /* + * If one ends in '/' and the other one doesn't, + * they're the same. + */ + if (a[0] == '/' && a[1] == '\0' && b[0] == '\0') + return (0); + if (a[0] == '\0' && b[0] == '/' && b[1] == '\0') + return (0); + /* 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) *** 813 LINES SKIPPED ***