From: Peter Avalos Date: Fri, 27 Apr 2007 22:01:15 +0000 (+0000) Subject: Import libarchive 2.1.9. X-Git-Tag: v2.0.1~3163^2 X-Git-Url: https://gitweb.dragonflybsd.org/dragonfly.git/commitdiff_plain/ebf32849c23b17b0981d6b3e954e457a210230b9 Import libarchive 2.1.9. --- diff --git a/contrib/libarchive-2.1/COPYING b/contrib/libarchive-2.1/COPYING new file mode 100644 index 0000000000..6128d175fd --- /dev/null +++ b/contrib/libarchive-2.1/COPYING @@ -0,0 +1,26 @@ +All of the C source code and documentation in this package is subject +to the following: + +Copyright (c) 2003-2006 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 + in this position and unchanged. +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. diff --git a/contrib/libarchive-2.1/NEWS b/contrib/libarchive-2.1/NEWS new file mode 100644 index 0000000000..73f81b0119 --- /dev/null +++ b/contrib/libarchive-2.1/NEWS @@ -0,0 +1,192 @@ + +Apr 24, 2007: libarchive 2.1.9 released +Apr 24, 2007: Fix some recently-introduced problems with libraries + (Just let automake handle it and it all works much better.) + Finish isolating major()/minor()/makedev() in archive_entry.c. + +Apr 23, 2007: libarchive 2.1.8 released +Apr 23, 2007: Minor fixes found from building on MacOS X + +Apr 22, 2007: libarchive 2.1.7 released +Apr 22, 2007: Eliminated all uses of 'struct stat' from the + format readers/writers. This should improve portability; + 'struct stat' is now only used in archive_entry and in + code that actually touches the disk. + +Apr 17, 2007: libarchive 2.1.6 released + Libarchive now compiles and passes all tests on Interix. + +Apr 16, 2007: libarchive 2.1.5 released + +Apr 15, 2007: libarchive 2.1b2 released +Apr 15, 2007: New libarchive_internals.3 documentation of internal APIs. + Not complete, but should prove helpful. +Apr 15, 2007: Experimental "read_compress_program" and "write_compress_program" + for using libarchive with external compression. Not yet + well tested, and likely has portability issues. Feedback + appreciated. + +Apr 14, 2007: libarchive 2.0.31 released +Apr 14, 2007: More fixes for Interix, more 'ar' work + +Apr 14, 2007: libarchive 2.0.30 released +Apr 13, 2007: libarchive now enforces trailing '/' on dirs + written to tar archives + +Apr 11, 2007: libarchive 2.0.29 released +Apr 11, 2007: Make it easier to statically configure for different platforms. +Apr 11, 2007: Updated config.guess, config.sub, libtool + +Apr 06, 2007: libarchive 2.0.28 released +Apr 06, 2007: 'ar' format read/write support thanks to Kai Wang. + +Apr 01, 2007: libarchive 2.0.27 released +Mar 31, 2007: Several minor fixes from Colin Percival and Joerg Sonnenberger. + +Mar 12, 2007: libarchive 2.0.25 released +Mar 12, 2007: Fix broken --unlink flag. + +Mar 11, 2007: libarchive 2.0.24 released +Mar 10, 2007: Correct an ACL blunder that causes any ACL with an entry + that refers to a non-existent user or group to not be restored correctly. + The fix both makes the parser more tolerant (so that archives created + with the buggy ACLs can be read now) and corrects the ACL formatter. +Mar 10, 2007: More work on test portability to Linux. + +Mar 10, 2007: libarchive 2.0.22 released +Mar 10, 2007: Header cleanups; added linux/fs.h, removed + some unnecessary headers, added #include guards in bsdtar. + If you see any obvious compile failures from this, let me know. +Mar 10, 2007: Work on bsdtar test scripts: not yet robust enough + to enable as part of "make check", but getting better. +Mar 10, 2007: libarchive now returns ARCHIVE_FAILED when + a header write fails in a way that only affects this item. + Less bad than ARCHIVE_FATAL, but worse than ARCHIVE_WARN. + +Mar 07, 2007: libarchive 2.0.21 released +Mar 07, 2007: Add some ACL tests (only for the system-independent + portion of the ACL support for now). +Mar 07, 2007: tar's ability to read ACLs off disk got + turned off for FreeBSD; re-enable it. (ACL restores and + libarchive support for storing/reading ACLs from pax + archives was unaffected.) + +Mar 02, 2007: libarchive 2.0.20 released +Mar 2, 2007: It's not perfect, but it's pretty good. + Libarchive 2.0 is officially out of beta. + +Feb 28, 2007: libarchive 2.0b17 released +Feb 27, 2007: Make the GID restore checks more robust by checking + whether the current user has too few or too many privileges. + +Feb 26, 2007: libarchive 2.0b15 released +Feb 26, 2007: Don't lose symlinks when extracting from ISOs. + Thanks to Diego "Flameeyes" Pettenò for telling me about the + broken testcase on Gentoo that (finally!) led me to the cause + of this long-standing bug. + +Feb 26, 2007: libarchive 2.0b14 released +Feb 26, 2007: Fix a broken test on platforms that lack lchmod(). + +Feb 25, 2007: libarchive 2.0b13 released +Feb 25, 2007: Empty archives were being written as empty files, + without a proper end-of-archive marker. Fixed. + +Feb 23, 2007: libarchive 2.0b12 released +Feb 22, 2007: Basic security checks added: _EXTRACT_SECURE_NODOTDOT + and _EXTRACT_SECURE_SYMLINK. These checks used to be in bsdtar, + but they belong down in libarchive where they can be used by + other tools and where they can be better optimized. + +Feb 11, 2007: libarchive 2.0b11 released +Feb 10, 2007: Fixed a bunch of errors in libarchive's handling + of EXTRACT_PERM and EXTRACT_OWNER, especially relating + to SUID and SGID bits. + +Jan 31, 2007: libarchive 2.0b9 released +Jan 31, 2007: Added read support for "empty" archives as a + distinct archive format. Bsdtar uses this to handle, e.g., + "touch foo.tar; tar -rf foo.tar" + +Jan 22, 2007: libarchive 2.0b6 released +Jan 22, 2007: archive_write_disk API is now in place. It provides + a finer-grained interface than archive_read_extract. In particular, + you can use it to create objects on disk without having an archive + around (just feed it archive_entry objects describing what you + want to create), you can override the uname/gname-to-uid/gid lookups + (minitar uses this to avoid getpwXXX() and getgrXXX() bloat). + +Jan 09, 2007: libarchive 2.0a3 released +Jan 9, 2007: archive_extract is now much better; it handles the + most common cases with a minimal number of system calls. + Some features still need a lot of testing, especially corner + cases involving objects that already exist on disk. I expect + the next round of API overhaul will simplify building test cases. +Jan 9, 2007: a number of fixes thanks to Colin Percival, especially + corrections to the skip() framework and handling of large files. +Jan 9, 2007: Fixes for large ISOs. The code should correctly handle + very large ISOs with entries up to 4G. Thanks to Robert Sciuk + for pointing out these issues. + +Sep 05, 2006: libarchive 1.3.1 released +Sep 5, 2006: Bump version to 1.3 for new I/O wrappers. +Sep 4, 2006: New memory and FILE read/write wrappers. +Sep 4, 2006: libarchive test harness is now minimally functional; + it's located a few minor bugs in error-handling logic + +Aug 17, 2006: libarchive 1.2.54 released +Aug 17, 2006: Outline ABI changes for libarchive 2.0; these + are protected behind #ifdef's until I think I've found everything + that needs to change. +Aug 17, 2006: Fix error-handling in archive_read/write_close() + They weren't returning any errors before. +Aug 17, 2006: Fix recursive-add logic to not trigger if it's not set + Fixes a bug adding files when writing archive to pipe or when + using archive_write_open() directly. +Jul 2006: New "skip" handling improves performance extracting + single files from large uncompressed archives. + +Mar 21, 2006: 1.2.52 released +Mar 21, 2006: Fix -p on platforms that don't have platform-specific + extended attribute code. +Mar 20, 2006: Add NEWS file; fill in some older history from other + files. I'll try to keep this file up-to-date from now on. + +OLDER NEWS SUMMARIES + +Mar 19, 2006: libarchive 1.2.51 released +Mar 18, 2006: Many fixes to extended attribute support, including a redesign + of the storage format to simplify debugging. +Mar 12, 2006: Remove 'tp' support; it was a fun idea, but not worth + spending much time on. +Mar 11, 2006: Incorporated Jaakko Heinonen's still-experimental support + for extended attributes (Currently Linux-only.). +Mar 11, 2006: Reorganized distribution package: There is now one tar.gz + file that builds both libarchive and bsdtar. +Feb 13, 2006: Minor bug fixes: correctly read cpio device entries, write + Pax attribute entry names. +Nov 7, 2005: Experimental 'tp' format support in libarchive. Feedback + appreciated; this is not enabled by archive_read_support_format_all() + yet as I'm not quite content with the format detection heuristics. +Nov 7, 2005: Some more portability improvements thanks to Darin Broady, + minor bugfixes. +Oct 12, 2005: Use GNU libtool to build shared libraries on many systems. +Aug 9, 2005: Correctly detect that MacOS X does not have POSIX ACLs. +Apr 17, 2005: Kees Zeelenberg has ported libarchive and bsdtar to Windows: + http://gnuwin32.sourceforge.net/ +Apr 11, 2005: Extended Zip/Zip64 support thanks to Dan Nelson. -L/-h + fix from Jaakko Heinonen. +Mar 12, 2005: archive_read_extract can now handle very long + pathnames (I've tested with pathnames up to 1MB). +Mar 12, 2005: Marcus Geiger has written an article about libarchive + http://xsnil.antbear.org/2005/02/05/archive-mit-libarchive-verarbeiten/ + including examples of using it from Objective-C. His MoinX + http://moinx.antbear.org/ desktop Wiki uses + libarchive for archiving and restoring Wiki pages. +Jan 22, 2005: Preliminary ZIP extraction support, + new directory-walking code for bsdtar. +Jan 16, 2005: ISO9660 extraction code added; manpage corrections. +May 22, 2004: Many gtar-compatible long options have been added; almost + all FreeBSD ports extract correctly with bsdtar. +May 18, 2004: bsdtar can read Solaris, HP-UX, Unixware, star, gtar, + and pdtar archives. diff --git a/contrib/libarchive-2.1/README b/contrib/libarchive-2.1/README new file mode 100644 index 0000000000..bcc4143d02 --- /dev/null +++ b/contrib/libarchive-2.1/README @@ -0,0 +1,114 @@ +README for libarchive bundle. + +This distribution bundle includes the following components: + + * libarchive: a library for reading and writing streaming archives + * tar: the 'bsdtar' program is a full-featured 'tar' + replacement built on libarchive + * minitar: a compact sample demonstrating use of libarchive + +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 + * configure - configuration script, see INSTALL for details. + +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 + * config.aux/* - auxiliary scripts used by build system + +Guide to Documentation installed by this system: + * bsdtar.1 explains the use of the bsdtar program + * libarchive.3 gives an overview of the library as a whole + * archive_read.3, archive_write.3, and archive_write_disk.3 provide + detailed calling sequences for the read and write APIs + * archive_entry.3 details the "struct archive_entry" utility class + * libarchive-formats.5 documents the file formats supported by the library + * tar.5 provides some detailed information about a variety of different + "tar" formats. + +You should also read the copious comments in "archive.h" and the source +code for the sample "bsdtar" program for more details. Please let me know +about any errors or omissions you find. + +Currently, the library automatically detects and reads the following: + * gzip compression + * bzip2 compression + * compress/LZW compression + * GNU tar format (including GNU long filenames, long link names, and + sparse files) + * Solaris 9 extended tar format (including ACLs) + * Old V7 tar archives + * POSIX ustar + * POSIX pax interchange format + * POSIX octet-oriented cpio + * SVR4 ASCII cpio + * Binary cpio (big-endian or little-endian) + * ISO9660 CD-ROM images (with optional Rockridge extensions) + * ZIP archives (with uncompressed or "deflate" compressed entries) + * GNU and BSD 'ar' archives + +The library can write: + * gzip compression + * bzip2 compression + * 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). + * POSIX octet-oriented cpio + * shar archives + * GNU and BSD 'ar' archives + +Notes about the library architecture: + + * This is a heavily stream-oriented system. There is no direct + support for in-place modification or random access and no intention + of ever adding such support. Adding such support would require + sacrificing a lot of other features, so don't bother asking. + + * 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. + + * On read, compression and format are always detected automatically. + + * 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. + 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. + + * 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: "pax interchange format" is really an extended tar format, + despite what the name says. diff --git a/contrib/libarchive-2.1/libarchive/archive.h.in b/contrib/libarchive-2.1/libarchive/archive.h.in new file mode 100644 index 0000000000..cb0b3005f1 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive.h.in @@ -0,0 +1,508 @@ +/*- + * 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. + * + * $FreeBSD: src/lib/libarchive/archive.h.in,v 1.42 2007/04/14 22:34:10 kientzle Exp $ + */ + +#ifndef ARCHIVE_H_INCLUDED +#define ARCHIVE_H_INCLUDED + +/* + * This header file corresponds to: + * Library version @VERSION@ + * Shared library version @SHLIB_MAJOR@ + */ + +#include /* Linux requires this for off_t */ +@ARCHIVE_H_INCLUDE_INTTYPES_H@ +#include /* For FILE * */ +#ifndef _WIN32 +#include /* For ssize_t and size_t */ +#else +typedef long ssize_t; +typedef unsigned int uid_t; +typedef unsigned int gid_t; +typedef unsigned short mode_t; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * If ARCHIVE_API_VERSION != archive_api_version(), then the library you + * were linked with is using an incompatible API to the one you were + * compiled with. This is almost certainly a fatal problem. + * + * ARCHIVE_API_FEATURE is incremented with each significant feature + * addition, so you can test (at compile or run time) if a particular + * feature is implemented. It's no big deal if ARCHIVE_API_FEATURE != + * archive_api_feature(), as long as both are high enough to include + * the features you're relying on. Specific values of FEATURE are + * documented here: + * + * 1 - Version tests are available. + * 2 - archive_{read,write}_close available separately from _finish. + * 3 - open_memory, open_memory2, open_FILE, open_fd available + * 5 - archive_write_disk interface available + */ +#define ARCHIVE_API_VERSION @ARCHIVE_API_MAJOR@ +int archive_api_version(void); +#define ARCHIVE_API_FEATURE @ARCHIVE_API_MINOR@ +int archive_api_feature(void); +/* Textual name/version of the library. */ +#define ARCHIVE_LIBRARY_VERSION "libarchive @VERSION@" +const char * archive_version(void); + +#define ARCHIVE_BYTES_PER_RECORD 512 +#define ARCHIVE_DEFAULT_BYTES_PER_BLOCK 10240 + +/* Declare our basic types. */ +struct archive; +struct archive_entry; + +/* + * Error codes: Use archive_errno() and archive_error_string() + * to retrieve details. Unless specified otherwise, all functions + * that return 'int' use these codes. + */ +#define ARCHIVE_EOF 1 /* Found end of archive. */ +#define ARCHIVE_OK 0 /* Operation was successful. */ +#define ARCHIVE_RETRY (-10) /* Retry might succeed. */ +#define ARCHIVE_WARN (-20) /* Partial success. */ +/* For example, if write_header "fails", then you can't push data. */ +#define ARCHIVE_FAILED (-25) /* Current operation cannot complete. */ +#define ARCHIVE_FATAL (-30) /* No more operations are possible. */ + +/* + * As far as possible, archive_errno returns standard platform errno codes. + * Of course, the details vary by platform, so the actual definitions + * here are stored in "archive_platform.h". The symbols are listed here + * for reference; as a rule, clients should not need to know the exact + * platform-dependent error code. + */ +/* Unrecognized or invalid file format. */ +/* #define ARCHIVE_ERRNO_FILE_FORMAT */ +/* Illegal usage of the library. */ +/* #define ARCHIVE_ERRNO_PROGRAMMER_ERROR */ +/* Unknown or unclassified error. */ +/* #define ARCHIVE_ERRNO_MISC */ + +/* + * Callbacks are invoked to automatically read/skip/write/open/close the + * archive. You can provide your own for complex tasks (like breaking + * archives across multiple tapes) or use standard ones built into the + * library. + */ + +/* Returns pointer and size of next block of data from archive. */ +typedef ssize_t archive_read_callback(struct archive *, void *_client_data, + const void **_buffer); +/* Skips at most request bytes from archive and returns the skipped amount */ +#if ARCHIVE_API_VERSION < 2 +typedef ssize_t archive_skip_callback(struct archive *, void *_client_data, + size_t request); +#else +typedef off_t archive_skip_callback(struct archive *, void *_client_data, + off_t request); +#endif +/* Returns size actually written, zero on EOF, -1 on error. */ +typedef ssize_t archive_write_callback(struct archive *, void *_client_data, + const void *_buffer, size_t _length); +typedef int archive_open_callback(struct archive *, void *_client_data); +typedef int archive_close_callback(struct archive *, void *_client_data); + +/* + * Codes for archive_compression. + */ +#define ARCHIVE_COMPRESSION_NONE 0 +#define ARCHIVE_COMPRESSION_GZIP 1 +#define ARCHIVE_COMPRESSION_BZIP2 2 +#define ARCHIVE_COMPRESSION_COMPRESS 3 +#define ARCHIVE_COMPRESSION_PROGRAM 4 + +/* + * Codes returned by archive_format. + * + * Top 16 bits identifies the format family (e.g., "tar"); lower + * 16 bits indicate the variant. This is updated by read_next_header. + * Note that the lower 16 bits will often vary from entry to entry. + * In some cases, this variation occurs as libarchive learns more about + * the archive (for example, later entries might utilize extensions that + * weren't necessary earlier in the archive; in this case, libarchive + * will change the format code to indicate the extended format that + * was used). In other cases, it's because different tools have + * modified the archive and so different parts of the archive + * actually have slightly different formts. (Both tar and cpio store + * format codes in each entry, so it is quite possible for each + * entry to be in a different format.) + */ +#define ARCHIVE_FORMAT_BASE_MASK 0xff0000 +#define ARCHIVE_FORMAT_CPIO 0x10000 +#define ARCHIVE_FORMAT_CPIO_POSIX (ARCHIVE_FORMAT_CPIO | 1) +#define ARCHIVE_FORMAT_CPIO_BIN_LE (ARCHIVE_FORMAT_CPIO | 2) +#define ARCHIVE_FORMAT_CPIO_BIN_BE (ARCHIVE_FORMAT_CPIO | 3) +#define ARCHIVE_FORMAT_CPIO_SVR4_NOCRC (ARCHIVE_FORMAT_CPIO | 4) +#define ARCHIVE_FORMAT_CPIO_SVR4_CRC (ARCHIVE_FORMAT_CPIO | 5) +#define ARCHIVE_FORMAT_SHAR 0x20000 +#define ARCHIVE_FORMAT_SHAR_BASE (ARCHIVE_FORMAT_SHAR | 1) +#define ARCHIVE_FORMAT_SHAR_DUMP (ARCHIVE_FORMAT_SHAR | 2) +#define ARCHIVE_FORMAT_TAR 0x30000 +#define ARCHIVE_FORMAT_TAR_USTAR (ARCHIVE_FORMAT_TAR | 1) +#define ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE (ARCHIVE_FORMAT_TAR | 2) +#define ARCHIVE_FORMAT_TAR_PAX_RESTRICTED (ARCHIVE_FORMAT_TAR | 3) +#define ARCHIVE_FORMAT_TAR_GNUTAR (ARCHIVE_FORMAT_TAR | 4) +#define ARCHIVE_FORMAT_ISO9660 0x40000 +#define ARCHIVE_FORMAT_ISO9660_ROCKRIDGE (ARCHIVE_FORMAT_ISO9660 | 1) +#define ARCHIVE_FORMAT_ZIP 0x50000 +#define ARCHIVE_FORMAT_EMPTY 0x60000 +#define ARCHIVE_FORMAT_AR 0x70000 +#define ARCHIVE_FORMAT_AR_GNU (ARCHIVE_FORMAT_AR | 1) +#define ARCHIVE_FORMAT_AR_BSD (ARCHIVE_FORMAT_AR | 2) + +/*- + * Basic outline for reading an archive: + * 1) Ask archive_read_new for an archive reader object. + * 2) Update any global properties as appropriate. + * In particular, you'll certainly want to call appropriate + * archive_read_support_XXX functions. + * 3) Call archive_read_open_XXX to open the archive + * 4) Repeatedly call archive_read_next_header to get information about + * successive archive entries. Call archive_read_data to extract + * data for entries of interest. + * 5) Call archive_read_finish to end processing. + */ +struct archive *archive_read_new(void); + +/* + * The archive_read_support_XXX calls enable auto-detect for this + * archive handle. They also link in the necessary support code. + * For example, if you don't want bzlib linked in, don't invoke + * support_compression_bzip2(). The "all" functions provide the + * obvious shorthand. + */ +int archive_read_support_compression_all(struct archive *); +int archive_read_support_compression_bzip2(struct archive *); +int archive_read_support_compression_compress(struct archive *); +int archive_read_support_compression_gzip(struct archive *); +int archive_read_support_compression_none(struct archive *); +int archive_read_support_compression_program(struct archive *, + const char *command); + +int archive_read_support_format_all(struct archive *); +int archive_read_support_format_ar(struct archive *); +int archive_read_support_format_cpio(struct archive *); +int archive_read_support_format_empty(struct archive *); +int archive_read_support_format_gnutar(struct archive *); +int archive_read_support_format_iso9660(struct archive *); +int archive_read_support_format_tar(struct archive *); +int archive_read_support_format_zip(struct archive *); + + +/* Open the archive using callbacks for archive I/O. */ +int archive_read_open(struct archive *, void *_client_data, + archive_open_callback *, archive_read_callback *, + archive_close_callback *); +int archive_read_open2(struct archive *, void *_client_data, + archive_open_callback *, archive_read_callback *, + archive_skip_callback *, archive_close_callback *); + +/* + * A variety of shortcuts that invoke archive_read_open() with + * canned callbacks suitable for common situations. The ones that + * accept a block size handle tape blocking correctly. + */ +/* Use this if you know the filename. Note: NULL indicates stdin. */ +int archive_read_open_filename(struct archive *, + const char *_filename, size_t _block_size); +/* archive_read_open_file() is a deprecated synonym for ..._open_filename(). */ +int archive_read_open_file(struct archive *, + const char *_filename, size_t _block_size); +/* Read an archive that's stored in memory. */ +int archive_read_open_memory(struct archive *, + void * buff, size_t size); +/* A more involved version that is only used for internal testing. */ +int archive_read_open_memory2(struct archive *a, void *buff, + size_t size, size_t read_size); +/* Read an archive that's already open, using the file descriptor. */ +int archive_read_open_fd(struct archive *, int _fd, + size_t _block_size); +/* Read an archive that's already open, using a FILE *. */ +/* Note: DO NOT use this with tape drives. */ +int archive_read_open_FILE(struct archive *, FILE *_file); + +/* Parses and returns next entry header. */ +int archive_read_next_header(struct archive *, + struct archive_entry **); + +/* + * Retrieve the byte offset in UNCOMPRESSED data where last-read + * header started. + */ +int64_t archive_read_header_position(struct archive *); + +/* Read data from the body of an entry. Similar to read(2). */ +ssize_t archive_read_data(struct archive *, void *, size_t); +/* + * A zero-copy version of archive_read_data that also exposes the file offset + * of each returned block. Note that the client has no way to specify + * the desired size of the block. The API does guarantee that offsets will + * be strictly increasing and that returned blocks will not overlap. + */ +int archive_read_data_block(struct archive *a, + const void **buff, size_t *size, off_t *offset); + +/*- + * Some convenience functions that are built on archive_read_data: + * 'skip': skips entire entry + * 'into_buffer': writes data into memory buffer that you provide + * 'into_fd': writes data to specified filedes + */ +int archive_read_data_skip(struct archive *); +int archive_read_data_into_buffer(struct archive *, void *buffer, + ssize_t len); +int archive_read_data_into_fd(struct archive *, int fd); + +/*- + * Convenience function to recreate the current entry (whose header + * has just been read) on disk. + * + * This does quite a bit more than just copy data to disk. It also: + * - Creates intermediate directories as required. + * - Manages directory permissions: non-writable directories will + * be initially created with write permission enabled; when the + * archive is closed, dir permissions are edited to the values specified + * in the archive. + * - Checks hardlinks: hardlinks will not be extracted unless the + * linked-to file was also extracted within the same session. (TODO) + */ + +/* The "flags" argument selects optional behavior, 'OR' the flags you want. */ + +/* Default: Do not try to set owner/group. */ +#define ARCHIVE_EXTRACT_OWNER (1) +/* Default: Do obey umask, do not restore SUID/SGID/SVTX bits. */ +#define ARCHIVE_EXTRACT_PERM (2) +/* Default: Do not restore mtime/atime. */ +#define ARCHIVE_EXTRACT_TIME (4) +/* Default: Replace existing files. */ +#define ARCHIVE_EXTRACT_NO_OVERWRITE (8) +/* Default: Try create first, unlink only if create fails with EEXIST. */ +#define ARCHIVE_EXTRACT_UNLINK (16) +/* Default: Do not restore ACLs. */ +#define ARCHIVE_EXTRACT_ACL (32) +/* Default: Do not restore fflags. */ +#define ARCHIVE_EXTRACT_FFLAGS (64) +/* Default: Do not restore xattrs. */ +#define ARCHIVE_EXTRACT_XATTR (128) +/* Default: Do not try to guard against extracts redirected by symlinks. */ +/* Note: With ARCHIVE_EXTRACT_UNLINK, will remove any intermediate symlink. */ +#define ARCHIVE_EXTRACT_SECURE_SYMLINKS (256) +/* Default: Do not reject entries with '..' as path elements. */ +#define ARCHIVE_EXTRACT_SECURE_NODOTDOT (512) + +int archive_read_extract(struct archive *, struct archive_entry *, + int flags); +void archive_read_extract_set_progress_callback(struct archive *, + void (*_progress_func)(void *), void *_user_data); + +/* Record the dev/ino of a file that will not be written. This is + * generally set to the dev/ino of the archive being read. */ +void archive_read_extract_set_skip_file(struct archive *, + dev_t, ino_t); + +/* Close the file and release most resources. */ +int archive_read_close(struct archive *); +/* Release all resources and destroy the object. */ +/* Note that archive_read_finish will call archive_read_close for you. */ +#if ARCHIVE_API_VERSION > 1 +int archive_read_finish(struct archive *); +#else +/* Temporarily allow library to compile with either 1.x or 2.0 API. */ +/* Erroneously declared to return void in libarchive 1.x */ +void archive_read_finish(struct archive *); +#endif + +/*- + * To create an archive: + * 1) Ask archive_write_new for a archive writer object. + * 2) Set any global properties. In particular, you should set + * the compression and format to use. + * 3) Call archive_write_open to open the file (most people + * will use archive_write_open_file or archive_write_open_fd, + * which provide convenient canned I/O callbacks for you). + * 4) For each entry: + * - construct an appropriate struct archive_entry structure + * - archive_write_header to write the header + * - archive_write_data to write the entry data + * 5) archive_write_close to close the output + * 6) archive_write_finish to cleanup the writer and release resources + */ +struct archive *archive_write_new(void); +int archive_write_set_bytes_per_block(struct archive *, + int bytes_per_block); +int archive_write_get_bytes_per_block(struct archive *); +/* XXX This is badly misnamed; suggestions appreciated. XXX */ +int archive_write_set_bytes_in_last_block(struct archive *, + int bytes_in_last_block); +int archive_write_get_bytes_in_last_block(struct archive *); + +/* The dev/ino of a file that won't be archived. This is used + * to avoid recursively adding an archive to itself. */ +int archive_write_set_skip_file(struct archive *, dev_t, ino_t); + +int archive_write_set_compression_bzip2(struct archive *); +int archive_write_set_compression_gzip(struct archive *); +int archive_write_set_compression_none(struct archive *); +int archive_write_set_compression_program(struct archive *, + const char *cmd); +/* A convenience function to set the format based on the code or name. */ +int archive_write_set_format(struct archive *, int format_code); +int archive_write_set_format_by_name(struct archive *, + const char *name); +/* To minimize link pollution, use one or more of the following. */ +int archive_write_set_format_ar_bsd(struct archive *); +int archive_write_set_format_ar_svr4(struct archive *); +int archive_write_set_format_cpio(struct archive *); +/* TODO: int archive_write_set_format_old_tar(struct archive *); */ +int archive_write_set_format_pax(struct archive *); +int archive_write_set_format_pax_restricted(struct archive *); +int archive_write_set_format_shar(struct archive *); +int archive_write_set_format_shar_dump(struct archive *); +int archive_write_set_format_ustar(struct archive *); +int archive_write_open(struct archive *, void *, + archive_open_callback *, archive_write_callback *, + archive_close_callback *); +int archive_write_open_fd(struct archive *, int _fd); +int archive_write_open_filename(struct archive *, const char *_file); +/* A deprecated synonym for archive_write_open_filename() */ +int archive_write_open_file(struct archive *, const char *_file); +int archive_write_open_FILE(struct archive *, FILE *); +/* _buffSize is the size of the buffer, _used refers to a variable that + * will be updated after each write into the buffer. */ +int archive_write_open_memory(struct archive *, + void *_buffer, size_t _buffSize, size_t *_used); + +/* + * Note that the library will truncate writes beyond the size provided + * to archive_write_header or pad if the provided data is short. + */ +int archive_write_header(struct archive *, + struct archive_entry *); +#if ARCHIVE_API_VERSION > 1 +ssize_t archive_write_data(struct archive *, const void *, size_t); +#else +/* Temporarily allow library to compile with either 1.x or 2.0 API. */ +/* This was erroneously declared to return "int" in libarchive 1.x. */ +int archive_write_data(struct archive *, const void *, size_t); +#endif +ssize_t archive_write_data_block(struct archive *, const void *, size_t, off_t); +int archive_write_finish_entry(struct archive *); +int archive_write_close(struct archive *); +#if ARCHIVE_API_VERSION > 1 +int archive_write_finish(struct archive *); +#else +/* Temporarily allow library to compile with either 1.x or 2.0 API. */ +/* Return value was incorrect in libarchive 1.x. */ +void archive_write_finish(struct archive *); +#endif + +/*- + * To create objects on disk: + * 1) Ask archive_write_disk_new for a new archive_write_disk object. + * 2) Set any global properties. In particular, you should set + * the compression and format to use. + * 3) For each entry: + * - construct an appropriate struct archive_entry structure + * - archive_write_header to create the file/dir/etc on disk + * - archive_write_data to write the entry data + * 4) archive_write_finish to cleanup the writer and release resources + * + * In particular, you can use this in conjunction with archive_read() + * to pull entries out of an archive and create them on disk. + */ +struct archive *archive_write_disk_new(void); +/* This file will not be overwritten. */ +int archive_write_disk_set_skip_file(struct archive *, + dev_t, ino_t); +/* Set flags to control how the next item gets created. */ +int archive_write_disk_set_options(struct archive *, + int flags); +/* + * The lookup functions are given uname/uid (or gname/gid) pairs and + * return a uid (gid) suitable for this system. These are used for + * restoring ownership and for setting ACLs. The default functions + * are naive, they just return the uid/gid. These are small, so reasonable + * for applications that don't need to preserve ownership; they + * are probably also appropriate for applications that are doing + * same-system backup and restore. + */ +/* + * The "standard" lookup functions use common system calls to lookup + * the uname/gname, falling back to the uid/gid if the names can't be + * found. They cache lookups and are reasonably fast, but can be very + * large, so they are not used unless you ask for them. In + * particular, these match the specifications of POSIX "pax" and old + * POSIX "tar". + */ +int archive_write_disk_set_standard_lookup(struct archive *); +/* + * If neither the default (naive) nor the standard (big) functions suit + * your needs, you can write your own and register them. Be sure to + * include a cleanup function if you have allocated private data. + */ +int archive_write_disk_set_group_lookup(struct archive *, + void *private_data, + gid_t (*loookup)(void *, const char *gname, gid_t gid), + void (*cleanup)(void *)); +int archive_write_disk_set_user_lookup(struct archive *, + void *private_data, + uid_t (*)(void *, const char *uname, uid_t uid), + void (*cleanup)(void *)); + +/* + * Accessor functions to read/set various information in + * the struct archive object: + */ +/* Bytes written after compression or read before decompression. */ +int64_t archive_position_compressed(struct archive *); +/* Bytes written to compressor or read from decompressor. */ +int64_t archive_position_uncompressed(struct archive *); + +const char *archive_compression_name(struct archive *); +int archive_compression(struct archive *); +int archive_errno(struct archive *); +const char *archive_error_string(struct archive *); +const char *archive_format_name(struct archive *); +int archive_format(struct archive *); +void archive_clear_error(struct archive *); +void archive_set_error(struct archive *, int _err, const char *fmt, ...); +void archive_copy_error(struct archive *dest, struct archive *src); + +#ifdef __cplusplus +} +#endif + +#endif /* !ARCHIVE_H_INCLUDED */ diff --git a/contrib/libarchive-2.1/libarchive/archive_check_magic.c b/contrib/libarchive-2.1/libarchive/archive_check_magic.c new file mode 100644 index 0000000000..715486dcfe --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_check_magic.c @@ -0,0 +1,118 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_check_magic.c,v 1.8 2007/04/02 00:15:45 kientzle Exp $"); + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "archive_private.h" + +static void +errmsg(const char *m) +{ + write(STDERR_FILENO, m, strlen(m)); +} + +static void +diediedie(void) +{ + *(char *)0 = 1; /* Deliberately segfault and force a coredump. */ + _exit(1); /* If that didn't work, just exit with an error. */ +} + +static const char * +state_name(unsigned s) +{ + switch (s) { + case ARCHIVE_STATE_NEW: return ("new"); + case ARCHIVE_STATE_HEADER: return ("header"); + case ARCHIVE_STATE_DATA: return ("data"); + case ARCHIVE_STATE_EOF: return ("eof"); + case ARCHIVE_STATE_CLOSED: return ("closed"); + case ARCHIVE_STATE_FATAL: return ("fatal"); + default: return ("??"); + } +} + + +static void +write_all_states(unsigned int states) +{ + unsigned int lowbit; + + /* A trick for computing the lowest set bit. */ + while ((lowbit = states & (-states)) != 0) { + states &= ~lowbit; /* Clear the low bit. */ + errmsg(state_name(lowbit)); + if (states != 0) + errmsg("/"); + } +} + +/* + * Check magic value and current state; bail if it isn't valid. + * + * This is designed to catch serious programming errors that violate + * the libarchive API. + */ +void +__archive_check_magic(struct archive *a, unsigned int magic, + unsigned int state, const char *function) +{ + if (a->magic != magic) { + errmsg("INTERNAL ERROR: Function "); + errmsg(function); + errmsg(" invoked with invalid struct archive structure.\n"); + diediedie(); + } + + if (state == ARCHIVE_STATE_ANY) + return; + + if ((a->state & state) == 0) { + errmsg("INTERNAL ERROR: Function '"); + errmsg(function); + errmsg("' invoked with archive structure in state '"); + write_all_states(a->state); + errmsg("', should be in state '"); + write_all_states(state); + errmsg("'\n"); + diediedie(); + } +} diff --git a/contrib/libarchive-2.1/libarchive/archive_entry.3 b/contrib/libarchive-2.1/libarchive/archive_entry.3 new file mode 100644 index 0000000000..807fd32744 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_entry.3 @@ -0,0 +1,344 @@ +.\" 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. +.\" +.\" $FreeBSD: src/lib/libarchive/archive_entry.3,v 1.13 2007/03/03 07:37:36 kientzle Exp $ +.\" +.Dd December 15, 2003 +.Dt archive_entry 3 +.Os +.Sh NAME +.Nm archive_entry_acl_add_entry , +.Nm archive_entry_acl_add_entry_w , +.Nm archive_entry_acl_clear , +.Nm archive_entry_acl_count , +.Nm archive_entry_acl_next , +.Nm archive_entry_acl_next_w , +.Nm archive_entry_acl_reset , +.Nm archive_entry_acl_text_w , +.Nm archive_entry_atime , +.Nm archive_entry_atime_nsec , +.Nm archive_entry_clear , +.Nm archive_entry_clone , +.Nm archive_entry_copy_fflags_text_w , +.Nm archive_entry_copy_gname_w , +.Nm archive_entry_copy_hardlink , +.Nm archive_entry_copy_hardlink_w , +.Nm archive_entry_copy_pathname_w , +.Nm archive_entry_copy_stat , +.Nm archive_entry_copy_symlink , +.Nm archive_entry_copy_symlink_w , +.Nm archive_entry_copy_uname_w , +.Nm archive_entry_dev , +.Nm archive_entry_fflags , +.Nm archive_entry_fflags_text , +.Nm archive_entry_free , +.Nm archive_entry_gid , +.Nm archive_entry_gname , +.Nm archive_entry_hardlink , +.Nm archive_entry_ino , +.Nm archive_entry_mode , +.Nm archive_entry_mtime , +.Nm archive_entry_mtime_nsec , +.Nm archive_entry_new , +.Nm archive_entry_pathname , +.Nm archive_entry_pathname_w , +.Nm archive_entry_rdev , +.Nm archive_entry_rdevmajor , +.Nm archive_entry_rdevminor , +.Nm archive_entry_set_fflags , +.Nm archive_entry_set_gid , +.Nm archive_entry_set_gname , +.Nm archive_entry_set_hardlink , +.Nm archive_entry_set_link , +.Nm archive_entry_set_mode , +.Nm archive_entry_set_mtime , +.Nm archive_entry_set_pathname , +.Nm archive_entry_set_rdevmajor , +.Nm archive_entry_set_rdevminor , +.Nm archive_entry_set_size , +.Nm archive_entry_set_symlink , +.Nm archive_entry_set_uid , +.Nm archive_entry_set_uname , +.Nm archive_entry_size , +.Nm archive_entry_stat , +.Nm archive_entry_symlink , +.Nm archive_entry_uid , +.Nm archive_entry_uname +.Nd functions for manipulating archive entry descriptions +.Sh SYNOPSIS +.In archive_entry.h +.Ft void +.Fn archive_entry_acl_add_entry "struct archive_entry *" "int type" "int permset" "int tag" "int qual" "const char *name" +.Ft void +.Fn archive_entry_acl_add_entry_w "struct archive_entry *" "int type" "int permset" "int tag" "int qual" "const wchar_t *name" +.Ft void +.Fn archive_entry_acl_clear "struct archive_entry *" +.Ft int +.Fn archive_entry_acl_count "struct archive_entry *" "int type" +.Ft int +.Fn archive_entry_acl_next "struct archive_entry *" "int want_type" "int *type" "int *permset" "int *tag" "int *qual" "const char **name" +.Ft int +.Fn archive_entry_acl_next_w "struct archive_entry *" "int want_type" "int *type" "int *permset" "int *tag" "int *qual" "const wchar_t **name" +.Ft int +.Fn archive_entry_acl_reset "struct archive_entry *" "int want_type" +.Ft const wchar_t * +.Fn archive_entry_acl_text_w "struct archive_entry *" "int flags" +.Ft time_t +.Fn archive_entry_atime "struct archive_entry *" +.Ft long +.Fn archive_entry_atime_nsec "struct archive_entry *" +.Ft "struct archive_entry *" +.Fn archive_entry_clear "struct archive_entry *" +.Ft struct archive_entry * +.Fn archive_entry_clone "struct archive_entry *" +.Ft const wchar_t * +.Fn archive_entry_copy_fflags_text_w "struct archive_entry *" "const wchar_t *" +.Ft void +.Fn archive_entry_copy_gname_w "struct archive_entry *" "const wchar_t *" +.Ft void +.Fn archive_entry_copy_hardlink "struct archive_entry *" "const char *" +.Ft void +.Fn archive_entry_copy_hardlink_w "struct archive_entry *" "const wchar_t *" +.Ft void +.Fn archive_entry_copy_pathname_w "struct archive_entry *" "const wchar_t *" +.Ft void +.Fn archive_entry_copy_stat "struct archive_entry *" "const struct stat *" +.Ft void +.Fn archive_entry_copy_symlink "struct archive_entry *" "const char *" +.Ft void +.Fn archive_entry_copy_symlink_w "struct archive_entry *" "const wchar_t *" +.Ft void +.Fn archive_entry_copy_uname_w "struct archive_entry *" "const wchar_t *" +.Ft dev_t +.Fn archive_entry_dev "struct archive_entry *" +.Ft void +.Fn archive_entry_fflags "struct archive_entry *" "unsigned long *set" "unsigned long *clear" +.Ft const char * +.Fn archive_entry_fflags_text "struct archive_entry *" +.Ft void +.Fn archive_entry_free "struct archive_entry *" +.Ft const char * +.Fn archive_entry_gname "struct archive_entry *" +.Ft const char * +.Fn archive_entry_hardlink "struct archive_entry *" +.Ft ino_t +.Fn archive_entry_ino "struct archive_entry *" +.Ft mode_t +.Fn archive_entry_mode "struct archive_entry *" +.Ft time_t +.Fn archive_entry_mtime "struct archive_entry *" +.Ft long +.Fn archive_entry_mtime_nsec "struct archive_entry *" +.Ft struct archive_entry * +.Fn archive_entry_new "void" +.Ft const char * +.Fn archive_entry_pathname "struct archive_entry *" +.Ft const wchar_t * +.Fn archive_entry_pathname_w "struct archive_entry *" +.Ft dev_t +.Fn archive_entry_rdev "struct archive_entry *" +.Ft dev_t +.Fn archive_entry_rdevmajor "struct archive_entry *" +.Ft dev_t +.Fn archive_entry_rdevminor "struct archive_entry *" +.Ft void +.Fn archive_entry_set_fflags "struct archive_entry *" "unsigned long set" "unsigned long clear" +.Ft void +.Fn archive_entry_set_gid "struct archive_entry *" "gid_t" +.Ft void +.Fn archive_entry_set_gname "struct archive_entry *" "const char *" +.Ft void +.Fn archive_entry_set_hardlink "struct archive_entry *" "const char *" +.Ft void +.Fn archive_entry_set_link "struct archive_entry *" "const char *" +.Ft void +.Fn archive_entry_set_mode "struct archive_entry *" "mode_t" +.Ft void +.Fn archive_entry_set_mtime "struct archive_entry *" "time_t" "long nanos" +.Ft void +.Fn archive_entry_set_pathname "struct archive_entry *" "const char *" +.Ft void +.Fn archive_entry_set_rdevmajor "struct archive_entry *" "dev_t" +.Ft void +.Fn archive_entry_set_rdevminor "struct archive_entry *" "dev_t" +.Ft void +.Fn archive_entry_set_size "struct archive_entry *" "int64_t" +.Ft void +.Fn archive_entry_set_symlink "struct archive_entry *" "const char *" +.Ft void +.Fn archive_entry_set_uid "struct archive_entry *" "uid_t" +.Ft void +.Fn archive_entry_set_uname "struct archive_entry *" "const char *" +.Ft int64_t +.Fn archive_entry_size "struct archive_entry *" +.Ft const struct stat * +.Fn archive_entry_stat "struct archive_entry *" +.Ft const char * +.Fn archive_entry_symlink "struct archive_entry *" +.Ft const char * +.Fn archive_entry_uname "struct archive_entry *" +.Sh DESCRIPTION +These functions create and manipulate data objects that +represent entries within an archive. +You can think of a +.Tn struct archive_entry +as a heavy-duty version of +.Tn struct stat : +it includes everything from +.Tn struct stat +plus associated pathname, textual group and user names, etc. +These objects are used by +.Xr libarchive 3 +to represent the metadata associated with a particular +entry in an archive. +.Ss Create and Destroy +There are functions to allocate, destroy, clear, and copy +.Va archive_entry +objects: +.Bl -tag -compact -width indent +.It Fn archive_entry_clear +Erases the object, resetting all internal fields to the +same state as a newly-created object. +This is provided to allow you to quickly recycle objects +without thrashing the heap. +.It Fn archive_entry_clone +A deep copy operation; all text fields are duplicated. +.It Fn archive_entry_free +Releases the +.Tn struct archive_entry +object. +.It Fn archive_entry_new +Allocate and return a blank +.Tn struct archive_entry +object. +.El +.Ss Set and Get Functions +Most of the functions here set or read entries in an object. +Such functions have one of the following forms: +.Bl -tag -compact -width indent +.It Fn archive_entry_set_XXXX +Stores the provided data in the object. +In particular, for strings, the pointer is stored, +not the referenced string. +.It Fn archive_entry_copy_XXXX +As above, except that the referenced data is copied +into the object. +.It Fn archive_entry_XXXX +Returns the specified data. +In the case of strings, a const-qualified pointer to +the string is returned. +.El +String data can be set or accessed as wide character strings +or normal +.Va char +strings. +The functions that use wide character strings are suffixed with +.Cm _w . +Note that these are different representations of the same data: +For example, if you store a narrow string and read the corresponding +wide string, the object will transparently convert formats +using the current locale. +Similarly, if you store a wide string and then store a +narrow string for the same data, the previously-set wide string will +be discarded in favor of the new data. +.Pp +There are a few set/get functions that merit additional description: +.Bl -tag -compact -width indent +.It Fn archive_entry_set_link +This function sets the symlink field if it is already set. +Otherwise, it sets the hardlink field. +.El +.Ss File Flags +File flags are transparently converted between a bitmap +representation and a textual format. +For example, if you set the bitmap and ask for text, the library +will build a canonical text format. +However, if you set a text format and request a text format, +you will get back the same text, even if it is ill-formed. +If you need to canonicalize a textual flags string, you should first set the +text form, then request the bitmap form, then use that to set the bitmap form. +Setting the bitmap format will clear the internal text representation +and force it to be reconstructed when you next request the text form. +.Pp +The bitmap format consists of two integers, one containing bits +that should be set, the other specifying bits that should be +cleared. +Bits not mentioned in either bitmap will be ignored. +Usually, the bitmap of bits to be cleared will be set to zero. +In unusual circumstances, you can force a fully-specified set +of file flags by setting the bitmap of flags to clear to the complement +of the bitmap of flags to set. +(This differs from +.Xr fflagstostr 3 , +which only includes names for set bits.) +Converting a bitmap to a textual string is a platform-specific +operation; bits that are not meaningful on the current platform +will be ignored. +.Pp +The canonical text format is a comma-separated list of flag names. +The +.Fn archive_entry_copy_fflags_text_w +function parses the provided text and sets the internal bitmap values. +This is a platform-specific operation; names that are not meaningful +on the current platform will be ignored. +The function returns a pointer to the start of the first name that was not +recognized, or NULL if every name was recognized. +Note that every name--including names that follow an unrecognized name--will +be evaluated, and the bitmaps will be set to reflect every name that is +recognized. +(In particular, this differs from +.Xr strtofflags 3 , +which stops parsing at the first unrecognized name.) +.Ss ACL Handling +XXX This needs serious help. +XXX +.Pp +An +.Dq Access Control List +(ACL) is a list of permissions that grant access to particular users or +groups beyond what would normally be provided by standard POSIX mode bits. +The ACL handling here addresses some deficiencies in the POSIX.1e draft 17 ACL +specification. +In particular, POSIX.1e draft 17 specifies several different formats, but +none of those formats include both textual user/group names and numeric +UIDs/GIDs. +.Pp +XXX explain ACL stuff XXX +.\" .Sh EXAMPLE +.\" .Sh RETURN VALUES +.\" .Sh ERRORS +.Sh SEE ALSO +.Xr archive 3 +.Sh HISTORY +The +.Nm libarchive +library first appeared in +.Fx 5.3 . +.Sh AUTHORS +.An -nosplit +The +.Nm libarchive +library was written by +.An Tim Kientzle Aq kientzle@acm.org . +.\" .Sh BUGS diff --git a/contrib/libarchive-2.1/libarchive/archive_entry.c b/contrib/libarchive-2.1/libarchive/archive_entry.c new file mode 100644 index 0000000000..bd6c85cb02 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_entry.c @@ -0,0 +1,1842 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry.c,v 1.42 2007/04/14 02:37:22 kientzle Exp $"); + +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef MAJOR_IN_MKDEV +#include +# if !defined makedev && (defined mkdev || defined _WIN32 || defined __WIN32__) +# define makedev mkdev +# endif +#else +#ifdef MAJOR_IN_SYSMACROS +#include +#endif +#endif +#ifdef HAVE_EXT2FS_EXT2_FS_H +#include /* for Linux file flags */ +#endif +#ifdef HAVE_LIMITS_H +#include +#endif +#ifdef HAVE_LINUX_FS_H +#include /* for Linux file flags */ +#endif +#ifdef HAVE_LINUX_EXT2_FS_H +#include /* for Linux file flags */ +#endif +#include +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_WCHAR_H +#include +#endif + +#ifndef HAVE_WCSCPY +static wchar_t * wcscpy(wchar_t *s1, const wchar_t *s2) +{ + wchar_t *dest = s1; + while ((*s1 = *s2) != L'\0') + ++s1, ++s2; + return dest; +} +#endif +#ifndef HAVE_WCSLEN +static size_t wcslen(const wchar_t *s) +{ + const wchar_t *p = s; + while (*p != L'\0') + ++p; + return p - s; +} +#endif +#ifndef HAVE_WMEMCMP +/* Good enough for simple equality testing, but not for sorting. */ +#define wmemcmp(a,b,i) memcmp((a), (b), (i) * sizeof(wchar_t)) +#endif +#ifndef HAVE_WMEMCPY +#define wmemcpy(a,b,i) (wchar_t *)memcpy((a), (b), (i) * sizeof(wchar_t)) +#endif + +#include "archive.h" +#include "archive_entry.h" +#include "archive_private.h" + +#undef max +#define max(a, b) ((a)>(b)?(a):(b)) + +/* + * Handle wide character (i.e., Unicode) and non-wide character + * strings transparently. + * + */ + +struct aes { + const char *aes_mbs; + char *aes_mbs_alloc; + const wchar_t *aes_wcs; + wchar_t *aes_wcs_alloc; +}; + +struct ae_acl { + struct ae_acl *next; + int type; /* E.g., access or default */ + int tag; /* E.g., user/group/other/mask */ + int permset; /* r/w/x bits */ + int id; /* uid/gid for user/group */ + struct aes name; /* uname/gname */ +}; + +struct ae_xattr { + struct ae_xattr *next; + + char *name; + void *value; + size_t size; +}; + +static void aes_clean(struct aes *); +static void aes_copy(struct aes *dest, struct aes *src); +static const char * aes_get_mbs(struct aes *); +static const wchar_t * aes_get_wcs(struct aes *); +static void aes_set_mbs(struct aes *, const char *mbs); +static void aes_copy_mbs(struct aes *, const char *mbs); +/* static void aes_set_wcs(struct aes *, const wchar_t *wcs); */ +static void aes_copy_wcs(struct aes *, const wchar_t *wcs); +static void aes_copy_wcs_len(struct aes *, const wchar_t *wcs, size_t); + +static char * ae_fflagstostr(unsigned long bitset, unsigned long bitclear); +static const wchar_t *ae_wcstofflags(const wchar_t *stringp, + unsigned long *setp, unsigned long *clrp); +static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, + const wchar_t *wname, int perm, int id); +static void append_id_w(wchar_t **wp, int id); + +static int acl_special(struct archive_entry *entry, + int type, int permset, int tag); +static struct ae_acl *acl_new_entry(struct archive_entry *entry, + int type, int permset, int tag, int id); +static int isint_w(const wchar_t *start, const wchar_t *end, int *result); +static void next_field_w(const wchar_t **wp, const wchar_t **start, + const wchar_t **end, wchar_t *sep); +static int prefix_w(const wchar_t *start, const wchar_t *end, + const wchar_t *test); +static void +archive_entry_acl_add_entry_w_len(struct archive_entry *entry, int type, + int permset, int tag, int id, const wchar_t *name, size_t); + + +/* + * Description of an archive entry. + * + * Basically, this is a "struct stat" with a few text fields added in. + * + * TODO: Add "comment", "charset", and possibly other entries + * that are supported by "pax interchange" format. However, GNU, ustar, + * cpio, and other variants don't support these features, so they're not an + * excruciatingly high priority right now. + * + * TODO: "pax interchange" format allows essentially arbitrary + * key/value attributes to be attached to any entry. Supporting + * such extensions may make this library useful for special + * applications (e.g., a package manager could attach special + * package-management attributes to each entry). There are tricky + * API issues involved, so this is not going to happen until + * there's a real demand for it. + * + * TODO: Design a good API for handling sparse files. + */ +struct archive_entry { + /* + * Note that ae_stat.st_mode & S_IFMT can be 0! + * + * This occurs when the actual file type of the object is not + * in the archive. For example, 'tar' archives store + * hardlinks without marking the type of the underlying + * object. + */ + struct stat ae_stat; + + /* + * Use aes here so that we get transparent mbs<->wcs conversions. + */ + struct aes ae_fflags_text; /* Text fflags per fflagstostr(3) */ + unsigned long ae_fflags_set; /* Bitmap fflags */ + unsigned long ae_fflags_clear; + struct aes ae_gname; /* Name of owning group */ + struct aes ae_hardlink; /* Name of target for hardlink */ + struct aes ae_pathname; /* Name of entry */ + struct aes ae_symlink; /* symlink contents */ + struct aes ae_uname; /* Name of owner */ + + struct ae_acl *acl_head; + struct ae_acl *acl_p; + int acl_state; /* See acl_next for details. */ + wchar_t *acl_text_w; + + struct ae_xattr *xattr_head; + struct ae_xattr *xattr_p; +}; + +static void +aes_clean(struct aes *aes) +{ + if (aes->aes_mbs_alloc) { + free(aes->aes_mbs_alloc); + aes->aes_mbs_alloc = NULL; + } + if (aes->aes_wcs_alloc) { + free(aes->aes_wcs_alloc); + aes->aes_wcs_alloc = NULL; + } + memset(aes, 0, sizeof(*aes)); +} + +static void +aes_copy(struct aes *dest, struct aes *src) +{ + *dest = *src; + if (src->aes_mbs != NULL) { + dest->aes_mbs_alloc = strdup(src->aes_mbs); + dest->aes_mbs = dest->aes_mbs_alloc; + if (dest->aes_mbs == NULL) + __archive_errx(1, "No memory for aes_copy()"); + } + + if (src->aes_wcs != NULL) { + dest->aes_wcs_alloc = (wchar_t *)malloc((wcslen(src->aes_wcs) + 1) + * sizeof(wchar_t)); + dest->aes_wcs = dest->aes_wcs_alloc; + if (dest->aes_wcs == NULL) + __archive_errx(1, "No memory for aes_copy()"); + wcscpy(dest->aes_wcs_alloc, src->aes_wcs); + } +} + +static const char * +aes_get_mbs(struct aes *aes) +{ + if (aes->aes_mbs == NULL && aes->aes_wcs == NULL) + return NULL; + if (aes->aes_mbs == NULL && aes->aes_wcs != NULL) { + /* + * XXX Need to estimate the number of byte in the + * multi-byte form. Assume that, on average, wcs + * chars encode to no more than 3 bytes. There must + * be a better way... XXX + */ + size_t mbs_length = wcslen(aes->aes_wcs) * 3 + 64; + + aes->aes_mbs_alloc = (char *)malloc(mbs_length); + aes->aes_mbs = aes->aes_mbs_alloc; + if (aes->aes_mbs == NULL) + __archive_errx(1, "No memory for aes_get_mbs()"); + wcstombs(aes->aes_mbs_alloc, aes->aes_wcs, mbs_length - 1); + aes->aes_mbs_alloc[mbs_length - 1] = 0; + } + return (aes->aes_mbs); +} + +static const wchar_t * +aes_get_wcs(struct aes *aes) +{ + if (aes->aes_wcs == NULL && aes->aes_mbs == NULL) + return NULL; + if (aes->aes_wcs == NULL && aes->aes_mbs != NULL) { + /* + * No single byte will be more than one wide character, + * so this length estimate will always be big enough. + */ + size_t wcs_length = strlen(aes->aes_mbs); + + aes->aes_wcs_alloc + = (wchar_t *)malloc((wcs_length + 1) * sizeof(wchar_t)); + aes->aes_wcs = aes->aes_wcs_alloc; + if (aes->aes_wcs == NULL) + __archive_errx(1, "No memory for aes_get_wcs()"); + mbstowcs(aes->aes_wcs_alloc, aes->aes_mbs, wcs_length); + aes->aes_wcs_alloc[wcs_length] = 0; + } + return (aes->aes_wcs); +} + +static void +aes_set_mbs(struct aes *aes, const char *mbs) +{ + if (aes->aes_mbs_alloc) { + free(aes->aes_mbs_alloc); + aes->aes_mbs_alloc = NULL; + } + if (aes->aes_wcs_alloc) { + free(aes->aes_wcs_alloc); + aes->aes_wcs_alloc = NULL; + } + aes->aes_mbs = mbs; + aes->aes_wcs = NULL; +} + +static void +aes_copy_mbs(struct aes *aes, const char *mbs) +{ + if (aes->aes_mbs_alloc) { + free(aes->aes_mbs_alloc); + aes->aes_mbs_alloc = NULL; + } + if (aes->aes_wcs_alloc) { + free(aes->aes_wcs_alloc); + aes->aes_wcs_alloc = NULL; + } + aes->aes_mbs_alloc = (char *)malloc((strlen(mbs) + 1) * sizeof(char)); + if (aes->aes_mbs_alloc == NULL) + __archive_errx(1, "No memory for aes_copy_mbs()"); + strcpy(aes->aes_mbs_alloc, mbs); + aes->aes_mbs = aes->aes_mbs_alloc; + aes->aes_wcs = NULL; +} + +#if 0 +static void +aes_set_wcs(struct aes *aes, const wchar_t *wcs) +{ + if (aes->aes_mbs_alloc) { + free(aes->aes_mbs_alloc); + aes->aes_mbs_alloc = NULL; + } + if (aes->aes_wcs_alloc) { + free(aes->aes_wcs_alloc); + aes->aes_wcs_alloc = NULL; + } + aes->aes_mbs = NULL; + aes->aes_wcs = wcs; +} +#endif + +static void +aes_copy_wcs(struct aes *aes, const wchar_t *wcs) +{ + aes_copy_wcs_len(aes, wcs, wcslen(wcs)); +} + +static void +aes_copy_wcs_len(struct aes *aes, const wchar_t *wcs, size_t len) +{ + if (aes->aes_mbs_alloc) { + free(aes->aes_mbs_alloc); + aes->aes_mbs_alloc = NULL; + } + if (aes->aes_wcs_alloc) { + free(aes->aes_wcs_alloc); + aes->aes_wcs_alloc = NULL; + } + aes->aes_mbs = NULL; + aes->aes_wcs_alloc = (wchar_t *)malloc((len + 1) * sizeof(wchar_t)); + if (aes->aes_wcs_alloc == NULL) + __archive_errx(1, "No memory for aes_copy_wcs()"); + wmemcpy(aes->aes_wcs_alloc, wcs, len); + aes->aes_wcs_alloc[len] = L'\0'; + aes->aes_wcs = aes->aes_wcs_alloc; +} + +struct archive_entry * +archive_entry_clear(struct archive_entry *entry) +{ + aes_clean(&entry->ae_fflags_text); + aes_clean(&entry->ae_gname); + aes_clean(&entry->ae_hardlink); + aes_clean(&entry->ae_pathname); + aes_clean(&entry->ae_symlink); + aes_clean(&entry->ae_uname); + archive_entry_acl_clear(entry); + archive_entry_xattr_clear(entry); + memset(entry, 0, sizeof(*entry)); + return entry; +} + +struct archive_entry * +archive_entry_clone(struct archive_entry *entry) +{ + struct archive_entry *entry2; + + /* Allocate new structure and copy over all of the fields. */ + entry2 = (struct archive_entry *)malloc(sizeof(*entry2)); + if (entry2 == NULL) + return (NULL); + memset(entry2, 0, sizeof(*entry2)); + entry2->ae_stat = entry->ae_stat; + entry2->ae_fflags_set = entry->ae_fflags_set; + entry2->ae_fflags_clear = entry->ae_fflags_clear; + + aes_copy(&entry2->ae_fflags_text, &entry->ae_fflags_text); + aes_copy(&entry2->ae_gname, &entry->ae_gname); + aes_copy(&entry2->ae_hardlink, &entry->ae_hardlink); + aes_copy(&entry2->ae_pathname, &entry->ae_pathname); + aes_copy(&entry2->ae_symlink, &entry->ae_symlink); + aes_copy(&entry2->ae_uname, &entry->ae_uname); + + /* XXX TODO: Copy ACL data over as well. XXX */ + /* XXX TODO: Copy xattr data over as well. XXX */ + return (entry2); +} + +void +archive_entry_free(struct archive_entry *entry) +{ + archive_entry_clear(entry); + free(entry); +} + +struct archive_entry * +archive_entry_new(void) +{ + struct archive_entry *entry; + + entry = (struct archive_entry *)malloc(sizeof(*entry)); + if (entry == NULL) + return (NULL); + memset(entry, 0, sizeof(*entry)); + return (entry); +} + +/* + * Functions for reading fields from an archive_entry. + */ + +time_t +archive_entry_atime(struct archive_entry *entry) +{ + return (entry->ae_stat.st_atime); +} + +long +archive_entry_atime_nsec(struct archive_entry *entry) +{ + (void)entry; /* entry can be unused here. */ + return (ARCHIVE_STAT_ATIME_NANOS(&entry->ae_stat)); +} + +time_t +archive_entry_ctime(struct archive_entry *entry) +{ + return (entry->ae_stat.st_ctime); +} + +long +archive_entry_ctime_nsec(struct archive_entry *entry) +{ + (void)entry; /* entry can be unused here. */ + return (ARCHIVE_STAT_CTIME_NANOS(&entry->ae_stat)); +} + +dev_t +archive_entry_dev(struct archive_entry *entry) +{ + return (entry->ae_stat.st_dev); +} + +mode_t +archive_entry_filetype(struct archive_entry *entry) +{ + return (S_IFMT & entry->ae_stat.st_mode); +} + +void +archive_entry_fflags(struct archive_entry *entry, + unsigned long *set, unsigned long *clear) +{ + *set = entry->ae_fflags_set; + *clear = entry->ae_fflags_clear; +} + +/* + * Note: if text was provided, this just returns that text. If you + * really need the text to be rebuilt in a canonical form, set the + * text, ask for the bitmaps, then set the bitmaps. (Setting the + * bitmaps clears any stored text.) This design is deliberate: if + * we're editing archives, we don't want to discard flags just because + * they aren't supported on the current system. The bitmap<->text + * conversions are platform-specific (see below). + */ +const char * +archive_entry_fflags_text(struct archive_entry *entry) +{ + const char *f; + char *p; + + f = aes_get_mbs(&entry->ae_fflags_text); + if (f != NULL) + return (f); + + if (entry->ae_fflags_set == 0 && entry->ae_fflags_clear == 0) + return (NULL); + + p = ae_fflagstostr(entry->ae_fflags_set, entry->ae_fflags_clear); + if (p == NULL) + return (NULL); + + aes_copy_mbs(&entry->ae_fflags_text, p); + free(p); + f = aes_get_mbs(&entry->ae_fflags_text); + return (f); +} + +gid_t +archive_entry_gid(struct archive_entry *entry) +{ + return (entry->ae_stat.st_gid); +} + +const char * +archive_entry_gname(struct archive_entry *entry) +{ + return (aes_get_mbs(&entry->ae_gname)); +} + +const wchar_t * +archive_entry_gname_w(struct archive_entry *entry) +{ + return (aes_get_wcs(&entry->ae_gname)); +} + +const char * +archive_entry_hardlink(struct archive_entry *entry) +{ + return (aes_get_mbs(&entry->ae_hardlink)); +} + +const wchar_t * +archive_entry_hardlink_w(struct archive_entry *entry) +{ + return (aes_get_wcs(&entry->ae_hardlink)); +} + +ino_t +archive_entry_ino(struct archive_entry *entry) +{ + return (entry->ae_stat.st_ino); +} + +mode_t +archive_entry_mode(struct archive_entry *entry) +{ + return (entry->ae_stat.st_mode); +} + +time_t +archive_entry_mtime(struct archive_entry *entry) +{ + return (entry->ae_stat.st_mtime); +} + +long +archive_entry_mtime_nsec(struct archive_entry *entry) +{ + (void)entry; /* entry can be unused here. */ + return (ARCHIVE_STAT_MTIME_NANOS(&entry->ae_stat)); +} + +unsigned int +archive_entry_nlink(struct archive_entry *entry) +{ + return (entry->ae_stat.st_nlink); +} + +const char * +archive_entry_pathname(struct archive_entry *entry) +{ + return (aes_get_mbs(&entry->ae_pathname)); +} + +const wchar_t * +archive_entry_pathname_w(struct archive_entry *entry) +{ + return (aes_get_wcs(&entry->ae_pathname)); +} + +dev_t +archive_entry_rdev(struct archive_entry *entry) +{ + return (entry->ae_stat.st_rdev); +} + +dev_t +archive_entry_rdevmajor(struct archive_entry *entry) +{ + return (major(entry->ae_stat.st_rdev)); +} + +dev_t +archive_entry_rdevminor(struct archive_entry *entry) +{ + return (minor(entry->ae_stat.st_rdev)); +} + +int64_t +archive_entry_size(struct archive_entry *entry) +{ + return (entry->ae_stat.st_size); +} + +const struct stat * +archive_entry_stat(struct archive_entry *entry) +{ + return (&entry->ae_stat); +} + +const char * +archive_entry_symlink(struct archive_entry *entry) +{ + return (aes_get_mbs(&entry->ae_symlink)); +} + +const wchar_t * +archive_entry_symlink_w(struct archive_entry *entry) +{ + return (aes_get_wcs(&entry->ae_symlink)); +} + +uid_t +archive_entry_uid(struct archive_entry *entry) +{ + return (entry->ae_stat.st_uid); +} + +const char * +archive_entry_uname(struct archive_entry *entry) +{ + return (aes_get_mbs(&entry->ae_uname)); +} + +const wchar_t * +archive_entry_uname_w(struct archive_entry *entry) +{ + return (aes_get_wcs(&entry->ae_uname)); +} + +/* + * Functions to set archive_entry properties. + */ + +/* + * Note "copy" not "set" here. The "set" functions that accept a pointer + * only store the pointer; they don't copy the underlying object. + */ +void +archive_entry_copy_stat(struct archive_entry *entry, const struct stat *st) +{ + entry->ae_stat = *st; +} + +void +archive_entry_set_filetype(struct archive_entry *entry, unsigned int type) +{ + entry->ae_stat.st_mode &= ~AE_IFMT; + entry->ae_stat.st_mode |= AE_IFMT & type; +} + +void +archive_entry_set_fflags(struct archive_entry *entry, + unsigned long set, unsigned long clear) +{ + aes_clean(&entry->ae_fflags_text); + entry->ae_fflags_set = set; + entry->ae_fflags_clear = clear; +} + +const wchar_t * +archive_entry_copy_fflags_text_w(struct archive_entry *entry, + const wchar_t *flags) +{ + aes_copy_wcs(&entry->ae_fflags_text, flags); + return (ae_wcstofflags(flags, + &entry->ae_fflags_set, &entry->ae_fflags_clear)); +} + +void +archive_entry_set_gid(struct archive_entry *entry, gid_t g) +{ + entry->ae_stat.st_gid = g; +} + +void +archive_entry_set_gname(struct archive_entry *entry, const char *name) +{ + aes_set_mbs(&entry->ae_gname, name); +} + +void +archive_entry_copy_gname_w(struct archive_entry *entry, const wchar_t *name) +{ + aes_copy_wcs(&entry->ae_gname, name); +} + +void +archive_entry_set_ino(struct archive_entry *entry, unsigned long ino) +{ + entry->ae_stat.st_ino = ino; +} + +void +archive_entry_set_hardlink(struct archive_entry *entry, const char *target) +{ + aes_set_mbs(&entry->ae_hardlink, target); +} + +void +archive_entry_copy_hardlink(struct archive_entry *entry, const char *target) +{ + aes_copy_mbs(&entry->ae_hardlink, target); +} + +void +archive_entry_copy_hardlink_w(struct archive_entry *entry, const wchar_t *target) +{ + aes_copy_wcs(&entry->ae_hardlink, target); +} + +void +archive_entry_set_atime(struct archive_entry *entry, time_t t, long ns) +{ + entry->ae_stat.st_atime = t; + ARCHIVE_STAT_SET_ATIME_NANOS(&entry->ae_stat, ns); +} + +void +archive_entry_set_ctime(struct archive_entry *entry, time_t t, long ns) +{ + entry->ae_stat.st_ctime = t; + ARCHIVE_STAT_SET_CTIME_NANOS(&entry->ae_stat, ns); +} + +void +archive_entry_set_dev(struct archive_entry *entry, dev_t d) +{ + entry->ae_stat.st_dev = d; +} + +void +archive_entry_set_devmajor(struct archive_entry *entry, dev_t m) +{ + dev_t d; + + d = entry->ae_stat.st_dev; + entry->ae_stat.st_dev = makedev(major(m), minor(d)); +} + +void +archive_entry_set_devminor(struct archive_entry *entry, dev_t m) +{ + dev_t d; + + d = entry->ae_stat.st_dev; + entry->ae_stat.st_dev = makedev(major(d), minor(m)); +} + +/* Set symlink if symlink is already set, else set hardlink. */ +void +archive_entry_set_link(struct archive_entry *entry, const char *target) +{ + if (entry->ae_symlink.aes_mbs != NULL || + entry->ae_symlink.aes_wcs != NULL) + aes_set_mbs(&entry->ae_symlink, target); + else + aes_set_mbs(&entry->ae_hardlink, target); +} + +void +archive_entry_set_mode(struct archive_entry *entry, mode_t m) +{ + entry->ae_stat.st_mode = m; +} + +void +archive_entry_set_mtime(struct archive_entry *entry, time_t m, long ns) +{ + entry->ae_stat.st_mtime = m; + ARCHIVE_STAT_SET_MTIME_NANOS(&entry->ae_stat, ns); +} + +void +archive_entry_set_nlink(struct archive_entry *entry, unsigned int nlink) +{ + entry->ae_stat.st_nlink = nlink; +} + +void +archive_entry_set_pathname(struct archive_entry *entry, const char *name) +{ + aes_set_mbs(&entry->ae_pathname, name); +} + +void +archive_entry_copy_pathname(struct archive_entry *entry, const char *name) +{ + aes_copy_mbs(&entry->ae_pathname, name); +} + +void +archive_entry_copy_pathname_w(struct archive_entry *entry, const wchar_t *name) +{ + aes_copy_wcs(&entry->ae_pathname, name); +} + +void +archive_entry_set_rdev(struct archive_entry *entry, dev_t m) +{ + entry->ae_stat.st_rdev = m; +} + +void +archive_entry_set_rdevmajor(struct archive_entry *entry, dev_t m) +{ + dev_t d; + + d = entry->ae_stat.st_rdev; + entry->ae_stat.st_rdev = makedev(major(m), minor(d)); +} + +void +archive_entry_set_rdevminor(struct archive_entry *entry, dev_t m) +{ + dev_t d; + + d = entry->ae_stat.st_rdev; + entry->ae_stat.st_rdev = makedev(major(d), minor(m)); +} + +void +archive_entry_set_size(struct archive_entry *entry, int64_t s) +{ + entry->ae_stat.st_size = s; +} + +void +archive_entry_set_symlink(struct archive_entry *entry, const char *linkname) +{ + aes_set_mbs(&entry->ae_symlink, linkname); +} + +void +archive_entry_copy_symlink(struct archive_entry *entry, const char *linkname) +{ + aes_copy_mbs(&entry->ae_symlink, linkname); +} + +void +archive_entry_copy_symlink_w(struct archive_entry *entry, const wchar_t *linkname) +{ + aes_copy_wcs(&entry->ae_symlink, linkname); +} + +void +archive_entry_set_uid(struct archive_entry *entry, uid_t u) +{ + entry->ae_stat.st_uid = u; +} + +void +archive_entry_set_uname(struct archive_entry *entry, const char *name) +{ + aes_set_mbs(&entry->ae_uname, name); +} + +void +archive_entry_copy_uname_w(struct archive_entry *entry, const wchar_t *name) +{ + aes_copy_wcs(&entry->ae_uname, name); +} + +/* + * ACL management. The following would, of course, be a lot simpler + * if: 1) the last draft of POSIX.1e were a really thorough and + * complete standard that addressed the needs of ACL archiving and 2) + * everyone followed it faithfully. Alas, neither is true, so the + * following is a lot more complex than might seem necessary to the + * uninitiated. + */ + +void +archive_entry_acl_clear(struct archive_entry *entry) +{ + struct ae_acl *ap; + + while (entry->acl_head != NULL) { + ap = entry->acl_head->next; + aes_clean(&entry->acl_head->name); + free(entry->acl_head); + entry->acl_head = ap; + } + if (entry->acl_text_w != NULL) { + free(entry->acl_text_w); + entry->acl_text_w = NULL; + } + entry->acl_p = NULL; + entry->acl_state = 0; /* Not counting. */ +} + +/* + * Add a single ACL entry to the internal list of ACL data. + */ +void +archive_entry_acl_add_entry(struct archive_entry *entry, + int type, int permset, int tag, int id, const char *name) +{ + struct ae_acl *ap; + + if (acl_special(entry, type, permset, tag) == 0) + return; + ap = acl_new_entry(entry, type, permset, tag, id); + if (ap == NULL) { + /* XXX Error XXX */ + return; + } + if (name != NULL && *name != '\0') + aes_copy_mbs(&ap->name, name); + else + aes_clean(&ap->name); +} + +/* + * As above, but with a wide-character name. + */ +void +archive_entry_acl_add_entry_w(struct archive_entry *entry, + int type, int permset, int tag, int id, const wchar_t *name) +{ + archive_entry_acl_add_entry_w_len(entry, type, permset, tag, id, name, wcslen(name)); +} + +void +archive_entry_acl_add_entry_w_len(struct archive_entry *entry, + int type, int permset, int tag, int id, const wchar_t *name, size_t len) +{ + struct ae_acl *ap; + + if (acl_special(entry, type, permset, tag) == 0) + return; + ap = acl_new_entry(entry, type, permset, tag, id); + if (ap == NULL) { + /* XXX Error XXX */ + return; + } + if (name != NULL && *name != L'\0' && len > 0) + aes_copy_wcs_len(&ap->name, name, len); + else + aes_clean(&ap->name); +} + +/* + * If this ACL entry is part of the standard POSIX permissions set, + * store the permissions in the stat structure and return zero. + */ +static int +acl_special(struct archive_entry *entry, int type, int permset, int tag) +{ + if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) { + switch (tag) { + case ARCHIVE_ENTRY_ACL_USER_OBJ: + entry->ae_stat.st_mode &= ~0700; + entry->ae_stat.st_mode |= (permset & 7) << 6; + return (0); + case ARCHIVE_ENTRY_ACL_GROUP_OBJ: + entry->ae_stat.st_mode &= ~0070; + entry->ae_stat.st_mode |= (permset & 7) << 3; + return (0); + case ARCHIVE_ENTRY_ACL_OTHER: + entry->ae_stat.st_mode &= ~0007; + entry->ae_stat.st_mode |= permset & 7; + return (0); + } + } + return (1); +} + +/* + * Allocate and populate a new ACL entry with everything but the + * name. + */ +static struct ae_acl * +acl_new_entry(struct archive_entry *entry, + int type, int permset, int tag, int id) +{ + struct ae_acl *ap; + + if (type != ARCHIVE_ENTRY_ACL_TYPE_ACCESS && + type != ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) + return (NULL); + if (entry->acl_text_w != NULL) { + free(entry->acl_text_w); + entry->acl_text_w = NULL; + } + + /* XXX TODO: More sanity-checks on the arguments XXX */ + + /* If there's a matching entry already in the list, overwrite it. */ + for (ap = entry->acl_head; ap != NULL; ap = ap->next) { + if (ap->type == type && ap->tag == tag && ap->id == id) { + ap->permset = permset; + return (ap); + } + } + + /* Add a new entry to the list. */ + ap = (struct ae_acl *)malloc(sizeof(*ap)); + if (ap == NULL) + return (NULL); + memset(ap, 0, sizeof(*ap)); + ap->next = entry->acl_head; + entry->acl_head = ap; + ap->type = type; + ap->tag = tag; + ap->id = id; + ap->permset = permset; + return (ap); +} + +/* + * Return a count of entries matching "want_type". + */ +int +archive_entry_acl_count(struct archive_entry *entry, int want_type) +{ + int count; + struct ae_acl *ap; + + count = 0; + ap = entry->acl_head; + while (ap != NULL) { + if ((ap->type & want_type) != 0) + count++; + ap = ap->next; + } + + if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) + count += 3; + return (count); +} + +/* + * Prepare for reading entries from the ACL data. Returns a count + * of entries matching "want_type", or zero if there are no + * non-extended ACL entries of that type. + */ +int +archive_entry_acl_reset(struct archive_entry *entry, int want_type) +{ + int count, cutoff; + + count = archive_entry_acl_count(entry, want_type); + + /* + * If the only entries are the three standard ones, + * then don't return any ACL data. (In this case, + * client can just use chmod(2) to set permissions.) + */ + if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) + cutoff = 3; + else + cutoff = 0; + + if (count > cutoff) + entry->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ; + else + entry->acl_state = 0; + entry->acl_p = entry->acl_head; + return (count); +} + +/* + * Return the next ACL entry in the list. Fake entries for the + * standard permissions and include them in the returned list. + */ + +int +archive_entry_acl_next(struct archive_entry *entry, int want_type, int *type, + int *permset, int *tag, int *id, const char **name) +{ + *name = NULL; + *id = -1; + + /* + * The acl_state is either zero (no entries available), -1 + * (reading from list), or an entry type (retrieve that type + * from ae_stat.st_mode). + */ + if (entry->acl_state == 0) + return (ARCHIVE_WARN); + + /* The first three access entries are special. */ + if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { + switch (entry->acl_state) { + case ARCHIVE_ENTRY_ACL_USER_OBJ: + *permset = (entry->ae_stat.st_mode >> 6) & 7; + *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; + *tag = ARCHIVE_ENTRY_ACL_USER_OBJ; + entry->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ; + return (ARCHIVE_OK); + case ARCHIVE_ENTRY_ACL_GROUP_OBJ: + *permset = (entry->ae_stat.st_mode >> 3) & 7; + *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; + *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; + entry->acl_state = ARCHIVE_ENTRY_ACL_OTHER; + return (ARCHIVE_OK); + case ARCHIVE_ENTRY_ACL_OTHER: + *permset = entry->ae_stat.st_mode & 7; + *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; + *tag = ARCHIVE_ENTRY_ACL_OTHER; + entry->acl_state = -1; + entry->acl_p = entry->acl_head; + return (ARCHIVE_OK); + default: + break; + } + } + + while (entry->acl_p != NULL && (entry->acl_p->type & want_type) == 0) + entry->acl_p = entry->acl_p->next; + if (entry->acl_p == NULL) { + entry->acl_state = 0; + return (ARCHIVE_EOF); /* End of ACL entries. */ + } + *type = entry->acl_p->type; + *permset = entry->acl_p->permset; + *tag = entry->acl_p->tag; + *id = entry->acl_p->id; + *name = aes_get_mbs(&entry->acl_p->name); + entry->acl_p = entry->acl_p->next; + return (ARCHIVE_OK); +} + +/* + * Generate a text version of the ACL. The flags parameter controls + * the style of the generated ACL. + */ +const wchar_t * +archive_entry_acl_text_w(struct archive_entry *entry, int flags) +{ + int count; + int length; + const wchar_t *wname; + const wchar_t *prefix; + wchar_t separator; + struct ae_acl *ap; + int id; + wchar_t *wp; + + if (entry->acl_text_w != NULL) { + free (entry->acl_text_w); + entry->acl_text_w = NULL; + } + + separator = L','; + count = 0; + length = 0; + ap = entry->acl_head; + while (ap != NULL) { + if ((ap->type & flags) != 0) { + count++; + if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) && + (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)) + length += 8; /* "default:" */ + length += 5; /* tag name */ + length += 1; /* colon */ + wname = aes_get_wcs(&ap->name); + if (wname != NULL) + length += wcslen(wname); + else + length += sizeof(uid_t) * 3 + 1; + length ++; /* colon */ + length += 3; /* rwx */ + length += 1; /* colon */ + length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1; + length ++; /* newline */ + } + ap = ap->next; + } + + if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) { + length += 10; /* "user::rwx\n" */ + length += 11; /* "group::rwx\n" */ + length += 11; /* "other::rwx\n" */ + } + + if (count == 0) + return (NULL); + + /* Now, allocate the string and actually populate it. */ + wp = entry->acl_text_w = (wchar_t *)malloc(length * sizeof(wchar_t)); + if (wp == NULL) + __archive_errx(1, "No memory to generate the text version of the ACL"); + count = 0; + if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { + append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL, + entry->ae_stat.st_mode & 0700, -1); + *wp++ = ','; + append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL, + entry->ae_stat.st_mode & 0070, -1); + *wp++ = ','; + append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL, + entry->ae_stat.st_mode & 0007, -1); + count += 3; + + ap = entry->acl_head; + while (ap != NULL) { + if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { + wname = aes_get_wcs(&ap->name); + *wp++ = separator; + if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) + id = ap->id; + else + id = -1; + append_entry_w(&wp, NULL, ap->tag, wname, + ap->permset, id); + count++; + } + ap = ap->next; + } + } + + + if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { + if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) + prefix = L"default:"; + else + prefix = NULL; + ap = entry->acl_head; + count = 0; + while (ap != NULL) { + if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { + wname = aes_get_wcs(&ap->name); + if (count > 0) + *wp++ = separator; + if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) + id = ap->id; + else + id = -1; + append_entry_w(&wp, prefix, ap->tag, + wname, ap->permset, id); + count ++; + } + ap = ap->next; + } + } + + return (entry->acl_text_w); +} + +static void +append_id_w(wchar_t **wp, int id) +{ + if (id < 0) + id = 0; + if (id > 9) + append_id_w(wp, id / 10); + *(*wp)++ = L"0123456789"[id % 10]; +} + +static void +append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, + const wchar_t *wname, int perm, int id) +{ + if (prefix != NULL) { + wcscpy(*wp, prefix); + *wp += wcslen(*wp); + } + switch (tag) { + case ARCHIVE_ENTRY_ACL_USER_OBJ: + wname = NULL; + id = -1; + /* FALLTHROUGH */ + case ARCHIVE_ENTRY_ACL_USER: + wcscpy(*wp, L"user"); + break; + case ARCHIVE_ENTRY_ACL_GROUP_OBJ: + wname = NULL; + id = -1; + /* FALLTHROUGH */ + case ARCHIVE_ENTRY_ACL_GROUP: + wcscpy(*wp, L"group"); + break; + case ARCHIVE_ENTRY_ACL_MASK: + wcscpy(*wp, L"mask"); + wname = NULL; + id = -1; + break; + case ARCHIVE_ENTRY_ACL_OTHER: + wcscpy(*wp, L"other"); + wname = NULL; + id = -1; + break; + } + *wp += wcslen(*wp); + *(*wp)++ = L':'; + if (wname != NULL) { + wcscpy(*wp, wname); + *wp += wcslen(*wp); + } else if (tag == ARCHIVE_ENTRY_ACL_USER + || tag == ARCHIVE_ENTRY_ACL_GROUP) { + append_id_w(wp, id); + id = -1; + } + *(*wp)++ = L':'; + *(*wp)++ = (perm & 0444) ? L'r' : L'-'; + *(*wp)++ = (perm & 0222) ? L'w' : L'-'; + *(*wp)++ = (perm & 0111) ? L'x' : L'-'; + if (id != -1) { + *(*wp)++ = L':'; + append_id_w(wp, id); + } + **wp = L'\0'; +} + +/* + * Parse a textual ACL. This automatically recognizes and supports + * extensions described above. The 'type' argument is used to + * indicate the type that should be used for any entries not + * explicitly marked as "default:". + */ +int +__archive_entry_acl_parse_w(struct archive_entry *entry, + const wchar_t *text, int default_type) +{ + struct { + const wchar_t *start; + const wchar_t *end; + } field[4]; + + int fields; + int type, tag, permset, id; + const wchar_t *p; + wchar_t sep; + + while (text != NULL && *text != L'\0') { + /* + * Parse the fields out of the next entry, + * advance 'text' to start of next entry. + */ + fields = 0; + do { + const wchar_t *start, *end; + next_field_w(&text, &start, &end, &sep); + if (fields < 4) { + field[fields].start = start; + field[fields].end = end; + } + ++fields; + } while (sep == L':'); + + if (fields < 3) + return (ARCHIVE_WARN); + + /* Check for a numeric ID in field 1 or 3. */ + id = -1; + isint_w(field[1].start, field[1].end, &id); + /* Field 3 is optional. */ + if (id == -1 && fields > 3) + isint_w(field[3].start, field[3].end, &id); + + /* Parse the permissions from field 2. */ + permset = 0; + p = field[2].start; + while (p < field[2].end) { + switch (*p++) { + case 'r': case 'R': + permset |= ARCHIVE_ENTRY_ACL_READ; + break; + case 'w': case 'W': + permset |= ARCHIVE_ENTRY_ACL_WRITE; + break; + case 'x': case 'X': + permset |= ARCHIVE_ENTRY_ACL_EXECUTE; + break; + case '-': + break; + default: + return (ARCHIVE_WARN); + } + } + + /* + * Solaris extension: "defaultuser::rwx" is the + * default ACL corresponding to "user::rwx", etc. + */ + if (field[0].end-field[0].start > 7 + && wmemcmp(field[0].start, L"default", 7) == 0) { + type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; + field[0].start += 7; + } else + type = default_type; + + if (prefix_w(field[0].start, field[0].end, L"user")) { + if (id != -1 || field[1].start < field[1].end) + tag = ARCHIVE_ENTRY_ACL_USER; + else + tag = ARCHIVE_ENTRY_ACL_USER_OBJ; + } else if (prefix_w(field[0].start, field[0].end, L"group")) { + if (id != -1 || field[1].start < field[1].end) + tag = ARCHIVE_ENTRY_ACL_GROUP; + else + tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; + } else if (prefix_w(field[0].start, field[0].end, L"other")) { + if (id != -1 || field[1].start < field[1].end) + return (ARCHIVE_WARN); + tag = ARCHIVE_ENTRY_ACL_OTHER; + } else if (prefix_w(field[0].start, field[0].end, L"mask")) { + if (id != -1 || field[1].start < field[1].end) + return (ARCHIVE_WARN); + tag = ARCHIVE_ENTRY_ACL_MASK; + } else + return (ARCHIVE_WARN); + + /* Add entry to the internal list. */ + archive_entry_acl_add_entry_w_len(entry, type, permset, + tag, id, field[1].start, field[1].end - field[1].start); + } + return (ARCHIVE_OK); +} + +/* + * extended attribute handling + */ + +void +archive_entry_xattr_clear(struct archive_entry *entry) +{ + struct ae_xattr *xp; + + while (entry->xattr_head != NULL) { + xp = entry->xattr_head->next; + free(entry->xattr_head->name); + free(entry->xattr_head->value); + free(entry->xattr_head); + entry->xattr_head = xp; + } + + entry->xattr_head = NULL; +} + +void +archive_entry_xattr_add_entry(struct archive_entry *entry, + const char *name, const void *value, size_t size) +{ + struct ae_xattr *xp; + + for (xp = entry->xattr_head; xp != NULL; xp = xp->next) + ; + + if ((xp = (struct ae_xattr *)malloc(sizeof(struct ae_xattr))) == NULL) + /* XXX Error XXX */ + return; + + xp->name = strdup(name); + if ((xp->value = malloc(size)) != NULL) { + memcpy(xp->value, value, size); + xp->size = size; + } else + xp->size = 0; + + xp->next = entry->xattr_head; + entry->xattr_head = xp; +} + + +/* + * returns number of the extended attribute entries + */ +int +archive_entry_xattr_count(struct archive_entry *entry) +{ + struct ae_xattr *xp; + int count = 0; + + for (xp = entry->xattr_head; xp != NULL; xp = xp->next) + count++; + + return count; +} + +int +archive_entry_xattr_reset(struct archive_entry * entry) +{ + entry->xattr_p = entry->xattr_head; + + return archive_entry_xattr_count(entry); +} + +int +archive_entry_xattr_next(struct archive_entry * entry, + const char **name, const void **value, size_t *size) +{ + if (entry->xattr_p) { + *name = entry->xattr_p->name; + *value = entry->xattr_p->value; + *size = entry->xattr_p->size; + + entry->xattr_p = entry->xattr_p->next; + + return (ARCHIVE_OK); + } else { + *name = NULL; + *name = NULL; + *size = (size_t)0; + return (ARCHIVE_WARN); + } +} + +/* + * end of xattr handling + */ + +/* + * Parse a string to a positive decimal integer. Returns true if + * the string is non-empty and consists only of decimal digits, + * false otherwise. + */ +static int +isint_w(const wchar_t *start, const wchar_t *end, int *result) +{ + int n = 0; + if (start >= end) + return (0); + while (start < end) { + if (*start < '0' || *start > '9') + return (0); + if (n > (INT_MAX / 10)) + n = INT_MAX; + else { + n *= 10; + n += *start - '0'; + } + start++; + } + *result = n; + return (1); +} + +/* + * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated + * to point to just after the separator. *start points to the first + * character of the matched text and *end just after the last + * character of the matched identifier. In particular *end - *start + * is the length of the field body, not including leading or trailing + * whitespace. + */ +static void +next_field_w(const wchar_t **wp, const wchar_t **start, + const wchar_t **end, wchar_t *sep) +{ + /* Skip leading whitespace to find start of field. */ + while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') { + (*wp)++; + } + *start = *wp; + + /* Scan for the separator. */ + while (**wp != L'\0' && **wp != L',' && **wp != L':' && + **wp != L'\n') { + (*wp)++; + } + *sep = **wp; + + /* Trim trailing whitespace to locate end of field. */ + *end = *wp - 1; + while (**end == L' ' || **end == L'\t' || **end == L'\n') { + (*end)--; + } + (*end)++; + + /* Adjust scanner location. */ + if (**wp != L'\0') + (*wp)++; +} + +/* + * Return true if the characters [start...end) are a prefix of 'test'. + * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc. + */ +static int +prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test) +{ + if (start == end) + return (0); + + if (*start++ != *test++) + return (0); + + while (start < end && *start++ == *test++) + ; + + if (start < end) + return (0); + + return (1); +} + + +/* + * Following code is modified from UC Berkeley sources, and + * is subject to the following copyright notice. + */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. 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. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. + */ + +static struct flag { + const char *name; + const wchar_t *wname; + unsigned long set; + unsigned long clear; +} flags[] = { + /* Preferred (shorter) names per flag first, all prefixed by "no" */ +#ifdef SF_APPEND + { "nosappnd", L"nosappnd", SF_APPEND, 0 }, + { "nosappend", L"nosappend", SF_APPEND, 0 }, +#endif +#ifdef EXT2_APPEND_FL /* 'a' */ + { "nosappnd", L"nosappnd", EXT2_APPEND_FL, 0 }, + { "nosappend", L"nosappend", EXT2_APPEND_FL, 0 }, +#endif +#ifdef SF_ARCHIVED + { "noarch", L"noarch", SF_ARCHIVED, 0 }, + { "noarchived", L"noarchived", SF_ARCHIVED, 0 }, +#endif +#ifdef SF_IMMUTABLE + { "noschg", L"noschg", SF_IMMUTABLE, 0 }, + { "noschange", L"noschange", SF_IMMUTABLE, 0 }, + { "nosimmutable", L"nosimmutable", SF_IMMUTABLE, 0 }, +#endif +#ifdef EXT2_IMMUTABLE_FL /* 'i' */ + { "noschg", L"noschg", EXT2_IMMUTABLE_FL, 0 }, + { "noschange", L"noschange", EXT2_IMMUTABLE_FL, 0 }, + { "nosimmutable", L"nosimmutable", EXT2_IMMUTABLE_FL, 0 }, +#endif +#ifdef SF_NOUNLINK + { "nosunlnk", L"nosunlnk", SF_NOUNLINK, 0 }, + { "nosunlink", L"nosunlink", SF_NOUNLINK, 0 }, +#endif +#ifdef SF_SNAPSHOT + { "nosnapshot", L"nosnapshot", SF_SNAPSHOT, 0 }, +#endif +#ifdef UF_APPEND + { "nouappnd", L"nouappnd", UF_APPEND, 0 }, + { "nouappend", L"nouappend", UF_APPEND, 0 }, +#endif +#ifdef UF_IMMUTABLE + { "nouchg", L"nouchg", UF_IMMUTABLE, 0 }, + { "nouchange", L"nouchange", UF_IMMUTABLE, 0 }, + { "nouimmutable", L"nouimmutable", UF_IMMUTABLE, 0 }, +#endif +#ifdef UF_NODUMP + { "nodump", L"nodump", 0, UF_NODUMP}, +#endif +#ifdef EXT2_NODUMP_FL /* 'd' */ + { "nodump", L"nodump", 0, EXT2_NODUMP_FL}, +#endif +#ifdef UF_OPAQUE + { "noopaque", L"noopaque", UF_OPAQUE, 0 }, +#endif +#ifdef UF_NOUNLINK + { "nouunlnk", L"nouunlnk", UF_NOUNLINK, 0 }, + { "nouunlink", L"nouunlink", UF_NOUNLINK, 0 }, +#endif +#ifdef EXT2_COMPR_FL /* 'c' */ + { "nocompress", L"nocompress", EXT2_COMPR_FL, 0 }, +#endif + +#ifdef EXT2_NOATIME_FL /* 'A' */ + { "noatime", L"noatime", 0, EXT2_NOATIME_FL}, +#endif + { NULL, NULL, 0, 0 } +}; + +/* + * fflagstostr -- + * Convert file flags to a comma-separated string. If no flags + * are set, return the empty string. + */ +char * +ae_fflagstostr(unsigned long bitset, unsigned long bitclear) +{ + char *string, *dp; + const char *sp; + unsigned long bits; + struct flag *flag; + size_t length; + + bits = bitset | bitclear; + length = 0; + for (flag = flags; flag->name != NULL; flag++) + if (bits & (flag->set | flag->clear)) { + length += strlen(flag->name) + 1; + bits &= ~(flag->set | flag->clear); + } + + if (length == 0) + return (NULL); + string = (char *)malloc(length); + if (string == NULL) + return (NULL); + + dp = string; + for (flag = flags; flag->name != NULL; flag++) { + if (bitset & flag->set || bitclear & flag->clear) { + sp = flag->name + 2; + } else if (bitset & flag->clear || bitclear & flag->set) { + sp = flag->name; + } else + continue; + bitset &= ~(flag->set | flag->clear); + bitclear &= ~(flag->set | flag->clear); + if (dp > string) + *dp++ = ','; + while ((*dp++ = *sp++) != '\0') + ; + dp--; + } + + *dp = '\0'; + return (string); +} + +/* + * wcstofflags -- + * Take string of arguments and return file flags. This + * version works a little differently than strtofflags(3). + * In particular, it always tests every token, skipping any + * unrecognized tokens. It returns a pointer to the first + * unrecognized token, or NULL if every token was recognized. + * This version is also const-correct and does not modify the + * provided string. + */ +const wchar_t * +ae_wcstofflags(const wchar_t *s, unsigned long *setp, unsigned long *clrp) +{ + const wchar_t *start, *end; + struct flag *flag; + unsigned long set, clear; + const wchar_t *failed; + + set = clear = 0; + start = s; + failed = NULL; + /* Find start of first token. */ + while (*start == L'\t' || *start == L' ' || *start == L',') + start++; + while (*start != L'\0') { + /* Locate end of token. */ + end = start; + while (*end != L'\0' && *end != L'\t' && + *end != L' ' && *end != L',') + end++; + for (flag = flags; flag->wname != NULL; flag++) { + if (wmemcmp(start, flag->wname, end - start) == 0) { + /* Matched "noXXXX", so reverse the sense. */ + clear |= flag->set; + set |= flag->clear; + break; + } else if (wmemcmp(start, flag->wname + 2, end - start) + == 0) { + /* Matched "XXXX", so don't reverse. */ + set |= flag->set; + clear |= flag->clear; + break; + } + } + /* Ignore unknown flag names. */ + if (flag->wname == NULL && failed == NULL) + failed = start; + + /* Find start of next token. */ + start = end; + while (*start == L'\t' || *start == L' ' || *start == L',') + start++; + + } + + if (setp) + *setp = set; + if (clrp) + *clrp = clear; + + /* Return location of first failure. */ + return (failed); +} + + +#ifdef TEST +#include +int +main(int argc, char **argv) +{ + struct archive_entry *entry = archive_entry_new(); + unsigned long set, clear; + const wchar_t *remainder; + + remainder = archive_entry_copy_fflags_text_w(entry, L"nosappnd dump archive,,,,,,,"); + archive_entry_fflags(entry, &set, &clear); + + wprintf(L"set=0x%lX clear=0x%lX remainder='%ls'\n", set, clear, remainder); + + wprintf(L"new flags='%s'\n", archive_entry_fflags_text(entry)); + return (0); +} +#endif diff --git a/contrib/libarchive-2.1/libarchive/archive_entry.h b/contrib/libarchive-2.1/libarchive/archive_entry.h new file mode 100644 index 0000000000..9dceb3772f --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_entry.h @@ -0,0 +1,271 @@ +/*- + * 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. + * + * $FreeBSD: src/lib/libarchive/archive_entry.h,v 1.21 2007/03/01 06:22:34 kientzle Exp $ + */ + +#ifndef ARCHIVE_ENTRY_H_INCLUDED +#define ARCHIVE_ENTRY_H_INCLUDED + +#include /* for wchar_t */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * Description of an archive entry. + * + * Basically, a "struct stat" with a few text fields added in. + * + * TODO: Add "comment", "charset", and possibly other entries that are + * supported by "pax interchange" format. However, GNU, ustar, cpio, + * and other variants don't support these features, so they're not an + * excruciatingly high priority right now. + * + * TODO: "pax interchange" format allows essentially arbitrary + * key/value attributes to be attached to any entry. Supporting + * such extensions may make this library useful for special + * applications (e.g., a package manager could attach special + * package-management attributes to each entry). + */ +struct archive_entry; + +/* + * File-type constants. These are returned from archive_entry_filetype(). + */ +#define AE_IFMT 0170000 +#define AE_IFREG 0100000 +#define AE_IFLNK 0120000 +#define AE_IFCHR 0020000 +#define AE_IFBLK 0060000 +#define AE_IFDIR 0040000 +#define AE_IFIFO 0010000 + +/* + * Basic object manipulation + */ + +struct archive_entry *archive_entry_clear(struct archive_entry *); +/* The 'clone' function does a deep copy; all of the strings are copied too. */ +struct archive_entry *archive_entry_clone(struct archive_entry *); +void archive_entry_free(struct archive_entry *); +struct archive_entry *archive_entry_new(void); + +/* + * Retrieve fields from an archive_entry. + */ + +time_t archive_entry_atime(struct archive_entry *); +long archive_entry_atime_nsec(struct archive_entry *); +time_t archive_entry_ctime(struct archive_entry *); +long archive_entry_ctime_nsec(struct archive_entry *); +dev_t archive_entry_dev(struct archive_entry *); +mode_t archive_entry_filetype(struct archive_entry *); +void archive_entry_fflags(struct archive_entry *, + unsigned long *set, unsigned long *clear); +const char *archive_entry_fflags_text(struct archive_entry *); +gid_t archive_entry_gid(struct archive_entry *); +const char *archive_entry_gname(struct archive_entry *); +const wchar_t *archive_entry_gname_w(struct archive_entry *); +const char *archive_entry_hardlink(struct archive_entry *); +const wchar_t *archive_entry_hardlink_w(struct archive_entry *); +ino_t archive_entry_ino(struct archive_entry *); +mode_t archive_entry_mode(struct archive_entry *); +time_t archive_entry_mtime(struct archive_entry *); +long archive_entry_mtime_nsec(struct archive_entry *); +unsigned int archive_entry_nlink(struct archive_entry *); +const char *archive_entry_pathname(struct archive_entry *); +const wchar_t *archive_entry_pathname_w(struct archive_entry *); +dev_t archive_entry_rdev(struct archive_entry *); +dev_t archive_entry_rdevmajor(struct archive_entry *); +dev_t archive_entry_rdevminor(struct archive_entry *); +int64_t archive_entry_size(struct archive_entry *); +const struct stat *archive_entry_stat(struct archive_entry *); +const char *archive_entry_symlink(struct archive_entry *); +const wchar_t *archive_entry_symlink_w(struct archive_entry *); +uid_t archive_entry_uid(struct archive_entry *); +const char *archive_entry_uname(struct archive_entry *); +const wchar_t *archive_entry_uname_w(struct archive_entry *); + +/* + * Set fields in an archive_entry. + * + * Note that string 'set' functions do not copy the string, only the pointer. + * In contrast, 'copy' functions do copy the object pointed to. + */ + +void archive_entry_copy_stat(struct archive_entry *, const struct stat *); +void archive_entry_set_atime(struct archive_entry *, time_t, long); +void archive_entry_set_ctime(struct archive_entry *, time_t, long); +void archive_entry_set_dev(struct archive_entry *, dev_t); +void archive_entry_set_devmajor(struct archive_entry *, dev_t); +void archive_entry_set_devminor(struct archive_entry *, dev_t); +void archive_entry_set_filetype(struct archive_entry *, unsigned int); +void archive_entry_set_fflags(struct archive_entry *, + unsigned long set, unsigned long clear); +/* Returns pointer to start of first invalid token, or NULL if none. */ +/* Note that all recognized tokens are processed, regardless. */ +const wchar_t *archive_entry_copy_fflags_text_w(struct archive_entry *, + const wchar_t *); +void archive_entry_set_gid(struct archive_entry *, gid_t); +void archive_entry_set_gname(struct archive_entry *, const char *); +void archive_entry_copy_gname_w(struct archive_entry *, const wchar_t *); +void archive_entry_set_hardlink(struct archive_entry *, const char *); +void archive_entry_copy_hardlink(struct archive_entry *, const char *); +void archive_entry_copy_hardlink_w(struct archive_entry *, const wchar_t *); +void archive_entry_set_ino(struct archive_entry *, unsigned long); +void archive_entry_set_link(struct archive_entry *, const char *); +void archive_entry_set_mode(struct archive_entry *, mode_t); +void archive_entry_set_mtime(struct archive_entry *, time_t, long); +void archive_entry_set_nlink(struct archive_entry *, unsigned int); +void archive_entry_set_pathname(struct archive_entry *, const char *); +void archive_entry_copy_pathname(struct archive_entry *, const char *); +void archive_entry_copy_pathname_w(struct archive_entry *, const wchar_t *); +void archive_entry_set_rdev(struct archive_entry *, dev_t); +void archive_entry_set_rdevmajor(struct archive_entry *, dev_t); +void archive_entry_set_rdevminor(struct archive_entry *, dev_t); +void archive_entry_set_size(struct archive_entry *, int64_t); +void archive_entry_set_symlink(struct archive_entry *, const char *); +void archive_entry_copy_symlink(struct archive_entry *, const char *); +void archive_entry_copy_symlink_w(struct archive_entry *, const wchar_t *); +void archive_entry_set_uid(struct archive_entry *, uid_t); +void archive_entry_set_uname(struct archive_entry *, const char *); +void archive_entry_copy_uname_w(struct archive_entry *, const wchar_t *); + +/* + * ACL routines. This used to simply store and return text-format ACL + * strings, but that proved insufficient for a number of reasons: + * = clients need control over uname/uid and gname/gid mappings + * = there are many different ACL text formats + * = would like to be able to read/convert archives containing ACLs + * on platforms that lack ACL libraries + */ + +/* + * Permission bits mimic POSIX.1e. Note that I've not followed POSIX.1e's + * "permset"/"perm" abstract type nonsense. A permset is just a simple + * bitmap, following long-standing Unix tradition. + */ +#define ARCHIVE_ENTRY_ACL_EXECUTE 1 +#define ARCHIVE_ENTRY_ACL_WRITE 2 +#define ARCHIVE_ENTRY_ACL_READ 4 + +/* We need to be able to specify either or both of these. */ +#define ARCHIVE_ENTRY_ACL_TYPE_ACCESS 256 +#define ARCHIVE_ENTRY_ACL_TYPE_DEFAULT 512 + +/* Tag values mimic POSIX.1e */ +#define ARCHIVE_ENTRY_ACL_USER 10001 /* Specified user. */ +#define ARCHIVE_ENTRY_ACL_USER_OBJ 10002 /* User who owns the file. */ +#define ARCHIVE_ENTRY_ACL_GROUP 10003 /* Specified group. */ +#define ARCHIVE_ENTRY_ACL_GROUP_OBJ 10004 /* Group who owns the file. */ +#define ARCHIVE_ENTRY_ACL_MASK 10005 /* Modify group access. */ +#define ARCHIVE_ENTRY_ACL_OTHER 10006 /* Public. */ + +/* + * Set the ACL by clearing it and adding entries one at a time. + * Unlike the POSIX.1e ACL routines, you must specify the type + * (access/default) for each entry. Internally, the ACL data is just + * a soup of entries. API calls here allow you to retrieve just the + * entries of interest. This design (which goes against the spirit of + * POSIX.1e) is useful for handling archive formats that combine + * default and access information in a single ACL list. + */ +void archive_entry_acl_clear(struct archive_entry *); +void archive_entry_acl_add_entry(struct archive_entry *, + int type, int permset, int tag, int qual, const char *name); +void archive_entry_acl_add_entry_w(struct archive_entry *, + int type, int permset, int tag, int qual, const wchar_t *name); + +/* + * To retrieve the ACL, first "reset", then repeatedly ask for the + * "next" entry. The want_type parameter allows you to request only + * access entries or only default entries. + */ +int archive_entry_acl_reset(struct archive_entry *, int want_type); +int archive_entry_acl_next(struct archive_entry *, int want_type, + int *type, int *permset, int *tag, int *qual, const char **name); +int archive_entry_acl_next_w(struct archive_entry *, int want_type, + int *type, int *permset, int *tag, int *qual, + const wchar_t **name); + +/* + * Construct a text-format ACL. The flags argument is a bitmask that + * can include any of the following: + * + * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - Include access entries. + * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - Include default entries. + * ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID - Include extra numeric ID field in + * each ACL entry. (As used by 'star'.) + * ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT - Include "default:" before each + * default ACL entry. + */ +#define ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID 1024 +#define ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT 2048 +const wchar_t *archive_entry_acl_text_w(struct archive_entry *, int flags); + +/* Return a count of entries matching 'want_type' */ +int archive_entry_acl_count(struct archive_entry *, int want_type); + +/* + * Private ACL parser. This is private because it handles some + * very weird formats that clients should not be messing with. + * Clients should only deal with their platform-native formats. + * Because of the need to support many formats cleanly, new arguments + * are likely to get added on a regular basis. Clients who try to use + * this interface are likely to be surprised when it changes. + * + * You were warned! + */ +int __archive_entry_acl_parse_w(struct archive_entry *, + const wchar_t *, int type); + + +#ifdef __cplusplus +} +#endif + +/* + * extended attributes + */ + +void archive_entry_xattr_clear(struct archive_entry *); +void archive_entry_xattr_add_entry(struct archive_entry *, + const char *name, const void *value, size_t size); + +/* + * To retrieve the xattr list, first "reset", then repeatedly ask for the + * "next" entry. + */ + +int archive_entry_xattr_count(struct archive_entry *); +int archive_entry_xattr_reset(struct archive_entry *); +int archive_entry_xattr_next(struct archive_entry *, + const char **name, const void **value, size_t *); + + +#endif /* !ARCHIVE_ENTRY_H_INCLUDED */ diff --git a/contrib/libarchive-2.1/libarchive/archive_platform.h b/contrib/libarchive-2.1/libarchive/archive_platform.h new file mode 100644 index 0000000000..592ff6f97f --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_platform.h @@ -0,0 +1,154 @@ +/*- + * 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. + * + * $FreeBSD: src/lib/libarchive/archive_platform.h,v 1.26 2007/04/15 00:53:38 kientzle Exp $ + */ + +/* + * This header is the first thing included in any of the libarchive + * source files. As far as possible, platform-specific issues should + * be dealt with here and not within individual source files. I'm + * actively trying to minimize #if blocks within the main source, + * since they obfuscate the code. + */ + +#ifndef ARCHIVE_PLATFORM_H_INCLUDED +#define ARCHIVE_PLATFORM_H_INCLUDED + +#if defined(PLATFORM_CONFIG_H) +/* Use hand-built config.h in environments that need it. */ +#include PLATFORM_CONFIG_H +#elif defined(HAVE_CONFIG_H) +/* Most POSIX platforms use the 'configure' script to build config.h */ +#include "../config.h" +#else +/* Warn if the library hasn't been (automatically or manually) configured. */ +#error Oops: No config.h and no pre-built configuration in archive_platform.h. +#endif + +/* + * The config files define a lot of feature macros. The following + * uses those macros to select/define replacements and include key + * headers as required. + */ + +/* No non-FreeBSD platform will have __FBSDID, so just define it here. */ +#ifdef __FreeBSD__ +#include /* For __FBSDID */ +#else +#define __FBSDID(a) /* null */ +#endif + +/* Try to get standard C99-style integer type definitions. */ +#if HAVE_INTTYPES_H +#include +#elif HAVE_STDINT_H +#include +#endif + +/* Some platforms lack the standard *_MAX definitions. */ +#if !HAVE_DECL_SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif +#if !HAVE_DECL_UINT32_MAX +#define UINT32_MAX (~(uint32_t)0) +#endif +#if !HAVE_DECL_UINT64_MAX +#define UINT64_MAX (~(uint64_t)0) +#endif +#if !HAVE_DECL_INT64_MAX +#define INT64_MAX ((int64_t)(UINT64_MAX >> 1)) +#endif +#if !HAVE_DECL_INT64_MIN +#define INT64_MIN ((int64_t)(~INT64_MAX)) +#endif + +/* + * If this platform has , acl_create(), acl_init(), + * acl_set_file(), and ACL_USER, we assume it has the rest of the + * POSIX.1e draft functions used in archive_read_extract.c. + */ +#if HAVE_SYS_ACL_H && HAVE_ACL_CREATE_ENTRY && HAVE_ACL_INIT && HAVE_ACL_SET_FILE && HAVE_ACL_USER +#define HAVE_POSIX_ACL 1 +#endif + +/* + * If we can't restore metadata using a file descriptor, then + * for compatibility's sake, close files before trying to restore metadata. + */ +#if defined(HAVE_FCHMOD) || defined(HAVE_FUTIMES) || defined(HAVE_ACL_SET_FD) || defined(HAVE_ACL_SET_FD_NP) || defined(HAVE_FCHOWN) +#define CAN_RESTORE_METADATA_FD +#endif + +/* Set up defaults for internal error codes. */ +#ifndef ARCHIVE_ERRNO_FILE_FORMAT +#if HAVE_EFTYPE +#define ARCHIVE_ERRNO_FILE_FORMAT EFTYPE +#else +#if HAVE_EILSEQ +#define ARCHIVE_ERRNO_FILE_FORMAT EILSEQ +#else +#define ARCHIVE_ERRNO_FILE_FORMAT EINVAL +#endif +#endif +#endif + +#ifndef ARCHIVE_ERRNO_PROGRAMMER +#define ARCHIVE_ERRNO_PROGRAMMER EINVAL +#endif + +#ifndef ARCHIVE_ERRNO_MISC +#define ARCHIVE_ERRNO_MISC (-1) +#endif + +/* Select the best way to set/get hi-res timestamps. */ +#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC +/* FreeBSD uses "timespec" members. */ +#define ARCHIVE_STAT_ATIME_NANOS(st) (st)->st_atimespec.tv_nsec +#define ARCHIVE_STAT_CTIME_NANOS(st) (st)->st_ctimespec.tv_nsec +#define ARCHIVE_STAT_MTIME_NANOS(st) (st)->st_mtimespec.tv_nsec +#define ARCHIVE_STAT_SET_ATIME_NANOS(st, n) (st)->st_atimespec.tv_nsec = (n) +#define ARCHIVE_STAT_SET_CTIME_NANOS(st, n) (st)->st_ctimespec.tv_nsec = (n) +#define ARCHIVE_STAT_SET_MTIME_NANOS(st, n) (st)->st_mtimespec.tv_nsec = (n) +#else +#if HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC +/* Linux uses "tim" members. */ +#define ARCHIVE_STAT_ATIME_NANOS(pstat) (pstat)->st_atim.tv_nsec +#define ARCHIVE_STAT_CTIME_NANOS(pstat) (pstat)->st_ctim.tv_nsec +#define ARCHIVE_STAT_MTIME_NANOS(pstat) (pstat)->st_mtim.tv_nsec +#define ARCHIVE_STAT_SET_ATIME_NANOS(st, n) (st)->st_atim.tv_nsec = (n) +#define ARCHIVE_STAT_SET_CTIME_NANOS(st, n) (st)->st_ctim.tv_nsec = (n) +#define ARCHIVE_STAT_SET_MTIME_NANOS(st, n) (st)->st_mtim.tv_nsec = (n) +#else +/* If we can't find a better way, just use stubs. */ +#define ARCHIVE_STAT_ATIME_NANOS(pstat) 0 +#define ARCHIVE_STAT_CTIME_NANOS(pstat) 0 +#define ARCHIVE_STAT_MTIME_NANOS(pstat) 0 +#define ARCHIVE_STAT_SET_ATIME_NANOS(st, n) ((void)(n)) +#define ARCHIVE_STAT_SET_CTIME_NANOS(st, n) ((void)(n)) +#define ARCHIVE_STAT_SET_MTIME_NANOS(st, n) ((void)(n)) +#endif +#endif + +#endif /* !ARCHIVE_H_INCLUDED */ diff --git a/contrib/libarchive-2.1/libarchive/archive_private.h b/contrib/libarchive-2.1/libarchive/archive_private.h new file mode 100644 index 0000000000..9ca5893d80 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_private.h @@ -0,0 +1,99 @@ +/*- + * 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. + * + * $FreeBSD: src/lib/libarchive/archive_private.h,v 1.29 2007/04/02 00:15:45 kientzle Exp $ + */ + +#ifndef ARCHIVE_PRIVATE_H_INCLUDED +#define ARCHIVE_PRIVATE_H_INCLUDED + +#include "archive.h" +#include "archive_string.h" + +#define ARCHIVE_WRITE_MAGIC (0xb0c5c0deU) +#define ARCHIVE_READ_MAGIC (0xdeb0c5U) +#define ARCHIVE_WRITE_DISK_MAGIC (0xc001b0c5U) + +#define ARCHIVE_STATE_ANY 0xFFFFU +#define ARCHIVE_STATE_NEW 1U +#define ARCHIVE_STATE_HEADER 2U +#define ARCHIVE_STATE_DATA 4U +#define ARCHIVE_STATE_DATA_END 8U +#define ARCHIVE_STATE_EOF 0x10U +#define ARCHIVE_STATE_CLOSED 0x20U +#define ARCHIVE_STATE_FATAL 0x8000U + +struct archive_vtable { + int (*archive_write_close)(struct archive *); + int (*archive_write_finish)(struct archive *); + int (*archive_write_header)(struct archive *, + struct archive_entry *); + int (*archive_write_finish_entry)(struct archive *); + ssize_t (*archive_write_data)(struct archive *, + const void *, size_t); + ssize_t (*archive_write_data_block)(struct archive *, + const void *, size_t, off_t); +}; + +struct archive { + /* + * The magic/state values are used to sanity-check the + * client's usage. If an API function is called at a + * ridiculous time, or the client passes us an invalid + * pointer, these values allow me to catch that. + */ + unsigned int magic; + unsigned int state; + + /* + * Some public API functions depend on the "real" type of the + * archive object. + */ + struct archive_vtable *vtable; + + int archive_format; + const char *archive_format_name; + + int compression_code; /* Currently active compression. */ + const char *compression_name; + + /* Position in UNCOMPRESSED data stream. */ + off_t file_position; + /* Position in COMPRESSED data stream. */ + off_t raw_position; + + int archive_error_number; + const char *error; + struct archive_string error_string; +}; + +/* Check magic value and state; exit if it isn't valid. */ +void __archive_check_magic(struct archive *, unsigned int magic, + unsigned int state, const char *func); + +void __archive_errx(int retvalue, const char *msg); + +#define err_combine(a,b) ((a) < (b) ? (a) : (b)) + +#endif diff --git a/contrib/libarchive-2.1/libarchive/archive_read.3 b/contrib/libarchive-2.1/libarchive/archive_read.3 new file mode 100644 index 0000000000..e50a28e15e --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_read.3 @@ -0,0 +1,516 @@ +.\" 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. +.\" +.\" $FreeBSD: src/lib/libarchive/archive_read.3,v 1.33 2007/04/07 05:53:11 kientzle Exp $ +.\" +.Dd August 19, 2006 +.Dt archive_read 3 +.Os +.Sh NAME +.Nm archive_read_new , +.Nm archive_read_support_compression_all , +.Nm archive_read_support_compression_bzip2 , +.Nm archive_read_support_compression_compress , +.Nm archive_read_support_compression_gzip , +.Nm archive_read_support_compression_none , +.Nm archive_read_support_format_all , +.Nm archive_read_support_format_cpio , +.Nm archive_read_support_format_empty , +.Nm archive_read_support_format_iso9660 , +.Nm archive_read_support_format_tar , +.Nm archive_read_support_format_zip , +.Nm archive_read_open , +.Nm archive_read_open2 , +.Nm archive_read_open_fd , +.Nm archive_read_open_FILE , +.Nm archive_read_open_filename , +.Nm archive_read_open_memory , +.Nm archive_read_next_header , +.Nm archive_read_data , +.Nm archive_read_data_block , +.Nm archive_read_data_skip , +.\" #if ARCHIVE_API_VERSION < 3 +.Nm archive_read_data_into_buffer , +.\" #endif +.Nm archive_read_data_into_fd , +.Nm archive_read_extract , +.Nm archive_read_extract_set_progress_callback , +.Nm archive_read_close , +.Nm archive_read_finish +.Nd functions for reading streaming archives +.Sh SYNOPSIS +.In archive.h +.Ft struct archive * +.Fn archive_read_new "void" +.Ft int +.Fn archive_read_support_compression_all "struct archive *" +.Ft int +.Fn archive_read_support_compression_bzip2 "struct archive *" +.Ft int +.Fn archive_read_support_compression_compress "struct archive *" +.Ft int +.Fn archive_read_support_compression_gzip "struct archive *" +.Ft int +.Fn archive_read_support_compression_none "struct archive *" +.Ft int +.Fn archive_read_support_format_all "struct archive *" +.Ft int +.Fn archive_read_support_format_cpio "struct archive *" +.Ft int +.Fn archive_read_support_format_empty "struct archive *" +.Ft int +.Fn archive_read_support_format_iso9660 "struct archive *" +.Ft int +.Fn archive_read_support_format_tar "struct archive *" +.Ft int +.Fn archive_read_support_format_zip "struct archive *" +.Ft int +.Fn archive_read_open "struct archive *" "void *client_data" "archive_open_callback *" "archive_read_callback *" "archive_close_callback *" +.Ft int +.Fn archive_read_open2 "struct archive *" "void *client_data" "archive_open_callback *" "archive_read_callback *" "archive_skip_callback *" "archive_close_callback *" +.Ft int +.Fn archive_read_open_FILE "struct archive *" "FILE *file" +.Ft int +.Fn archive_read_open_fd "struct archive *" "int fd" "size_t block_size" +.Ft int +.Fn archive_read_open_filename "struct archive *" "const char *filename" "size_t block_size" +.Ft int +.Fn archive_read_open_memory "struct archive *" "void *buff" "size_t size" +.Ft int +.Fn archive_read_next_header "struct archive *" "struct archive_entry **" +.Ft ssize_t +.Fn archive_read_data "struct archive *" "void *buff" "size_t len" +.Ft int +.Fn archive_read_data_block "struct archive *" "const void **buff" "size_t *len" "off_t *offset" +.Ft int +.Fn archive_read_data_skip "struct archive *" +.\" #if ARCHIVE_API_VERSION < 3 +.Ft int +.Fn archive_read_data_into_buffer "struct archive *" "void *" "ssize_t len" +.\" #endif +.Ft int +.Fn archive_read_data_into_fd "struct archive *" "int fd" +.Ft int +.Fn archive_read_extract "struct archive *" "struct archive_entry *" "int flags" +.Ft void +.Fn archive_read_extract_set_progress_callback "struct archive *" "void (*func)(void *)" "void *user_data" +.Ft int +.Fn archive_read_close "struct archive *" +.Ft int +.Fn archive_read_finish "struct archive *" +.Sh DESCRIPTION +These functions provide a complete API for reading streaming archives. +The general process is to first create the +.Tn struct archive +object, set options, initialize the reader, iterate over the archive +headers and associated data, then close the archive and release all +resources. +The following summary describes the functions in approximately the +order they would be used: +.Bl -tag -compact -width indent +.It Fn archive_read_new +Allocates and initializes a +.Tn struct archive +object suitable for reading from an archive. +.It Fn archive_read_support_compression_all , Fn archive_read_support_compression_bzip2 , Fn archive_read_support_compression_compress , Fn archive_read_support_compression_gzip , Fn archive_read_support_compression_none +Enables auto-detection code and decompression support for the +specified compression. +Note that +.Dq none +is always enabled by default. +For convenience, +.Fn archive_read_support_compression_all +enables all available decompression code. +.It Fn archive_read_support_format_all , Fn archive_read_support_format_cpio , Fn archive_read_support_format_empty , Fn archive_read_support_format_iso9660 , Fn archive_read_support_format_tar, Fn archive_read_support_format_zip +Enables support---including auto-detection code---for the +specified archive format. +For example, +.Fn archive_read_support_format_tar +enables support for a variety of standard tar formats, old-style tar, +ustar, pax interchange format, and many common variants. +For convenience, +.Fn archive_read_support_format_all +enables support for all available formats. +Only empty archives are supported by default. +.It Fn archive_read_open +The same as +.Fn archive_read_open2 , +except that the skip callback is assumed to be +.Dv NULL . +.It Fn archive_read_open2 +Freeze the settings, open the archive, and prepare for reading entries. +This is the most generic version of this call, which accepts +four callback functions. +Most clients will want to use +.Fn archive_read_open_filename , +.Fn archive_read_open_FILE , +.Fn archive_read_open_fd , +or +.Fn archive_read_open_memory +instead. +The library invokes the client-provided functions to obtain +raw bytes from the archive. +.It Fn archive_read_open_FILE +Like +.Fn archive_read_open , +except that it accepts a +.Ft "FILE *" +pointer. +This function should not be used with tape drives or other devices +that require strict I/O blocking. +.It Fn archive_read_open_fd +Like +.Fn archive_read_open , +except that it accepts a file descriptor and block size rather than +a set of function pointers. +Note that the file descriptor will not be automatically closed at +end-of-archive. +This function is safe for use with tape drives or other blocked devices. +.It Fn archive_read_open_file +This is a deprecated synonym for +.Fn archive_read_open_filename . +.It Fn archive_read_open_filename +Like +.Fn archive_read_open , +except that it accepts a simple filename and a block size. +A NULL filename represents standard input. +This function is safe for use with tape drives or other blocked devices. +.It Fn archive_read_open_memory +Like +.Fn archive_read_open , +except that it accepts a pointer and size of a block of +memory containing the archive data. +.It Fn archive_read_next_header +Read the header for the next entry and return a pointer to +a +.Tn struct archive_entry . +.It Fn archive_read_data +Read data associated with the header just read. +Internally, this is a convenience function that calls +.Fn archive_read_data_block +and fills any gaps with nulls so that callers see a single +continuous stream of data. +.It Fn archive_read_data_block +Return the next available block of data for this entry. +Unlike +.Fn archive_read_data , +the +.Fn archive_read_data_block +function avoids copying data and allows you to correctly handle +sparse files, as supported by some archive formats. +The library guarantees that offsets will increase and that blocks +will not overlap. +Note that the blocks returned from this function can be much larger +than the block size read from disk, due to compression +and internal buffer optimizations. +.It Fn archive_read_data_skip +A convenience function that repeatedly calls +.Fn archive_read_data_block +to skip all of the data for this archive entry. +.\" #if ARCHIVE_API_VERSION < 3 +.It Fn archive_read_data_into_buffer +This function is deprecated and will be removed. +Use +.Fn archive_read_data +instead. +.\" #endif +.It Fn archive_read_data_into_fd +A convenience function that repeatedly calls +.Fn archive_read_data_block +to copy the entire entry to the provided file descriptor. +.It Fn archive_read_extract , Fn archive_read_extract_set_skip_file +A convenience function that wraps the corresponding +.Xr archive_write_disk 3 +interfaces. +The first call to +.Fn archive_read_extract +creates a restore object using +.Xr archive_write_disk_new 3 +and +.Xr archive_write_disk_set_standard_lookup 3 , +then transparently invokes +.Xr archive_write_disk_set_options 3 , +.Xr archive_write_header 3 , +.Xr archive_write_data 3 , +and +.Xr archive_write_finish_entry 3 +to create the entry on disk and copy data into it. +The +.Va flags +argument is passed unmodified to +.Xr archiv_write_disk_set_options 3 . +.It Fn archive_read_extract_set_progress_callback +Sets a pointer to a user-defined callback that can be used +for updating progress displays during extraction. +The progress function will be invoked during the extraction of large +regular files. +The progress function will be invoked with the pointer provided to this call. +Generally, the data pointed to should include a reference to the archive +object and the archive_entry object so that various statistics +can be retrieved for the progress display. +.It Fn archive_read_close +Complete the archive and invoke the close callback. +.It Fn archive_read_finish +Invokes +.Fn archive_read_close +if it was not invoked manually, then release all resources. +Note: In libarchive 1.x, this function was declared to return +.Ft void , +which made it impossible to detect certain errors when +.Fn archive_read_close +was invoked implicitly from this function. +The declaration is corrected beginning with libarchive 2.0. +.El +.Pp +Note that the library determines most of the relevant information about +the archive by inspection. +In particular, it automatically detects +.Xr gzip 1 +or +.Xr bzip2 1 +compression and transparently performs the appropriate decompression. +It also automatically detects the archive format. +.Pp +A complete description of the +.Tn struct archive +and +.Tn struct archive_entry +objects can be found in the overview manual page for +.Xr libarchive 3 . +.Sh CLIENT CALLBACKS +The callback functions must match the following prototypes: +.Bl -item -offset indent +.It +.Ft typedef ssize_t +.Fn archive_read_callback "struct archive *" "void *client_data" "const void **buffer" +.It +.\" #if ARCHIVE_API_VERSION < 2 +.Ft typedef int +.Fn archive_skip_callback "struct archive *" "void *client_data" "size_t request" +.\" #else +.\" .Ft typedef off_t +.\" .Fn archive_skip_callback "struct archive *" "void *client_data" "off_t request" +.\" #endif +.It +.Ft typedef int +.Fn archive_open_callback "struct archive *" "void *client_data" +.It +.Ft typedef int +.Fn archive_close_callback "struct archive *" "void *client_data" +.El +.Pp +The open callback is invoked by +.Fn archive_open . +It should return +.Cm ARCHIVE_OK +if the underlying file or data source is successfully +opened. +If the open fails, it should call +.Fn archive_set_error +to register an error code and message and return +.Cm ARCHIVE_FATAL . +.Pp +The read callback is invoked whenever the library +requires raw bytes from the archive. +The read callback should read data into a buffer, +set the +.Li const void **buffer +argument to point to the available data, and +return a count of the number of bytes available. +The library will invoke the read callback again +only after it has consumed this data. +The library imposes no constraints on the size +of the data blocks returned. +On end-of-file, the read callback should +return zero. +On error, the read callback should invoke +.Fn archive_set_error +to register an error code and message and +return -1. +.Pp +The skip callback is invoked when the +library wants to ignore a block of data. +The return value is the number of bytes actually +skipped, which may differ from the request. +If the callback cannot skip data, it should return +zero. +If the skip callback is not provided (the +function pointer is +.Dv NULL ), +the library will invoke the read function +instead and simply discard the result. +A skip callback can provide significant +performance gains when reading uncompressed +archives from slow disk drives or other media +that can skip quickly. +.Pp +The close callback is invoked by archive_close when +the archive processing is complete. +The callback should return +.Cm ARCHIVE_OK +on success. +On failure, the callback should invoke +.Fn archive_set_error +to register an error code and message and +return +.Cm ARCHIVE_FATAL. +.Sh EXAMPLE +The following illustrates basic usage of the library. +In this example, +the callback functions are simply wrappers around the standard +.Xr open 2 , +.Xr read 2 , +and +.Xr close 2 +system calls. +.Bd -literal -offset indent +void +list_archive(const char *name) +{ + struct mydata *mydata; + struct archive *a; + struct archive_entry *entry; + + mydata = malloc(sizeof(struct mydata)); + a = archive_read_new(); + mydata->name = name; + archive_read_support_compression_all(a); + archive_read_support_format_all(a); + archive_read_open(a, mydata, myopen, myread, myclose); + while (archive_read_next_header(a, &entry) == ARCHIVE_OK) { + printf("%s\\n",archive_entry_pathname(entry)); + archive_read_data_skip(a); + } + archive_read_finish(a); + free(mydata); +} + +ssize_t +myread(struct archive *a, void *client_data, const void **buff) +{ + struct mydata *mydata = client_data; + + *buff = mydata->buff; + return (read(mydata->fd, mydata->buff, 10240)); +} + +int +myopen(struct archive *a, void *client_data) +{ + struct mydata *mydata = client_data; + + mydata->fd = open(mydata->name, O_RDONLY); + return (mydata->fd >= 0 ? ARCHIVE_OK : ARCHIVE_FATAL); +} + +int +myclose(struct archive *a, void *client_data) +{ + struct mydata *mydata = client_data; + + if (mydata->fd > 0) + close(mydata->fd); + return (ARCHIVE_OK); +} +.Ed +.Sh RETURN VALUES +Most functions return zero on success, non-zero on error. +The possible return codes include: +.Cm ARCHIVE_OK +(the operation succeeded), +.Cm ARCHIVE_WARN +(the operation succeeded but a non-critical error was encountered), +.Cm ARCHIVE_EOF +(end-of-archive was encountered), +.Cm ARCHIVE_RETRY +(the operation failed but can be retried), +and +.Cm ARCHIVE_FATAL +(there was a fatal error; the archive should be closed immediately). +Detailed error codes and textual descriptions are available from the +.Fn archive_errno +and +.Fn archive_error_string +functions. +.Pp +.Fn archive_read_new +returns a pointer to a freshly allocated +.Tn struct archive +object. +It returns +.Dv NULL +on error. +.Pp +.Fn archive_read_data +returns a count of bytes actually read or zero at the end of the entry. +On error, a value of +.Cm ARCHIVE_FATAL , +.Cm ARCHIVE_WARN , +or +.Cm ARCHIVE_RETRY +is returned and an error code and textual description can be retrieved from the +.Fn archive_errno +and +.Fn archive_error_string +functions. +.Pp +The library expects the client callbacks to behave similarly. +If there is an error, you can use +.Fn archive_set_error +to set an appropriate error code and description, +then return one of the non-zero values above. +(Note that the value eventually returned to the client may +not be the same; many errors that are not critical at the level +of basic I/O can prevent the archive from being properly read, +thus most I/O errors eventually cause +.Cm ARCHIVE_FATAL +to be returned.) +.\" .Sh ERRORS +.Sh SEE ALSO +.Xr tar 1 , +.Xr archive 3 , +.Xr archive_util 3 , +.Xr tar 5 +.Sh HISTORY +The +.Nm libarchive +library first appeared in +.Fx 5.3 . +.Sh AUTHORS +.An -nosplit +The +.Nm libarchive +library was written by +.An Tim Kientzle Aq kientzle@acm.org . +.Sh BUGS +Many traditional archiver programs treat +empty files as valid empty archives. +For example, many implementations of +.Xr tar 1 +allow you to append entries to an empty file. +Of course, it is impossible to determine the format of an empty file +by inspecting the contents, so this library treats empty files as +having a special +.Dq empty +format. diff --git a/contrib/libarchive-2.1/libarchive/archive_read.c b/contrib/libarchive-2.1/libarchive/archive_read.c new file mode 100644 index 0000000000..9eab499890 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_read.c @@ -0,0 +1,737 @@ +/*- + * 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. + */ + +/* + * This file contains the "essential" portions of the read API, that + * is, stuff that will probably always be used by any client that + * actually needs to read an archive. Optional pieces have been, as + * far as possible, separated out into separate files to avoid + * needlessly bloating statically-linked clients. + */ + +#include "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read.c,v 1.34 2007/04/05 15:51:19 cperciva Exp $"); + +#ifdef HAVE_ERRNO_H +#include +#endif +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "archive.h" +#include "archive_entry.h" +#include "archive_private.h" +#include "archive_read_private.h" + +static void choose_decompressor(struct archive_read *, const void*, size_t); +static int choose_format(struct archive_read *); +static off_t dummy_skip(struct archive_read *, off_t); + +/* + * Allocate, initialize and return a struct archive object. + */ +struct archive * +archive_read_new(void) +{ + struct archive_read *a; + unsigned char *nulls; + + a = (struct archive_read *)malloc(sizeof(*a)); + if (a == NULL) + return (NULL); + memset(a, 0, sizeof(*a)); + a->archive.magic = ARCHIVE_READ_MAGIC; + a->bytes_per_block = ARCHIVE_DEFAULT_BYTES_PER_BLOCK; + + a->null_length = 1024; + nulls = (unsigned char *)malloc(a->null_length); + if (nulls == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate archive object 'nulls' element"); + free(a); + return (NULL); + } + memset(nulls, 0, a->null_length); + a->nulls = nulls; + + a->archive.state = ARCHIVE_STATE_NEW; + a->entry = archive_entry_new(); + + /* We always support uncompressed archives. */ + archive_read_support_compression_none(&a->archive); + + return (&a->archive); +} + +/* + * Record the do-not-extract-to file. This belongs in archive_read_extract.c. + */ +void +archive_read_extract_set_skip_file(struct archive *_a, dev_t d, ino_t i) +{ + struct archive_read *a = (struct archive_read *)_a; + __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY, + "archive_read_extract_set_skip_file"); + a->skip_file_dev = d; + a->skip_file_ino = i; +} + + +/* + * Open the archive + */ +int +archive_read_open(struct archive *a, void *client_data, + archive_open_callback *client_opener, archive_read_callback *client_reader, + archive_close_callback *client_closer) +{ + /* Old archive_read_open() is just a thin shell around + * archive_read_open2. */ + return archive_read_open2(a, client_data, client_opener, + client_reader, NULL, client_closer); +} + +int +archive_read_open2(struct archive *_a, void *client_data, + archive_open_callback *client_opener, + archive_read_callback *client_reader, + archive_skip_callback *client_skipper, + archive_close_callback *client_closer) +{ + struct archive_read *a = (struct archive_read *)_a; + const void *buffer; + ssize_t bytes_read; + int e; + + __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_open"); + + if (client_reader == NULL) + __archive_errx(1, + "No reader function provided to archive_read_open"); + + /* + * Set these NULL initially. If the open or initial read fails, + * we'll leave them NULL to indicate that the file is invalid. + * (In particular, this helps ensure that the closer doesn't + * get called more than once.) + */ + a->client_opener = NULL; + a->client_reader = NULL; + a->client_skipper = NULL; + a->client_closer = NULL; + a->client_data = NULL; + + /* Open data source. */ + if (client_opener != NULL) { + e =(client_opener)(&a->archive, client_data); + if (e != 0) { + /* If the open failed, call the closer to clean up. */ + if (client_closer) + (client_closer)(&a->archive, client_data); + return (e); + } + } + + /* Read first block now for compress format detection. */ + bytes_read = (client_reader)(&a->archive, client_data, &buffer); + + if (bytes_read < 0) { + /* If the first read fails, close before returning error. */ + if (client_closer) + (client_closer)(&a->archive, client_data); + /* client_reader should have already set error information. */ + return (ARCHIVE_FATAL); + } + + /* Now that the client callbacks have worked, remember them. */ + a->client_opener = client_opener; /* Do we need to remember this? */ + a->client_reader = client_reader; + a->client_skipper = client_skipper; + a->client_closer = client_closer; + a->client_data = client_data; + + /* Select a decompression routine. */ + choose_decompressor(a, buffer, (size_t)bytes_read); + if (a->decompressor == NULL) + return (ARCHIVE_FATAL); + + /* Initialize decompression routine with the first block of data. */ + e = (a->decompressor->init)(a, buffer, (size_t)bytes_read); + + if (e == ARCHIVE_OK) + a->archive.state = ARCHIVE_STATE_HEADER; + + /* + * If the decompressor didn't register a skip function, provide a + * dummy compression-layer skip function. + */ + if (a->decompressor->skip == NULL) + a->decompressor->skip = dummy_skip; + + return (e); +} + +/* + * Allow each registered decompression routine to bid on whether it + * wants to handle this stream. Return index of winning bidder. + */ +static void +choose_decompressor(struct archive_read *a, + const void *buffer, size_t bytes_read) +{ + int decompression_slots, i, bid, best_bid; + struct decompressor_t *decompressor, *best_decompressor; + + decompression_slots = sizeof(a->decompressors) / + sizeof(a->decompressors[0]); + + best_bid = 0; + a->decompressor = NULL; + best_decompressor = NULL; + + decompressor = a->decompressors; + for (i = 0; i < decompression_slots; i++) { + if (decompressor->bid) { + bid = (decompressor->bid)(buffer, bytes_read); + if (bid > best_bid || best_decompressor == NULL) { + best_bid = bid; + best_decompressor = decompressor; + } + } + decompressor ++; + } + + /* + * There were no bidders; this is a serious programmer error + * and demands a quick and definitive abort. + */ + if (best_decompressor == NULL) + __archive_errx(1, "No decompressors were registered; you " + "must call at least one " + "archive_read_support_compression_XXX function in order " + "to successfully read an archive."); + + /* + * There were bidders, but no non-zero bids; this means we can't + * support this stream. + */ + if (best_bid < 1) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Unrecognized archive format"); + return; + } + + /* Record the best decompressor for this stream. */ + a->decompressor = best_decompressor; +} + +/* + * Dummy skip function, for use if the compression layer doesn't provide + * one: This code just reads data and discards it. + */ +static off_t +dummy_skip(struct archive_read * a, off_t request) +{ + const void * dummy_buffer; + ssize_t bytes_read; + off_t bytes_skipped; + + for (bytes_skipped = 0; request > 0;) { + bytes_read = (a->decompressor->read_ahead)(a, &dummy_buffer, 1); + if (bytes_read < 0) + return (bytes_read); + if (bytes_read == 0) { + /* Premature EOF. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Truncated input file (need to skip %jd bytes)", + (intmax_t)request); + return (ARCHIVE_FATAL); + } + if (bytes_read > request) + bytes_read = (ssize_t)request; + (a->decompressor->consume)(a, (size_t)bytes_read); + request -= bytes_read; + bytes_skipped += bytes_read; + } + + return (bytes_skipped); +} + +/* + * Read header of next entry. + */ +int +archive_read_next_header(struct archive *_a, struct archive_entry **entryp) +{ + struct archive_read *a = (struct archive_read *)_a; + struct archive_entry *entry; + int slot, ret; + + __archive_check_magic(_a, ARCHIVE_READ_MAGIC, + ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, + "archive_read_next_header"); + + *entryp = NULL; + entry = a->entry; + archive_entry_clear(entry); + archive_clear_error(&a->archive); + + /* + * If client didn't consume entire data, skip any remainder + * (This is especially important for GNU incremental directories.) + */ + if (a->archive.state == ARCHIVE_STATE_DATA) { + ret = archive_read_data_skip(&a->archive); + if (ret == ARCHIVE_EOF) { + archive_set_error(&a->archive, EIO, "Premature end-of-file."); + a->archive.state = ARCHIVE_STATE_FATAL; + return (ARCHIVE_FATAL); + } + if (ret != ARCHIVE_OK) + return (ret); + } + + /* Record start-of-header. */ + a->header_position = a->archive.file_position; + + slot = choose_format(a); + if (slot < 0) { + a->archive.state = ARCHIVE_STATE_FATAL; + return (ARCHIVE_FATAL); + } + a->format = &(a->formats[slot]); + ret = (a->format->read_header)(a, entry); + + /* + * EOF and FATAL are persistent at this layer. By + * modifying the state, we guarantee that future calls to + * read a header or read data will fail. + */ + switch (ret) { + case ARCHIVE_EOF: + a->archive.state = ARCHIVE_STATE_EOF; + break; + case ARCHIVE_OK: + a->archive.state = ARCHIVE_STATE_DATA; + break; + case ARCHIVE_WARN: + a->archive.state = ARCHIVE_STATE_DATA; + break; + case ARCHIVE_RETRY: + break; + case ARCHIVE_FATAL: + a->archive.state = ARCHIVE_STATE_FATAL; + break; + } + + *entryp = entry; + a->read_data_output_offset = 0; + a->read_data_remaining = 0; + return (ret); +} + +/* + * Allow each registered format to bid on whether it wants to handle + * the next entry. Return index of winning bidder. + */ +static int +choose_format(struct archive_read *a) +{ + int slots; + int i; + int bid, best_bid; + int best_bid_slot; + + slots = sizeof(a->formats) / sizeof(a->formats[0]); + best_bid = -1; + best_bid_slot = -1; + + /* Set up a->format and a->pformat_data for convenience of bidders. */ + a->format = &(a->formats[0]); + for (i = 0; i < slots; i++, a->format++) { + if (a->format->bid) { + bid = (a->format->bid)(a); + if (bid == ARCHIVE_FATAL) + return (ARCHIVE_FATAL); + if ((bid > best_bid) || (best_bid_slot < 0)) { + best_bid = bid; + best_bid_slot = i; + } + } + } + + /* + * There were no bidders; this is a serious programmer error + * and demands a quick and definitive abort. + */ + if (best_bid_slot < 0) + __archive_errx(1, "No formats were registered; you must " + "invoke at least one archive_read_support_format_XXX " + "function in order to successfully read an archive."); + + /* + * There were bidders, but no non-zero bids; this means we + * can't support this stream. + */ + if (best_bid < 1) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Unrecognized archive format"); + return (ARCHIVE_FATAL); + } + + return (best_bid_slot); +} + +/* + * Return the file offset (within the uncompressed data stream) where + * the last header started. + */ +int64_t +archive_read_header_position(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + __archive_check_magic(_a, ARCHIVE_READ_MAGIC, + ARCHIVE_STATE_ANY, "archive_read_header_position"); + return (a->header_position); +} + +/* + * Read data from an archive entry, using a read(2)-style interface. + * This is a convenience routine that just calls + * archive_read_data_block and copies the results into the client + * buffer, filling any gaps with zero bytes. Clients using this + * API can be completely ignorant of sparse-file issues; sparse files + * will simply be padded with nulls. + * + * DO NOT intermingle calls to this function and archive_read_data_block + * to read a single entry body. + */ +ssize_t +archive_read_data(struct archive *_a, void *buff, size_t s) +{ + struct archive_read *a = (struct archive_read *)_a; + char *dest; + const void *read_buf; + size_t bytes_read; + size_t len; + int r; + + bytes_read = 0; + dest = (char *)buff; + + while (s > 0) { + if (a->read_data_remaining == 0) { + read_buf = a->read_data_block; + r = archive_read_data_block(&a->archive, &read_buf, + &a->read_data_remaining, &a->read_data_offset); + a->read_data_block = read_buf; + if (r == ARCHIVE_EOF) + return (bytes_read); + /* + * Error codes are all negative, so the status + * return here cannot be confused with a valid + * byte count. (ARCHIVE_OK is zero.) + */ + if (r < ARCHIVE_OK) + return (r); + } + + if (a->read_data_offset < a->read_data_output_offset) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Encountered out-of-order sparse blocks"); + return (ARCHIVE_RETRY); + } + + /* Compute the amount of zero padding needed. */ + if (a->read_data_output_offset + (off_t)s < + a->read_data_offset) { + len = s; + } else if (a->read_data_output_offset < + a->read_data_offset) { + len = a->read_data_offset - + a->read_data_output_offset; + } else + len = 0; + + /* Add zeroes. */ + memset(dest, 0, len); + s -= len; + a->read_data_output_offset += len; + dest += len; + bytes_read += len; + + /* Copy data if there is any space left. */ + if (s > 0) { + len = a->read_data_remaining; + if (len > s) + len = s; + memcpy(dest, a->read_data_block, len); + s -= len; + a->read_data_block += len; + a->read_data_remaining -= len; + a->read_data_output_offset += len; + a->read_data_offset += len; + dest += len; + bytes_read += len; + } + } + return (bytes_read); +} + +#if ARCHIVE_API_VERSION < 3 +/* + * Obsolete function provided for compatibility only. Note that the API + * of this function doesn't allow the caller to detect if the remaining + * data from the archive entry is shorter than the buffer provided, or + * even if an error occurred while reading data. + */ +int +archive_read_data_into_buffer(struct archive *a, void *d, ssize_t len) +{ + + archive_read_data(a, d, len); + return (ARCHIVE_OK); +} +#endif + +/* + * Skip over all remaining data in this entry. + */ +int +archive_read_data_skip(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + int r; + const void *buff; + size_t size; + off_t offset; + + __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA, + "archive_read_data_skip"); + + if (a->format->read_data_skip != NULL) + r = (a->format->read_data_skip)(a); + else { + while ((r = archive_read_data_block(&a->archive, + &buff, &size, &offset)) + == ARCHIVE_OK) + ; + } + + if (r == ARCHIVE_EOF) + r = ARCHIVE_OK; + + a->archive.state = ARCHIVE_STATE_HEADER; + return (r); +} + +/* + * Read the next block of entry data from the archive. + * This is a zero-copy interface; the client receives a pointer, + * size, and file offset of the next available block of data. + * + * Returns ARCHIVE_OK if the operation is successful, ARCHIVE_EOF if + * the end of entry is encountered. + */ +int +archive_read_data_block(struct archive *_a, + const void **buff, size_t *size, off_t *offset) +{ + struct archive_read *a = (struct archive_read *)_a; + __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA, + "archive_read_data_block"); + + if (a->format->read_data == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Internal error: " + "No format_read_data_block function registered"); + return (ARCHIVE_FATAL); + } + + return (a->format->read_data)(a, buff, size, offset); +} + +/* + * Close the file and release most resources. + * + * Be careful: client might just call read_new and then read_finish. + * Don't assume we actually read anything or performed any non-trivial + * initialization. + */ +int +archive_read_close(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + int r = ARCHIVE_OK, r1 = ARCHIVE_OK; + size_t i, n; + + __archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC, + ARCHIVE_STATE_ANY, "archive_read_close"); + a->archive.state = ARCHIVE_STATE_CLOSED; + + /* Call cleanup functions registered by optional components. */ + if (a->cleanup_archive_extract != NULL) + r = (a->cleanup_archive_extract)(a); + + /* TODO: Clean up the formatters. */ + + /* Clean up the decompressors. */ + n = sizeof(a->decompressors)/sizeof(a->decompressors[0]); + for (i = 0; i < n; i++) { + if (a->decompressors[i].finish != NULL) { + r1 = (a->decompressors[i].finish)(a); + if (r1 < r) + r = r1; + } + } + + /* Close the client stream. */ + if (a->client_closer != NULL) { + r1 = ((a->client_closer)(&a->archive, a->client_data)); + if (r1 < r) + r = r1; + } + + return (r); +} + +/* + * Release memory and other resources. + */ +#if ARCHIVE_API_VERSION > 1 +int +#else +/* Temporarily allow library to compile with either 1.x or 2.0 API. */ +void +#endif +archive_read_finish(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + int i; + int slots; + int r = ARCHIVE_OK; + + __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY, + "archive_read_finish"); + if (a->archive.state != ARCHIVE_STATE_CLOSED) + r = archive_read_close(&a->archive); + + /* Cleanup format-specific data. */ + slots = sizeof(a->formats) / sizeof(a->formats[0]); + for (i = 0; i < slots; i++) { + a->format = &(a->formats[i]); + if (a->formats[i].cleanup) + (a->formats[i].cleanup)(a); + } + + /* Casting a pointer to int allows us to remove 'const.' */ + free((void *)(uintptr_t)(const void *)a->nulls); + archive_string_free(&a->archive.error_string); + if (a->entry) + archive_entry_free(a->entry); + a->archive.magic = 0; + free(a); +#if ARCHIVE_API_VERSION > 1 + return (r); +#endif +} + +/* + * Used internally by read format handlers to register their bid and + * initialization functions. + */ +int +__archive_read_register_format(struct archive_read *a, + void *format_data, + int (*bid)(struct archive_read *), + int (*read_header)(struct archive_read *, struct archive_entry *), + int (*read_data)(struct archive_read *, const void **, size_t *, off_t *), + int (*read_data_skip)(struct archive_read *), + int (*cleanup)(struct archive_read *)) +{ + int i, number_slots; + + __archive_check_magic(&a->archive, + ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, + "__archive_read_register_format"); + + number_slots = sizeof(a->formats) / sizeof(a->formats[0]); + + for (i = 0; i < number_slots; i++) { + if (a->formats[i].bid == bid) + return (ARCHIVE_WARN); /* We've already installed */ + if (a->formats[i].bid == NULL) { + a->formats[i].bid = bid; + a->formats[i].read_header = read_header; + a->formats[i].read_data = read_data; + a->formats[i].read_data_skip = read_data_skip; + a->formats[i].cleanup = cleanup; + a->formats[i].data = format_data; + return (ARCHIVE_OK); + } + } + + __archive_errx(1, "Not enough slots for format registration"); + return (ARCHIVE_FATAL); /* Never actually called. */ +} + +/* + * Used internally by decompression routines to register their bid and + * initialization functions. + */ +struct decompressor_t * +__archive_read_register_compression(struct archive_read *a, + int (*bid)(const void *, size_t), + int (*init)(struct archive_read *, const void *, size_t)) +{ + int i, number_slots; + + __archive_check_magic(&a->archive, + ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, + "__archive_read_register_compression"); + + number_slots = sizeof(a->decompressors) / sizeof(a->decompressors[0]); + + for (i = 0; i < number_slots; i++) { + if (a->decompressors[i].bid == bid) + return (a->decompressors + i); + if (a->decompressors[i].bid == NULL) { + a->decompressors[i].bid = bid; + a->decompressors[i].init = init; + return (a->decompressors + i); + } + } + + __archive_errx(1, "Not enough slots for compression registration"); + return (NULL); /* Never actually executed. */ +} diff --git a/contrib/libarchive-2.1/libarchive/archive_read_data_into_fd.c b/contrib/libarchive-2.1/libarchive/archive_read_data_into_fd.c new file mode 100644 index 0000000000..664cc2c3c2 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_read_data_into_fd.c @@ -0,0 +1,89 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_data_into_fd.c,v 1.15 2007/04/02 00:21:46 kientzle Exp $"); + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "archive.h" +#include "archive_private.h" + +/* Maximum amount of data to write at one time. */ +#define MAX_WRITE (1024 * 1024) + +/* + * This implementation minimizes copying of data and is sparse-file aware. + */ +int +archive_read_data_into_fd(struct archive *a, int fd) +{ + int r; + const void *buff; + size_t size, bytes_to_write; + ssize_t bytes_written, total_written; + off_t offset; + off_t output_offset; + + __archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA, "archive_read_data_into_fd"); + + total_written = 0; + output_offset = 0; + + while ((r = archive_read_data_block(a, &buff, &size, &offset)) == + ARCHIVE_OK) { + const char *p = buff; + if (offset > output_offset) { + lseek(fd, offset - output_offset, SEEK_CUR); + output_offset = offset; + } + while (size > 0) { + bytes_to_write = size; + if (bytes_to_write > MAX_WRITE) + bytes_to_write = MAX_WRITE; + bytes_written = write(fd, p, bytes_to_write); + if (bytes_written < 0) { + archive_set_error(a, errno, "Write error"); + return (-1); + } + output_offset += bytes_written; + total_written += bytes_written; + p += bytes_written; + size -= bytes_written; + } + } + + if (r != ARCHIVE_EOF) + return (r); + return (ARCHIVE_OK); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_read_extract.c b/contrib/libarchive-2.1/libarchive/archive_read_extract.c new file mode 100644 index 0000000000..a317ca916f --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_read_extract.c @@ -0,0 +1,170 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_extract.c,v 1.58 2007/04/16 04:04:49 cperciva Exp $"); + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "archive.h" +#include "archive_private.h" +#include "archive_read_private.h" +#include "archive_write_disk_private.h" + +struct extract { + struct archive *ad; /* archive_write_disk object */ + + /* Progress function invoked during extract. */ + void (*extract_progress)(void *); + void *extract_progress_user_data; +}; + +static int archive_read_extract_cleanup(struct archive_read *); +static int copy_data(struct archive *ar, struct archive *aw); +static struct extract *get_extract(struct archive_read *); + +static struct extract * +get_extract(struct archive_read *a) +{ + /* If we haven't initialized, do it now. */ + /* This also sets up a lot of global state. */ + if (a->extract == NULL) { + a->extract = (struct extract *)malloc(sizeof(*a->extract)); + if (a->extract == NULL) { + archive_set_error(&a->archive, ENOMEM, "Can't extract"); + return (NULL); + } + a->extract->ad = archive_write_disk_new(); + if (a->extract->ad == NULL) { + archive_set_error(&a->archive, ENOMEM, "Can't extract"); + return (NULL); + } + archive_write_disk_set_standard_lookup(a->extract->ad); + a->cleanup_archive_extract = archive_read_extract_cleanup; + } + return (a->extract); +} + +int +archive_read_extract(struct archive *_a, struct archive_entry *entry, int flags) +{ + struct archive_read *a = (struct archive_read *)_a; + struct extract *extract; + int r, r2; + + extract = get_extract(a); + if (extract == NULL) + return (ARCHIVE_FATAL); + + /* Set up for this particular entry. */ + extract = a->extract; + archive_write_disk_set_options(a->extract->ad, flags); + archive_write_disk_set_skip_file(a->extract->ad, + a->skip_file_dev, a->skip_file_ino); + r = archive_write_header(a->extract->ad, entry); + if (r < ARCHIVE_WARN) + r = ARCHIVE_WARN; + if (r != ARCHIVE_OK) + /* If _write_header failed, copy the error. */ + archive_copy_error(&a->archive, extract->ad); + else + /* Otherwise, pour data into the entry. */ + r = copy_data(_a, a->extract->ad); + r2 = archive_write_finish_entry(a->extract->ad); + if (r2 < ARCHIVE_WARN) + r2 = ARCHIVE_WARN; + /* Use the first message. */ + if (r2 != ARCHIVE_OK && r == ARCHIVE_OK) + archive_copy_error(&a->archive, extract->ad); + /* Use the worst error return. */ + if (r2 < r) + r = r2; + return (r); +} + +void +archive_read_extract_set_progress_callback(struct archive *_a, + void (*progress_func)(void *), void *user_data) +{ + struct archive_read *a = (struct archive_read *)_a; + struct extract *extract = get_extract(a); + if (extract != NULL) { + extract->extract_progress = progress_func; + extract->extract_progress_user_data = user_data; + } +} + +static int +copy_data(struct archive *ar, struct archive *aw) +{ + int r; + const void *buff; + size_t size; + off_t offset; + + for (;;) { + r = archive_read_data_block(ar, &buff, &size, &offset); + if (r == ARCHIVE_EOF) + return (ARCHIVE_OK); + if (r != ARCHIVE_OK) + return (r); + r = archive_write_data_block(aw, buff, size, offset); + if (r < ARCHIVE_WARN) + r = ARCHIVE_WARN; + if (r != ARCHIVE_OK) { + archive_set_error(ar, archive_errno(aw), + "%s", archive_error_string(aw)); + return (r); + } + } +} + +/* + * Cleanup function for archive_extract. + */ +static int +archive_read_extract_cleanup(struct archive_read *a) +{ + int ret = ARCHIVE_OK; + +#if ARCHIVE_API_VERSION > 1 + ret = +#endif + archive_write_finish(a->extract->ad); + free(a->extract); + a->extract = NULL; + return (ret); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_read_open_fd.c b/contrib/libarchive-2.1/libarchive/archive_read_open_fd.c new file mode 100644 index 0000000000..4db681c3a2 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_read_open_fd.c @@ -0,0 +1,165 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_fd.c,v 1.11 2007/01/09 08:05:55 kientzle Exp $"); + +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "archive.h" + +struct read_fd_data { + int fd; + size_t block_size; + void *buffer; +}; + +static int file_close(struct archive *, void *); +static int file_open(struct archive *, void *); +static ssize_t file_read(struct archive *, void *, const void **buff); +#if ARCHIVE_API_VERSION < 2 +static ssize_t file_skip(struct archive *, void *, size_t request); +#else +static off_t file_skip(struct archive *, void *, off_t request); +#endif + +int +archive_read_open_fd(struct archive *a, int fd, size_t block_size) +{ + struct read_fd_data *mine; + + mine = (struct read_fd_data *)malloc(sizeof(*mine)); + if (mine == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); + } + mine->block_size = block_size; + mine->buffer = malloc(mine->block_size); + if (mine->buffer == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + free(mine); + return (ARCHIVE_FATAL); + } + mine->fd = fd; + return (archive_read_open2(a, mine, file_open, file_read, file_skip, file_close)); +} + +static int +file_open(struct archive *a, void *client_data) +{ + struct read_fd_data *mine = (struct read_fd_data *)client_data; + struct stat st; + + if (fstat(mine->fd, &st) != 0) { + archive_set_error(a, errno, "Can't stat fd %d", mine->fd); + return (ARCHIVE_FATAL); + } + + if (S_ISREG(st.st_mode)) + archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino); + return (ARCHIVE_OK); +} + +static ssize_t +file_read(struct archive *a, void *client_data, const void **buff) +{ + struct read_fd_data *mine = (struct read_fd_data *)client_data; + ssize_t bytes_read; + + *buff = mine->buffer; + bytes_read = read(mine->fd, mine->buffer, mine->block_size); + if (bytes_read < 0) { + archive_set_error(a, errno, "Error reading fd %d", mine->fd); + } + return (bytes_read); +} + +#if ARCHIVE_API_VERSION < 2 +static ssize_t +file_skip(struct archive *a, void *client_data, size_t request) +#else +static off_t +file_skip(struct archive *a, void *client_data, off_t request) +#endif +{ + struct read_fd_data *mine = (struct read_fd_data *)client_data; + off_t old_offset, new_offset; + + /* Reduce request to the next smallest multiple of block_size */ + request = (request / mine->block_size) * mine->block_size; + /* + * Hurray for lazy evaluation: if the first lseek fails, the second + * one will not be executed. + */ + if (((old_offset = lseek(mine->fd, 0, SEEK_CUR)) < 0) || + ((new_offset = lseek(mine->fd, request, SEEK_CUR)) < 0)) + { + if (errno == ESPIPE) + { + /* + * Failure to lseek() can be caused by the file + * descriptor pointing to a pipe, socket or FIFO. + * Return 0 here, so the compression layer will use + * read()s instead to advance the file descriptor. + * It's slower of course, but works as well. + */ + return (0); + } + /* + * There's been an error other than ESPIPE. This is most + * likely caused by a programmer error (too large request) + * or a corrupted archive file. + */ + archive_set_error(a, errno, "Error seeking"); + return (-1); + } + return (new_offset - old_offset); +} + +static int +file_close(struct archive *a, void *client_data) +{ + struct read_fd_data *mine = (struct read_fd_data *)client_data; + + (void)a; /* UNUSED */ + if (mine->buffer != NULL) + free(mine->buffer); + free(mine); + return (ARCHIVE_OK); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_read_open_file.c b/contrib/libarchive-2.1/libarchive/archive_read_open_file.c new file mode 100644 index 0000000000..6d83955163 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_read_open_file.c @@ -0,0 +1,157 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_file.c,v 1.19 2007/01/09 08:05:55 kientzle Exp $"); + +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "archive.h" + +struct read_FILE_data { + FILE *f; + size_t block_size; + void *buffer; +}; + +static int file_close(struct archive *, void *); +static int file_open(struct archive *, void *); +static ssize_t file_read(struct archive *, void *, const void **buff); +#if ARCHIVE_API_VERSION < 2 +static ssize_t file_skip(struct archive *, void *, size_t request); +#else +static off_t file_skip(struct archive *, void *, off_t request); +#endif + +int +archive_read_open_FILE(struct archive *a, FILE *f) +{ + struct read_FILE_data *mine; + + mine = (struct read_FILE_data *)malloc(sizeof(*mine)); + if (mine == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); + } + mine->block_size = 128 * 1024; + mine->buffer = malloc(mine->block_size); + if (mine->buffer == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + free(mine); + return (ARCHIVE_FATAL); + } + mine->f = f; + return (archive_read_open2(a, mine, file_open, file_read, + file_skip, file_close)); +} + +static int +file_open(struct archive *a, void *client_data) +{ + struct read_FILE_data *mine = (struct read_FILE_data *)client_data; + struct stat st; + + /* + * If we can't fstat() the file, it may just be that + * it's not a file. (FILE * objects can wrap many kinds + * of I/O streams.) + */ + if (fstat(fileno(mine->f), &st) == 0 && S_ISREG(st.st_mode)) + archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino); + + return (ARCHIVE_OK); +} + +static ssize_t +file_read(struct archive *a, void *client_data, const void **buff) +{ + struct read_FILE_data *mine = (struct read_FILE_data *)client_data; + ssize_t bytes_read; + + *buff = mine->buffer; + bytes_read = fread(mine->buffer, 1, mine->block_size, mine->f); + if (bytes_read < 0) { + archive_set_error(a, errno, "Error reading file"); + } + return (bytes_read); +} + +#if ARCHIVE_API_VERSION < 2 +static ssize_t +file_skip(struct archive *a, void *client_data, size_t request) +#else +static off_t +file_skip(struct archive *a, void *client_data, off_t request) +#endif +{ + struct read_FILE_data *mine = (struct read_FILE_data *)client_data; + + /* + * Note: the 'fd' and 'filename' versions round the request + * down to a multiple of the block size to ensure proper + * operation on block-oriented media such as tapes. But stdio + * doesn't work with such media (it doesn't ensure blocking), + * so we don't need to bother. + */ +#if HAVE_FSEEKO + if (fseeko(mine->f, request, SEEK_CUR) != 0) +#else + if (fseek(mine->f, request, SEEK_CUR) != 0) +#endif + { + archive_set_error(a, errno, "Error skipping forward"); + return (ARCHIVE_FATAL); + } + return (request); +} + +static int +file_close(struct archive *a, void *client_data) +{ + struct read_FILE_data *mine = (struct read_FILE_data *)client_data; + + (void)a; /* UNUSED */ + if (mine->buffer != NULL) + free(mine->buffer); + free(mine); + return (ARCHIVE_OK); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_read_open_filename.c b/contrib/libarchive-2.1/libarchive/archive_read_open_filename.c new file mode 100644 index 0000000000..47bc4efaa5 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_read_open_filename.c @@ -0,0 +1,240 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_filename.c,v 1.18 2007/01/09 08:05:55 kientzle Exp $"); + +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "archive.h" + +struct read_file_data { + int fd; + size_t block_size; + void *buffer; + mode_t st_mode; /* Mode bits for opened file. */ + char filename[1]; /* Must be last! */ +}; + +static int file_close(struct archive *, void *); +static int file_open(struct archive *, void *); +static ssize_t file_read(struct archive *, void *, const void **buff); +#if ARCHIVE_API_VERSION < 2 +static ssize_t file_skip(struct archive *, void *, size_t request); +#else +static off_t file_skip(struct archive *, void *, off_t request); +#endif + +int +archive_read_open_file(struct archive *a, const char *filename, + size_t block_size) +{ + return (archive_read_open_filename(a, filename, block_size)); +} + +int +archive_read_open_filename(struct archive *a, const char *filename, + size_t block_size) +{ + struct read_file_data *mine; + + if (filename == NULL || filename[0] == '\0') { + mine = (struct read_file_data *)malloc(sizeof(*mine)); + if (mine == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); + } + mine->filename[0] = '\0'; + } else { + mine = (struct read_file_data *)malloc(sizeof(*mine) + strlen(filename)); + if (mine == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); + } + strcpy(mine->filename, filename); + } + mine->block_size = block_size; + mine->buffer = NULL; + mine->fd = -1; + return (archive_read_open2(a, mine, file_open, file_read, file_skip, file_close)); +} + +static int +file_open(struct archive *a, void *client_data) +{ + struct read_file_data *mine = (struct read_file_data *)client_data; + struct stat st; + + mine->buffer = malloc(mine->block_size); + if (mine->buffer == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); + } + if (mine->filename[0] != '\0') + mine->fd = open(mine->filename, O_RDONLY); + else + mine->fd = 0; /* Fake "open" for stdin. */ + if (mine->fd < 0) { + archive_set_error(a, errno, "Failed to open '%s'", + mine->filename); + return (ARCHIVE_FATAL); + } + if (fstat(mine->fd, &st) == 0) { + /* If we're reading a file from disk, ensure that we don't + overwrite it with an extracted file. */ + if (S_ISREG(st.st_mode)) + archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino); + /* Remember mode so close can decide whether to flush. */ + mine->st_mode = st.st_mode; + } else { + if (mine->filename[0] == '\0') + archive_set_error(a, errno, "Can't stat stdin"); + else + archive_set_error(a, errno, "Can't stat '%s'", + mine->filename); + return (ARCHIVE_FATAL); + } + return (0); +} + +static ssize_t +file_read(struct archive *a, void *client_data, const void **buff) +{ + struct read_file_data *mine = (struct read_file_data *)client_data; + ssize_t bytes_read; + + *buff = mine->buffer; + bytes_read = read(mine->fd, mine->buffer, mine->block_size); + if (bytes_read < 0) { + if (mine->filename[0] == '\0') + archive_set_error(a, errno, "Error reading stdin"); + else + archive_set_error(a, errno, "Error reading '%s'", + mine->filename); + } + return (bytes_read); +} + +#if ARCHIVE_API_VERSION < 2 +static ssize_t +file_skip(struct archive *a, void *client_data, size_t request) +#else +static off_t +file_skip(struct archive *a, void *client_data, off_t request) +#endif +{ + struct read_file_data *mine = (struct read_file_data *)client_data; + off_t old_offset, new_offset; + + /* Reduce request to the next smallest multiple of block_size */ + request = (request / mine->block_size) * mine->block_size; + /* + * Hurray for lazy evaluation: if the first lseek fails, the second + * one will not be executed. + */ + if (((old_offset = lseek(mine->fd, 0, SEEK_CUR)) < 0) || + ((new_offset = lseek(mine->fd, request, SEEK_CUR)) < 0)) + { + if (errno == ESPIPE) + { + /* + * Failure to lseek() can be caused by the file + * descriptor pointing to a pipe, socket or FIFO. + * Return 0 here, so the compression layer will use + * read()s instead to advance the file descriptor. + * It's slower of course, but works as well. + */ + return (0); + } + /* + * There's been an error other than ESPIPE. This is most + * likely caused by a programmer error (too large request) + * or a corrupted archive file. + */ + if (mine->filename[0] == '\0') + /* + * Should never get here, since lseek() on stdin ought + * to return an ESPIPE error. + */ + archive_set_error(a, errno, "Error seeking in stdin"); + else + archive_set_error(a, errno, "Error seeking in '%s'", + mine->filename); + return (-1); + } + return (new_offset - old_offset); +} + +static int +file_close(struct archive *a, void *client_data) +{ + struct read_file_data *mine = (struct read_file_data *)client_data; + + (void)a; /* UNUSED */ + + /* + * Sometimes, we should flush the input before closing. + * Regular files: faster to just close without flush. + * Devices: must not flush (user might need to + * read the "next" item on a non-rewind device). + * Pipes and sockets: must flush (otherwise, the + * program feeding the pipe or socket may complain). + * Here, I flush everything except for regular files and + * device nodes. + */ + if (!S_ISREG(mine->st_mode) + && !S_ISCHR(mine->st_mode) + && !S_ISBLK(mine->st_mode)) { + ssize_t bytesRead; + do { + bytesRead = read(mine->fd, mine->buffer, + mine->block_size); + } while (bytesRead > 0); + } + /* If a named file was opened, then it needs to be closed. */ + if (mine->filename[0] != '\0') + close(mine->fd); + if (mine->buffer != NULL) + free(mine->buffer); + free(mine); + return (ARCHIVE_OK); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_read_open_memory.c b/contrib/libarchive-2.1/libarchive/archive_read_open_memory.c new file mode 100644 index 0000000000..3fc8522bc7 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_read_open_memory.c @@ -0,0 +1,156 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_memory.c,v 1.4 2007/04/02 00:25:11 kientzle Exp $"); + +#include +#include +#include + +#include "archive.h" + +/* + * Glue to read an archive from a block of memory. + * + * This is mostly a huge help in building test harnesses; + * test programs can build archives in memory and read them + * back again without having to mess with files on disk. + */ + +struct read_memory_data { + unsigned char *buffer; + unsigned char *end; + ssize_t read_size; +}; + +static int memory_read_close(struct archive *, void *); +static int memory_read_open(struct archive *, void *); +#if ARCHIVE_API_VERSION < 2 +static ssize_t memory_read_skip(struct archive *, void *, size_t request); +#else +static off_t memory_read_skip(struct archive *, void *, off_t request); +#endif +static ssize_t memory_read(struct archive *, void *, const void **buff); + +int +archive_read_open_memory(struct archive *a, void *buff, size_t size) +{ + return archive_read_open_memory2(a, buff, size, size); +} + +/* + * Don't use _open_memory2() in production code; the archive_read_open_memory() + * version is the one you really want. This is just here so that + * test harnesses can exercise block operations inside the library. + */ +int +archive_read_open_memory2(struct archive *a, void *buff, + size_t size, size_t read_size) +{ + struct read_memory_data *mine; + + mine = (struct read_memory_data *)malloc(sizeof(*mine)); + if (mine == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); + } + memset(mine, 0, sizeof(*mine)); + mine->buffer = (unsigned char *)buff; + mine->end = mine->buffer + size; + mine->read_size = read_size; + return (archive_read_open2(a, mine, memory_read_open, + memory_read, memory_read_skip, memory_read_close)); +} + +/* + * There's nothing to open. + */ +static int +memory_read_open(struct archive *a, void *client_data) +{ + (void)a; /* UNUSED */ + (void)client_data; /* UNUSED */ + return (ARCHIVE_OK); +} + +/* + * This is scary simple: Just advance a pointer. Limiting + * to read_size is not technically necessary, but it exercises + * more of the internal logic when used with a small block size + * in a test harness. Production use should not specify a block + * size; then this is much faster. + */ +static ssize_t +memory_read(struct archive *a, void *client_data, const void **buff) +{ + struct read_memory_data *mine = (struct read_memory_data *)client_data; + ssize_t size; + + (void)a; /* UNUSED */ + *buff = mine->buffer; + size = mine->end - mine->buffer; + if (size > mine->read_size) + size = mine->read_size; + mine->buffer += size; + return (size); +} + +/* + * Advancing is just as simple. Again, this is doing more than + * necessary in order to better exercise internal code when used + * as a test harness. + */ +#if ARCHIVE_API_VERSION < 2 +static ssize_t +memory_read_skip(struct archive *a, void *client_data, size_t skip) +#else +static off_t +memory_read_skip(struct archive *a, void *client_data, off_t skip) +#endif +{ + struct read_memory_data *mine = (struct read_memory_data *)client_data; + + (void)a; /* UNUSED */ + if (skip > mine->end - mine->buffer) + skip = mine->end - mine->buffer; + /* Round down to block size. */ + skip /= mine->read_size; + skip *= mine->read_size; + mine->buffer += skip; + return (skip); +} + +/* + * Close is just cleaning up our one small bit of data. + */ +static int +memory_read_close(struct archive *a, void *client_data) +{ + struct read_memory_data *mine = (struct read_memory_data *)client_data; + (void)a; /* UNUSED */ + free(mine); + return (ARCHIVE_OK); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_read_private.h b/contrib/libarchive-2.1/libarchive/archive_read_private.h new file mode 100644 index 0000000000..d8cfa75386 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_read_private.h @@ -0,0 +1,176 @@ +/*- + * 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. + * + * $FreeBSD: src/lib/libarchive/archive_read_private.h,v 1.2 2007/04/02 00:11:54 kientzle Exp $ + */ + +#ifndef ARCHIVE_READ_PRIVATE_H_INCLUDED +#define ARCHIVE_READ_PRIVATE_H_INCLUDED + +#include "archive.h" +#include "archive_string.h" +#include "archive_private.h" + +struct archive_read { + struct archive archive; + + struct archive_entry *entry; + + /* Dev/ino of the archive being read/written. */ + dev_t skip_file_dev; + ino_t skip_file_ino; + + /* Utility: Pointer to a block of nulls. */ + const unsigned char *nulls; + size_t null_length; + + /* + * Used by archive_read_data() to track blocks and copy + * data to client buffers, filling gaps with zero bytes. + */ + const char *read_data_block; + off_t read_data_offset; + off_t read_data_output_offset; + size_t read_data_remaining; + + /* Callbacks to open/read/write/close archive stream. */ + archive_open_callback *client_opener; + archive_read_callback *client_reader; + archive_skip_callback *client_skipper; + archive_write_callback *client_writer; + archive_close_callback *client_closer; + void *client_data; + + /* + * Blocking information. Note that bytes_in_last_block is + * misleadingly named; I should find a better name. These + * control the final output from all compressors, including + * compression_none. + */ + int bytes_per_block; + int bytes_in_last_block; + + /* + * These control whether data within a gzip/bzip2 compressed + * stream gets padded or not. If pad_uncompressed is set, + * the data will be padded to a full block before being + * compressed. The pad_uncompressed_byte determines the value + * that will be used for padding. Note that these have no + * effect on compression "none." + */ + int pad_uncompressed; + int pad_uncompressed_byte; /* TODO: Support this. */ + + /* File offset of beginning of most recently-read header. */ + off_t header_position; + + /* + * Decompressors have a very specific lifecycle: + * public setup function initializes a slot in this table + * 'config' holds minimal configuration data + * bid() examines a block of data and returns a bid [1] + * init() is called for successful bidder + * 'data' is initialized by init() + * read() returns a pointer to the next block of data + * consume() indicates how much data is used + * skip() ignores bytes of data + * finish() cleans up and frees 'data' and 'config' + * + * [1] General guideline: bid the number of bits that you actually + * test, e.g., 16 if you test a 2-byte magic value. + */ + struct decompressor_t { + void *config; + void *data; + int (*bid)(const void *buff, size_t); + int (*init)(struct archive_read *, + const void *buff, size_t); + int (*finish)(struct archive_read *); + ssize_t (*read_ahead)(struct archive_read *, + const void **, size_t); + ssize_t (*consume)(struct archive_read *, size_t); + off_t (*skip)(struct archive_read *, off_t); + } decompressors[4]; + + /* Pointer to current decompressor. */ + struct decompressor_t *decompressor; + + /* + * Format detection is mostly the same as compression + * detection, with two significant differences: The bidders + * use the read_ahead calls above to examine the stream rather + * than having the supervisor hand them a block of data to + * examine, and the auction is repeated for every header. + * Winning bidders should set the archive_format and + * archive_format_name appropriately. Bid routines should + * check archive_format and decline to bid if the format of + * the last header was incompatible. + * + * Again, write support is considerably simpler because there's + * no need for an auction. + */ + + struct archive_format_descriptor { + void *data; + int (*bid)(struct archive_read *); + int (*read_header)(struct archive_read *, struct archive_entry *); + int (*read_data)(struct archive_read *, const void **, size_t *, off_t *); + int (*read_data_skip)(struct archive_read *); + int (*cleanup)(struct archive_read *); + } formats[8]; + struct archive_format_descriptor *format; /* Active format. */ + + /* + * Pointers to format-specific functions for writing. They're + * initialized by archive_write_set_format_XXX() calls. + */ + int (*format_init)(struct archive *); /* Only used on write. */ + int (*format_finish)(struct archive *); + int (*format_finish_entry)(struct archive *); + int (*format_write_header)(struct archive *, + struct archive_entry *); + ssize_t (*format_write_data)(struct archive *, + const void *buff, size_t); + + /* + * Various information needed by archive_extract. + */ + struct extract *extract; + int (*cleanup_archive_extract)(struct archive_read *); +}; + +int __archive_read_register_format(struct archive_read *a, + void *format_data, + int (*bid)(struct archive_read *), + int (*read_header)(struct archive_read *, struct archive_entry *), + int (*read_data)(struct archive_read *, const void **, size_t *, off_t *), + int (*read_data_skip)(struct archive_read *), + int (*cleanup)(struct archive_read *)); + +struct decompressor_t + *__archive_read_register_compression(struct archive_read *a, + int (*bid)(const void *, size_t), + int (*init)(struct archive_read *, const void *, size_t)); + +#endif diff --git a/contrib/libarchive-2.1/libarchive/archive_read_support_compression_all.c b/contrib/libarchive-2.1/libarchive/archive_read_support_compression_all.c new file mode 100644 index 0000000000..da2b246bed --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_read_support_compression_all.c @@ -0,0 +1,43 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_all.c,v 1.6 2007/01/09 08:05:55 kientzle Exp $"); + +#include "archive.h" + +int +archive_read_support_compression_all(struct archive *a) +{ +#if HAVE_BZLIB_H + archive_read_support_compression_bzip2(a); +#endif + /* The decompress code doesn't use an outside library. */ + archive_read_support_compression_compress(a); +#if HAVE_ZLIB_H + archive_read_support_compression_gzip(a); +#endif + return (ARCHIVE_OK); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_read_support_compression_bzip2.c b/contrib/libarchive-2.1/libarchive/archive_read_support_compression_bzip2.c new file mode 100644 index 0000000000..ca0ac2574d --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_read_support_compression_bzip2.c @@ -0,0 +1,408 @@ +/*- + * 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 "archive_platform.h" + +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_bzip2.c,v 1.15 2007/04/05 05:18:16 kientzle Exp $"); + +#ifdef HAVE_ERRNO_H +#include +#endif +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_BZLIB_H +#include +#endif + +#include "archive.h" +#include "archive_private.h" +#include "archive_read_private.h" + +#if HAVE_BZLIB_H +struct private_data { + bz_stream stream; + char *uncompressed_buffer; + size_t uncompressed_buffer_size; + char *read_next; + int64_t total_out; +}; + +static int finish(struct archive_read *); +static ssize_t read_ahead(struct archive_read *, const void **, size_t); +static ssize_t read_consume(struct archive_read *, size_t); +static int drive_decompressor(struct archive_read *a, struct private_data *); +#endif + +/* These two functions are defined even if we lack bzlib. See below. */ +static int bid(const void *, size_t); +static int init(struct archive_read *, const void *, size_t); + +int +archive_read_support_compression_bzip2(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + if (__archive_read_register_compression(a, bid, init) != NULL) + return (ARCHIVE_OK); + return (ARCHIVE_FATAL); +} + +/* + * Test whether we can handle this data. + * + * This logic returns zero if any part of the signature fails. It + * also tries to Do The Right Thing if a very short buffer prevents us + * from verifying as much as we would like. + */ +static int +bid(const void *buff, size_t len) +{ + const unsigned char *buffer; + int bits_checked; + + if (len < 1) + return (0); + + buffer = (const unsigned char *)buff; + bits_checked = 0; + if (buffer[0] != 'B') /* Verify first ID byte. */ + return (0); + bits_checked += 8; + if (len < 2) + return (bits_checked); + + if (buffer[1] != 'Z') /* Verify second ID byte. */ + return (0); + bits_checked += 8; + if (len < 3) + return (bits_checked); + + if (buffer[2] != 'h') /* Verify third ID byte. */ + return (0); + bits_checked += 8; + if (len < 4) + return (bits_checked); + + if (buffer[3] < '1' || buffer[3] > '9') + return (0); + bits_checked += 5; + + /* + * Research Question: Can we do any more to verify that this + * really is BZip2 format?? For 99.9% of the time, the above + * test is sufficient, but it would be nice to do a more + * thorough check. It's especially troubling that the BZip2 + * signature begins with all ASCII characters; a tar archive + * whose first filename begins with 'BZh3' would potentially + * fool this logic. (It may also be possible to guard against + * such anomalies in archive_read_support_compression_none.) + */ + + return (bits_checked); +} + +#ifndef HAVE_BZLIB_H + +/* + * If we don't have bzlib on this system, we can't actually do the + * decompression. We can, however, still detect bzip2-compressed + * archives and emit a useful message. + */ +static int +init(struct archive_read *a, const void *buff, size_t n) +{ + (void)a; /* UNUSED */ + (void)buff; /* UNUSED */ + (void)n; /* UNUSED */ + + archive_set_error(a, -1, + "This version of libarchive was compiled without bzip2 support"); + return (ARCHIVE_FATAL); +} + + +#else + +/* + * Setup the callbacks. + */ +static int +init(struct archive_read *a, const void *buff, size_t n) +{ + struct private_data *state; + int ret; + + a->archive.compression_code = ARCHIVE_COMPRESSION_BZIP2; + a->archive.compression_name = "bzip2"; + + state = (struct private_data *)malloc(sizeof(*state)); + if (state == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate data for %s decompression", + a->archive.compression_name); + return (ARCHIVE_FATAL); + } + memset(state, 0, sizeof(*state)); + + state->uncompressed_buffer_size = 64 * 1024; + state->uncompressed_buffer = (char *)malloc(state->uncompressed_buffer_size); + state->stream.next_out = state->uncompressed_buffer; + state->read_next = state->uncompressed_buffer; + state->stream.avail_out = state->uncompressed_buffer_size; + + if (state->uncompressed_buffer == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate %s decompression buffers", + a->archive.compression_name); + free(state); + return (ARCHIVE_FATAL); + } + + /* + * A bug in bzlib.h: stream.next_in should be marked 'const' + * but isn't (the library never alters data through the + * next_in pointer, only reads it). The result: this ugly + * cast to remove 'const'. + */ + state->stream.next_in = (char *)(uintptr_t)(const void *)buff; + state->stream.avail_in = n; + + a->decompressor->read_ahead = read_ahead; + a->decompressor->consume = read_consume; + a->decompressor->skip = NULL; /* not supported */ + a->decompressor->finish = finish; + + /* Initialize compression library. */ + ret = BZ2_bzDecompressInit(&(state->stream), + 0 /* library verbosity */, + 0 /* don't use slow low-mem algorithm */); + + /* If init fails, try using low-memory algorithm instead. */ + if (ret == BZ_MEM_ERROR) { + ret = BZ2_bzDecompressInit(&(state->stream), + 0 /* library verbosity */, + 1 /* do use slow low-mem algorithm */); + } + + if (ret == BZ_OK) { + a->decompressor->data = state; + return (ARCHIVE_OK); + } + + /* Library setup failed: Clean up. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing %s library", + a->archive.compression_name); + free(state->uncompressed_buffer); + free(state); + + /* Override the error message if we know what really went wrong. */ + switch (ret) { + case BZ_PARAM_ERROR: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing compression library: " + "invalid setup parameter"); + break; + case BZ_MEM_ERROR: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing compression library: " + "out of memory"); + break; + case BZ_CONFIG_ERROR: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing compression library: " + "mis-compiled library"); + break; + } + + return (ARCHIVE_FATAL); +} + +/* + * Return a block of data from the decompression buffer. Decompress more + * as necessary. + */ +static ssize_t +read_ahead(struct archive_read *a, const void **p, size_t min) +{ + struct private_data *state; + size_t read_avail, was_avail; + int ret; + + state = (struct private_data *)a->decompressor->data; + if (!a->client_reader) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "No read callback is registered? " + "This is probably an internal programming error."); + return (ARCHIVE_FATAL); + } + + read_avail = state->stream.next_out - state->read_next; + + if (read_avail + state->stream.avail_out < min) { + memmove(state->uncompressed_buffer, state->read_next, + read_avail); + state->read_next = state->uncompressed_buffer; + state->stream.next_out = state->read_next + read_avail; + state->stream.avail_out + = state->uncompressed_buffer_size - read_avail; + } + + while (read_avail < min && /* Haven't satisfied min. */ + read_avail < state->uncompressed_buffer_size) { /* !full */ + was_avail = read_avail; + if ((ret = drive_decompressor(a, state)) != ARCHIVE_OK) + return (ret); + read_avail = state->stream.next_out - state->read_next; + if (was_avail == read_avail) /* No progress? */ + break; + } + + *p = state->read_next; + return (read_avail); +} + +/* + * Mark a previously-returned block of data as read. + */ +static ssize_t +read_consume(struct archive_read *a, size_t n) +{ + struct private_data *state; + + state = (struct private_data *)a->decompressor->data; + a->archive.file_position += n; + state->read_next += n; + if (state->read_next > state->stream.next_out) + __archive_errx(1, "Request to consume too many " + "bytes from bzip2 decompressor"); + return (n); +} + +/* + * Clean up the decompressor. + */ +static int +finish(struct archive_read *a) +{ + struct private_data *state; + int ret; + + state = (struct private_data *)a->decompressor->data; + ret = ARCHIVE_OK; + switch (BZ2_bzDecompressEnd(&(state->stream))) { + case BZ_OK: + break; + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Failed to clean up %s compressor", + a->archive.compression_name); + ret = ARCHIVE_FATAL; + } + + free(state->uncompressed_buffer); + free(state); + + a->decompressor->data = NULL; + return (ret); +} + +/* + * Utility function to pull data through decompressor, reading input + * blocks as necessary. + */ +static int +drive_decompressor(struct archive_read *a, struct private_data *state) +{ + ssize_t ret; + int decompressed, total_decompressed; + char *output; + const void *read_buf; + + total_decompressed = 0; + for (;;) { + if (state->stream.avail_in == 0) { + read_buf = state->stream.next_in; + ret = (a->client_reader)(&a->archive, a->client_data, + &read_buf); + state->stream.next_in = (void *)(uintptr_t)read_buf; + if (ret < 0) { + /* + * TODO: Find a better way to handle + * this read failure. + */ + goto fatal; + } + if (ret == 0 && total_decompressed == 0) { + archive_set_error(&a->archive, EIO, + "Premature end of %s compressed data", + a->archive.compression_name); + return (ARCHIVE_FATAL); + } + a->archive.raw_position += ret; + state->stream.avail_in = ret; + } + + { + output = state->stream.next_out; + + /* Decompress some data. */ + ret = BZ2_bzDecompress(&(state->stream)); + decompressed = state->stream.next_out - output; + + /* Accumulate the total bytes of output. */ + state->total_out += decompressed; + total_decompressed += decompressed; + + switch (ret) { + case BZ_OK: /* Decompressor made some progress. */ + if (decompressed > 0) + return (ARCHIVE_OK); + break; + case BZ_STREAM_END: /* Found end of stream. */ + return (ARCHIVE_OK); + default: + /* Any other return value is an error. */ + goto fatal; + } + } + } + return (ARCHIVE_OK); + + /* Return a fatal error. */ +fatal: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "%s decompression failed", a->archive.compression_name); + return (ARCHIVE_FATAL); +} + +#endif /* HAVE_BZLIB_H */ diff --git a/contrib/libarchive-2.1/libarchive/archive_read_support_compression_compress.c b/contrib/libarchive-2.1/libarchive/archive_read_support_compression_compress.c new file mode 100644 index 0000000000..cb434105b4 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_read_support_compression_compress.c @@ -0,0 +1,493 @@ +/*- + * 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. + */ + +/* + * This code borrows heavily from "compress" source code, which is + * protected by the following copyright. (Clause 3 dropped by request + * of the Regents.) + */ + +/*- + * Copyright (c) 1985, 1986, 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis and James A. Woods, derived from original + * work by Spencer Thomas and Joseph Orost. + * + * 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. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_compress.c,v 1.9 2007/04/05 05:18:16 kientzle Exp $"); + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "archive.h" +#include "archive_private.h" +#include "archive_read_private.h" + +/* + * Because LZW decompression is pretty simple, I've just implemented + * the whole decompressor here (cribbing from "compress" source code, + * of course), rather than relying on an external library. I have + * made an effort to clarify and simplify the algorithm, so the + * names and structure here don't exactly match those used by compress. + */ + +struct private_data { + /* Input variables. */ + const unsigned char *next_in; + size_t avail_in; + int bit_buffer; + int bits_avail; + size_t bytes_in_section; + + /* Output variables. */ + size_t uncompressed_buffer_size; + void *uncompressed_buffer; + unsigned char *read_next; /* Data for client. */ + unsigned char *next_out; /* Where to write new data. */ + size_t avail_out; /* Space at end of buffer. */ + + /* Decompression status variables. */ + int use_reset_code; + int end_of_stream; /* EOF status. */ + int maxcode; /* Largest code. */ + int maxcode_bits; /* Length of largest code. */ + int section_end_code; /* When to increase bits. */ + int bits; /* Current code length. */ + int oldcode; /* Previous code. */ + int finbyte; /* Last byte of prev code. */ + + /* Dictionary. */ + int free_ent; /* Next dictionary entry. */ + unsigned char suffix[65536]; + uint16_t prefix[65536]; + + /* + * Scratch area for expanding dictionary entries. Note: + * "worst" case here comes from compressing /dev/zero: the + * last code in the dictionary will code a sequence of + * 65536-256 zero bytes. Thus, we need stack space to expand + * a 65280-byte dictionary entry. (Of course, 32640:1 + * compression could also be considered the "best" case. ;-) + */ + unsigned char *stackp; + unsigned char stack[65300]; +}; + +static int bid(const void *, size_t); +static int finish(struct archive_read *); +static int init(struct archive_read *, const void *, size_t); +static ssize_t read_ahead(struct archive_read *, const void **, size_t); +static ssize_t read_consume(struct archive_read *, size_t); +static int getbits(struct archive_read *, struct private_data *, int n); +static int next_code(struct archive_read *a, struct private_data *state); + +int +archive_read_support_compression_compress(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + if (__archive_read_register_compression(a, bid, init) != NULL) + return (ARCHIVE_OK); + return (ARCHIVE_FATAL); +} + +/* + * Test whether we can handle this data. + * + * This logic returns zero if any part of the signature fails. It + * also tries to Do The Right Thing if a very short buffer prevents us + * from verifying as much as we would like. + */ +static int +bid(const void *buff, size_t len) +{ + const unsigned char *buffer; + int bits_checked; + + if (len < 1) + return (0); + + buffer = (const unsigned char *)buff; + bits_checked = 0; + if (buffer[0] != 037) /* Verify first ID byte. */ + return (0); + bits_checked += 8; + if (len < 2) + return (bits_checked); + + if (buffer[1] != 0235) /* Verify second ID byte. */ + return (0); + bits_checked += 8; + if (len < 3) + return (bits_checked); + + /* + * TODO: Verify more. + */ + + return (bits_checked); +} + +/* + * Setup the callbacks. + */ +static int +init(struct archive_read *a, const void *buff, size_t n) +{ + struct private_data *state; + int code; + + a->archive.compression_code = ARCHIVE_COMPRESSION_COMPRESS; + a->archive.compression_name = "compress (.Z)"; + + a->decompressor->read_ahead = read_ahead; + a->decompressor->consume = read_consume; + a->decompressor->skip = NULL; /* not supported */ + a->decompressor->finish = finish; + + state = (struct private_data *)malloc(sizeof(*state)); + if (state == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate data for %s decompression", + a->archive.compression_name); + return (ARCHIVE_FATAL); + } + memset(state, 0, sizeof(*state)); + a->decompressor->data = state; + + state->uncompressed_buffer_size = 64 * 1024; + state->uncompressed_buffer = malloc(state->uncompressed_buffer_size); + + if (state->uncompressed_buffer == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate %s decompression buffers", + a->archive.compression_name); + goto fatal; + } + + state->next_in = (const unsigned char *)buff; + state->avail_in = n; + state->read_next = state->next_out = (unsigned char *)state->uncompressed_buffer; + state->avail_out = state->uncompressed_buffer_size; + + code = getbits(a, state, 8); + if (code != 037) /* This should be impossible. */ + goto fatal; + + code = getbits(a, state, 8); + if (code != 0235) { + /* This can happen if the library is receiving 1-byte + * blocks and gzip and compress are both enabled. + * You can't distinguish gzip and compress only from + * the first byte. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Compress signature did not match."); + goto fatal; + } + + code = getbits(a, state, 8); + state->maxcode_bits = code & 0x1f; + state->maxcode = (1 << state->maxcode_bits); + state->use_reset_code = code & 0x80; + + /* Initialize decompressor. */ + state->free_ent = 256; + state->stackp = state->stack; + if (state->use_reset_code) + state->free_ent++; + state->bits = 9; + state->section_end_code = (1<bits) - 1; + state->oldcode = -1; + for (code = 255; code >= 0; code--) { + state->prefix[code] = 0; + state->suffix[code] = code; + } + next_code(a, state); + return (ARCHIVE_OK); + +fatal: + finish(a); + return (ARCHIVE_FATAL); +} + +/* + * Return a block of data from the decompression buffer. Decompress more + * as necessary. + */ +static ssize_t +read_ahead(struct archive_read *a, const void **p, size_t min) +{ + struct private_data *state; + size_t read_avail; + int ret; + + state = (struct private_data *)a->decompressor->data; + if (!a->client_reader) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "No read callback is registered? " + "This is probably an internal programming error."); + return (ARCHIVE_FATAL); + } + + read_avail = state->next_out - state->read_next; + + if (read_avail < min && state->end_of_stream) { + if (state->end_of_stream == ARCHIVE_EOF) + return (0); + else + return (-1); + } + + if (read_avail < min) { + memmove(state->uncompressed_buffer, state->read_next, + read_avail); + state->read_next = (unsigned char *)state->uncompressed_buffer; + state->next_out = state->read_next + read_avail; + state->avail_out + = state->uncompressed_buffer_size - read_avail; + + while (read_avail < state->uncompressed_buffer_size + && !state->end_of_stream) { + if (state->stackp > state->stack) { + *state->next_out++ = *--state->stackp; + state->avail_out--; + read_avail++; + } else { + ret = next_code(a, state); + if (ret == ARCHIVE_EOF) + state->end_of_stream = ret; + else if (ret != ARCHIVE_OK) + return (ret); + } + } + } + + *p = state->read_next; + return (read_avail); +} + +/* + * Mark a previously-returned block of data as read. + */ +static ssize_t +read_consume(struct archive_read *a, size_t n) +{ + struct private_data *state; + + state = (struct private_data *)a->decompressor->data; + a->archive.file_position += n; + state->read_next += n; + if (state->read_next > state->next_out) + __archive_errx(1, "Request to consume too many " + "bytes from compress decompressor"); + return (n); +} + +/* + * Clean up the decompressor. + */ +static int +finish(struct archive_read *a) +{ + struct private_data *state; + int ret = ARCHIVE_OK; + + state = (struct private_data *)a->decompressor->data; + + if (state != NULL) { + if (state->uncompressed_buffer != NULL) + free(state->uncompressed_buffer); + free(state); + } + + a->decompressor->data = NULL; + return (ret); +} + +/* + * Process the next code and fill the stack with the expansion + * of the code. Returns ARCHIVE_FATAL if there is a fatal I/O or + * format error, ARCHIVE_EOF if we hit end of data, ARCHIVE_OK otherwise. + */ +static int +next_code(struct archive_read *a, struct private_data *state) +{ + int code, newcode; + + static int debug_buff[1024]; + static unsigned debug_index; + + code = newcode = getbits(a, state, state->bits); + if (code < 0) + return (code); + + debug_buff[debug_index++] = code; + if (debug_index >= sizeof(debug_buff)/sizeof(debug_buff[0])) + debug_index = 0; + + /* If it's a reset code, reset the dictionary. */ + if ((code == 256) && state->use_reset_code) { + /* + * The original 'compress' implementation blocked its + * I/O in a manner that resulted in junk bytes being + * inserted after every reset. The next section skips + * this junk. (Yes, the number of *bytes* to skip is + * a function of the current *bit* length.) + */ + int skip_bytes = state->bits - + (state->bytes_in_section % state->bits); + skip_bytes %= state->bits; + state->bits_avail = 0; /* Discard rest of this byte. */ + while (skip_bytes-- > 0) { + code = getbits(a, state, 8); + if (code < 0) + return (code); + } + /* Now, actually do the reset. */ + state->bytes_in_section = 0; + state->bits = 9; + state->section_end_code = (1 << state->bits) - 1; + state->free_ent = 257; + state->oldcode = -1; + return (next_code(a, state)); + } + + if (code > state->free_ent) { + /* An invalid code is a fatal error. */ + archive_set_error(&a->archive, -1, "Invalid compressed data"); + return (ARCHIVE_FATAL); + } + + /* Special case for KwKwK string. */ + if (code >= state->free_ent) { + *state->stackp++ = state->finbyte; + code = state->oldcode; + } + + /* Generate output characters in reverse order. */ + while (code >= 256) { + *state->stackp++ = state->suffix[code]; + code = state->prefix[code]; + } + *state->stackp++ = state->finbyte = code; + + /* Generate the new entry. */ + code = state->free_ent; + if (code < state->maxcode && state->oldcode >= 0) { + state->prefix[code] = state->oldcode; + state->suffix[code] = state->finbyte; + ++state->free_ent; + } + if (state->free_ent > state->section_end_code) { + state->bits++; + state->bytes_in_section = 0; + if (state->bits == state->maxcode_bits) + state->section_end_code = state->maxcode; + else + state->section_end_code = (1 << state->bits) - 1; + } + + /* Remember previous code. */ + state->oldcode = newcode; + return (ARCHIVE_OK); +} + +/* + * Return next 'n' bits from stream. + * + * -1 indicates end of available data. + */ +static int +getbits(struct archive_read *a, struct private_data *state, int n) +{ + int code, ret; + static const int mask[] = { + 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff, + 0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff, 0x7fff, 0xffff + }; + const void *read_buf; + + while (state->bits_avail < n) { + if (state->avail_in <= 0) { + read_buf = state->next_in; + ret = (a->client_reader)(&a->archive, a->client_data, + &read_buf); + state->next_in = read_buf; + if (ret < 0) + return (ARCHIVE_FATAL); + if (ret == 0) + return (ARCHIVE_EOF); + a->archive.raw_position += ret; + state->avail_in = ret; + } + state->bit_buffer |= *state->next_in++ << state->bits_avail; + state->avail_in--; + state->bits_avail += 8; + state->bytes_in_section++; + } + + code = state->bit_buffer; + state->bit_buffer >>= n; + state->bits_avail -= n; + + return (code & mask[n]); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_read_support_compression_gzip.c b/contrib/libarchive-2.1/libarchive/archive_read_support_compression_gzip.c new file mode 100644 index 0000000000..8b6cb97781 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_read_support_compression_gzip.c @@ -0,0 +1,546 @@ +/*- + * 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 "archive_platform.h" + +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_gzip.c,v 1.14 2007/04/05 05:18:16 kientzle Exp $"); + + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_ZLIB_H +#include +#endif + +#include "archive.h" +#include "archive_private.h" +#include "archive_read_private.h" + +#ifdef HAVE_ZLIB_H +struct private_data { + z_stream stream; + unsigned char *uncompressed_buffer; + size_t uncompressed_buffer_size; + unsigned char *read_next; + int64_t total_out; + unsigned long crc; + char header_done; +}; + +static int finish(struct archive_read *); +static ssize_t read_ahead(struct archive_read *, const void **, size_t); +static ssize_t read_consume(struct archive_read *, size_t); +static int drive_decompressor(struct archive_read *a, struct private_data *); +#endif + +/* These two functions are defined even if we lack zlib. See below. */ +static int bid(const void *, size_t); +static int init(struct archive_read *, const void *, size_t); + +int +archive_read_support_compression_gzip(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + if(__archive_read_register_compression(a, bid, init) != NULL) + return (ARCHIVE_OK); + return (ARCHIVE_FATAL); +} + +/* + * Test whether we can handle this data. + * + * This logic returns zero if any part of the signature fails. It + * also tries to Do The Right Thing if a very short buffer prevents us + * from verifying as much as we would like. + */ +static int +bid(const void *buff, size_t len) +{ + const unsigned char *buffer; + int bits_checked; + + if (len < 1) + return (0); + + buffer = (const unsigned char *)buff; + bits_checked = 0; + if (buffer[0] != 037) /* Verify first ID byte. */ + return (0); + bits_checked += 8; + if (len < 2) + return (bits_checked); + + if (buffer[1] != 0213) /* Verify second ID byte. */ + return (0); + bits_checked += 8; + if (len < 3) + return (bits_checked); + + if (buffer[2] != 8) /* Compression must be 'deflate'. */ + return (0); + bits_checked += 8; + if (len < 4) + return (bits_checked); + + if ((buffer[3] & 0xE0)!= 0) /* No reserved flags set. */ + return (0); + bits_checked += 3; + if (len < 5) + return (bits_checked); + + /* + * TODO: Verify more; in particular, gzip has an optional + * header CRC, which would give us 16 more verified bits. We + * may also be able to verify certain constraints on other + * fields. + */ + + return (bits_checked); +} + + +#ifndef HAVE_ZLIB_H + +/* + * If we don't have zlib on this system, we can't actually do the + * decompression. We can, however, still detect gzip-compressed + * archives and emit a useful message. + */ +static int +init(struct archive_read *a, const void *buff, size_t n) +{ + (void)a; /* UNUSED */ + (void)buff; /* UNUSED */ + (void)n; /* UNUSED */ + + archive_set_error(a, -1, + "This version of libarchive was compiled without gzip support"); + return (ARCHIVE_FATAL); +} + + +#else + +/* + * Setup the callbacks. + */ +static int +init(struct archive_read *a, const void *buff, size_t n) +{ + struct private_data *state; + int ret; + + a->archive.compression_code = ARCHIVE_COMPRESSION_GZIP; + a->archive.compression_name = "gzip"; + + state = (struct private_data *)malloc(sizeof(*state)); + if (state == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate data for %s decompression", + a->archive.compression_name); + return (ARCHIVE_FATAL); + } + memset(state, 0, sizeof(*state)); + + state->crc = crc32(0L, NULL, 0); + state->header_done = 0; /* We've not yet begun to parse header... */ + + state->uncompressed_buffer_size = 64 * 1024; + state->uncompressed_buffer = (unsigned char *)malloc(state->uncompressed_buffer_size); + state->stream.next_out = state->uncompressed_buffer; + state->read_next = state->uncompressed_buffer; + state->stream.avail_out = state->uncompressed_buffer_size; + + if (state->uncompressed_buffer == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate %s decompression buffers", + a->archive.compression_name); + free(state); + return (ARCHIVE_FATAL); + } + + /* + * A bug in zlib.h: stream.next_in should be marked 'const' + * but isn't (the library never alters data through the + * next_in pointer, only reads it). The result: this ugly + * cast to remove 'const'. + */ + state->stream.next_in = (Bytef *)(uintptr_t)(const void *)buff; + state->stream.avail_in = n; + + a->decompressor->read_ahead = read_ahead; + a->decompressor->consume = read_consume; + a->decompressor->skip = NULL; /* not supported */ + a->decompressor->finish = finish; + + /* + * TODO: Do I need to parse the gzip header before calling + * inflateInit2()? In particular, one of the header bytes + * marks "best compression" or "fastest", which may be + * appropriate for setting the second parameter here. + * However, I think the only penalty for not setting it + * correctly is wasted memory. If this is necessary, it + * should probably go into drive_decompressor() below. + */ + + /* Initialize compression library. */ + ret = inflateInit2(&(state->stream), + -15 /* Don't check for zlib header */); + if (ret == Z_OK) { + a->decompressor->data = state; + return (ARCHIVE_OK); + } + + /* Library setup failed: Clean up. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing %s library", + a->archive.compression_name); + free(state->uncompressed_buffer); + free(state); + + /* Override the error message if we know what really went wrong. */ + switch (ret) { + case Z_STREAM_ERROR: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing compression library: " + "invalid setup parameter"); + break; + case Z_MEM_ERROR: + archive_set_error(&a->archive, ENOMEM, + "Internal error initializing compression library: " + "out of memory"); + break; + case Z_VERSION_ERROR: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing compression library: " + "invalid library version"); + break; + } + + return (ARCHIVE_FATAL); +} + +/* + * Return a block of data from the decompression buffer. Decompress more + * as necessary. + */ +static ssize_t +read_ahead(struct archive_read *a, const void **p, size_t min) +{ + struct private_data *state; + size_t read_avail, was_avail; + int ret; + + state = (struct private_data *)a->decompressor->data; + if (!a->client_reader) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "No read callback is registered? " + "This is probably an internal programming error."); + return (ARCHIVE_FATAL); + } + + read_avail = state->stream.next_out - state->read_next; + + if (read_avail + state->stream.avail_out < min) { + memmove(state->uncompressed_buffer, state->read_next, + read_avail); + state->read_next = state->uncompressed_buffer; + state->stream.next_out = state->read_next + read_avail; + state->stream.avail_out + = state->uncompressed_buffer_size - read_avail; + } + + while (read_avail < min && /* Haven't satisfied min. */ + read_avail < state->uncompressed_buffer_size) { /* !full */ + was_avail = read_avail; + if ((ret = drive_decompressor(a, state)) != ARCHIVE_OK) + return (ret); + read_avail = state->stream.next_out - state->read_next; + if (was_avail == read_avail) /* No progress? */ + break; + } + + *p = state->read_next; + return (read_avail); +} + +/* + * Mark a previously-returned block of data as read. + */ +static ssize_t +read_consume(struct archive_read *a, size_t n) +{ + struct private_data *state; + + state = (struct private_data *)a->decompressor->data; + a->archive.file_position += n; + state->read_next += n; + if (state->read_next > state->stream.next_out) + __archive_errx(1, "Request to consume too many " + "bytes from gzip decompressor"); + return (n); +} + +/* + * Clean up the decompressor. + */ +static int +finish(struct archive_read *a) +{ + struct private_data *state; + int ret; + + state = (struct private_data *)a->decompressor->data; + ret = ARCHIVE_OK; + switch (inflateEnd(&(state->stream))) { + case Z_OK: + break; + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Failed to clean up %s compressor", + a->archive.compression_name); + ret = ARCHIVE_FATAL; + } + + free(state->uncompressed_buffer); + free(state); + + a->decompressor->data = NULL; + return (ret); +} + +/* + * Utility function to pull data through decompressor, reading input + * blocks as necessary. + */ +static int +drive_decompressor(struct archive_read *a, struct private_data *state) +{ + ssize_t ret; + size_t decompressed, total_decompressed; + int count, flags, header_state; + unsigned char *output; + unsigned char b; + const void *read_buf; + + flags = 0; + count = 0; + header_state = 0; + total_decompressed = 0; + for (;;) { + if (state->stream.avail_in == 0) { + read_buf = state->stream.next_in; + ret = (a->client_reader)(&a->archive, a->client_data, + &read_buf); + state->stream.next_in = (unsigned char *)(uintptr_t)read_buf; + if (ret < 0) { + /* + * TODO: Find a better way to handle + * this read failure. + */ + goto fatal; + } + if (ret == 0 && total_decompressed == 0) { + archive_set_error(&a->archive, EIO, + "Premature end of %s compressed data", + a->archive.compression_name); + return (ARCHIVE_FATAL); + } + a->archive.raw_position += ret; + state->stream.avail_in = ret; + } + + if (!state->header_done) { + /* + * If still parsing the header, interpret the + * next byte. + */ + b = *(state->stream.next_in++); + state->stream.avail_in--; + + /* + * Yes, this is somewhat crude, but it works, + * GZip format isn't likely to change anytime + * in the near future, and header parsing is + * certainly not a performance issue, so + * there's little point in making this more + * elegant. Of course, if you see an easy way + * to make this more elegant, please let me + * know.. ;-) + */ + switch (header_state) { + case 0: /* First byte of signature. */ + if (b != 037) + goto fatal; + header_state = 1; + break; + case 1: /* Second byte of signature. */ + if (b != 0213) + goto fatal; + header_state = 2; + break; + case 2: /* Compression type must be 8. */ + if (b != 8) + goto fatal; + header_state = 3; + break; + case 3: /* GZip flags. */ + flags = b; + header_state = 4; + break; + case 4: case 5: case 6: case 7: /* Mod time. */ + header_state++; + break; + case 8: /* Deflate flags. */ + header_state = 9; + break; + case 9: /* OS. */ + header_state = 10; + break; + case 10: /* Optional Extra: First byte of Length. */ + if ((flags & 4)) { + count = 255 & (int)b; + header_state = 11; + break; + } + /* + * Fall through if there is no + * Optional Extra field. + */ + case 11: /* Optional Extra: Second byte of Length. */ + if ((flags & 4)) { + count = (0xff00 & ((int)b << 8)) | count; + header_state = 12; + break; + } + /* + * Fall through if there is no + * Optional Extra field. + */ + case 12: /* Optional Extra Field: counted length. */ + if ((flags & 4)) { + --count; + if (count == 0) header_state = 13; + else header_state = 12; + break; + } + /* + * Fall through if there is no + * Optional Extra field. + */ + case 13: /* Optional Original Filename. */ + if ((flags & 8)) { + if (b == 0) header_state = 14; + else header_state = 13; + break; + } + /* + * Fall through if no Optional + * Original Filename. + */ + case 14: /* Optional Comment. */ + if ((flags & 16)) { + if (b == 0) header_state = 15; + else header_state = 14; + break; + } + /* Fall through if no Optional Comment. */ + case 15: /* Optional Header CRC: First byte. */ + if ((flags & 2)) { + header_state = 16; + break; + } + /* Fall through if no Optional Header CRC. */ + case 16: /* Optional Header CRC: Second byte. */ + if ((flags & 2)) { + header_state = 17; + break; + } + /* Fall through if no Optional Header CRC. */ + case 17: /* First byte of compressed data. */ + state->header_done = 1; /* done with header */ + state->stream.avail_in++; + state->stream.next_in--; + } + + /* + * TODO: Consider moving the inflateInit2 call + * here so it can include the compression type + * from the header? + */ + } else { + output = state->stream.next_out; + + /* Decompress some data. */ + ret = inflate(&(state->stream), 0); + decompressed = state->stream.next_out - output; + + /* Accumulate the CRC of the uncompressed data. */ + state->crc = crc32(state->crc, output, decompressed); + + /* Accumulate the total bytes of output. */ + state->total_out += decompressed; + total_decompressed += decompressed; + + switch (ret) { + case Z_OK: /* Decompressor made some progress. */ + if (decompressed > 0) + return (ARCHIVE_OK); + break; + case Z_STREAM_END: /* Found end of stream. */ + /* + * TODO: Verify gzip trailer + * (uncompressed length and CRC). + */ + return (ARCHIVE_OK); + default: + /* Any other return value is an error. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "gzip decompression failed (%s)", + state->stream.msg); + goto fatal; + } + } + } + return (ARCHIVE_OK); + + /* Return a fatal error. */ +fatal: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "%s decompression failed", a->archive.compression_name); + return (ARCHIVE_FATAL); +} + +#endif /* HAVE_ZLIB_H */ diff --git a/contrib/libarchive-2.1/libarchive/archive_read_support_compression_none.c b/contrib/libarchive-2.1/libarchive/archive_read_support_compression_none.c new file mode 100644 index 0000000000..e976398bb8 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_read_support_compression_none.c @@ -0,0 +1,365 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_none.c,v 1.16 2007/04/05 05:18:16 kientzle Exp $"); + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_LIMITS_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "archive.h" +#include "archive_private.h" +#include "archive_read_private.h" + +struct archive_decompress_none { + char *buffer; + size_t buffer_size; + char *next; /* Current read location. */ + size_t avail; /* Bytes in my buffer. */ + const void *client_buff; /* Client buffer information. */ + size_t client_total; + const char *client_next; + size_t client_avail; + char end_of_file; + char fatal; +}; + +/* + * Size of internal buffer used for combining short reads. This is + * also an upper limit on the size of a read request. Recall, + * however, that we can (and will!) return blocks of data larger than + * this. The read semantics are: you ask for a minimum, I give you a + * pointer to my best-effort match and tell you how much data is + * there. It could be less than you asked for, it could be much more. + * For example, a client might use mmap() to "read" the entire file as + * a single block. In that case, I will return that entire block to + * my clients. + */ +#define BUFFER_SIZE 65536 + +#define minimum(a, b) (a < b ? a : b) + +static int archive_decompressor_none_bid(const void *, size_t); +static int archive_decompressor_none_finish(struct archive_read *); +static int archive_decompressor_none_init(struct archive_read *, + const void *, size_t); +static ssize_t archive_decompressor_none_read_ahead(struct archive_read *, + const void **, size_t); +static ssize_t archive_decompressor_none_read_consume(struct archive_read *, + size_t); +static off_t archive_decompressor_none_skip(struct archive_read *, off_t); + +int +archive_read_support_compression_none(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + if (__archive_read_register_compression(a, + archive_decompressor_none_bid, + archive_decompressor_none_init) != NULL) + return (ARCHIVE_OK); + return (ARCHIVE_FATAL); +} + +/* + * Try to detect an "uncompressed" archive. + */ +static int +archive_decompressor_none_bid(const void *buff, size_t len) +{ + (void)buff; + (void)len; + + return (1); /* Default: We'll take it if noone else does. */ +} + +static int +archive_decompressor_none_init(struct archive_read *a, const void *buff, size_t n) +{ + struct archive_decompress_none *state; + + a->archive.compression_code = ARCHIVE_COMPRESSION_NONE; + a->archive.compression_name = "none"; + + state = (struct archive_decompress_none *)malloc(sizeof(*state)); + if (!state) { + archive_set_error(&a->archive, ENOMEM, "Can't allocate input data"); + return (ARCHIVE_FATAL); + } + memset(state, 0, sizeof(*state)); + + state->buffer_size = BUFFER_SIZE; + state->buffer = (char *)malloc(state->buffer_size); + state->next = state->buffer; + if (state->buffer == NULL) { + free(state); + archive_set_error(&a->archive, ENOMEM, "Can't allocate input buffer"); + return (ARCHIVE_FATAL); + } + + /* Save reference to first block of data. */ + state->client_buff = buff; + state->client_total = n; + state->client_next = state->client_buff; + state->client_avail = state->client_total; + + a->decompressor->data = state; + a->decompressor->read_ahead = archive_decompressor_none_read_ahead; + a->decompressor->consume = archive_decompressor_none_read_consume; + a->decompressor->skip = archive_decompressor_none_skip; + a->decompressor->finish = archive_decompressor_none_finish; + + return (ARCHIVE_OK); +} + +/* + * We just pass through pointers to the client buffer if we can. + * If the client buffer is short, then we copy stuff to our internal + * buffer to combine reads. + */ +static ssize_t +archive_decompressor_none_read_ahead(struct archive_read *a, const void **buff, + size_t min) +{ + struct archive_decompress_none *state; + ssize_t bytes_read; + + state = (struct archive_decompress_none *)a->decompressor->data; + if (state->fatal) + return (-1); + + /* + * Don't make special efforts to handle requests larger than + * the copy buffer. + */ + if (min > state->buffer_size) + min = state->buffer_size; + + /* + * Try to satisfy the request directly from the client + * buffer. We can do this if all of the data in the copy + * buffer was copied from the current client buffer. This + * also covers the case where the copy buffer is empty and + * the client buffer has all the data we need. + */ + if (state->client_total >= state->client_avail + state->avail + && state->client_avail + state->avail >= min) { + state->client_avail += state->avail; + state->client_next -= state->avail; + state->avail = 0; + state->next = state->buffer; + *buff = state->client_next; + return (state->client_avail); + } + + /* + * If we can't use client buffer, we'll have to use copy buffer. + */ + + /* Move data forward in copy buffer if necessary. */ + if (state->next > state->buffer && + state->next + min > state->buffer + state->buffer_size) { + if (state->avail > 0) + memmove(state->buffer, state->next, state->avail); + state->next = state->buffer; + } + + /* Collect data in copy buffer to fulfill request. */ + while (state->avail < min) { + /* Copy data from client buffer to our copy buffer. */ + if (state->client_avail > 0) { + /* First estimate: copy to fill rest of buffer. */ + size_t tocopy = (state->buffer + state->buffer_size) + - (state->next + state->avail); + /* Don't copy more than is available. */ + if (tocopy > state->client_avail) + tocopy = state->client_avail; + memcpy(state->next + state->avail, state->client_next, + tocopy); + state->client_next += tocopy; + state->client_avail -= tocopy; + state->avail += tocopy; + } else { + /* There is no more client data: fetch more. */ + /* + * It seems to me that const void ** and const + * char ** should be compatible, but they + * aren't, hence the cast. + */ + bytes_read = (a->client_reader)(&a->archive, + a->client_data, &state->client_buff); + if (bytes_read < 0) { /* Read error. */ + state->client_total = state->client_avail = 0; + state->client_next = state->client_buff = NULL; + state->fatal = 1; + return (-1); + } + if (bytes_read == 0) { /* End-of-file. */ + state->client_total = state->client_avail = 0; + state->client_next = state->client_buff = NULL; + state->end_of_file = 1; + break; + } + a->archive.raw_position += bytes_read; + state->client_total = bytes_read; + state->client_avail = state->client_total; + state->client_next = state->client_buff; + } + } + + *buff = state->next; + return (state->avail); +} + +/* + * Mark the appropriate data as used. Note that the request here will + * often be much smaller than the size of the previous read_ahead + * request. + */ +static ssize_t +archive_decompressor_none_read_consume(struct archive_read *a, size_t request) +{ + struct archive_decompress_none *state; + + state = (struct archive_decompress_none *)a->decompressor->data; + if (state->avail > 0) { + /* Read came from copy buffer. */ + state->next += request; + state->avail -= request; + } else { + /* Read came from client buffer. */ + state->client_next += request; + state->client_avail -= request; + } + a->archive.file_position += request; + return (request); +} + +/* + * Skip forward by exactly the requested bytes or else return + * ARCHIVE_FATAL. Note that this differs from the contract for + * read_ahead, which does not guarantee a minimum count. + */ +static off_t +archive_decompressor_none_skip(struct archive_read *a, off_t request) +{ + struct archive_decompress_none *state; + off_t bytes_skipped, total_bytes_skipped = 0; + size_t min; + + state = (struct archive_decompress_none *)a->decompressor->data; + if (state->fatal) + return (-1); + /* + * If there is data in the buffers already, use that first. + */ + if (state->avail > 0) { + min = minimum(request, (off_t)state->avail); + bytes_skipped = archive_decompressor_none_read_consume(a, min); + request -= bytes_skipped; + total_bytes_skipped += bytes_skipped; + } + if (state->client_avail > 0) { + min = minimum(request, (off_t)state->client_avail); + bytes_skipped = archive_decompressor_none_read_consume(a, min); + request -= bytes_skipped; + total_bytes_skipped += bytes_skipped; + } + if (request == 0) + return (total_bytes_skipped); + /* + * If a client_skipper was provided, try that first. + */ +#if ARCHIVE_API_VERSION < 2 + if ((a->client_skipper != NULL) && (request < SSIZE_MAX)) { +#else + if (a->client_skipper != NULL) { +#endif + bytes_skipped = (a->client_skipper)(&a->archive, + a->client_data, request); + if (bytes_skipped < 0) { /* error */ + state->client_total = state->client_avail = 0; + state->client_next = state->client_buff = NULL; + state->fatal = 1; + return (bytes_skipped); + } + total_bytes_skipped += bytes_skipped; + a->archive.file_position += bytes_skipped; + request -= bytes_skipped; + state->client_next = state->client_buff; + a->archive.raw_position += bytes_skipped; + state->client_avail = state->client_total = 0; + } + /* + * Note that client_skipper will usually not satisfy the + * full request (due to low-level blocking concerns), + * so even if client_skipper is provided, we may still + * have to use ordinary reads to finish out the request. + */ + while (request > 0) { + const void* dummy_buffer; + ssize_t bytes_read; + bytes_read = archive_decompressor_none_read_ahead(a, + &dummy_buffer, request); + if (bytes_read < 0) + return (bytes_read); + if (bytes_read == 0) { + /* We hit EOF before we satisfied the skip request. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Truncated input file (need to skip %jd bytes)", + (intmax_t)request); + return (ARCHIVE_FATAL); + } + min = (size_t)(minimum(bytes_read, request)); + bytes_read = archive_decompressor_none_read_consume(a, min); + total_bytes_skipped += bytes_read; + request -= bytes_read; + } + return (total_bytes_skipped); +} + +static int +archive_decompressor_none_finish(struct archive_read *a) +{ + struct archive_decompress_none *state; + + state = (struct archive_decompress_none *)a->decompressor->data; + free(state->buffer); + free(state); + a->decompressor->data = NULL; + return (ARCHIVE_OK); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_read_support_compression_program.c b/contrib/libarchive-2.1/libarchive/archive_read_support_compression_program.c new file mode 100644 index 0000000000..d40679769f --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_read_support_compression_program.c @@ -0,0 +1,312 @@ +/*- + * Copyright (c) 2007 Joerg Sonnenberger + * 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 "archive_platform.h" +__FBSDID("$FreeBSD$"); + +#ifdef HAVE_SYS_WAIT_H +# include +#endif +#ifdef HAVE_ERRNO_H +# include +#endif +#ifdef HAVE_FCNTL_H +# include +#endif +#ifdef HAVE_LIMITS_H +# include +#endif +#ifdef HAVE_STDLIB_H +# include +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif + +#include "archive.h" +#include "archive_private.h" +#include "archive_read_private.h" + +#include "filter_fork.h" + +struct archive_decompress_program { + char *description; + pid_t child; + int child_stdin, child_stdout; + + char *child_out_buf; + char *child_out_buf_next; + size_t child_out_buf_len, child_out_buf_avail; + + const char *child_in_buf; + size_t child_in_buf_avail; +}; + +static int archive_decompressor_program_bid(const void *, size_t); +static int archive_decompressor_program_finish(struct archive_read *); +static int archive_decompressor_program_init(struct archive_read *, + const void *, size_t); +static ssize_t archive_decompressor_program_read_ahead(struct archive_read *, + const void **, size_t); +static ssize_t archive_decompressor_program_read_consume(struct archive_read *, + size_t); + +int +archive_read_support_compression_program(struct archive *_a, const char *cmd) +{ + struct archive_read *a = (struct archive_read *)_a; + struct decompressor_t *decompressor; + + if (cmd == NULL || *cmd == '\0') + return (ARCHIVE_WARN); + + decompressor = __archive_read_register_compression(a, + archive_decompressor_program_bid, + archive_decompressor_program_init); + if (decompressor == NULL) + return (ARCHIVE_WARN); + + decompressor->config = strdup(cmd); + return (ARCHIVE_OK); +} + +/* + * If the user used us to register, they must really want us to + * handle it, so this module always bids INT_MAX. + */ +static int +archive_decompressor_program_bid(const void *buff, size_t len) +{ + (void)buff; /* UNUSED */ + (void)len; /* UNUSED */ + + return (INT_MAX); /* Default: We'll take it. */ +} + +static ssize_t +child_read(struct archive_read *a, char *buf, size_t buf_len) +{ + struct archive_decompress_program *state = a->decompressor->data; + ssize_t ret, requested; + + if (state->child_stdout == -1) + return (-1); + + if (buf_len == 0) + return (-1); + +restart_read: + requested = buf_len > SSIZE_MAX ? SSIZE_MAX : buf_len; + + do { + ret = read(state->child_stdout, buf, requested); + } while (ret == -1 && errno == EINTR); + + if (ret > 0) + return (ret); + if (ret == 0 || (ret == -1 && errno == EPIPE)) { + close(state->child_stdout); + state->child_stdout = -1; + return (0); + } + if (ret == -1 && errno != EAGAIN) + return (-1); + + if (state->child_in_buf_avail == 0) { + ret = (a->client_reader)(&a->archive, + a->client_data, (const void **)&state->child_in_buf); + + if (ret < 0) { + close(state->child_stdin); + state->child_stdin = -1; + fcntl(state->child_stdout, F_SETFL, 0); + return (-1); + } + if (ret == 0) { + close(state->child_stdin); + state->child_stdin = -1; + fcntl(state->child_stdout, F_SETFL, 0); + goto restart_read; + } + state->child_in_buf_avail = ret; + } + + do { + ret = write(state->child_stdin, state->child_in_buf, + state->child_in_buf_avail); + } while (ret == -1 && errno == EINTR); + + if (ret > 0) { + state->child_in_buf += ret; + state->child_in_buf_avail -= ret; + goto restart_read; + } else if (ret == -1 && errno == EAGAIN) { + __archive_check_child(state->child_stdin, state->child_stdout); + goto restart_read; + } else if (ret == 0 || (ret == -1 && errno == EPIPE)) { + close(state->child_stdin); + state->child_stdout = -1; + fcntl(state->child_stdout, F_SETFL, 0); + goto restart_read; + } else { + close(state->child_stdin); + state->child_stdin = -1; + fcntl(state->child_stdout, F_SETFL, 0); + return (-1); + } +} + +static int +archive_decompressor_program_init(struct archive_read *a, const void *buff, size_t n) +{ + struct archive_decompress_program *state; + const char *cmd = a->decompressor->config; + const char *prefix = "Program: "; + + + state = (struct archive_decompress_program *)malloc(sizeof(*state)); + if (!state) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate input data"); + return (ARCHIVE_FATAL); + } + + a->archive.compression_code = ARCHIVE_COMPRESSION_PROGRAM; + state->description = (char *)malloc(strlen(prefix) + strlen(cmd) + 1); + strcpy(state->description, prefix); + strcat(state->description, cmd); + a->archive.compression_name = state->description; + + state->child_out_buf_next = state->child_out_buf = malloc(65536); + if (!state->child_out_buf) { + free(state); + archive_set_error(&a->archive, ENOMEM, + "Can't allocate filter buffer"); + return (ARCHIVE_FATAL); + } + state->child_out_buf_len = 65536; + state->child_out_buf_avail = 0; + + state->child_in_buf = buff; + state->child_in_buf_avail = n; + + if ((state->child = __archive_create_child(cmd, + &state->child_stdin, &state->child_stdout)) == -1) { + free(state->child_out_buf); + free(state); + archive_set_error(&a->archive, EINVAL, + "Can't initialise filter"); + return (ARCHIVE_FATAL); + } + + a->decompressor->data = state; + a->decompressor->read_ahead = archive_decompressor_program_read_ahead; + a->decompressor->consume = archive_decompressor_program_read_consume; + a->decompressor->skip = NULL; + a->decompressor->finish = archive_decompressor_program_finish; + + /* XXX Check that we can read at least one byte? */ + return (ARCHIVE_OK); +} + +static ssize_t +archive_decompressor_program_read_ahead(struct archive_read *a, const void **buff, + size_t min) +{ + struct archive_decompress_program *state; + ssize_t bytes_read; + + state = (struct archive_decompress_program *)a->decompressor->data; + + if (min > state->child_out_buf_len) + min = state->child_out_buf_len; + + while (state->child_stdout != -1 && min > state->child_out_buf_avail) { + if (state->child_out_buf != state->child_out_buf_next) { + memmove(state->child_out_buf, state->child_out_buf_next, + state->child_out_buf_avail); + state->child_out_buf_next = state->child_out_buf; + } + + bytes_read = child_read(a, + state->child_out_buf + state->child_out_buf_avail, + state->child_out_buf_len - state->child_out_buf_avail); + if (bytes_read == -1) + return (-1); + if (bytes_read == 0) + break; + state->child_out_buf_avail += bytes_read; + a->archive.raw_position += bytes_read; + } + + *buff = state->child_out_buf_next; + return (state->child_out_buf_avail); +} + +static ssize_t +archive_decompressor_program_read_consume(struct archive_read *a, size_t request) +{ + struct archive_decompress_program *state; + + state = (struct archive_decompress_program *)a->decompressor->data; + + state->child_out_buf_next += request; + state->child_out_buf_avail -= request; + + a->archive.file_position += request; + return (request); +} + +static int +archive_decompressor_program_finish(struct archive_read *a) +{ + struct archive_decompress_program *state; + int status; + + state = (struct archive_decompress_program *)a->decompressor->data; + + /* Release our configuration data. */ + free(a->decompressor->config); + a->decompressor->config = NULL; + + /* Shut down the child. */ + if (state->child_stdin != -1) + close(state->child_stdin); + if (state->child_stdout != -1) + close(state->child_stdout); + while (waitpid(state->child, &status, 0) == -1 && errno == EINTR) + continue; + + /* Release our private data. */ + free(state->child_out_buf); + free(state->description); + free(state); + a->decompressor->data = NULL; + + return (ARCHIVE_OK); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_read_support_format_all.c b/contrib/libarchive-2.1/libarchive/archive_read_support_format_all.c new file mode 100644 index 0000000000..3fdfa05285 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_read_support_format_all.c @@ -0,0 +1,41 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_all.c,v 1.9 2007/04/07 05:54:23 kientzle Exp $"); + +#include "archive.h" + +int +archive_read_support_format_all(struct archive *a) +{ + archive_read_support_format_ar(a); + archive_read_support_format_cpio(a); + archive_read_support_format_empty(a); + archive_read_support_format_iso9660(a); + archive_read_support_format_tar(a); + archive_read_support_format_zip(a); + return (ARCHIVE_OK); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_read_support_format_ar.c b/contrib/libarchive-2.1/libarchive/archive_read_support_format_ar.c new file mode 100644 index 0000000000..4ffc1cd80a --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_read_support_format_ar.c @@ -0,0 +1,606 @@ +/*- + * Copyright (c) 2007 Kai Wang + * Copyright (c) 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 + * in this position and unchanged. + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_ar.c,v 1.5 2007/04/15 00:53:38 kientzle Exp $"); + +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "archive.h" +#include "archive_entry.h" +#include "archive_private.h" +#include "archive_read_private.h" + +struct ar { + int bid; + off_t entry_bytes_remaining; + off_t entry_offset; + off_t entry_padding; + char *strtab; + size_t strtab_size; +}; + +/* + * Define structure of the "ar" header. + */ +#define AR_name_offset 0 +#define AR_name_size 16 +#define AR_date_offset 16 +#define AR_date_size 12 +#define AR_uid_offset 28 +#define AR_uid_size 6 +#define AR_gid_offset 34 +#define AR_gid_size 6 +#define AR_mode_offset 40 +#define AR_mode_size 8 +#define AR_size_offset 48 +#define AR_size_size 10 +#define AR_fmag_offset 58 +#define AR_fmag_size 2 + +#define isdigit(x) (x) >= '0' && (x) <= '9' + +static int archive_read_format_ar_bid(struct archive_read *a); +static int archive_read_format_ar_cleanup(struct archive_read *a); +static int archive_read_format_ar_read_data(struct archive_read *a, + const void **buff, size_t *size, off_t *offset); +static int archive_read_format_ar_skip(struct archive_read *a); +static int archive_read_format_ar_read_header(struct archive_read *a, + struct archive_entry *e); +static uint64_t ar_atol8(const char *p, unsigned char_cnt); +static uint64_t ar_atol10(const char *p, unsigned char_cnt); +static int ar_parse_gnu_filename_table(struct archive_read *, struct ar *, + const void *, size_t); +static int ar_parse_common_header(struct ar *ar, struct archive_entry *, + const char *h); + +int +archive_read_support_format_ar(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + struct ar *ar; + int r; + + ar = (struct ar *)malloc(sizeof(*ar)); + if (ar == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate ar data"); + return (ARCHIVE_FATAL); + } + memset(ar, 0, sizeof(*ar)); + ar->bid = -1; + ar->strtab = NULL; + + r = __archive_read_register_format(a, + ar, + archive_read_format_ar_bid, + archive_read_format_ar_read_header, + archive_read_format_ar_read_data, + archive_read_format_ar_skip, + archive_read_format_ar_cleanup); + + if (r != ARCHIVE_OK) { + free(ar); + return (r); + } + return (ARCHIVE_OK); +} + +static int +archive_read_format_ar_cleanup(struct archive_read *a) +{ + struct ar *ar; + + ar = (struct ar *)(a->format->data); + if (ar->strtab) + free(ar->strtab); + free(ar); + (a->format->data) = NULL; + return (ARCHIVE_OK); +} + +static int +archive_read_format_ar_bid(struct archive_read *a) +{ + struct ar *ar; + ssize_t bytes_read; + const void *h; + + if (a->archive.archive_format != 0 && + (a->archive.archive_format & ARCHIVE_FORMAT_BASE_MASK) != + ARCHIVE_FORMAT_AR) + return(0); + + ar = (struct ar *)(a->format->data); + + if (ar->bid > 0) + return (ar->bid); + + /* + * Verify the 8-byte file signature. + * TODO: Do we need to check more than this? + */ + bytes_read = (a->decompressor->read_ahead)(a, &h, 8); + if (bytes_read < 8) + return (-1); + if (strncmp((const char*)h, "!\n", 8) == 0) { + ar->bid = 64; + return (ar->bid); + } + return (-1); +} + +static int +archive_read_format_ar_read_header(struct archive_read *a, + struct archive_entry *entry) +{ + char filename[AR_name_size + 1]; + struct ar *ar; + uint64_t number; /* Used to hold parsed numbers before validation. */ + ssize_t bytes_read; + size_t bsd_name_length, entry_size; + char *p; + const void *b; + const char *h; + int r; + + ar = (struct ar*)(a->format->data); + + if (a->archive.file_position == 0) { + /* + * We are now at the beginning of the archive, + * so we need first consume the ar global header. + */ + (a->decompressor->consume)(a, 8); + /* Set a default format code for now. */ + a->archive.archive_format = ARCHIVE_FORMAT_AR; + } + + /* Read the header for the next file entry. */ + bytes_read = (a->decompressor->read_ahead)(a, &b, 60); + if (bytes_read < 60) { + /* Broken header. */ + return (ARCHIVE_EOF); + } + (a->decompressor->consume)(a, 60); + h = (const char *)b; + + /* Verify the magic signature on the file header. */ + if (strncmp(h + AR_fmag_offset, "`\n", 2) != 0) { + archive_set_error(&a->archive, EINVAL, + "Consistency check failed"); + return (ARCHIVE_WARN); + } + + /* Copy filename into work buffer. */ + strncpy(filename, h + AR_name_offset, AR_name_size); + filename[AR_name_size] = '\0'; + + /* + * Guess the format variant based on the filename. + */ + if (a->archive.archive_format == ARCHIVE_FORMAT_AR) { + /* We don't already know the variant, so let's guess. */ + /* + * Biggest clue is presence of '/': GNU starts special + * filenames with '/', appends '/' as terminator to + * non-special names, so anything with '/' should be + * GNU except for BSD long filenames. + */ + if (strncmp(filename, "#1/", 3) == 0) + a->archive.archive_format = ARCHIVE_FORMAT_AR_BSD; + else if (strchr(filename, '/') != NULL) + a->archive.archive_format = ARCHIVE_FORMAT_AR_GNU; + else if (strncmp(filename, "__.SYMDEF", 9) == 0) + a->archive.archive_format = ARCHIVE_FORMAT_AR_BSD; + /* + * XXX Do GNU/SVR4 'ar' programs ever omit trailing '/' + * if name exactly fills 16-byte field? If so, we + * can't assume entries without '/' are BSD. XXX + */ + } + + /* Update format name from the code. */ + if (a->archive.archive_format == ARCHIVE_FORMAT_AR_GNU) + a->archive.archive_format_name = "ar (GNU/SVR4)"; + else if (a->archive.archive_format == ARCHIVE_FORMAT_AR_BSD) + a->archive.archive_format_name = "ar (BSD)"; + else + a->archive.archive_format_name = "ar"; + + /* + * Remove trailing spaces from the filename. GNU and BSD + * variants both pad filename area out with spaces. + * This will only be wrong if GNU/SVR4 'ar' implementations + * omit trailing '/' for 16-char filenames and we have + * a 16-char filename that ends in ' '. + */ + p = filename + AR_name_size - 1; + while (p >= filename && *p == ' ') { + *p = '\0'; + p--; + } + + /* + * Remove trailing slash unless first character is '/'. + * (BSD entries never end in '/', so this will only trim + * GNU-format entries. GNU special entries start with '/' + * and are not terminated in '/', so we don't trim anything + * that starts with '/'.) + */ + if (filename[0] != '/' && *p == '/') + *p = '\0'; + + /* + * '//' is the GNU filename table. + * Later entries can refer to names in this table. + */ + if (strcmp(filename, "//") == 0) { + /* This must come before any call to _read_ahead. */ + ar_parse_common_header(ar, entry, h); + archive_entry_copy_pathname(entry, filename); + archive_entry_set_mode(entry, + S_IFREG | (archive_entry_mode(entry) & 0777)); + /* Get the size of the filename table. */ + number = ar_atol10(h + AR_size_offset, AR_size_size); + if (number > SIZE_MAX) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Filename table too large"); + return (ARCHIVE_FATAL); + } + entry_size = (size_t)number; + /* Read the filename table into memory. */ + bytes_read = (a->decompressor->read_ahead)(a, &b, entry_size); + if (bytes_read <= 0) + return (ARCHIVE_FATAL); + if ((size_t)bytes_read < entry_size) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Truncated input file"); + return (ARCHIVE_FATAL); + } + /* + * Don't consume the contents, so the client will + * also get a shot at reading it. + */ + + /* Parse the filename table. */ + return (ar_parse_gnu_filename_table(a, ar, b, entry_size)); + } + + /* + * GNU variant handles long filenames by storing / + * to indicate a name stored in the filename table. + */ + if (filename[0] == '/' && isdigit(filename[1])) { + number = ar_atol10(h + AR_name_offset + 1, AR_name_size - 1); + /* + * If we can't look up the real name, warn and return + * the entry with the wrong name. + */ + if (ar->strtab == NULL || number > ar->strtab_size) { + archive_set_error(&a->archive, EINVAL, + "Can't find long filename for entry"); + archive_entry_copy_pathname(entry, filename); + /* Parse the time, owner, mode, size fields. */ + ar_parse_common_header(ar, entry, h); + return (ARCHIVE_WARN); + } + + archive_entry_copy_pathname(entry, &ar->strtab[(size_t)number]); + /* Parse the time, owner, mode, size fields. */ + return (ar_parse_common_header(ar, entry, h)); + } + + /* + * BSD handles long filenames by storing "#1/" followed by the + * length of filename as a decimal number, then prepends the + * the filename to the file contents. + */ + if (strncmp(filename, "#1/", 3) == 0) { + /* Parse the time, owner, mode, size fields. */ + /* This must occur before _read_ahead is called again. */ + ar_parse_common_header(ar, entry, h); + + /* Parse the size of the name, adjust the file size. */ + number = ar_atol10(h + AR_name_offset + 3, AR_name_size - 3); + if ((off_t)number > ar->entry_bytes_remaining) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Bad input file size"); + return (ARCHIVE_FATAL); + } + bsd_name_length = (size_t)number; + ar->entry_bytes_remaining -= bsd_name_length; + /* Adjust file size reported to client. */ + archive_entry_set_size(entry, ar->entry_bytes_remaining); + + /* Read the long name into memory. */ + bytes_read = (a->decompressor->read_ahead)(a, &b, bsd_name_length); + if (bytes_read <= 0) + return (ARCHIVE_FATAL); + if ((size_t)bytes_read < bsd_name_length) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Truncated input file"); + return (ARCHIVE_FATAL); + } + (a->decompressor->consume)(a, bsd_name_length); + + /* Store it in the entry. */ + p = (char *)malloc(bsd_name_length + 1); + if (p == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate fname buffer"); + return (ARCHIVE_FATAL); + } + strncpy(p, b, bsd_name_length); + p[bsd_name_length] = '\0'; + archive_entry_copy_pathname(entry, p); + free(p); + return (ARCHIVE_OK); + } + + /* + * "/" is the SVR4/GNU archive symbol table. + */ + if (strcmp(filename, "/") == 0) { + archive_entry_copy_pathname(entry, "/"); + /* Parse the time, owner, mode, size fields. */ + r = ar_parse_common_header(ar, entry, h); + /* Force the file type to a regular file. */ + archive_entry_set_mode(entry, + S_IFREG | (archive_entry_mode(entry) & 0777)); + return (r); + } + + /* + * "__.SYMDEF" is a BSD archive symbol table. + */ + if (strcmp(filename, "__.SYMDEF") == 0) { + archive_entry_copy_pathname(entry, filename); + /* Parse the time, owner, mode, size fields. */ + return (ar_parse_common_header(ar, entry, h)); + } + + /* + * Otherwise, this is a standard entry. The filename + * has already been trimmed as much as possible, based + * on our current knowledge of the format. + */ + archive_entry_copy_pathname(entry, filename); + return (ar_parse_common_header(ar, entry, h)); +} + +static int +ar_parse_common_header(struct ar *ar, struct archive_entry *entry, + const char *h) +{ + uint64_t n; + + /* Copy remaining header */ + archive_entry_set_mtime(entry, + (time_t)ar_atol10(h + AR_date_offset, AR_date_size), 0L); + archive_entry_set_uid(entry, + (uid_t)ar_atol10(h + AR_uid_offset, AR_uid_size)); + archive_entry_set_gid(entry, + (gid_t)ar_atol10(h + AR_gid_offset, AR_gid_size)); + archive_entry_set_mode(entry, + (mode_t)ar_atol8(h + AR_mode_offset, AR_mode_size)); + n = ar_atol10(h + AR_size_offset, AR_size_size); + + ar->entry_offset = 0; + ar->entry_padding = n % 2; + archive_entry_set_size(entry, n); + ar->entry_bytes_remaining = n; + return (ARCHIVE_OK); +} + +static int +archive_read_format_ar_read_data(struct archive_read *a, + const void **buff, size_t *size, off_t *offset) +{ + ssize_t bytes_read; + struct ar *ar; + + ar = (struct ar *)(a->format->data); + + if (ar->entry_bytes_remaining > 0) { + bytes_read = (a->decompressor->read_ahead)(a, buff, 1); + if (bytes_read == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Truncated ar archive"); + return (ARCHIVE_FATAL); + } + if (bytes_read < 0) + return (ARCHIVE_FATAL); + if (bytes_read > ar->entry_bytes_remaining) + bytes_read = (ssize_t)ar->entry_bytes_remaining; + *size = bytes_read; + *offset = ar->entry_offset; + ar->entry_offset += bytes_read; + ar->entry_bytes_remaining -= bytes_read; + (a->decompressor->consume)(a, (size_t)bytes_read); + return (ARCHIVE_OK); + } else { + while (ar->entry_padding > 0) { + bytes_read = (a->decompressor->read_ahead)(a, buff, 1); + if (bytes_read <= 0) + return (ARCHIVE_FATAL); + if (bytes_read > ar->entry_padding) + bytes_read = (ssize_t)ar->entry_padding; + (a->decompressor->consume)(a, (size_t)bytes_read); + ar->entry_padding -= bytes_read; + } + *buff = NULL; + *size = 0; + *offset = ar->entry_offset; + return (ARCHIVE_EOF); + } +} + +static int +archive_read_format_ar_skip(struct archive_read *a) +{ + off_t bytes_skipped; + struct ar* ar; + int r = ARCHIVE_OK; + const void *b; /* Dummy variables */ + size_t s; + off_t o; + + ar = (struct ar *)(a->format->data); + if (a->decompressor->skip == NULL) { + while (r == ARCHIVE_OK) + r = archive_read_format_ar_read_data(a, &b, &s, &o); + return (r); + } + + bytes_skipped = (a->decompressor->skip)(a, ar->entry_bytes_remaining + + ar->entry_padding); + if (bytes_skipped < 0) + return (ARCHIVE_FATAL); + + ar->entry_bytes_remaining = 0; + ar->entry_padding = 0; + + return (ARCHIVE_OK); +} + +static int +ar_parse_gnu_filename_table(struct archive_read *a, struct ar *ar, + const void *h, size_t size) +{ + char *p; + + if (ar->strtab != NULL) { + archive_set_error(&a->archive, EINVAL, + "More than one string tables exist"); + return (ARCHIVE_WARN); + } + + if (size == 0) { + archive_set_error(&a->archive, EINVAL, "Invalid string table"); + return (ARCHIVE_WARN); + } + + ar->strtab_size = size; + ar->strtab = malloc(size); + if (ar->strtab == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate string table buffer"); + return (ARCHIVE_FATAL); + } + + (void)memcpy(ar->strtab, h, size); + for (p = ar->strtab; p < ar->strtab + size - 1; ++p) { + if (*p == '/') { + *p++ = '\0'; + if (*p != '\n') + goto bad_string_table; + *p = '\0'; + } + } + /* + * Sanity check, last two chars must be `/\n' or '\n\n', + * depending on whether the string table is padded by a '\n' + * (string table produced by GNU ar always has a even size). + */ + if (p != ar->strtab + size && *p != '\n') + goto bad_string_table; + + /* Enforce zero termination. */ + ar->strtab[size - 1] = '\0'; + + return (ARCHIVE_OK); + +bad_string_table: + archive_set_error(&a->archive, EINVAL, + "Invalid string table"); + free(ar->strtab); + ar->strtab = NULL; + return (ARCHIVE_WARN); +} + +static uint64_t +ar_atol8(const char *p, unsigned char_cnt) +{ + uint64_t l, limit, last_digit_limit; + unsigned int digit, base; + + base = 8; + limit = UINT64_MAX / base; + last_digit_limit = UINT64_MAX % base; + + while ((*p == ' ' || *p == '\t') && char_cnt-- > 0) + p++; + + l = 0; + digit = *p - '0'; + while (*p >= '0' && digit < base && char_cnt-- > 0) { + if (l>limit || (l == limit && digit > last_digit_limit)) { + l = UINT64_MAX; /* Truncate on overflow. */ + break; + } + l = (l * base) + digit; + digit = *++p - '0'; + } + return (l); +} + +static uint64_t +ar_atol10(const char *p, unsigned char_cnt) +{ + uint64_t l, limit, last_digit_limit; + unsigned int base, digit; + + base = 10; + limit = UINT64_MAX / base; + last_digit_limit = UINT64_MAX % base; + + while ((*p == ' ' || *p == '\t') && char_cnt-- > 0) + p++; + l = 0; + digit = *p - '0'; + while (*p >= '0' && digit < base && char_cnt-- > 0) { + if (l > limit || (l == limit && digit > last_digit_limit)) { + l = UINT64_MAX; /* Truncate on overflow. */ + break; + } + l = (l * base) + digit; + digit = *++p - '0'; + } + return (l); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_read_support_format_cpio.c b/contrib/libarchive-2.1/libarchive/archive_read_support_format_cpio.c new file mode 100644 index 0000000000..d816e9ed9f --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_read_support_format_cpio.c @@ -0,0 +1,607 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_cpio.c,v 1.23 2007/04/13 16:07:25 kientzle Exp $"); + +#ifdef HAVE_ERRNO_H +#include +#endif +/* #include */ /* See archive_platform.h */ +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "archive.h" +#include "archive_entry.h" +#include "archive_private.h" +#include "archive_read_private.h" + +struct cpio_bin_header { + unsigned char c_magic[2]; + unsigned char c_dev[2]; + unsigned char c_ino[2]; + unsigned char c_mode[2]; + unsigned char c_uid[2]; + unsigned char c_gid[2]; + unsigned char c_nlink[2]; + unsigned char c_rdev[2]; + unsigned char c_mtime[4]; + unsigned char c_namesize[2]; + unsigned char c_filesize[4]; +}; + +struct cpio_odc_header { + char c_magic[6]; + char c_dev[6]; + char c_ino[6]; + char c_mode[6]; + char c_uid[6]; + char c_gid[6]; + char c_nlink[6]; + char c_rdev[6]; + char c_mtime[11]; + char c_namesize[6]; + char c_filesize[11]; +}; + +struct cpio_newc_header { + char c_magic[6]; + char c_ino[8]; + char c_mode[8]; + char c_uid[8]; + char c_gid[8]; + char c_nlink[8]; + char c_mtime[8]; + char c_filesize[8]; + char c_devmajor[8]; + char c_devminor[8]; + char c_rdevmajor[8]; + char c_rdevminor[8]; + char c_namesize[8]; + char c_crc[8]; +}; + +struct links_entry { + struct links_entry *next; + struct links_entry *previous; + int links; + dev_t dev; + ino_t ino; + char *name; +}; + +#define CPIO_MAGIC 0x13141516 +struct cpio { + int magic; + int (*read_header)(struct archive_read *, struct cpio *, + struct archive_entry *, size_t *, size_t *); + struct links_entry *links_head; + struct archive_string entry_name; + struct archive_string entry_linkname; + off_t entry_bytes_remaining; + off_t entry_offset; + off_t entry_padding; +}; + +static int64_t atol16(const char *, unsigned); +static int64_t atol8(const char *, unsigned); +static int archive_read_format_cpio_bid(struct archive_read *); +static int archive_read_format_cpio_cleanup(struct archive_read *); +static int archive_read_format_cpio_read_data(struct archive_read *, + const void **, size_t *, off_t *); +static int archive_read_format_cpio_read_header(struct archive_read *, + struct archive_entry *); +static int be4(const unsigned char *); +static int header_bin_be(struct archive_read *, struct cpio *, + struct archive_entry *, size_t *, size_t *); +static int header_bin_le(struct archive_read *, struct cpio *, + struct archive_entry *, size_t *, size_t *); +static int header_newc(struct archive_read *, struct cpio *, + struct archive_entry *, size_t *, size_t *); +static int header_odc(struct archive_read *, struct cpio *, + struct archive_entry *, size_t *, size_t *); +static int le4(const unsigned char *); +static void record_hardlink(struct cpio *cpio, struct archive_entry *entry); + +int +archive_read_support_format_cpio(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + struct cpio *cpio; + int r; + + cpio = (struct cpio *)malloc(sizeof(*cpio)); + if (cpio == NULL) { + archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data"); + return (ARCHIVE_FATAL); + } + memset(cpio, 0, sizeof(*cpio)); + cpio->magic = CPIO_MAGIC; + + r = __archive_read_register_format(a, + cpio, + archive_read_format_cpio_bid, + archive_read_format_cpio_read_header, + archive_read_format_cpio_read_data, + NULL, + archive_read_format_cpio_cleanup); + + if (r != ARCHIVE_OK) + free(cpio); + return (ARCHIVE_OK); +} + + +static int +archive_read_format_cpio_bid(struct archive_read *a) +{ + int bid, bytes_read; + const void *h; + const unsigned char *p; + struct cpio *cpio; + + cpio = (struct cpio *)(a->format->data); + bid = 0; + bytes_read = (a->decompressor->read_ahead)(a, &h, 6); + /* Convert error code into error return. */ + if (bytes_read < 0) + return ((int)bytes_read); + if (bytes_read < 6) + return (-1); + + p = (const unsigned char *)h; + if (memcmp(p, "070707", 6) == 0) { + /* ASCII cpio archive (odc, POSIX.1) */ + cpio->read_header = header_odc; + bid += 48; + /* + * XXX TODO: More verification; Could check that only octal + * digits appear in appropriate header locations. XXX + */ + } else if (memcmp(p, "070701", 6) == 0) { + /* ASCII cpio archive (SVR4 without CRC) */ + cpio->read_header = header_newc; + bid += 48; + /* + * XXX TODO: More verification; Could check that only hex + * digits appear in appropriate header locations. XXX + */ + } else if (memcmp(p, "070702", 6) == 0) { + /* ASCII cpio archive (SVR4 with CRC) */ + /* XXX TODO: Flag that we should check the CRC. XXX */ + cpio->read_header = header_newc; + bid += 48; + /* + * XXX TODO: More verification; Could check that only hex + * digits appear in appropriate header locations. XXX + */ + } else if (p[0] * 256 + p[1] == 070707) { + /* big-endian binary cpio archives */ + cpio->read_header = header_bin_be; + bid += 16; + /* Is more verification possible here? */ + } else if (p[0] + p[1] * 256 == 070707) { + /* little-endian binary cpio archives */ + cpio->read_header = header_bin_le; + bid += 16; + /* Is more verification possible here? */ + } else + return (ARCHIVE_WARN); + + return (bid); +} + +static int +archive_read_format_cpio_read_header(struct archive_read *a, + struct archive_entry *entry) +{ + struct cpio *cpio; + size_t bytes; + const void *h; + size_t namelength; + size_t name_pad; + int r; + + cpio = (struct cpio *)(a->format->data); + r = (cpio->read_header(a, cpio, entry, &namelength, &name_pad)); + + if (r != ARCHIVE_OK) + return (r); + + /* Read name from buffer. */ + bytes = (a->decompressor->read_ahead)(a, &h, namelength + name_pad); + if (bytes < namelength + name_pad) + return (ARCHIVE_FATAL); + (a->decompressor->consume)(a, namelength + name_pad); + archive_strncpy(&cpio->entry_name, (const char *)h, namelength); + archive_entry_set_pathname(entry, cpio->entry_name.s); + cpio->entry_offset = 0; + + /* If this is a symlink, read the link contents. */ + if (archive_entry_filetype(entry) == AE_IFLNK) { + bytes = (a->decompressor->read_ahead)(a, &h, + cpio->entry_bytes_remaining); + if ((off_t)bytes < cpio->entry_bytes_remaining) + return (ARCHIVE_FATAL); + (a->decompressor->consume)(a, cpio->entry_bytes_remaining); + archive_strncpy(&cpio->entry_linkname, (const char *)h, + cpio->entry_bytes_remaining); + archive_entry_set_symlink(entry, cpio->entry_linkname.s); + cpio->entry_bytes_remaining = 0; + } + + /* Compare name to "TRAILER!!!" to test for end-of-archive. */ + if (namelength == 11 && strcmp((const char *)h, "TRAILER!!!") == 0) { + /* TODO: Store file location of start of block. */ + archive_set_error(&a->archive, 0, NULL); + return (ARCHIVE_EOF); + } + + /* Detect and record hardlinks to previously-extracted entries. */ + record_hardlink(cpio, entry); + + return (ARCHIVE_OK); +} + +static int +archive_read_format_cpio_read_data(struct archive_read *a, + const void **buff, size_t *size, off_t *offset) +{ + ssize_t bytes_read; + struct cpio *cpio; + + cpio = (struct cpio *)(a->format->data); + if (cpio->entry_bytes_remaining > 0) { + bytes_read = (a->decompressor->read_ahead)(a, buff, 1); + if (bytes_read <= 0) + return (ARCHIVE_FATAL); + if (bytes_read > cpio->entry_bytes_remaining) + bytes_read = cpio->entry_bytes_remaining; + *size = bytes_read; + *offset = cpio->entry_offset; + cpio->entry_offset += bytes_read; + cpio->entry_bytes_remaining -= bytes_read; + (a->decompressor->consume)(a, bytes_read); + return (ARCHIVE_OK); + } else { + while (cpio->entry_padding > 0) { + bytes_read = (a->decompressor->read_ahead)(a, buff, 1); + if (bytes_read <= 0) + return (ARCHIVE_FATAL); + if (bytes_read > cpio->entry_padding) + bytes_read = cpio->entry_padding; + (a->decompressor->consume)(a, bytes_read); + cpio->entry_padding -= bytes_read; + } + *buff = NULL; + *size = 0; + *offset = cpio->entry_offset; + return (ARCHIVE_EOF); + } +} + +static int +header_newc(struct archive_read *a, struct cpio *cpio, + struct archive_entry *entry, size_t *namelength, size_t *name_pad) +{ + const void *h; + const struct cpio_newc_header *header; + size_t bytes; + + /* Read fixed-size portion of header. */ + bytes = (a->decompressor->read_ahead)(a, &h, sizeof(struct cpio_newc_header)); + if (bytes < sizeof(struct cpio_newc_header)) + return (ARCHIVE_FATAL); + (a->decompressor->consume)(a, sizeof(struct cpio_newc_header)); + + /* Parse out hex fields. */ + header = (const struct cpio_newc_header *)h; + + if (memcmp(header->c_magic, "070701", 6) == 0) { + a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_NOCRC; + a->archive.archive_format_name = "ASCII cpio (SVR4 with no CRC)"; + } else if (memcmp(header->c_magic, "070702", 6) == 0) { + a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_CRC; + a->archive.archive_format_name = "ASCII cpio (SVR4 with CRC)"; + } else { + /* TODO: Abort here? */ + } + + archive_entry_set_devmajor(entry, atol16(header->c_devmajor, sizeof(header->c_devmajor))); + archive_entry_set_devminor(entry, atol16(header->c_devminor, sizeof(header->c_devminor))); + archive_entry_set_ino(entry, atol16(header->c_ino, sizeof(header->c_ino))); + archive_entry_set_mode(entry, atol16(header->c_mode, sizeof(header->c_mode))); + archive_entry_set_uid(entry, atol16(header->c_uid, sizeof(header->c_uid))); + archive_entry_set_gid(entry, atol16(header->c_gid, sizeof(header->c_gid))); + archive_entry_set_nlink(entry, atol16(header->c_nlink, sizeof(header->c_nlink))); + archive_entry_set_rdevmajor(entry, atol16(header->c_rdevmajor, sizeof(header->c_rdevmajor))); + archive_entry_set_rdevminor(entry, atol16(header->c_rdevminor, sizeof(header->c_rdevminor))); + archive_entry_set_mtime(entry, atol16(header->c_mtime, sizeof(header->c_mtime)), 0); + *namelength = atol16(header->c_namesize, sizeof(header->c_namesize)); + /* Pad name to 2 more than a multiple of 4. */ + *name_pad = (2 - *namelength) & 3; + + /* + * Note: entry_bytes_remaining is at least 64 bits and + * therefore guaranteed to be big enough for a 33-bit file + * size. + */ + cpio->entry_bytes_remaining = + atol16(header->c_filesize, sizeof(header->c_filesize)); + archive_entry_set_size(entry, cpio->entry_bytes_remaining); + /* Pad file contents to a multiple of 4. */ + cpio->entry_padding = 3 & -cpio->entry_bytes_remaining; + return (ARCHIVE_OK); +} + +static int +header_odc(struct archive_read *a, struct cpio *cpio, + struct archive_entry *entry, size_t *namelength, size_t *name_pad) +{ + const void *h; + const struct cpio_odc_header *header; + size_t bytes; + + a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX; + a->archive.archive_format_name = "POSIX octet-oriented cpio"; + + /* Read fixed-size portion of header. */ + bytes = (a->decompressor->read_ahead)(a, &h, sizeof(struct cpio_odc_header)); + if (bytes < sizeof(struct cpio_odc_header)) + return (ARCHIVE_FATAL); + (a->decompressor->consume)(a, sizeof(struct cpio_odc_header)); + + /* Parse out octal fields. */ + header = (const struct cpio_odc_header *)h; + + archive_entry_set_dev(entry, atol8(header->c_dev, sizeof(header->c_dev))); + archive_entry_set_ino(entry, atol8(header->c_ino, sizeof(header->c_ino))); + archive_entry_set_mode(entry, atol8(header->c_mode, sizeof(header->c_mode))); + archive_entry_set_uid(entry, atol8(header->c_uid, sizeof(header->c_uid))); + archive_entry_set_gid(entry, atol8(header->c_gid, sizeof(header->c_gid))); + archive_entry_set_nlink(entry, atol8(header->c_nlink, sizeof(header->c_nlink))); + archive_entry_set_rdev(entry, atol8(header->c_rdev, sizeof(header->c_rdev))); + archive_entry_set_mtime(entry, atol8(header->c_mtime, sizeof(header->c_mtime)), 0); + *namelength = atol8(header->c_namesize, sizeof(header->c_namesize)); + *name_pad = 0; /* No padding of filename. */ + + /* + * Note: entry_bytes_remaining is at least 64 bits and + * therefore guaranteed to be big enough for a 33-bit file + * size. + */ + cpio->entry_bytes_remaining = + atol8(header->c_filesize, sizeof(header->c_filesize)); + archive_entry_set_size(entry, cpio->entry_bytes_remaining); + cpio->entry_padding = 0; + return (ARCHIVE_OK); +} + +static int +header_bin_le(struct archive_read *a, struct cpio *cpio, + struct archive_entry *entry, size_t *namelength, size_t *name_pad) +{ + const void *h; + const struct cpio_bin_header *header; + size_t bytes; + + a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_LE; + a->archive.archive_format_name = "cpio (little-endian binary)"; + + /* Read fixed-size portion of header. */ + bytes = (a->decompressor->read_ahead)(a, &h, sizeof(struct cpio_bin_header)); + if (bytes < sizeof(struct cpio_bin_header)) + return (ARCHIVE_FATAL); + (a->decompressor->consume)(a, sizeof(struct cpio_bin_header)); + + /* Parse out binary fields. */ + header = (const struct cpio_bin_header *)h; + + archive_entry_set_dev(entry, header->c_dev[0] + header->c_dev[1] * 256); + archive_entry_set_ino(entry, header->c_ino[0] + header->c_ino[1] * 256); + archive_entry_set_mode(entry, header->c_mode[0] + header->c_mode[1] * 256); + archive_entry_set_uid(entry, header->c_uid[0] + header->c_uid[1] * 256); + archive_entry_set_gid(entry, header->c_gid[0] + header->c_gid[1] * 256); + archive_entry_set_nlink(entry, header->c_nlink[0] + header->c_nlink[1] * 256); + archive_entry_set_rdev(entry, header->c_rdev[0] + header->c_rdev[1] * 256); + archive_entry_set_mtime(entry, le4(header->c_mtime), 0); + *namelength = header->c_namesize[0] + header->c_namesize[1] * 256; + *name_pad = *namelength & 1; /* Pad to even. */ + + cpio->entry_bytes_remaining = le4(header->c_filesize); + archive_entry_set_size(entry, cpio->entry_bytes_remaining); + cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */ + return (ARCHIVE_OK); +} + +static int +header_bin_be(struct archive_read *a, struct cpio *cpio, + struct archive_entry *entry, size_t *namelength, size_t *name_pad) +{ + const void *h; + const struct cpio_bin_header *header; + size_t bytes; + + a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_BE; + a->archive.archive_format_name = "cpio (big-endian binary)"; + + /* Read fixed-size portion of header. */ + bytes = (a->decompressor->read_ahead)(a, &h, + sizeof(struct cpio_bin_header)); + if (bytes < sizeof(struct cpio_bin_header)) + return (ARCHIVE_FATAL); + (a->decompressor->consume)(a, sizeof(struct cpio_bin_header)); + + /* Parse out binary fields. */ + header = (const struct cpio_bin_header *)h; + archive_entry_set_dev(entry, header->c_dev[0] * 256 + header->c_dev[1]); + archive_entry_set_ino(entry, header->c_ino[0] * 256 + header->c_ino[1]); + archive_entry_set_mode(entry, header->c_mode[0] * 256 + header->c_mode[1]); + archive_entry_set_uid(entry, header->c_uid[0] * 256 + header->c_uid[1]); + archive_entry_set_gid(entry, header->c_gid[0] * 256 + header->c_gid[1]); + archive_entry_set_nlink(entry, header->c_nlink[0] * 256 + header->c_nlink[1]); + archive_entry_set_rdev(entry, header->c_rdev[0] * 256 + header->c_rdev[1]); + archive_entry_set_mtime(entry, be4(header->c_mtime), 0); + *namelength = header->c_namesize[0] * 256 + header->c_namesize[1]; + *name_pad = *namelength & 1; /* Pad to even. */ + + cpio->entry_bytes_remaining = be4(header->c_filesize); + archive_entry_set_size(entry, cpio->entry_bytes_remaining); + cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */ + return (ARCHIVE_OK); +} + +static int +archive_read_format_cpio_cleanup(struct archive_read *a) +{ + struct cpio *cpio; + + cpio = (struct cpio *)(a->format->data); + /* Free inode->name map */ + while (cpio->links_head != NULL) { + struct links_entry *lp = cpio->links_head->next; + + if (cpio->links_head->name) + free(cpio->links_head->name); + free(cpio->links_head); + cpio->links_head = lp; + } + archive_string_free(&cpio->entry_name); + free(cpio); + (a->format->data) = NULL; + return (ARCHIVE_OK); +} + +static int +le4(const unsigned char *p) +{ + return ((p[0]<<16) + (p[1]<<24) + (p[2]<<0) + (p[3]<<8)); +} + + +static int +be4(const unsigned char *p) +{ + return (p[0] + (p[1]<<8) + (p[2]<<16) + (p[3]<<24)); +} + +/* + * Note that this implementation does not (and should not!) obey + * locale settings; you cannot simply substitute strtol here, since + * it does obey locale. + */ +static int64_t +atol8(const char *p, unsigned char_cnt) +{ + int64_t l; + int digit; + + l = 0; + while (char_cnt-- > 0) { + if (*p >= '0' && *p <= '7') + digit = *p - '0'; + else + return (l); + p++; + l <<= 3; + l |= digit; + } + return (l); +} + +static int64_t +atol16(const char *p, unsigned char_cnt) +{ + int64_t l; + int digit; + + l = 0; + while (char_cnt-- > 0) { + if (*p >= 'a' && *p <= 'f') + digit = *p - 'a' + 10; + else if (*p >= 'A' && *p <= 'F') + digit = *p - 'A' + 10; + else if (*p >= '0' && *p <= '9') + digit = *p - '0'; + else + return (l); + p++; + l <<= 4; + l |= digit; + } + return (l); +} + +static void +record_hardlink(struct cpio *cpio, struct archive_entry *entry) +{ + struct links_entry *le; + dev_t dev; + ino_t ino; + + dev = archive_entry_dev(entry); + ino = archive_entry_ino(entry); + + /* + * First look in the list of multiply-linked files. If we've + * already dumped it, convert this entry to a hard link entry. + */ + for (le = cpio->links_head; le; le = le->next) { + if (le->dev == dev && le->ino == ino) { + archive_entry_set_hardlink(entry, le->name); + + if (--le->links <= 0) { + if (le->previous != NULL) + le->previous->next = le->next; + if (le->next != NULL) + le->next->previous = le->previous; + if (cpio->links_head == le) + cpio->links_head = le->next; + free(le); + } + + return; + } + } + + le = (struct links_entry *)malloc(sizeof(struct links_entry)); + if (le == NULL) + __archive_errx(1, "Out of memory adding file to list"); + if (cpio->links_head != NULL) + cpio->links_head->previous = le; + le->next = cpio->links_head; + le->previous = NULL; + cpio->links_head = le; + le->dev = dev; + le->ino = ino; + le->links = archive_entry_nlink(entry) - 1; + le->name = strdup(archive_entry_pathname(entry)); + if (le->name == NULL) + __archive_errx(1, "Out of memory adding file to list"); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_read_support_format_empty.c b/contrib/libarchive-2.1/libarchive/archive_read_support_format_empty.c new file mode 100644 index 0000000000..f664b874d0 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_read_support_format_empty.c @@ -0,0 +1,92 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_empty.c,v 1.2 2007/03/03 07:37:36 kientzle Exp $"); + +#include "archive.h" +#include "archive_entry.h" +#include "archive_private.h" +#include "archive_read_private.h" + +static int archive_read_format_empty_bid(struct archive_read *); +static int archive_read_format_empty_read_data(struct archive_read *, + const void **, size_t *, off_t *); +static int archive_read_format_empty_read_header(struct archive_read *, + struct archive_entry *); +int +archive_read_support_format_empty(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + int r; + + r = __archive_read_register_format(a, + NULL, + archive_read_format_empty_bid, + archive_read_format_empty_read_header, + archive_read_format_empty_read_data, + NULL, + NULL); + + return (r); +} + + +static int +archive_read_format_empty_bid(struct archive_read *a) +{ + int bytes_read; + const void *h; + + bytes_read = (a->decompressor->read_ahead)(a, &h, 1); + if (bytes_read > 0) + return (-1); + return (1); +} + +static int +archive_read_format_empty_read_header(struct archive_read *a, + struct archive_entry *entry) +{ + (void)a; /* UNUSED */ + (void)entry; /* UNUSED */ + + a->archive.archive_format = ARCHIVE_FORMAT_EMPTY; + a->archive.archive_format_name = "Empty file"; + + return (ARCHIVE_EOF); +} + +static int +archive_read_format_empty_read_data(struct archive_read *a, + const void **buff, size_t *size, off_t *offset) +{ + (void)a; /* UNUSED */ + (void)buff; /* UNUSED */ + (void)size; /* UNUSED */ + (void)offset; /* UNUSED */ + + return (ARCHIVE_EOF); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_read_support_format_iso9660.c b/contrib/libarchive-2.1/libarchive/archive_read_support_format_iso9660.c new file mode 100644 index 0000000000..fbc4c89d92 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_read_support_format_iso9660.c @@ -0,0 +1,1119 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_iso9660.c,v 1.22 2007/04/02 00:29:52 kientzle Exp $"); + +#ifdef HAVE_ERRNO_H +#include +#endif +/* #include */ /* See archive_platform.h */ +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#include + +#include "archive.h" +#include "archive_entry.h" +#include "archive_private.h" +#include "archive_read_private.h" +#include "archive_string.h" + +/* + * An overview of ISO 9660 format: + * + * Each disk is laid out as follows: + * * 32k reserved for private use + * * Volume descriptor table. Each volume descriptor + * is 2k and specifies basic format information. + * The "Primary Volume Descriptor" (PVD) is defined by the + * standard and should always be present; other volume + * descriptors include various vendor-specific extensions. + * * Files and directories. Each file/dir is specified by + * an "extent" (starting sector and length in bytes). + * Dirs are just files with directory records packed one + * after another. The PVD contains a single dir entry + * specifying the location of the root directory. Everything + * else follows from there. + * + * This module works by first reading the volume descriptors, then + * building a list of directory entries, sorted by starting + * sector. At each step, I look for the earliest dir entry that + * hasn't yet been read, seek forward to that location and read + * that entry. If it's a dir, I slurp in the new dir entries and + * add them to the heap; if it's a regular file, I return the + * corresponding archive_entry and wait for the client to request + * the file body. This strategy allows us to read most compliant + * CDs with a single pass through the data, as required by libarchive. + */ + +/* Structure of on-disk primary volume descriptor. */ +#define PVD_type_offset 0 +#define PVD_type_size 1 +#define PVD_id_offset (PVD_type_offset + PVD_type_size) +#define PVD_id_size 5 +#define PVD_version_offset (PVD_id_offset + PVD_id_size) +#define PVD_version_size 1 +#define PVD_reserved1_offset (PVD_version_offset + PVD_version_size) +#define PVD_reserved1_size 1 +#define PVD_system_id_offset (PVD_reserved1_offset + PVD_reserved1_size) +#define PVD_system_id_size 32 +#define PVD_volume_id_offset (PVD_system_id_offset + PVD_system_id_size) +#define PVD_volume_id_size 32 +#define PVD_reserved2_offset (PVD_volume_id_offset + PVD_volume_id_size) +#define PVD_reserved2_size 8 +#define PVD_volume_space_size_offset (PVD_reserved2_offset + PVD_reserved2_size) +#define PVD_volume_space_size_size 8 +#define PVD_reserved3_offset (PVD_volume_space_size_offset + PVD_volume_space_size_size) +#define PVD_reserved3_size 32 +#define PVD_volume_set_size_offset (PVD_reserved3_offset + PVD_reserved3_size) +#define PVD_volume_set_size_size 4 +#define PVD_volume_sequence_number_offset (PVD_volume_set_size_offset + PVD_volume_set_size_size) +#define PVD_volume_sequence_number_size 4 +#define PVD_logical_block_size_offset (PVD_volume_sequence_number_offset + PVD_volume_sequence_number_size) +#define PVD_logical_block_size_size 4 +#define PVD_path_table_size_offset (PVD_logical_block_size_offset + PVD_logical_block_size_size) +#define PVD_path_table_size_size 8 +#define PVD_type_1_path_table_offset (PVD_path_table_size_offset + PVD_path_table_size_size) +#define PVD_type_1_path_table_size 4 +#define PVD_opt_type_1_path_table_offset (PVD_type_1_path_table_offset + PVD_type_1_path_table_size) +#define PVD_opt_type_1_path_table_size 4 +#define PVD_type_m_path_table_offset (PVD_opt_type_1_path_table_offset + PVD_opt_type_1_path_table_size) +#define PVD_type_m_path_table_size 4 +#define PVD_opt_type_m_path_table_offset (PVD_type_m_path_table_offset + PVD_type_m_path_table_size) +#define PVD_opt_type_m_path_table_size 4 +#define PVD_root_directory_record_offset (PVD_opt_type_m_path_table_offset + PVD_opt_type_m_path_table_size) +#define PVD_root_directory_record_size 34 +#define PVD_volume_set_id_offset (PVD_root_directory_record_offset + PVD_root_directory_record_size) +#define PVD_volume_set_id_size 128 +#define PVD_publisher_id_offset (PVD_volume_set_id_offset + PVD_volume_set_id_size) +#define PVD_publisher_id_size 128 +#define PVD_preparer_id_offset (PVD_publisher_id_offset + PVD_publisher_id_size) +#define PVD_preparer_id_size 128 +#define PVD_application_id_offset (PVD_preparer_id_offset + PVD_preparer_id_size) +#define PVD_application_id_size 128 +#define PVD_copyright_file_id_offset (PVD_application_id_offset + PVD_application_id_size) +#define PVD_copyright_file_id_size 37 +#define PVD_abstract_file_id_offset (PVD_copyright_file_id_offset + PVD_copyright_file_id_size) +#define PVD_abstract_file_id_size 37 +#define PVD_bibliographic_file_id_offset (PVD_abstract_file_id_offset + PVD_abstract_file_id_size) +#define PVD_bibliographic_file_id_size 37 +#define PVD_creation_date_offset (PVD_bibliographic_file_id_offset + PVD_bibliographic_file_id_size) +#define PVD_creation_date_size 17 +#define PVD_modification_date_offset (PVD_creation_date_offset + PVD_creation_date_size) +#define PVD_modification_date_size 17 +#define PVD_expiration_date_offset (PVD_modification_date_offset + PVD_modification_date_size) +#define PVD_expiration_date_size 17 +#define PVD_effective_date_offset (PVD_expiration_date_offset + PVD_expiration_date_size) +#define PVD_effective_date_size 17 +#define PVD_file_structure_version_offset (PVD_effective_date_offset + PVD_effective_date_size) +#define PVD_file_structure_version_size 1 +#define PVD_reserved4_offset (PVD_file_structure_version_offset + PVD_file_structure_version_size) +#define PVD_reserved4_size 1 +#define PVD_application_data_offset (PVD_reserved4_offset + PVD_reserved4_size) +#define PVD_application_data_size 512 + +/* Structure of an on-disk directory record. */ +/* Note: ISO9660 stores each multi-byte integer twice, once in + * each byte order. The sizes here are the size of just one + * of the two integers. (This is why the offset of a field isn't + * the same as the offset+size of the previous field.) */ +#define DR_length_offset 0 +#define DR_length_size 1 +#define DR_ext_attr_length_offset 1 +#define DR_ext_attr_length_size 1 +#define DR_extent_offset 2 +#define DR_extent_size 4 +#define DR_size_offset 10 +#define DR_size_size 4 +#define DR_date_offset 18 +#define DR_date_size 7 +#define DR_flags_offset 25 +#define DR_flags_size 1 +#define DR_file_unit_size_offset 26 +#define DR_file_unit_size_size 1 +#define DR_interleave_offset 27 +#define DR_interleave_size 1 +#define DR_volume_sequence_number_offset 28 +#define DR_volume_sequence_number_size 2 +#define DR_name_len_offset 32 +#define DR_name_len_size 1 +#define DR_name_offset 33 + +/* + * Our private data. + */ + +/* In-memory storage for a directory record. */ +struct file_info { + struct file_info *parent; + int refcount; + uint64_t offset; /* Offset on disk. */ + uint64_t size; /* File size in bytes. */ + uint64_t ce_offset; /* Offset of CE */ + uint64_t ce_size; /* Size of CE */ + time_t mtime; /* File last modified time. */ + time_t atime; /* File last accessed time. */ + time_t ctime; /* File creation time. */ + mode_t mode; + uid_t uid; + gid_t gid; + ino_t inode; + int nlinks; + char *name; /* Null-terminated filename. */ + struct archive_string symlink; +}; + + +struct iso9660 { + int magic; +#define ISO9660_MAGIC 0x96609660 + int bid; /* If non-zero, return this as our bid. */ + struct archive_string pathname; + char seenRockridge; /* Set true if RR extensions are used. */ + unsigned char suspOffset; + + uint64_t previous_offset; + uint64_t previous_size; + struct archive_string previous_pathname; + + /* TODO: Make this a heap for fast inserts and deletions. */ + struct file_info **pending_files; + int pending_files_allocated; + int pending_files_used; + + uint64_t current_position; + ssize_t logical_block_size; + + off_t entry_sparse_offset; + int64_t entry_bytes_remaining; +}; + +static void add_entry(struct iso9660 *iso9660, struct file_info *file); +static int archive_read_format_iso9660_bid(struct archive_read *); +static int archive_read_format_iso9660_cleanup(struct archive_read *); +static int archive_read_format_iso9660_read_data(struct archive_read *, + const void **, size_t *, off_t *); +static int archive_read_format_iso9660_read_data_skip(struct archive_read *); +static int archive_read_format_iso9660_read_header(struct archive_read *, + struct archive_entry *); +static const char *build_pathname(struct archive_string *, struct file_info *); +static void dump_isodirrec(FILE *, const unsigned char *isodirrec); +static time_t time_from_tm(struct tm *); +static time_t isodate17(const unsigned char *); +static time_t isodate7(const unsigned char *); +static int isPVD(struct iso9660 *, const unsigned char *); +static struct file_info *next_entry(struct iso9660 *); +static int next_entry_seek(struct archive_read *a, struct iso9660 *iso9660, + struct file_info **pfile); +static struct file_info * + parse_file_info(struct iso9660 *iso9660, + struct file_info *parent, const unsigned char *isodirrec); +static void parse_rockridge(struct iso9660 *iso9660, + struct file_info *file, const unsigned char *start, + const unsigned char *end); +static void release_file(struct iso9660 *, struct file_info *); +static unsigned toi(const void *p, int n); + +int +archive_read_support_format_iso9660(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + struct iso9660 *iso9660; + int r; + + iso9660 = (struct iso9660 *)malloc(sizeof(*iso9660)); + if (iso9660 == NULL) { + archive_set_error(&a->archive, ENOMEM, "Can't allocate iso9660 data"); + return (ARCHIVE_FATAL); + } + memset(iso9660, 0, sizeof(*iso9660)); + iso9660->magic = ISO9660_MAGIC; + iso9660->bid = -1; /* We haven't yet bid. */ + + r = __archive_read_register_format(a, + iso9660, + archive_read_format_iso9660_bid, + archive_read_format_iso9660_read_header, + archive_read_format_iso9660_read_data, + archive_read_format_iso9660_read_data_skip, + archive_read_format_iso9660_cleanup); + + if (r != ARCHIVE_OK) { + free(iso9660); + return (r); + } + return (ARCHIVE_OK); +} + + +static int +archive_read_format_iso9660_bid(struct archive_read *a) +{ + struct iso9660 *iso9660; + ssize_t bytes_read; + const void *h; + const unsigned char *p; + + iso9660 = (struct iso9660 *)(a->format->data); + + if (iso9660->bid >= 0) + return (iso9660->bid); + + /* + * Skip the first 32k (reserved area) and get the first + * 8 sectors of the volume descriptor table. Of course, + * if the I/O layer gives us more, we'll take it. + */ + bytes_read = (a->decompressor->read_ahead)(a, &h, 32768 + 8*2048); + if (bytes_read < 32768 + 8*2048) + return (iso9660->bid = -1); + p = (const unsigned char *)h; + + /* Skip the reserved area. */ + bytes_read -= 32768; + p += 32768; + + /* Check each volume descriptor to locate the PVD. */ + for (; bytes_read > 2048; bytes_read -= 2048, p += 2048) { + iso9660->bid = isPVD(iso9660, p); + if (iso9660->bid > 0) + return (iso9660->bid); + if (*p == '\177') /* End-of-volume-descriptor marker. */ + break; + } + + /* We didn't find a valid PVD; return a bid of zero. */ + iso9660->bid = 0; + return (iso9660->bid); +} + +static int +isPVD(struct iso9660 *iso9660, const unsigned char *h) +{ + struct file_info *file; + + if (h[0] != 1) + return (0); + if (memcmp(h+1, "CD001", 5) != 0) + return (0); + + iso9660->logical_block_size = toi(h + PVD_logical_block_size_offset, 2); + + /* Store the root directory in the pending list. */ + file = parse_file_info(iso9660, NULL, h + PVD_root_directory_record_offset); + add_entry(iso9660, file); + return (48); +} + +static int +archive_read_format_iso9660_read_header(struct archive_read *a, + struct archive_entry *entry) +{ + struct iso9660 *iso9660; + struct file_info *file; + ssize_t bytes_read; + int r; + + iso9660 = (struct iso9660 *)(a->format->data); + + if (!a->archive.archive_format) { + a->archive.archive_format = ARCHIVE_FORMAT_ISO9660; + a->archive.archive_format_name = "ISO9660"; + } + + /* Get the next entry that appears after the current offset. */ + r = next_entry_seek(a, iso9660, &file); + if (r != ARCHIVE_OK) + return (r); + + iso9660->entry_bytes_remaining = file->size; + iso9660->entry_sparse_offset = 0; /* Offset for sparse-file-aware clients. */ + + /* Set up the entry structure with information about this entry. */ + archive_entry_set_mode(entry, file->mode); + archive_entry_set_uid(entry, file->uid); + archive_entry_set_gid(entry, file->gid); + archive_entry_set_nlink(entry, file->nlinks); + archive_entry_set_ino(entry, file->inode); + archive_entry_set_mtime(entry, file->mtime, 0); + archive_entry_set_ctime(entry, file->ctime, 0); + archive_entry_set_atime(entry, file->atime, 0); + archive_entry_set_size(entry, iso9660->entry_bytes_remaining); + archive_string_empty(&iso9660->pathname); + archive_entry_set_pathname(entry, + build_pathname(&iso9660->pathname, file)); + if (file->symlink.s != NULL) + archive_entry_copy_symlink(entry, file->symlink.s); + + /* If this entry points to the same data as the previous + * entry, convert this into a hardlink to that entry. + * But don't bother for zero-length files. */ + if (file->offset == iso9660->previous_offset + && file->size == iso9660->previous_size + && file->size > 0) { + archive_entry_set_hardlink(entry, + iso9660->previous_pathname.s); + iso9660->entry_bytes_remaining = 0; + iso9660->entry_sparse_offset = 0; + release_file(iso9660, file); + return (ARCHIVE_OK); + } + + /* If the offset is before our current position, we can't + * seek backwards to extract it, so issue a warning. */ + if (file->offset < iso9660->current_position) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Ignoring out-of-order file"); + iso9660->entry_bytes_remaining = 0; + iso9660->entry_sparse_offset = 0; + release_file(iso9660, file); + return (ARCHIVE_WARN); + } + + iso9660->previous_size = file->size; + iso9660->previous_offset = file->offset; + archive_strcpy(&iso9660->previous_pathname, iso9660->pathname.s); + + /* If this is a directory, read in all of the entries right now. */ + if (archive_entry_filetype(entry) == AE_IFDIR) { + while (iso9660->entry_bytes_remaining > 0) { + const void *block; + const unsigned char *p; + ssize_t step = iso9660->logical_block_size; + if (step > iso9660->entry_bytes_remaining) + step = iso9660->entry_bytes_remaining; + bytes_read = (a->decompressor->read_ahead)(a, &block, step); + if (bytes_read < step) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Failed to read full block when scanning ISO9660 directory list"); + release_file(iso9660, file); + return (ARCHIVE_FATAL); + } + if (bytes_read > step) + bytes_read = step; + (a->decompressor->consume)(a, bytes_read); + iso9660->current_position += bytes_read; + iso9660->entry_bytes_remaining -= bytes_read; + for (p = (const unsigned char *)block; + *p != 0 && p < (const unsigned char *)block + bytes_read; + p += *p) { + struct file_info *child; + + /* Skip '.' entry. */ + if (*(p + DR_name_len_offset) == 1 + && *(p + DR_name_offset) == '\0') + continue; + /* Skip '..' entry. */ + if (*(p + DR_name_len_offset) == 1 + && *(p + DR_name_offset) == '\001') + continue; + child = parse_file_info(iso9660, file, p); + add_entry(iso9660, child); + if (iso9660->seenRockridge) { + a->archive.archive_format = + ARCHIVE_FORMAT_ISO9660_ROCKRIDGE; + a->archive.archive_format_name = + "ISO9660 with Rockridge extensions"; + } + } + } + } + + release_file(iso9660, file); + return (ARCHIVE_OK); +} + +static int +archive_read_format_iso9660_read_data_skip(struct archive_read *a) +{ + /* Because read_next_header always does an explicit skip + * to the next entry, we don't need to do anything here. */ + (void)a; /* UNUSED */ + return (ARCHIVE_OK); +} + +static int +archive_read_format_iso9660_read_data(struct archive_read *a, + const void **buff, size_t *size, off_t *offset) +{ + ssize_t bytes_read; + struct iso9660 *iso9660; + + iso9660 = (struct iso9660 *)(a->format->data); + if (iso9660->entry_bytes_remaining <= 0) { + *buff = NULL; + *size = 0; + *offset = iso9660->entry_sparse_offset; + return (ARCHIVE_EOF); + } + + bytes_read = (a->decompressor->read_ahead)(a, buff, 1); + if (bytes_read == 0) + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Truncated input file"); + if (bytes_read <= 0) + return (ARCHIVE_FATAL); + if (bytes_read > iso9660->entry_bytes_remaining) + bytes_read = iso9660->entry_bytes_remaining; + *size = bytes_read; + *offset = iso9660->entry_sparse_offset; + iso9660->entry_sparse_offset += bytes_read; + iso9660->entry_bytes_remaining -= bytes_read; + iso9660->current_position += bytes_read; + (a->decompressor->consume)(a, bytes_read); + return (ARCHIVE_OK); +} + +static int +archive_read_format_iso9660_cleanup(struct archive_read *a) +{ + struct iso9660 *iso9660; + struct file_info *file; + + iso9660 = (struct iso9660 *)(a->format->data); + while ((file = next_entry(iso9660)) != NULL) + release_file(iso9660, file); + archive_string_free(&iso9660->pathname); + archive_string_free(&iso9660->previous_pathname); + if (iso9660->pending_files) + free(iso9660->pending_files); + free(iso9660); + (a->format->data) = NULL; + return (ARCHIVE_OK); +} + +/* + * This routine parses a single ISO directory record, makes sense + * of any extensions, and stores the result in memory. + */ +static struct file_info * +parse_file_info(struct iso9660 *iso9660, struct file_info *parent, + const unsigned char *isodirrec) +{ + struct file_info *file; + size_t name_len; + int flags; + + /* TODO: Sanity check that name_len doesn't exceed length, etc. */ + + /* Create a new file entry and copy data from the ISO dir record. */ + file = (struct file_info *)malloc(sizeof(*file)); + if (file == NULL) + return (NULL); + memset(file, 0, sizeof(*file)); + file->parent = parent; + if (parent != NULL) + parent->refcount++; + file->offset = toi(isodirrec + DR_extent_offset, DR_extent_size) + * iso9660->logical_block_size; + file->size = toi(isodirrec + DR_size_offset, DR_size_size); + file->mtime = isodate7(isodirrec + DR_date_offset); + file->ctime = file->atime = file->mtime; + name_len = (size_t)*(const unsigned char *)(isodirrec + DR_name_len_offset); + file->name = (char *)malloc(name_len + 1); + if (file->name == NULL) { + free(file); + return (NULL); + } + memcpy(file->name, isodirrec + DR_name_offset, name_len); + file->name[name_len] = '\0'; + flags = *(isodirrec + DR_flags_offset); + if (flags & 0x02) + file->mode = AE_IFDIR | 0700; + else + file->mode = AE_IFREG | 0400; + + /* Rockridge extensions overwrite information from above. */ + { + const unsigned char *rr_start, *rr_end; + rr_end = (const unsigned char *)isodirrec + + *(isodirrec + DR_length_offset); + rr_start = (const unsigned char *)(isodirrec + DR_name_offset + + name_len); + if ((name_len & 1) == 0) + rr_start++; + rr_start += iso9660->suspOffset; + parse_rockridge(iso9660, file, rr_start, rr_end); + } + + /* DEBUGGING: Warn about attributes I don't yet fully support. */ + if ((flags & ~0x02) != 0) { + fprintf(stderr, "\n ** Unrecognized flag: "); + dump_isodirrec(stderr, isodirrec); + fprintf(stderr, "\n"); + } else if (toi(isodirrec + DR_volume_sequence_number_offset, 2) != 1) { + fprintf(stderr, "\n ** Unrecognized sequence number: "); + dump_isodirrec(stderr, isodirrec); + fprintf(stderr, "\n"); + } else if (*(isodirrec + DR_file_unit_size_offset) != 0) { + fprintf(stderr, "\n ** Unexpected file unit size: "); + dump_isodirrec(stderr, isodirrec); + fprintf(stderr, "\n"); + } else if (*(isodirrec + DR_interleave_offset) != 0) { + fprintf(stderr, "\n ** Unexpected interleave: "); + dump_isodirrec(stderr, isodirrec); + fprintf(stderr, "\n"); + } else if (*(isodirrec + DR_ext_attr_length_offset) != 0) { + fprintf(stderr, "\n ** Unexpected extended attribute length: "); + dump_isodirrec(stderr, isodirrec); + fprintf(stderr, "\n"); + } + + return (file); +} + +static void +add_entry(struct iso9660 *iso9660, struct file_info *file) +{ + /* Expand our pending files list as necessary. */ + if (iso9660->pending_files_used >= iso9660->pending_files_allocated) { + struct file_info **new_pending_files; + int new_size = iso9660->pending_files_allocated * 2; + + if (new_size < 1024) + new_size = 1024; + new_pending_files = (struct file_info **)malloc(new_size * sizeof(new_pending_files[0])); + if (new_pending_files == NULL) + __archive_errx(1, "Out of memory"); + memcpy(new_pending_files, iso9660->pending_files, + iso9660->pending_files_allocated * sizeof(new_pending_files[0])); + if (iso9660->pending_files != NULL) + free(iso9660->pending_files); + iso9660->pending_files = new_pending_files; + iso9660->pending_files_allocated = new_size; + } + + iso9660->pending_files[iso9660->pending_files_used++] = file; +} + +static void +parse_rockridge(struct iso9660 *iso9660, struct file_info *file, + const unsigned char *p, const unsigned char *end) +{ + (void)iso9660; /* UNUSED */ + + while (p + 4 < end /* Enough space for another entry. */ + && p[0] >= 'A' && p[0] <= 'Z' /* Sanity-check 1st char of name. */ + && p[1] >= 'A' && p[1] <= 'Z' /* Sanity-check 2nd char of name. */ + && p + p[2] <= end) { /* Sanity-check length. */ + const unsigned char *data = p + 4; + int data_length = p[2] - 4; + int version = p[3]; + + /* + * Yes, each 'if' here does test p[0] again. + * Otherwise, the fall-through handling to catch + * unsupported extensions doesn't work. + */ + switch(p[0]) { + case 'C': + if (p[0] == 'C' && p[1] == 'E' && version == 1) { + /* + * CE extension comprises: + * 8 byte sector containing extension + * 8 byte offset w/in above sector + * 8 byte length of continuation + */ + file->ce_offset = toi(data, 4) + * iso9660->logical_block_size + + toi(data + 8, 4); + file->ce_size = toi(data + 16, 4); + break; + } + /* FALLTHROUGH */ + case 'N': + if (p[0] == 'N' && p[1] == 'M' && version == 1 + && *data == 0) { + /* NM extension with flag byte == 0 */ + /* + * NM extension comprises: + * one byte flag + * rest is long name + */ + /* TODO: Obey flags. */ + char *old_name = file->name; + + data++; /* Skip flag byte. */ + data_length--; + file->name = (char *)malloc(data_length + 1); + if (file->name != NULL) { + free(old_name); + memcpy(file->name, data, data_length); + file->name[data_length] = '\0'; + } else + file->name = old_name; + break; + } + /* FALLTHROUGH */ + case 'P': + if (p[0] == 'P' && p[1] == 'D' && version == 1) { + /* + * PD extension is padding; + * contents are always ignored. + */ + break; + } + if (p[0] == 'P' && p[1] == 'X' && version == 1) { + /* + * PX extension comprises: + * 8 bytes for mode, + * 8 bytes for nlinks, + * 8 bytes for uid, + * 8 bytes for gid, + * 8 bytes for inode. + */ + if (data_length == 32) { + file->mode = toi(data, 4); + file->nlinks = toi(data + 8, 4); + file->uid = toi(data + 16, 4); + file->gid = toi(data + 24, 4); + file->inode = toi(data + 32, 4); + } + break; + } + /* FALLTHROUGH */ + case 'R': + if (p[0] == 'R' && p[1] == 'R' && version == 1) { + iso9660->seenRockridge = 1; + /* + * RR extension comprises: + * one byte flag value + */ + /* TODO: Handle RR extension. */ + break; + } + /* FALLTHROUGH */ + case 'S': + if (p[0] == 'S' && p[1] == 'L' && version == 1 + && *data == 0) { + int cont = 1; + /* SL extension with flags == 0 */ + /* TODO: handle non-zero flag values. */ + data++; /* Skip flag byte. */ + data_length--; + while (data_length > 0) { + unsigned char flag = *data++; + unsigned char nlen = *data++; + data_length -= 2; + + if (cont == 0) + archive_strcat(&file->symlink, "/"); + cont = 0; + + switch(flag) { + case 0x01: /* Continue */ + archive_strncat(&file->symlink, + (const char *)data, nlen); + cont = 1; + break; + case 0x02: /* Current */ + archive_strcat(&file->symlink, "."); + break; + case 0x04: /* Parent */ + archive_strcat(&file->symlink, ".."); + break; + case 0x08: /* Root */ + case 0x10: /* Volume root */ + archive_string_empty(&file->symlink); + break; + case 0x20: /* Hostname */ + archive_strcat(&file->symlink, "hostname"); + break; + case 0: + archive_strncat(&file->symlink, + (const char *)data, nlen); + break; + default: + /* TODO: issue a warning ? */ + break; + } + data += nlen; + data_length -= nlen; + } + break; + } + if (p[0] == 'S' && p[1] == 'P' + && version == 1 && data_length == 7 + && data[0] == (unsigned char)'\xbe' + && data[1] == (unsigned char)'\xef') { + /* + * SP extension stores the suspOffset + * (Number of bytes to skip between + * filename and SUSP records.) + * It is mandatory by the SUSP standard + * (IEEE 1281). + * + * It allows SUSP to coexist with + * non-SUSP uses of the System + * Use Area by placing non-SUSP data + * before SUSP data. + * + * TODO: Add a check for 'SP' in + * first directory entry, disable all SUSP + * processing if not found. + */ + iso9660->suspOffset = data[2]; + break; + } + if (p[0] == 'S' && p[1] == 'T' + && data_length == 0 && version == 1) { + /* + * ST extension marks end of this + * block of SUSP entries. + * + * It allows SUSP to coexist with + * non-SUSP uses of the System + * Use Area by placing non-SUSP data + * after SUSP data. + */ + return; + } + case 'T': + if (p[0] == 'T' && p[1] == 'F' && version == 1) { + char flag = data[0]; + /* + * TF extension comprises: + * one byte flag + * create time (optional) + * modify time (optional) + * access time (optional) + * attribute time (optional) + * Time format and presence of fields + * is controlled by flag bits. + */ + data++; + if (flag & 0x80) { + /* Use 17-byte time format. */ + if (flag & 1) /* Create time. */ + data += 17; + if (flag & 2) { /* Modify time. */ + file->mtime = isodate17(data); + data += 17; + } + if (flag & 4) { /* Access time. */ + file->atime = isodate17(data); + data += 17; + } + if (flag & 8) { /* Attribute time. */ + file->ctime = isodate17(data); + data += 17; + } + } else { + /* Use 7-byte time format. */ + if (flag & 1) /* Create time. */ + data += 7; + if (flag & 2) { /* Modify time. */ + file->mtime = isodate7(data); + data += 7; + } + if (flag & 4) { /* Access time. */ + file->atime = isodate7(data); + data += 7; + } + if (flag & 8) { /* Attribute time. */ + file->ctime = isodate7(data); + data += 7; + } + } + break; + } + /* FALLTHROUGH */ + default: + /* The FALLTHROUGHs above leave us here for + * any unsupported extension. */ + { + const unsigned char *t; + fprintf(stderr, "\nUnsupported RRIP extension for %s\n", file->name); + fprintf(stderr, " %c%c(%d):", p[0], p[1], data_length); + for (t = data; t < data + data_length && t < data + 16; t++) + fprintf(stderr, " %02x", *t); + fprintf(stderr, "\n"); + } + } + + + + p += p[2]; + } +} + +static void +release_file(struct iso9660 *iso9660, struct file_info *file) +{ + struct file_info *parent; + + if (file->refcount == 0) { + parent = file->parent; + if (file->name) + free(file->name); + archive_string_free(&file->symlink); + free(file); + if (parent != NULL) { + parent->refcount--; + release_file(iso9660, parent); + } + } +} + +static int +next_entry_seek(struct archive_read *a, struct iso9660 *iso9660, + struct file_info **pfile) +{ + struct file_info *file; + uint64_t offset; + + *pfile = NULL; + for (;;) { + *pfile = file = next_entry(iso9660); + if (file == NULL) + return (ARCHIVE_EOF); + + /* CE area precedes actual file data? Ignore it. */ + if (file->ce_offset > file->offset) { +fprintf(stderr, " *** Discarding CE data.\n"); + file->ce_offset = 0; + file->ce_size = 0; + } + + /* If CE exists, find and read it now. */ + if (file->ce_offset > 0) + offset = file->ce_offset; + else + offset = file->offset; + + /* Seek forward to the start of the entry. */ + if (iso9660->current_position < offset) { + off_t step = offset - iso9660->current_position; + off_t bytes_read; + bytes_read = (a->decompressor->skip)(a, step); + if (bytes_read < 0) + return (bytes_read); + iso9660->current_position = offset; + } + + /* We found body of file; handle it now. */ + if (offset == file->offset) + return (ARCHIVE_OK); + + /* Found CE? Process it and push the file back onto list. */ + if (offset == file->ce_offset) { + const void *p; + ssize_t size = file->ce_size; + ssize_t bytes_read; + const unsigned char *rr_start; + + file->ce_offset = 0; + file->ce_size = 0; + bytes_read = (a->decompressor->read_ahead)(a, &p, size); + if (bytes_read > size) + bytes_read = size; + rr_start = (const unsigned char *)p; + parse_rockridge(iso9660, file, rr_start, + rr_start + bytes_read); + (a->decompressor->consume)(a, bytes_read); + iso9660->current_position += bytes_read; + add_entry(iso9660, file); + } + } +} + +static struct file_info * +next_entry(struct iso9660 *iso9660) +{ + int least_index; + uint64_t least_end_offset; + int i; + struct file_info *r; + + if (iso9660->pending_files_used < 1) + return (NULL); + + /* Assume the first file in the list is the earliest on disk. */ + least_index = 0; + least_end_offset = iso9660->pending_files[0]->offset + + iso9660->pending_files[0]->size; + + /* Now, try to find an earlier one. */ + for (i = 0; i < iso9660->pending_files_used; i++) { + /* Use the position of the file *end* as our comparison. */ + uint64_t end_offset = iso9660->pending_files[i]->offset + + iso9660->pending_files[i]->size; + if (iso9660->pending_files[i]->ce_offset > 0 + && iso9660->pending_files[i]->ce_offset < iso9660->pending_files[i]->offset) + end_offset = iso9660->pending_files[i]->ce_offset + + iso9660->pending_files[i]->ce_size; + if (least_end_offset > end_offset) { + least_index = i; + least_end_offset = end_offset; + } + } + r = iso9660->pending_files[least_index]; + iso9660->pending_files[least_index] + = iso9660->pending_files[--iso9660->pending_files_used]; + return (r); +} + +static unsigned int +toi(const void *p, int n) +{ + const unsigned char *v = (const unsigned char *)p; + if (n > 1) + return v[0] + 256 * toi(v + 1, n - 1); + if (n == 1) + return v[0]; + return (0); +} + +static time_t +isodate7(const unsigned char *v) +{ + struct tm tm; + int offset; + memset(&tm, 0, sizeof(tm)); + tm.tm_year = v[0]; + tm.tm_mon = v[1] - 1; + tm.tm_mday = v[2]; + tm.tm_hour = v[3]; + tm.tm_min = v[4]; + tm.tm_sec = v[5]; + /* v[6] is the signed timezone offset, in 1/4-hour increments. */ + offset = ((const signed char *)v)[6]; + if (offset > -48 && offset < 52) { + tm.tm_hour -= offset / 4; + tm.tm_min -= (offset % 4) * 15; + } + return (time_from_tm(&tm)); +} + +static time_t +isodate17(const unsigned char *v) +{ + struct tm tm; + int offset; + memset(&tm, 0, sizeof(tm)); + tm.tm_year = (v[0] - '0') * 1000 + (v[1] - '0') * 100 + + (v[2] - '0') * 10 + (v[3] - '0') + - 1900; + tm.tm_mon = (v[4] - '0') * 10 + (v[5] - '0'); + tm.tm_mday = (v[6] - '0') * 10 + (v[7] - '0'); + tm.tm_hour = (v[8] - '0') * 10 + (v[9] - '0'); + tm.tm_min = (v[10] - '0') * 10 + (v[11] - '0'); + tm.tm_sec = (v[12] - '0') * 10 + (v[13] - '0'); + /* v[16] is the signed timezone offset, in 1/4-hour increments. */ + offset = ((const signed char *)v)[16]; + if (offset > -48 && offset < 52) { + tm.tm_hour -= offset / 4; + tm.tm_min -= (offset % 4) * 15; + } + return (time_from_tm(&tm)); +} + +/* + * timegm() converts a struct tm to a time_t, except it isn't standard, + * so I provide my own function here that (ideally) is just a wrapper + * for timegm(). + */ +static time_t +time_from_tm(struct tm *t) +{ +#if HAVE_TIMEGM + return (timegm(t)); +#elif HAVE_STRUCT_TM_TM_GMTOFF + /* + * Unfortunately, timegm() isn't standard. The standard + * mktime() function is a close match, except that it uses + * local timezone instead of GMT. You can compensate for + * this by adding the timezone and DST offsets back in, at + * the cost of two calls to mktime(). + */ + mktime(t); /* Normalize the time and get the TZ offset. */ + t->tm_sec += t->tm_gmtoff; /* Try to adjust for the timezone and DST.*/ + if (t->tm_isdst) + t->tm_hour -= 1; + return (mktime(t)); /* Re-convert. */ +#else + /* + * If you don't have tm_gmtoff, let's try resetting the timezone + * (yecch!). + */ + time_t ret; + char *tz; + + tz = getenv("TZ"); + setenv("TZ", "UTC 0", 1); + tzset(); + ret = mktime(t); + if (tz) + setenv("TZ", tz, 1); + else + unsetenv("TZ"); + tzset(); + return ret; +#endif +} + +static const char * +build_pathname(struct archive_string *as, struct file_info *file) +{ + if (file->parent != NULL && file->parent->name[0] != '\0') { + build_pathname(as, file->parent); + archive_strcat(as, "/"); + } + if (file->name[0] == '\0') + archive_strcat(as, "."); + else + archive_strcat(as, file->name); + return (as->s); +} + +static void +dump_isodirrec(FILE *out, const unsigned char *isodirrec) +{ + fprintf(out, " l %d,", + toi(isodirrec + DR_length_offset, DR_length_size)); + fprintf(out, " a %d,", + toi(isodirrec + DR_ext_attr_length_offset, DR_ext_attr_length_size)); + fprintf(out, " ext 0x%x,", + toi(isodirrec + DR_extent_offset, DR_extent_size)); + fprintf(out, " s %d,", + toi(isodirrec + DR_size_offset, DR_extent_size)); + fprintf(out, " f 0x%02x,", + toi(isodirrec + DR_flags_offset, DR_flags_size)); + fprintf(out, " u %d,", + toi(isodirrec + DR_file_unit_size_offset, DR_file_unit_size_size)); + fprintf(out, " ilv %d,", + toi(isodirrec + DR_interleave_offset, DR_interleave_size)); + fprintf(out, " seq %d,", + toi(isodirrec + DR_volume_sequence_number_offset, DR_volume_sequence_number_size)); + fprintf(out, " nl %d:", + toi(isodirrec + DR_name_len_offset, DR_name_len_size)); + fprintf(out, " `%.*s'", + toi(isodirrec + DR_name_len_offset, DR_name_len_size), isodirrec + DR_name_offset); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_read_support_format_tar.c b/contrib/libarchive-2.1/libarchive/archive_read_support_format_tar.c new file mode 100644 index 0000000000..c2243b1fcf --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_read_support_format_tar.c @@ -0,0 +1,1907 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_tar.c,v 1.54 2007/04/15 00:53:38 kientzle Exp $"); + +#ifdef HAVE_ERRNO_H +#include +#endif +#include +/* #include */ /* See archive_platform.h */ +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +/* Obtain suitable wide-character manipulation functions. */ +#ifdef HAVE_WCHAR_H +#include +#else +/* Good enough for equality testing, which is all we need. */ +static int wcscmp(const wchar_t *s1, const wchar_t *s2) +{ + int diff = *s1 - *s2; + while (*s1 && diff == 0) + diff = (int)*++s1 - (int)*++s2; + return diff; +} +/* Good enough for equality testing, which is all we need. */ +static int wcsncmp(const wchar_t *s1, const wchar_t *s2, size_t n) +{ + int diff = *s1 - *s2; + while (*s1 && diff == 0 && n-- > 0) + diff = (int)*++s1 - (int)*++s2; + return diff; +} +static size_t wcslen(const wchar_t *s) +{ + const wchar_t *p = s; + while (*p) + p++; + return p - s; +} +#endif + +#include "archive.h" +#include "archive_entry.h" +#include "archive_private.h" +#include "archive_read_private.h" + +/* + * Layout of POSIX 'ustar' tar header. + */ +struct archive_entry_header_ustar { + char name[100]; + char mode[8]; + char uid[8]; + char gid[8]; + char size[12]; + char mtime[12]; + char checksum[8]; + char typeflag[1]; + char linkname[100]; /* "old format" header ends here */ + char magic[6]; /* For POSIX: "ustar\0" */ + char version[2]; /* For POSIX: "00" */ + char uname[32]; + char gname[32]; + char rdevmajor[8]; + char rdevminor[8]; + char prefix[155]; +}; + +/* + * Structure of GNU tar header + */ +struct gnu_sparse { + char offset[12]; + char numbytes[12]; +}; + +struct archive_entry_header_gnutar { + char name[100]; + char mode[8]; + char uid[8]; + char gid[8]; + char size[12]; + char mtime[12]; + char checksum[8]; + char typeflag[1]; + char linkname[100]; + char magic[8]; /* "ustar \0" (note blank/blank/null at end) */ + char uname[32]; + char gname[32]; + char rdevmajor[8]; + char rdevminor[8]; + char atime[12]; + char ctime[12]; + char offset[12]; + char longnames[4]; + char unused[1]; + struct gnu_sparse sparse[4]; + char isextended[1]; + char realsize[12]; + /* + * GNU doesn't use POSIX 'prefix' field; they use the 'L' (longname) + * entry instead. + */ +}; + +/* + * Data specific to this format. + */ +struct sparse_block { + struct sparse_block *next; + off_t offset; + off_t remaining; +}; + +struct tar { + struct archive_string acl_text; + struct archive_string entry_name; + struct archive_string entry_linkname; + struct archive_string entry_uname; + struct archive_string entry_gname; + struct archive_string longlink; + struct archive_string longname; + struct archive_string pax_header; + struct archive_string pax_global; + wchar_t *pax_entry; + size_t pax_entry_length; + int header_recursion_depth; + off_t entry_bytes_remaining; + off_t entry_offset; + off_t entry_padding; + struct sparse_block *sparse_list; +}; + +static size_t UTF8_mbrtowc(wchar_t *pwc, const char *s, size_t n); +static int archive_block_is_null(const unsigned char *p); +static char *base64_decode(const wchar_t *, size_t, size_t *); +static int gnu_read_sparse_data(struct archive_read *, struct tar *, + const struct archive_entry_header_gnutar *header); +static void gnu_parse_sparse_data(struct archive_read *, struct tar *, + const struct gnu_sparse *sparse, int length); +static int header_Solaris_ACL(struct archive_read *, struct tar *, + struct archive_entry *, const void *); +static int header_common(struct archive_read *, struct tar *, + struct archive_entry *, const void *); +static int header_old_tar(struct archive_read *, struct tar *, + struct archive_entry *, const void *); +static int header_pax_extensions(struct archive_read *, struct tar *, + struct archive_entry *, const void *); +static int header_pax_global(struct archive_read *, struct tar *, + struct archive_entry *, const void *h); +static int header_longlink(struct archive_read *, struct tar *, + struct archive_entry *, const void *h); +static int header_longname(struct archive_read *, struct tar *, + struct archive_entry *, const void *h); +static int header_volume(struct archive_read *, struct tar *, + struct archive_entry *, const void *h); +static int header_ustar(struct archive_read *, struct tar *, + struct archive_entry *, const void *h); +static int header_gnutar(struct archive_read *, struct tar *, + struct archive_entry *, const void *h); +static int archive_read_format_tar_bid(struct archive_read *); +static int archive_read_format_tar_cleanup(struct archive_read *); +static int archive_read_format_tar_read_data(struct archive_read *a, + const void **buff, size_t *size, off_t *offset); +static int archive_read_format_tar_skip(struct archive_read *a); +static int archive_read_format_tar_read_header(struct archive_read *, + struct archive_entry *); +static int checksum(struct archive_read *, const void *); +static int pax_attribute(struct archive_entry *, + wchar_t *key, wchar_t *value); +static int pax_header(struct archive_read *, struct tar *, + struct archive_entry *, char *attr); +static void pax_time(const wchar_t *, int64_t *sec, long *nanos); +static int read_body_to_string(struct archive_read *, struct tar *, + struct archive_string *, const void *h); +static int64_t tar_atol(const char *, unsigned); +static int64_t tar_atol10(const wchar_t *, unsigned); +static int64_t tar_atol256(const char *, unsigned); +static int64_t tar_atol8(const char *, unsigned); +static int tar_read_header(struct archive_read *, struct tar *, + struct archive_entry *); +static int tohex(int c); +static char *url_decode(const char *); +static int utf8_decode(wchar_t *, const char *, size_t length); +static char *wide_to_narrow(const wchar_t *wval); + +int +archive_read_support_format_gnutar(struct archive *a) +{ + return (archive_read_support_format_tar(a)); +} + + +int +archive_read_support_format_tar(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + struct tar *tar; + int r; + + tar = (struct tar *)malloc(sizeof(*tar)); + if (tar == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate tar data"); + return (ARCHIVE_FATAL); + } + memset(tar, 0, sizeof(*tar)); + + r = __archive_read_register_format(a, tar, + archive_read_format_tar_bid, + archive_read_format_tar_read_header, + archive_read_format_tar_read_data, + archive_read_format_tar_skip, + archive_read_format_tar_cleanup); + + if (r != ARCHIVE_OK) + free(tar); + return (ARCHIVE_OK); +} + +static int +archive_read_format_tar_cleanup(struct archive_read *a) +{ + struct tar *tar; + + tar = (struct tar *)(a->format->data); + archive_string_free(&tar->acl_text); + archive_string_free(&tar->entry_name); + archive_string_free(&tar->entry_linkname); + archive_string_free(&tar->entry_uname); + archive_string_free(&tar->entry_gname); + archive_string_free(&tar->pax_global); + archive_string_free(&tar->pax_header); + if (tar->pax_entry != NULL) + free(tar->pax_entry); + free(tar); + (a->format->data) = NULL; + return (ARCHIVE_OK); +} + + +static int +archive_read_format_tar_bid(struct archive_read *a) +{ + int bid; + ssize_t bytes_read; + const void *h; + const struct archive_entry_header_ustar *header; + + /* + * If we're already reading a non-tar file, don't + * bother to bid. + */ + if (a->archive.archive_format != 0 && + (a->archive.archive_format & ARCHIVE_FORMAT_BASE_MASK) != + ARCHIVE_FORMAT_TAR) + return (0); + bid = 0; + + /* + * If we're already reading a tar format, start the bid at 1 as + * a failsafe. + */ + if ((a->archive.archive_format & ARCHIVE_FORMAT_BASE_MASK) == + ARCHIVE_FORMAT_TAR) + bid++; + + /* Now let's look at the actual header and see if it matches. */ + if (a->decompressor->read_ahead != NULL) + bytes_read = (a->decompressor->read_ahead)(a, &h, 512); + else + bytes_read = 0; /* Empty file. */ + if (bytes_read < 0) + return (ARCHIVE_FATAL); + if (bytes_read == 0 && bid > 0) { + /* An archive without a proper end-of-archive marker. */ + /* Hold our nose and bid 1 anyway. */ + return (1); + } + if (bytes_read < 512) { + /* If it's a new archive, then just return a zero bid. */ + if (bid == 0) + return (0); + /* + * If we already know this is a tar archive, + * then we have a problem. + */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated tar archive"); + return (ARCHIVE_FATAL); + } + + /* If it's an end-of-archive mark, we can handle it. */ + if ((*(const char *)h) == 0 && archive_block_is_null((const unsigned char *)h)) { + /* If it's a known tar file, end-of-archive is definite. */ + if ((a->archive.archive_format & ARCHIVE_FORMAT_BASE_MASK) == + ARCHIVE_FORMAT_TAR) + return (512); + /* Empty archive? */ + return (1); + } + + /* If it's not an end-of-archive mark, it must have a valid checksum.*/ + if (!checksum(a, h)) + return (0); + bid += 48; /* Checksum is usually 6 octal digits. */ + + header = (const struct archive_entry_header_ustar *)h; + + /* Recognize POSIX formats. */ + if ((memcmp(header->magic, "ustar\0", 6) == 0) + &&(memcmp(header->version, "00", 2)==0)) + bid += 56; + + /* Recognize GNU tar format. */ + if ((memcmp(header->magic, "ustar ", 6) == 0) + &&(memcmp(header->version, " \0", 2)==0)) + bid += 56; + + /* Type flag must be null, digit or A-Z, a-z. */ + if (header->typeflag[0] != 0 && + !( header->typeflag[0] >= '0' && header->typeflag[0] <= '9') && + !( header->typeflag[0] >= 'A' && header->typeflag[0] <= 'Z') && + !( header->typeflag[0] >= 'a' && header->typeflag[0] <= 'z') ) + return (0); + bid += 2; /* 6 bits of variation in an 8-bit field leaves 2 bits. */ + + /* Sanity check: Look at first byte of mode field. */ + switch (255 & (unsigned)header->mode[0]) { + case 0: case 255: + /* Base-256 value: No further verification possible! */ + break; + case ' ': /* Not recommended, but not illegal, either. */ + break; + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + /* Octal Value. */ + /* TODO: Check format of remainder of this field. */ + break; + default: + /* Not a valid mode; bail out here. */ + return (0); + } + /* TODO: Sanity test uid/gid/size/mtime/rdevmajor/rdevminor fields. */ + + return (bid); +} + +/* + * The function invoked by archive_read_header(). This + * just sets up a few things and then calls the internal + * tar_read_header() function below. + */ +static int +archive_read_format_tar_read_header(struct archive_read *a, + struct archive_entry *entry) +{ + /* + * When converting tar archives to cpio archives, it is + * essential that each distinct file have a distinct inode + * number. To simplify this, we keep a static count here to + * assign fake dev/inode numbers to each tar entry. Note that + * pax format archives may overwrite this with something more + * useful. + * + * Ideally, we would track every file read from the archive so + * that we could assign the same dev/ino pair to hardlinks, + * but the memory required to store a complete lookup table is + * probably not worthwhile just to support the relatively + * obscure tar->cpio conversion case. + */ + static int default_inode; + static int default_dev; + struct tar *tar; + const char *p; + int r; + size_t l; + + /* Assign default device/inode values. */ + archive_entry_set_dev(entry, 1 + default_dev); /* Don't use zero. */ + archive_entry_set_ino(entry, ++default_inode); /* Don't use zero. */ + /* Limit generated st_ino number to 16 bits. */ + if (default_inode >= 0xffff) { + ++default_dev; + default_inode = 0; + } + + tar = (struct tar *)(a->format->data); + tar->entry_offset = 0; + + r = tar_read_header(a, tar, entry); + + if (r == ARCHIVE_OK) { + /* + * "Regular" entry with trailing '/' is really + * directory: This is needed for certain old tar + * variants and even for some broken newer ones. + */ + p = archive_entry_pathname(entry); + l = strlen(p); + if (archive_entry_filetype(entry) == AE_IFREG + && p[l-1] == '/') + archive_entry_set_filetype(entry, AE_IFDIR); + } + return (r); +} + +static int +archive_read_format_tar_read_data(struct archive_read *a, + const void **buff, size_t *size, off_t *offset) +{ + ssize_t bytes_read; + struct tar *tar; + struct sparse_block *p; + + tar = (struct tar *)(a->format->data); + if (tar->sparse_list != NULL) { + /* Remove exhausted entries from sparse list. */ + while (tar->sparse_list != NULL && + tar->sparse_list->remaining == 0) { + p = tar->sparse_list; + tar->sparse_list = p->next; + free(p); + } + if (tar->sparse_list == NULL) { + /* We exhausted the entire sparse list. */ + tar->entry_bytes_remaining = 0; + } + } + + if (tar->entry_bytes_remaining > 0) { + bytes_read = (a->decompressor->read_ahead)(a, buff, 1); + if (bytes_read == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Truncated tar archive"); + return (ARCHIVE_FATAL); + } + if (bytes_read < 0) + return (ARCHIVE_FATAL); + if (bytes_read > tar->entry_bytes_remaining) + bytes_read = tar->entry_bytes_remaining; + if (tar->sparse_list != NULL) { + /* Don't read more than is available in the + * current sparse block. */ + if (tar->sparse_list->remaining < bytes_read) + bytes_read = tar->sparse_list->remaining; + tar->entry_offset = tar->sparse_list->offset; + tar->sparse_list->remaining -= bytes_read; + tar->sparse_list->offset += bytes_read; + } + *size = bytes_read; + *offset = tar->entry_offset; + tar->entry_offset += bytes_read; + tar->entry_bytes_remaining -= bytes_read; + (a->decompressor->consume)(a, bytes_read); + return (ARCHIVE_OK); + } else { + if ((a->decompressor->skip)(a, tar->entry_padding) < 0) + return (ARCHIVE_FATAL); + tar->entry_padding = 0; + *buff = NULL; + *size = 0; + *offset = tar->entry_offset; + return (ARCHIVE_EOF); + } +} + +static int +archive_read_format_tar_skip(struct archive_read *a) +{ + off_t bytes_skipped; + struct tar* tar; + struct sparse_block *p; + + tar = (struct tar *)(a->format->data); + + /* + * Compression layer skip functions are required to either skip the + * length requested or fail, so we can rely upon the entire entry + * plus padding being skipped. + */ + bytes_skipped = (a->decompressor->skip)(a, tar->entry_bytes_remaining + + tar->entry_padding); + if (bytes_skipped < 0) + return (ARCHIVE_FATAL); + + tar->entry_bytes_remaining = 0; + tar->entry_padding = 0; + + /* Free the sparse list. */ + while (tar->sparse_list != NULL) { + p = tar->sparse_list; + tar->sparse_list = p->next; + free(p); + } + + return (ARCHIVE_OK); +} + +/* + * This function recursively interprets all of the headers associated + * with a single entry. + */ +static int +tar_read_header(struct archive_read *a, struct tar *tar, + struct archive_entry *entry) +{ + ssize_t bytes; + int err; + const void *h; + const struct archive_entry_header_ustar *header; + + /* Read 512-byte header record */ + bytes = (a->decompressor->read_ahead)(a, &h, 512); + if (bytes < 512) { + /* + * If we're here, it's becase the _bid function accepted + * this file. So just call a short read end-of-archive + * and be done with it. + */ + return (ARCHIVE_EOF); + } + (a->decompressor->consume)(a, 512); + + /* Check for end-of-archive mark. */ + if (((*(const char *)h)==0) && archive_block_is_null((const unsigned char *)h)) { + /* Try to consume a second all-null record, as well. */ + bytes = (a->decompressor->read_ahead)(a, &h, 512); + if (bytes > 0) + (a->decompressor->consume)(a, bytes); + archive_set_error(&a->archive, 0, NULL); + return (ARCHIVE_EOF); + } + + /* + * Note: If the checksum fails and we return ARCHIVE_RETRY, + * then the client is likely to just retry. This is a very + * crude way to search for the next valid header! + * + * TODO: Improve this by implementing a real header scan. + */ + if (!checksum(a, h)) { + archive_set_error(&a->archive, EINVAL, "Damaged tar archive"); + return (ARCHIVE_RETRY); /* Retryable: Invalid header */ + } + + if (++tar->header_recursion_depth > 32) { + archive_set_error(&a->archive, EINVAL, "Too many special headers"); + return (ARCHIVE_WARN); + } + + /* Determine the format variant. */ + header = (const struct archive_entry_header_ustar *)h; + switch(header->typeflag[0]) { + case 'A': /* Solaris tar ACL */ + a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; + a->archive.archive_format_name = "Solaris tar"; + err = header_Solaris_ACL(a, tar, entry, h); + break; + case 'g': /* POSIX-standard 'g' header. */ + a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; + a->archive.archive_format_name = "POSIX pax interchange format"; + err = header_pax_global(a, tar, entry, h); + break; + case 'K': /* Long link name (GNU tar, others) */ + err = header_longlink(a, tar, entry, h); + break; + case 'L': /* Long filename (GNU tar, others) */ + err = header_longname(a, tar, entry, h); + break; + case 'V': /* GNU volume header */ + err = header_volume(a, tar, entry, h); + break; + case 'X': /* Used by SUN tar; same as 'x'. */ + a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; + a->archive.archive_format_name = + "POSIX pax interchange format (Sun variant)"; + err = header_pax_extensions(a, tar, entry, h); + break; + case 'x': /* POSIX-standard 'x' header. */ + a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; + a->archive.archive_format_name = "POSIX pax interchange format"; + err = header_pax_extensions(a, tar, entry, h); + break; + default: + if (memcmp(header->magic, "ustar \0", 8) == 0) { + a->archive.archive_format = ARCHIVE_FORMAT_TAR_GNUTAR; + a->archive.archive_format_name = "GNU tar format"; + err = header_gnutar(a, tar, entry, h); + } else if (memcmp(header->magic, "ustar", 5) == 0) { + if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE) { + a->archive.archive_format = ARCHIVE_FORMAT_TAR_USTAR; + a->archive.archive_format_name = "POSIX ustar format"; + } + err = header_ustar(a, tar, entry, h); + } else { + a->archive.archive_format = ARCHIVE_FORMAT_TAR; + a->archive.archive_format_name = "tar (non-POSIX)"; + err = header_old_tar(a, tar, entry, h); + } + } + --tar->header_recursion_depth; + return (err); +} + +/* + * Return true if block checksum is correct. + */ +static int +checksum(struct archive_read *a, const void *h) +{ + const unsigned char *bytes; + const struct archive_entry_header_ustar *header; + int check, i, sum; + + (void)a; /* UNUSED */ + bytes = (const unsigned char *)h; + header = (const struct archive_entry_header_ustar *)h; + + /* + * Test the checksum. Note that POSIX specifies _unsigned_ + * bytes for this calculation. + */ + sum = tar_atol(header->checksum, sizeof(header->checksum)); + check = 0; + for (i = 0; i < 148; i++) + check += (unsigned char)bytes[i]; + for (; i < 156; i++) + check += 32; + for (; i < 512; i++) + check += (unsigned char)bytes[i]; + if (sum == check) + return (1); + + /* + * Repeat test with _signed_ bytes, just in case this archive + * was created by an old BSD, Solaris, or HP-UX tar with a + * broken checksum calculation. + */ + check = 0; + for (i = 0; i < 148; i++) + check += (signed char)bytes[i]; + for (; i < 156; i++) + check += 32; + for (; i < 512; i++) + check += (signed char)bytes[i]; + if (sum == check) + return (1); + + return (0); +} + +/* + * Return true if this block contains only nulls. + */ +static int +archive_block_is_null(const unsigned char *p) +{ + unsigned i; + + for (i = 0; i < ARCHIVE_BYTES_PER_RECORD / sizeof(*p); i++) + if (*p++) + return (0); + return (1); +} + +/* + * Interpret 'A' Solaris ACL header + */ +static int +header_Solaris_ACL(struct archive_read *a, struct tar *tar, + struct archive_entry *entry, const void *h) +{ + int err, err2; + char *p; + wchar_t *wp; + + err = read_body_to_string(a, tar, &(tar->acl_text), h); + err2 = tar_read_header(a, tar, entry); + err = err_combine(err, err2); + + /* XXX Ensure p doesn't overrun acl_text */ + + /* Skip leading octal number. */ + /* XXX TODO: Parse the octal number and sanity-check it. */ + p = tar->acl_text.s; + while (*p != '\0') + p++; + p++; + + wp = (wchar_t *)malloc((strlen(p) + 1) * sizeof(wchar_t)); + if (wp != NULL) { + utf8_decode(wp, p, strlen(p)); + err2 = __archive_entry_acl_parse_w(entry, wp, + ARCHIVE_ENTRY_ACL_TYPE_ACCESS); + err = err_combine(err, err2); + free(wp); + } + + return (err); +} + +/* + * Interpret 'K' long linkname header. + */ +static int +header_longlink(struct archive_read *a, struct tar *tar, + struct archive_entry *entry, const void *h) +{ + int err, err2; + + err = read_body_to_string(a, tar, &(tar->longlink), h); + err2 = tar_read_header(a, tar, entry); + if (err == ARCHIVE_OK && err2 == ARCHIVE_OK) { + /* Set symlink if symlink already set, else hardlink. */ + archive_entry_set_link(entry, tar->longlink.s); + } + return (err_combine(err, err2)); +} + +/* + * Interpret 'L' long filename header. + */ +static int +header_longname(struct archive_read *a, struct tar *tar, + struct archive_entry *entry, const void *h) +{ + int err, err2; + + err = read_body_to_string(a, tar, &(tar->longname), h); + /* Read and parse "real" header, then override name. */ + err2 = tar_read_header(a, tar, entry); + if (err == ARCHIVE_OK && err2 == ARCHIVE_OK) + archive_entry_set_pathname(entry, tar->longname.s); + return (err_combine(err, err2)); +} + + +/* + * Interpret 'V' GNU tar volume header. + */ +static int +header_volume(struct archive_read *a, struct tar *tar, + struct archive_entry *entry, const void *h) +{ + (void)h; + + /* Just skip this and read the next header. */ + return (tar_read_header(a, tar, entry)); +} + +/* + * Read body of an archive entry into an archive_string object. + */ +static int +read_body_to_string(struct archive_read *a, struct tar *tar, + struct archive_string *as, const void *h) +{ + off_t size, padded_size; + ssize_t bytes_read, bytes_to_copy; + const struct archive_entry_header_ustar *header; + const void *src; + char *dest; + + (void)tar; /* UNUSED */ + header = (const struct archive_entry_header_ustar *)h; + size = tar_atol(header->size, sizeof(header->size)); + + /* Read the body into the string. */ + archive_string_ensure(as, size+1); + padded_size = (size + 511) & ~ 511; + dest = as->s; + while (padded_size > 0) { + bytes_read = (a->decompressor->read_ahead)(a, &src, padded_size); + if (bytes_read < 0) + return (ARCHIVE_FATAL); + if (bytes_read > padded_size) + bytes_read = padded_size; + (a->decompressor->consume)(a, bytes_read); + bytes_to_copy = bytes_read; + if ((off_t)bytes_to_copy > size) + bytes_to_copy = (ssize_t)size; + memcpy(dest, src, bytes_to_copy); + dest += bytes_to_copy; + size -= bytes_to_copy; + padded_size -= bytes_read; + } + *dest = '\0'; + return (ARCHIVE_OK); +} + +/* + * Parse out common header elements. + * + * This would be the same as header_old_tar, except that the + * filename is handled slightly differently for old and POSIX + * entries (POSIX entries support a 'prefix'). This factoring + * allows header_old_tar and header_ustar + * to handle filenames differently, while still putting most of the + * common parsing into one place. + */ +static int +header_common(struct archive_read *a, struct tar *tar, + struct archive_entry *entry, const void *h) +{ + const struct archive_entry_header_ustar *header; + char tartype; + + (void)a; /* UNUSED */ + + header = (const struct archive_entry_header_ustar *)h; + if (header->linkname[0]) + archive_strncpy(&(tar->entry_linkname), header->linkname, + sizeof(header->linkname)); + else + archive_string_empty(&(tar->entry_linkname)); + + /* Parse out the numeric fields (all are octal) */ + archive_entry_set_mode(entry, tar_atol(header->mode, sizeof(header->mode))); + archive_entry_set_uid(entry, tar_atol(header->uid, sizeof(header->uid))); + archive_entry_set_gid(entry, tar_atol(header->gid, sizeof(header->gid))); + archive_entry_set_size(entry, tar_atol(header->size, sizeof(header->size))); + archive_entry_set_mtime(entry, tar_atol(header->mtime, sizeof(header->mtime)), 0); + + /* Handle the tar type flag appropriately. */ + tartype = header->typeflag[0]; + + switch (tartype) { + case '1': /* Hard link */ + archive_entry_set_hardlink(entry, tar->entry_linkname.s); + /* + * The following may seem odd, but: Technically, tar + * does not store the file type for a "hard link" + * entry, only the fact that it is a hard link. So, I + * leave the type zero normally. But, pax interchange + * format allows hard links to have data, which + * implies that the underlying entry is a regular + * file. + */ + if (archive_entry_size(entry) > 0) + archive_entry_set_filetype(entry, AE_IFREG); + + /* + * A tricky point: Traditionally, tar readers have + * ignored the size field when reading hardlink + * entries, and some writers put non-zero sizes even + * though the body is empty. POSIX.1-2001 broke with + * this tradition by permitting hardlink entries to + * store valid bodies in pax interchange format, but + * not in ustar format. Since there is no hard and + * fast way to distinguish pax interchange from + * earlier archives (the 'x' and 'g' entries are + * optional, after all), we need a heuristic. Here, I + * use the bid function to test whether or not there's + * a valid header following. Of course, if we know + * this is pax interchange format, then we must obey + * the size. + * + * This heuristic will only fail for a pax interchange + * archive that is storing hardlink bodies, no pax + * extended attribute entries have yet occurred, and + * we encounter a hardlink entry for a file that is + * itself an uncompressed tar archive. + */ + if (archive_entry_size(entry) > 0 && + a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE && + archive_read_format_tar_bid(a) > 50) + archive_entry_set_size(entry, 0); + break; + case '2': /* Symlink */ + archive_entry_set_filetype(entry, AE_IFLNK); + archive_entry_set_size(entry, 0); + archive_entry_set_symlink(entry, tar->entry_linkname.s); + break; + case '3': /* Character device */ + archive_entry_set_filetype(entry, AE_IFCHR); + archive_entry_set_size(entry, 0); + break; + case '4': /* Block device */ + archive_entry_set_filetype(entry, AE_IFBLK); + archive_entry_set_size(entry, 0); + break; + case '5': /* Dir */ + archive_entry_set_filetype(entry, AE_IFDIR); + archive_entry_set_size(entry, 0); + break; + case '6': /* FIFO device */ + archive_entry_set_filetype(entry, AE_IFIFO); + archive_entry_set_size(entry, 0); + break; + case 'D': /* GNU incremental directory type */ + /* + * No special handling is actually required here. + * It might be nice someday to preprocess the file list and + * provide it to the client, though. + */ + archive_entry_set_filetype(entry, AE_IFDIR); + break; + case 'M': /* GNU "Multi-volume" (remainder of file from last archive)*/ + /* + * As far as I can tell, this is just like a regular file + * entry, except that the contents should be _appended_ to + * the indicated file at the indicated offset. This may + * require some API work to fully support. + */ + break; + case 'N': /* Old GNU "long filename" entry. */ + /* The body of this entry is a script for renaming + * previously-extracted entries. Ugh. It will never + * be supported by libarchive. */ + archive_entry_set_filetype(entry, AE_IFREG); + break; + case 'S': /* GNU sparse files */ + /* + * Sparse files are really just regular files with + * sparse information in the extended area. + */ + /* FALLTHROUGH */ + default: /* Regular file and non-standard types */ + /* + * Per POSIX: non-recognized types should always be + * treated as regular files. + */ + archive_entry_set_filetype(entry, AE_IFREG); + break; + } + return (0); +} + +/* + * Parse out header elements for "old-style" tar archives. + */ +static int +header_old_tar(struct archive_read *a, struct tar *tar, + struct archive_entry *entry, const void *h) +{ + const struct archive_entry_header_ustar *header; + + /* Copy filename over (to ensure null termination). */ + header = (const struct archive_entry_header_ustar *)h; + archive_strncpy(&(tar->entry_name), header->name, sizeof(header->name)); + archive_entry_set_pathname(entry, tar->entry_name.s); + + /* Grab rest of common fields */ + header_common(a, tar, entry, h); + + tar->entry_bytes_remaining = archive_entry_size(entry); + tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining); + return (0); +} + +/* + * Parse a file header for a pax extended archive entry. + */ +static int +header_pax_global(struct archive_read *a, struct tar *tar, + struct archive_entry *entry, const void *h) +{ + int err, err2; + + err = read_body_to_string(a, tar, &(tar->pax_global), h); + err2 = tar_read_header(a, tar, entry); + return (err_combine(err, err2)); +} + +static int +header_pax_extensions(struct archive_read *a, struct tar *tar, + struct archive_entry *entry, const void *h) +{ + int err, err2; + + read_body_to_string(a, tar, &(tar->pax_header), h); + + /* Parse the next header. */ + err = tar_read_header(a, tar, entry); + + /* + * TODO: Parse global/default options into 'entry' struct here + * before handling file-specific options. + * + * This design (parse standard header, then overwrite with pax + * extended attribute data) usually works well, but isn't ideal; + * it would be better to parse the pax extended attributes first + * and then skip any fields in the standard header that were + * defined in the pax header. + */ + err2 = pax_header(a, tar, entry, tar->pax_header.s); + err = err_combine(err, err2); + tar->entry_bytes_remaining = archive_entry_size(entry); + tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining); + return (err); +} + + +/* + * Parse a file header for a Posix "ustar" archive entry. This also + * handles "pax" or "extended ustar" entries. + */ +static int +header_ustar(struct archive_read *a, struct tar *tar, + struct archive_entry *entry, const void *h) +{ + const struct archive_entry_header_ustar *header; + struct archive_string *as; + + header = (const struct archive_entry_header_ustar *)h; + + /* Copy name into an internal buffer to ensure null-termination. */ + as = &(tar->entry_name); + if (header->prefix[0]) { + archive_strncpy(as, header->prefix, sizeof(header->prefix)); + if (as->s[archive_strlen(as) - 1] != '/') + archive_strappend_char(as, '/'); + archive_strncat(as, header->name, sizeof(header->name)); + } else + archive_strncpy(as, header->name, sizeof(header->name)); + + archive_entry_set_pathname(entry, as->s); + + /* Handle rest of common fields. */ + header_common(a, tar, entry, h); + + /* Handle POSIX ustar fields. */ + archive_strncpy(&(tar->entry_uname), header->uname, + sizeof(header->uname)); + archive_entry_set_uname(entry, tar->entry_uname.s); + + archive_strncpy(&(tar->entry_gname), header->gname, + sizeof(header->gname)); + archive_entry_set_gname(entry, tar->entry_gname.s); + + /* Parse out device numbers only for char and block specials. */ + if (header->typeflag[0] == '3' || header->typeflag[0] == '4') { + archive_entry_set_rdevmajor(entry, + tar_atol(header->rdevmajor, sizeof(header->rdevmajor))); + archive_entry_set_rdevminor(entry, + tar_atol(header->rdevminor, sizeof(header->rdevminor))); + } + + tar->entry_bytes_remaining = archive_entry_size(entry); + tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining); + + return (0); +} + + +/* + * Parse the pax extended attributes record. + * + * Returns non-zero if there's an error in the data. + */ +static int +pax_header(struct archive_read *a, struct tar *tar, + struct archive_entry *entry, char *attr) +{ + size_t attr_length, l, line_length; + char *line, *p; + wchar_t *key, *wp, *value; + int err, err2; + + attr_length = strlen(attr); + err = ARCHIVE_OK; + while (attr_length > 0) { + /* Parse decimal length field at start of line. */ + line_length = 0; + l = attr_length; + line = p = attr; /* Record start of line. */ + while (l>0) { + if (*p == ' ') { + p++; + l--; + break; + } + if (*p < '0' || *p > '9') + return (-1); + line_length *= 10; + line_length += *p - '0'; + if (line_length > 999999) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Rejecting pax extended attribute > 1MB"); + return (ARCHIVE_WARN); + } + p++; + l--; + } + + if (line_length > attr_length) + return (0); + + /* Ensure pax_entry buffer is big enough. */ + if (tar->pax_entry_length <= line_length) { + wchar_t *old_entry = tar->pax_entry; + + if (tar->pax_entry_length <= 0) + tar->pax_entry_length = 1024; + while (tar->pax_entry_length <= line_length + 1) + tar->pax_entry_length *= 2; + + old_entry = tar->pax_entry; + tar->pax_entry = (wchar_t *)realloc(tar->pax_entry, + tar->pax_entry_length * sizeof(wchar_t)); + if (tar->pax_entry == NULL) { + free(old_entry); + archive_set_error(&a->archive, ENOMEM, + "No memory"); + return (ARCHIVE_FATAL); + } + } + + /* Decode UTF-8 to wchar_t, null-terminate result. */ + if (utf8_decode(tar->pax_entry, p, + line_length - (p - attr) - 1)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Invalid UTF8 character in pax extended attribute"); + err = err_combine(err, ARCHIVE_WARN); + } + + /* Null-terminate 'key' value. */ + wp = key = tar->pax_entry; + if (key[0] == L'=') + return (-1); + while (*wp && *wp != L'=') + ++wp; + if (*wp == L'\0' || wp == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Invalid pax extended attributes"); + return (ARCHIVE_WARN); + } + *wp = 0; + + /* Identify null-terminated 'value' portion. */ + value = wp + 1; + + /* Identify this attribute and set it in the entry. */ + err2 = pax_attribute(entry, key, value); + err = err_combine(err, err2); + + /* Skip to next line */ + attr += line_length; + attr_length -= line_length; + } + return (err); +} + +static int +pax_attribute_xattr(struct archive_entry *entry, + wchar_t *name, wchar_t *value) +{ + char *name_decoded, *name_narrow; + void *value_decoded; + size_t value_len; + + if (wcslen(name) < 18 || (wcsncmp(name, L"LIBARCHIVE.xattr.", 17)) != 0) + return 3; + + name += 17; + + /* URL-decode name */ + name_narrow = wide_to_narrow(name); + if (name_narrow == NULL) + return 2; + name_decoded = url_decode(name_narrow); + free(name_narrow); + if (name_decoded == NULL) + return 2; + + /* Base-64 decode value */ + value_decoded = base64_decode(value, wcslen(value), &value_len); + if (value_decoded == NULL) { + free(name_decoded); + return 1; + } + + archive_entry_xattr_add_entry(entry, name_decoded, + value_decoded, value_len); + + free(name_decoded); + free(value_decoded); + return 0; +} + +/* + * Parse a single key=value attribute. key/value pointers are + * assumed to point into reasonably long-lived storage. + * + * Note that POSIX reserves all-lowercase keywords. Vendor-specific + * extensions should always have keywords of the form "VENDOR.attribute" + * In particular, it's quite feasible to support many different + * vendor extensions here. I'm using "LIBARCHIVE" for extensions + * unique to this library (currently, there are none). + * + * Investigate other vendor-specific extensions, as well and see if + * any of them look useful. + */ +static int +pax_attribute(struct archive_entry *entry, + wchar_t *key, wchar_t *value) +{ + int64_t s; + long n; + + switch (key[0]) { + case 'L': + /* Our extensions */ +/* TODO: Handle arbitrary extended attributes... */ +/* + if (strcmp(key, "LIBARCHIVE.xxxxxxx")==0) + archive_entry_set_xxxxxx(entry, value); +*/ + if (wcsncmp(key, L"LIBARCHIVE.xattr.", 17)==0) + pax_attribute_xattr(entry, key, value); + break; + case 'S': + /* We support some keys used by the "star" archiver */ + if (wcscmp(key, L"SCHILY.acl.access")==0) + __archive_entry_acl_parse_w(entry, value, + ARCHIVE_ENTRY_ACL_TYPE_ACCESS); + else if (wcscmp(key, L"SCHILY.acl.default")==0) + __archive_entry_acl_parse_w(entry, value, + ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); + else if (wcscmp(key, L"SCHILY.devmajor")==0) + archive_entry_set_rdevmajor(entry, tar_atol10(value, wcslen(value))); + else if (wcscmp(key, L"SCHILY.devminor")==0) + archive_entry_set_rdevminor(entry, tar_atol10(value, wcslen(value))); + else if (wcscmp(key, L"SCHILY.fflags")==0) + archive_entry_copy_fflags_text_w(entry, value); + else if (wcscmp(key, L"SCHILY.dev")==0) + archive_entry_set_dev(entry, tar_atol10(value, wcslen(value))); + else if (wcscmp(key, L"SCHILY.ino")==0) + archive_entry_set_ino(entry, tar_atol10(value, wcslen(value))); + else if (wcscmp(key, L"SCHILY.nlink")==0) + archive_entry_set_nlink(entry, tar_atol10(value, wcslen(value))); + break; + case 'a': + if (wcscmp(key, L"atime")==0) { + pax_time(value, &s, &n); + archive_entry_set_atime(entry, s, n); + } + break; + case 'c': + if (wcscmp(key, L"ctime")==0) { + pax_time(value, &s, &n); + archive_entry_set_ctime(entry, s, n); + } else if (wcscmp(key, L"charset")==0) { + /* TODO: Publish charset information in entry. */ + } else if (wcscmp(key, L"comment")==0) { + /* TODO: Publish comment in entry. */ + } + break; + case 'g': + if (wcscmp(key, L"gid")==0) + archive_entry_set_gid(entry, tar_atol10(value, wcslen(value))); + else if (wcscmp(key, L"gname")==0) + archive_entry_copy_gname_w(entry, value); + break; + case 'l': + /* pax interchange doesn't distinguish hardlink vs. symlink. */ + if (wcscmp(key, L"linkpath")==0) { + if (archive_entry_hardlink(entry)) + archive_entry_copy_hardlink_w(entry, value); + else + archive_entry_copy_symlink_w(entry, value); + } + break; + case 'm': + if (wcscmp(key, L"mtime")==0) { + pax_time(value, &s, &n); + archive_entry_set_mtime(entry, s, n); + } + break; + case 'p': + if (wcscmp(key, L"path")==0) + archive_entry_copy_pathname_w(entry, value); + break; + case 'r': + /* POSIX has reserved 'realtime.*' */ + break; + case 's': + /* POSIX has reserved 'security.*' */ + /* Someday: if (wcscmp(key, L"security.acl")==0) { ... } */ + if (wcscmp(key, L"size")==0) + archive_entry_set_size(entry, tar_atol10(value, wcslen(value))); + break; + case 'u': + if (wcscmp(key, L"uid")==0) + archive_entry_set_uid(entry, tar_atol10(value, wcslen(value))); + else if (wcscmp(key, L"uname")==0) + archive_entry_copy_uname_w(entry, value); + break; + } + return (0); +} + + + +/* + * parse a decimal time value, which may include a fractional portion + */ +static void +pax_time(const wchar_t *p, int64_t *ps, long *pn) +{ + char digit; + int64_t s; + unsigned long l; + int sign; + int64_t limit, last_digit_limit; + + limit = INT64_MAX / 10; + last_digit_limit = INT64_MAX % 10; + + s = 0; + sign = 1; + if (*p == '-') { + sign = -1; + p++; + } + while (*p >= '0' && *p <= '9') { + digit = *p - '0'; + if (s > limit || + (s == limit && digit > last_digit_limit)) { + s = UINT64_MAX; + break; + } + s = (s * 10) + digit; + ++p; + } + + *ps = s * sign; + + /* Calculate nanoseconds. */ + *pn = 0; + + if (*p != '.') + return; + + l = 100000000UL; + do { + ++p; + if (*p >= '0' && *p <= '9') + *pn += (*p - '0') * l; + else + break; + } while (l /= 10); +} + +/* + * Parse GNU tar header + */ +static int +header_gnutar(struct archive_read *a, struct tar *tar, + struct archive_entry *entry, const void *h) +{ + const struct archive_entry_header_gnutar *header; + + (void)a; + + /* + * GNU header is like POSIX ustar, except 'prefix' is + * replaced with some other fields. This also means the + * filename is stored as in old-style archives. + */ + + /* Grab fields common to all tar variants. */ + header_common(a, tar, entry, h); + + /* Copy filename over (to ensure null termination). */ + header = (const struct archive_entry_header_gnutar *)h; + archive_strncpy(&(tar->entry_name), header->name, + sizeof(header->name)); + archive_entry_set_pathname(entry, tar->entry_name.s); + + /* Fields common to ustar and GNU */ + /* XXX Can the following be factored out since it's common + * to ustar and gnu tar? Is it okay to move it down into + * header_common, perhaps? */ + archive_strncpy(&(tar->entry_uname), + header->uname, sizeof(header->uname)); + archive_entry_set_uname(entry, tar->entry_uname.s); + + archive_strncpy(&(tar->entry_gname), + header->gname, sizeof(header->gname)); + archive_entry_set_gname(entry, tar->entry_gname.s); + + /* Parse out device numbers only for char and block specials */ + if (header->typeflag[0] == '3' || header->typeflag[0] == '4') { + archive_entry_set_rdevmajor(entry, + tar_atol(header->rdevmajor, sizeof(header->rdevmajor))); + archive_entry_set_rdevminor(entry, + tar_atol(header->rdevminor, sizeof(header->rdevminor))); + } else + archive_entry_set_rdev(entry, 0); + + tar->entry_bytes_remaining = archive_entry_size(entry); + tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining); + + /* Grab GNU-specific fields. */ + archive_entry_set_atime(entry, + tar_atol(header->atime, sizeof(header->atime)), 0); + archive_entry_set_ctime(entry, + tar_atol(header->ctime, sizeof(header->ctime)), 0); + if (header->realsize[0] != 0) { + archive_entry_set_size(entry, + tar_atol(header->realsize, sizeof(header->realsize))); + } + + if (header->sparse[0].offset[0] != 0) { + gnu_read_sparse_data(a, tar, header); + } else { + if (header->isextended[0] != 0) { + /* XXX WTF? XXX */ + } + } + + return (0); +} + +static int +gnu_read_sparse_data(struct archive_read *a, struct tar *tar, + const struct archive_entry_header_gnutar *header) +{ + ssize_t bytes_read; + const void *data; + struct extended { + struct gnu_sparse sparse[21]; + char isextended[1]; + char padding[7]; + }; + const struct extended *ext; + + gnu_parse_sparse_data(a, tar, header->sparse, 4); + if (header->isextended[0] == 0) + return (ARCHIVE_OK); + + do { + bytes_read = (a->decompressor->read_ahead)(a, &data, 512); + if (bytes_read < 0) + return (ARCHIVE_FATAL); + if (bytes_read < 512) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated tar archive " + "detected while reading sparse file data"); + return (ARCHIVE_FATAL); + } + (a->decompressor->consume)(a, 512); + ext = (const struct extended *)data; + gnu_parse_sparse_data(a, tar, ext->sparse, 21); + } while (ext->isextended[0] != 0); + if (tar->sparse_list != NULL) + tar->entry_offset = tar->sparse_list->offset; + return (ARCHIVE_OK); +} + +static void +gnu_parse_sparse_data(struct archive_read *a, struct tar *tar, + const struct gnu_sparse *sparse, int length) +{ + struct sparse_block *last; + struct sparse_block *p; + + (void)a; /* UNUSED */ + + last = tar->sparse_list; + while (last != NULL && last->next != NULL) + last = last->next; + + while (length > 0 && sparse->offset[0] != 0) { + p = (struct sparse_block *)malloc(sizeof(*p)); + if (p == NULL) + __archive_errx(1, "Out of memory"); + memset(p, 0, sizeof(*p)); + if (last != NULL) + last->next = p; + else + tar->sparse_list = p; + last = p; + p->offset = tar_atol(sparse->offset, sizeof(sparse->offset)); + p->remaining = + tar_atol(sparse->numbytes, sizeof(sparse->numbytes)); + sparse++; + length--; + } +} + +/*- + * Convert text->integer. + * + * Traditional tar formats (including POSIX) specify base-8 for + * all of the standard numeric fields. This is a significant limitation + * in practice: + * = file size is limited to 8GB + * = rdevmajor and rdevminor are limited to 21 bits + * = uid/gid are limited to 21 bits + * + * There are two workarounds for this: + * = pax extended headers, which use variable-length string fields + * = GNU tar and STAR both allow either base-8 or base-256 in + * most fields. The high bit is set to indicate base-256. + * + * On read, this implementation supports both extensions. + */ +static int64_t +tar_atol(const char *p, unsigned char_cnt) +{ + /* + * Technically, GNU tar considers a field to be in base-256 + * only if the first byte is 0xff or 0x80. + */ + if (*p & 0x80) + return (tar_atol256(p, char_cnt)); + return (tar_atol8(p, char_cnt)); +} + +/* + * Note that this implementation does not (and should not!) obey + * locale settings; you cannot simply substitute strtol here, since + * it does obey locale. + */ +static int64_t +tar_atol8(const char *p, unsigned char_cnt) +{ + int64_t l, limit, last_digit_limit; + int digit, sign, base; + + base = 8; + limit = INT64_MAX / base; + last_digit_limit = INT64_MAX % base; + + while (*p == ' ' || *p == '\t') + p++; + if (*p == '-') { + sign = -1; + p++; + } else + sign = 1; + + l = 0; + digit = *p - '0'; + while (digit >= 0 && digit < base && char_cnt-- > 0) { + if (l>limit || (l == limit && digit > last_digit_limit)) { + l = UINT64_MAX; /* Truncate on overflow. */ + break; + } + l = (l * base) + digit; + digit = *++p - '0'; + } + return (sign < 0) ? -l : l; +} + + +/* + * Note that this implementation does not (and should not!) obey + * locale settings; you cannot simply substitute strtol here, since + * it does obey locale. + */ +static int64_t +tar_atol10(const wchar_t *p, unsigned char_cnt) +{ + int64_t l, limit, last_digit_limit; + int base, digit, sign; + + base = 10; + limit = INT64_MAX / base; + last_digit_limit = INT64_MAX % base; + + while (*p == ' ' || *p == '\t') + p++; + if (*p == '-') { + sign = -1; + p++; + } else + sign = 1; + + l = 0; + digit = *p - '0'; + while (digit >= 0 && digit < base && char_cnt-- > 0) { + if (l > limit || (l == limit && digit > last_digit_limit)) { + l = UINT64_MAX; /* Truncate on overflow. */ + break; + } + l = (l * base) + digit; + digit = *++p - '0'; + } + return (sign < 0) ? -l : l; +} + +/* + * Parse a base-256 integer. This is just a straight signed binary + * value in big-endian order, except that the high-order bit is + * ignored. Remember that "int64_t" may or may not be exactly 64 + * bits; the implementation here tries to avoid making any assumptions + * about the actual size of an int64_t. It does assume we're using + * twos-complement arithmetic, though. + */ +static int64_t +tar_atol256(const char *_p, unsigned char_cnt) +{ + int64_t l, upper_limit, lower_limit; + const unsigned char *p = (const unsigned char *)_p; + + upper_limit = INT64_MAX / 256; + lower_limit = INT64_MIN / 256; + + /* Pad with 1 or 0 bits, depending on sign. */ + if ((0x40 & *p) == 0x40) + l = (int64_t)-1; + else + l = 0; + l = (l << 6) | (0x3f & *p++); + while (--char_cnt > 0) { + if (l > upper_limit) { + l = INT64_MAX; /* Truncate on overflow */ + break; + } else if (l < lower_limit) { + l = INT64_MIN; + break; + } + l = (l << 8) | (0xff & (int64_t)*p++); + } + return (l); +} + +static int +utf8_decode(wchar_t *dest, const char *src, size_t length) +{ + size_t n; + int err; + + err = 0; + while (length > 0) { + n = UTF8_mbrtowc(dest, src, length); + if (n == 0) + break; + dest++; + src += n; + length -= n; + } + *dest++ = L'\0'; + return (err); +} + +/* + * Copied and simplified from FreeBSD libc/locale. + */ +static size_t +UTF8_mbrtowc(wchar_t *pwc, const char *s, size_t n) +{ + int ch, i, len, mask; + unsigned long wch; + + if (s == NULL || n == 0 || pwc == NULL) + return (0); + + /* + * Determine the number of octets that make up this character from + * the first octet, and a mask that extracts the interesting bits of + * the first octet. + */ + ch = (unsigned char)*s; + if ((ch & 0x80) == 0) { + mask = 0x7f; + len = 1; + } else if ((ch & 0xe0) == 0xc0) { + mask = 0x1f; + len = 2; + } else if ((ch & 0xf0) == 0xe0) { + mask = 0x0f; + len = 3; + } else if ((ch & 0xf8) == 0xf0) { + mask = 0x07; + len = 4; + } else if ((ch & 0xfc) == 0xf8) { + mask = 0x03; + len = 5; + } else if ((ch & 0xfe) == 0xfc) { + mask = 0x01; + len = 6; + } else { + /* Invalid first byte; convert to '?' */ + *pwc = '?'; + return (1); + } + + if (n < (size_t)len) { + /* Invalid first byte; convert to '?' */ + *pwc = '?'; + return (1); + } + + /* + * Decode the octet sequence representing the character in chunks + * of 6 bits, most significant first. + */ + wch = (unsigned char)*s++ & mask; + i = len; + while (--i != 0) { + if ((*s & 0xc0) != 0x80) { + /* Invalid intermediate byte; consume one byte and + * emit '?' */ + *pwc = '?'; + return (1); + } + wch <<= 6; + wch |= *s++ & 0x3f; + } + + /* Assign the value to the output; out-of-range values + * just get truncated. */ + *pwc = (wchar_t)wch; +#ifdef WCHAR_MAX + /* + * If platform has WCHAR_MAX, we can do something + * more sensible with out-of-range values. + */ + if (wch >= WCHAR_MAX) + *pwc = '?'; +#endif + /* Return number of bytes input consumed: 0 for end-of-string. */ + return (wch == L'\0' ? 0 : len); +} + + +/* + * base64_decode - Base64 decode + * + * This accepts most variations of base-64 encoding, including: + * * with or without line breaks + * * with or without the final group padded with '=' or '_' characters + * (The most economical Base-64 variant does not pad the last group and + * omits line breaks; RFC1341 used for MIME requires both.) + */ +static char * +base64_decode(const wchar_t *src, size_t len, size_t *out_len) +{ + static const unsigned char digits[64] = { + 'A','B','C','D','E','F','G','H','I','J','K','L','M','N', + 'O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b', + 'c','d','e','f','g','h','i','j','k','l','m','n','o','p', + 'q','r','s','t','u','v','w','x','y','z','0','1','2','3', + '4','5','6','7','8','9','+','/' }; + static unsigned char decode_table[128]; + char *out, *d; + + /* If the decode table is not yet initialized, prepare it. */ + if (decode_table[digits[1]] != 1) { + size_t i; + memset(decode_table, 0xff, sizeof(decode_table)); + for (i = 0; i < sizeof(digits); i++) + decode_table[digits[i]] = i; + } + + /* Allocate enough space to hold the entire output. */ + /* Note that we may not use all of this... */ + out = (char *)malloc((len * 3 + 3) / 4); + if (out == NULL) { + *out_len = 0; + return (NULL); + } + d = out; + + while (len > 0) { + /* Collect the next group of (up to) four characters. */ + int v = 0; + int group_size = 0; + while (group_size < 4 && len > 0) { + /* '=' or '_' padding indicates final group. */ + if (*src == '=' || *src == '_') { + len = 0; + break; + } + /* Skip illegal characters (including line breaks) */ + if (*src > 127 || *src < 32 + || decode_table[*src] == 0xff) { + len--; + src++; + continue; + } + v <<= 6; + v |= decode_table[*src++]; + len --; + group_size++; + } + /* Align a short group properly. */ + v <<= 6 * (4 - group_size); + /* Unpack the group we just collected. */ + switch (group_size) { + case 4: d[2] = v & 0xff; + /* FALLTHROUGH */ + case 3: d[1] = (v >> 8) & 0xff; + /* FALLTHROUGH */ + case 2: d[0] = (v >> 16) & 0xff; + break; + case 1: /* this is invalid! */ + break; + } + d += group_size * 3 / 4; + } + + *out_len = d - out; + return (out); +} + +/* + * This is a little tricky because the C99 standard wcstombs() + * function returns the number of bytes that were converted, + * not the number that should be converted. As a result, + * we can never accurately size the output buffer (without + * doing a tedious output size calculation in advance). + * This approach (try a conversion, then try again if it fails) + * will almost always succeed on the first try, and is thus + * much faster, at the cost of sometimes requiring multiple + * passes while we expand the buffer. + */ +static char * +wide_to_narrow(const wchar_t *wval) +{ + int converted_length; + /* Guess an output buffer size and try the conversion. */ + int alloc_length = wcslen(wval) * 3; + char *mbs_val = (char *)malloc(alloc_length + 1); + if (mbs_val == NULL) + return (NULL); + converted_length = wcstombs(mbs_val, wval, alloc_length); + + /* If we exhausted the buffer, resize and try again. */ + while (converted_length >= alloc_length) { + free(mbs_val); + alloc_length *= 2; + mbs_val = (char *)malloc(alloc_length + 1); + if (mbs_val == NULL) + return (NULL); + converted_length = wcstombs(mbs_val, wval, alloc_length); + } + + /* Ensure a trailing null and return the final string. */ + mbs_val[alloc_length] = '\0'; + return (mbs_val); +} + +static char * +url_decode(const char *in) +{ + char *out, *d; + const char *s; + + out = (char *)malloc(strlen(in) + 1); + if (out == NULL) + return (NULL); + for (s = in, d = out; *s != '\0'; ) { + if (*s == '%') { + /* Try to convert % escape */ + int digit1 = tohex(s[1]); + int digit2 = tohex(s[2]); + if (digit1 >= 0 && digit2 >= 0) { + /* Looks good, consume three chars */ + s += 3; + /* Convert output */ + *d++ = ((digit1 << 4) | digit2); + continue; + } + /* Else fall through and treat '%' as normal char */ + } + *d++ = *s++; + } + *d = '\0'; + return (out); +} + +static int +tohex(int c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + else if (c >= 'A' && c <= 'F') + return (c - 'A' + 10); + else if (c >= 'a' && c <= 'f') + return (c - 'a' + 10); + else + return (-1); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_read_support_format_zip.c b/contrib/libarchive-2.1/libarchive/archive_read_support_format_zip.c new file mode 100644 index 0000000000..a1c1d53b0b --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_read_support_format_zip.c @@ -0,0 +1,799 @@ +/*- + * Copyright (c) 2004 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_zip.c,v 1.12 2007/04/15 00:53:38 kientzle Exp $"); + +#ifdef HAVE_ERRNO_H +#include +#endif +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#include +#ifdef HAVE_ZLIB_H +#include +#endif + +#include "archive.h" +#include "archive_entry.h" +#include "archive_private.h" +#include "archive_read_private.h" + +struct zip { + /* entry_bytes_remaining is the number of bytes we expect. */ + int64_t entry_bytes_remaining; + int64_t entry_offset; + + /* These count the number of bytes actually read for the entry. */ + int64_t entry_compressed_bytes_read; + int64_t entry_uncompressed_bytes_read; + + unsigned version; + unsigned system; + unsigned flags; + unsigned compression; + const char * compression_name; + time_t mtime; + time_t ctime; + time_t atime; + mode_t mode; + uid_t uid; + gid_t gid; + + /* Flags to mark progress of decompression. */ + char decompress_init; + char end_of_entry; + char end_of_entry_cleanup; + + long crc32; + ssize_t filename_length; + ssize_t extra_length; + int64_t uncompressed_size; + int64_t compressed_size; + + unsigned char *uncompressed_buffer; + size_t uncompressed_buffer_size; +#ifdef HAVE_ZLIB_H + z_stream stream; +#endif + + struct archive_string pathname; + struct archive_string extra; + char format_name[64]; +}; + +#define ZIP_LENGTH_AT_END 8 + +struct zip_file_header { + char signature[4]; + char version[2]; + char flags[2]; + char compression[2]; + char timedate[4]; + char crc32[4]; + char compressed_size[4]; + char uncompressed_size[4]; + char filename_length[2]; + char extra_length[2]; +}; + +static const char *compression_names[] = { + "uncompressed", + "shrinking", + "reduced-1", + "reduced-2", + "reduced-3", + "reduced-4", + "imploded", + "reserved", + "deflation" +}; + +static int archive_read_format_zip_bid(struct archive_read *); +static int archive_read_format_zip_cleanup(struct archive_read *); +static int archive_read_format_zip_read_data(struct archive_read *, + const void **, size_t *, off_t *); +static int archive_read_format_zip_read_data_skip(struct archive_read *a); +static int archive_read_format_zip_read_header(struct archive_read *, + struct archive_entry *); +static int i2(const char *); +static int i4(const char *); +static unsigned int u2(const char *); +static unsigned int u4(const char *); +static uint64_t u8(const char *); +static int zip_read_data_deflate(struct archive_read *a, const void **buff, + size_t *size, off_t *offset); +static int zip_read_data_none(struct archive_read *a, const void **buff, + size_t *size, off_t *offset); +static int zip_read_file_header(struct archive_read *a, + struct archive_entry *entry, struct zip *zip); +static time_t zip_time(const char *); +static void process_extra(const void* extra, struct zip* zip); + +int +archive_read_support_format_zip(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + struct zip *zip; + int r; + + zip = (struct zip *)malloc(sizeof(*zip)); + if (zip == NULL) { + archive_set_error(&a->archive, ENOMEM, "Can't allocate zip data"); + return (ARCHIVE_FATAL); + } + memset(zip, 0, sizeof(*zip)); + + r = __archive_read_register_format(a, + zip, + archive_read_format_zip_bid, + archive_read_format_zip_read_header, + archive_read_format_zip_read_data, + archive_read_format_zip_read_data_skip, + archive_read_format_zip_cleanup); + + if (r != ARCHIVE_OK) + free(zip); + return (ARCHIVE_OK); +} + + +static int +archive_read_format_zip_bid(struct archive_read *a) +{ + int bytes_read; + int bid = 0; + const void *h; + const char *p; + + if (a->archive.archive_format == ARCHIVE_FORMAT_ZIP) + bid += 1; + + bytes_read = (a->decompressor->read_ahead)(a, &h, 4); + if (bytes_read < 4) + return (-1); + p = (const char *)h; + + if (p[0] == 'P' && p[1] == 'K') { + bid += 16; + if (p[2] == '\001' && p[3] == '\002') + bid += 16; + else if (p[2] == '\003' && p[3] == '\004') + bid += 16; + else if (p[2] == '\005' && p[3] == '\006') + bid += 16; + else if (p[2] == '\007' && p[3] == '\010') + bid += 16; + } + return (bid); +} + +static int +archive_read_format_zip_read_header(struct archive_read *a, + struct archive_entry *entry) +{ + int bytes_read; + const void *h; + const char *signature; + struct zip *zip; + + a->archive.archive_format = ARCHIVE_FORMAT_ZIP; + if (a->archive.archive_format_name == NULL) + a->archive.archive_format_name = "ZIP"; + + zip = (struct zip *)(a->format->data); + zip->decompress_init = 0; + zip->end_of_entry = 0; + zip->end_of_entry_cleanup = 0; + zip->entry_uncompressed_bytes_read = 0; + zip->entry_compressed_bytes_read = 0; + bytes_read = (a->decompressor->read_ahead)(a, &h, 4); + if (bytes_read < 4) + return (ARCHIVE_FATAL); + + signature = (const char *)h; + if (signature[0] != 'P' || signature[1] != 'K') { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Bad ZIP file"); + return (ARCHIVE_FATAL); + } + + if (signature[2] == '\001' && signature[3] == '\002') { + /* Beginning of central directory. */ + return (ARCHIVE_EOF); + } + + if (signature[2] == '\003' && signature[3] == '\004') { + /* Regular file entry. */ + return (zip_read_file_header(a, entry, zip)); + } + + if (signature[2] == '\005' && signature[3] == '\006') { + /* End-of-archive record. */ + return (ARCHIVE_EOF); + } + + if (signature[2] == '\007' && signature[3] == '\010') { + /* + * We should never encounter this record here; + * see ZIP_LENGTH_AT_END handling below for details. + */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Bad ZIP file: Unexpected end-of-entry record"); + return (ARCHIVE_FATAL); + } + + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Damaged ZIP file or unsupported format variant (%d,%d)", + signature[2], signature[3]); + return (ARCHIVE_FATAL); +} + +int +zip_read_file_header(struct archive_read *a, struct archive_entry *entry, + struct zip *zip) +{ + const struct zip_file_header *p; + const void *h; + int bytes_read; + + bytes_read = + (a->decompressor->read_ahead)(a, &h, sizeof(struct zip_file_header)); + if (bytes_read < (int)sizeof(struct zip_file_header)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated ZIP file header"); + return (ARCHIVE_FATAL); + } + p = (const struct zip_file_header *)h; + + zip->version = p->version[0]; + zip->system = p->version[1]; + zip->flags = i2(p->flags); + zip->compression = i2(p->compression); + if (zip->compression < + sizeof(compression_names)/sizeof(compression_names[0])) + zip->compression_name = compression_names[zip->compression]; + else + zip->compression_name = "??"; + zip->mtime = zip_time(p->timedate); + zip->ctime = 0; + zip->atime = 0; + zip->mode = 0; + zip->uid = 0; + zip->gid = 0; + zip->crc32 = i4(p->crc32); + zip->filename_length = i2(p->filename_length); + zip->extra_length = i2(p->extra_length); + zip->uncompressed_size = u4(p->uncompressed_size); + zip->compressed_size = u4(p->compressed_size); + + (a->decompressor->consume)(a, sizeof(struct zip_file_header)); + + + /* Read the filename. */ + bytes_read = (a->decompressor->read_ahead)(a, &h, zip->filename_length); + if (bytes_read < zip->filename_length) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated ZIP file header"); + return (ARCHIVE_FATAL); + } + archive_string_ensure(&zip->pathname, zip->filename_length); + archive_strncpy(&zip->pathname, (const char *)h, zip->filename_length); + (a->decompressor->consume)(a, zip->filename_length); + archive_entry_set_pathname(entry, zip->pathname.s); + + if (zip->pathname.s[archive_strlen(&zip->pathname) - 1] == '/') + zip->mode = AE_IFDIR | 0777; + else + zip->mode = AE_IFREG | 0777; + + /* Read the extra data. */ + bytes_read = (a->decompressor->read_ahead)(a, &h, zip->extra_length); + if (bytes_read < zip->extra_length) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated ZIP file header"); + return (ARCHIVE_FATAL); + } + process_extra(h, zip); + (a->decompressor->consume)(a, zip->extra_length); + + /* Populate some additional entry fields: */ + archive_entry_set_mode(entry, zip->mode); + archive_entry_set_uid(entry, zip->uid); + archive_entry_set_gid(entry, zip->gid); + archive_entry_set_mtime(entry, zip->mtime, 0); + archive_entry_set_ctime(entry, zip->ctime, 0); + archive_entry_set_atime(entry, zip->atime, 0); + archive_entry_set_size(entry, zip->uncompressed_size); + + zip->entry_bytes_remaining = zip->compressed_size; + zip->entry_offset = 0; + + /* Set up a more descriptive format name. */ + sprintf(zip->format_name, "ZIP %d.%d (%s)", + zip->version / 10, zip->version % 10, + zip->compression_name); + a->archive.archive_format_name = zip->format_name; + + return (ARCHIVE_OK); +} + +/* Convert an MSDOS-style date/time into Unix-style time. */ +static time_t +zip_time(const char *p) +{ + int msTime, msDate; + struct tm ts; + + msTime = (0xff & (unsigned)p[0]) + 256 * (0xff & (unsigned)p[1]); + msDate = (0xff & (unsigned)p[2]) + 256 * (0xff & (unsigned)p[3]); + + memset(&ts, 0, sizeof(ts)); + ts.tm_year = ((msDate >> 9) & 0x7f) + 80; /* Years since 1900. */ + ts.tm_mon = ((msDate >> 5) & 0x0f) - 1; /* Month number. */ + ts.tm_mday = msDate & 0x1f; /* Day of month. */ + ts.tm_hour = (msTime >> 11) & 0x1f; + ts.tm_min = (msTime >> 5) & 0x3f; + ts.tm_sec = (msTime << 1) & 0x3e; + ts.tm_isdst = -1; + return mktime(&ts); +} + +static int +archive_read_format_zip_read_data(struct archive_read *a, + const void **buff, size_t *size, off_t *offset) +{ + int r; + struct zip *zip; + + zip = (struct zip *)(a->format->data); + + /* + * If we hit end-of-entry last time, clean up and return + * ARCHIVE_EOF this time. + */ + if (zip->end_of_entry) { + if (!zip->end_of_entry_cleanup) { + if (zip->flags & ZIP_LENGTH_AT_END) { + const void *h; + const char *p; + int bytes_read = + (a->decompressor->read_ahead)(a, &h, 16); + if (bytes_read < 16) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated ZIP end-of-file record"); + return (ARCHIVE_FATAL); + } + p = (const char *)h; + zip->crc32 = i4(p + 4); + zip->compressed_size = u4(p + 8); + zip->uncompressed_size = u4(p + 12); + bytes_read = (a->decompressor->consume)(a, 16); + } + + /* Check file size, CRC against these values. */ + if (zip->compressed_size != zip->entry_compressed_bytes_read) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "ZIP compressed data is wrong size"); + return (ARCHIVE_WARN); + } + /* Size field only stores the lower 32 bits of the actual size. */ + if ((zip->uncompressed_size & UINT32_MAX) + != (zip->entry_uncompressed_bytes_read & UINT32_MAX)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "ZIP uncompressed data is wrong size"); + return (ARCHIVE_WARN); + } +/* TODO: Compute CRC. */ +/* + if (zip->crc32 != zip->entry_crc32_calculated) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "ZIP data CRC error"); + return (ARCHIVE_WARN); + } +*/ + /* End-of-entry cleanup done. */ + zip->end_of_entry_cleanup = 1; + } + return (ARCHIVE_EOF); + } + + switch(zip->compression) { + case 0: /* No compression. */ + r = zip_read_data_none(a, buff, size, offset); + break; + case 8: /* Deflate compression. */ + r = zip_read_data_deflate(a, buff, size, offset); + break; + default: /* Unsupported compression. */ + *buff = NULL; + *size = 0; + *offset = 0; + /* Return a warning. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Unsupported ZIP compression method (%s)", + zip->compression_name); + if (zip->flags & ZIP_LENGTH_AT_END) { + /* + * ZIP_LENGTH_AT_END requires us to + * decompress the entry in order to + * skip it, but we don't know this + * compression method, so we give up. + */ + r = ARCHIVE_FATAL; + } else { + /* We know compressed size; just skip it. */ + archive_read_format_zip_read_data_skip(a); + r = ARCHIVE_WARN; + } + break; + } + return (r); +} + +/* + * Read "uncompressed" data. According to the current specification, + * if ZIP_LENGTH_AT_END is specified, then the size fields in the + * initial file header are supposed to be set to zero. This would, of + * course, make it impossible for us to read the archive, since we + * couldn't determine the end of the file data. Info-ZIP seems to + * include the real size fields both before and after the data in this + * case (the CRC only appears afterwards), so this works as you would + * expect. + * + * Returns ARCHIVE_OK if successful, ARCHIVE_FATAL otherwise, sets + * zip->end_of_entry if it consumes all of the data. + */ +static int +zip_read_data_none(struct archive_read *a, const void **buff, + size_t *size, off_t *offset) +{ + struct zip *zip; + ssize_t bytes_avail; + + zip = (struct zip *)(a->format->data); + + if (zip->entry_bytes_remaining == 0) { + *buff = NULL; + *size = 0; + *offset = zip->entry_offset; + zip->end_of_entry = 1; + return (ARCHIVE_OK); + } + /* + * Note: '1' here is a performance optimization. + * Recall that the decompression layer returns a count of + * available bytes; asking for more than that forces the + * decompressor to combine reads by copying data. + */ + bytes_avail = (a->decompressor->read_ahead)(a, buff, 1); + if (bytes_avail <= 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated ZIP file data"); + return (ARCHIVE_FATAL); + } + if (bytes_avail > zip->entry_bytes_remaining) + bytes_avail = zip->entry_bytes_remaining; + (a->decompressor->consume)(a, bytes_avail); + *size = bytes_avail; + *offset = zip->entry_offset; + zip->entry_offset += *size; + zip->entry_bytes_remaining -= *size; + zip->entry_uncompressed_bytes_read += *size; + zip->entry_compressed_bytes_read += *size; + return (ARCHIVE_OK); +} + +#ifdef HAVE_ZLIB_H +static int +zip_read_data_deflate(struct archive_read *a, const void **buff, + size_t *size, off_t *offset) +{ + struct zip *zip; + ssize_t bytes_avail; + const void *compressed_buff; + int r; + + zip = (struct zip *)(a->format->data); + + /* If the buffer hasn't been allocated, allocate it now. */ + if (zip->uncompressed_buffer == NULL) { + zip->uncompressed_buffer_size = 32 * 1024; + zip->uncompressed_buffer + = (unsigned char *)malloc(zip->uncompressed_buffer_size); + if (zip->uncompressed_buffer == NULL) { + archive_set_error(&a->archive, ENOMEM, + "No memory for ZIP decompression"); + return (ARCHIVE_FATAL); + } + } + + /* If we haven't yet read any data, initialize the decompressor. */ + if (!zip->decompress_init) { + r = inflateInit2(&zip->stream, + -15 /* Don't check for zlib header */); + if (r != Z_OK) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Can't initialize ZIP decompression."); + return (ARCHIVE_FATAL); + } + zip->decompress_init = 1; + } + + /* + * Note: '1' here is a performance optimization. + * Recall that the decompression layer returns a count of + * available bytes; asking for more than that forces the + * decompressor to combine reads by copying data. + */ + bytes_avail = (a->decompressor->read_ahead)(a, &compressed_buff, 1); + if (bytes_avail <= 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated ZIP file body"); + return (ARCHIVE_FATAL); + } + + /* + * A bug in zlib.h: stream.next_in should be marked 'const' + * but isn't (the library never alters data through the + * next_in pointer, only reads it). The result: this ugly + * cast to remove 'const'. + */ + zip->stream.next_in = (Bytef *)(uintptr_t)(const void *)compressed_buff; + zip->stream.avail_in = bytes_avail; + zip->stream.total_in = 0; + zip->stream.next_out = zip->uncompressed_buffer; + zip->stream.avail_out = zip->uncompressed_buffer_size; + zip->stream.total_out = 0; + + r = inflate(&zip->stream, 0); + switch (r) { + case Z_OK: + break; + case Z_STREAM_END: + zip->end_of_entry = 1; + break; + case Z_MEM_ERROR: + archive_set_error(&a->archive, ENOMEM, + "Out of memory for ZIP decompression"); + return (ARCHIVE_FATAL); + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "ZIP decompression failed (%d)", r); + return (ARCHIVE_FATAL); + } + + /* Consume as much as the compressor actually used. */ + bytes_avail = zip->stream.total_in; + (a->decompressor->consume)(a, bytes_avail); + zip->entry_bytes_remaining -= bytes_avail; + zip->entry_compressed_bytes_read += bytes_avail; + + *offset = zip->entry_offset; + *size = zip->stream.total_out; + zip->entry_uncompressed_bytes_read += *size; + *buff = zip->uncompressed_buffer; + zip->entry_offset += *size; + return (ARCHIVE_OK); +} +#else +static int +zip_read_data_deflate(struct archive_read *a, const void **buff, + size_t *size, off_t *offset) +{ + *buff = NULL; + *size = 0; + *offset = 0; + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "libarchive compiled without deflate support (no libz)"); + return (ARCHIVE_FATAL); +} +#endif + +static int +archive_read_format_zip_read_data_skip(struct archive_read *a) +{ + struct zip *zip; + const void *buff = NULL; + ssize_t bytes_avail; + + zip = (struct zip *)(a->format->data); + + /* + * If the length is at the end, we have no choice but + * to decompress all the data to find the end marker. + */ + if (zip->flags & ZIP_LENGTH_AT_END) { + size_t size; + off_t offset; + int r; + do { + r = archive_read_format_zip_read_data(a, &buff, + &size, &offset); + } while (r == ARCHIVE_OK); + return (r); + } + + /* + * If the length is at the beginning, we can skip the + * compressed data much more quickly. + */ + while (zip->entry_bytes_remaining > 0) { + bytes_avail = (a->decompressor->read_ahead)(a, &buff, 1); + if (bytes_avail <= 0) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated ZIP file body"); + return (ARCHIVE_FATAL); + } + if (bytes_avail > zip->entry_bytes_remaining) + bytes_avail = zip->entry_bytes_remaining; + (a->decompressor->consume)(a, bytes_avail); + zip->entry_bytes_remaining -= bytes_avail; + } + /* This entry is finished and done. */ + zip->end_of_entry_cleanup = zip->end_of_entry = 1; + return (ARCHIVE_OK); +} + +static int +archive_read_format_zip_cleanup(struct archive_read *a) +{ + struct zip *zip; + + zip = (struct zip *)(a->format->data); + if (zip->uncompressed_buffer != NULL) + free(zip->uncompressed_buffer); + archive_string_free(&(zip->pathname)); + archive_string_free(&(zip->extra)); + free(zip); + (a->format->data) = NULL; + return (ARCHIVE_OK); +} + +static int +i2(const char *p) +{ + return ((0xff & (int)p[0]) + 256 * (0xff & (int)p[1])); +} + + +static int +i4(const char *p) +{ + return ((0xffff & i2(p)) + 0x10000 * (0xffff & i2(p+2))); +} + +static unsigned int +u2(const char *p) +{ + return ((0xff & (unsigned int)p[0]) + 256 * (0xff & (unsigned int)p[1])); +} + +static unsigned int +u4(const char *p) +{ + return u2(p) + 0x10000 * u2(p+2); +} + +static uint64_t +u8(const char *p) +{ + return u4(p) + 0x100000000LL * u4(p+4); +} + +/* + * The extra data is stored as a list of + * id1+size1+data1 + id2+size2+data2 ... + * triplets. id and size are 2 bytes each. + */ +static void +process_extra(const void* extra, struct zip* zip) +{ + int offset = 0; + const char *p = (const char *)extra; + while (offset < zip->extra_length - 4) + { + unsigned short headerid = u2(p + offset); + unsigned short datasize = u2(p + offset + 2); + offset += 4; + if (offset + datasize > zip->extra_length) + break; +#ifdef DEBUG + fprintf(stderr, "Header id 0x%04x, length %d\n", + headerid, datasize); +#endif + switch (headerid) { + case 0x0001: + /* Zip64 extended information extra field. */ + if (datasize >= 8) + zip->uncompressed_size = u8(p + offset); + if (datasize >= 16) + zip->compressed_size = u8(p + offset + 8); + break; + case 0x5455: + { + /* Extended time field "UT". */ + int flags = p[offset]; + offset++; + datasize--; + /* Flag bits indicate which dates are present. */ + if (flags & 0x01) + { +#ifdef DEBUG + fprintf(stderr, "mtime: %d -> %d\n", + zip->mtime, i4(p + offset)); +#endif + if (datasize < 4) + break; + zip->mtime = i4(p + offset); + offset += 4; + datasize -= 4; + } + if (flags & 0x02) + { + if (datasize < 4) + break; + zip->atime = i4(p + offset); + offset += 4; + datasize -= 4; + } + if (flags & 0x04) + { + if (datasize < 4) + break; + zip->ctime = i4(p + offset); + offset += 4; + datasize -= 4; + } + break; + } + case 0x7855: + /* Info-ZIP Unix Extra Field (type 2) "Ux". */ +#ifdef DEBUG + fprintf(stderr, "uid %d gid %d\n", + i2(p + offset), i2(p + offset + 2)); +#endif + if (datasize >= 2) + zip->uid = i2(p + offset); + if (datasize >= 4) + zip->gid = i2(p + offset + 2); + break; + default: + break; + } + offset += datasize; + } +#ifdef DEBUG + if (offset != zip->extra_length) + { + fprintf(stderr, + "Extra data field contents do not match reported size!"); + } +#endif +} diff --git a/contrib/libarchive-2.1/libarchive/archive_string.c b/contrib/libarchive-2.1/libarchive/archive_string.c new file mode 100644 index 0000000000..e1041453ff --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_string.c @@ -0,0 +1,125 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_string.c,v 1.9 2007/01/13 03:30:14 kientzle Exp $"); + +/* + * Basic resizable string support, to simplify manipulating arbitrary-sized + * strings while minimizing heap activity. + */ + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "archive_private.h" +#include "archive_string.h" + +struct archive_string * +__archive_string_append(struct archive_string *as, const char *p, size_t s) +{ + __archive_string_ensure(as, as->length + s + 1); + memcpy(as->s + as->length, p, s); + as->s[as->length + s] = 0; + as->length += s; + return (as); +} + +void +__archive_string_copy(struct archive_string *dest, struct archive_string *src) +{ + __archive_string_ensure(dest, src->length + 1); + memcpy(dest->s, src->s, src->length); + dest->length = src->length; + dest->s[dest->length] = 0; +} + +void +__archive_string_free(struct archive_string *as) +{ + as->length = 0; + as->buffer_length = 0; + if (as->s != NULL) + free(as->s); +} + +struct archive_string * +__archive_string_ensure(struct archive_string *as, size_t s) +{ + if (as->s && (s <= as->buffer_length)) + return (as); + + if (as->buffer_length < 32) + as->buffer_length = 32; + while (as->buffer_length < s) + as->buffer_length *= 2; + as->s = (char *)realloc(as->s, as->buffer_length); + /* TODO: Return null instead and fix up all of our callers to + * handle this correctly. */ + if (as->s == NULL) + __archive_errx(1, "Out of memory"); + return (as); +} + +struct archive_string * +__archive_strncat(struct archive_string *as, const char *p, size_t n) +{ + size_t s; + const char *pp; + + /* Like strlen(p), except won't examine positions beyond p[n]. */ + s = 0; + pp = p; + while (*pp && s < n) { + pp++; + s++; + } + return (__archive_string_append(as, p, s)); +} + +struct archive_string * +__archive_strappend_char(struct archive_string *as, char c) +{ + return (__archive_string_append(as, &c, 1)); +} + +struct archive_string * +__archive_strappend_int(struct archive_string *as, int d, int base) +{ + static const char *digits = "0123456789abcdef"; + + if (d < 0) { + __archive_strappend_char(as, '-'); + d = -d; + } + if (d >= base) + __archive_strappend_int(as, d/base, base); + __archive_strappend_char(as, digits[d % base]); + return (as); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_string.h b/contrib/libarchive-2.1/libarchive/archive_string.h new file mode 100644 index 0000000000..749f591d60 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_string.h @@ -0,0 +1,119 @@ +/*- + * 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. + * + * $FreeBSD: src/lib/libarchive/archive_string.h,v 1.8 2007/01/09 08:05:55 kientzle Exp $ + * + */ + +#ifndef ARCHIVE_STRING_H_INCLUDED +#define ARCHIVE_STRING_H_INCLUDED + +#include +#ifdef HAVE_STRING_H +#include +#endif + +/* + * Basic resizable/reusable string support a la Java's "StringBuffer." + * + * Unlike sbuf(9), the buffers here are fully reusable and track the + * length throughout. + * + * Note that all visible symbols here begin with "__archive" as they + * are internal symbols not intended for anyone outside of this library + * to see or use. + */ + +struct archive_string { + char *s; /* Pointer to the storage */ + size_t length; /* Length of 's' */ + size_t buffer_length; /* Length of malloc-ed storage */ +}; + +/* Initialize an archive_string object on the stack or elsewhere. */ +#define archive_string_init(a) \ + do { (a)->s = NULL; (a)->length = 0; (a)->buffer_length = 0; } while(0) + +/* Append a C char to an archive_string, resizing as necessary. */ +struct archive_string * +__archive_strappend_char(struct archive_string *, char); +#define archive_strappend_char __archive_strappend_char + +/* Append a char to an archive_string using UTF8. */ +struct archive_string * +__archive_strappend_char_UTF8(struct archive_string *, int); +#define archive_strappend_char_UTF8 __archive_strappend_char_UTF8 + +/* Append an integer in the specified base (2 <= base <= 16). */ +struct archive_string * +__archive_strappend_int(struct archive_string *as, int d, int base); +#define archive_strappend_int __archive_strappend_int + +/* Basic append operation. */ +struct archive_string * +__archive_string_append(struct archive_string *as, const char *p, size_t s); + +/* Copy one archive_string to another */ +void +__archive_string_copy(struct archive_string *dest, struct archive_string *src); +#define archive_string_copy(dest, src) \ + __archive_string_copy(dest, src) + +/* Ensure that the underlying buffer is at least as large as the request. */ +struct archive_string * +__archive_string_ensure(struct archive_string *, size_t); +#define archive_string_ensure __archive_string_ensure + +/* Append C string, which may lack trailing \0. */ +struct archive_string * +__archive_strncat(struct archive_string *, const char *, size_t); +#define archive_strncat __archive_strncat + +/* Append a C string to an archive_string, resizing as necessary. */ +#define archive_strcat(as,p) __archive_string_append((as),(p),strlen(p)) + +/* Copy a C string to an archive_string, resizing as necessary. */ +#define archive_strcpy(as,p) \ + ((as)->length = 0, __archive_string_append((as), (p), strlen(p))) + +/* Copy a C string to an archive_string with limit, resizing as necessary. */ +#define archive_strncpy(as,p,l) \ + ((as)->length=0, archive_strncat((as), (p), (l))) + +/* Return length of string. */ +#define archive_strlen(a) ((a)->length) + +/* Set string length to zero. */ +#define archive_string_empty(a) ((a)->length = 0) + +/* Release any allocated storage resources. */ +void __archive_string_free(struct archive_string *); +#define archive_string_free __archive_string_free + +/* Like 'vsprintf', but resizes the underlying string as necessary. */ +void __archive_string_vsprintf(struct archive_string *, const char *, + va_list); +#define archive_string_vsprintf __archive_string_vsprintf + +#endif diff --git a/contrib/libarchive-2.1/libarchive/archive_string_sprintf.c b/contrib/libarchive-2.1/libarchive/archive_string_sprintf.c new file mode 100644 index 0000000000..13eff5c103 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_string_sprintf.c @@ -0,0 +1,128 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_string_sprintf.c,v 1.8 2007/01/09 08:05:55 kientzle Exp $"); + +/* + * The use of printf()-family functions can be troublesome + * for space-constrained applications. In addition, correctly + * implementing this function in terms of vsnprintf() requires + * two calls (one to determine the size, another to format the + * result), which in turn requires duplicating the argument list + * using va_copy, which isn't yet universally available. + * + * So, I've implemented a bare minimum of printf()-like capability + * here. This is only used to format error messages, so doesn't + * require any floating-point support or field-width handling. + */ + +#include + +#include "archive_string.h" + +/* + * Like 'vsprintf', but ensures the target is big enough, resizing if + * necessary. + */ +void +__archive_string_vsprintf(struct archive_string *as, const char *fmt, + va_list ap) +{ + char long_flag; + intmax_t s; /* Signed integer temp. */ + uintmax_t u; /* Unsigned integer temp. */ + const char *p, *p2; + + __archive_string_ensure(as, 64); + + if (fmt == NULL) { + as->s[0] = 0; + return; + } + + long_flag = '\0'; + for (p = fmt; *p != '\0'; p++) { + const char *saved_p = p; + + if (*p != '%') { + archive_strappend_char(as, *p); + continue; + } + + p++; + + switch(*p) { + case 'j': + long_flag = 'j'; + p++; + break; + case 'l': + long_flag = 'l'; + p++; + break; + } + + switch (*p) { + case '%': + __archive_strappend_char(as, '%'); + break; + case 'c': + s = va_arg(ap, int); + __archive_strappend_char(as, s); + break; + case 'd': + switch(long_flag) { + case 'j': s = va_arg(ap, intmax_t); break; + case 'l': s = va_arg(ap, long); break; + default: s = va_arg(ap, int); break; + } + archive_strappend_int(as, s, 10); + break; + case 's': + p2 = va_arg(ap, char *); + archive_strcat(as, p2); + break; + case 'o': case 'u': case 'x': case 'X': + /* Common handling for unsigned integer formats. */ + switch(long_flag) { + case 'j': u = va_arg(ap, uintmax_t); break; + case 'l': u = va_arg(ap, unsigned long); break; + default: u = va_arg(ap, unsigned int); break; + } + /* Format it in the correct base. */ + switch (*p) { + case 'o': archive_strappend_int(as, u, 8); break; + case 'u': archive_strappend_int(as, u, 10); break; + default: archive_strappend_int(as, u, 16); break; + } + break; + default: + /* Rewind and print the initial '%' literally. */ + p = saved_p; + archive_strappend_char(as, *p); + } + } +} diff --git a/contrib/libarchive-2.1/libarchive/archive_util.3 b/contrib/libarchive-2.1/libarchive/archive_util.3 new file mode 100644 index 0000000000..4fc2a4871e --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_util.3 @@ -0,0 +1,146 @@ +.\" 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. +.\" +.\" $FreeBSD: src/lib/libarchive/archive_util.3,v 1.6 2007/03/03 07:37:36 kientzle Exp $ +.\" +.Dd January 8, 2005 +.Dt archive_util 3 +.Os +.Sh NAME +.Nm archive_clear_error , +.Nm archive_compression , +.Nm archive_compression_name , +.Nm archive_errno , +.Nm archive_error_string , +.Nm archive_format , +.Nm archive_format_name , +.Nm archive_set_error , +.Nm archive_copy_error +.Nd libarchive utility functions +.Sh SYNOPSIS +.In archive.h +.Ft void +.Fn archive_clear_error "struct archive *" +.Ft int +.Fn archive_compression "struct archive *" +.Ft const char * +.Fn archive_compression_name "struct archive *" +.Ft int +.Fn archive_errno "struct archive *" +.Ft const char * +.Fn archive_error_string "struct archive *" +.Ft int +.Fn archive_format "struct archive *" +.Ft const char * +.Fn archive_format_name "struct archive *" +.Ft void +.Fn archive_set_error "struct archive *" "int error_code" "const char *fmt" "..." +.Ft void +.Fn archive_copy_error "struct archive *" "struct archive *" +.Sh DESCRIPTION +These functions provide access to various information about the +.Tn struct archive +object used in the +.Xr libarchive 3 +library. +.Bl -tag -compact -width indent +.It Fn archive_clear_error +Clears any error information left over from a previous call. +Not generally used in client code. +.It Fn archive_compression +Returns a numeric code indicating the current compression. +This value is set by +.Fn archive_read_open . +.It Fn archive_compression_name +Returns a text description of the current compression suitable for display. +.It Fn archive_errno +Returns a numeric error code (see +.Xr errno 2 ) +indicating the reason for the most recent error return. +.It Fn archive_error_string +Returns a textual error message suitable for display. +The error message here is usually more specific than that +obtained from passing the result of +.Fn archive_errno +to +.Xr strerror 3 . +.It Fn archive_format +Returns a numeric code indicating the format of the current +archive entry. +This value is set by a successful call to +.Fn archive_read_next_header . +Note that it is common for this value to change from +entry to entry. +For example, a tar archive might have several entries that +utilize GNU tar extensions and several entries that do not. +These entries will have different format codes. +.It Fn archive_format_name +A textual description of the format of the current entry. +.It Fn archive_set_error +Sets the numeric error code and error description that will be returned +by +.Fn archive_errno +and +.Fn archive_error_string . +This function should be used within I/O callbacks to set system-specific +error codes and error descriptions. +This function accepts a printf-like format string and arguments. +However, you should be careful to use only the following printf +format specifiers: +.Dq %c , +.Dq %d , +.Dq %jd , +.Dq %jo , +.Dq %ju , +.Dq %jx , +.Dq %ld , +.Dq %lo , +.Dq %lu , +.Dq %lx , +.Dq %o , +.Dq %u , +.Dq %s , +.Dq %x , +.Dq %% . +Field-width specifiers and other printf features are +not uniformly supported and should not be used. +.It Fn archive_copy_error +Copies error information from one archive to another. +.El +.Sh SEE ALSO +.Xr archive_read 3 , +.Xr archive_write 3 , +.Xr libarchive 3 , +.Xr printf 3 +.Sh HISTORY +The +.Nm libarchive +library first appeared in +.Fx 5.3 . +.Sh AUTHORS +.An -nosplit +The +.Nm libarchive +library was written by +.An Tim Kientzle Aq kientzle@acm.org . diff --git a/contrib/libarchive-2.1/libarchive/archive_util.c b/contrib/libarchive-2.1/libarchive/archive_util.c new file mode 100644 index 0000000000..63bd5ae908 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_util.c @@ -0,0 +1,181 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_util.c,v 1.13 2007/03/03 07:37:36 kientzle Exp $"); + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "archive.h" +#include "archive_private.h" + +int +archive_api_feature(void) +{ + return (ARCHIVE_API_FEATURE); +} + +int +archive_api_version(void) +{ + return (ARCHIVE_API_VERSION); +} + +const char * +archive_version(void) +{ + return (PACKAGE_NAME " " PACKAGE_VERSION); +} + +int +archive_errno(struct archive *a) +{ + return (a->archive_error_number); +} + +const char * +archive_error_string(struct archive *a) +{ + + if (a->error != NULL && *a->error != '\0') + return (a->error); + else + return ("(Empty error message)"); +} + + +int +archive_format(struct archive *a) +{ + return (a->archive_format); +} + +const char * +archive_format_name(struct archive *a) +{ + return (a->archive_format_name); +} + + +int +archive_compression(struct archive *a) +{ + return (a->compression_code); +} + +const char * +archive_compression_name(struct archive *a) +{ + return (a->compression_name); +} + + +/* + * Return a count of the number of compressed bytes processed. + */ +int64_t +archive_position_compressed(struct archive *a) +{ + return (a->raw_position); +} + +/* + * Return a count of the number of uncompressed bytes processed. + */ +int64_t +archive_position_uncompressed(struct archive *a) +{ + return (a->file_position); +} + +void +archive_clear_error(struct archive *a) +{ + archive_string_empty(&a->error_string); + a->error = NULL; +} + +void +archive_set_error(struct archive *a, int error_number, const char *fmt, ...) +{ + va_list ap; +#ifdef HAVE_STRERROR_R + char errbuff[512]; +#endif + char *errp; + + a->archive_error_number = error_number; + if (fmt == NULL) { + a->error = NULL; + return; + } + + va_start(ap, fmt); + archive_string_vsprintf(&(a->error_string), fmt, ap); + if (error_number > 0) { + archive_strcat(&(a->error_string), ": "); +#ifdef HAVE_STRERROR_R +#ifdef STRERROR_R_CHAR_P + errp = strerror_r(error_number, errbuff, sizeof(errbuff)); +#else + strerror_r(error_number, errbuff, sizeof(errbuff)); + errp = errbuff; +#endif +#else + /* Note: this is not threadsafe! */ + errp = strerror(error_number); +#endif + archive_strcat(&(a->error_string), errp); + } + a->error = a->error_string.s; + va_end(ap); +} + +void +archive_copy_error(struct archive *dest, struct archive *src) +{ + dest->archive_error_number = src->archive_error_number; + + archive_string_copy(&dest->error_string, &src->error_string); + dest->error = dest->error_string.s; +} + +void +__archive_errx(int retvalue, const char *msg) +{ + static const char *msg1 = "Fatal Internal Error in libarchive: "; + write(2, msg1, strlen(msg1)); + write(2, msg, strlen(msg)); + write(2, "\n", 1); + exit(retvalue); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_virtual.c b/contrib/libarchive-2.1/libarchive/archive_virtual.c new file mode 100644 index 0000000000..a431058c67 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_virtual.c @@ -0,0 +1,81 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_virtual.c,v 1.1 2007/03/03 07:37:36 kientzle Exp $"); + +#include "archive.h" +#include "archive_entry.h" +#include "archive_private.h" + +int +archive_write_close(struct archive *a) +{ + return ((a->vtable->archive_write_close)(a)); +} + +#if ARCHIVE_API_VERSION > 1 +int +archive_write_finish(struct archive *a) +{ + return ((a->vtable->archive_write_finish)(a)); +} +#else +/* Temporarily allow library to compile with either 1.x or 2.0 API. */ +void +archive_write_finish(struct archive *a) +{ + (void)(a->vtable->archive_write_finish)(a); +} +#endif + +int +archive_write_header(struct archive *a, struct archive_entry *entry) +{ + return ((a->vtable->archive_write_header)(a, entry)); +} + +int +archive_write_finish_entry(struct archive *a) +{ + return ((a->vtable->archive_write_finish_entry)(a)); +} + +#if ARCHIVE_API_VERSION > 1 +ssize_t +#else +/* Temporarily allow library to compile with either 1.x or 2.0 API. */ +int +#endif +archive_write_data(struct archive *a, const void *buff, size_t s) +{ + return ((a->vtable->archive_write_data)(a, buff, s)); +} + +ssize_t +archive_write_data_block(struct archive *a, const void *buff, size_t s, off_t o) +{ + return ((a->vtable->archive_write_data_block)(a, buff, s, o)); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_write.3 b/contrib/libarchive-2.1/libarchive/archive_write.3 new file mode 100644 index 0000000000..d3f19fb4ab --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_write.3 @@ -0,0 +1,514 @@ +.\" 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. +.\" +.\" $FreeBSD: src/lib/libarchive/archive_write.3,v 1.21 2007/03/03 07:37:36 kientzle Exp $ +.\" +.Dd August 19, 2006 +.Dt archive_write 3 +.Os +.Sh NAME +.Nm archive_write_new , +.Nm archive_write_set_format_cpio , +.Nm archive_write_set_format_pax , +.Nm archive_write_set_format_pax_restricted , +.Nm archive_write_set_format_shar , +.Nm archive_write_set_format_shar_binary , +.Nm archive_write_set_format_ustar , +.Nm archive_write_get_bytes_per_block , +.Nm archive_write_set_bytes_per_block , +.Nm archive_write_set_bytes_in_last_block , +.Nm archive_write_set_compressor_gzip , +.Nm archive_write_set_compressor_bzip2 , +.Nm archive_write_open , +.Nm archive_write_open_fd , +.Nm archive_write_open_FILE , +.Nm archive_write_open_filename , +.Nm archive_write_open_memory , +.Nm archive_write_header , +.Nm archive_write_data , +.Nm archive_write_finish_entry , +.Nm archive_write_close , +.Nm archive_write_finish +.Nd functions for creating archives +.Sh SYNOPSIS +.In archive.h +.Ft struct archive * +.Fn archive_write_new "void" +.Ft int +.Fn archive_write_get_bytes_per_block "struct archive *" +.Ft int +.Fn archive_write_set_bytes_per_block "struct archive *" "int bytes_per_block" +.Ft int +.Fn archive_write_set_bytes_in_last_block "struct archive *" "int" +.Ft int +.Fn archive_write_set_compressor_gzip "struct archive *" +.Ft int +.Fn archive_write_set_compressor_bzip2 "struct archive *" +.Ft int +.Fn archive_write_set_format_cpio "struct archive *" +.Ft int +.Fn archive_write_set_format_pax "struct archive *" +.Ft int +.Fn archive_write_set_format_pax_restricted "struct archive *" +.Ft int +.Fn archive_write_set_format_shar "struct archive *" +.Ft int +.Fn archive_write_set_format_shar_binary "struct archive *" +.Ft int +.Fn archive_write_set_format_ustar "struct archive *" +.Ft int +.Fn archive_write_open "struct archive *" "void *client_data" "archive_open_callback *" "archive_write_callback *" "archive_close_callback *" +.Ft int +.Fn archive_write_open_fd "struct archive *" "int fd" +.Ft int +.Fn archive_write_open_FILE "struct archive *" "FILE *file" +.Ft int +.Fn archive_write_open_filename "struct archive *" "const char *filename" +.Ft int +.Fn archive_write_open_memory "struct archive *" "void *buffer" "size_t bufferSize" "size_t *outUsed" +.Ft int +.Fn archive_write_header "struct archive *" "struct archive_entry *" +.Ft ssize_t +.Fn archive_write_data "struct archive *" "const void *" "size_t" +.Ft int +.Fn archive_write_finish_entry "struct archive *" +.Ft int +.Fn archive_write_close "struct archive *" +.Ft int +.Fn archive_write_finish "struct archive *" +.Sh DESCRIPTION +These functions provide a complete API for creating streaming +archive files. +The general process is to first create the +.Tn struct archive +object, set any desired options, initialize the archive, append entries, then +close the archive and release all resources. +The following summary describes the functions in approximately +the order they are ordinarily used: +.Bl -tag -width indent +.It Fn archive_write_new +Allocates and initializes a +.Tn struct archive +object suitable for writing a tar archive. +.It Fn archive_write_set_bytes_per_block +Sets the block size used for writing the archive data. +Every call to the write callback function, except possibly the last one, will +use this value for the length. +The third parameter is a boolean that specifies whether or not the final block +written will be padded to the full block size. +If it is zero, the last block will not be padded. +If it is non-zero, padding will be added both before and after compression. +The default is to use a block size of 10240 bytes and to pad the last block. +Note that a block size of zero will suppress internal blocking +and cause writes to be sent directly to the write callback as they occur. +.It Fn archive_write_get_bytes_per_block +Retrieve the block size to be used for writing. +A value of -1 here indicates that the library should use default values. +A value of zero indicates that internal blocking is suppressed. +.It Fn archive_write_set_bytes_in_last_block +Sets the block size used for writing the last block. +If this value is zero, the last block will be padded to the same size +as the other blocks. +Otherwise, the final block will be padded to a multiple of this size. +In particular, setting it to 1 will cause the final block to not be padded. +For compressed output, any padding generated by this option +is applied only after the compression. +The uncompressed data is always unpadded. +The default is to pad the last block to the full block size (note that +.Fn archive_write_open_filename +will set this based on the file type). +Unlike the other +.Dq set +functions, this function can be called after the archive is opened. +.It Fn archive_write_get_bytes_in_last_block +Retrieve the currently-set value for last block size. +A value of -1 here indicates that the library should use default values. +.It Fn archive_write_set_format_cpio , Fn archive_write_set_format_pax , Fn archive_write_set_format_pax_restricted , Fn archive_write_set_format_shar , Fn archive_write_set_format_shar_binary , Fn archive_write_set_format_ustar +Sets the format that will be used for the archive. +The library can write +POSIX octet-oriented cpio format archives, +POSIX-standard +.Dq pax interchange +format archives, +traditional +.Dq shar +archives, +enhanced +.Dq binary +shar archives that store a variety of file attributes and handle binary files, +and +POSIX-standard +.Dq ustar +archives. +The pax interchange format is a backwards-compatible tar format that +adds key/value attributes to each entry and supports arbitrary +filenames, linknames, uids, sizes, etc. +.Dq Restricted pax interchange format +is the library default; this is the same as pax format, but suppresses +the pax extended header for most normal files. +In most cases, this will result in ordinary ustar archives. +.It Fn archive_write_set_compression_gzip , Fn archive_write_set_compression_bzip2 +The resulting archive will be compressed as specified. +Note that the compressed output is always properly blocked. +.It Fn archive_write_open +Freeze the settings, open the archive, and prepare for writing entries. +This is the most generic form of this function, which accepts +pointers to three callback functions which will be invoked by +the compression layer to write the constructed archive. +.It Fn archive_write_open_fd +A convenience form of +.Fn archive_write_open +that accepts a file descriptor. +The +.Fn archive_write_open_fd +function is safe for use with tape drives or other +block-oriented devices. +.It Fn archive_write_open_FILE +A convenience form of +.Fn archive_write_open +that accepts a +.Ft "FILE *" +pointer. +Note that +.Fn archive_write_open_FILE +is not safe for writing to tape drives or other devices +that require correct blocking. +.It Fn archive_write_open_file +A deprecated synonym for +.Fn archive_write_open_filename . +.It Fn archive_write_open_filename +A convenience form of +.Fn archive_write_open +that accepts a filename. +A NULL argument indicates that the output should be written to standard output; +an argument of +.Dq - +will open a file with that name. +If you have not invoked +.Fn archive_write_set_bytes_in_last_block , +then +.Fn archive_write_open_filename +will adjust the last-block padding depending on the file: +it will enable padding when writing to standard output or +to a character or block device node, it will disable padding otherwise. +You can override this by manually invoking +.Fn archive_write_set_bytes_in_last_block +before calling +.Fn archive_write_open . +The +.Fn archive_write_open_filename +function is safe for use with tape drives or other +block-oriented devices. +.It Fn archive_write_open_memory +A convenience form of +.Fn archive_write_open +that accepts a pointer to a block of memory that will receive +the archive. +The final +.Ft "size_t *" +argument points to a variable that will be updated +after each write to reflect how much of the buffer +is currently in use. +You should be careful to ensure that this variable +remains allocated until after the archive is +closed. +.It Fn archive_write_header +Build and write a header using the data in the provided +.Tn struct archive_entry +structure. +See +.Xr archive_entry 3 +for information on creating and populating +.Tn struct archive_entry +objects. +.It Fn archive_write_data +Write data corresponding to the header just written. +Returns number of bytes written or -1 on error. +.It Fn archive_write_finish_entry +Close out the entry just written. +In particular, this writes out the final padding required by some formats. +Ordinarily, clients never need to call this, as it +is called automatically by +.Fn archive_write_next_header +and +.Fn archive_write_close +as needed. +.It Fn archive_write_close +Complete the archive and invoke the close callback. +.It Fn archive_write_finish +Invokes +.Fn archive_write_close +if it was not invoked manually, then releases all resources. +Note that this function was declared to return +.Ft void +in libarchive 1.x, which made it impossible to detect errors when +.Fn archive_write_close +was invoked implicitly from this function. +This is corrected beginning with libarchive 2.0. +.El +More information about the +.Va struct archive +object and the overall design of the library can be found in the +.Xr libarchive 3 +overview. +.Sh IMPLEMENTATION +Compression support is built-in to libarchive, which uses zlib and bzlib +to handle gzip and bzip2 compression, respectively. +.Sh CLIENT CALLBACKS +To use this library, you will need to define and register +callback functions that will be invoked to write data to the +resulting archive. +These functions are registered by calling +.Fn archive_write_open : +.Bl -item -offset indent +.It +.Ft typedef int +.Fn archive_open_callback "struct archive *" "void *client_data" +.El +.Pp +The open callback is invoked by +.Fn archive_write_open . +It should return +.Cm ARCHIVE_OK +if the underlying file or data source is successfully +opened. +If the open fails, it should call +.Fn archive_set_error +to register an error code and message and return +.Cm ARCHIVE_FATAL . +.Bl -item -offset indent +.It +.Ft typedef ssize_t +.Fn archive_write_callback "struct archive *" "void *client_data" "void *buffer" "size_t length" +.El +.Pp +The write callback is invoked whenever the library +needs to write raw bytes to the archive. +For correct blocking, each call to the write callback function +should translate into a single +.Xr write 2 +system call. +This is especially critical when writing archives to tape drives. +On success, the write callback should return the +number of bytes actually written. +On error, the callback should invoke +.Fn archive_set_error +to register an error code and message and return -1. +.Bl -item -offset indent +.It +.Ft typedef int +.Fn archive_close_callback "struct archive *" "void *client_data" +.El +.Pp +The close callback is invoked by archive_close when +the archive processing is complete. +The callback should return +.Cm ARCHIVE_OK +on success. +On failure, the callback should invoke +.Fn archive_set_error +to register an error code and message and +return +.Cm ARCHIVE_FATAL. +.Sh EXAMPLE +The following sketch illustrates basic usage of the library. +In this example, +the callback functions are simply wrappers around the standard +.Xr open 2 , +.Xr write 2 , +and +.Xr close 2 +system calls. +.Bd -literal -offset indent +#include +#include +#include +#include +#include +#include + +struct mydata { + const char *name; + int fd; +}; + +int +myopen(struct archive *a, void *client_data) +{ + struct mydata *mydata = client_data; + + mydata->fd = open(mydata->name, O_WRONLY | O_CREAT, 0644); + if (mydata->fd >= 0) + return (ARCHIVE_OK); + else + return (ARCHIVE_FATAL); +} + +ssize_t +mywrite(struct archive *a, void *client_data, void *buff, size_t n) +{ + struct mydata *mydata = client_data; + + return (write(mydata->fd, buff, n)); +} + +int +myclose(struct archive *a, void *client_data) +{ + struct mydata *mydata = client_data; + + if (mydata->fd > 0) + close(mydata->fd); + return (0); +} + +void +write_archive(const char *outname, const char **filename) +{ + struct mydata *mydata = malloc(sizeof(struct mydata)); + struct archive *a; + struct archive_entry *entry; + struct stat st; + char buff[8192]; + int len; + int fd; + + a = archive_write_new(); + mydata->name = outname; + archive_write_set_compression_gzip(a); + archive_write_set_format_ustar(a); + archive_write_open(a, mydata, myopen, mywrite, myclose); + while (*filename) { + stat(*filename, &st); + entry = archive_entry_new(); + archive_entry_copy_stat(entry, &st); + archive_entry_set_pathname(entry, *filename); + archive_write_header(a, entry); + fd = open(*filename, O_RDONLY); + len = read(fd, buff, sizeof(buff)); + while ( len > 0 ) { + archive_write_data(a, buff, len); + len = read(fd, buff, sizeof(buff)); + } + archive_entry_free(entry); + filename++; + } + archive_write_finish(a); +} + +int main(int argc, const char **argv) +{ + const char *outname; + argv++; + outname = argv++; + write_archive(outname, argv); + return 0; +} +.Ed +.Sh RETURN VALUES +Most functions return +.Cm ARCHIVE_OK +(zero) on success, or one of several non-zero +error codes for errors. +Specific error codes include: +.Cm ARCHIVE_RETRY +for operations that might succeed if retried, +.Cm ARCHIVE_WARN +for unusual conditions that do not prevent further operations, and +.Cm ARCHIVE_FATAL +for serious errors that make remaining operations impossible. +The +.Fn archive_errno +and +.Fn archive_error_string +functions can be used to retrieve an appropriate error code and a +textual error message. +.Pp +.Fn archive_write_new +returns a pointer to a newly-allocated +.Tn struct archive +object. +.Pp +.Fn archive_write_data +returns a count of the number of bytes actually written. +On error, -1 is returned and the +.Fn archive_errno +and +.Fn archive_error_string +functions will return appropriate values. +Note that if the client-provided write callback function +returns a non-zero value, that error will be propagated back to the caller +through whatever API function resulted in that call, which +may include +.Fn archive_write_header , +.Fn archive_write_data , +.Fn archive_write_close , +or +.Fn archive_write_finish . +The client callback can call +.Fn archive_set_error +to provide values that can then be retrieved by +.Fn archive_errno +and +.Fn archive_error_string . +.Sh SEE ALSO +.Xr tar 1 , +.Xr libarchive 3 , +.Xr tar 5 +.Sh HISTORY +The +.Nm libarchive +library first appeared in +.Fx 5.3 . +.Sh AUTHORS +.An -nosplit +The +.Nm libarchive +library was written by +.An Tim Kientzle Aq kientzle@acm.org . +.Sh BUGS +There are many peculiar bugs in historic tar implementations that may cause +certain programs to reject archives written by this library. +For example, several historic implementations calculated header checksums +incorrectly and will thus reject valid archives; GNU tar does not fully support +pax interchange format; some old tar implementations required specific +field terminations. +.Pp +The default pax interchange format eliminates most of the historic +tar limitations and provides a generic key/value attribute facility +for vendor-defined extensions. +One oversight in POSIX is the failure to provide a standard attribute +for large device numbers. +This library uses +.Dq SCHILY.devminor +and +.Dq SCHILY.devmajor +for device numbers that exceed the range supported by the backwards-compatible +ustar header. +These keys are compatible with Joerg Schilling's +.Nm star +archiver. +Other implementations may not recognize these keys and will thus be unable +to correctly restore device nodes with large device numbers from archives +created by this library. diff --git a/contrib/libarchive-2.1/libarchive/archive_write.c b/contrib/libarchive-2.1/libarchive/archive_write.c new file mode 100644 index 0000000000..77e410b177 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_write.c @@ -0,0 +1,353 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_write.c,v 1.25 2007/03/11 10:29:52 kientzle Exp $"); + +/* + * This file contains the "essential" portions of the write API, that + * is, stuff that will essentially always be used by any client that + * actually needs to write a archive. Optional pieces have been, as + * far as possible, separated out into separate files to reduce + * needlessly bloating statically-linked clients. + */ + +#ifdef HAVE_SYS_WAIT_H +#include +#endif +#ifdef HAVE_LIMITS_H +#include +#endif +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "archive.h" +#include "archive_entry.h" +#include "archive_private.h" +#include "archive_write_private.h" + +static struct archive_vtable *archive_write_vtable(void); + +static int _archive_write_close(struct archive *); +static int _archive_write_finish(struct archive *); +static int _archive_write_header(struct archive *, struct archive_entry *); +static int _archive_write_finish_entry(struct archive *); +static ssize_t _archive_write_data(struct archive *, const void *, size_t); + +static struct archive_vtable * +archive_write_vtable(void) +{ + static struct archive_vtable av; + static int inited = 0; + + if (!inited) { + av.archive_write_close = _archive_write_close; + av.archive_write_finish = _archive_write_finish; + av.archive_write_header = _archive_write_header; + av.archive_write_finish_entry = _archive_write_finish_entry; + av.archive_write_data = _archive_write_data; + } + return (&av); +} + +/* + * Allocate, initialize and return an archive object. + */ +struct archive * +archive_write_new(void) +{ + struct archive_write *a; + unsigned char *nulls; + + a = (struct archive_write *)malloc(sizeof(*a)); + if (a == NULL) + return (NULL); + memset(a, 0, sizeof(*a)); + a->archive.magic = ARCHIVE_WRITE_MAGIC; + a->archive.state = ARCHIVE_STATE_NEW; + a->archive.vtable = archive_write_vtable(); + a->bytes_per_block = ARCHIVE_DEFAULT_BYTES_PER_BLOCK; + a->bytes_in_last_block = -1; /* Default */ + + /* Initialize a block of nulls for padding purposes. */ + a->null_length = 1024; + nulls = (unsigned char *)malloc(a->null_length); + if (nulls == NULL) { + free(a); + return (NULL); + } + memset(nulls, 0, a->null_length); + a->nulls = nulls; + /* + * Set default compression, but don't set a default format. + * Were we to set a default format here, we would force every + * client to link in support for that format, even if they didn't + * ever use it. + */ + archive_write_set_compression_none(&a->archive); + return (&a->archive); +} + +/* + * Set the block size. Returns 0 if successful. + */ +int +archive_write_set_bytes_per_block(struct archive *_a, int bytes_per_block) +{ + struct archive_write *a = (struct archive_write *)_a; + __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_set_bytes_per_block"); + a->bytes_per_block = bytes_per_block; + return (ARCHIVE_OK); +} + +/* + * Get the current block size. -1 if it has never been set. + */ +int +archive_write_get_bytes_per_block(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_ANY, "archive_write_get_bytes_per_block"); + return (a->bytes_per_block); +} + +/* + * Set the size for the last block. + * Returns 0 if successful. + */ +int +archive_write_set_bytes_in_last_block(struct archive *_a, int bytes) +{ + struct archive_write *a = (struct archive_write *)_a; + __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_ANY, "archive_write_set_bytes_in_last_block"); + a->bytes_in_last_block = bytes; + return (ARCHIVE_OK); +} + +/* + * Return the value set above. -1 indicates it has not been set. + */ +int +archive_write_get_bytes_in_last_block(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_ANY, "archive_write_get_bytes_in_last_block"); + return (a->bytes_in_last_block); +} + + +/* + * dev/ino of a file to be rejected. Used to prevent adding + * an archive to itself recursively. + */ +int +archive_write_set_skip_file(struct archive *_a, dev_t d, ino_t i) +{ + struct archive_write *a = (struct archive_write *)_a; + __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_ANY, "archive_write_set_skip_file"); + a->skip_file_dev = d; + a->skip_file_ino = i; + return (ARCHIVE_OK); +} + + +/* + * Open the archive using the current settings. + */ +int +archive_write_open(struct archive *_a, void *client_data, + archive_open_callback *opener, archive_write_callback *writer, + archive_close_callback *closer) +{ + struct archive_write *a = (struct archive_write *)_a; + int ret; + + ret = ARCHIVE_OK; + __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_open"); + archive_clear_error(&a->archive); + a->archive.state = ARCHIVE_STATE_HEADER; + a->client_data = client_data; + a->client_writer = writer; + a->client_opener = opener; + a->client_closer = closer; + ret = (a->compressor.init)(a); + if (a->format_init && ret == ARCHIVE_OK) + ret = (a->format_init)(a); + return (ret); +} + + +/* + * Close out the archive. + * + * Be careful: user might just call write_new and then write_finish. + * Don't assume we actually wrote anything or performed any non-trivial + * initialization. + */ +static int +_archive_write_close(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + int r = ARCHIVE_OK, r1 = ARCHIVE_OK; + + __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_ANY, "archive_write_close"); + + /* Finish the last entry. */ + if (a->archive.state & ARCHIVE_STATE_DATA) + r = ((a->format_finish_entry)(a)); + + /* Finish off the archive. */ + if (a->format_finish != NULL) { + r1 = (a->format_finish)(a); + if (r1 < r) + r = r1; + } + + /* Release format resources. */ + if (a->format_destroy != NULL) { + r1 = (a->format_destroy)(a); + if (r1 < r) + r = r1; + } + + /* Finish the compression and close the stream. */ + if (a->compressor.finish != NULL) { + r1 = (a->compressor.finish)(a); + if (r1 < r) + r = r1; + } + + /* Close out the client stream. */ + if (a->client_closer != NULL) { + r1 = (a->client_closer)(&a->archive, a->client_data); + if (r1 < r) + r = r1; + } + + a->archive.state = ARCHIVE_STATE_CLOSED; + return (r); +} + +/* + * Destroy the archive structure. + */ +static int +_archive_write_finish(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + int r = ARCHIVE_OK; + + __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_ANY, "archive_write_finish"); + if (a->archive.state != ARCHIVE_STATE_CLOSED) + r = archive_write_close(&a->archive); + + /* Release various dynamic buffers. */ + free((void *)(uintptr_t)(const void *)a->nulls); + archive_string_free(&a->archive.error_string); + a->archive.magic = 0; + free(a); + return (r); +} + +/* + * Write the appropriate header. + */ +static int +_archive_write_header(struct archive *_a, struct archive_entry *entry) +{ + struct archive_write *a = (struct archive_write *)_a; + int ret, r2; + + __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_DATA | ARCHIVE_STATE_HEADER, "archive_write_header"); + archive_clear_error(&a->archive); + + /* In particular, "retry" and "fatal" get returned immediately. */ + ret = archive_write_finish_entry(&a->archive); + if (ret < ARCHIVE_OK && ret != ARCHIVE_WARN) + return (ret); + + if (a->skip_file_dev != 0 && + archive_entry_dev(entry) == a->skip_file_dev && + a->skip_file_ino != 0 && + archive_entry_ino(entry) == a->skip_file_ino) { + archive_set_error(&a->archive, 0, + "Can't add archive to itself"); + return (ARCHIVE_FAILED); + } + + /* Format and write header. */ + r2 = ((a->format_write_header)(a, entry)); + if (r2 < ret) + ret = r2; + + a->archive.state = ARCHIVE_STATE_DATA; + return (ret); +} + +static int +_archive_write_finish_entry(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + int ret = ARCHIVE_OK; + + __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, + "archive_write_finish_entry"); + if (a->archive.state & ARCHIVE_STATE_DATA) + ret = (a->format_finish_entry)(a); + a->archive.state = ARCHIVE_STATE_HEADER; + return (ret); +} + +/* + * Note that the compressor is responsible for blocking. + */ +static ssize_t +_archive_write_data(struct archive *_a, const void *buff, size_t s) +{ + struct archive_write *a = (struct archive_write *)_a; + __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_DATA, "archive_write_data"); + archive_clear_error(&a->archive); + return ((a->format_write_data)(a, buff, s)); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_write_disk.3 b/contrib/libarchive-2.1/libarchive/archive_write_disk.3 new file mode 100644 index 0000000000..880a399b94 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_write_disk.3 @@ -0,0 +1,358 @@ +.\" 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. +.\" +.\" $FreeBSD: src/lib/libarchive/archive_write_disk.3,v 1.1 2007/03/03 07:37:36 kientzle Exp $ +.\" +.Dd March 2, 2007 +.Dt archive_write_disk 3 +.Os +.Sh NAME +.Nm archive_write_disk_new , +.Nm archive_write_disk_set_options , +.Nm archive_write_disk_set_skip_file , +.Nm archive_write_disk_set_group_lookup , +.Nm archive_write_disk_set_standard_lookup , +.Nm archive_write_disk_set_user_lookup , +.Nm archive_write_header , +.Nm archive_write_data , +.Nm archive_write_finish_entry , +.Nm archive_write_close , +.Nm archive_write_finish +.Nd functions for creating objects on disk +.Sh SYNOPSIS +.In archive.h +.Ft struct archive * +.Fn archive_write_disk_new "void" +.Ft int +.Fn archive_write_disk_set_options "struct archive *" "int flags" +.Ft int +.Fn archive_write_disk_set_skip_file "struct archive *" "dev_t" "ino_t" +.Ft int +.Fn archive_write_disk_set_group_lookup "struct archive *" "void *" "gid_t (*)(void *, const char *gname, gid_t gid)" "void (*cleanup)(void *)" +.Ft int +.Fn archive_write_disk_set_standard_lookup "struct archive *" +.Ft int +.Fn archive_write_disk_set_user_lookup "struct archive *" "void *" "uid_t (*)(void *, const char *uname, uid_t uid)" "void (*cleanup)(void *)" +.Ft int +.Fn archive_write_header "struct archive *" "struct archive_entry *" +.Ft ssize_t +.Fn archive_write_data "struct archive *" "const void *" "size_t" +.Ft int +.Fn archive_write_finish_entry "struct archive *" +.Ft int +.Fn archive_write_close "struct archive *" +.Ft int +.Fn archive_write_finish "struct archive *" +.Sh DESCRIPTION +These functions provide a complete API for creating objects on +disk from +.Tn struct archive_entry +descriptions. +They are most naturally used when extracting objects from an archive +using the +.Fn archive_read +interface. +The general process is to read +.Tn struct archive_entry +objects from an archive, then write those objects to a +.Tn struct archive +object created using the +.Fn archive_write_disk +family functions. +This interface is deliberately very similar to the +.Fn archive_write +interface used to write objects to a streaming archive. +.Bl -tag -width indent +.It Fn archive_write_disk_new +Allocates and initializes a +.Tn struct archive +object suitable for writing objects to disk. +.It Fn archive_write_disk_set_skip_file +Records the device and inode numbers of a file that should not be +overwritten. +This is typically used to ensure that an extraction process does not +overwrite the archive from which objects are being read. +This capability is technically unnecessary but can be a significant +performance optimization in practice. +.It Fn archive_write_disk_set_options +The options field consists of a bitwise OR of one or more of the +following values: +.Bl -tag -compact -width "indent" +.It Cm ARCHIVE_EXTRACT_OWNER +The user and group IDs should be set on the restored file. +By default, the user and group IDs are not restored. +.It Cm ARCHIVE_EXTRACT_PERM +Full permissions (including SGID, SUID, and sticky bits) should +be restored exactly as specified, without obeying the +current umask. +Note that SUID and SGID bits can only be restored if the +user and group ID of the object on disk are correct. +If +.Cm ARCHIVE_EXTRACT_OWNER +is not specified, then SUID and SGID bits will only be restored +if the default user and group IDs of newly-created objects on disk +happen to match those specified in the archive entry. +By default, only basic permissions are restored, and umask is obeyed. +.It Cm ARCHIVE_EXTRACT_TIME +The timestamps (mtime, ctime, and atime) should be restored. +By default, they are ignored. +Note that restoring of atime is not currently supported. +.It Cm ARCHIVE_EXTRACT_NO_OVERWRITE +Existing files on disk will not be overwritten. +By default, existing regular files are truncated and overwritten; +existing directories will have their permissions updated; +other pre-existing objects are unlinked and recreated from scratch. +.It Cm ARCHIVE_EXTRACT_UNLINK +Existing files on disk will be unlinked before any attempt to +create them. +In some cases, this can prove to be a significant performance improvement. +By default, existing files are truncated and rewritten, but +the file is not recreated. +In particular, the default behavior does not break existing hard links. +.It Cm ARCHIVE_EXTRACT_ACL +Attempt to restore ACLs. +By default, extended ACLs are ignored. +.It Cm ARCHIVE_EXTRACT_FFLAGS +Attempt to restore extended file flags. +By default, file flags are ignored. +.It Cm ARCHIVE_EXTRACT_XATTR +Attempt to restore POSIX.1e extended attributes. +By default, they are ignored. +.It Cm ARCHIVE_EXTRACT_SECURE_SYMLINKS +Refuse to extract any object whose final location would be altered +by a symlink on disk. +This is intended to help guard against a variety of mischief +caused by archives that (deliberately or otherwise) extract +files outside of the current directory. +The default is not to perform this check. +If +.Cm ARCHIVE_EXTRACT_UNLINK +is specified together with this option, the library will +remove any intermediate symlinks it finds and return an +error only if such symlink could not be removed. +.It Cm ARCHIVE_EXTRACT_SECURE_NODOTDOT +Refuse to extract a path that contains a +.Pa .. +element anywhere within it. +The default is to not refuse such paths. +Note that paths ending in +.Pa .. +always cause an error, regardless of this flag. +.El +.It Fn archive_write_disk_set_group_lookup , Fn archive_write_disk_set_user_lookup +The +.Tn struct archive_entry +objects contain both names and ids that can be used to identify users +and groups. +These names and ids describe the ownership of the file itself and +also appear in ACL lists. +By default, the library uses the ids and ignores the names, but +this can be overridden by registering user and group lookup functions. +To register, you must provide a lookup function which +accepts both a name and id and returns a suitable id. +You may also provide a +.Tn void * +pointer to a private data structure and a cleanup function for +that data. +The cleanup function will be invoked when the +.Tn struct archive +object is destroyed. +.It Fn archive_write_disk_set_standard_lookup +This convenience function installs a standard set of user +and group lookup functions. +These functions use +.Xr getpwnam 3 +and +.Xr getgrnam 3 +to convert names to ids, defaulting to the ids if the names cannot +be looked up. +These functions also implement a simple memory cache to reduce +the number of calls to +.Xr getpwnam 3 +and +.Xr getgrnam 3 . +.It Fn archive_write_header +Build and write a header using the data in the provided +.Tn struct archive_entry +structure. +See +.Xr archive_entry 3 +for information on creating and populating +.Tn struct archive_entry +objects. +.It Fn archive_write_data +Write data corresponding to the header just written. +Returns number of bytes written or -1 on error. +.It Fn archive_write_finish_entry +Close out the entry just written. +Ordinarily, clients never need to call this, as it +is called automatically by +.Fn archive_write_next_header +and +.Fn archive_write_close +as needed. +.It Fn archive_write_close +Set any attributes that could not be set during the initial restore. +For example, directory timestamps are not restored initially because +restoring a subsequent file would alter that timestamp. +Similarly, non-writable directories are initially created with +write permissions (so that their contents can be restored). +The +.Nm +library maintains a list of all such deferred attributes and +sets them when this function is invoked. +.It Fn archive_write_finish +Invokes +.Fn archive_write_close +if it was not invoked manually, then releases all resources. +.El +More information about the +.Va struct archive +object and the overall design of the library can be found in the +.Xr libarchive 3 +overview. +Many of these functions are also documented under +.Xr archive_write 3 . +.Sh RETURN VALUES +Most functions return +.Cm ARCHIVE_OK +(zero) on success, or one of several non-zero +error codes for errors. +Specific error codes include: +.Cm ARCHIVE_RETRY +for operations that might succeed if retried, +.Cm ARCHIVE_WARN +for unusual conditions that do not prevent further operations, and +.Cm ARCHIVE_FATAL +for serious errors that make remaining operations impossible. +The +.Fn archive_errno +and +.Fn archive_error_string +functions can be used to retrieve an appropriate error code and a +textual error message. +.Pp +.Fn archive_write_disk_new +returns a pointer to a newly-allocated +.Tn struct archive +object. +.Pp +.Fn archive_write_data +returns a count of the number of bytes actually written. +On error, -1 is returned and the +.Fn archive_errno +and +.Fn archive_error_string +functions will return appropriate values. +.Sh SEE ALSO +.Xr archive_read 3 , +.Xr archive_write 3 , +.Xr tar 1 , +.Xr libarchive 3 +.Sh HISTORY +The +.Nm libarchive +library first appeared in +.Fx 5.3 . +The +.Nm archive_write_disk +interface was added to +.Nm libarchive 2.0 +and first appeared in +.Fx 6.3 . +.Sh AUTHORS +.An -nosplit +The +.Nm libarchive +library was written by +.An Tim Kientzle Aq kientzle@acm.org . +.Sh BUGS +Directories are actually extracted in two distinct phases. +Directories are created during +.Fn archive_write_header , +but final permissions are not set until +.Fn archive_write_close . +This separation is necessary to correctly handle borderline +cases such as a non-writable directory containing +files, but can cause unexpected results. +In particular, directory permissions are not fully +restored until the archive is closed. +If you use +.Xr chdir 2 +to change the current directory between calls to +.Fn archive_read_extract +or before calling +.Fn archive_read_close , +you may confuse the permission-setting logic with +the result that directory permissions are restored +incorrectly. +.Pp +The library attempts to create objects with filenames longer than +.Cm PATH_MAX +by creating prefixes of the full path and changing the current directory. +Currently, this logic is limited in scope; the fixup pass does +not work correctly for such objects and the symlink security check +option disables the support for very long pathnames. +.Pp +Restoring the path +.Pa aa/../bb +does create each intermediate directory. +In particular, the directory +.Pa aa +is created as well as the final object +.Pa bb . +In theory, this can be exploited to create an entire directory heirarchy +with a single request. +Of course, this does not work if the +.Cm ARCHIVE_EXTRACT_NODOTDOT +option is specified. +.Pp +Implicit directories are always created obeying the current umask. +Explicit objects are created obeying the current umask unless +.Cm ARCHIVE_EXTRACT_PERM +is specified, in which case they current umask is ignored. +.Pp +SGID and SUID bits are restored only if the correct user and +group could be set. +If +.Cm ARCHIVE_EXTRACT_OWNER +is not specified, then no attempt is made to set the ownership. +In this case, SGID and SUID bits are restored only if the +user and group of the final object happen to match those specified +in the entry. +.Pp +The +.Dq standard +user-id and group-id lookup functions are not the defaults because +.Xr getgrnam 3 +and +.Xr getpwnam 3 +are sometimes too large for particular applications. +The current design allows the application author to use a more +compact implementation when appropriate. +.Pp +There should be a corresponding +.Nm archive_read_disk +interface that walks a directory heirarchy and returns archive +entry objects. \ No newline at end of file diff --git a/contrib/libarchive-2.1/libarchive/archive_write_disk.c b/contrib/libarchive-2.1/libarchive/archive_write_disk.c new file mode 100644 index 0000000000..052cd25e6b --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_write_disk.c @@ -0,0 +1,2019 @@ +/*- + * 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 + * in this position and unchanged. + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_disk.c,v 1.10 2007/04/15 04:43:12 kientzle Exp $"); + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_ACL_H +#include +#endif +#ifdef HAVE_ATTR_XATTR_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#ifdef HAVE_EXT2FS_EXT2_FS_H +#include /* for Linux file flags */ +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_GRP_H +#include +#endif +#ifdef HAVE_LINUX_FS_H +#include /* for Linux file flags */ +#endif +#ifdef HAVE_LINUX_EXT2_FS_H +#include /* for Linux file flags */ +#endif +#ifdef HAVE_LIMITS_H +#include +#endif +#ifdef HAVE_PWD_H +#include +#endif +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_UTIME_H +#include +#endif + +#include "archive.h" +#include "archive_string.h" +#include "archive_entry.h" +#include "archive_private.h" + +struct fixup_entry { + struct fixup_entry *next; + mode_t mode; + int64_t mtime; + int64_t atime; + unsigned long mtime_nanos; + unsigned long atime_nanos; + unsigned long fflags_set; + int fixup; /* bitmask of what needs fixing */ + char *name; +}; + +/* + * We use a bitmask to track which operations remain to be done for + * this file. In particular, this helps us avoid unnecessary + * operations when it's possible to take care of one step as a + * side-effect of another. For example, mkdir() can specify the mode + * for the newly-created object but symlink() cannot. This means we + * can skip chmod() if mkdir() succeeded, but we must explicitly + * chmod() if we're trying to create a directory that already exists + * (mkdir() failed) or if we're restoring a symlink. Similarly, we + * need to verify UID/GID before trying to restore SUID/SGID bits; + * that verification can occur explicitly through a stat() call or + * implicitly because of a successful chown() call. + */ +#define TODO_MODE_FORCE 0x40000000 +#define TODO_MODE_BASE 0x20000000 +#define TODO_SUID 0x10000000 +#define TODO_SUID_CHECK 0x08000000 +#define TODO_SGID 0x04000000 +#define TODO_SGID_CHECK 0x02000000 +#define TODO_MODE (TODO_MODE_BASE|TODO_SUID|TODO_SGID) +#define TODO_TIMES ARCHIVE_EXTRACT_TIME +#define TODO_OWNER ARCHIVE_EXTRACT_OWNER +#define TODO_FFLAGS ARCHIVE_EXTRACT_FFLAGS +#define TODO_ACLS ARCHIVE_EXTRACT_ACL +#define TODO_XATTR ARCHIVE_EXTRACT_XATTR + +struct archive_write_disk { + struct archive archive; + + mode_t user_umask; + struct fixup_entry *fixup_list; + struct fixup_entry *current_fixup; + uid_t user_uid; + dev_t skip_file_dev; + ino_t skip_file_ino; + + gid_t (*lookup_gid)(void *private, const char *gname, gid_t gid); + void (*cleanup_gid)(void *private); + void *lookup_gid_data; + uid_t (*lookup_uid)(void *private, const char *gname, gid_t gid); + void (*cleanup_uid)(void *private); + void *lookup_uid_data; + + /* + * Full path of last file to satisfy symlink checks. + */ + struct archive_string path_safe; + + /* + * Cached stat data from disk for the current entry. + * If this is valid, pst points to st. Otherwise, + * pst is null. + */ + struct stat st; + struct stat *pst; + + /* Information about the object being restored right now. */ + struct archive_entry *entry; /* Entry being extracted. */ + char *name; /* Name of entry, possibly edited. */ + struct archive_string _name_data; /* backing store for 'name' */ + /* Tasks remaining for this object. */ + int todo; + /* Tasks deferred until end-of-archive. */ + int deferred; + /* Options requested by the client. */ + int flags; + /* Handle for the file we're restoring. */ + int fd; + /* Current offset for writing data to the file. */ + off_t offset; + /* Dir we were in before this restore; only for deep paths. */ + int restore_pwd; + /* Mode we should use for this entry; affected by _PERM and umask. */ + mode_t mode; + /* UID/GID to use in restoring this entry. */ + uid_t uid; + gid_t gid; +}; + +/* + * Default mode for dirs created automatically (will be modified by umask). + * Note that POSIX specifies 0777 for implicity-created dirs, "modified + * by the process' file creation mask." + */ +#define DEFAULT_DIR_MODE 0777 +/* + * Dir modes are restored in two steps: During the extraction, the permissions + * in the archive are modified to match the following limits. During + * the post-extract fixup pass, the permissions from the archive are + * applied. + */ +#define MINIMUM_DIR_MODE 0700 +#define MAXIMUM_DIR_MODE 0775 + +static int check_symlinks(struct archive_write_disk *); +static int create_filesystem_object(struct archive_write_disk *); +static struct fixup_entry *current_fixup(struct archive_write_disk *, const char *pathname); +#ifdef HAVE_FCHDIR +static void edit_deep_directories(struct archive_write_disk *ad); +#endif +static int cleanup_pathname(struct archive_write_disk *); +static int create_dir(struct archive_write_disk *, char *); +static int create_parent_dir(struct archive_write_disk *, char *); +static int restore_entry(struct archive_write_disk *); +#ifdef HAVE_POSIX_ACL +static int set_acl(struct archive_write_disk *, int fd, struct archive_entry *, + acl_type_t, int archive_entry_acl_type, const char *tn); +#endif +static int set_acls(struct archive_write_disk *); +static int set_xattrs(struct archive_write_disk *); +static int set_fflags(struct archive_write_disk *); +static int set_fflags_platform(struct archive_write_disk *, int fd, + const char *name, mode_t mode, + unsigned long fflags_set, unsigned long fflags_clear); +static int set_ownership(struct archive_write_disk *); +static int set_mode(struct archive_write_disk *, int mode); +static int set_time(struct archive_write_disk *); +static struct fixup_entry *sort_dir_list(struct fixup_entry *p); +static gid_t trivial_lookup_gid(void *, const char *, gid_t); +static uid_t trivial_lookup_uid(void *, const char *, uid_t); + + +static struct archive_vtable *archive_write_disk_vtable(void); + +static int _archive_write_close(struct archive *); +static int _archive_write_finish(struct archive *); +static int _archive_write_header(struct archive *, struct archive_entry *); +static int _archive_write_finish_entry(struct archive *); +static ssize_t _archive_write_data(struct archive *, const void *, size_t); +static ssize_t _archive_write_data_block(struct archive *, const void *, size_t, off_t); + +static struct archive_vtable * +archive_write_disk_vtable(void) +{ + static struct archive_vtable av; + static int inited = 0; + + if (!inited) { + av.archive_write_close = _archive_write_close; + av.archive_write_finish = _archive_write_finish; + av.archive_write_header = _archive_write_header; + av.archive_write_finish_entry = _archive_write_finish_entry; + av.archive_write_data = _archive_write_data; + av.archive_write_data_block = _archive_write_data_block; + } + return (&av); +} + + +int +archive_write_disk_set_options(struct archive *_a, int flags) +{ + struct archive_write_disk *a = (struct archive_write_disk *)_a; + + a->flags = flags; + return (ARCHIVE_OK); +} + + +/* + * Extract this entry to disk. + * + * TODO: Validate hardlinks. According to the standards, we're + * supposed to check each extracted hardlink and squawk if it refers + * to a file that we didn't restore. I'm not entirely convinced this + * is a good idea, but more importantly: Is there any way to validate + * hardlinks without keeping a complete list of filenames from the + * entire archive?? Ugh. + * + */ +static int +_archive_write_header(struct archive *_a, struct archive_entry *entry) +{ + struct archive_write_disk *a = (struct archive_write_disk *)_a; + struct fixup_entry *fe; + int ret, r; + + __archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, + ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, + "archive_write_disk_header"); + archive_clear_error(&a->archive); + if (a->archive.state & ARCHIVE_STATE_DATA) { + r = _archive_write_finish_entry(&a->archive); + if (r != ARCHIVE_OK) + return (r); + } + + /* Set up for this particular entry. */ + a->pst = NULL; + a->current_fixup = NULL; + a->deferred = 0; + a->entry = entry; + a->fd = -1; + a->offset = 0; + a->uid = a->user_uid; + a->mode = archive_entry_mode(a->entry); + archive_strcpy(&(a->_name_data), archive_entry_pathname(a->entry)); + a->name = a->_name_data.s; + archive_clear_error(&a->archive); + + /* + * Clean up the requested path. This is necessary for correct + * dir restores; the dir restore logic otherwise gets messed + * up by nonsense like "dir/.". + */ + ret = cleanup_pathname(a); + if (ret != ARCHIVE_OK) + return (ret); + + /* + * Set the umask to zero so we get predictable mode settings. + * This gets done on every call to _write_header in case the + * user edits their umask during the extraction for some + * reason. This will be reset before we return. Note that we + * don't need to do this in _finish_entry, as the chmod(), etc, + * system calls don't obey umask. + */ + a->user_umask = umask(0); + /* From here on, early exit requires "goto done" to clean up. */ + + /* Figure out what we need to do for this entry. */ + a->todo = TODO_MODE_BASE; + if (a->flags & ARCHIVE_EXTRACT_PERM) { + a->todo |= TODO_MODE_FORCE; /* Be pushy about permissions. */ + /* + * SGID requires an extra "check" step because we + * cannot easily predict the GID that the system will + * assign. (Different systems assign GIDs to files + * based on a variety of criteria, including process + * credentials and the gid of the enclosing + * directory.) We can only restore the SGID bit if + * the file has the right GID, and we only know the + * GID if we either set it (see set_ownership) or if + * we've actually called stat() on the file after it + * was restored. Since there are several places at + * which we might verify the GID, we need a TODO bit + * to keep track. + */ + if (a->mode & S_ISGID) + a->todo |= TODO_SGID | TODO_SGID_CHECK; + /* + * Verifying the SUID is simpler, but can still be + * done in multiple ways, hence the separate "check" bit. + */ + if (a->mode & S_ISUID) + a->todo |= TODO_SUID | TODO_SUID_CHECK; + } else { + /* + * User didn't request full permissions, so don't + * restore SUID, SGID bits and obey umask. + */ + a->mode &= ~S_ISUID; + a->mode &= ~S_ISGID; + a->mode &= ~S_ISVTX; + a->mode &= ~a->user_umask; + } + if (a->flags & ARCHIVE_EXTRACT_OWNER) + a->todo |= TODO_OWNER; + if (a->flags & ARCHIVE_EXTRACT_TIME) + a->todo |= TODO_TIMES; + if (a->flags & ARCHIVE_EXTRACT_ACL) + a->todo |= TODO_ACLS; + if (a->flags & ARCHIVE_EXTRACT_FFLAGS) + a->todo |= TODO_FFLAGS; + if (a->flags & ARCHIVE_EXTRACT_SECURE_SYMLINKS) { + ret = check_symlinks(a); + if (ret != ARCHIVE_OK) + goto done; + } +#ifdef HAVE_FCHDIR + /* If path exceeds PATH_MAX, shorten the path. */ + edit_deep_directories(a); +#endif + + ret = restore_entry(a); + +#ifdef HAVE_FCHDIR + /* If we changed directory above, restore it here. */ + if (a->restore_pwd >= 0) { + fchdir(a->restore_pwd); + close(a->restore_pwd); + a->restore_pwd = -1; + } +#endif + + /* + * Fixup uses the unedited pathname from archive_entry_pathname(), + * because it is relative to the base dir and the edited path + * might be relative to some intermediate dir as a result of the + * deep restore logic. + */ + if (a->deferred & TODO_MODE) { + fe = current_fixup(a, archive_entry_pathname(entry)); + fe->fixup |= TODO_MODE_BASE; + fe->mode = a->mode; + } + + if (a->deferred & TODO_TIMES) { + fe = current_fixup(a, archive_entry_pathname(entry)); + fe->fixup |= TODO_TIMES; + fe->mtime = archive_entry_mtime(entry); + fe->mtime_nanos = archive_entry_mtime_nsec(entry); + fe->atime = archive_entry_atime(entry); + fe->atime_nanos = archive_entry_atime_nsec(entry); + } + + if (a->deferred & TODO_FFLAGS) { + fe = current_fixup(a, archive_entry_pathname(entry)); + fe->fixup |= TODO_FFLAGS; + /* TODO: Complete this.. defer fflags from below. */ + } + + /* We've created the object and are ready to pour data into it. */ + if (ret == ARCHIVE_OK) + a->archive.state = ARCHIVE_STATE_DATA; +done: + /* Restore the user's umask before returning. */ + umask(a->user_umask); + + return (ret); +} + +int +archive_write_disk_set_skip_file(struct archive *_a, dev_t d, ino_t i) +{ + struct archive_write_disk *a = (struct archive_write_disk *)_a; + __archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, + ARCHIVE_STATE_ANY, "archive_write_disk_set_skip_file"); + a->skip_file_dev = d; + a->skip_file_ino = i; + return (ARCHIVE_OK); +} + +static ssize_t +_archive_write_data_block(struct archive *_a, + const void *buff, size_t size, off_t offset) +{ + struct archive_write_disk *a = (struct archive_write_disk *)_a; + ssize_t bytes_written = 0; + + __archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, + ARCHIVE_STATE_DATA, "archive_write_disk_block"); + if (a->fd < 0) + return (ARCHIVE_OK); + archive_clear_error(&a->archive); + + /* Seek if necessary to the specified offset. */ + if (offset != a->offset) { + if (lseek(a->fd, offset, SEEK_SET) < 0) { + archive_set_error(&a->archive, errno, "Seek failed"); + return (ARCHIVE_WARN); + } + a->offset = offset; + } + + /* Write the data. */ + while (size > 0) { + bytes_written = write(a->fd, buff, size); + if (bytes_written < 0) { + archive_set_error(&a->archive, errno, "Write failed"); + return (ARCHIVE_WARN); + } + size -= bytes_written; + a->offset += bytes_written; + } + return (ARCHIVE_OK); +} + +static ssize_t +_archive_write_data(struct archive *_a, const void *buff, size_t size) +{ + struct archive_write_disk *a = (struct archive_write_disk *)_a; + __archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, + ARCHIVE_STATE_DATA, "archive_write_data"); + if (a->fd < 0) + return (ARCHIVE_OK); + + return (_archive_write_data_block(_a, buff, size, a->offset)); +} + +static int +_archive_write_finish_entry(struct archive *_a) +{ + struct archive_write_disk *a = (struct archive_write_disk *)_a; + int ret = ARCHIVE_OK; + + __archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, + ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, + "archive_write_finish_entry"); + if (a->archive.state & ARCHIVE_STATE_HEADER) + return (ARCHIVE_OK); + archive_clear_error(&a->archive); + + /* Restore metadata. */ + + /* + * Look up the "real" UID only if we're going to need it. We + * need this for TODO_SGID because chown() requires both. + */ + if (a->todo & (TODO_OWNER | TODO_SUID | TODO_SGID)) { + a->uid = a->lookup_uid(a->lookup_uid_data, + archive_entry_uname(a->entry), + archive_entry_uid(a->entry)); + } + /* Look up the "real" GID only if we're going to need it. */ + if (a->todo & (TODO_OWNER | TODO_SGID | TODO_SUID)) { + a->gid = a->lookup_gid(a->lookup_gid_data, + archive_entry_gname(a->entry), + archive_entry_gid(a->entry)); + } + /* + * If restoring ownership, do it before trying to restore suid/sgid + * bits. If we set the owner, we know what it is and can skip + * a stat() call to examine the ownership of the file on disk. + */ + if (a->todo & TODO_OWNER) + ret = set_ownership(a); + if (a->todo & TODO_MODE) { + int r2 = set_mode(a, a->mode); + if (r2 < ret) ret = r2; + } + if (a->todo & TODO_TIMES) { + int r2 = set_time(a); + if (r2 < ret) ret = r2; + } + if (a->todo & TODO_ACLS) { + int r2 = set_acls(a); + if (r2 < ret) ret = r2; + } + if (a->todo & TODO_XATTR) { + int r2 = set_xattrs(a); + if (r2 < ret) ret = r2; + } + if (a->todo & TODO_FFLAGS) { + int r2 = set_fflags(a); + if (r2 < ret) ret = r2; + } + + /* If there's an fd, we can close it now. */ + if (a->fd >= 0) { + close(a->fd); + a->fd = -1; + } + a->archive.state = ARCHIVE_STATE_HEADER; + return (ret); +} + +int +archive_write_disk_set_group_lookup(struct archive *_a, + void *private_data, + gid_t (*lookup_gid)(void *private, const char *gname, gid_t gid), + void (*cleanup_gid)(void *private)) +{ + struct archive_write_disk *a = (struct archive_write_disk *)_a; + __archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, + ARCHIVE_STATE_ANY, "archive_write_disk_set_group_lookup"); + + a->lookup_gid = lookup_gid; + a->cleanup_gid = cleanup_gid; + a->lookup_gid_data = private_data; + return (ARCHIVE_OK); +} + +int +archive_write_disk_set_user_lookup(struct archive *_a, + void *private_data, + uid_t (*lookup_uid)(void *private, const char *uname, uid_t uid), + void (*cleanup_uid)(void *private)) +{ + struct archive_write_disk *a = (struct archive_write_disk *)_a; + __archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, + ARCHIVE_STATE_ANY, "archive_write_disk_set_user_lookup"); + + a->lookup_uid = lookup_uid; + a->cleanup_uid = cleanup_uid; + a->lookup_uid_data = private_data; + return (ARCHIVE_OK); +} + + +/* + * Create a new archive_write_disk object and initialize it with global state. + */ +struct archive * +archive_write_disk_new(void) +{ + struct archive_write_disk *a; + + a = (struct archive_write_disk *)malloc(sizeof(*a)); + if (a == NULL) + return (NULL); + memset(a, 0, sizeof(*a)); + a->archive.magic = ARCHIVE_WRITE_DISK_MAGIC; + /* We're ready to write a header immediately. */ + a->archive.state = ARCHIVE_STATE_HEADER; + a->archive.vtable = archive_write_disk_vtable(); + a->lookup_uid = trivial_lookup_uid; + a->lookup_gid = trivial_lookup_gid; + a->user_uid = geteuid(); + archive_string_ensure(&a->path_safe, 64); + return (&a->archive); +} + + +/* + * If pathname is longer than PATH_MAX, chdir to a suitable + * intermediate dir and edit the path down to a shorter suffix. Note + * that this routine never returns an error; if the chdir() attempt + * fails for any reason, we just go ahead with the long pathname. The + * object creation is likely to fail, but any error will get handled + * at that time. + */ +#ifdef HAVE_FCHDIR +static void +edit_deep_directories(struct archive_write_disk *a) +{ + int ret; + char *tail = a->name; + + a->restore_pwd = -1; + + /* If path is short, avoid the open() below. */ + if (strlen(tail) <= PATH_MAX) + return; + + /* Try to record our starting dir. */ + a->restore_pwd = open(".", O_RDONLY); + if (a->restore_pwd < 0) + return; + + /* As long as the path is too long... */ + while (strlen(tail) > PATH_MAX) { + /* Locate a dir prefix shorter than PATH_MAX. */ + tail += PATH_MAX - 8; + while (tail > a->name && *tail != '/') + tail--; + /* Exit if we find a too-long path component. */ + if (tail <= a->name) + return; + /* Create the intermediate dir and chdir to it. */ + *tail = '\0'; /* Terminate dir portion */ + ret = create_dir(a, a->name); + if (ret == ARCHIVE_OK && chdir(a->name) != 0) + ret = ARCHIVE_WARN; + *tail = '/'; /* Restore the / we removed. */ + if (ret != ARCHIVE_OK) + return; + tail++; + /* The chdir() succeeded; we've now shortened the path. */ + a->name = tail; + } + return; +} +#endif + +/* + * The main restore function. + */ +static int +restore_entry(struct archive_write_disk *a) +{ + int ret = ARCHIVE_OK, en; + + if (a->flags & ARCHIVE_EXTRACT_UNLINK && !S_ISDIR(a->mode)) { + if (unlink(a->name) == 0) { + /* We removed it, we're done. */ + } else if (errno == ENOENT) { + /* File didn't exist, that's just as good. */ + } else if (rmdir(a->name) == 0) { + /* It was a dir, but now it's gone. */ + } else { + /* We tried, but couldn't get rid of it. */ + archive_set_error(&a->archive, errno, + "Could not unlink"); + return(ARCHIVE_WARN); + } + } + + /* Try creating it first; if this fails, we'll try to recover. */ + en = create_filesystem_object(a); + + if (en == ENOTDIR || en == ENOENT) { + /* If the parent dir doesn't exist, try creating it. */ + create_parent_dir(a, a->name); + /* Now try to create the object again. */ + en = create_filesystem_object(a); + } + + if ((en == EISDIR || en == EEXIST) + && (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) { + /* If we're not overwriting, we're done. */ + archive_set_error(&a->archive, en, "Already exists"); + return (ARCHIVE_WARN); + } + + /* + * Some platforms return EISDIR if you call + * open(O_WRONLY | O_EXCL | O_CREAT) on a directory, some + * return EEXIST. POSIX is ambiguous, requiring EISDIR + * for open(O_WRONLY) on a dir and EEXIST for open(O_EXCL | O_CREAT) + * on an existing item. + */ + if (en == EISDIR) { + /* A dir is in the way of a non-dir, rmdir it. */ + if (rmdir(a->name) != 0) { + archive_set_error(&a->archive, errno, + "Can't remove already-existing dir"); + return (ARCHIVE_WARN); + } + /* Try again. */ + en = create_filesystem_object(a); + } else if (en == EEXIST) { + /* + * We know something is in the way, but we don't know what; + * we need to find out before we go any further. + */ + if (lstat(a->name, &a->st) != 0) { + archive_set_error(&a->archive, errno, + "Can't stat existing object"); + return (ARCHIVE_WARN); + } + + /* TODO: if it's a symlink... */ + + /* If it's our archive, we're done. */ + if (a->skip_file_dev > 0 && + a->skip_file_ino > 0 && + a->st.st_dev == a->skip_file_dev && + a->st.st_ino == a->skip_file_ino) { + archive_set_error(&a->archive, 0, "Refusing to overwrite archive"); + return (ARCHIVE_FAILED); + } + + if (!S_ISDIR(a->st.st_mode)) { + /* A non-dir is in the way, unlink it. */ + if (unlink(a->name) != 0) { + archive_set_error(&a->archive, errno, + "Can't unlink already-existing object"); + return (ARCHIVE_WARN); + } + /* Try again. */ + en = create_filesystem_object(a); + } else if (!S_ISDIR(a->mode)) { + /* A dir is in the way of a non-dir, rmdir it. */ + if (rmdir(a->name) != 0) { + archive_set_error(&a->archive, errno, + "Can't remove already-existing dir"); + return (ARCHIVE_WARN); + } + /* Try again. */ + en = create_filesystem_object(a); + } else { + /* + * There's a dir in the way of a dir. Don't + * waste time with rmdir()/mkdir(), just fix + * up the permissions on the existing dir. + * Note that we don't change perms on existing + * dirs unless _EXTRACT_PERM is specified. + */ + if ((a->mode != a->st.st_mode) + && (a->todo & TODO_MODE_FORCE)) + a->deferred |= (a->todo & TODO_MODE); + /* Ownership doesn't need deferred fixup. */ + en = 0; /* Forget the EEXIST. */ + } + } + + if (en) { + /* Everything failed; give up here. */ + archive_set_error(&a->archive, en, "Can't create '%s'", a->name); + return (ARCHIVE_WARN); + } + + a->pst = NULL; /* Cached stat data no longer valid. */ + return (ret); +} + +/* + * Returns 0 if creation succeeds, or else returns errno value from + * the failed system call. Note: This function should only ever perform + * a single system call. + */ +int +create_filesystem_object(struct archive_write_disk *a) +{ + /* Create the entry. */ + const char *linkname; + mode_t final_mode, mode; + int r; + + /* We identify hard/symlinks according to the link names. */ + /* Since link(2) and symlink(2) don't handle modes, we're done here. */ + linkname = archive_entry_hardlink(a->entry); + if (linkname != NULL) + return link(linkname, a->name) ? errno : 0; + linkname = archive_entry_symlink(a->entry); + if (linkname != NULL) + return symlink(linkname, a->name) ? errno : 0; + + /* + * The remaining system calls all set permissions, so let's + * try to take advantage of that to avoid an extra chmod() + * call. (Recall that umask is set to zero right now!) + */ + + /* Mode we want for the final restored object (w/o file type bits). */ + final_mode = a->mode & 07777; + /* + * The mode that will actually be restored in this step. Note + * that SUID, SGID, etc, require additional work to ensure + * security, so we never restore them at this point. + */ + mode = final_mode & 0777; + + switch (a->mode & S_IFMT) { + default: + /* POSIX requires that we fall through here. */ + /* FALLTHROUGH */ + case S_IFREG: + a->fd = open(a->name, + O_WRONLY | O_CREAT | O_EXCL, mode); + r = (a->fd < 0); + break; + case S_IFCHR: + r = mknod(a->name, mode | S_IFCHR, + archive_entry_rdev(a->entry)); + break; + case S_IFBLK: + r = mknod(a->name, mode | S_IFBLK, + archive_entry_rdev(a->entry)); + break; + case S_IFDIR: + mode = (mode | MINIMUM_DIR_MODE) & MAXIMUM_DIR_MODE; + r = mkdir(a->name, mode); + if (r == 0) { + /* Defer setting dir times. */ + a->deferred |= (a->todo & TODO_TIMES); + a->todo &= ~TODO_TIMES; + /* Never use an immediate chmod(). */ + if (mode != final_mode) + a->deferred |= (a->todo & TODO_MODE); + a->todo &= ~TODO_MODE; + } + break; + case S_IFIFO: + r = mkfifo(a->name, mode); + break; + } + + /* All the system calls above set errno on failure. */ + if (r) + return (errno); + + /* If we managed to set the final mode, we've avoided a chmod(). */ + if (mode == final_mode) + a->todo &= ~TODO_MODE; + return (0); +} + +/* + * Cleanup function for archive_extract. Mostly, this involves processing + * the fixup list, which is used to address a number of problems: + * * Dir permissions might prevent us from restoring a file in that + * dir, so we restore the dir with minimum 0700 permissions first, + * then correct the mode at the end. + * * Similarly, the act of restoring a file touches the directory + * and changes the timestamp on the dir, so we have to touch-up dir + * timestamps at the end as well. + * * Some file flags can interfere with the restore by, for example, + * preventing the creation of hardlinks to those files. + * + * Note that tar/cpio do not require that archives be in a particular + * order; there is no way to know when the last file has been restored + * within a directory, so there's no way to optimize the memory usage + * here by fixing up the directory any earlier than the + * end-of-archive. + * + * XXX TODO: Directory ACLs should be restored here, for the same + * reason we set directory perms here. XXX + */ +static int +_archive_write_close(struct archive *_a) +{ + struct archive_write_disk *a = (struct archive_write_disk *)_a; + struct fixup_entry *next, *p; + int ret; + + __archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, + ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, + "archive_write_disk_close"); + ret = _archive_write_finish_entry(&a->archive); + + /* Sort dir list so directories are fixed up in depth-first order. */ + p = sort_dir_list(a->fixup_list); + + while (p != NULL) { + a->pst = NULL; /* Mark stat cache as out-of-date. */ + if (p->fixup & TODO_TIMES) { +#ifdef HAVE_UTIMES + /* {f,l,}utimes() are preferred, when available. */ + struct timeval times[2]; + times[1].tv_sec = p->mtime; + times[1].tv_usec = p->mtime_nanos / 1000; + times[0].tv_sec = p->atime; + times[0].tv_usec = p->atime_nanos / 1000; +#ifdef HAVE_LUTIMES + lutimes(p->name, times); +#else + utimes(p->name, times); +#endif +#else + /* utime() is more portable, but less precise. */ + struct utimbuf times; + times.modtime = p->mtime; + times.actime = p->atime; + + utime(p->name, ×); +#endif + } + if (p->fixup & TODO_MODE_BASE) + chmod(p->name, p->mode); + + if (p->fixup & TODO_FFLAGS) + set_fflags_platform(a, -1, p->name, + p->mode, p->fflags_set, 0); + + next = p->next; + free(p->name); + free(p); + p = next; + } + a->fixup_list = NULL; + return (ret); +} + +static int +_archive_write_finish(struct archive *_a) +{ + struct archive_write_disk *a = (struct archive_write_disk *)_a; + int ret; + ret = _archive_write_close(&a->archive); + if (a->cleanup_gid != NULL && a->lookup_gid_data != NULL) + (a->cleanup_gid)(a->lookup_gid_data); + if (a->cleanup_uid != NULL && a->lookup_uid_data != NULL) + (a->cleanup_uid)(a->lookup_uid_data); + archive_string_free(&a->_name_data); + archive_string_free(&a->archive.error_string); + archive_string_free(&a->path_safe); + free(a); + return (ret); +} + +/* + * Simple O(n log n) merge sort to order the fixup list. In + * particular, we want to restore dir timestamps depth-first. + */ +static struct fixup_entry * +sort_dir_list(struct fixup_entry *p) +{ + struct fixup_entry *a, *b, *t; + + if (p == NULL) + return (NULL); + /* A one-item list is already sorted. */ + if (p->next == NULL) + return (p); + + /* Step 1: split the list. */ + t = p; + a = p->next->next; + while (a != NULL) { + /* Step a twice, t once. */ + a = a->next; + if (a != NULL) + a = a->next; + t = t->next; + } + /* Now, t is at the mid-point, so break the list here. */ + b = t->next; + t->next = NULL; + a = p; + + /* Step 2: Recursively sort the two sub-lists. */ + a = sort_dir_list(a); + b = sort_dir_list(b); + + /* Step 3: Merge the returned lists. */ + /* Pick the first element for the merged list. */ + if (strcmp(a->name, b->name) > 0) { + t = p = a; + a = a->next; + } else { + t = p = b; + b = b->next; + } + + /* Always put the later element on the list first. */ + while (a != NULL && b != NULL) { + if (strcmp(a->name, b->name) > 0) { + t->next = a; + a = a->next; + } else { + t->next = b; + b = b->next; + } + t = t->next; + } + + /* Only one list is non-empty, so just splice it on. */ + if (a != NULL) + t->next = a; + if (b != NULL) + t->next = b; + + return (p); +} + +/* + * Returns a new, initialized fixup entry. + * + * TODO: Reduce the memory requirements for this list by using a tree + * structure rather than a simple list of names. + */ +static struct fixup_entry * +new_fixup(struct archive_write_disk *a, const char *pathname) +{ + struct fixup_entry *fe; + + fe = (struct fixup_entry *)malloc(sizeof(struct fixup_entry)); + if (fe == NULL) + return (NULL); + fe->next = a->fixup_list; + a->fixup_list = fe; + fe->fixup = 0; + fe->name = strdup(pathname); + return (fe); +} + +/* + * Returns a fixup structure for the current entry. + */ +static struct fixup_entry * +current_fixup(struct archive_write_disk *a, const char *pathname) +{ + if (a->current_fixup == NULL) + a->current_fixup = new_fixup(a, pathname); + return (a->current_fixup); +} + +/* TODO: Make this work. */ +/* + * TODO: The deep-directory support bypasses this; disable deep directory + * support if we're doing symlink checks. + */ +/* + * TODO: Someday, integrate this with the deep dir support; they both + * scan the path and both can be optimized by comparing against other + * recent paths. + */ +static int +check_symlinks(struct archive_write_disk *a) +{ + char *pn, *p; + char c; + int r; + struct stat st; + + /* + * Gaurd against symlink tricks. Reject any archive entry whose + * destination would be altered by a symlink. + */ + /* Whatever we checked last time doesn't need to be re-checked. */ + pn = a->name; + p = a->path_safe.s; + while ((*pn != '\0') && (*p == *pn)) + ++p, ++pn; + c = pn[0]; + /* Keep going until we've checked the entire name. */ + while (pn[0] != '\0' && (pn[0] != '/' || pn[1] != '\0')) { + /* Skip the next path element. */ + while (*pn != '\0' && *pn != '/') + ++pn; + c = pn[0]; + pn[0] = '\0'; + /* Check that we haven't hit a symlink. */ + r = lstat(a->name, &st); + if (r != 0) { + /* We've hit a dir that doesn't exist; stop now. */ + if (errno == ENOENT) + break; + } else if (S_ISLNK(st.st_mode)) { + if (c == '\0') { + /* + * Last element is symlink; remove it + * so we can overwrite it with the + * item being extracted. + */ + if (unlink(a->name)) { + archive_set_error(&a->archive, errno, + "Could not remove symlink %s", + a->name); + pn[0] = c; + return (ARCHIVE_WARN); + } + /* + * Even if we did remove it, a warning + * is in order. The warning is silly, + * though, if we're just replacing one + * symlink with another symlink. + */ + if (!S_ISLNK(a->mode)) { + archive_set_error(&a->archive, 0, + "Removing symlink %s", + a->name); + } + /* Symlink gone. No more problem! */ + pn[0] = c; + return (0); + } else if (a->flags & ARCHIVE_EXTRACT_UNLINK) { + /* User asked us to remove problems. */ + if (unlink(a->name) != 0) { + archive_set_error(&a->archive, 0, + "Cannot remove intervening symlink %s", + a->name); + pn[0] = c; + return (ARCHIVE_WARN); + } + } else { + archive_set_error(&a->archive, 0, + "Cannot extract through symlink %s", + a->name); + pn[0] = c; + return (ARCHIVE_WARN); + } + } + } + pn[0] = c; + /* We've checked and/or cleaned the whole path, so remember it. */ + archive_strcpy(&a->path_safe, a->name); + return (ARCHIVE_OK); +} + +/* + * Canonicalize the pathname. In particular, this strips duplicate + * '/' characters, '.' elements, and trailing '/'. It also raises an + * error for an empty path, a trailing '..' or (if _SECURE_NODOTDOT is + * set) any '..' in the path. + */ +static int +cleanup_pathname(struct archive_write_disk *a) +{ + char *dest, *src; + char separator = '\0'; + int lastdotdot = 0; /* True if last elt copied was '..' */ + + dest = src = a->name; + if (*src == '\0') { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Invalid empty pathname"); + return (ARCHIVE_WARN); + } + + /* Skip leading '/'. */ + if (*src == '/') + separator = *src++; + + /* Scan the pathname one element at a time. */ + for (;;) { + /* src points to first char after '/' */ + if (src[0] == '\0') { + break; + } else if (src[0] == '/') { + /* Found '//', ignore second one. */ + src++; + continue; + } else if (src[0] == '.') { + if (src[1] == '\0') { + /* Ignore trailing '.' */ + break; + } else if (src[1] == '/') { + /* Skip './'. */ + src += 2; + continue; + } else if (src[1] == '.') { + if (src[2] == '/' || src[2] == '\0') { + /* Conditionally warn about '..' */ + if (a->flags & ARCHIVE_EXTRACT_SECURE_NODOTDOT) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "Path contains '..'"); + return (ARCHIVE_WARN); + } + lastdotdot = 1; + } else + lastdotdot = 0; + /* + * Note: Under no circumstances do we + * remove '..' elements. In + * particular, restoring + * '/foo/../bar/' should create the + * 'foo' dir as a side-effect. + */ + } else + lastdotdot = 0; + } else + lastdotdot = 0; + + /* Copy current element, including leading '/'. */ + if (separator) + *dest++ = '/'; + while (*src != '\0' && *src != '/') { + *dest++ = *src++; + } + + if (*src == '\0') + break; + + /* Skip '/' separator. */ + separator = *src++; + } + /* + * We've just copied zero or more path elements, not including the + * final '/'. + */ + if (lastdotdot) { + /* Trailing '..' is always wrong. */ + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "Path contains trailing '..'"); + return (ARCHIVE_WARN); + } + if (dest == a->name) { + /* + * Nothing got copied. The path must have been something + * like '.' or '/' or './' or '/././././/./'. + */ + if (separator) + *dest++ = '/'; + else + *dest++ = '.'; + } + /* Terminate the result. */ + *dest = '\0'; + return (ARCHIVE_OK); +} + +/* + * Create the parent directory of the specified path, assuming path + * is already in mutable storage. + */ +static int +create_parent_dir(struct archive_write_disk *a, char *path) +{ + char *slash; + int r; + + /* Remove tail element to obtain parent name. */ + slash = strrchr(path, '/'); + if (slash == NULL) + return (ARCHIVE_OK); + *slash = '\0'; + r = create_dir(a, path); + *slash = '/'; + return (r); +} + +/* + * Create the specified dir, recursing to create parents as necessary. + * + * Returns ARCHIVE_OK if the path exists when we're done here. + * Otherwise, returns ARCHIVE_WARN. + * Assumes path is in mutable storage; path is unchanged on exit. + */ +static int +create_dir(struct archive_write_disk *a, char *path) +{ + struct stat st; + struct fixup_entry *le; + char *slash, *base; + mode_t mode_final, mode; + int r; + + r = ARCHIVE_OK; + + /* Check for special names and just skip them. */ + slash = strrchr(path, '/'); + base = strrchr(path, '/'); + if (slash == NULL) + base = path; + else + base = slash + 1; + + if (base[0] == '\0' || + (base[0] == '.' && base[1] == '\0') || + (base[0] == '.' && base[1] == '.' && base[2] == '\0')) { + /* Don't bother trying to create null path, '.', or '..'. */ + if (slash != NULL) { + *slash = '\0'; + r = create_dir(a, path); + *slash = '/'; + return (r); + } + return (ARCHIVE_OK); + } + + /* + * Yes, this should be stat() and not lstat(). Using lstat() + * here loses the ability to extract through symlinks. Also note + * that this should not use the a->st cache. + */ + if (stat(path, &st) == 0) { + if (S_ISDIR(st.st_mode)) + return (ARCHIVE_OK); + if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) { + archive_set_error(&a->archive, EEXIST, + "Can't create directory '%s'", path); + return (ARCHIVE_WARN); + } + if (unlink(path) != 0) { + archive_set_error(&a->archive, errno, + "Can't create directory '%s': " + "Conflicting file cannot be removed"); + return (ARCHIVE_WARN); + } + } else if (errno != ENOENT && errno != ENOTDIR) { + /* Stat failed? */ + archive_set_error(&a->archive, errno, "Can't test directory '%s'", path); + return (ARCHIVE_WARN); + } else if (slash != NULL) { + *slash = '\0'; + r = create_dir(a, path); + *slash = '/'; + if (r != ARCHIVE_OK) + return (r); + } + + /* + * Mode we want for the final restored directory. Per POSIX, + * implicitly-created dirs must be created obeying the umask. + * There's no mention whether this is different for privileged + * restores (which the rest of this code handles by pretending + * umask=0). I've chosen here to always obey the user's umask for + * implicit dirs, even if _EXTRACT_PERM was specified. + */ + mode_final = DEFAULT_DIR_MODE & ~a->user_umask; + /* Mode we want on disk during the restore process. */ + mode = mode_final; + mode |= MINIMUM_DIR_MODE; + mode &= MAXIMUM_DIR_MODE; + if (mkdir(path, mode) == 0) { + if (mode != mode_final) { + le = new_fixup(a, path); + le->fixup |=TODO_MODE_BASE; + le->mode = mode_final; + } + return (ARCHIVE_OK); + } + + /* + * Without the following check, a/b/../b/c/d fails at the + * second visit to 'b', so 'd' can't be created. Note that we + * don't add it to the fixup list here, as it's already been + * added. + */ + if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) + return (ARCHIVE_OK); + + archive_set_error(&a->archive, errno, "Failed to create dir '%s'", path); + return (ARCHIVE_WARN); +} + +/* + * Note: Although we can skip setting the user id if the desired user + * id matches the current user, we cannot skip setting the group, as + * many systems set the gid bit based on the containing directory. So + * we have to perform a chown syscall if we want to restore the SGID + * bit. (The alternative is to stat() and then possibly chown(); it's + * more efficient to skip the stat() and just always chown().) Note + * that a successful chown() here clears the TODO_SGID_CHECK bit, which + * allows set_mode to skip the stat() check for the GID. + */ +static int +set_ownership(struct archive_write_disk *a) +{ + /* If we know we can't change it, don't bother trying. */ + if (a->user_uid != 0 && a->user_uid != a->uid) { + archive_set_error(&a->archive, errno, + "Can't set UID=%d", a->uid); + return (ARCHIVE_WARN); + } + +#ifdef HAVE_FCHOWN + if (a->fd >= 0 && fchown(a->fd, a->uid, a->gid) == 0) + goto success; +#endif + +#ifdef HAVE_LCHOWN + if (lchown(a->name, a->uid, a->gid) == 0) + goto success; +#else + if (!S_ISLNK(a->mode) && chown(a->name, a->uid, a->gid) == 0) + goto success; +#endif + + archive_set_error(&a->archive, errno, + "Can't set user=%d/group=%d for %s", a->uid, a->gid, + a->name); + return (ARCHIVE_WARN); +success: + a->todo &= ~TODO_OWNER; + /* We know the user/group are correct now. */ + a->todo &= ~TODO_SGID_CHECK; + a->todo &= ~TODO_SUID_CHECK; + return (ARCHIVE_OK); +} + +#ifdef HAVE_UTIMES +/* + * The utimes()-family functions provide high resolution and + * a way to set time on an fd or a symlink. We prefer them + * when they're available. + */ +static int +set_time(struct archive_write_disk *a) +{ + struct timeval times[2]; + + times[1].tv_sec = archive_entry_mtime(a->entry); + times[1].tv_usec = archive_entry_mtime_nsec(a->entry) / 1000; + + times[0].tv_sec = archive_entry_atime(a->entry); + times[0].tv_usec = archive_entry_atime_nsec(a->entry) / 1000; + +#ifdef HAVE_FUTIMES + if (a->fd >= 0 && futimes(a->fd, times) == 0) { + return (ARCHIVE_OK); + } +#endif + +#ifdef HAVE_LUTIMES + if (lutimes(a->name, times) != 0) +#else + if (!S_ISLNK(a->mode) && utimes(a->name, times) != 0) +#endif + { + archive_set_error(&a->archive, errno, "Can't update time for %s", + a->name); + return (ARCHIVE_WARN); + } + + /* + * Note: POSIX does not provide a portable way to restore ctime. + * (Apart from resetting the system clock, which is distasteful.) + * So, any restoration of ctime will necessarily be OS-specific. + */ + + /* XXX TODO: Can FreeBSD restore ctime? XXX */ + return (ARCHIVE_OK); +} +#elif defined(HAVE_UTIME) +/* + * utime() is an older, more standard interface that we'll use + * if utimes() isn't available. + */ +static int +set_time(struct archive_write_disk *a) +{ + struct utimbuf times; + + times.modtime = archive_entry_mtime(a->entry); + times.actime = archive_entry_atime(a->entry); + if (!S_ISLNK(a->mode) && utime(a->name, ×) != 0) { + archive_set_error(&a->archive, errno, + "Can't update time for %s", a->name); + return (ARCHIVE_WARN); + } + return (ARCHIVE_OK); +} +#else +/* This platform doesn't give us a way to restore the time. */ +static int +set_time(struct archive_write_disk *a) +{ + (void)a; /* UNUSED */ + archive_set_error(&a->archive, errno, + "Can't update time for %s", a->name); + return (ARCHIVE_WARN); +} +#endif + + +static int +set_mode(struct archive_write_disk *a, int mode) +{ + int r = ARCHIVE_OK; + mode &= 07777; /* Strip off file type bits. */ + + if (a->todo & TODO_SGID_CHECK) { + /* + * If we don't know the GID is right, we must stat() + * to verify it. We can't just check the GID of this + * process, since systems sometimes set GID from + * the enclosing dir or based on ACLs. + */ + if (a->pst != NULL) { + /* Already have stat() data available. */ +#ifdef HAVE_FSTAT + } else if (fd >= 0 && fstat(fd, &a->st) == 0) { + a->pst = &a->st; +#endif + } else if (stat(a->name, &a->st) == 0) { + a->pst = &a->st; + } else { + archive_set_error(&a->archive, errno, + "Couldn't stat file"); + return (ARCHIVE_WARN); + } + if (a->pst->st_gid != a->gid) { + mode &= ~ S_ISGID; + archive_set_error(&a->archive, -1, "Can't restore SGID bit"); + r = ARCHIVE_WARN; + } + /* While we're here, double-check the UID. */ + if (a->pst->st_uid != a->uid + && (a->todo & TODO_SUID)) { + mode &= ~ S_ISUID; + archive_set_error(&a->archive, -1, "Can't restore SUID bit"); + r = ARCHIVE_WARN; + } + a->todo &= ~TODO_SGID_CHECK; + a->todo &= ~TODO_SUID_CHECK; + } else if (a->todo & TODO_SUID_CHECK) { + /* + * If we don't know the UID is right, we can just check + * the user, since all systems set the file UID from + * the process UID. + */ + if (a->user_uid != a->uid) { + mode &= ~ S_ISUID; + archive_set_error(&a->archive, -1, "Can't make file SUID"); + r = ARCHIVE_WARN; + } + a->todo &= ~TODO_SUID_CHECK; + } + + if (S_ISLNK(a->mode)) { +#ifdef HAVE_LCHMOD + /* + * If this is a symlink, use lchmod(). If the + * platform doesn't support lchmod(), just skip it. A + * platform that doesn't provide a way to set + * permissions on symlinks probably ignores + * permissions on symlinks, so a failure here has no + * impact. + */ + if (lchmod(a->name, mode) != 0) { + archive_set_error(&a->archive, errno, + "Can't set permissions to 0%o", (int)mode); + r = ARCHIVE_WARN; + } +#endif + } else if (!S_ISDIR(a->mode)) { + /* + * If it's not a symlink and not a dir, then use + * fchmod() or chmod(), depending on whether we have + * an fd. Dirs get their perms set during the + * post-extract fixup, which is handled elsewhere. + */ +#ifdef HAVE_FCHMOD + if (a->fd >= 0) { + if (fchmod(a->fd, mode) != 0) { + archive_set_error(&a->archive, errno, + "Can't set permissions to 0%o", (int)mode); + r = ARCHIVE_WARN; + } + } else +#endif + /* If this platform lacks fchmod(), then + * we'll just use chmod(). */ + if (chmod(a->name, mode) != 0) { + archive_set_error(&a->archive, errno, + "Can't set permissions to 0%o", (int)mode); + r = ARCHIVE_WARN; + } + } + return (r); +} + +static int +set_fflags(struct archive_write_disk *a) +{ + struct fixup_entry *le; + unsigned long set, clear; + int r; + int critical_flags; + mode_t mode = archive_entry_mode(a->entry); + + /* + * Make 'critical_flags' hold all file flags that can't be + * immediately restored. For example, on BSD systems, + * SF_IMMUTABLE prevents hardlinks from being created, so + * should not be set until after any hardlinks are created. To + * preserve some semblance of portability, this uses #ifdef + * extensively. Ugly, but it works. + * + * Yes, Virginia, this does create a security race. It's mitigated + * somewhat by the practice of creating dirs 0700 until the extract + * is done, but it would be nice if we could do more than that. + * People restoring critical file systems should be wary of + * other programs that might try to muck with files as they're + * being restored. + */ + /* Hopefully, the compiler will optimize this mess into a constant. */ + critical_flags = 0; +#ifdef SF_IMMUTABLE + critical_flags |= SF_IMMUTABLE; +#endif +#ifdef UF_IMMUTABLE + critical_flags |= UF_IMMUTABLE; +#endif +#ifdef SF_APPEND + critical_flags |= SF_APPEND; +#endif +#ifdef UF_APPEND + critical_flags |= UF_APPEND; +#endif +#ifdef EXT2_APPEND_FL + critical_flags |= EXT2_APPEND_FL; +#endif +#ifdef EXT2_IMMUTABLE_FL + critical_flags |= EXT2_IMMUTABLE_FL; +#endif + + if (a->todo & TODO_FFLAGS) { + archive_entry_fflags(a->entry, &set, &clear); + + /* + * The first test encourages the compiler to eliminate + * all of this if it's not necessary. + */ + if ((critical_flags != 0) && (set & critical_flags)) { + le = current_fixup(a, a->name); + le->fixup |= TODO_FFLAGS; + le->fflags_set = set; + /* Store the mode if it's not already there. */ + if ((le->fixup & TODO_MODE) == 0) + le->mode = mode; + } else { + r = set_fflags_platform(a, a->fd, + a->name, mode, set, clear); + if (r != ARCHIVE_OK) + return (r); + } + } + return (ARCHIVE_OK); +} + + +#if ( defined(HAVE_LCHFLAGS) || defined(HAVE_CHFLAGS) || defined(HAVE_FCHFLAGS) ) && !defined(__linux) +static int +set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, + mode_t mode, unsigned long set, unsigned long clear) +{ + (void)mode; /* UNUSED */ + if (set == 0 && clear == 0) + return (ARCHIVE_OK); + + /* + * XXX Is the stat here really necessary? Or can I just use + * the 'set' flags directly? In particular, I'm not sure + * about the correct approach if we're overwriting an existing + * file that already has flags on it. XXX + */ + if (fd >= 0 && fstat(fd, &a->st) == 0) + a->pst = &a->st; + else if (lstat(name, &a->st) == 0) + a->pst = &a->st; + else { + archive_set_error(&a->archive, errno, + "Couldn't stat file"); + return (ARCHIVE_WARN); + } + + a->st.st_flags &= ~clear; + a->st.st_flags |= set; +#ifdef HAVE_FCHFLAGS + /* If platform has fchflags() and we were given an fd, use it. */ + if (fd >= 0 && fchflags(fd, a->st.st_flags) == 0) + return (ARCHIVE_OK); +#endif + /* + * If we can't use the fd to set the flags, we'll use the + * pathname to set flags. We prefer lchflags() but will use + * chflags() if we must. + */ +#ifdef HAVE_LCHFLAGS + if (lchflags(name, a->st.st_flags) == 0) + return (ARCHIVE_OK); +#elif defined(HAVE_CHFLAGS) + if (S_ISLNK(a->st.st_mode)) { + archive_set_error(&a->archive, errno, + "Can't set file flags on symlink."); + return (ARCHIVE_WARN); + } + if (chflags(name, a->st.st_flags) == 0) + return (ARCHIVE_OK); +#endif + archive_set_error(&a->archive, errno, + "Failed to set file flags"); + return (ARCHIVE_WARN); +} + +#elif defined(__linux) && defined(EXT2_IOC_GETFLAGS) && defined(EXT2_IOC_SETFLAGS) + +/* + * Linux has flags too, but uses ioctl() to access them instead of + * having a separate chflags() system call. + */ +static int +set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, + mode_t mode, unsigned long set, unsigned long clear) +{ + int ret; + int myfd = fd; + unsigned long newflags, oldflags; + unsigned long sf_mask = 0; + + if (set == 0 && clear == 0) + return (ARCHIVE_OK); + /* Only regular files and dirs can have flags. */ + if (!S_ISREG(mode) && !S_ISDIR(mode)) + return (ARCHIVE_OK); + + /* If we weren't given an fd, open it ourselves. */ + if (myfd < 0) + myfd = open(name, O_RDONLY|O_NONBLOCK); + if (myfd < 0) + return (ARCHIVE_OK); + + /* + * Linux has no define for the flags that are only settable by + * the root user. This code may seem a little complex, but + * there seem to be some Linux systems that lack these + * defines. (?) The code below degrades reasonably gracefully + * if sf_mask is incomplete. + */ +#ifdef EXT2_IMMUTABLE_FL + sf_mask |= EXT2_IMMUTABLE_FL; +#endif +#ifdef EXT2_APPEND_FL + sf_mask |= EXT2_APPEND_FL; +#endif + /* + * XXX As above, this would be way simpler if we didn't have + * to read the current flags from disk. XXX + */ + ret = ARCHIVE_OK; + /* Try setting the flags as given. */ + if (ioctl(myfd, EXT2_IOC_GETFLAGS, &oldflags) >= 0) { + newflags = (oldflags & ~clear) | set; + if (ioctl(myfd, EXT2_IOC_SETFLAGS, &newflags) >= 0) + goto cleanup; + if (errno != EPERM) + goto fail; + } + /* If we couldn't set all the flags, try again with a subset. */ + if (ioctl(myfd, EXT2_IOC_GETFLAGS, &oldflags) >= 0) { + newflags &= ~sf_mask; + oldflags &= sf_mask; + newflags |= oldflags; + if (ioctl(myfd, EXT2_IOC_SETFLAGS, &newflags) >= 0) + goto cleanup; + } + /* We couldn't set the flags, so report the failure. */ +fail: + archive_set_error(&a->archive, errno, + "Failed to set file flags"); + ret = ARCHIVE_WARN; +cleanup: + if (fd < 0) + close(myfd); + return (ret); +} + +#else /* Not HAVE_CHFLAGS && Not __linux */ + +/* + * Of course, some systems have neither BSD chflags() nor Linux' flags + * support through ioctl(). + */ +static int +set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, + mode_t mode, unsigned long set, unsigned long clear) +{ + (void)a; /* UNUSED */ + (void)fd; /* UNUSED */ + (void)name; /* UNUSED */ + (void)mode; /* UNUSED */ + (void)set; /* UNUSED */ + (void)clear; /* UNUSED */ + return (ARCHIVE_OK); +} + +#endif /* __linux */ + +#ifndef HAVE_POSIX_ACL +/* Default empty function body to satisfy mainline code. */ +static int +set_acls(struct archive_write_disk *a) +{ + (void)a; /* UNUSED */ + return (ARCHIVE_OK); +} + +#else + +/* + * XXX TODO: What about ACL types other than ACCESS and DEFAULT? + */ +static int +set_acls(struct archive_write_disk *a) +{ + int ret; + + ret = set_acl(a, a->fd, a->entry, ACL_TYPE_ACCESS, + ARCHIVE_ENTRY_ACL_TYPE_ACCESS, "access"); + if (ret != ARCHIVE_OK) + return (ret); + ret = set_acl(a, a->fd, a->entry, ACL_TYPE_DEFAULT, + ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, "default"); + return (ret); +} + + +static int +set_acl(struct archive_write_disk *a, int fd, struct archive_entry *entry, + acl_type_t acl_type, int ae_requested_type, const char *tname) +{ + acl_t acl; + acl_entry_t acl_entry; + acl_permset_t acl_permset; + int ret; + int ae_type, ae_permset, ae_tag, ae_id; + uid_t ae_uid; + gid_t ae_gid; + const char *ae_name; + int entries; + const char *name; + + ret = ARCHIVE_OK; + entries = archive_entry_acl_reset(entry, ae_requested_type); + if (entries == 0) + return (ARCHIVE_OK); + acl = acl_init(entries); + while (archive_entry_acl_next(entry, ae_requested_type, &ae_type, + &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) { + acl_create_entry(&acl, &acl_entry); + + switch (ae_tag) { + case ARCHIVE_ENTRY_ACL_USER: + acl_set_tag_type(acl_entry, ACL_USER); + ae_uid = a->lookup_uid(a->lookup_uid_data, + ae_name, ae_id); + acl_set_qualifier(acl_entry, &ae_uid); + break; + case ARCHIVE_ENTRY_ACL_GROUP: + acl_set_tag_type(acl_entry, ACL_GROUP); + ae_gid = a->lookup_gid(a->lookup_gid_data, + ae_name, ae_id); + acl_set_qualifier(acl_entry, &ae_gid); + break; + case ARCHIVE_ENTRY_ACL_USER_OBJ: + acl_set_tag_type(acl_entry, ACL_USER_OBJ); + break; + case ARCHIVE_ENTRY_ACL_GROUP_OBJ: + acl_set_tag_type(acl_entry, ACL_GROUP_OBJ); + break; + case ARCHIVE_ENTRY_ACL_MASK: + acl_set_tag_type(acl_entry, ACL_MASK); + break; + case ARCHIVE_ENTRY_ACL_OTHER: + acl_set_tag_type(acl_entry, ACL_OTHER); + break; + default: + /* XXX */ + break; + } + + acl_get_permset(acl_entry, &acl_permset); + acl_clear_perms(acl_permset); + if (ae_permset & ARCHIVE_ENTRY_ACL_EXECUTE) + acl_add_perm(acl_permset, ACL_EXECUTE); + if (ae_permset & ARCHIVE_ENTRY_ACL_WRITE) + acl_add_perm(acl_permset, ACL_WRITE); + if (ae_permset & ARCHIVE_ENTRY_ACL_READ) + acl_add_perm(acl_permset, ACL_READ); + } + + name = archive_entry_pathname(entry); + + /* Try restoring the ACL through 'fd' if we can. */ +#if HAVE_ACL_SET_FD + if (fd >= 0 && acl_type == ACL_TYPE_ACCESS && acl_set_fd(fd, acl) == 0) + ret = ARCHIVE_OK; + else +#else +#if HAVE_ACL_SET_FD_NP + if (fd >= 0 && acl_set_fd_np(fd, acl, acl_type) == 0) + ret = ARCHIVE_OK; + else +#endif +#endif + if (acl_set_file(name, acl_type, acl) != 0) { + archive_set_error(&a->archive, errno, "Failed to set %s acl", tname); + ret = ARCHIVE_WARN; + } + acl_free(acl); + return (ret); +} +#endif + +#if HAVE_LSETXATTR +/* + * Restore extended attributes - Linux implementation + */ +static int +set_xattrs(struct archive_write_disk *a) +{ + struct archive_entry *entry = a->entry; + static int warning_done = 0; + int ret = ARCHIVE_OK; + int i = archive_entry_xattr_reset(entry); + + while (i--) { + const char *name; + const void *value; + size_t size; + archive_entry_xattr_next(entry, &name, &value, &size); + if (name != NULL && + strncmp(name, "xfsroot.", 8) != 0 && + strncmp(name, "system.", 7) != 0) { + int e; +#if HAVE_FSETXATTR + if (a->fd >= 0) + e = fsetxattr(a->fd, name, value, size, 0); + else +#endif + { + e = lsetxattr(archive_entry_pathname(entry), + name, value, size, 0); + } + if (e == -1) { + if (errno == ENOTSUP) { + if (!warning_done) { + warning_done = 1; + archive_set_error(&a->archive, errno, + "Cannot restore extended " + "attributes on this file " + "system"); + } + } else + archive_set_error(&a->archive, errno, + "Failed to set extended attribute"); + ret = ARCHIVE_WARN; + } + } else { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Invalid extended attribute encountered"); + ret = ARCHIVE_WARN; + } + } + return (ret); +} +#else +/* + * Restore extended attributes - stub implementation for unsupported systems + */ +static int +set_xattrs(struct archive_write_disk *a) +{ + static int warning_done = 0; + + /* If there aren't any extended attributes, then it's okay not + * to extract them, otherwise, issue a single warning. */ + if (archive_entry_xattr_count(a->entry) != 0 && !warning_done) { + warning_done = 1; + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Cannot restore extended attributes on this system"); + return (ARCHIVE_WARN); + } + /* Warning was already emitted; suppress further warnings. */ + return (ARCHIVE_OK); +} +#endif + + +/* + * Trivial implementations of gid/uid lookup functions. + * These are normally overridden by the client, but these stub + * versions ensure that we always have something that works. + */ +static gid_t +trivial_lookup_gid(void *private_data, const char *gname, gid_t gid) +{ + (void)private_data; /* UNUSED */ + (void)gname; /* UNUSED */ + return (gid); +} + +static uid_t +trivial_lookup_uid(void *private_data, const char *uname, uid_t uid) +{ + (void)private_data; /* UNUSED */ + (void)uname; /* UNUSED */ + return (uid); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_write_disk_private.h b/contrib/libarchive-2.1/libarchive/archive_write_disk_private.h new file mode 100644 index 0000000000..1e8ad6922e --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_write_disk_private.h @@ -0,0 +1,34 @@ +/*- + * 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 + * in this position and unchanged. + * 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. + * + * $FreeBSD: src/lib/libarchive/archive_write_disk_private.h,v 1.1 2007/03/03 07:37:36 kientzle Exp $ + */ + +#ifndef ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED +#define ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED + +struct archive_write_disk; + +#endif diff --git a/contrib/libarchive-2.1/libarchive/archive_write_disk_set_standard_lookup.c b/contrib/libarchive-2.1/libarchive/archive_write_disk_set_standard_lookup.c new file mode 100644 index 0000000000..054b942f27 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_write_disk_set_standard_lookup.c @@ -0,0 +1,193 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_disk_set_standard_lookup.c,v 1.2 2007/04/20 15:32:13 kientzle Exp $"); + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_GRP_H +#include +#endif +#ifdef HAVE_PWD_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "archive.h" +#include "archive_private.h" +#include "archive_read_private.h" +#include "archive_write_disk_private.h" + +struct bucket { + char *name; + int hash; + id_t id; +}; + +static const size_t cache_size = 127; +static unsigned int hash(const char *); +static gid_t lookup_gid(void *, const char *uname, gid_t); +static uid_t lookup_uid(void *, const char *uname, uid_t); +static void cleanup(void *); + +/* + * Installs functions that use getpwnam()/getgrnam()---along with + * a simple cache to accelerate such lookups---into the archive_write_disk + * object. This is in a separate file because getpwnam()/getgrnam() + * can pull in a LOT of library code (including NIS/LDAP functions, which + * pull in DNS resolveers, etc). This can easily top 500kB, which makes + * it inappropriate for some space-constrained applications. + * + * Applications that are size-sensitive may want to just use the + * real default functions (defined in archive_write_disk.c) that just + * use the uid/gid without the lookup. Or define your own custom functions + * if you prefer. + * + * TODO: Replace these hash tables with simpler move-to-front LRU + * lists with a bounded size (128 items?). The hash is a bit faster, + * but has a bad pathology in which it thrashes a single bucket. Even + * walking a list of 128 items is a lot faster than calling + * getpwnam()! + */ +int +archive_write_disk_set_standard_lookup(struct archive *a) +{ + struct bucket *ucache = malloc(sizeof(struct bucket[cache_size])); + struct bucket *gcache = malloc(sizeof(struct bucket[cache_size])); + memset(ucache, 0, sizeof(struct bucket[cache_size])); + memset(gcache, 0, sizeof(struct bucket[cache_size])); + archive_write_disk_set_group_lookup(a, gcache, lookup_gid, cleanup); + archive_write_disk_set_user_lookup(a, ucache, lookup_uid, cleanup); + return (ARCHIVE_OK); +} + +static gid_t +lookup_gid(void *private_data, const char *gname, gid_t gid) +{ + int h; + struct bucket *b; + struct bucket *gcache = (struct bucket *)private_data; + + /* If no gname, just use the gid provided. */ + if (gname == NULL || *gname == '\0') + return (gid); + + /* Try to find gname in the cache. */ + h = hash(gname); + b = &gcache[h % cache_size ]; + if (b->name != NULL && b->hash == h && strcmp(gname, b->name) == 0) + return ((gid_t)b->id); + + /* Free the cache slot for a new entry. */ + if (b->name != NULL) + free(b->name); + b->name = strdup(gname); + /* Note: If strdup fails, that's okay; we just won't cache. */ + b->hash = h; +#if HAVE_GRP_H + { + struct group *grent = getgrnam(gname); + if (grent != NULL) + gid = grent->gr_gid; + } +#elif _WIN32 + /* TODO: do a gname->gid lookup for Windows. */ +#endif + b->id = gid; + + return (gid); +} + +static uid_t +lookup_uid(void *private_data, const char *uname, uid_t uid) +{ + int h; + struct bucket *b; + struct bucket *ucache = (struct bucket *)private_data; + + /* If no uname, just use the uid provided. */ + if (uname == NULL || *uname == '\0') + return (uid); + + /* Try to find uname in the cache. */ + h = hash(uname); + b = &ucache[h % cache_size ]; + if (b->name != NULL && b->hash == h && strcmp(uname, b->name) == 0) + return ((uid_t)b->id); + + /* Free the cache slot for a new entry. */ + if (b->name != NULL) + free(b->name); + b->name = strdup(uname); + /* Note: If strdup fails, that's okay; we just won't cache. */ + b->hash = h; +#if HAVE_PWD_H + { + struct passwd *pwent = getpwnam(uname); + if (pwent != NULL) + uid = pwent->pw_uid; + } +#elif _WIN32 + /* TODO: do a uname->uid lookup for Windows. */ +#endif + b->id = uid; + + return (uid); +} + +static void +cleanup(void *private) +{ + size_t i; + struct bucket *cache = (struct bucket *)private; + + for (i = 0; i < cache_size; i++) + free(cache[i].name); + free(cache); +} + + +static unsigned int +hash(const char *p) +{ + /* A 32-bit version of Peter Weinberger's (PJW) hash algorithm, + as used by ELF for hashing function names. */ + unsigned g, h = 0; + while (*p != '\0') { + h = ( h << 4 ) + *p++; + if (( g = h & 0xF0000000 )) { + h ^= g >> 24; + h &= 0x0FFFFFFF; + } + } + return h; +} diff --git a/contrib/libarchive-2.1/libarchive/archive_write_open_fd.c b/contrib/libarchive-2.1/libarchive/archive_write_open_fd.c new file mode 100644 index 0000000000..35e258c4cc --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_write_open_fd.c @@ -0,0 +1,132 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_open_fd.c,v 1.9 2007/01/09 08:05:56 kientzle Exp $"); + +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "archive.h" + +struct write_fd_data { + off_t offset; + int fd; +}; + +static int file_close(struct archive *, void *); +static int file_open(struct archive *, void *); +static ssize_t file_write(struct archive *, void *, const void *buff, size_t); + +int +archive_write_open_fd(struct archive *a, int fd) +{ + struct write_fd_data *mine; + + mine = (struct write_fd_data *)malloc(sizeof(*mine)); + if (mine == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); + } + mine->fd = fd; + return (archive_write_open(a, mine, + file_open, file_write, file_close)); +} + +static int +file_open(struct archive *a, void *client_data) +{ + struct write_fd_data *mine; + struct stat st; + + mine = (struct write_fd_data *)client_data; + + if (fstat(mine->fd, &st) != 0) { + archive_set_error(a, errno, "Couldn't stat fd %d", mine->fd); + return (ARCHIVE_FATAL); + } + + /* + * If this is a regular file, don't add it to itself. + */ + if (S_ISREG(st.st_mode)) + archive_write_set_skip_file(a, st.st_dev, st.st_ino); + + /* + * If client hasn't explicitly set the last block handling, + * then set it here. + */ + if (archive_write_get_bytes_in_last_block(a) < 0) { + /* If the output is a block or character device, fifo, + * or stdout, pad the last block, otherwise leave it + * unpadded. */ + if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) || + S_ISFIFO(st.st_mode) || (mine->fd == 1)) + /* Last block will be fully padded. */ + archive_write_set_bytes_in_last_block(a, 0); + else + archive_write_set_bytes_in_last_block(a, 1); + } + + return (ARCHIVE_OK); +} + +static ssize_t +file_write(struct archive *a, void *client_data, const void *buff, size_t length) +{ + struct write_fd_data *mine; + ssize_t bytesWritten; + + mine = (struct write_fd_data *)client_data; + bytesWritten = write(mine->fd, buff, length); + if (bytesWritten <= 0) { + archive_set_error(a, errno, "Write error"); + return (-1); + } + return (bytesWritten); +} + +static int +file_close(struct archive *a, void *client_data) +{ + struct write_fd_data *mine = (struct write_fd_data *)client_data; + + (void)a; /* UNUSED */ + free(mine); + return (ARCHIVE_OK); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_write_open_file.c b/contrib/libarchive-2.1/libarchive/archive_write_open_file.c new file mode 100644 index 0000000000..5c0c737f85 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_write_open_file.c @@ -0,0 +1,105 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_open_file.c,v 1.19 2007/01/09 08:05:56 kientzle Exp $"); + +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "archive.h" + +struct write_FILE_data { + FILE *f; +}; + +static int file_close(struct archive *, void *); +static int file_open(struct archive *, void *); +static ssize_t file_write(struct archive *, void *, const void *buff, size_t); + +int +archive_write_open_FILE(struct archive *a, FILE *f) +{ + struct write_FILE_data *mine; + + mine = (struct write_FILE_data *)malloc(sizeof(*mine)); + if (mine == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); + } + mine->f = f; + return (archive_write_open(a, mine, + file_open, file_write, file_close)); +} + +static int +file_open(struct archive *a, void *client_data) +{ + (void)a; /* UNUSED */ + (void)client_data; /* UNUSED */ + + return (ARCHIVE_OK); +} + +static ssize_t +file_write(struct archive *a, void *client_data, const void *buff, size_t length) +{ + struct write_FILE_data *mine; + size_t bytesWritten; + + mine = client_data; + bytesWritten = fwrite(buff, 1, length, mine->f); + if (bytesWritten < length) { + archive_set_error(a, errno, "Write error"); + return (-1); + } + return (bytesWritten); +} + +static int +file_close(struct archive *a, void *client_data) +{ + struct write_FILE_data *mine = client_data; + + (void)a; /* UNUSED */ + free(mine); + return (ARCHIVE_OK); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_write_open_filename.c b/contrib/libarchive-2.1/libarchive/archive_write_open_filename.c new file mode 100644 index 0000000000..fcaaaca4a6 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_write_open_filename.c @@ -0,0 +1,175 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_open_filename.c,v 1.19 2007/01/09 08:05:56 kientzle Exp $"); + +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "archive.h" + +struct write_file_data { + int fd; + char filename[1]; +}; + +static int file_close(struct archive *, void *); +static int file_open(struct archive *, void *); +static ssize_t file_write(struct archive *, void *, const void *buff, size_t); + +int +archive_write_open_file(struct archive *a, const char *filename) +{ + return (archive_write_open_filename(a, filename)); +} + +int +archive_write_open_filename(struct archive *a, const char *filename) +{ + struct write_file_data *mine; + + if (filename == NULL || filename[0] == '\0') { + mine = (struct write_file_data *)malloc(sizeof(*mine)); + if (mine == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); + } + mine->filename[0] = '\0'; /* Record that we're using stdout. */ + } else { + mine = (struct write_file_data *)malloc(sizeof(*mine) + strlen(filename)); + if (mine == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); + } + strcpy(mine->filename, filename); + } + mine->fd = -1; + return (archive_write_open(a, mine, + file_open, file_write, file_close)); +} + +static int +file_open(struct archive *a, void *client_data) +{ + int flags; + struct write_file_data *mine; + struct stat st; + + mine = (struct write_file_data *)client_data; + flags = O_WRONLY | O_CREAT | O_TRUNC; + + /* + * Open the file. + */ + if (mine->filename[0] != '\0') { + mine->fd = open(mine->filename, flags, 0666); + if (mine->fd < 0) { + archive_set_error(a, errno, "Failed to open '%s'", + mine->filename); + return (ARCHIVE_FATAL); + } + } else { + /* + * NULL filename is stdout. + */ + mine->fd = 1; + /* By default, pad archive when writing to stdout. */ + if (archive_write_get_bytes_in_last_block(a) < 0) + archive_write_set_bytes_in_last_block(a, 0); + } + + if (fstat(mine->fd, &st) != 0) { + archive_set_error(a, errno, "Couldn't stat '%s'", + mine->filename); + return (ARCHIVE_FATAL); + } + + /* + * Set up default last block handling. + */ + if (archive_write_get_bytes_in_last_block(a) < 0) { + if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) || + S_ISFIFO(st.st_mode)) + /* Pad last block when writing to device or FIFO. */ + archive_write_set_bytes_in_last_block(a, 0); + else + /* Don't pad last block otherwise. */ + archive_write_set_bytes_in_last_block(a, 1); + } + + /* + * If the output file is a regular file, don't add it to + * itself. If it's a device file, it's okay to add the device + * entry to the output archive. + */ + if (S_ISREG(st.st_mode)) + archive_write_set_skip_file(a, st.st_dev, st.st_ino); + + return (ARCHIVE_OK); +} + +static ssize_t +file_write(struct archive *a, void *client_data, const void *buff, size_t length) +{ + struct write_file_data *mine; + ssize_t bytesWritten; + + mine = (struct write_file_data *)client_data; + bytesWritten = write(mine->fd, buff, length); + if (bytesWritten <= 0) { + archive_set_error(a, errno, "Write error"); + return (-1); + } + return (bytesWritten); +} + +static int +file_close(struct archive *a, void *client_data) +{ + struct write_file_data *mine = (struct write_file_data *)client_data; + + (void)a; /* UNUSED */ + if (mine->filename[0] != '\0') + close(mine->fd); + free(mine); + return (ARCHIVE_OK); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_write_open_memory.c b/contrib/libarchive-2.1/libarchive/archive_write_open_memory.c new file mode 100644 index 0000000000..d235ca01dc --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_write_open_memory.c @@ -0,0 +1,126 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_open_memory.c,v 1.3 2007/01/09 08:05:56 kientzle Exp $"); + +#include +#include +#include + +#include "archive.h" + +/* + * This is a little tricky. I used to allow the + * compression handling layer to fork the compressor, + * which means this write function gets invoked in + * a separate process. That would, of course, make it impossible + * to actually use the data stored into memory here. + * Fortunately, none of the compressors fork today and + * I'm reluctant to use that route in the future but, if + * forking compressors ever do reappear, this will have + * to get a lot more complicated. + */ + +struct write_memory_data { + size_t used; + size_t size; + size_t * client_size; + unsigned char * buff; +}; + +static int memory_write_close(struct archive *, void *); +static int memory_write_open(struct archive *, void *); +static ssize_t memory_write(struct archive *, void *, const void *buff, size_t); + +/* + * Client provides a pointer to a block of memory to receive + * the data. The 'size' param both tells us the size of the + * client buffer and lets us tell the client the final size. + */ +int +archive_write_open_memory(struct archive *a, void *buff, size_t buffSize, size_t *used) +{ + struct write_memory_data *mine; + + mine = (struct write_memory_data *)malloc(sizeof(*mine)); + if (mine == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); + } + memset(mine, 0, sizeof(*mine)); + mine->buff = buff; + mine->size = buffSize; + mine->client_size = used; + return (archive_write_open(a, mine, + memory_write_open, memory_write, memory_write_close)); +} + +static int +memory_write_open(struct archive *a, void *client_data) +{ + struct write_memory_data *mine; + mine = client_data; + mine->used = 0; + if (mine->client_size != NULL) + *mine->client_size = mine->used; + /* Disable padding if it hasn't been set explicitly. */ + if (-1 == archive_write_get_bytes_in_last_block(a)) + archive_write_set_bytes_in_last_block(a, 1); + return (ARCHIVE_OK); +} + +/* + * Copy the data into the client buffer. + * Note that we update mine->client_size on every write. + * In particular, this means the client can follow exactly + * how much has been written into their buffer at any time. + */ +static ssize_t +memory_write(struct archive *a, void *client_data, const void *buff, size_t length) +{ + struct write_memory_data *mine; + mine = client_data; + + if (mine->used + length > mine->size) { + archive_set_error(a, ENOMEM, "Buffer exhausted"); + return (ARCHIVE_FATAL); + } + memcpy(mine->buff + mine->used, buff, length); + mine->used += length; + if (mine->client_size != NULL) + *mine->client_size = mine->used; + return (length); +} + +static int +memory_write_close(struct archive *a, void *client_data) +{ + struct write_memory_data *mine; + (void)a; /* UNUSED */ + mine = client_data; + free(mine); + return (ARCHIVE_OK); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_write_private.h b/contrib/libarchive-2.1/libarchive/archive_write_private.h new file mode 100644 index 0000000000..083b6cac7f --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_write_private.h @@ -0,0 +1,120 @@ +/*- + * 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. + * + * $FreeBSD: src/lib/libarchive/archive_write_private.h,v 1.1 2007/03/03 07:37:36 kientzle Exp $ + */ + +#ifndef ARCHIVE_WRITE_PRIVATE_H_INCLUDED +#define ARCHIVE_WRITE_PRIVATE_H_INCLUDED + +#include "archive.h" +#include "archive_string.h" +#include "archive_private.h" + +struct archive_write { + struct archive archive; + + /* Dev/ino of the archive being written. */ + dev_t skip_file_dev; + ino_t skip_file_ino; + + /* Utility: Pointer to a block of nulls. */ + const unsigned char *nulls; + size_t null_length; + + /* Callbacks to open/read/write/close archive stream. */ + archive_open_callback *client_opener; + archive_write_callback *client_writer; + archive_close_callback *client_closer; + void *client_data; + + /* + * Blocking information. Note that bytes_in_last_block is + * misleadingly named; I should find a better name. These + * control the final output from all compressors, including + * compression_none. + */ + int bytes_per_block; + int bytes_in_last_block; + + /* + * These control whether data within a gzip/bzip2 compressed + * stream gets padded or not. If pad_uncompressed is set, + * the data will be padded to a full block before being + * compressed. The pad_uncompressed_byte determines the value + * that will be used for padding. Note that these have no + * effect on compression "none." + */ + int pad_uncompressed; + int pad_uncompressed_byte; /* TODO: Support this. */ + + /* + * On write, the client just invokes an archive_write_set function + * which sets up the data here directly. + */ + struct { + void *data; + void *config; + int (*init)(struct archive_write *); + int (*finish)(struct archive_write *); + int (*write)(struct archive_write *, const void *, size_t); + } compressor; + + /* + * Again, write support is considerably simpler because there's + * no need for an auction. + */ + int archive_format; + const char *archive_format_name; + + /* + * Pointers to format-specific functions for writing. They're + * initialized by archive_write_set_format_XXX() calls. + */ + void *format_data; + int (*format_init)(struct archive_write *); + int (*format_finish)(struct archive_write *); + int (*format_destroy)(struct archive_write *); + int (*format_finish_entry)(struct archive_write *); + int (*format_write_header)(struct archive_write *, + struct archive_entry *); + ssize_t (*format_write_data)(struct archive_write *, + const void *buff, size_t); +}; + +/* + * Utility function to format a USTAR header into a buffer. If + * "strict" is set, this tries to create the absolutely most portable + * version of a ustar header. If "strict" is set to 0, then it will + * relax certain requirements. + * + * Generally, format-specific declarations don't belong in this + * header; this is a rare example of a function that is shared by + * two very similar formats (ustar and pax). + */ +int +__archive_write_format_header_ustar(struct archive_write *, char buff[512], + struct archive_entry *, int tartype, int strict); + +#endif diff --git a/contrib/libarchive-2.1/libarchive/archive_write_set_compression_bzip2.c b/contrib/libarchive-2.1/libarchive/archive_write_set_compression_bzip2.c new file mode 100644 index 0000000000..43e1d08c60 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_write_set_compression_bzip2.c @@ -0,0 +1,348 @@ +/*- + * 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 "archive_platform.h" + +/* Don't compile this if we don't have bzlib. */ +#if HAVE_BZLIB_H + +__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_bzip2.c,v 1.11 2007/03/03 07:37:36 kientzle Exp $"); + +#ifdef HAVE_ERRNO_H +#include +#endif +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_BZLIB_H +#include +#endif + +#include "archive.h" +#include "archive_private.h" +#include "archive_write_private.h" + +struct private_data { + bz_stream stream; + int64_t total_in; + char *compressed; + size_t compressed_buffer_size; +}; + + +/* + * Yuck. bzlib.h is not const-correct, so I need this one bit + * of ugly hackery to convert a const * pointer to a non-const pointer. + */ +#define SET_NEXT_IN(st,src) \ + (st)->stream.next_in = (char *)(uintptr_t)(const void *)(src) + +static int archive_compressor_bzip2_finish(struct archive_write *); +static int archive_compressor_bzip2_init(struct archive_write *); +static int archive_compressor_bzip2_write(struct archive_write *, + const void *, size_t); +static int drive_compressor(struct archive_write *, struct private_data *, + int finishing); + +/* + * Allocate, initialize and return an archive object. + */ +int +archive_write_set_compression_bzip2(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_set_compression_bzip2"); + a->compressor.init = &archive_compressor_bzip2_init; + return (ARCHIVE_OK); +} + +/* + * Setup callback. + */ +static int +archive_compressor_bzip2_init(struct archive_write *a) +{ + int ret; + struct private_data *state; + + a->archive.compression_code = ARCHIVE_COMPRESSION_BZIP2; + a->archive.compression_name = "bzip2"; + + if (a->client_opener != NULL) { + ret = (a->client_opener)(&a->archive, a->client_data); + if (ret != 0) + return (ret); + } + + state = (struct private_data *)malloc(sizeof(*state)); + if (state == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate data for compression"); + return (ARCHIVE_FATAL); + } + memset(state, 0, sizeof(*state)); + + state->compressed_buffer_size = a->bytes_per_block; + state->compressed = (char *)malloc(state->compressed_buffer_size); + + if (state->compressed == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate data for compression buffer"); + free(state); + return (ARCHIVE_FATAL); + } + + state->stream.next_out = state->compressed; + state->stream.avail_out = state->compressed_buffer_size; + a->compressor.write = archive_compressor_bzip2_write; + a->compressor.finish = archive_compressor_bzip2_finish; + + /* Initialize compression library */ + ret = BZ2_bzCompressInit(&(state->stream), 9, 0, 30); + if (ret == BZ_OK) { + a->compressor.data = state; + return (ARCHIVE_OK); + } + + /* Library setup failed: clean up. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing compression library"); + free(state->compressed); + free(state); + + /* Override the error message if we know what really went wrong. */ + switch (ret) { + case BZ_PARAM_ERROR: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing compression library: " + "invalid setup parameter"); + break; + case BZ_MEM_ERROR: + archive_set_error(&a->archive, ENOMEM, + "Internal error initializing compression library: " + "out of memory"); + break; + case BZ_CONFIG_ERROR: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing compression library: " + "mis-compiled library"); + break; + } + + return (ARCHIVE_FATAL); + +} + +/* + * Write data to the compressed stream. + * + * Returns ARCHIVE_OK if all data written, error otherwise. + */ +static int +archive_compressor_bzip2_write(struct archive_write *a, const void *buff, + size_t length) +{ + struct private_data *state; + + state = (struct private_data *)a->compressor.data; + if (a->client_writer == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "No write callback is registered? " + "This is probably an internal programming error."); + return (ARCHIVE_FATAL); + } + + /* Update statistics */ + state->total_in += length; + + /* Compress input data to output buffer */ + SET_NEXT_IN(state, buff); + state->stream.avail_in = length; + if (drive_compressor(a, state, 0)) + return (ARCHIVE_FATAL); + a->archive.file_position += length; + return (ARCHIVE_OK); +} + + +/* + * Finish the compression. + */ +static int +archive_compressor_bzip2_finish(struct archive_write *a) +{ + ssize_t block_length; + int ret; + struct private_data *state; + ssize_t target_block_length; + ssize_t bytes_written; + unsigned tocopy; + + state = (struct private_data *)a->compressor.data; + ret = ARCHIVE_OK; + if (a->client_writer == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "No write callback is registered?\n" + "This is probably an internal programming error."); + ret = ARCHIVE_FATAL; + goto cleanup; + } + + /* By default, always pad the uncompressed data. */ + if (a->pad_uncompressed) { + tocopy = a->bytes_per_block - + (state->total_in % a->bytes_per_block); + while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) { + SET_NEXT_IN(state, a->nulls); + state->stream.avail_in = tocopy < a->null_length ? + tocopy : a->null_length; + state->total_in += state->stream.avail_in; + tocopy -= state->stream.avail_in; + ret = drive_compressor(a, state, 0); + if (ret != ARCHIVE_OK) + goto cleanup; + } + } + + /* Finish compression cycle. */ + if ((ret = drive_compressor(a, state, 1))) + goto cleanup; + + /* Optionally, pad the final compressed block. */ + block_length = state->stream.next_out - state->compressed; + + + /* Tricky calculation to determine size of last block. */ + target_block_length = block_length; + if (a->bytes_in_last_block <= 0) + /* Default or Zero: pad to full block */ + target_block_length = a->bytes_per_block; + else + /* Round length to next multiple of bytes_in_last_block. */ + target_block_length = a->bytes_in_last_block * + ( (block_length + a->bytes_in_last_block - 1) / + a->bytes_in_last_block); + if (target_block_length > a->bytes_per_block) + target_block_length = a->bytes_per_block; + if (block_length < target_block_length) { + memset(state->stream.next_out, 0, + target_block_length - block_length); + block_length = target_block_length; + } + + /* Write the last block */ + bytes_written = (a->client_writer)(&a->archive, a->client_data, + state->compressed, block_length); + + /* TODO: Handle short write of final block. */ + if (bytes_written <= 0) + ret = ARCHIVE_FATAL; + else { + a->archive.raw_position += ret; + ret = ARCHIVE_OK; + } + + /* Cleanup: shut down compressor, release memory, etc. */ +cleanup: + switch (BZ2_bzCompressEnd(&(state->stream))) { + case BZ_OK: + break; + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Failed to clean up compressor"); + ret = ARCHIVE_FATAL; + } + + free(state->compressed); + free(state); + return (ret); +} + +/* + * Utility function to push input data through compressor, writing + * full output blocks as necessary. + * + * Note that this handles both the regular write case (finishing == + * false) and the end-of-archive case (finishing == true). + */ +static int +drive_compressor(struct archive_write *a, struct private_data *state, int finishing) +{ + ssize_t bytes_written; + int ret; + + for (;;) { + if (state->stream.avail_out == 0) { + bytes_written = (a->client_writer)(&a->archive, + a->client_data, state->compressed, + state->compressed_buffer_size); + if (bytes_written <= 0) { + /* TODO: Handle this write failure */ + return (ARCHIVE_FATAL); + } else if ((size_t)bytes_written < state->compressed_buffer_size) { + /* Short write: Move remainder to + * front and keep filling */ + memmove(state->compressed, + state->compressed + bytes_written, + state->compressed_buffer_size - bytes_written); + } + + a->archive.raw_position += bytes_written; + state->stream.next_out = state->compressed + + state->compressed_buffer_size - bytes_written; + state->stream.avail_out = bytes_written; + } + + ret = BZ2_bzCompress(&(state->stream), + finishing ? BZ_FINISH : BZ_RUN); + + switch (ret) { + case BZ_RUN_OK: + /* In non-finishing case, did compressor + * consume everything? */ + if (!finishing && state->stream.avail_in == 0) + return (ARCHIVE_OK); + break; + case BZ_FINISH_OK: /* Finishing: There's more work to do */ + break; + case BZ_STREAM_END: /* Finishing: all done */ + /* Only occurs in finishing case */ + return (ARCHIVE_OK); + default: + /* Any other return value indicates an error */ + archive_set_error(&a->archive, + ARCHIVE_ERRNO_PROGRAMMER, + "Bzip2 compression failed"); + return (ARCHIVE_FATAL); + } + } +} + +#endif /* HAVE_BZLIB_H */ diff --git a/contrib/libarchive-2.1/libarchive/archive_write_set_compression_gzip.c b/contrib/libarchive-2.1/libarchive/archive_write_set_compression_gzip.c new file mode 100644 index 0000000000..6e74048052 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_write_set_compression_gzip.c @@ -0,0 +1,405 @@ +/*- + * 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 "archive_platform.h" + +/* Don't compile this if we don't have zlib. */ +#if HAVE_ZLIB_H + +__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_gzip.c,v 1.13 2007/03/03 07:37:36 kientzle Exp $"); + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#include +#ifdef HAVE_ZLIB_H +#include +#endif + +#include "archive.h" +#include "archive_private.h" +#include "archive_write_private.h" + +struct private_data { + z_stream stream; + int64_t total_in; + unsigned char *compressed; + size_t compressed_buffer_size; + unsigned long crc; +}; + + +/* + * Yuck. zlib.h is not const-correct, so I need this one bit + * of ugly hackery to convert a const * pointer to a non-const pointer. + */ +#define SET_NEXT_IN(st,src) \ + (st)->stream.next_in = (Bytef *)(uintptr_t)(const void *)(src) + +static int archive_compressor_gzip_finish(struct archive_write *); +static int archive_compressor_gzip_init(struct archive_write *); +static int archive_compressor_gzip_write(struct archive_write *, + const void *, size_t); +static int drive_compressor(struct archive_write *, struct private_data *, + int finishing); + + +/* + * Allocate, initialize and return a archive object. + */ +int +archive_write_set_compression_gzip(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_set_compression_gzip"); + a->compressor.init = &archive_compressor_gzip_init; + a->archive.compression_code = ARCHIVE_COMPRESSION_GZIP; + a->archive.compression_name = "gzip"; + return (ARCHIVE_OK); +} + +/* + * Setup callback. + */ +static int +archive_compressor_gzip_init(struct archive_write *a) +{ + int ret; + struct private_data *state; + time_t t; + + a->archive.compression_code = ARCHIVE_COMPRESSION_GZIP; + a->archive.compression_name = "gzip"; + + if (a->client_opener != NULL) { + ret = (a->client_opener)(&a->archive, a->client_data); + if (ret != ARCHIVE_OK) + return (ret); + } + + state = (struct private_data *)malloc(sizeof(*state)); + if (state == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate data for compression"); + return (ARCHIVE_FATAL); + } + memset(state, 0, sizeof(*state)); + + state->compressed_buffer_size = a->bytes_per_block; + state->compressed = (unsigned char *)malloc(state->compressed_buffer_size); + state->crc = crc32(0L, NULL, 0); + + if (state->compressed == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate data for compression buffer"); + free(state); + return (ARCHIVE_FATAL); + } + + state->stream.next_out = state->compressed; + state->stream.avail_out = state->compressed_buffer_size; + + /* Prime output buffer with a gzip header. */ + t = time(NULL); + state->compressed[0] = 0x1f; /* GZip signature bytes */ + state->compressed[1] = 0x8b; + state->compressed[2] = 0x08; /* "Deflate" compression */ + state->compressed[3] = 0; /* No options */ + state->compressed[4] = (t)&0xff; /* Timestamp */ + state->compressed[5] = (t>>8)&0xff; + state->compressed[6] = (t>>16)&0xff; + state->compressed[7] = (t>>24)&0xff; + state->compressed[8] = 0; /* No deflate options */ + state->compressed[9] = 3; /* OS=Unix */ + state->stream.next_out += 10; + state->stream.avail_out -= 10; + + a->compressor.write = archive_compressor_gzip_write; + a->compressor.finish = archive_compressor_gzip_finish; + + /* Initialize compression library. */ + ret = deflateInit2(&(state->stream), + Z_DEFAULT_COMPRESSION, + Z_DEFLATED, + -15 /* < 0 to suppress zlib header */, + 8, + Z_DEFAULT_STRATEGY); + + if (ret == Z_OK) { + a->compressor.data = state; + return (0); + } + + /* Library setup failed: clean up. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal error " + "initializing compression library"); + free(state->compressed); + free(state); + + /* Override the error message if we know what really went wrong. */ + switch (ret) { + case Z_STREAM_ERROR: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing " + "compression library: invalid setup parameter"); + break; + case Z_MEM_ERROR: + archive_set_error(&a->archive, ENOMEM, "Internal error initializing " + "compression library"); + break; + case Z_VERSION_ERROR: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing " + "compression library: invalid library version"); + break; + } + + return (ARCHIVE_FATAL); +} + +/* + * Write data to the compressed stream. + */ +static int +archive_compressor_gzip_write(struct archive_write *a, const void *buff, + size_t length) +{ + struct private_data *state; + int ret; + + state = (struct private_data *)a->compressor.data; + if (a->client_writer == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "No write callback is registered? " + "This is probably an internal programming error."); + return (ARCHIVE_FATAL); + } + + /* Update statistics */ + state->crc = crc32(state->crc, (const Bytef *)buff, length); + state->total_in += length; + + /* Compress input data to output buffer */ + SET_NEXT_IN(state, buff); + state->stream.avail_in = length; + if ((ret = drive_compressor(a, state, 0)) != ARCHIVE_OK) + return (ret); + + a->archive.file_position += length; + return (ARCHIVE_OK); +} + + +/* + * Finish the compression... + */ +static int +archive_compressor_gzip_finish(struct archive_write *a) +{ + ssize_t block_length, target_block_length, bytes_written; + int ret; + struct private_data *state; + unsigned tocopy; + unsigned char trailer[8]; + + state = (struct private_data *)a->compressor.data; + ret = 0; + if (a->client_writer == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "No write callback is registered? " + "This is probably an internal programming error."); + ret = ARCHIVE_FATAL; + goto cleanup; + } + + /* By default, always pad the uncompressed data. */ + if (a->pad_uncompressed) { + tocopy = a->bytes_per_block - + (state->total_in % a->bytes_per_block); + while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) { + SET_NEXT_IN(state, a->nulls); + state->stream.avail_in = tocopy < a->null_length ? + tocopy : a->null_length; + state->crc = crc32(state->crc, a->nulls, + state->stream.avail_in); + state->total_in += state->stream.avail_in; + tocopy -= state->stream.avail_in; + ret = drive_compressor(a, state, 0); + if (ret != ARCHIVE_OK) + goto cleanup; + } + } + + /* Finish compression cycle */ + if (((ret = drive_compressor(a, state, 1))) != ARCHIVE_OK) + goto cleanup; + + /* Build trailer: 4-byte CRC and 4-byte length. */ + trailer[0] = (state->crc)&0xff; + trailer[1] = (state->crc >> 8)&0xff; + trailer[2] = (state->crc >> 16)&0xff; + trailer[3] = (state->crc >> 24)&0xff; + trailer[4] = (state->total_in)&0xff; + trailer[5] = (state->total_in >> 8)&0xff; + trailer[6] = (state->total_in >> 16)&0xff; + trailer[7] = (state->total_in >> 24)&0xff; + + /* Add trailer to current block. */ + tocopy = 8; + if (tocopy > state->stream.avail_out) + tocopy = state->stream.avail_out; + memcpy(state->stream.next_out, trailer, tocopy); + state->stream.next_out += tocopy; + state->stream.avail_out -= tocopy; + + /* If it overflowed, flush and start a new block. */ + if (tocopy < 8) { + bytes_written = (a->client_writer)(&a->archive, a->client_data, + state->compressed, state->compressed_buffer_size); + if (bytes_written <= 0) { + ret = ARCHIVE_FATAL; + goto cleanup; + } + a->archive.raw_position += bytes_written; + state->stream.next_out = state->compressed; + state->stream.avail_out = state->compressed_buffer_size; + memcpy(state->stream.next_out, trailer + tocopy, 8-tocopy); + state->stream.next_out += 8-tocopy; + state->stream.avail_out -= 8-tocopy; + } + + /* Optionally, pad the final compressed block. */ + block_length = state->stream.next_out - state->compressed; + + + /* Tricky calculation to determine size of last block. */ + target_block_length = block_length; + if (a->bytes_in_last_block <= 0) + /* Default or Zero: pad to full block */ + target_block_length = a->bytes_per_block; + else + /* Round length to next multiple of bytes_in_last_block. */ + target_block_length = a->bytes_in_last_block * + ( (block_length + a->bytes_in_last_block - 1) / + a->bytes_in_last_block); + if (target_block_length > a->bytes_per_block) + target_block_length = a->bytes_per_block; + if (block_length < target_block_length) { + memset(state->stream.next_out, 0, + target_block_length - block_length); + block_length = target_block_length; + } + + /* Write the last block */ + bytes_written = (a->client_writer)(&a->archive, a->client_data, + state->compressed, block_length); + if (bytes_written <= 0) { + ret = ARCHIVE_FATAL; + goto cleanup; + } + a->archive.raw_position += bytes_written; + + /* Cleanup: shut down compressor, release memory, etc. */ +cleanup: + switch (deflateEnd(&(state->stream))) { + case Z_OK: + break; + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Failed to clean up compressor"); + ret = ARCHIVE_FATAL; + } + free(state->compressed); + free(state); + return (ret); +} + +/* + * Utility function to push input data through compressor, + * writing full output blocks as necessary. + * + * Note that this handles both the regular write case (finishing == + * false) and the end-of-archive case (finishing == true). + */ +static int +drive_compressor(struct archive_write *a, struct private_data *state, int finishing) +{ + ssize_t bytes_written; + int ret; + + for (;;) { + if (state->stream.avail_out == 0) { + bytes_written = (a->client_writer)(&a->archive, + a->client_data, state->compressed, + state->compressed_buffer_size); + if (bytes_written <= 0) { + /* TODO: Handle this write failure */ + return (ARCHIVE_FATAL); + } else if ((size_t)bytes_written < state->compressed_buffer_size) { + /* Short write: Move remaining to + * front of block and keep filling */ + memmove(state->compressed, + state->compressed + bytes_written, + state->compressed_buffer_size - bytes_written); + } + a->archive.raw_position += bytes_written; + state->stream.next_out + = state->compressed + + state->compressed_buffer_size - bytes_written; + state->stream.avail_out = bytes_written; + } + + ret = deflate(&(state->stream), + finishing ? Z_FINISH : Z_NO_FLUSH ); + + switch (ret) { + case Z_OK: + /* In non-finishing case, check if compressor + * consumed everything */ + if (!finishing && state->stream.avail_in == 0) + return (ARCHIVE_OK); + /* In finishing case, this return always means + * there's more work */ + break; + case Z_STREAM_END: + /* This return can only occur in finishing case. */ + return (ARCHIVE_OK); + default: + /* Any other return value indicates an error. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "GZip compression failed"); + return (ARCHIVE_FATAL); + } + } +} + +#endif /* HAVE_ZLIB_H */ diff --git a/contrib/libarchive-2.1/libarchive/archive_write_set_compression_none.c b/contrib/libarchive-2.1/libarchive/archive_write_set_compression_none.c new file mode 100644 index 0000000000..7b254a85d4 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_write_set_compression_none.c @@ -0,0 +1,242 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_none.c,v 1.14 2007/04/15 04:42:52 kientzle Exp $"); + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "archive.h" +#include "archive_private.h" +#include "archive_write_private.h" + +static int archive_compressor_none_finish(struct archive_write *a); +static int archive_compressor_none_init(struct archive_write *); +static int archive_compressor_none_write(struct archive_write *, + const void *, size_t); + +struct archive_none { + char *buffer; + ssize_t buffer_size; + char *next; /* Current insert location */ + ssize_t avail; /* Free space left in buffer */ +}; + +int +archive_write_set_compression_none(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_set_compression_none"); + a->compressor.init = &archive_compressor_none_init; + return (0); +} + +/* + * Setup callback. + */ +static int +archive_compressor_none_init(struct archive_write *a) +{ + int ret; + struct archive_none *state; + + a->archive.compression_code = ARCHIVE_COMPRESSION_NONE; + a->archive.compression_name = "none"; + + if (a->client_opener != NULL) { + ret = (a->client_opener)(&a->archive, a->client_data); + if (ret != 0) + return (ret); + } + + state = (struct archive_none *)malloc(sizeof(*state)); + if (state == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate data for output buffering"); + return (ARCHIVE_FATAL); + } + memset(state, 0, sizeof(*state)); + + state->buffer_size = a->bytes_per_block; + if (state->buffer_size != 0) { + state->buffer = (char *)malloc(state->buffer_size); + if (state->buffer == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate output buffer"); + free(state); + return (ARCHIVE_FATAL); + } + } + + state->next = state->buffer; + state->avail = state->buffer_size; + + a->compressor.data = state; + a->compressor.write = archive_compressor_none_write; + a->compressor.finish = archive_compressor_none_finish; + return (ARCHIVE_OK); +} + +/* + * Write data to the stream. + */ +static int +archive_compressor_none_write(struct archive_write *a, const void *vbuff, + size_t length) +{ + const char *buff; + ssize_t remaining, to_copy; + ssize_t bytes_written; + struct archive_none *state; + + state = (struct archive_none *)a->compressor.data; + buff = (const char *)vbuff; + if (a->client_writer == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "No write callback is registered? " + "This is probably an internal programming error."); + return (ARCHIVE_FATAL); + } + + remaining = length; + + /* + * If there is no buffer for blocking, just pass the data + * straight through to the client write callback. In + * particular, this supports "no write delay" operation for + * special applications. Just set the block size to zero. + */ + if (state->buffer_size == 0) { + while (remaining > 0) { + bytes_written = (a->client_writer)(&a->archive, + a->client_data, buff, remaining); + if (bytes_written <= 0) + return (ARCHIVE_FATAL); + a->archive.raw_position += bytes_written; + remaining -= bytes_written; + buff += bytes_written; + } + a->archive.file_position += length; + return (ARCHIVE_OK); + } + + while ((remaining > 0) || (state->avail == 0)) { + /* + * If we have a full output block, write it and reset the + * output buffer. + */ + if (state->avail == 0) { + bytes_written = (a->client_writer)(&a->archive, + a->client_data, state->buffer, state->buffer_size); + if (bytes_written <= 0) + return (ARCHIVE_FATAL); + /* XXX TODO: if bytes_written < state->buffer_size */ + a->archive.raw_position += bytes_written; + state->next = state->buffer; + state->avail = state->buffer_size; + } + + /* Now we have space in the buffer; copy new data into it. */ + to_copy = (remaining > state->avail) ? + state->avail : remaining; + memcpy(state->next, buff, to_copy); + state->next += to_copy; + state->avail -= to_copy; + buff += to_copy; + remaining -= to_copy; + } + a->archive.file_position += length; + return (ARCHIVE_OK); +} + + +/* + * Finish the compression. + */ +static int +archive_compressor_none_finish(struct archive_write *a) +{ + ssize_t block_length; + ssize_t target_block_length; + ssize_t bytes_written; + int ret; + int ret2; + struct archive_none *state; + + state = (struct archive_none *)a->compressor.data; + ret = ret2 = ARCHIVE_OK; + if (a->client_writer == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "No write callback is registered? " + "This is probably an internal programming error."); + return (ARCHIVE_FATAL); + } + + /* If there's pending data, pad and write the last block */ + if (state->next != state->buffer) { + block_length = state->buffer_size - state->avail; + + /* Tricky calculation to determine size of last block */ + target_block_length = block_length; + if (a->bytes_in_last_block <= 0) + /* Default or Zero: pad to full block */ + target_block_length = a->bytes_per_block; + else + /* Round to next multiple of bytes_in_last_block. */ + target_block_length = a->bytes_in_last_block * + ( (block_length + a->bytes_in_last_block - 1) / + a->bytes_in_last_block); + if (target_block_length > a->bytes_per_block) + target_block_length = a->bytes_per_block; + if (block_length < target_block_length) { + memset(state->next, 0, + target_block_length - block_length); + block_length = target_block_length; + } + bytes_written = (a->client_writer)(&a->archive, + a->client_data, state->buffer, block_length); + if (bytes_written <= 0) + ret = ARCHIVE_FATAL; + else { + a->archive.raw_position += bytes_written; + ret = ARCHIVE_OK; + } + } + if (state->buffer) + free(state->buffer); + free(state); + a->compressor.data = NULL; + + return (ret); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_write_set_compression_program.c b/contrib/libarchive-2.1/libarchive/archive_write_set_compression_program.c new file mode 100644 index 0000000000..4f28a0feff --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_write_set_compression_program.c @@ -0,0 +1,322 @@ +/*- + * Copyright (c) 2007 Joerg Sonnenberger + * 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 "archive_platform.h" + +__FBSDID("$FreeBSD$"); + +#ifdef HAVE_SYS_WAIT_H +# include +#endif +#ifdef HAVE_ERRNO_H +# include +#endif +#ifdef HAVE_FCNTL_H +# include +#endif +#ifdef HAVE_STDLIB_H +# include +#endif +#ifdef HAVE_STRING_H +# include +#endif + +#include "archive.h" +#include "archive_private.h" +#include "archive_write_private.h" + +#include "filter_fork.h" + +struct private_data { + char *description; + pid_t child; + int child_stdin, child_stdout; + + char *child_buf; + size_t child_buf_len, child_buf_avail; +}; + +static int archive_compressor_program_finish(struct archive_write *); +static int archive_compressor_program_init(struct archive_write *); +static int archive_compressor_program_write(struct archive_write *, + const void *, size_t); + +/* + * Allocate, initialize and return a archive object. + */ +int +archive_write_set_compression_program(struct archive *_a, const char *cmd) +{ + struct archive_write *a = (struct archive_write *)_a; + __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_set_compression_program"); + a->compressor.init = &archive_compressor_program_init; + a->compressor.config = strdup(cmd); + return (ARCHIVE_OK); +} + +/* + * Setup callback. + */ +static int +archive_compressor_program_init(struct archive_write *a) +{ + int ret; + struct private_data *state; + static const char *prefix = "Program: "; + char *cmd = a->compressor.config; + + if (a->client_opener != NULL) { + ret = (a->client_opener)(&a->archive, a->client_data); + if (ret != ARCHIVE_OK) + return (ret); + } + + state = (struct private_data *)malloc(sizeof(*state)); + if (state == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate data for compression"); + return (ARCHIVE_FATAL); + } + memset(state, 0, sizeof(*state)); + + a->archive.compression_code = ARCHIVE_COMPRESSION_PROGRAM; + state->description = (char *)malloc(strlen(prefix) + strlen(cmd) + 1); + strcpy(state->description, prefix); + strcat(state->description, cmd); + a->archive.compression_name = state->description; + + state->child_buf_len = a->bytes_per_block; + state->child_buf_avail = 0; + state->child_buf = malloc(state->child_buf_len); + + if (state->child_buf == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate data for compression buffer"); + free(state); + return (ARCHIVE_FATAL); + } + + if ((state->child = __archive_create_child(cmd, + &state->child_stdin, &state->child_stdout)) == -1) { + archive_set_error(&a->archive, EINVAL, + "Can't initialise filter"); + free(state->child_buf); + free(state); + return (ARCHIVE_FATAL); + } + + a->compressor.write = archive_compressor_program_write; + a->compressor.finish = archive_compressor_program_finish; + + a->compressor.data = state; + return (0); +} + +static ssize_t +child_write(struct archive_write *a, const char *buf, size_t buf_len) +{ + struct private_data *state = a->compressor.data; + ssize_t ret; + + if (state->child_stdin == -1) + return (-1); + + if (buf_len == 0) + return (-1); + +restart_write: + do { + ret = write(state->child_stdin, buf, buf_len); + } while (ret == -1 && errno == EINTR); + + if (ret > 0) + return (ret); + if (ret == 0) { + close(state->child_stdin); + state->child_stdin = -1; + fcntl(state->child_stdout, F_SETFL, 0); + return (0); + } + if (ret == -1 && errno != EAGAIN) + return (-1); + + do { + ret = read(state->child_stdout, + state->child_buf + state->child_buf_avail, + state->child_buf_len - state->child_buf_avail); + } while (ret == -1 && errno == EINTR); + + if (ret == 0 || (ret == -1 && errno == EPIPE)) { + close(state->child_stdout); + state->child_stdout = -1; + fcntl(state->child_stdin, F_SETFL, 0); + goto restart_write; + } + if (ret == -1 && errno == EAGAIN) { + __archive_check_child(state->child_stdin, state->child_stdout); + goto restart_write; + } + if (ret == -1) + return (-1); + + state->child_buf_avail += ret; + + ret = (a->client_writer)(&a->archive, a->client_data, + state->child_buf, state->child_buf_avail); + if (ret <= 0) + return (-1); + + if ((size_t)ret < state->child_buf_avail) { + memmove(state->child_buf, state->child_buf + ret, + state->child_buf_avail - ret); + } + state->child_buf_avail -= ret; + a->archive.raw_position += ret; + goto restart_write; +} + +/* + * Write data to the compressed stream. + */ +static int +archive_compressor_program_write(struct archive_write *a, const void *buff, + size_t length) +{ + struct private_data *state; + ssize_t ret; + const char *buf; + + state = (struct private_data *)a->compressor.data; + if (a->client_writer == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "No write callback is registered? " + "This is probably an internal programming error."); + return (ARCHIVE_FATAL); + } + + buf = buff; + while (length > 0) { + ret = child_write(a, buf, length); + if (ret == -1 || ret == 0) { + archive_set_error(&a->archive, EIO, + "Can't write to filter"); + return (ARCHIVE_FATAL); + } + length -= ret; + buf += ret; + } + + a->archive.file_position += length; + return (ARCHIVE_OK); +} + + +/* + * Finish the compression... + */ +static int +archive_compressor_program_finish(struct archive_write *a) +{ + int ret, status; + ssize_t bytes_read, bytes_written; + struct private_data *state; + + state = (struct private_data *)a->compressor.data; + ret = 0; + if (a->client_writer == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "No write callback is registered? " + "This is probably an internal programming error."); + ret = ARCHIVE_FATAL; + goto cleanup; + } + + /* XXX pad compressed data. */ + + close(state->child_stdin); + state->child_stdin = -1; + fcntl(state->child_stdout, F_SETFL, 0); + + for (;;) { + do { + bytes_read = read(state->child_stdout, + state->child_buf + state->child_buf_avail, + state->child_buf_len - state->child_buf_avail); + } while (bytes_read == -1 && errno == EINTR); + + if (bytes_read == 0 || (bytes_read == -1 && errno == EPIPE)) + break; + + if (bytes_read == -1) { + archive_set_error(&a->archive, errno, + "Read from filter failed unexpectedly."); + ret = ARCHIVE_FATAL; + goto cleanup; + } + state->child_buf_avail += bytes_read; + + bytes_written = (a->client_writer)(&a->archive, a->client_data, + state->child_buf, state->child_buf_avail); + if (bytes_written <= 0) { + ret = ARCHIVE_FATAL; + goto cleanup; + } + if ((size_t)bytes_written < state->child_buf_avail) { + memmove(state->child_buf, + state->child_buf + bytes_written, + state->child_buf_avail - bytes_written); + } + state->child_buf_avail -= bytes_written; + a->archive.raw_position += bytes_written; + } + + /* XXX pad final compressed block. */ + +cleanup: + /* Shut down the child. */ + if (state->child_stdin != -1) + close(state->child_stdin); + if (state->child_stdout != -1) + close(state->child_stdout); + while (waitpid(state->child, &status, 0) == -1 && errno == EINTR) + continue; + + if (status != 0) { + archive_set_error(&a->archive, EIO, + "Filter exited with failure."); + ret = ARCHIVE_FATAL; + } + + /* Release our configuration data. */ + free(a->compressor.config); + a->compressor.config = NULL; + + /* Release our private state data. */ + free(state->child_buf); + free(state->description); + free(state); + return (ret); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_write_set_format.c b/contrib/libarchive-2.1/libarchive/archive_write_set_format.c new file mode 100644 index 0000000000..b04bef7fba --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_write_set_format.c @@ -0,0 +1,69 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format.c,v 1.4 2007/01/09 08:05:56 kientzle Exp $"); + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "archive.h" +#include "archive_private.h" + +/* A table that maps format codes to functions. */ +static +struct { int code; int (*setter)(struct archive *); } codes[] = +{ + { ARCHIVE_FORMAT_CPIO, archive_write_set_format_cpio }, + { ARCHIVE_FORMAT_CPIO_POSIX, archive_write_set_format_cpio }, + { ARCHIVE_FORMAT_SHAR, archive_write_set_format_shar }, + { ARCHIVE_FORMAT_SHAR_BASE, archive_write_set_format_shar }, + { ARCHIVE_FORMAT_SHAR_DUMP, archive_write_set_format_shar_dump }, + { ARCHIVE_FORMAT_TAR, archive_write_set_format_pax_restricted }, + { ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE, archive_write_set_format_pax }, + { ARCHIVE_FORMAT_TAR_PAX_RESTRICTED, + archive_write_set_format_pax_restricted }, + { ARCHIVE_FORMAT_TAR_USTAR, archive_write_set_format_ustar }, + { 0, NULL } +}; + +int +archive_write_set_format(struct archive *a, int code) +{ + int i; + + for (i = 0; codes[i].code != 0; i++) { + if (code == codes[i].code) + return ((codes[i].setter)(a)); + } + + archive_set_error(a, EINVAL, "No such format"); + return (ARCHIVE_FATAL); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_write_set_format_ar.c b/contrib/libarchive-2.1/libarchive/archive_write_set_format_ar.c new file mode 100644 index 0000000000..52af5f9a63 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_write_set_format_ar.c @@ -0,0 +1,528 @@ +/*- + * Copyright (c) 2007 Kai Wang + * Copyright (c) 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 + * in this position and unchanged. + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_ar.c,v 1.2 2007/04/14 22:34:10 kientzle Exp $"); + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "archive.h" +#include "archive_entry.h" +#include "archive_private.h" +#include "archive_write_private.h" + +struct ar_w { + uint64_t entry_bytes_remaining; + uint64_t entry_padding; + int is_strtab; + int has_strtab; + char *strtab; +}; + +/* + * Define structure of the "ar" header. + */ +#define AR_name_offset 0 +#define AR_name_size 16 +#define AR_date_offset 16 +#define AR_date_size 12 +#define AR_uid_offset 28 +#define AR_uid_size 6 +#define AR_gid_offset 34 +#define AR_gid_size 6 +#define AR_mode_offset 40 +#define AR_mode_size 8 +#define AR_size_offset 48 +#define AR_size_size 10 +#define AR_fmag_offset 58 +#define AR_fmag_size 2 + +static int archive_write_set_format_ar(struct archive_write *); +static int archive_write_ar_header(struct archive_write *, + struct archive_entry *); +static ssize_t archive_write_ar_data(struct archive_write *, + const void *buff, size_t s); +static int archive_write_ar_destroy(struct archive_write *); +static int archive_write_ar_finish_entry(struct archive_write *); +static const char *basename(const char *path); +static int format_octal(int64_t v, char *p, int s); +static int format_decimal(int64_t v, char *p, int s); + +int +archive_write_set_format_ar_bsd(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + int r = archive_write_set_format_ar(a); + if (r == ARCHIVE_OK) { + a->archive_format = ARCHIVE_FORMAT_AR_BSD; + a->archive_format_name = "ar (BSD)"; + } + return (r); +} + +int +archive_write_set_format_ar_svr4(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + int r = archive_write_set_format_ar(a); + if (r == ARCHIVE_OK) { + a->archive_format = ARCHIVE_FORMAT_AR_GNU; + a->archive_format_name = "ar (GNU/SVR4)"; + } + return (r); +} + +/* + * Generic initialization. + */ +static int +archive_write_set_format_ar(struct archive_write *a) +{ + struct ar_w *ar; + + /* If someone else was already registered, unregister them. */ + if (a->format_destroy != NULL) + (a->format_destroy)(a); + + ar = (struct ar_w *)malloc(sizeof(*ar)); + if (ar == NULL) { + archive_set_error(&a->archive, ENOMEM, "Can't allocate ar data"); + return (ARCHIVE_FATAL); + } + memset(ar, 0, sizeof(*ar)); + a->format_data = ar; + + a->format_write_header = archive_write_ar_header; + a->format_write_data = archive_write_ar_data; + a->format_finish = NULL; + a->format_destroy = archive_write_ar_destroy; + a->format_finish_entry = archive_write_ar_finish_entry; + return (ARCHIVE_OK); +} + +static int +archive_write_ar_header(struct archive_write *a, struct archive_entry *entry) +{ + int ret, append_fn; + char buff[60]; + char *ss, *se; + struct ar_w *ar; + const char *pathname; + const char *filename; + + ret = 0; + append_fn = 0; + ar = (struct ar_w *)a->format_data; + ar->is_strtab = 0; + filename = NULL; + + /* + * Reject files with empty name. + */ + pathname = archive_entry_pathname(entry); + if (*pathname == '\0') { + archive_set_error(&a->archive, EINVAL, + "Invalid filename"); + return (ARCHIVE_WARN); + } + + /* + * If we are now at the beginning of the archive, + * we need first write the ar global header. + */ + if (a->archive.file_position == 0) + (a->compressor.write)(a, "!\n", 8); + + memset(buff, ' ', 60); + strncpy(&buff[AR_fmag_offset], "`\n", 2); + + if (strcmp(pathname, "/") == 0 ) { + /* Entry is archive symbol table in GNU format */ + buff[AR_name_offset] = '/'; + goto stat; + } + if (strcmp(pathname, "__.SYMDEF") == 0) { + /* Entry is archive symbol table in BSD format */ + strncpy(buff + AR_name_offset, "__.SYMDEF", 9); + goto stat; + } + if (strcmp(pathname, "//") == 0) { + /* + * Entry is archive filename table, inform that we should + * collect strtab in next _data call. + */ + ar->is_strtab = 1; + buff[AR_name_offset] = buff[AR_name_offset + 1] = '/'; + /* + * For archive string table, only ar_size filed should + * be set. + */ + goto size; + } + + /* + * Otherwise, entry is a normal archive member. + * Strip leading paths from filenames, if any. + */ + if ((filename = basename(pathname)) == NULL) { + /* Reject filenames with trailing "/" */ + archive_set_error(&a->archive, EINVAL, + "Invalid filename"); + return (ARCHIVE_WARN); + } + + if (a->archive_format == ARCHIVE_FORMAT_AR_GNU) { + /* + * SVR4/GNU variant use a "/" to mark then end of the filename, + * make it possible to have embedded spaces in the filename. + * So, the longest filename here (without extension) is + * actually 15 bytes. + */ + if (strlen(filename) <= 15) { + strncpy(&buff[AR_name_offset], + filename, strlen(filename)); + buff[AR_name_offset + strlen(filename)] = '/'; + } else { + /* + * For filename longer than 15 bytes, GNU variant + * makes use of a string table and instead stores the + * offset of the real filename to in the ar_name field. + * The string table should have been written before. + */ + if (ar->has_strtab <= 0) { + archive_set_error(&a->archive, EINVAL, + "Can't find string table"); + return (ARCHIVE_WARN); + } + + se = (char *)malloc(strlen(filename) + 3); + if (se == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate filename buffer"); + return (ARCHIVE_FATAL); + } + + strncpy(se, filename, strlen(filename)); + strcpy(se + strlen(filename), "/\n"); + + ss = strstr(ar->strtab, se); + free(se); + + if (ss == NULL) { + archive_set_error(&a->archive, EINVAL, + "Invalid string table"); + return (ARCHIVE_WARN); + } + + /* + * GNU variant puts "/" followed by digits into + * ar_name field. These digits indicates the real + * filename string's offset to the string table. + */ + buff[AR_name_offset] = '/'; + if (format_decimal(ss - ar->strtab, + buff + AR_name_offset + 1, + AR_name_size - 1)) { + archive_set_error(&a->archive, ERANGE, + "string table offset too large"); + return (ARCHIVE_WARN); + } + } + } else if (a->archive_format == ARCHIVE_FORMAT_AR_BSD) { + /* + * BSD variant: for any file name which is more than + * 16 chars or contains one or more embedded space(s), the + * string "#1/" followed by the ASCII length of the name is + * put into the ar_name field. The file size (stored in the + * ar_size field) is incremented by the length of the name. + * The name is then written immediately following the + * archive header. + */ + if (strlen(filename) <= 16 && strchr(filename, ' ') == NULL) { + strncpy(&buff[AR_name_offset], filename, strlen(filename)); + buff[AR_name_offset + strlen(filename)] = ' '; + } + else { + strncpy(buff + AR_name_offset, "#1/", 3); + if (format_decimal(strlen(filename), + buff + AR_name_offset + 3, + AR_name_size - 3)) { + archive_set_error(&a->archive, ERANGE, + "File name too long"); + return (ARCHIVE_WARN); + } + append_fn = 1; + archive_entry_set_size(entry, + archive_entry_size(entry) + strlen(filename)); + } + } + +stat: + if (format_decimal(archive_entry_mtime(entry), buff + AR_date_offset, AR_date_size)) { + archive_set_error(&a->archive, ERANGE, + "File modification time too large"); + return (ARCHIVE_WARN); + } + if (format_decimal(archive_entry_uid(entry), buff + AR_uid_offset, AR_uid_size)) { + archive_set_error(&a->archive, ERANGE, + "Numeric user ID too large"); + return (ARCHIVE_WARN); + } + if (format_decimal(archive_entry_gid(entry), buff + AR_gid_offset, AR_gid_size)) { + archive_set_error(&a->archive, ERANGE, + "Numeric group ID too large"); + return (ARCHIVE_WARN); + } + if (format_octal(archive_entry_mode(entry), buff + AR_mode_offset, AR_mode_size)) { + archive_set_error(&a->archive, ERANGE, + "Numeric mode too large"); + return (ARCHIVE_WARN); + } + /* + * Sanity Check: A non-pseudo archive member should always be + * a regular file. + */ + if (filename != NULL && archive_entry_filetype(entry) != AE_IFREG) { + archive_set_error(&a->archive, EINVAL, + "Regular file required for non-pseudo member"); + return (ARCHIVE_WARN); + } + +size: + if (format_decimal(archive_entry_size(entry), buff + AR_size_offset, + AR_size_size)) { + archive_set_error(&a->archive, ERANGE, + "File size out of range"); + return (ARCHIVE_WARN); + } + + ret = (a->compressor.write)(a, buff, 60); + if (ret != ARCHIVE_OK) + return (ret); + + ar->entry_bytes_remaining = archive_entry_size(entry); + ar->entry_padding = ar->entry_bytes_remaining % 2; + + if (append_fn > 0) { + ret = (a->compressor.write)(a, filename, strlen(filename)); + if (ret != ARCHIVE_OK) + return (ret); + ar->entry_bytes_remaining -= strlen(filename); + } + + return (ARCHIVE_OK); +} + +static ssize_t +archive_write_ar_data(struct archive_write *a, const void *buff, size_t s) +{ + struct ar_w *ar; + int ret; + + ar = (struct ar_w *)a->format_data; + if (s > ar->entry_bytes_remaining) + s = ar->entry_bytes_remaining; + + if (ar->is_strtab > 0) { + if (ar->has_strtab > 0) { + archive_set_error(&a->archive, EINVAL, + "More than one string tables exist"); + return (ARCHIVE_WARN); + } + + ar->strtab = (char *)malloc(s); + if (ar->strtab == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate strtab buffer"); + return (ARCHIVE_FATAL); + } + strncpy(ar->strtab, buff, s); + ar->has_strtab = 1; + } + + ret = (a->compressor.write)(a, buff, s); + if (ret != ARCHIVE_OK) + return (ret); + + ar->entry_bytes_remaining -= s; + return (s); +} + +static int +archive_write_ar_destroy(struct archive_write *a) +{ + struct ar_w *ar; + + ar = (struct ar_w *)a->format_data; + + if (ar->has_strtab > 0) { + free(ar->strtab); + ar->strtab = NULL; + } + + free(ar); + a->format_data = NULL; + return (ARCHIVE_OK); +} + +static int +archive_write_ar_finish_entry(struct archive_write *a) +{ + struct ar_w *ar; + int ret; + + ar = (struct ar_w *)a->format_data; + + if (ar->entry_bytes_remaining != 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Entry remaining bytes larger than 0"); + return (ARCHIVE_WARN); + } + + if (ar->entry_padding == 0) { + return (ARCHIVE_OK); + } + + if (ar->entry_padding != 1) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Padding wrong size: %d should be 1 or 0", + ar->entry_padding); + return (ARCHIVE_WARN); + } + + ret = (a->compressor.write)(a, "\n", 1); + return (ret); +} + +/* + * Format a number into the specified field using base-8. + * NB: This version is slightly different from the one in + * _ustar.c + */ +static int +format_octal(int64_t v, char *p, int s) +{ + int len; + char *h; + + len = s; + h = p; + + /* Octal values can't be negative, so use 0. */ + if (v < 0) { + while (len-- > 0) + *p++ = '0'; + return (-1); + } + + p += s; /* Start at the end and work backwards. */ + do { + *--p = (char)('0' + (v & 7)); + v >>= 3; + } while (--s > 0 && v > 0); + + if (v == 0) { + memmove(h, p, len - s); + p = h + len - s; + while (s-- > 0) + *p++ = ' '; + return (0); + } + /* If it overflowed, fill field with max value. */ + while (len-- > 0) + *p++ = '7'; + + return (-1); +} + +/* + * Format a number into the specified field using base-10. + */ +static int +format_decimal(int64_t v, char *p, int s) +{ + int len; + char *h; + + len = s; + h = p; + + /* Negative values in ar header are meaningless , so use 0. */ + if (v < 0) { + while (len-- > 0) + *p++ = '0'; + return (-1); + } + + p += s; + do { + *--p = (char)('0' + (v % 10)); + v /= 10; + } while (--s > 0 && v > 0); + + if (v == 0) { + memmove(h, p, len - s); + p = h + len - s; + while (s-- > 0) + *p++ = ' '; + return (0); + } + /* If it overflowed, fill field with max value. */ + while (len-- > 0) + *p++ = '9'; + + return (-1); +} + +static const char * +basename(const char *path) +{ + const char *endp, *startp; + + endp = path + strlen(path) - 1; + /* + * For filename with trailing slash(es), we return + * NULL indicating an error. + */ + if (*endp == '/') + return (NULL); + + /* Find the start of the base */ + startp = endp; + while (startp > path && *(startp - 1) != '/') + startp--; + + return (startp); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_write_set_format_by_name.c b/contrib/libarchive-2.1/libarchive/archive_write_set_format_by_name.c new file mode 100644 index 0000000000..1759a8f19b --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_write_set_format_by_name.c @@ -0,0 +1,72 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_by_name.c,v 1.6 2007/04/14 22:34:10 kientzle Exp $"); + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "archive.h" +#include "archive_private.h" + +/* A table that maps names to functions. */ +static +struct { const char *name; int (*setter)(struct archive *); } names[] = +{ + { "arbsd", archive_write_set_format_ar_bsd }, + { "ar", archive_write_set_format_ar_bsd }, + { "argnu", archive_write_set_format_ar_svr4 }, + { "arsvr4", archive_write_set_format_ar_svr4 }, + { "cpio", archive_write_set_format_cpio }, + { "pax", archive_write_set_format_pax }, + { "posix", archive_write_set_format_pax }, + { "shar", archive_write_set_format_shar }, + { "shardump", archive_write_set_format_shar_dump }, + { "ustar", archive_write_set_format_ustar }, + { NULL, NULL } +}; + +int +archive_write_set_format_by_name(struct archive *a, const char *name) +{ + int i; + + for (i = 0; names[i].name != NULL; i++) { + if (strcmp(name, names[i].name) == 0) + return ((names[i].setter)(a)); + } + + archive_set_error(a, EINVAL, "No such format '%s'", name); + return (ARCHIVE_FATAL); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_write_set_format_cpio.c b/contrib/libarchive-2.1/libarchive/archive_write_set_format_cpio.c new file mode 100644 index 0000000000..a713bfe84b --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_write_set_format_cpio.c @@ -0,0 +1,261 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_cpio.c,v 1.10 2007/03/03 07:37:36 kientzle Exp $"); + +#ifdef HAVE_ERRNO_H +#include +#endif +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "archive.h" +#include "archive_entry.h" +#include "archive_private.h" +#include "archive_write_private.h" + +static ssize_t archive_write_cpio_data(struct archive_write *, + const void *buff, size_t s); +static int archive_write_cpio_finish(struct archive_write *); +static int archive_write_cpio_destroy(struct archive_write *); +static int archive_write_cpio_finish_entry(struct archive_write *); +static int archive_write_cpio_header(struct archive_write *, + struct archive_entry *); +static int format_octal(int64_t, void *, int); +static int64_t format_octal_recursive(int64_t, char *, int); + +struct cpio { + uint64_t entry_bytes_remaining; +}; + +struct cpio_header { + char c_magic[6]; + char c_dev[6]; + char c_ino[6]; + char c_mode[6]; + char c_uid[6]; + char c_gid[6]; + char c_nlink[6]; + char c_rdev[6]; + char c_mtime[11]; + char c_namesize[6]; + char c_filesize[11]; +}; + +/* + * Set output format to 'cpio' format. + */ +int +archive_write_set_format_cpio(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + struct cpio *cpio; + + /* If someone else was already registered, unregister them. */ + if (a->format_destroy != NULL) + (a->format_destroy)(a); + + cpio = (struct cpio *)malloc(sizeof(*cpio)); + if (cpio == NULL) { + archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data"); + return (ARCHIVE_FATAL); + } + memset(cpio, 0, sizeof(*cpio)); + a->format_data = cpio; + + a->pad_uncompressed = 1; + a->format_write_header = archive_write_cpio_header; + a->format_write_data = archive_write_cpio_data; + a->format_finish_entry = archive_write_cpio_finish_entry; + a->format_finish = archive_write_cpio_finish; + a->format_destroy = archive_write_cpio_destroy; + a->archive_format = ARCHIVE_FORMAT_CPIO_POSIX; + a->archive_format_name = "POSIX cpio"; + return (ARCHIVE_OK); +} + +static int +archive_write_cpio_header(struct archive_write *a, struct archive_entry *entry) +{ + struct cpio *cpio; + const char *p, *path; + int pathlength, ret; + struct cpio_header h; + + cpio = (struct cpio *)a->format_data; + ret = 0; + + path = archive_entry_pathname(entry); + pathlength = strlen(path) + 1; /* Include trailing null. */ + + memset(&h, 0, sizeof(h)); + format_octal(070707, &h.c_magic, sizeof(h.c_magic)); + format_octal(archive_entry_dev(entry), &h.c_dev, sizeof(h.c_dev)); + /* + * TODO: Generate artificial inode numbers rather than just + * re-using the ones off the disk. That way, the 18-bit c_ino + * field only limits the number of files in the archive. + */ + if (archive_entry_ino(entry) > 0777777) { + archive_set_error(&a->archive, ERANGE, "large inode number truncated"); + ret = ARCHIVE_WARN; + } + + format_octal(archive_entry_ino(entry) & 0777777, &h.c_ino, sizeof(h.c_ino)); + format_octal(archive_entry_mode(entry), &h.c_mode, sizeof(h.c_mode)); + format_octal(archive_entry_uid(entry), &h.c_uid, sizeof(h.c_uid)); + format_octal(archive_entry_gid(entry), &h.c_gid, sizeof(h.c_gid)); + format_octal(archive_entry_nlink(entry), &h.c_nlink, sizeof(h.c_nlink)); + if (archive_entry_filetype(entry) == AE_IFBLK + || archive_entry_filetype(entry) == AE_IFCHR) + format_octal(archive_entry_dev(entry), &h.c_rdev, sizeof(h.c_rdev)); + else + format_octal(0, &h.c_rdev, sizeof(h.c_rdev)); + format_octal(archive_entry_mtime(entry), &h.c_mtime, sizeof(h.c_mtime)); + format_octal(pathlength, &h.c_namesize, sizeof(h.c_namesize)); + + /* Symlinks get the link written as the body of the entry. */ + p = archive_entry_symlink(entry); + if (p != NULL && *p != '\0') + format_octal(strlen(p), &h.c_filesize, sizeof(h.c_filesize)); + else + format_octal(archive_entry_size(entry), &h.c_filesize, sizeof(h.c_filesize)); + + ret = (a->compressor.write)(a, &h, sizeof(h)); + if (ret != ARCHIVE_OK) + return (ARCHIVE_FATAL); + + ret = (a->compressor.write)(a, path, pathlength); + if (ret != ARCHIVE_OK) + return (ARCHIVE_FATAL); + + cpio->entry_bytes_remaining = archive_entry_size(entry); + + /* Write the symlink now. */ + if (p != NULL && *p != '\0') + ret = (a->compressor.write)(a, p, strlen(p)); + + return (ret); +} + +static ssize_t +archive_write_cpio_data(struct archive_write *a, const void *buff, size_t s) +{ + struct cpio *cpio; + int ret; + + cpio = (struct cpio *)a->format_data; + if (s > cpio->entry_bytes_remaining) + s = cpio->entry_bytes_remaining; + + ret = (a->compressor.write)(a, buff, s); + cpio->entry_bytes_remaining -= s; + if (ret >= 0) + return (s); + else + return (ret); +} + +/* + * Format a number into the specified field. + */ +static int +format_octal(int64_t v, void *p, int digits) +{ + int64_t max; + int ret; + + max = (((int64_t)1) << (digits * 3)) - 1; + if (v >= 0 && v <= max) { + format_octal_recursive(v, (char *)p, digits); + ret = 0; + } else { + format_octal_recursive(max, (char *)p, digits); + ret = -1; + } + return (ret); +} + +static int64_t +format_octal_recursive(int64_t v, char *p, int s) +{ + if (s == 0) + return (v); + v = format_octal_recursive(v, p+1, s-1); + *p = '0' + (v & 7); + return (v >>= 3); +} + +static int +archive_write_cpio_finish(struct archive_write *a) +{ + struct cpio *cpio; + int er; + struct archive_entry *trailer; + + cpio = (struct cpio *)a->format_data; + trailer = archive_entry_new(); + archive_entry_set_nlink(trailer, 1); + archive_entry_set_pathname(trailer, "TRAILER!!!"); + er = archive_write_cpio_header(a, trailer); + archive_entry_free(trailer); + return (er); +} + +static int +archive_write_cpio_destroy(struct archive_write *a) +{ + struct cpio *cpio; + + cpio = (struct cpio *)a->format_data; + free(cpio); + a->format_data = NULL; + return (ARCHIVE_OK); +} + +static int +archive_write_cpio_finish_entry(struct archive_write *a) +{ + struct cpio *cpio; + int to_write, ret; + + cpio = (struct cpio *)a->format_data; + ret = ARCHIVE_OK; + while (cpio->entry_bytes_remaining > 0) { + to_write = cpio->entry_bytes_remaining < a->null_length ? + cpio->entry_bytes_remaining : a->null_length; + ret = (a->compressor.write)(a, a->nulls, to_write); + if (ret != ARCHIVE_OK) + return (ret); + cpio->entry_bytes_remaining -= to_write; + } + return (ret); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_write_set_format_pax.c b/contrib/libarchive-2.1/libarchive/archive_write_set_format_pax.c new file mode 100644 index 0000000000..9a3f7d9552 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_write_set_format_pax.c @@ -0,0 +1,1218 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_pax.c,v 1.39 2007/03/03 07:37:36 kientzle Exp $"); + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "archive.h" +#include "archive_entry.h" +#include "archive_private.h" +#include "archive_write_private.h" + +struct pax { + uint64_t entry_bytes_remaining; + uint64_t entry_padding; + struct archive_string pax_header; +}; + +static void add_pax_attr(struct archive_string *, const char *key, + const char *value); +static void add_pax_attr_int(struct archive_string *, + const char *key, int64_t value); +static void add_pax_attr_time(struct archive_string *, + const char *key, int64_t sec, + unsigned long nanos); +static void add_pax_attr_w(struct archive_string *, + const char *key, const wchar_t *wvalue); +static ssize_t archive_write_pax_data(struct archive_write *, + const void *, size_t); +static int archive_write_pax_finish(struct archive_write *); +static int archive_write_pax_destroy(struct archive_write *); +static int archive_write_pax_finish_entry(struct archive_write *); +static int archive_write_pax_header(struct archive_write *, + struct archive_entry *); +static char *base64_encode(const char *src, size_t len); +static char *build_pax_attribute_name(char *dest, const char *src); +static char *build_ustar_entry_name(char *dest, const char *src, + size_t src_length, const char *insert); +static char *format_int(char *dest, int64_t); +static int has_non_ASCII(const wchar_t *); +static char *url_encode(const char *in); +static int write_nulls(struct archive_write *, size_t); + +/* + * Set output format to 'restricted pax' format. + * + * This is the same as normal 'pax', but tries to suppress + * the pax header whenever possible. This is the default for + * bsdtar, for instance. + */ +int +archive_write_set_format_pax_restricted(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + int r; + r = archive_write_set_format_pax(&a->archive); + a->archive_format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED; + a->archive_format_name = "restricted POSIX pax interchange"; + return (r); +} + +/* + * Set output format to 'pax' format. + */ +int +archive_write_set_format_pax(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + struct pax *pax; + + if (a->format_destroy != NULL) + (a->format_destroy)(a); + + pax = (struct pax *)malloc(sizeof(*pax)); + if (pax == NULL) { + archive_set_error(&a->archive, ENOMEM, "Can't allocate pax data"); + return (ARCHIVE_FATAL); + } + memset(pax, 0, sizeof(*pax)); + a->format_data = pax; + + a->pad_uncompressed = 1; + a->format_write_header = archive_write_pax_header; + a->format_write_data = archive_write_pax_data; + a->format_finish = archive_write_pax_finish; + a->format_destroy = archive_write_pax_destroy; + a->format_finish_entry = archive_write_pax_finish_entry; + a->archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; + a->archive_format_name = "POSIX pax interchange"; + return (ARCHIVE_OK); +} + +/* + * Note: This code assumes that 'nanos' has the same sign as 'sec', + * which implies that sec=-1, nanos=200000000 represents -1.2 seconds + * and not -0.8 seconds. This is a pretty pedantic point, as we're + * unlikely to encounter many real files created before Jan 1, 1970, + * much less ones with timestamps recorded to sub-second resolution. + */ +static void +add_pax_attr_time(struct archive_string *as, const char *key, + int64_t sec, unsigned long nanos) +{ + int digit, i; + char *t; + /* + * Note that each byte contributes fewer than 3 base-10 + * digits, so this will always be big enough. + */ + char tmp[1 + 3*sizeof(sec) + 1 + 3*sizeof(nanos)]; + + tmp[sizeof(tmp) - 1] = 0; + t = tmp + sizeof(tmp) - 1; + + /* Skip trailing zeros in the fractional part. */ + for (digit = 0, i = 10; i > 0 && digit == 0; i--) { + digit = nanos % 10; + nanos /= 10; + } + + /* Only format the fraction if it's non-zero. */ + if (i > 0) { + while (i > 0) { + *--t = "0123456789"[digit]; + digit = nanos % 10; + nanos /= 10; + i--; + } + *--t = '.'; + } + t = format_int(t, sec); + + add_pax_attr(as, key, t); +} + +static char * +format_int(char *t, int64_t i) +{ + int sign; + + if (i < 0) { + sign = -1; + i = -i; + } else + sign = 1; + + do { + *--t = "0123456789"[i % 10]; + } while (i /= 10); + if (sign < 0) + *--t = '-'; + return (t); +} + +static void +add_pax_attr_int(struct archive_string *as, const char *key, int64_t value) +{ + char tmp[1 + 3 * sizeof(value)]; + + tmp[sizeof(tmp) - 1] = 0; + add_pax_attr(as, key, format_int(tmp + sizeof(tmp) - 1, value)); +} + +static char * +utf8_encode(const wchar_t *wval) +{ + int utf8len; + const wchar_t *wp; + unsigned long wc; + char *utf8_value, *p; + + utf8len = 0; + for (wp = wval; *wp != L'\0'; ) { + wc = *wp++; + if (wc <= 0x7f) + utf8len++; + else if (wc <= 0x7ff) + utf8len += 2; + else if (wc <= 0xffff) + utf8len += 3; + else if (wc <= 0x1fffff) + utf8len += 4; + else if (wc <= 0x3ffffff) + utf8len += 5; + else if (wc <= 0x7fffffff) + utf8len += 6; + /* Ignore larger values; UTF-8 can't encode them. */ + } + + utf8_value = (char *)malloc(utf8len + 1); + if (utf8_value == NULL) { + __archive_errx(1, "Not enough memory for attributes"); + return (NULL); + } + + for (wp = wval, p = utf8_value; *wp != L'\0'; ) { + wc = *wp++; + if (wc <= 0x7f) { + *p++ = (char)wc; + } else if (wc <= 0x7ff) { + p[0] = 0xc0 | ((wc >> 6) & 0x1f); + p[1] = 0x80 | (wc & 0x3f); + p += 2; + } else if (wc <= 0xffff) { + p[0] = 0xe0 | ((wc >> 12) & 0x0f); + p[1] = 0x80 | ((wc >> 6) & 0x3f); + p[2] = 0x80 | (wc & 0x3f); + p += 3; + } else if (wc <= 0x1fffff) { + p[0] = 0xf0 | ((wc >> 18) & 0x07); + p[1] = 0x80 | ((wc >> 12) & 0x3f); + p[2] = 0x80 | ((wc >> 6) & 0x3f); + p[3] = 0x80 | (wc & 0x3f); + p += 4; + } else if (wc <= 0x3ffffff) { + p[0] = 0xf8 | ((wc >> 24) & 0x03); + p[1] = 0x80 | ((wc >> 18) & 0x3f); + p[2] = 0x80 | ((wc >> 12) & 0x3f); + p[3] = 0x80 | ((wc >> 6) & 0x3f); + p[4] = 0x80 | (wc & 0x3f); + p += 5; + } else if (wc <= 0x7fffffff) { + p[0] = 0xfc | ((wc >> 30) & 0x01); + p[1] = 0x80 | ((wc >> 24) & 0x3f); + p[1] = 0x80 | ((wc >> 18) & 0x3f); + p[2] = 0x80 | ((wc >> 12) & 0x3f); + p[3] = 0x80 | ((wc >> 6) & 0x3f); + p[4] = 0x80 | (wc & 0x3f); + p += 6; + } + /* Ignore larger values; UTF-8 can't encode them. */ + } + *p = '\0'; + + return (utf8_value); +} + +static void +add_pax_attr_w(struct archive_string *as, const char *key, const wchar_t *wval) +{ + char *utf8_value = utf8_encode(wval); + if (utf8_value == NULL) + return; + add_pax_attr(as, key, utf8_value); + free(utf8_value); +} + +/* + * Add a key/value attribute to the pax header. This function handles + * the length field and various other syntactic requirements. + */ +static void +add_pax_attr(struct archive_string *as, const char *key, const char *value) +{ + int digits, i, len, next_ten; + char tmp[1 + 3 * sizeof(int)]; /* < 3 base-10 digits per byte */ + + /*- + * PAX attributes have the following layout: + * <=> + */ + len = 1 + strlen(key) + 1 + strlen(value) + 1; + + /* + * The field includes the length of the field, so + * computing the correct length is tricky. I start by + * counting the number of base-10 digits in 'len' and + * computing the next higher power of 10. + */ + next_ten = 1; + digits = 0; + i = len; + while (i > 0) { + i = i / 10; + digits++; + next_ten = next_ten * 10; + } + /* + * For example, if string without the length field is 99 + * chars, then adding the 2 digit length "99" will force the + * total length past 100, requiring an extra digit. The next + * statement adjusts for this effect. + */ + if (len + digits >= next_ten) + digits++; + + /* Now, we have the right length so we can build the line. */ + tmp[sizeof(tmp) - 1] = 0; /* Null-terminate the work area. */ + archive_strcat(as, format_int(tmp + sizeof(tmp) - 1, len + digits)); + archive_strappend_char(as, ' '); + archive_strcat(as, key); + archive_strappend_char(as, '='); + archive_strcat(as, value); + archive_strappend_char(as, '\n'); +} + +static void +archive_write_pax_header_xattrs(struct pax *pax, struct archive_entry *entry) +{ + struct archive_string s; + int i = archive_entry_xattr_reset(entry); + + while (i--) { + const char *name; + const void *value; + char *encoded_value; + char *url_encoded_name = NULL, *encoded_name = NULL; + wchar_t *wcs_name = NULL; + size_t size; + + archive_entry_xattr_next(entry, &name, &value, &size); + /* Name is URL-encoded, then converted to wchar_t, + * then UTF-8 encoded. */ + url_encoded_name = url_encode(name); + if (url_encoded_name != NULL) { + /* Convert narrow-character to wide-character. */ + int wcs_length = strlen(url_encoded_name); + wcs_name = (wchar_t *)malloc((wcs_length + 1) * sizeof(wchar_t)); + if (wcs_name == NULL) + __archive_errx(1, "No memory for xattr conversion"); + mbstowcs(wcs_name, url_encoded_name, wcs_length); + wcs_name[wcs_length] = 0; + free(url_encoded_name); /* Done with this. */ + } + if (wcs_name != NULL) { + encoded_name = utf8_encode(wcs_name); + free(wcs_name); /* Done with wchar_t name. */ + } + + encoded_value = base64_encode((const char *)value, size); + + if (encoded_name != NULL && encoded_value != NULL) { + archive_string_init(&s); + archive_strcpy(&s, "LIBARCHIVE.xattr."); + archive_strcat(&s, encoded_name); + add_pax_attr(&(pax->pax_header), s.s, encoded_value); + archive_string_free(&s); + } + free(encoded_name); + free(encoded_value); + } +} + +/* + * TODO: Consider adding 'comment' and 'charset' fields to + * archive_entry so that clients can specify them. Also, consider + * adding generic key/value tags so clients can add arbitrary + * key/value data. + */ +static int +archive_write_pax_header(struct archive_write *a, + struct archive_entry *entry_original) +{ + struct archive_entry *entry_main; + const char *linkname, *p; + char *t; + const char *hardlink; + const wchar_t *wp; + const char *suffix_start; + int need_extension, r, ret; + struct pax *pax; + + char paxbuff[512]; + char ustarbuff[512]; + char ustar_entry_name[256]; + char pax_entry_name[256]; + + need_extension = 0; + pax = (struct pax *)a->format_data; + + hardlink = archive_entry_hardlink(entry_original); + + /* Make sure this is a type of entry that we can handle here */ + if (hardlink == NULL) { + switch (archive_entry_filetype(entry_original)) { + case AE_IFBLK: + case AE_IFCHR: + case AE_IFIFO: + case AE_IFLNK: + case AE_IFREG: + break; + case AE_IFDIR: + /* + * Ensure a trailing '/'. Modify the original + * entry so the client sees the change. + */ + p = archive_entry_pathname(entry_original); + if (p[strlen(p) - 1] != '/') { + t = (char *)malloc(strlen(p) + 2); + if (t != NULL) { + strcpy(t, p); + strcat(t, "/"); + archive_entry_copy_pathname(entry_original, t); + free(t); + } + } + break; + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "tar format cannot archive this (type=0%lo)", + (unsigned long)archive_entry_filetype(entry_original)); + return (ARCHIVE_WARN); + } + } + + /* Copy entry so we can modify it as needed. */ + entry_main = archive_entry_clone(entry_original); + archive_string_empty(&(pax->pax_header)); /* Blank our work area. */ + + /* + * Determining whether or not the name is too big is ugly + * because of the rules for dividing names between 'name' and + * 'prefix' fields. Here, I pick out the longest possible + * suffix, then test whether the remaining prefix is too long. + */ + wp = archive_entry_pathname_w(entry_main); + p = archive_entry_pathname(entry_main); + if (strlen(p) <= 100) /* Short enough for just 'name' field */ + suffix_start = p; /* Record a zero-length prefix */ + else + /* Find the largest suffix that fits in 'name' field. */ + suffix_start = strchr(p + strlen(p) - 100 - 1, '/'); + + /* + * If name is too long, or has non-ASCII characters, add + * 'path' to pax extended attrs. + */ + if (suffix_start == NULL || suffix_start - p > 155 || has_non_ASCII(wp)) { + add_pax_attr_w(&(pax->pax_header), "path", wp); + archive_entry_set_pathname(entry_main, + build_ustar_entry_name(ustar_entry_name, p, strlen(p), NULL)); + need_extension = 1; + } + + /* If link name is too long or has non-ASCII characters, add + * 'linkpath' to pax extended attrs. */ + linkname = hardlink; + if (linkname == NULL) + linkname = archive_entry_symlink(entry_main); + + if (linkname != NULL) { + /* There is a link name, get the wide version as well. */ + if (hardlink != NULL) + wp = archive_entry_hardlink_w(entry_main); + else + wp = archive_entry_symlink_w(entry_main); + + /* If the link is long or has a non-ASCII character, + * store it as a pax extended attribute. */ + if (strlen(linkname) > 100 || has_non_ASCII(wp)) { + add_pax_attr_w(&(pax->pax_header), "linkpath", wp); + if (hardlink != NULL) + archive_entry_set_hardlink(entry_main, + "././@LongHardLink"); + else + archive_entry_set_symlink(entry_main, + "././@LongSymLink"); + need_extension = 1; + } + } + + /* If file size is too large, add 'size' to pax extended attrs. */ + if (archive_entry_size(entry_main) >= (((int64_t)1) << 33)) { + add_pax_attr_int(&(pax->pax_header), "size", + archive_entry_size(entry_main)); + need_extension = 1; + } + + /* If numeric GID is too large, add 'gid' to pax extended attrs. */ + if (archive_entry_gid(entry_main) >= (1 << 18)) { + add_pax_attr_int(&(pax->pax_header), "gid", + archive_entry_gid(entry_main)); + need_extension = 1; + } + + /* If group name is too large or has non-ASCII characters, add + * 'gname' to pax extended attrs. */ + p = archive_entry_gname(entry_main); + wp = archive_entry_gname_w(entry_main); + if (p != NULL && (strlen(p) > 31 || has_non_ASCII(wp))) { + add_pax_attr_w(&(pax->pax_header), "gname", wp); + archive_entry_set_gname(entry_main, NULL); + need_extension = 1; + } + + /* If numeric UID is too large, add 'uid' to pax extended attrs. */ + if (archive_entry_uid(entry_main) >= (1 << 18)) { + add_pax_attr_int(&(pax->pax_header), "uid", + archive_entry_uid(entry_main)); + need_extension = 1; + } + + /* If user name is too large, add 'uname' to pax extended attrs. */ + /* TODO: If uname has non-ASCII characters, use pax attribute. */ + p = archive_entry_uname(entry_main); + wp = archive_entry_uname_w(entry_main); + if (p != NULL && (strlen(p) > 31 || has_non_ASCII(wp))) { + add_pax_attr_w(&(pax->pax_header), "uname", wp); + archive_entry_set_uname(entry_main, NULL); + need_extension = 1; + } + + /* + * POSIX/SUSv3 doesn't provide a standard key for large device + * numbers. I use the same keys here that Joerg Schilling + * used for 'star.' (Which, somewhat confusingly, are called + * "devXXX" even though they code "rdev" values.) No doubt, + * other implementations use other keys. Note that there's no + * reason we can't write the same information into a number of + * different keys. + * + * Of course, this is only needed for block or char device entries. + */ + if (archive_entry_filetype(entry_main) == AE_IFBLK + || archive_entry_filetype(entry_main) == AE_IFCHR) { + /* + * If rdevmajor is too large, add 'SCHILY.devmajor' to + * extended attributes. + */ + dev_t rdevmajor, rdevminor; + rdevmajor = archive_entry_rdevmajor(entry_main); + rdevminor = archive_entry_rdevminor(entry_main); + if (rdevmajor >= (1 << 18)) { + add_pax_attr_int(&(pax->pax_header), "SCHILY.devmajor", + rdevmajor); + /* + * Non-strict formatting below means we don't + * have to truncate here. Not truncating improves + * the chance that some more modern tar archivers + * (such as GNU tar 1.13) can restore the full + * value even if they don't understand the pax + * extended attributes. See my rant below about + * file size fields for additional details. + */ + /* archive_entry_set_rdevmajor(entry_main, + rdevmajor & ((1 << 18) - 1)); */ + need_extension = 1; + } + + /* + * If devminor is too large, add 'SCHILY.devminor' to + * extended attributes. + */ + if (rdevminor >= (1 << 18)) { + add_pax_attr_int(&(pax->pax_header), "SCHILY.devminor", + rdevminor); + /* Truncation is not necessary here, either. */ + /* archive_entry_set_rdevminor(entry_main, + rdevminor & ((1 << 18) - 1)); */ + need_extension = 1; + } + } + + /* + * Technically, the mtime field in the ustar header can + * support 33 bits, but many platforms use signed 32-bit time + * values. The cutoff of 0x7fffffff here is a compromise. + * Yes, this check is duplicated just below; this helps to + * avoid writing an mtime attribute just to handle a + * high-resolution timestamp in "restricted pax" mode. + */ + if (!need_extension && + ((archive_entry_mtime(entry_main) < 0) + || (archive_entry_mtime(entry_main) >= 0x7fffffff))) + need_extension = 1; + + /* I use a star-compatible file flag attribute. */ + p = archive_entry_fflags_text(entry_main); + if (!need_extension && p != NULL && *p != '\0') + need_extension = 1; + + /* If there are non-trivial ACL entries, we need an extension. */ + if (!need_extension && archive_entry_acl_count(entry_original, + ARCHIVE_ENTRY_ACL_TYPE_ACCESS) > 0) + need_extension = 1; + + /* If there are non-trivial ACL entries, we need an extension. */ + if (!need_extension && archive_entry_acl_count(entry_original, + ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) > 0) + need_extension = 1; + + /* If there are extended attributes, we need an extension */ + if (!need_extension && archive_entry_xattr_count(entry_original) > 0) + need_extension = 1; + + /* + * The following items are handled differently in "pax + * restricted" format. In particular, in "pax restricted" + * format they won't be added unless need_extension is + * already set (we're already generating an extended header, so + * may as well include these). + */ + if (a->archive_format != ARCHIVE_FORMAT_TAR_PAX_RESTRICTED || + need_extension) { + + if (archive_entry_mtime(entry_main) < 0 || + archive_entry_mtime(entry_main) >= 0x7fffffff || + archive_entry_mtime_nsec(entry_main) != 0) + add_pax_attr_time(&(pax->pax_header), "mtime", + archive_entry_mtime(entry_main), + archive_entry_mtime_nsec(entry_main)); + + if (archive_entry_ctime(entry_main) != 0 || + archive_entry_ctime_nsec(entry_main) != 0) + add_pax_attr_time(&(pax->pax_header), "ctime", + archive_entry_ctime(entry_main), + archive_entry_ctime_nsec(entry_main)); + + if (archive_entry_atime(entry_main) != 0 || + archive_entry_atime_nsec(entry_main) != 0) + add_pax_attr_time(&(pax->pax_header), "atime", + archive_entry_atime(entry_main), + archive_entry_atime_nsec(entry_main)); + + /* I use a star-compatible file flag attribute. */ + p = archive_entry_fflags_text(entry_main); + if (p != NULL && *p != '\0') + add_pax_attr(&(pax->pax_header), "SCHILY.fflags", p); + + /* I use star-compatible ACL attributes. */ + wp = archive_entry_acl_text_w(entry_original, + ARCHIVE_ENTRY_ACL_TYPE_ACCESS | + ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID); + if (wp != NULL && *wp != L'\0') + add_pax_attr_w(&(pax->pax_header), + "SCHILY.acl.access", wp); + wp = archive_entry_acl_text_w(entry_original, + ARCHIVE_ENTRY_ACL_TYPE_DEFAULT | + ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID); + if (wp != NULL && *wp != L'\0') + add_pax_attr_w(&(pax->pax_header), + "SCHILY.acl.default", wp); + + /* Include star-compatible metadata info. */ + /* Note: "SCHILY.dev{major,minor}" are NOT the + * major/minor portions of "SCHILY.dev". */ + add_pax_attr_int(&(pax->pax_header), "SCHILY.dev", + archive_entry_dev(entry_main)); + add_pax_attr_int(&(pax->pax_header), "SCHILY.ino", + archive_entry_ino(entry_main)); + add_pax_attr_int(&(pax->pax_header), "SCHILY.nlink", + archive_entry_nlink(entry_main)); + + /* Store extended attributes */ + archive_write_pax_header_xattrs(pax, entry_original); + } + + /* Only regular files have data. */ + if (archive_entry_filetype(entry_main) != AE_IFREG) + archive_entry_set_size(entry_main, 0); + + /* + * Pax-restricted does not store data for hardlinks, in order + * to improve compatibility with ustar. + */ + if (a->archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE && + hardlink != NULL) + archive_entry_set_size(entry_main, 0); + + /* + * XXX Full pax interchange format does permit a hardlink + * entry to have data associated with it. I'm not supporting + * that here because the client expects me to tell them whether + * or not this format expects data for hardlinks. If I + * don't check here, then every pax archive will end up with + * duplicated data for hardlinks. Someday, there may be + * need to select this behavior, in which case the following + * will need to be revisited. XXX + */ + if (hardlink != NULL) + archive_entry_set_size(entry_main, 0); + + /* Format 'ustar' header for main entry. + * + * The trouble with file size: If the reader can't understand + * the file size, they may not be able to locate the next + * entry and the rest of the archive is toast. Pax-compliant + * readers are supposed to ignore the file size in the main + * header, so the question becomes how to maximize portability + * for readers that don't support pax attribute extensions. + * For maximum compatibility, I permit numeric extensions in + * the main header so that the file size stored will always be + * correct, even if it's in a format that only some + * implementations understand. The technique used here is: + * + * a) If possible, follow the standard exactly. This handles + * files up to 8 gigabytes minus 1. + * + * b) If that fails, try octal but omit the field terminator. + * That handles files up to 64 gigabytes minus 1. + * + * c) Otherwise, use base-256 extensions. That handles files + * up to 2^63 in this implementation, with the potential to + * go up to 2^94. That should hold us for a while. ;-) + * + * The non-strict formatter uses similar logic for other + * numeric fields, though they're less critical. + */ + __archive_write_format_header_ustar(a, ustarbuff, entry_main, -1, 0); + + /* If we built any extended attributes, write that entry first. */ + ret = ARCHIVE_OK; + if (archive_strlen(&(pax->pax_header)) > 0) { + struct archive_entry *pax_attr_entry; + time_t s; + uid_t uid; + gid_t gid; + mode_t mode; + long ns; + + pax_attr_entry = archive_entry_new(); + p = archive_entry_pathname(entry_main); + archive_entry_set_pathname(pax_attr_entry, + build_pax_attribute_name(pax_entry_name, p)); + archive_entry_set_size(pax_attr_entry, + archive_strlen(&(pax->pax_header))); + /* Copy uid/gid (but clip to ustar limits). */ + uid = archive_entry_uid(entry_main); + if (uid >= 1 << 18) + uid = (1 << 18) - 1; + archive_entry_set_uid(pax_attr_entry, uid); + gid = archive_entry_gid(entry_main); + if (gid >= 1 << 18) + gid = (1 << 18) - 1; + archive_entry_set_gid(pax_attr_entry, gid); + /* Copy mode over (but not setuid/setgid bits) */ + mode = archive_entry_mode(entry_main); +#ifdef S_ISUID + mode &= ~S_ISUID; +#endif +#ifdef S_ISGID + mode &= ~S_ISGID; +#endif +#ifdef S_ISVTX + mode &= ~S_ISVTX; +#endif + archive_entry_set_mode(pax_attr_entry, mode); + + /* Copy uname/gname. */ + archive_entry_set_uname(pax_attr_entry, + archive_entry_uname(entry_main)); + archive_entry_set_gname(pax_attr_entry, + archive_entry_gname(entry_main)); + + /* Copy mtime, but clip to ustar limits. */ + s = archive_entry_mtime(entry_main); + ns = archive_entry_mtime_nsec(entry_main); + if (s < 0) { s = 0; ns = 0; } + if (s > 0x7fffffff) { s = 0x7fffffff; ns = 0; } + archive_entry_set_mtime(pax_attr_entry, s, ns); + + /* Ditto for atime. */ + s = archive_entry_atime(entry_main); + ns = archive_entry_atime_nsec(entry_main); + if (s < 0) { s = 0; ns = 0; } + if (s > 0x7fffffff) { s = 0x7fffffff; ns = 0; } + archive_entry_set_atime(pax_attr_entry, s, ns); + + /* Standard ustar doesn't support ctime. */ + archive_entry_set_ctime(pax_attr_entry, 0, 0); + + ret = __archive_write_format_header_ustar(a, paxbuff, + pax_attr_entry, 'x', 1); + + archive_entry_free(pax_attr_entry); + + /* Note that the 'x' header shouldn't ever fail to format */ + if (ret != 0) { + const char *msg = "archive_write_pax_header: " + "'x' header failed?! This can't happen.\n"; + write(2, msg, strlen(msg)); + exit(1); + } + r = (a->compressor.write)(a, paxbuff, 512); + if (r != ARCHIVE_OK) { + pax->entry_bytes_remaining = 0; + pax->entry_padding = 0; + return (ARCHIVE_FATAL); + } + + pax->entry_bytes_remaining = archive_strlen(&(pax->pax_header)); + pax->entry_padding = 0x1ff & (-(int64_t)pax->entry_bytes_remaining); + + r = (a->compressor.write)(a, pax->pax_header.s, + archive_strlen(&(pax->pax_header))); + if (r != ARCHIVE_OK) { + /* If a write fails, we're pretty much toast. */ + return (ARCHIVE_FATAL); + } + /* Pad out the end of the entry. */ + r = write_nulls(a, pax->entry_padding); + if (r != ARCHIVE_OK) { + /* If a write fails, we're pretty much toast. */ + return (ARCHIVE_FATAL); + } + pax->entry_bytes_remaining = pax->entry_padding = 0; + } + + /* Write the header for main entry. */ + r = (a->compressor.write)(a, ustarbuff, 512); + if (r != ARCHIVE_OK) + return (r); + + /* + * Inform the client of the on-disk size we're using, so + * they can avoid unnecessarily writing a body for something + * that we're just going to ignore. + */ + archive_entry_set_size(entry_original, archive_entry_size(entry_main)); + pax->entry_bytes_remaining = archive_entry_size(entry_main); + pax->entry_padding = 0x1ff & (-(int64_t)pax->entry_bytes_remaining); + archive_entry_free(entry_main); + + return (ret); +} + +/* + * We need a valid name for the regular 'ustar' entry. This routine + * tries to hack something more-or-less reasonable. + * + * The approach here tries to preserve leading dir names. We do so by + * working with four sections: + * 1) "prefix" directory names, + * 2) "suffix" directory names, + * 3) inserted dir name (optional), + * 4) filename. + * + * These sections must satisfy the following requirements: + * * Parts 1 & 2 together form an initial portion of the dir name. + * * Part 3 is specified by the caller. (It should not contain a leading + * or trailing '/'.) + * * Part 4 forms an initial portion of the base filename. + * * The filename must be <= 99 chars to fit the ustar 'name' field. + * * Parts 2, 3, 4 together must be <= 99 chars to fit the ustar 'name' fld. + * * Part 1 must be <= 155 chars to fit the ustar 'prefix' field. + * * If the original name ends in a '/', the new name must also end in a '/' + * * Trailing '/.' sequences may be stripped. + * + * Note: Recall that the ustar format does not store the '/' separating + * parts 1 & 2, but does store the '/' separating parts 2 & 3. + */ +static char * +build_ustar_entry_name(char *dest, const char *src, size_t src_length, + const char *insert) +{ + const char *prefix, *prefix_end; + const char *suffix, *suffix_end; + const char *filename, *filename_end; + char *p; + int need_slash = 0; /* Was there a trailing slash? */ + size_t suffix_length = 99; + int insert_length; + + /* Length of additional dir element to be added. */ + if (insert == NULL) + insert_length = 0; + else + /* +2 here allows for '/' before and after the insert. */ + insert_length = strlen(insert) + 2; + + /* Step 0: Quick bailout in a common case. */ + if (src_length < 100 && insert == NULL) { + strncpy(dest, src, src_length); + dest[src_length] = '\0'; + return (dest); + } + + /* Step 1: Locate filename and enforce the length restriction. */ + filename_end = src + src_length; + /* Remove trailing '/' chars and '/.' pairs. */ + for (;;) { + if (filename_end > src && filename_end[-1] == '/') { + filename_end --; + need_slash = 1; /* Remember to restore trailing '/'. */ + continue; + } + if (filename_end > src + 1 && filename_end[-1] == '.' + && filename_end[-2] == '/') { + filename_end -= 2; + need_slash = 1; /* "foo/." will become "foo/" */ + continue; + } + break; + } + if (need_slash) + suffix_length--; + /* Find start of filename. */ + filename = filename_end - 1; + while ((filename > src) && (*filename != '/')) + filename --; + if ((*filename == '/') && (filename < filename_end - 1)) + filename ++; + /* Adjust filename_end so that filename + insert fits in 99 chars. */ + suffix_length -= insert_length; + if (filename_end > filename + suffix_length) + filename_end = filename + suffix_length; + /* Calculate max size for "suffix" section (#3 above). */ + suffix_length -= filename_end - filename; + + /* Step 2: Locate the "prefix" section of the dirname, including + * trailing '/'. */ + prefix = src; + prefix_end = prefix + 155; + if (prefix_end > filename) + prefix_end = filename; + while (prefix_end > prefix && *prefix_end != '/') + prefix_end--; + if ((prefix_end < filename) && (*prefix_end == '/')) + prefix_end++; + + /* Step 3: Locate the "suffix" section of the dirname, + * including trailing '/'. */ + suffix = prefix_end; + suffix_end = suffix + suffix_length; /* Enforce limit. */ + if (suffix_end > filename) + suffix_end = filename; + if (suffix_end < suffix) + suffix_end = suffix; + while (suffix_end > suffix && *suffix_end != '/') + suffix_end--; + if ((suffix_end < filename) && (*suffix_end == '/')) + suffix_end++; + + /* Step 4: Build the new name. */ + /* The OpenBSD strlcpy function is safer, but less portable. */ + /* Rather than maintain two versions, just use the strncpy version. */ + p = dest; + if (prefix_end > prefix) { + strncpy(p, prefix, prefix_end - prefix); + p += prefix_end - prefix; + } + if (suffix_end > suffix) { + strncpy(p, suffix, suffix_end - suffix); + p += suffix_end - suffix; + } + if (insert != NULL) { + /* Note: assume insert does not have leading or trailing '/' */ + strcpy(p, insert); + p += strlen(insert); + *p++ = '/'; + } + strncpy(p, filename, filename_end - filename); + p += filename_end - filename; + if (need_slash) + *p++ = '/'; + *p++ = '\0'; + + return (dest); +} + +/* + * The ustar header for the pax extended attributes must have a + * reasonable name: SUSv3 suggests 'dirname'/PaxHeader/'filename' + * + * Joerg Schiling has argued that this is unnecessary because, in practice, + * if the pax extended attributes get extracted as regular files, noone is + * going to bother reading those attributes to manually restore them. + * Based on this, 'star' uses /tmp/PaxHeader/'basename' as the ustar header + * name. This is a tempting argument, but I'm not entirely convinced. + * I'm also uncomfortable with the fact that "/tmp" is a Unix-ism. + * + * The following routine implements the SUSv3 recommendation, and is + * much simpler because build_ustar_entry_name() above already does + * most of the work (we just need to give it an extra path element to + * insert and handle a few pathological cases). + */ +static char * +build_pax_attribute_name(char *dest, const char *src) +{ + const char *p; + + /* Handle the null filename case. */ + if (src == NULL || *src == '\0') { + strcpy(dest, "PaxHeader/blank"); + return (dest); + } + + /* Prune final '/' and other unwanted final elements. */ + p = src + strlen(src); + for (;;) { + /* Ends in "/", remove the '/' */ + if (p > src && p[-1] == '/') { + --p; + continue; + } + /* Ends in "/.", remove the '.' */ + if (p > src + 1 && p[-1] == '.' + && p[-2] == '/') { + --p; + continue; + } + break; + } + + /* Pathological case: After above, there was nothing left. + * This includes "/." "/./." "/.//./." etc. */ + if (p == src) { + strcpy(dest, "/PaxHeader/rootdir"); + return (dest); + } + + /* Convert unadorned "." into a suitable filename. */ + if (*src == '.' && p == src + 1) { + strcpy(dest, "PaxHeader/currentdir"); + return (dest); + } + + /* General case: build a ustar-compatible name adding "/PaxHeader/". */ + build_ustar_entry_name(dest, src, p - src, "PaxHeader"); + + return (dest); +} + +/* Write two null blocks for the end of archive */ +static int +archive_write_pax_finish(struct archive_write *a) +{ + struct pax *pax; + int r; + + if (a->compressor.write == NULL) + return (ARCHIVE_OK); + + pax = (struct pax *)a->format_data; + r = write_nulls(a, 512 * 2); + return (r); +} + +static int +archive_write_pax_destroy(struct archive_write *a) +{ + struct pax *pax; + + pax = (struct pax *)a->format_data; + archive_string_free(&pax->pax_header); + free(pax); + a->format_data = NULL; + return (ARCHIVE_OK); +} + +static int +archive_write_pax_finish_entry(struct archive_write *a) +{ + struct pax *pax; + int ret; + + pax = (struct pax *)a->format_data; + ret = write_nulls(a, pax->entry_bytes_remaining + pax->entry_padding); + pax->entry_bytes_remaining = pax->entry_padding = 0; + return (ret); +} + +static int +write_nulls(struct archive_write *a, size_t padding) +{ + int ret, to_write; + + while (padding > 0) { + to_write = padding < a->null_length ? padding : a->null_length; + ret = (a->compressor.write)(a, a->nulls, to_write); + if (ret != ARCHIVE_OK) + return (ret); + padding -= to_write; + } + return (ARCHIVE_OK); +} + +static ssize_t +archive_write_pax_data(struct archive_write *a, const void *buff, size_t s) +{ + struct pax *pax; + int ret; + + pax = (struct pax *)a->format_data; + if (s > pax->entry_bytes_remaining) + s = pax->entry_bytes_remaining; + + ret = (a->compressor.write)(a, buff, s); + pax->entry_bytes_remaining -= s; + if (ret == ARCHIVE_OK) + return (s); + else + return (ret); +} + +static int +has_non_ASCII(const wchar_t *wp) +{ + while (*wp != L'\0' && *wp < 128) + wp++; + return (*wp != L'\0'); +} + +/* + * Used by extended attribute support; encodes the name + * so that there will be no '=' characters in the result. + */ +static char * +url_encode(const char *in) +{ + const char *s; + char *d; + int out_len = 0; + char *out; + + for (s = in; *s != '\0'; s++) { + if (*s < 33 || *s > 126 || *s == '%' || *s == '=') + out_len += 3; + else + out_len++; + } + + out = (char *)malloc(out_len + 1); + if (out == NULL) + return (NULL); + + for (s = in, d = out; *s != '\0'; s++) { + /* encode any non-printable ASCII character or '%' or '=' */ + if (*s < 33 || *s > 126 || *s == '%' || *s == '=') { + /* URL encoding is '%' followed by two hex digits */ + *d++ = '%'; + *d++ = "0123456789ABCDEF"[0x0f & (*s >> 4)]; + *d++ = "0123456789ABCDEF"[0x0f & *s]; + } else { + *d++ = *s; + } + } + *d = '\0'; + return (out); +} + +/* + * Encode a sequence of bytes into a C string using base-64 encoding. + * + * Returns a null-terminated C string allocated with malloc(); caller + * is responsible for freeing the result. + */ +static char * +base64_encode(const char *s, size_t len) +{ + static const char digits[64] = + { 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O', + 'P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d', + 'e','f','g','h','i','j','k','l','m','n','o','p','q','r','s', + 't','u','v','w','x','y','z','0','1','2','3','4','5','6','7', + '8','9','+','/' }; + int v; + char *d, *out; + + /* 3 bytes becomes 4 chars, but round up and allow for trailing NUL */ + out = (char *)malloc((len * 4 + 2) / 3 + 1); + if (out == NULL) + return (NULL); + d = out; + + /* Convert each group of 3 bytes into 4 characters. */ + while (len >= 3) { + v = (((int)s[0] << 16) & 0xff0000) + | (((int)s[1] << 8) & 0xff00) + | (((int)s[2]) & 0x00ff); + s += 3; + len -= 3; + *d++ = digits[(v >> 18) & 0x3f]; + *d++ = digits[(v >> 12) & 0x3f]; + *d++ = digits[(v >> 6) & 0x3f]; + *d++ = digits[(v) & 0x3f]; + } + /* Handle final group of 1 byte (2 chars) or 2 bytes (3 chars). */ + switch (len) { + case 0: break; + case 1: + v = (((int)s[0] << 16) & 0xff0000); + *d++ = digits[(v >> 18) & 0x3f]; + *d++ = digits[(v >> 12) & 0x3f]; + break; + case 2: + v = (((int)s[0] << 16) & 0xff0000) + | (((int)s[1] << 8) & 0xff00); + *d++ = digits[(v >> 18) & 0x3f]; + *d++ = digits[(v >> 12) & 0x3f]; + *d++ = digits[(v >> 6) & 0x3f]; + break; + } + /* Add trailing NUL character so output is a valid C string. */ + *d++ = '\0'; + return (out); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_write_set_format_shar.c b/contrib/libarchive-2.1/libarchive/archive_write_set_format_shar.c new file mode 100644 index 0000000000..40e7ced715 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_write_set_format_shar.c @@ -0,0 +1,553 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_shar.c,v 1.16 2007/03/03 07:37:36 kientzle Exp $"); + +#ifdef HAVE_ERRNO_H +#include +#endif +#include +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "archive.h" +#include "archive_entry.h" +#include "archive_private.h" +#include "archive_write_private.h" + +struct shar { + int dump; + int end_of_line; + struct archive_entry *entry; + int has_data; + char *last_dir; + char outbuff[1024]; + size_t outbytes; + size_t outpos; + int uuavail; + char uubuffer[3]; + int wrote_header; + struct archive_string work; +}; + +static int archive_write_shar_finish(struct archive_write *); +static int archive_write_shar_destroy(struct archive_write *); +static int archive_write_shar_header(struct archive_write *, + struct archive_entry *); +static ssize_t archive_write_shar_data_sed(struct archive_write *, + const void * buff, size_t); +static ssize_t archive_write_shar_data_uuencode(struct archive_write *, + const void * buff, size_t); +static int archive_write_shar_finish_entry(struct archive_write *); +static int shar_printf(struct archive_write *, const char *fmt, ...); +static void uuencode_group(struct shar *); + +static int +shar_printf(struct archive_write *a, const char *fmt, ...) +{ + struct shar *shar; + va_list ap; + int ret; + + shar = (struct shar *)a->format_data; + va_start(ap, fmt); + archive_string_empty(&(shar->work)); + archive_string_vsprintf(&(shar->work), fmt, ap); + ret = ((a->compressor.write)(a, shar->work.s, strlen(shar->work.s))); + va_end(ap); + return (ret); +} + +/* + * Set output format to 'shar' format. + */ +int +archive_write_set_format_shar(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + struct shar *shar; + + /* If someone else was already registered, unregister them. */ + if (a->format_destroy != NULL) + (a->format_destroy)(a); + + shar = (struct shar *)malloc(sizeof(*shar)); + if (shar == NULL) { + archive_set_error(&a->archive, ENOMEM, "Can't allocate shar data"); + return (ARCHIVE_FATAL); + } + memset(shar, 0, sizeof(*shar)); + a->format_data = shar; + + a->pad_uncompressed = 0; + a->format_write_header = archive_write_shar_header; + a->format_finish = archive_write_shar_finish; + a->format_destroy = archive_write_shar_destroy; + a->format_write_data = archive_write_shar_data_sed; + a->format_finish_entry = archive_write_shar_finish_entry; + a->archive_format = ARCHIVE_FORMAT_SHAR_BASE; + a->archive_format_name = "shar"; + return (ARCHIVE_OK); +} + +/* + * An alternate 'shar' that uses uudecode instead of 'sed' to encode + * file contents and can therefore be used to archive binary files. + * In addition, this variant also attempts to restore ownership, file modes, + * and other extended file information. + */ +int +archive_write_set_format_shar_dump(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + struct shar *shar; + + archive_write_set_format_shar(&a->archive); + shar = (struct shar *)a->format_data; + shar->dump = 1; + a->format_write_data = archive_write_shar_data_uuencode; + a->archive_format = ARCHIVE_FORMAT_SHAR_DUMP; + a->archive_format_name = "shar dump"; + return (ARCHIVE_OK); +} + +static int +archive_write_shar_header(struct archive_write *a, struct archive_entry *entry) +{ + const char *linkname; + const char *name; + char *p, *pp; + struct shar *shar; + int ret; + + shar = (struct shar *)a->format_data; + if (!shar->wrote_header) { + ret = shar_printf(a, "#!/bin/sh\n"); + if (ret != ARCHIVE_OK) + return (ret); + ret = shar_printf(a, "# This is a shell archive\n"); + if (ret != ARCHIVE_OK) + return (ret); + shar->wrote_header = 1; + } + + /* Save the entry for the closing. */ + if (shar->entry) + archive_entry_free(shar->entry); + shar->entry = archive_entry_clone(entry); + name = archive_entry_pathname(entry); + + /* Handle some preparatory issues. */ + switch(archive_entry_filetype(entry)) { + case AE_IFREG: + /* Only regular files have non-zero size. */ + break; + case AE_IFDIR: + archive_entry_set_size(entry, 0); + /* Don't bother trying to recreate '.' */ + if (strcmp(name, ".") == 0 || strcmp(name, "./") == 0) + return (ARCHIVE_OK); + break; + case AE_IFIFO: + case AE_IFCHR: + case AE_IFBLK: + /* All other file types have zero size in the archive. */ + archive_entry_set_size(entry, 0); + break; + default: + archive_entry_set_size(entry, 0); + if (archive_entry_hardlink(entry) == NULL && + archive_entry_symlink(entry) == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "shar format cannot archive this"); + return (ARCHIVE_WARN); + } + } + + /* Stock preparation for all file types. */ + ret = shar_printf(a, "echo x %s\n", name); + if (ret != ARCHIVE_OK) + return (ret); + + if (archive_entry_filetype(entry) != AE_IFDIR) { + /* Try to create the dir. */ + p = strdup(name); + pp = strrchr(p, '/'); + /* If there is a / character, try to create the dir. */ + if (pp != NULL) { + *pp = '\0'; + + /* Try to avoid a lot of redundant mkdir commands. */ + if (strcmp(p, ".") == 0) { + /* Don't try to "mkdir ." */ + } else if (shar->last_dir == NULL) { + ret = shar_printf(a, + "mkdir -p %s > /dev/null 2>&1\n", p); + if (ret != ARCHIVE_OK) + return (ret); + shar->last_dir = p; + } else if (strcmp(p, shar->last_dir) == 0) { + /* We've already created this exact dir. */ + free(p); + } else if (strlen(p) < strlen(shar->last_dir) && + strncmp(p, shar->last_dir, strlen(p)) == 0) { + /* We've already created a subdir. */ + free(p); + } else { + ret = shar_printf(a, + "mkdir -p %s > /dev/null 2>&1\n", p); + if (ret != ARCHIVE_OK) + return (ret); + free(shar->last_dir); + shar->last_dir = p; + } + } + } + + /* Handle file-type specific issues. */ + shar->has_data = 0; + if ((linkname = archive_entry_hardlink(entry)) != NULL) { + ret = shar_printf(a, "ln -f %s %s\n", linkname, name); + if (ret != ARCHIVE_OK) + return (ret); + } else if ((linkname = archive_entry_symlink(entry)) != NULL) { + ret = shar_printf(a, "ln -fs %s %s\n", linkname, name); + if (ret != ARCHIVE_OK) + return (ret); + } else { + switch(archive_entry_filetype(entry)) { + case AE_IFREG: + if (archive_entry_size(entry) == 0) { + /* More portable than "touch." */ + ret = shar_printf(a, "test -e \"%s\" || :> \"%s\"\n", name, name); + if (ret != ARCHIVE_OK) + return (ret); + } else { + if (shar->dump) { + ret = shar_printf(a, + "uudecode -o %s << 'SHAR_END'\n", + name); + if (ret != ARCHIVE_OK) + return (ret); + ret = shar_printf(a, "begin %o %s\n", + archive_entry_mode(entry) & 0777, + name); + if (ret != ARCHIVE_OK) + return (ret); + } else { + ret = shar_printf(a, + "sed 's/^X//' > %s << 'SHAR_END'\n", + name); + if (ret != ARCHIVE_OK) + return (ret); + } + shar->has_data = 1; + shar->end_of_line = 1; + shar->outpos = 0; + shar->outbytes = 0; + } + break; + case AE_IFDIR: + ret = shar_printf(a, "mkdir -p %s > /dev/null 2>&1\n", + name); + if (ret != ARCHIVE_OK) + return (ret); + /* Record that we just created this directory. */ + if (shar->last_dir != NULL) + free(shar->last_dir); + + shar->last_dir = strdup(name); + /* Trim a trailing '/'. */ + pp = strrchr(shar->last_dir, '/'); + if (pp != NULL && pp[1] == '\0') + *pp = '\0'; + /* + * TODO: Put dir name/mode on a list to be fixed + * up at end of archive. + */ + break; + case AE_IFIFO: + ret = shar_printf(a, "mkfifo %s\n", name); + if (ret != ARCHIVE_OK) + return (ret); + break; + case AE_IFCHR: + ret = shar_printf(a, "mknod %s c %d %d\n", name, + archive_entry_rdevmajor(entry), + archive_entry_rdevminor(entry)); + if (ret != ARCHIVE_OK) + return (ret); + break; + case AE_IFBLK: + ret = shar_printf(a, "mknod %s b %d %d\n", name, + archive_entry_rdevmajor(entry), + archive_entry_rdevminor(entry)); + if (ret != ARCHIVE_OK) + return (ret); + break; + default: + return (ARCHIVE_WARN); + } + } + + return (ARCHIVE_OK); +} + +/* XXX TODO: This could be more efficient XXX */ +static ssize_t +archive_write_shar_data_sed(struct archive_write *a, const void *buff, size_t n) +{ + struct shar *shar; + const char *src; + int ret; + size_t written = n; + + shar = (struct shar *)a->format_data; + if (!shar->has_data) + return (0); + + src = (const char *)buff; + ret = ARCHIVE_OK; + shar->outpos = 0; + while (n-- > 0) { + if (shar->end_of_line) { + shar->outbuff[shar->outpos++] = 'X'; + shar->end_of_line = 0; + } + if (*src == '\n') + shar->end_of_line = 1; + shar->outbuff[shar->outpos++] = *src++; + + if (shar->outpos > sizeof(shar->outbuff) - 2) { + ret = (a->compressor.write)(a, shar->outbuff, + shar->outpos); + if (ret != ARCHIVE_OK) + return (ret); + shar->outpos = 0; + } + } + + if (shar->outpos > 0) + ret = (a->compressor.write)(a, shar->outbuff, shar->outpos); + if (ret != ARCHIVE_OK) + return (ret); + return (written); +} + +#define UUENC(c) (((c)!=0) ? ((c) & 077) + ' ': '`') + +/* XXX This could be a lot more efficient. XXX */ +static void +uuencode_group(struct shar *shar) +{ + int t; + + t = 0; + if (shar->uuavail > 0) + t = 0xff0000 & (shar->uubuffer[0] << 16); + if (shar->uuavail > 1) + t |= 0x00ff00 & (shar->uubuffer[1] << 8); + if (shar->uuavail > 2) + t |= 0x0000ff & (shar->uubuffer[2]); + shar->outbuff[shar->outpos++] = UUENC( 0x3f & (t>>18) ); + shar->outbuff[shar->outpos++] = UUENC( 0x3f & (t>>12) ); + shar->outbuff[shar->outpos++] = UUENC( 0x3f & (t>>6) ); + shar->outbuff[shar->outpos++] = UUENC( 0x3f & (t) ); + shar->uuavail = 0; + shar->outbytes += shar->uuavail; + shar->outbuff[shar->outpos] = 0; +} + +static ssize_t +archive_write_shar_data_uuencode(struct archive_write *a, const void *buff, + size_t length) +{ + struct shar *shar; + const char *src; + size_t n; + int ret; + + shar = (struct shar *)a->format_data; + if (!shar->has_data) + return (ARCHIVE_OK); + src = (const char *)buff; + n = length; + while (n-- > 0) { + if (shar->uuavail == 3) + uuencode_group(shar); + if (shar->outpos >= 60) { + ret = shar_printf(a, "%c%s\n", UUENC(shar->outbytes), + shar->outbuff); + if (ret != ARCHIVE_OK) + return (ret); + shar->outpos = 0; + shar->outbytes = 0; + } + + shar->uubuffer[shar->uuavail++] = *src++; + shar->outbytes++; + } + return (length); +} + +static int +archive_write_shar_finish_entry(struct archive_write *a) +{ + const char *g, *p, *u; + struct shar *shar; + int ret; + + shar = (struct shar *)a->format_data; + if (shar->entry == NULL) + return (0); + + if (shar->dump) { + /* Finish uuencoded data. */ + if (shar->has_data) { + if (shar->uuavail > 0) + uuencode_group(shar); + if (shar->outpos > 0) { + ret = shar_printf(a, "%c%s\n", + UUENC(shar->outbytes), shar->outbuff); + if (ret != ARCHIVE_OK) + return (ret); + shar->outpos = 0; + shar->uuavail = 0; + shar->outbytes = 0; + } + ret = shar_printf(a, "%c\n", UUENC(0)); + if (ret != ARCHIVE_OK) + return (ret); + ret = shar_printf(a, "end\n", UUENC(0)); + if (ret != ARCHIVE_OK) + return (ret); + ret = shar_printf(a, "SHAR_END\n"); + if (ret != ARCHIVE_OK) + return (ret); + } + /* Restore file mode, owner, flags. */ + /* + * TODO: Don't immediately restore mode for + * directories; defer that to end of script. + */ + ret = shar_printf(a, "chmod %o %s\n", + archive_entry_mode(shar->entry) & 07777, + archive_entry_pathname(shar->entry)); + if (ret != ARCHIVE_OK) + return (ret); + + u = archive_entry_uname(shar->entry); + g = archive_entry_gname(shar->entry); + if (u != NULL || g != NULL) { + ret = shar_printf(a, "chown %s%s%s %s\n", + (u != NULL) ? u : "", + (g != NULL) ? ":" : "", (g != NULL) ? g : "", + archive_entry_pathname(shar->entry)); + if (ret != ARCHIVE_OK) + return (ret); + } + + if ((p = archive_entry_fflags_text(shar->entry)) != NULL) { + ret = shar_printf(a, "chflags %s %s\n", p, + archive_entry_pathname(shar->entry)); + if (ret != ARCHIVE_OK) + return (ret); + } + + /* TODO: restore ACLs */ + + } else { + if (shar->has_data) { + /* Finish sed-encoded data: ensure last line ends. */ + if (!shar->end_of_line) { + ret = shar_printf(a, "\n"); + if (ret != ARCHIVE_OK) + return (ret); + } + ret = shar_printf(a, "SHAR_END\n"); + if (ret != ARCHIVE_OK) + return (ret); + } + } + + archive_entry_free(shar->entry); + shar->entry = NULL; + return (0); +} + +static int +archive_write_shar_finish(struct archive_write *a) +{ + struct shar *shar; + int ret; + + /* + * TODO: Accumulate list of directory names/modes and + * fix them all up at end-of-archive. + */ + + shar = (struct shar *)a->format_data; + + /* + * Only write the end-of-archive markers if the archive was + * actually started. This avoids problems if someone sets + * shar format, then sets another format (which would invoke + * shar_finish to free the format-specific data). + */ + if (shar->wrote_header) { + ret = shar_printf(a, "exit\n"); + if (ret != ARCHIVE_OK) + return (ret); + /* Shar output is never padded. */ + archive_write_set_bytes_in_last_block(&a->archive, 1); + /* + * TODO: shar should also suppress padding of + * uncompressed data within gzip/bzip2 streams. + */ + } + return (ARCHIVE_OK); +} + +static int +archive_write_shar_destroy(struct archive_write *a) +{ + struct shar *shar; + + shar = (struct shar *)a->format_data; + if (shar->entry != NULL) + archive_entry_free(shar->entry); + if (shar->last_dir != NULL) + free(shar->last_dir); + archive_string_free(&(shar->work)); + free(shar); + a->format_data = NULL; + return (ARCHIVE_OK); +} diff --git a/contrib/libarchive-2.1/libarchive/archive_write_set_format_ustar.c b/contrib/libarchive-2.1/libarchive/archive_write_set_format_ustar.c new file mode 100644 index 0000000000..32971b5782 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/archive_write_set_format_ustar.c @@ -0,0 +1,558 @@ +/*- + * 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 "archive_platform.h" +__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_ustar.c,v 1.21 2007/04/02 00:34:36 kientzle Exp $"); + +#ifdef HAVE_ERRNO_H +#include +#endif +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "archive.h" +#include "archive_entry.h" +#include "archive_private.h" +#include "archive_write_private.h" + +struct ustar { + uint64_t entry_bytes_remaining; + uint64_t entry_padding; +}; + +/* + * Define structure of POSIX 'ustar' tar header. + */ +#define USTAR_name_offset 0 +#define USTAR_name_size 100 +#define USTAR_mode_offset 100 +#define USTAR_mode_size 6 +#define USTAR_mode_max_size 8 +#define USTAR_uid_offset 108 +#define USTAR_uid_size 6 +#define USTAR_uid_max_size 8 +#define USTAR_gid_offset 116 +#define USTAR_gid_size 6 +#define USTAR_gid_max_size 8 +#define USTAR_size_offset 124 +#define USTAR_size_size 11 +#define USTAR_size_max_size 12 +#define USTAR_mtime_offset 136 +#define USTAR_mtime_size 11 +#define USTAR_mtime_max_size 11 +#define USTAR_checksum_offset 148 +#define USTAR_checksum_size 8 +#define USTAR_typeflag_offset 156 +#define USTAR_typeflag_size 1 +#define USTAR_linkname_offset 157 +#define USTAR_linkname_size 100 +#define USTAR_magic_offset 257 +#define USTAR_magic_size 6 +#define USTAR_version_offset 263 +#define USTAR_version_size 2 +#define USTAR_uname_offset 265 +#define USTAR_uname_size 32 +#define USTAR_gname_offset 297 +#define USTAR_gname_size 32 +#define USTAR_rdevmajor_offset 329 +#define USTAR_rdevmajor_size 6 +#define USTAR_rdevmajor_max_size 8 +#define USTAR_rdevminor_offset 337 +#define USTAR_rdevminor_size 6 +#define USTAR_rdevminor_max_size 8 +#define USTAR_prefix_offset 345 +#define USTAR_prefix_size 155 +#define USTAR_padding_offset 500 +#define USTAR_padding_size 12 + +/* + * A filled-in copy of the header for initialization. + */ +static const char template_header[] = { + /* name: 100 bytes */ + 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, + 0,0,0,0, + /* Mode, space-null termination: 8 bytes */ + '0','0','0','0','0','0', ' ','\0', + /* uid, space-null termination: 8 bytes */ + '0','0','0','0','0','0', ' ','\0', + /* gid, space-null termination: 8 bytes */ + '0','0','0','0','0','0', ' ','\0', + /* size, space termation: 12 bytes */ + '0','0','0','0','0','0','0','0','0','0','0', ' ', + /* mtime, space termation: 12 bytes */ + '0','0','0','0','0','0','0','0','0','0','0', ' ', + /* Initial checksum value: 8 spaces */ + ' ',' ',' ',' ',' ',' ',' ',' ', + /* Typeflag: 1 byte */ + '0', /* '0' = regular file */ + /* Linkname: 100 bytes */ + 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, + 0,0,0,0, + /* Magic: 6 bytes, Version: 2 bytes */ + 'u','s','t','a','r','\0', '0','0', + /* Uname: 32 bytes */ + 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, + /* Gname: 32 bytes */ + 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, + /* rdevmajor + space/null padding: 8 bytes */ + '0','0','0','0','0','0', ' ','\0', + /* rdevminor + space/null padding: 8 bytes */ + '0','0','0','0','0','0', ' ','\0', + /* Prefix: 155 bytes */ + 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0, + /* Padding: 12 bytes */ + 0,0,0,0,0,0,0,0, 0,0,0,0 +}; + +static ssize_t archive_write_ustar_data(struct archive_write *a, const void *buff, + size_t s); +static int archive_write_ustar_destroy(struct archive_write *); +static int archive_write_ustar_finish(struct archive_write *); +static int archive_write_ustar_finish_entry(struct archive_write *); +static int archive_write_ustar_header(struct archive_write *, + struct archive_entry *entry); +static int format_256(int64_t, char *, int); +static int format_number(int64_t, char *, int size, int max, int strict); +static int format_octal(int64_t, char *, int); +static int write_nulls(struct archive_write *a, size_t); + +/* + * Set output format to 'ustar' format. + */ +int +archive_write_set_format_ustar(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + struct ustar *ustar; + + /* If someone else was already registered, unregister them. */ + if (a->format_destroy != NULL) + (a->format_destroy)(a); + + /* Basic internal sanity test. */ + if (sizeof(template_header) != 512) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal: template_header wrong size: %d should be 512", sizeof(template_header)); + return (ARCHIVE_FATAL); + } + + ustar = (struct ustar *)malloc(sizeof(*ustar)); + if (ustar == NULL) { + archive_set_error(&a->archive, ENOMEM, "Can't allocate ustar data"); + return (ARCHIVE_FATAL); + } + memset(ustar, 0, sizeof(*ustar)); + a->format_data = ustar; + + a->pad_uncompressed = 1; /* Mimic gtar in this respect. */ + a->format_write_header = archive_write_ustar_header; + a->format_write_data = archive_write_ustar_data; + a->format_finish = archive_write_ustar_finish; + a->format_destroy = archive_write_ustar_destroy; + a->format_finish_entry = archive_write_ustar_finish_entry; + a->archive_format = ARCHIVE_FORMAT_TAR_USTAR; + a->archive_format_name = "POSIX ustar"; + return (ARCHIVE_OK); +} + +static int +archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry) +{ + char buff[512]; + int ret; + struct ustar *ustar; + + ustar = (struct ustar *)a->format_data; + + /* Only regular files (not hardlinks) have data. */ + if (archive_entry_hardlink(entry) != NULL || + archive_entry_symlink(entry) != NULL || + !(archive_entry_filetype(entry) == AE_IFREG)) + archive_entry_set_size(entry, 0); + + if (AE_IFDIR == archive_entry_mode(entry)) { + const char *p; + char *t; + /* + * Ensure a trailing '/'. Modify the entry so + * the client sees the change. + */ + p = archive_entry_pathname(entry); + if (p[strlen(p) - 1] != '/') { + t = (char *)malloc(strlen(p) + 2); + if (t != NULL) { + strcpy(t, p); + strcat(t, "/"); + archive_entry_copy_pathname(entry, t); + free(t); + } + } + } + + ret = __archive_write_format_header_ustar(a, buff, entry, -1, 1); + if (ret != ARCHIVE_OK) + return (ret); + ret = (a->compressor.write)(a, buff, 512); + if (ret != ARCHIVE_OK) + return (ret); + + ustar->entry_bytes_remaining = archive_entry_size(entry); + ustar->entry_padding = 0x1ff & (-(int64_t)ustar->entry_bytes_remaining); + return (ARCHIVE_OK); +} + +/* + * Format a basic 512-byte "ustar" header. + * + * Returns -1 if format failed (due to field overflow). + * Note that this always formats as much of the header as possible. + * If "strict" is set to zero, it will extend numeric fields as + * necessary (overwriting terminators or using base-256 extensions). + * + * This is exported so that other 'tar' formats can use it. + */ +int +__archive_write_format_header_ustar(struct archive_write *a, char h[512], + struct archive_entry *entry, int tartype, int strict) +{ + unsigned int checksum; + int i, ret; + size_t copy_length; + const char *p, *pp; + int mytartype; + + ret = 0; + mytartype = -1; + /* + * The "template header" already includes the "ustar" + * signature, various end-of-field markers and other required + * elements. + */ + memcpy(h, &template_header, 512); + + /* + * Because the block is already null-filled, and strings + * are allowed to exactly fill their destination (without null), + * I use memcpy(dest, src, strlen()) here a lot to copy strings. + */ + + pp = archive_entry_pathname(entry); + if (strlen(pp) <= USTAR_name_size) + memcpy(h + USTAR_name_offset, pp, strlen(pp)); + else { + /* Store in two pieces, splitting at a '/'. */ + p = strchr(pp + strlen(pp) - USTAR_name_size - 1, '/'); + /* + * If there is no path separator, or the prefix or + * remaining name are too large, return an error. + */ + if (!p) { + archive_set_error(&a->archive, ENAMETOOLONG, + "Pathname too long"); + ret = ARCHIVE_WARN; + } else if (p > pp + USTAR_prefix_size) { + archive_set_error(&a->archive, ENAMETOOLONG, + "Pathname too long"); + ret = ARCHIVE_WARN; + } else { + /* Copy prefix and remainder to appropriate places */ + memcpy(h + USTAR_prefix_offset, pp, p - pp); + memcpy(h + USTAR_name_offset, p + 1, pp + strlen(pp) - p - 1); + } + } + + p = archive_entry_hardlink(entry); + if (p != NULL) + mytartype = '1'; + else + p = archive_entry_symlink(entry); + if (p != NULL && p[0] != '\0') { + copy_length = strlen(p); + if (copy_length > USTAR_linkname_size) { + archive_set_error(&a->archive, ENAMETOOLONG, + "Link contents too long"); + ret = ARCHIVE_WARN; + copy_length = USTAR_linkname_size; + } + memcpy(h + USTAR_linkname_offset, p, copy_length); + } + + p = archive_entry_uname(entry); + if (p != NULL && p[0] != '\0') { + copy_length = strlen(p); + if (copy_length > USTAR_uname_size) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Username too long"); + ret = ARCHIVE_WARN; + copy_length = USTAR_uname_size; + } + memcpy(h + USTAR_uname_offset, p, copy_length); + } + + p = archive_entry_gname(entry); + if (p != NULL && p[0] != '\0') { + copy_length = strlen(p); + if (strlen(p) > USTAR_gname_size) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Group name too long"); + ret = ARCHIVE_WARN; + copy_length = USTAR_gname_size; + } + memcpy(h + USTAR_gname_offset, p, copy_length); + } + + if (format_number(archive_entry_mode(entry) & 07777, h + USTAR_mode_offset, USTAR_mode_size, USTAR_mode_max_size, strict)) { + archive_set_error(&a->archive, ERANGE, "Numeric mode too large"); + ret = ARCHIVE_WARN; + } + + if (format_number(archive_entry_uid(entry), h + USTAR_uid_offset, USTAR_uid_size, USTAR_uid_max_size, strict)) { + archive_set_error(&a->archive, ERANGE, "Numeric user ID too large"); + ret = ARCHIVE_WARN; + } + + if (format_number(archive_entry_gid(entry), h + USTAR_gid_offset, USTAR_gid_size, USTAR_gid_max_size, strict)) { + archive_set_error(&a->archive, ERANGE, "Numeric group ID too large"); + ret = ARCHIVE_WARN; + } + + if (format_number(archive_entry_size(entry), h + USTAR_size_offset, USTAR_size_size, USTAR_size_max_size, strict)) { + archive_set_error(&a->archive, ERANGE, "File size out of range"); + ret = ARCHIVE_WARN; + } + + if (format_number(archive_entry_mtime(entry), h + USTAR_mtime_offset, USTAR_mtime_size, USTAR_mtime_max_size, strict)) { + archive_set_error(&a->archive, ERANGE, + "File modification time too large"); + ret = ARCHIVE_WARN; + } + + if (archive_entry_filetype(entry) == AE_IFBLK + || archive_entry_filetype(entry) == AE_IFCHR) { + if (format_number(archive_entry_rdevmajor(entry), h + USTAR_rdevmajor_offset, + USTAR_rdevmajor_size, USTAR_rdevmajor_max_size, strict)) { + archive_set_error(&a->archive, ERANGE, + "Major device number too large"); + ret = ARCHIVE_WARN; + } + + if (format_number(archive_entry_rdevminor(entry), h + USTAR_rdevminor_offset, + USTAR_rdevminor_size, USTAR_rdevminor_max_size, strict)) { + archive_set_error(&a->archive, ERANGE, + "Minor device number too large"); + ret = ARCHIVE_WARN; + } + } + + if (tartype >= 0) { + h[USTAR_typeflag_offset] = tartype; + } else if (mytartype >= 0) { + h[USTAR_typeflag_offset] = mytartype; + } else { + switch (archive_entry_filetype(entry)) { + case AE_IFREG: h[USTAR_typeflag_offset] = '0' ; break; + case AE_IFLNK: h[USTAR_typeflag_offset] = '2' ; break; + case AE_IFCHR: h[USTAR_typeflag_offset] = '3' ; break; + case AE_IFBLK: h[USTAR_typeflag_offset] = '4' ; break; + case AE_IFDIR: h[USTAR_typeflag_offset] = '5' ; break; + case AE_IFIFO: h[USTAR_typeflag_offset] = '6' ; break; + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "tar format cannot archive this (mode=0%lo)", + (unsigned long)archive_entry_mode(entry)); + ret = ARCHIVE_WARN; + } + } + + checksum = 0; + for (i = 0; i < 512; i++) + checksum += 255 & (unsigned int)h[i]; + h[USTAR_checksum_offset + 6] = '\0'; /* Can't be pre-set in the template. */ + /* h[USTAR_checksum_offset + 7] = ' '; */ /* This is pre-set in the template. */ + format_octal(checksum, h + USTAR_checksum_offset, 6); + return (ret); +} + +/* + * Format a number into a field, with some intelligence. + */ +static int +format_number(int64_t v, char *p, int s, int maxsize, int strict) +{ + int64_t limit; + + limit = ((int64_t)1 << (s*3)); + + /* "Strict" only permits octal values with proper termination. */ + if (strict) + return (format_octal(v, p, s)); + + /* + * In non-strict mode, we allow the number to overwrite one or + * more bytes of the field termination. Even old tar + * implementations should be able to handle this with no + * problem. + */ + if (v >= 0) { + while (s <= maxsize) { + if (v < limit) + return (format_octal(v, p, s)); + s++; + limit <<= 3; + } + } + + /* Base-256 can handle any number, positive or negative. */ + return (format_256(v, p, maxsize)); +} + +/* + * Format a number into the specified field using base-256. + */ +static int +format_256(int64_t v, char *p, int s) +{ + p += s; + while (s-- > 0) { + *--p = (char)(v & 0xff); + v >>= 8; + } + *p |= 0x80; /* Set the base-256 marker bit. */ + return (0); +} + +/* + * Format a number into the specified field. + */ +static int +format_octal(int64_t v, char *p, int s) +{ + int len; + + len = s; + + /* Octal values can't be negative, so use 0. */ + if (v < 0) { + while (len-- > 0) + *p++ = '0'; + return (-1); + } + + p += s; /* Start at the end and work backwards. */ + while (s-- > 0) { + *--p = (char)('0' + (v & 7)); + v >>= 3; + } + + if (v == 0) + return (0); + + /* If it overflowed, fill field with max value. */ + while (len-- > 0) + *p++ = '7'; + + return (-1); +} + +static int +archive_write_ustar_finish(struct archive_write *a) +{ + int r; + + if (a->compressor.write == NULL) + return (ARCHIVE_OK); + + r = write_nulls(a, 512*2); + return (r); +} + +static int +archive_write_ustar_destroy(struct archive_write *a) +{ + struct ustar *ustar; + + ustar = (struct ustar *)a->format_data; + free(ustar); + a->format_data = NULL; + return (ARCHIVE_OK); +} + +static int +archive_write_ustar_finish_entry(struct archive_write *a) +{ + struct ustar *ustar; + int ret; + + ustar = (struct ustar *)a->format_data; + ret = write_nulls(a, + ustar->entry_bytes_remaining + ustar->entry_padding); + ustar->entry_bytes_remaining = ustar->entry_padding = 0; + return (ret); +} + +static int +write_nulls(struct archive_write *a, size_t padding) +{ + int ret; + size_t to_write; + + while (padding > 0) { + to_write = padding < a->null_length ? padding : a->null_length; + ret = (a->compressor.write)(a, a->nulls, to_write); + if (ret != ARCHIVE_OK) + return (ret); + padding -= to_write; + } + return (ARCHIVE_OK); +} + +static ssize_t +archive_write_ustar_data(struct archive_write *a, const void *buff, size_t s) +{ + struct ustar *ustar; + int ret; + + ustar = (struct ustar *)a->format_data; + if (s > ustar->entry_bytes_remaining) + s = ustar->entry_bytes_remaining; + ret = (a->compressor.write)(a, buff, s); + ustar->entry_bytes_remaining -= s; + if (ret != ARCHIVE_OK) + return (ret); + return (s); +} diff --git a/contrib/libarchive-2.1/libarchive/config_freebsd.h b/contrib/libarchive-2.1/libarchive/config_freebsd.h new file mode 100644 index 0000000000..bec01b10fa --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/config_freebsd.h @@ -0,0 +1,109 @@ +/*- + * 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. + * + * $FreeBSD: src/lib/libarchive/config_freebsd.h,v 1.4 2007/04/15 00:53:38 kientzle Exp $ + */ + +/* FreeBSD 5.0 and later have ACL support. */ +#if __FreeBSD__ > 4 +#define HAVE_ACL_CREATE_ENTRY 1 +#define HAVE_ACL_INIT 1 +#define HAVE_ACL_SET_FD 1 +#define HAVE_ACL_SET_FD_NP 1 +#define HAVE_ACL_SET_FILE 1 +#define HAVE_ACL_USER 1 +#endif + +#define HAVE_BZLIB_H 1 +#define HAVE_CHFLAGS 1 +#define HAVE_DECL_INT64_MAX 1 +#define HAVE_DECL_INT64_MIN 1 +#define HAVE_DECL_SIZE_MAX 1 +#define HAVE_DECL_STRERROR_R 1 +#define HAVE_DECL_UINT32_MAX 1 +#define HAVE_DECL_UINT64_MAX 1 +#define HAVE_EFTYPE 1 +#define HAVE_EILSEQ 1 +#define HAVE_ERRNO_H 1 +#define HAVE_FCHDIR 1 +#define HAVE_FCHFLAGS 1 +#define HAVE_FCHMOD 1 +#define HAVE_FCHOWN 1 +#define HAVE_FCNTL_H 1 +#define HAVE_FSEEKO 1 +#define HAVE_FUTIMES 1 +#define HAVE_GRP_H 1 +#define HAVE_INTTYPES_H 1 +#define HAVE_LCHFLAGS 1 +#define HAVE_LCHMOD 1 +#define HAVE_LCHOWN 1 +#define HAVE_LIMITS_H 1 +#define HAVE_LUTIMES 1 +#define HAVE_MALLOC 1 +#define HAVE_MEMMOVE 1 +#define HAVE_MEMSET 1 +#define HAVE_MKDIR 1 +#define HAVE_MKFIFO 1 +#define HAVE_POLL 1 +#define HAVE_POLL_H 1 +#define HAVE_PWD_H 1 +#define HAVE_SELECT 1 +#define HAVE_STDINT_H 1 +#define HAVE_STDLIB_H 1 +#define HAVE_STRCHR 1 +#define HAVE_STRDUP 1 +#define HAVE_STRERROR 1 +#define HAVE_STRERROR_R 1 +#define HAVE_STRINGS_H 1 +#define HAVE_STRING_H 1 +#define HAVE_STRRCHR 1 +#define HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1 +#define HAVE_STRUCT_STAT_ST_RDEV 1 +#define HAVE_STRUCT_TM_TM_GMTOFF 1 +#define HAVE_SYS_ACL_H 1 +#define HAVE_SYS_IOCTL_H 1 +#define HAVE_SYS_SELECT_H 1 +#define HAVE_SYS_STAT_H 1 +#define HAVE_SYS_TIME_H 1 +#define HAVE_SYS_TYPES_H 1 +#define HAVE_SYS_WAIT_H 1 +#define HAVE_TIMEGM 1 +#define HAVE_UNISTD_H 1 +#define HAVE_UTIME 1 +#define HAVE_UTIMES 1 +#define HAVE_UTIME_H 1 +#define HAVE_WCHAR_H 1 +#define HAVE_WCSCPY 1 +#define HAVE_WCSLEN 1 +#define HAVE_WMEMCMP 1 +#define HAVE_WMEMCPY 1 +#define HAVE_ZLIB_H 1 +#define STDC_HEADERS 1 +#define TIME_WITH_SYS_TIME 1 + +/* FreeBSD 4 and earlier lack intmax_t/uintmax_t */ +#if __FreeBSD__ < 5 +#define intmax_t int64_t +#define uintmax_t uint64_t +#endif diff --git a/contrib/libarchive-2.1/libarchive/config_windows.h b/contrib/libarchive-2.1/libarchive/config_windows.h new file mode 100644 index 0000000000..074bb3bdf8 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/config_windows.h @@ -0,0 +1,171 @@ +/*- + * Copyright (c) 2003-2006 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 + * in this position and unchanged. + * 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. + * + * $FreeBSD$ + */ + +/* Start of configuration for native Win32 with Visual Studio. */ +/* TODO: Fix this. */ +#undef HAVE_ACL_CREATE_ENTRY +#undef HAVE_ACL_INIT +#undef HAVE_ACL_SET_FD +#undef HAVE_ACL_SET_FD_NP +#undef HAVE_ACL_SET_FILE +#undef HAVE_ACL_USER +#undef HAVE_BZLIB_H +#undef HAVE_CHFLAGS +#define HAVE_DECL_INT64_MAX 1 +#define HAVE_DECL_INT64_MIN 1 +#define HAVE_DECL_SIZE_MAX 1 +#define HAVE_DECL_STRERROR_R 1 +#define HAVE_DECL_UINT32_MAX 1 +#define HAVE_DECL_UINT64_MAX 1 +#define HAVE_EFTYPE 1 +#define HAVE_EILSEQ 1 +#define HAVE_ERRNO_H 1 +#undef HAVE_FCHDIR +#undef HAVE_FCHFLAGS +#undef HAVE_FCHMOD +#undef HAVE_FCHOWN +#define HAVE_FCNTL_H 1 +#undef HAVE_FSEEKO +#undef HAVE_FUTIMES +#undef HAVE_GRP_H +#undef HAVE_INTTYPES_H +#undef HAVE_LCHFLAGS +#undef HAVE_LCHMOD +#undef HAVE_LCHOWN +#define HAVE_LIMITS_H 1 +#undef HAVE_LUTIMES +#define HAVE_MALLOC 1 +#define HAVE_MEMMOVE 1 +#define HAVE_MEMORY_H 1 +#define HAVE_MEMSET 1 +#define HAVE_MKDIR 1 +#undef HAVE_MKFIFO +#undef HAVE_PATHS_H +#undef HAVE_POLL +#undef HAVE_POLL_H +#undef HAVE_PWD_H +#undef HAVE_SELECT +#undef HAVE_STDINT_H +#define HAVE_STDLIB_H 1 +#define HAVE_STRCHR 1 +#define HAVE_STRDUP 1 +#define HAVE_STRERROR 1 +#undef HAVE_STRERROR_R +#define HAVE_STRINGS_H 1 +#define HAVE_STRING_H 1 +#define HAVE_STRRCHR 1 +#undef HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC +#undef HAVE_STRUCT_STAT_ST_RDEV +#undef HAVE_STRUCT_TM_TM_GMTOFF +#undef HAVE_SYS_ACL_H +#undef HAVE_SYS_IOCTL_H +#undef HAVE_SYS_SELECT_H +#define HAVE_SYS_STAT_H 1 +#undef HAVE_SYS_TIME_H +#define HAVE_SYS_TYPES_H 1 +#undef HAVE_SYS_WAIT_H +#undef HAVE_TIMEGM +#undef HAVE_UNISTD_H +#undef HAVE_UTIME +#undef HAVE_UTIMES +#undef HAVE_UTIME_H +#define HAVE_WCHAR_H 1 +#define HAVE_WCSCPY 1 +#define HAVE_WCSLEN 1 +#define HAVE_WMEMCMP 1 +#define HAVE_WMEMCPY 1 +#undef HAVE_ZLIB_H +#define STDC_HEADERS 1 +#define TIME_WITH_SYS_TIME 1 + +/* + * TODO: libarchive relies heavily on having the file type + * encoded as part of the mode value. Windows kind-of, sort-of + * supports this, but the following needs to be carefully compared + * to Windows conventions and quite possibly changed extensively. + */ +#define S_IFIFO 0010000 /* named pipe (fifo) */ +//#define S_IFCHR 0020000 /* character special */ +//#define S_IFDIR 0040000 /* directory */ +#define S_IFBLK 0060000 /* block special */ +//#define S_IFREG 0100000 /* regular */ +#define S_IFLNK 0120000 /* symbolic link */ +#define S_IFSOCK 0140000 /* socket */ +#define S_ISVTX 0001000 /* save swapped text even after use */ +//#define S_ISUID +//#define S_ISGID +//#define PATH_MAX +#define S_IRWXU 0700 +#define S_IRWXG 0070 +#define S_IRWXO 0007 +#define S_ISDIR(m) (((m) & 0170000) == S_IFDIR) /* directory */ +#define S_ISCHR(m) (((m) & 0170000) == S_IFCHR) /* char special */ +#define S_ISBLK(m) (((m) & 0170000) == S_IFBLK) /* block special */ +#define S_ISREG(m) (((m) & 0170000) == S_IFREG) /* regular file */ +#define S_ISFIFO(m) (((m) & 0170000) == S_IFIFO) /* fifo or socket */ +#define S_ISLNK(m) (((m) & 0170000) == S_IFLNK) /* symbolic link */ +#define S_ISSOCK(m) (((m) & 0170000) == S_IFSOCK) /* socket */ + +/* Basic definitions for system and integer types. */ +typedef int uid_t; +typedef int gid_t; +typedef int id_t; +typedef unsigned short mode_t; +typedef unsigned _int64 uint64_t; +typedef unsigned _int16 uint16_t; +typedef uint64_t uintmax_t; +typedef _int64 intmax_t; + +/* Replacement for major/minor/makedev. */ +#define major(x) ((int)(0x00ff & ((x) >> 8))) +#define minor(x) ((int)(0xffff00ff & (x))) +#define makedev(maj,min) ((0xff00 & ((maj)<<8))|(0xffff00ff & (min))) + +#define EFTYPE 7 +#define STDERR_FILENO 2 + +/* Alias the Windows _function to the POSIX equivalent. */ +#include +#define write _write +#define read _read +#define lseek _lseek +#define open _open +#define chdir _chdir +#define mkdir _mkdir +#define close _close + +#define PACKAGE_NAME "libarchive" +#define PACKAGE_VERSION "2.0experimental" + +/* TODO: Fix the code, don't suppress the warnings. */ +#pragma warning(disable:4996) +#pragma warning(disable:4244) +#pragma warning(disable:4305) +#pragma warning(disable:4267) + +/* End of Win32/Visual Studio definitions. */ diff --git a/contrib/libarchive-2.1/libarchive/filter_fork.c b/contrib/libarchive-2.1/libarchive/filter_fork.c new file mode 100644 index 0000000000..a7ee48b463 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/filter_fork.c @@ -0,0 +1,137 @@ +/*- + * Copyright (c) 2007 Joerg Sonnenberger + * 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 "archive_platform.h" + +__FBSDID("$FreeBSD$"); + +#if defined(HAVE_POLL) +# if defined(HAVE_POLL_H) +# include +# endif +#elif defined(HAVE_SELECT) +# if defined(HAVE_SYS_SELECT_H) +# include +# elif defined(HAVE_UNISTD_H) +# include +# endif +#endif +#ifdef HAVE_FCNTL_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif + +#include "filter_fork.h" + +pid_t +__archive_create_child(const char *path, int *child_stdin, int *child_stdout) +{ + pid_t child; + int stdin_pipe[2], stdout_pipe[2], tmp; + + if (pipe(stdin_pipe) == -1) + goto state_allocated; + if (stdin_pipe[0] == STDOUT_FILENO) { + if ((tmp = dup(stdin_pipe[0])) == -1) + goto stdin_opened; + close(stdin_pipe[0]); + stdin_pipe[0] = tmp; + } + if (pipe(stdout_pipe) == -1) + goto stdin_opened; + if (stdout_pipe[1] == STDIN_FILENO) { + if ((tmp = dup(stdout_pipe[1])) == -1) + goto stdout_opened; + close(stdout_pipe[1]); + stdout_pipe[1] = tmp; + } + + switch ((child = vfork())) { + case -1: + goto stdout_opened; + case 0: + close(stdin_pipe[1]); + close(stdout_pipe[0]); + if (dup2(stdin_pipe[0], STDIN_FILENO) == -1) + _exit(254); + if (stdin_pipe[0] != STDIN_FILENO) + close(stdin_pipe[0]); + if (dup2(stdout_pipe[1], STDOUT_FILENO) == -1) + _exit(254); + if (stdout_pipe[1] != STDOUT_FILENO) + close(stdout_pipe[1]); + execlp(path, path, (char *)NULL); + _exit(254); + default: + close(stdin_pipe[0]); + close(stdout_pipe[1]); + + *child_stdin = stdin_pipe[1]; + fcntl(*child_stdin, F_SETFL, O_NONBLOCK); + *child_stdout = stdout_pipe[0]; + fcntl(*child_stdout, F_SETFL, O_NONBLOCK); + } + + return child; + +stdout_opened: + close(stdout_pipe[0]); + close(stdout_pipe[1]); +stdin_opened: + close(stdin_pipe[0]); + close(stdin_pipe[1]); +state_allocated: + return -1; +} + +void +__archive_check_child(int in, int out) +{ +#if defined(HAVE_POLL) + struct pollfd fds[2]; + + fds[0].fd = in; + fds[0].events = POLLOUT; + fds[1].fd = out; + fds[1].events = POLLIN; + + poll(fds, 2, -1); /* -1 == INFTIM, wait forever */ +#elif defined(HAVE_SELECT) + fd_set fds_in, fds_out, fds_error; + + FD_ZERO(&fds_in); + FD_SET(out, &fds_in); + FD_ZERO(&fds_out); + FD_SET(in, &fds_out); + FD_ZERO(&fds_error); + FD_SET(in, &fds_error); + FD_SET(out, &fds_error); + select(in < out ? out + 1 : in + 1, &fds_in, &fds_out, &fds_error, NULL); +#else + sleep(1); +#endif +} diff --git a/contrib/libarchive-2.1/libarchive/filter_fork.h b/contrib/libarchive-2.1/libarchive/filter_fork.h new file mode 100644 index 0000000000..c4c3c752f2 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/filter_fork.h @@ -0,0 +1,35 @@ +/*- + * Copyright (c) 2007 Joerg Sonnenberger + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FILTER_FORK_H +#define FILTER_FORK_H + +pid_t +__archive_create_child(const char *path, int *child_stdin, int *child_stdout); + +void +__archive_check_child(int in, int out); + +#endif diff --git a/contrib/libarchive-2.1/libarchive/libarchive-formats.5 b/contrib/libarchive-2.1/libarchive/libarchive-formats.5 new file mode 100644 index 0000000000..cd34622b2c --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/libarchive-formats.5 @@ -0,0 +1,258 @@ +.\" 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. +.\" +.\" $FreeBSD: src/lib/libarchive/libarchive-formats.5,v 1.14 2007/04/05 05:07:53 kientzle Exp $ +.\" +.Dd April 27, 2004 +.Dt libarchive-formats 3 +.Os +.Sh NAME +.Nm libarchive-formats +.Nd archive formats supported by the libarchive library +.Sh DESCRIPTION +The +.Xr libarchive 3 +library reads and writes a variety of streaming archive formats. +Generally speaking, all of these archive formats consist of a series of +.Dq entries . +Each entry stores a single file system object, such as a file, directory, +or symbolic link. +.Pp +The following provides a brief description of each format supported +by libarchive, with some information about recognized extensions or +limitations of the current library support. +Note that just because a format is supported by libarchive does not +imply that a program that uses libarchive will support that format. +Applications that use libarchive specify which formats they wish +to support. +.Ss Tar Formats +The +.Xr libarchive 3 +library can read most tar archives. +However, it only writes POSIX-standard +.Dq ustar +and +.Dq pax interchange +formats. +.Pp +All tar formats store each entry in one or more 512-byte records. +The first record is used for file metadata, including filename, +timestamp, and mode information, and the file data is stored in +subsequent records. +Later variants have extended this by either appropriating undefined +areas of the header record, extending the header to multiple records, +or by storing special entries that modify the interpretation of +subsequent entries. +.Pp +.Bl -tag -width indent +.It Cm gnutar +The +.Xr libarchive 3 +library can read GNU-format tar archives. +It currently supports the most popular GNU extensions, including +modern long filename and linkname support, as well as atime and ctime data. +The libarchive library does not support multi-volume +archives, nor the old GNU long filename format. +.It Cm pax +The +.Xr libarchive 3 +library can read and write POSIX-compliant pax interchange format +archives. +Pax interchange format archives are an extension of the older ustar +format that adds a separate entry with additional attributes stored +as key/value pairs. +The presence of this additional entry is the only difference between +pax interchange format and the older ustar format. +The extended attributes are of unlimited length and are stored +as UTF-8 Unicode strings. +Keywords defined in the standard are in all lowercase; vendors are allowed +to define custom keys by preceding them with the vendor name in all uppercase. +When writing pax archives, libarchive uses many of the SCHILY keys +defined by Joerg Schilling's +.Dq star +archiver. +The libarchive library can read most of the SCHILY keys. +It ignores any keywords that it does not understand. +.It Cm restricted pax +The libarchive library can also write pax archives in which it +attempts to suppress the extended attributes entry whenever +possible. +The result will be identical to a ustar archive unless the +extended attributes entry is required to store a long file +name, long linkname, extended ACL, file flags, or if any of the standard +ustar data (user name, group name, UID, GID, etc) cannot be fully +represented in the ustar header. +In all cases, the result can be dearchived by any program that +can read POSIX-compliant pax interchange format archives. +Programs that correctly read ustar format (see below) will also be +able to read this format; any extended attributes will be extracted as +separate files stored in +.Pa PaxHeader +directories. +.It Cm ustar +The libarchive library can both read and write this format. +This format has the following limitations: +.Bl -bullet -compact +.It +Device major and minor numbers are limited to 21 bits. +Nodes with larger numbers will not be added to the archive. +.It +Path names in the archive are limited to 255 bytes. +(Shorter if there is no / character in exactly the right place.) +.It +Symbolic links and hard links are stored in the archive with +the name of the referenced file. +This name is limited to 100 bytes. +.It +Extended attributes, file flags, and other extended +security information cannot be stored. +.It +Archive entries are limited to 2 gigabytes in size. +.El +Note that the pax interchange format has none of these restrictions. +.El +.Pp +The libarchive library can also read a variety of commonly-used extensions to +the basic tar format. +In particular, it supports base-256 values in certain numeric fields. +This essentially removes the limitations on file size, modification time, +and device numbers. +.Pp +The first tar program appeared in Sixth Edition Unix (circa 1976). +This makes the tar format one of the oldest and most widely-supported +archive formats. +The first official standard for the tar file format was the +.Dq ustar +(Unix Standard Tar) format defined by POSIX in 1988. +POSIX.1-2001 extended the ustar format to create the +.Dq pax interchange +format. +There have also been many custom variations. +.Ss Cpio Formats +The libarchive library can read a number of common cpio variants and can write +.Dq odc +format archives. +A cpio archive stores each entry as a fixed-size header followed +by a variable-length filename and variable-length data. +Unlike tar, cpio does only minimal padding of the header or file data. +There are a variety of cpio formats, which differ primarily in +how they store the initial header: some store the values as +octal or hexadecimal numbers in ASCII, others as binary values of +varying byte order and length. +.Bl -tag -width indent +.It Cm binary +The libarchive library can read both big-endian and little-endian +variants of the original binary cpio format. +This format used 32-bit binary values for file size and mtime, +and 16-bit binary values for the other fields. +.It Cm odc +The libarchive library can both read and write this +POSIX-standard format. +This format stores the header contents as octal values in ASCII. +It is standard, portable, and immune from byte-order confusion. +File sizes and mtime are limited to 33 bits (8GB file size), +other fields are limited to 18 bits. +.It Cm SVR4 +The libarchive library can read both CRC and non-CRC variants of +this format. +The SVR4 format uses eight-digit hexadecimal values for +all header fields. +This limits file size to 4GB, and also limits the mtime and +other fields to 32 bits. +The SVR4 format can optionally include a CRC of the file +contents, although libarchive does not currently verify this CRC. +.El +.Pp +Cpio is an old format that was widely used because of its simplicity +and its support for very long filenames. +Unfortunately, it has many limitations that make it unsuitable +for widespread use. +Only the POSIX format permits files over 4GB, and its 18-bit +limit for most other fields makes it unsuitable for modern systems. +In addition, cpio formats only store numeric UID/GID values (not +usernames and group names), which can make it very difficult to correctly +transfer archives across systems. +.Ss Shar Formats +A +.Dq shell archive +is a shell script that, when executed on a POSIX-compliant +system, will recreate a collection of file system objects. +The libarchive library can write two different kinds of shar archives: +.Bl -tag -width indent +.It Cm shar +The traditional shar format uses a limited set of POSIX +commands, including +.Xr echo 1 , +.Xr mkdir 1 , +and +.Xr sed 1 . +It is suitable for portably archiving small collections of plain text files. +However, it is not generally well-suited for large archives +(many implementations of +.Xr sh 1 +have limits on the size of a script) nor should it be used with non-text files. +.It Cm shardump +This format is similar to shar but encodes files using +.Xr uuencode 1 +so that the result will be a plain text file regardless of the file contents. +It also includes additional shell commands that attempt to reproduce as +many file attributes as possible, including owner, mode, and flags. +The additional commands used to restore file attributes make +shardump archives less portable than plain shar archives. +.El +.Ss ISO9660 format +Libarchive can read and extract from files containing ISO9660-compliant +CDROM images. +It also has partial support for Rockridge extensions. +In many cases, this can remove the need to burn a physical CDROM. +It also avoids security and complexity issues that come with +virtual mounts and loopback devices. +.Ss Zip format +Libarchive can extract from most zip format archives. +It currently only supports uncompressed entries and entries +compressed with the +.Dq deflate +algorithm. +Older zip compression algorithms are not supported. +.Ss Archive (library) file format +The Unix archive format (commonly created by the +.Xr ar 1 +archiver) is a general-purpose format which is +used almost exclusively for object files to be +read by the link editor +.Xr ld 1 . +The ar format has never been standardised. +There are two common variants: +the GNU format derived from SVR4, +and the BSD format, which first appeared in 4.4BSD. +Libarchive provides read and write support for both variants. +.Sh SEE ALSO +.Xr ar 1 , +.Xr cpio 1 , +.Xr mkisofs 1 , +.Xr shar 1 , +.Xr tar 1 , +.Xr zip 1 , +.Xr zlib 3 , +.Xr tar 5 diff --git a/contrib/libarchive-2.1/libarchive/libarchive.3 b/contrib/libarchive-2.1/libarchive/libarchive.3 new file mode 100644 index 0000000000..8c19d008a9 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/libarchive.3 @@ -0,0 +1,331 @@ +.\" 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. +.\" +.\" $FreeBSD: src/lib/libarchive/libarchive.3,v 1.11 2007/01/09 08:05:56 kientzle Exp $ +.\" +.Dd August 19, 2006 +.Dt LIBARCHIVE 3 +.Os +.Sh NAME +.Nm libarchive +.Nd functions for reading and writing streaming archives +.Sh LIBRARY +.Lb libarchive +.Sh OVERVIEW +The +.Nm +library provides a flexible interface for reading and writing +streaming archive files such as tar and cpio. +The library is inherently stream-oriented; readers serially iterate through +the archive, writers serially add things to the archive. +In particular, note that there is no built-in support for +random access nor for in-place modification. +.Pp +When reading an archive, the library automatically detects the +format and the compression. +The library currently has read support for: +.Bl -bullet -compact +.It +old-style tar archives, +.It +most variants of the POSIX +.Dq ustar +format, +.It +the POSIX +.Dq pax interchange +format, +.It +GNU-format tar archives, +.It +most common cpio archive formats, +.It +ISO9660 CD images (with or without RockRidge extensions), +.It +Zip archives. +.El +The library automatically detects archives compressed with +.Xr gzip 1 , +.Xr bzip2 1 , +or +.Xr compress 1 +and decompresses them transparently. +.Pp +When writing an archive, you can specify the compression +to be used and the format to use. +The library can write +.Bl -bullet -compact +.It +POSIX-standard +.Dq ustar +archives, +.It +POSIX +.Dq pax interchange format +archives, +.It +POSIX octet-oriented cpio archives, +.It +two different variants of shar archives. +.El +Pax interchange format is an extension of the tar archive format that +eliminates essentially all of the limitations of historic tar formats +in a standard fashion that is supported +by POSIX-compliant +.Xr pax 1 +implementations on many systems as well as several newer implementations of +.Xr tar 1 . +Note that the default write format will suppress the pax extended +attributes for most entries; explicitly requesting pax format will +enable those attributes for all entries. +.Pp +The read and write APIs are accessed through the +.Fn archive_read_XXX +functions and the +.Fn archive_write_XXX +functions, respectively, and either can be used independently +of the other. +.Pp +The rest of this manual page provides an overview of the library +operation. +More detailed information can be found in the individual manual +pages for each API or utility function. +.Sh READING AN ARCHIVE +To read an archive, you must first obtain an initialized +.Tn struct archive +object from +.Fn archive_read_new . +You can then modify this object for the desired operations with the +various +.Fn archive_read_set_XXX +and +.Fn archive_read_support_XXX +functions. +In particular, you will need to invoke appropriate +.Fn archive_read_support_XXX +functions to enable the corresponding compression and format +support. +Note that these latter functions perform two distinct operations: +they cause the corresponding support code to be linked into your +program, and they enable the corresponding auto-detect code. +Unless you have specific constraints, you will generally want +to invoke +.Fn archive_read_support_compression_all +and +.Fn archive_read_support_format_all +to enable auto-detect for all formats and compression types +currently supported by the library. +.Pp +Once you have prepared the +.Tn struct archive +object, you call +.Fn archive_read_open +to actually open the archive and prepare it for reading. +There are several variants of this function; +the most basic expects you to provide pointers to several +functions that can provide blocks of bytes from the archive. +There are convenience forms that allow you to +specify a filename, file descriptor, +.Ft "FILE *" +object, or a block of memory from which to read the archive data. +Note that the core library makes no assumptions about the +size of the blocks read; +callback functions are free to read whatever block size is +most appropriate for the medium. +.Pp +Each archive entry consists of a header followed by a certain +amount of data. +You can obtain the next header with +.Fn archive_read_next_header , +which returns a pointer to an +.Tn struct archive_entry +structure with information about the current archive element. +If the entry is a regular file, then the header will be followed +by the file data. +You can use +.Fn archive_read_data +(which works much like the +.Xr read 2 +system call) +to read this data from the archive. +You may prefer to use the higher-level +.Fn archive_read_data_skip , +which reads and discards the data for this entry, +.Fn archive_read_data_to_buffer , +which reads the data into an in-memory buffer, +.Fn archive_read_data_to_file , +which copies the data to the provided file descriptor, or +.Fn archive_read_extract , +which recreates the specified entry on disk and copies data +from the archive. +In particular, note that +.Fn archive_read_extract +uses the +.Tn struct archive_entry +structure that you provide it, which may differ from the +entry just read from the archive. +In particular, many applications will want to override the +pathname, file permissions, or ownership. +.Pp +Once you have finished reading data from the archive, you +should call +.Fn archive_read_close +to close the archive, then call +.Fn archive_read_finish +to release all resources, including all memory allocated by the library. +.Pp +The +.Xr archive_read 3 +manual page provides more detailed calling information for this API. +.Sh WRITING AN ARCHIVE +You use a similar process to write an archive. +The +.Fn archive_write_new +function creates an archive object useful for writing, +the various +.Fn archive_write_set_XXX +functions are used to set parameters for writing the archive, and +.Fn archive_write_open +completes the setup and opens the archive for writing. +.Pp +Individual archive entries are written in a three-step +process: +You first initialize a +.Tn struct archive_entry +structure with information about the new entry. +At a minimum, you should set the pathname of the +entry and provide a +.Va struct stat +with a valid +.Va st_mode +field, which specifies the type of object and +.Va st_size +field, which specifies the size of the data portion of the object. +The +.Fn archive_write_header +function actually writes the header data to the archive. +You can then use +.Fn archive_write_data +to write the actual data. +.Pp +After all entries have been written, use the +.Fn archive_write_finish +function to release all resources. +.Pp +The +.Xr archive_write 3 +manual page provides more detailed calling information for this API. +.Sh DESCRIPTION +Detailed descriptions of each function are provided by the +corresponding manual pages. +.Pp +All of the functions utilize an opaque +.Tn struct archive +datatype that provides access to the archive contents. +.Pp +The +.Tn struct archive_entry +structure contains a complete description of a single archive +entry. +It uses an opaque interface that is fully documented in +.Xr archive_entry 3 . +.Pp +Users familiar with historic formats should be aware that the newer +variants have eliminated most restrictions on the length of textual fields. +Clients should not assume that filenames, link names, user names, or +group names are limited in length. +In particular, pax interchange format can easily accommodate pathnames +in arbitrary character sets that exceed +.Va PATH_MAX . +.Sh RETURN VALUES +Most functions return zero on success, non-zero on error. +The return value indicates the general severity of the error, ranging +from +.Cm ARCHIVE_WARN , +which indicates a minor problem that should probably be reported +to the user, to +.Cm ARCHIVE_FATAL , +which indicates a serious problem that will prevent any further +operations on this archive. +On error, the +.Fn archive_errno +function can be used to retrieve a numeric error code (see +.Xr errno 2 ) . +The +.Fn archive_error_string +returns a textual error message suitable for display. +.Pp +.Fn archive_read_new +and +.Fn archive_write_new +return pointers to an allocated and initialized +.Tn struct archive +object. +.Pp +.Fn archive_read_data +and +.Fn archive_write_data +return a count of the number of bytes actually read or written. +A value of zero indicates the end of the data for this entry. +A negative value indicates an error, in which case the +.Fn archive_errno +and +.Fn archive_error_string +functions can be used to obtain more information. +.Sh ENVIRONMENT +There are character set conversions within the +.Xr archive_entry 3 +functions that are impacted by the currently-selected locale. +.Sh SEE ALSO +.Xr tar 1 , +.Xr archive_entry 3 , +.Xr archive_read 3 , +.Xr archive_util 3 , +.Xr archive_write 3 , +.Xr tar 5 +.Sh HISTORY +The +.Nm libarchive +library first appeared in +.Fx 5.3 . +.Sh AUTHORS +.An -nosplit +The +.Nm libarchive +library was written by +.An Tim Kientzle Aq kientzle@acm.org . +.Sh BUGS +Some archive formats support information that is not supported by +.Tn struct archive_entry . +Such information cannot be fully archived or restored using this library. +This includes, for example, comments, character sets, +or the arbitrary key/value pairs that can appear in +pax interchange format archives. +.Pp +Conversely, of course, not all of the information that can be +stored in an +.Tn struct archive_entry +is supported by all formats. +For example, cpio formats do not support nanosecond timestamps; +old tar formats do not support large device numbers. diff --git a/contrib/libarchive-2.1/libarchive/libarchive_internals.3 b/contrib/libarchive-2.1/libarchive/libarchive_internals.3 new file mode 100644 index 0000000000..c8f79ffac5 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/libarchive_internals.3 @@ -0,0 +1,376 @@ +.\" 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. +.\" +.\" $FreeBSD$ +.\" +.Dd April 16, 2007 +.Dt LIBARCHIVE 3 +.Os +.Sh NAME +.Nm libarchive_internals +.Nd description of libarchive internal interfaces +.Sh OVERVIEW +The +.Nm libarchive +library provides a flexible interface for reading and writing +streaming archive files such as tar and cpio. +Internally, it follows a modular layered design that should +make it easy to add new archive and compression formats. +.Sh GENERAL ARCHITECTURE +Externally, libarchive exposes most operations through an +opaque, object-style interface. +The +.Xr archive_entry 1 +objects store information about a single filesystem object. +The rest of the library provides facilities to write +.Xr archive_entry 1 +objects to archive files, +read them from archive files, +and write them to disk. +(There are plans to add a facility to read +.Xr archive_entry 1 +objects from disk as well.) +.Pp +The read and write APIs each have four layers: a public API +layer, a format layer that understands the archive file format, +a compression layer, and an I/O layer. +The I/O layer is completely exposed to clients who can replace +it entirely with their own functions. +.Pp +In order to provide as much consistency as possible for clients, +some public functions are virtualized. +Eventually, it should be possible for clients to open +an archive or disk writer, and then use a single set of +code to select and write entries, regardless of the target. +.Sh READ ARCHITECTURE +From the outside, clients use the +.Xr archive_read 3 +API to manipulate an +.Nm archive +object to read entries and bodies from an archive stream. +Internally, the +.Nm archive +object is cast to an +.Nm archive_read +object, which holds all read-specific data. +The API has four layers: +The lowest layer is the I/O layer. +This layer can be overridden by clients, but most clients use +the packaged I/O callbacks provided, for example, by +.Xr archive_read_open_memory 3 , +and +.Xr archive_read_open_fd 3 . +The compression layer calls the I/O layer to +read bytes and decompresses them for the format layer. +The format layer unpacks a stream of uncompressed bytes and +creates +.Nm archive_entry +objects from the incoming data. +The API layer tracks overall state +(for example, it prevents clients from reading data before reading a header) +and invokes the format and compression layer operations +through registered function pointers. +In particular, the API layer drives the format-detection process: +When opening the archive, it reads an initial block of data +and offers it to each registered compression handler. +The one with the highest bid is initialized with the first block. +Similarly, the format handlers are polled to see which handler +is the best for each header request. +(Note that a single file can have entries handled by different +format handlers; +this allows a simple handler for a generic version of a format +with more complex handlers implemented independently for +extended sub-formats.) +.Ss I/O Layer and Client Callbacks +The read API goes to some lengths to be nice to clients. +As a result, there are few restrictions on the behavior of +the client callbacks. +.Pp +The client read callback is expected to provide a block +of data on each call. +A zero-length return does indicate end of file, but otherwise +blocks may be as small as one byte or as large as the entire file. +In particular, blocks may be of different sizes. +.Pp +The client skip callback returns the number of bytes actually +skipped, which may be much smaller than the skip requested. +The only requirement is that the skip not be larger. +The skip callback must never be invoked with a negative value. +.Pp +Keep in mind that not all clients are reading from disk: +clients reading from networks may provide different-sized +blocks on every request and cannot skip at all; +advanced clients may use +.Xr mmap 2 +to read the entire file into memory at once and return the +entire file to libarchive as a single block; +other clients may begin asynchronous I/O operations for the +next block on each request. +.Ss Decompresssion Layer +The decompression layer not only handles decompression, +it also buffers data so that the format handlers see a +much nicer I/O model. +The decompression API is a two stage peek/consume model. +A read_ahead request specifies a minimum read amount; +the decompression layer must provide a pointer to at least +that much data. +If more data is immediately available, it should return more: +the format layer handles bulk data reads by asking for a minimum +of one byte and then copying as much data as is available. +.Pp +A subsequent call to the +.Fn consume +function advances the read pointer. +Note that data returned from a +.Fn read_ahead +call is guaranteed to remain in place until +the next call to +.Fn read_ahead . +Intervening calls to +.Fn consume +should not cause the data to move. +.Pp +Skip requests must always be handled exactly. +Decompression handlers that cannot seek forward should +not register a skip handler; +the API layer fills in a generic skip handler that reads and discards data. +.Pp +A decompression handler has a specific lifecycle: +.Bl -tag -compact -width indent +.It Registration/Configuration +When the client invokes the public support function, +the decompression handler invokes the internal +.Fn __archive_read_register_compression +function to provide bid and initialization functions. +This function returns +.Cm NULL +on error or else a pointer to a +.Cm struct decompressor_t . +This structure contains a +.Va void * config +slot that can be used for storing any customization information. +.It Bid +The bid function is invoked with a pointer and size of a block of data. +The decompressor can access its config data +through the +.Va decompressor +element of the +.Cm archive_read +object. +The bid function is otherwise stateless. +In particular, it must not perform any I/O operations. +.Pp +The value returned by the bid function indicates its suitability +for handling this data stream. +A bid of zero will ensure that this decompressor is never invoked. +Return zero if magic number checks fail. +Otherwise, your initial implementation should return the number of bits +actually checked. +For example, if you verify two full bytes and three bits of another +byte, bid 19. +Note that the initial block may be very short; +be careful to only inspect the data you are given. +(The current decompressors require two bytes for correct bidding.) +.It Initialize +The winning bidder will have its init function called. +This function should initialize the remaining slots of the +.Va struct decompressor_t +object pointed to by the +.Va decompressor +element of the +.Va archive_read +object. +In particular, it should allocate any working data it needs +in the +.Va data +slot of that structure. +The init function is called with the block of data that +was used for tasting. +At this point, the decompressor is responsible for all I/O +requests to the client callbacks. +The decompressor is free to read more data as and when +necessary. +.It Satisfy I/O requests +The format handler will invoke the +.Va read_ahead , +.Va consume , +and +.Va skip +functions as needed. +.It Finish +The finish method is called only once when the archive is closed. +It should release anything stored in the +.Va data +and +.Va config +slots of the +.Va decompressor +object. +It should not invoke the client close callback. +.El +.Ss Format Layer +The read formats have a similar lifecycle to the decompression handlers: +.Bl -tag -compact -width indent +.It Registration +Allocate your private data and initialize your pointers. +.It Bid +Formats bid by invoking the +.Fn read_ahead +decompression method but not calling the +.Fn consume +method. +This allows each bidder to look ahead in the input stream. +Bidders should not look further ahead than necessary, as long +look aheads put pressure on the compression layer to buffer +lots of data. +Most formats only require a few hundred bytes of look ahead; +look aheads of a few kilobytes are reasonable. +(The ISO9660 reader sometimes looks ahead by 48k, which +should be considered an upper limit.) +Note that the bidder is invoked for every entry. +For many formats, this is inappropriate; if you can only bid at +the beginning of the file, store your bid value and check that +each time your bid function is called. +For example, the ISO9660 reader initializes a +.Va bid +value to -1 at registration time; +each time the bid function is called, the bid value is returned +immediately if it is zero or larger. +.It Read header +The header read is usually the most complex part of any format. +There are a few strategies worth mentioning: +For formats such as tar or cpio, reading and parsing the header is +straightforward since headers alternate with data. +For formats that store all header data at the beginning of the file, +the first header read request may have to read all headers into +memory and store that data, sorted by the location of the file +data. +Subsequent header read requests will skip forward to the +beginning of the file data and return the corresponding header. +.It Read Data +The read data interface supports sparse files; this requires that +each call return a block of data specifying the file offset and +size. +This may require you to carefully track the location so that you +can return accurate file offsets for each read. +Remember that the decompressor will return as much data as it has. +Generally, you will want to request one byte, +examine the return value to see how much data is available, and +possibly trim that to the amount you can use. +You should invoke consume for each block just before you return it. +.It Skip All Data +The skip data call should skip over all file data and trailing padding. +This is called automatically by the API layer just before each +header read. +It is also called in response to the client calling the public +.Fn data_skip +function. +.It Cleanup +On cleanup, the format should release all of its allocated memory. +.El +.Ss API Layer +XXX to do XXX +.Sh WRITE ARCHITECTURE +The write API has a similar set of four layers: +an API layer, a format layer, a compression layer, and an I/O layer. +The registration here is much simpler because only +one format and one compression can be registered at a time. +.Ss I/O Layer and Client Callbacks +XXX To be written XXX +.Ss Compression Layer +XXX To be written XXX +.Ss Format Layer +XXX To be written XXX +.Ss API Layer +XXX To be written XXX +.Sh WRITE_DISK ARCHITECTURE +The write_disk API is intended to look just like the write API +to clients. +Since it does not handle multiple formats or compression, it +is not layered internally. +.Sh GENERAL SERVICES +The +.Nm archive_read , +.Nm archive_write , +and +.Nm archive_write_disk +objects all contain an initial +.Nm archive +object which provides common support for a set of standard services. +(Recall that ANSI/ISO C90 guarantees that you can cast freely between +a pointer to a structure and a pointer to the first element of that +structure.) +The +.Nm archive +object has a magic value that indicates which API this object +is associated with, +slots for storing error information, +and function pointers for virtualized API functions. +.Sh MISCELLANEOUS NOTES +Connecting existing archiving libraries into libarchive is generally +quite difficult. +In particular, many existing libraries strongly assume that you +are reading from a file; they seek forwards and backwards as necessary +to locate various pieces of information. +In contrast, libarchive never seeks backwards in its input, which +sometimes requires very different approaches. +.Pp +For example, libarchive's ISO9660 support operates very differently +from most ISO9660 readers. +The libarchive support utilizes a work-queue design that +keeps a list of known entries sorted by their location in the input. +Whenever libarchive's ISO9660 implementation is asked for the next +header, checks this list to find the next item on the disk. +Directories are parsed when they are encountered and new +items are added to the list. +This design relies heavily on the ISO9660 image being optimized so that +directories always occur earlier on the disk than the files they +describe. +.Pp +Depending on the specific format, such approaches may not be possible. +The ZIP format specification, for example, allows archivers to store +key information only at the end of the file. +In theory, it is possible to create ZIP archives that cannot +be read without seeking. +Fortunately, such archives are very rare, and libarchive can read +most ZIP archives, though it cannot always extract as much information +as a dedicated ZIP program. +.Sh SEE ALSO +.Xr archive 3 , +.Xr archive_entry 3 , +.Xr archive_read 3 , +.Xr archive_write 3 , +.Xr archive_write_disk 3 +.Sh HISTORY +The +.Nm libarchive +library first appeared in +.Fx 5.3 . +.Sh AUTHORS +.An -nosplit +The +.Nm libarchive +library was written by +.An Tim Kientzle Aq kientzle@acm.org . +.Sh BUGS diff --git a/contrib/libarchive-2.1/libarchive/tar.5 b/contrib/libarchive-2.1/libarchive/tar.5 new file mode 100644 index 0000000000..ab39df3338 --- /dev/null +++ b/contrib/libarchive-2.1/libarchive/tar.5 @@ -0,0 +1,730 @@ +.\" 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. +.\" +.\" $FreeBSD: src/lib/libarchive/tar.5,v 1.17 2007/01/09 08:05:56 kientzle Exp $ +.\" +.Dd May 20, 2004 +.Dt TAR 5 +.Os +.Sh NAME +.Nm tar +.Nd format of tape archive files +.Sh DESCRIPTION +The +.Nm +archive format collects any number of files, directories, and other +file system objects (symbolic links, device nodes, etc.) into a single +stream of bytes. +The format was originally designed to be used with +tape drives that operate with fixed-size blocks, but is widely used as +a general packaging mechanism. +.Ss General Format +A +.Nm +archive consists of a series of 512-byte records. +Each file system object requires a header record which stores basic metadata +(pathname, owner, permissions, etc.) and zero or more records containing any +file data. +The end of the archive is indicated by two records consisting +entirely of zero bytes. +.Pp +For compatibility with tape drives that use fixed block sizes, +programs that read or write tar files always read or write a fixed +number of records with each I/O operation. +These +.Dq blocks +are always a multiple of the record size. +The most common block size\(emand the maximum supported by historic +implementations\(emis 10240 bytes or 20 records. +(Note: the terms +.Dq block +and +.Dq record +here are not entirely standard; this document follows the +convention established by John Gilmore in documenting +.Nm pdtar . ) +.Ss Old-Style Archive Format +The original tar archive format has been extended many times to +include additional information that various implementors found +necessary. +This section describes the variant implemented by the tar command +included in +.At v7 , +which is one of the earliest widely-used versions of the tar program. +.Pp +The header record for an old-style +.Nm +archive consists of the following: +.Bd -literal -offset indent +struct header_old_tar { + char name[100]; + char mode[8]; + char uid[8]; + char gid[8]; + char size[12]; + char mtime[12]; + char checksum[8]; + char linkflag[1]; + char linkname[100]; + char pad[255]; +}; +.Ed +All unused bytes in the header record are filled with nulls. +.Bl -tag -width indent +.It Va name +Pathname, stored as a null-terminated string. +Early tar implementations only stored regular files (including +hardlinks to those files). +One common early convention used a trailing "/" character to indicate +a directory name, allowing directory permissions and owner information +to be archived and restored. +.It Va mode +File mode, stored as an octal number in ASCII. +.It Va uid , Va gid +User id and group id of owner, as octal numbers in ASCII. +.It Va size +Size of file, as octal number in ASCII. +For regular files only, this indicates the amount of data +that follows the header. +In particular, this field was ignored by early tar implementations +when extracting hardlinks. +Modern writers should always store a zero length for hardlink entries. +.It Va mtime +Modification time of file, as an octal number in ASCII. +This indicates the number of seconds since the start of the epoch, +00:00:00 UTC January 1, 1970. +Note that negative values should be avoided +here, as they are handled inconsistently. +.It Va checksum +Header checksum, stored as an octal number in ASCII. +To compute the checksum, set the checksum field to all spaces, +then sum all bytes in the header using unsigned arithmetic. +This field should be stored as six octal digits followed by a null and a space +character. +Note that many early implementations of tar used signed arithmetic +for the checksum field, which can cause interoperability problems +when transferring archives between systems. +Modern robust readers compute the checksum both ways and accept the +header if either computation matches. +.It Va linkflag , Va linkname +In order to preserve hardlinks and conserve tape, a file +with multiple links is only written to the archive the first +time it is encountered. +The next time it is encountered, the +.Va linkflag +is set to an ASCII +.Sq 1 +and the +.Va linkname +field holds the first name under which this file appears. +(Note that regular files have a null value in the +.Va linkflag +field.) +.El +.Pp +Early tar implementations varied in how they terminated these fields. +The tar command in +.At v7 +used the following conventions (this is also documented in early BSD manpages): +the pathname must be null-terminated; +the mode, uid, and gid fields must end in a space and a null byte; +the size and mtime fields must end in a space; +the checksum is terminated by a null and a space. +Early implementations filled the numeric fields with leading spaces. +This seems to have been common practice until the +.St -p1003.1-88 +standard was released. +For best portability, modern implementations should fill the numeric +fields with leading zeros. +.Ss Pre-POSIX Archives +An early draft of +.St -p1003.1-88 +served as the basis for John Gilmore's +.Nm pdtar +program and many system implementations from the late 1980s +and early 1990s. +These archives generally follow the POSIX ustar +format described below with the following variations: +.Bl -bullet -compact -width indent +.It +The magic value is +.Dq ustar\ \& +(note the following space). +The version field contains a space character followed by a null. +.It +The numeric fields are generally filled with leading spaces +(not leading zeros as recommended in the final standard). +.It +The prefix field is often not used, limiting pathnames to +the 100 characters of old-style archives. +.El +.Ss POSIX ustar Archives +.St -p1003.1-88 +defined a standard tar file format to be read and written +by compliant implementations of +.Xr tar 1 . +This format is often called the +.Dq ustar +format, after the magic value used +in the header. +(The name is an acronym for +.Dq Unix Standard TAR . ) +It extends the historic format with new fields: +.Bd -literal -offset indent +struct header_posix_ustar { + char name[100]; + char mode[8]; + char uid[8]; + char gid[8]; + char size[12]; + char mtime[12]; + char checksum[8]; + char typeflag[1]; + char linkname[100]; + char magic[6]; + char version[2]; + char uname[32]; + char gname[32]; + char devmajor[8]; + char devminor[8]; + char prefix[155]; + char pad[12]; +}; +.Ed +.Bl -tag -width indent +.It Va typeflag +Type of entry. +POSIX extended the earlier +.Va linkflag +field with several new type values: +.Bl -tag -width indent -compact +.It Dq 0 +Regular file. +NULL should be treated as a synonym, for compatibility purposes. +.It Dq 1 +Hard link. +.It Dq 2 +Symbolic link. +.It Dq 3 +Character device node. +.It Dq 4 +Block device node. +.It Dq 5 +Directory. +.It Dq 6 +FIFO node. +.It Dq 7 +Reserved. +.It Other +A POSIX-compliant implementation must treat any unrecognized typeflag value +as a regular file. +In particular, writers should ensure that all entries +have a valid filename so that they can be restored by readers that do not +support the corresponding extension. +Uppercase letters "A" through "Z" are reserved for custom extensions. +Note that sockets and whiteout entries are not archivable. +.El +It is worth noting that the +.Va size +field, in particular, has different meanings depending on the type. +For regular files, of course, it indicates the amount of data +following the header. +For directories, it may be used to indicate the total size of all +files in the directory, for use by operating systems that pre-allocate +directory space. +For all other types, it should be set to zero by writers and ignored +by readers. +.It Va magic +Contains the magic value +.Dq ustar +followed by a NULL byte to indicate that this is a POSIX standard archive. +Full compliance requires the uname and gname fields be properly set. +.It Va version +Version. +This should be +.Dq 00 +(two copies of the ASCII digit zero) for POSIX standard archives. +.It Va uname , Va gname +User and group names, as null-terminated ASCII strings. +These should be used in preference to the uid/gid values +when they are set and the corresponding names exist on +the system. +.It Va devmajor , Va devminor +Major and minor numbers for character device or block device entry. +.It Va prefix +First part of pathname. +If the pathname is too long to fit in the 100 bytes provided by the standard +format, it can be split at any +.Pa / +character with the first portion going here. +If the prefix field is not empty, the reader will prepend +the prefix value and a +.Pa / +character to the regular name field to obtain the full pathname. +.El +.Pp +Note that all unused bytes must be set to +.Dv NULL . +.Pp +Field termination is specified slightly differently by POSIX +than by previous implementations. +The +.Va magic , +.Va uname , +and +.Va gname +fields must have a trailing +.Dv NULL . +The +.Va pathname , +.Va linkname , +and +.Va prefix +fields must have a trailing +.Dv NULL +unless they fill the entire field. +(In particular, it is possible to store a 256-character pathname if it +happens to have a +.Pa / +as the 156th character.) +POSIX requires numeric fields to be zero-padded in the front, and allows +them to be terminated with either space or +.Dv NULL +characters. +.Pp +Currently, most tar implementations comply with the ustar +format, occasionally extending it by adding new fields to the +blank area at the end of the header record. +.Ss Pax Interchange Format +There are many attributes that cannot be portably stored in a +POSIX ustar archive. +.St -p1003.1-2001 +defined a +.Dq pax interchange format +that uses two new types of entries to hold text-formatted +metadata that applies to following entries. +Note that a pax interchange format archive is a ustar archive in every +respect. +The new data is stored in ustar-compatible archive entries that use the +.Dq x +or +.Dq g +typeflag. +In particular, older implementations that do not fully support these +extensions will extract the metadata into regular files, where the +metadata can be examined as necessary. +.Pp +An entry in a pax interchange format archive consists of one or +two standard ustar entries, each with its own header and data. +The first optional entry stores the extended attributes +for the following entry. +This optional first entry has an "x" typeflag and a size field that +indicates the total size of the extended attributes. +The extended attributes themselves are stored as a series of text-format +lines encoded in the portable UTF-8 encoding. +Each line consists of a decimal number, a space, a key string, an equals +sign, a value string, and a new line. +The decimal number indicates the length of the entire line, including the +initial length field and the trailing newline. +An example of such a field is: +.Dl 25 ctime=1084839148.1212\en +Keys in all lowercase are standard keys. +Vendors can add their own keys by prefixing them with an all uppercase +vendor name and a period. +Note that, unlike the historic header, numeric values are stored using +decimal, not octal. +A description of some common keys follows: +.Bl -tag -width indent +.It Cm atime , Cm ctime , Cm mtime +File access, inode change, and modification times. +These fields can be negative or include a decimal point and a fractional value. +.It Cm uname , Cm uid , Cm gname , Cm gid +User name, group name, and numeric UID and GID values. +The user name and group name stored here are encoded in UTF8 +and can thus include non-ASCII characters. +The UID and GID fields can be of arbitrary length. +.It Cm linkpath +The full path of the linked-to file. +Note that this is encoded in UTF8 and can thus include non-ASCII characters. +.It Cm path +The full pathname of the entry. +Note that this is encoded in UTF8 and can thus include non-ASCII characters. +.It Cm realtime.* , Cm security.* +These keys are reserved and may be used for future standardization. +.It Cm size +The size of the file. +Note that there is no length limit on this field, allowing conforming +archives to store files much larger than the historic 8GB limit. +.It Cm SCHILY.* +Vendor-specific attributes used by Joerg Schilling's +.Nm star +implementation. +.It Cm SCHILY.acl.access , Cm SCHILY.acl.default +Stores the access and default ACLs as textual strings in a format +that is an extension of the format specified by POSIX.1e draft 17. +In particular, each user or group access specification can include a fourth +colon-separated field with the numeric UID or GID. +This allows ACLs to be restored on systems that may not have complete +user or group information available (such as when NIS/YP or LDAP services +are temporarily unavailable). +.It Cm SCHILY.devminor , Cm SCHILY.devmajor +The full minor and major numbers for device nodes. +.It Cm SCHILY.dev, Cm SCHILY.ino , Cm SCHILY.nlinks +The device number, inode number, and link count for the entry. +In particular, note that a pax interchange format archive using Joerg +Schilling's +.Cm SCHILY.* +extensions can store all of the data from +.Va struct stat . +.It Cm LIBARCHIVE.xattr. Ns Ar namespace Ns . Ns Ar key +Libarchive stores POSIX.1e-style extended attributes using +keys of this form. +The +.Ar key +value is URL-encoded: +All non-ASCII characters and the two special characters +.Dq = +and +.Dq % +are encoded as +.Dq % +followed by two uppercase hexadecimal digits. +The value of this key is the extended attribute value +encoded in base 64. +XXX Detail the base-64 format here XXX +.It Cm VENDOR.* +XXX document other vendor-specific extensions XXX +.El +.Pp +Any values stored in an extended attribute override the corresponding +values in the regular tar header. +Note that compliant readers should ignore the regular fields when they +are overridden. +This is important, as existing archivers are known to store non-compliant +values in the standard header fields in this situation. +There are no limits on length for any of these fields. +In particular, numeric fields can be arbitrarily large. +All text fields are encoded in UTF8. +Compliant writers should store only portable 7-bit ASCII characters in +the standard ustar header and use extended +attributes whenever a text value contains non-ASCII characters. +.Pp +In addition to the +.Cm x +entry described above, the pax interchange format +also supports a +.Cm g +entry. +The +.Cm g +entry is identical in format, but specifies attributes that serve as +defaults for all subsequent archive entries. +The +.Cm g +entry is not widely used. +.Pp +Besides the new +.Cm x +and +.Cm g +entries, the pax interchange format has a few other minor variations +from the earlier ustar format. +The most troubling one is that hardlinks are permitted to have +data following them. +This allows readers to restore any hardlink to a file without +having to rewind the archive to find an earlier entry. +However, it creates complications for robust readers, as it is no longer +clear whether or not they should ignore the size field for hardlink entries. +.Ss GNU Tar Archives +The GNU tar program started with a pre-POSIX format similar to that +described earlier and has extended it using several different mechanisms: +It added new fields to the empty space in the header (some of which was later +used by POSIX for conflicting purposes); +it allowed the header to be continued over multiple records; +and it defined new entries that modify following entries +(similar in principle to the +.Cm x +entry described above, but each GNU special entry is single-purpose, +unlike the general-purpose +.Cm x +entry). +As a result, GNU tar archives are not POSIX compatible, although +more lenient POSIX-compliant readers can successfully extract most +GNU tar archives. +.Bd -literal -offset indent +struct header_gnu_tar { + char name[100]; + char mode[8]; + char uid[8]; + char gid[8]; + char size[12]; + char mtime[12]; + char checksum[8]; + char typeflag[1]; + char linkname[100]; + char magic[6]; + char version[2]; + char uname[32]; + char gname[32]; + char devmajor[8]; + char devminor[8]; + char atime[12]; + char ctime[12]; + char offset[12]; + char longnames[4]; + char unused[1]; + struct { + char offset[12]; + char numbytes[12]; + } sparse[4]; + char isextended[1]; + char realsize[12]; + char pad[17]; +}; +.Ed +.Bl -tag -width indent +.It Va typeflag +GNU tar uses the following special entry types, in addition to +those defined by POSIX: +.Bl -tag -width indent +.It "7" +GNU tar treats type "7" records identically to type "0" records, +except on one obscure RTOS where they are used to indicate the +pre-allocation of a contiguous file on disk. +.It "D" +This indicates a directory entry. +Unlike the POSIX-standard "5" +typeflag, the header is followed by data records listing the names +of files in this directory. +Each name is preceded by an ASCII "Y" +if the file is stored in this archive or "N" if the file is not +stored in this archive. +Each name is terminated with a null, and +an extra null marks the end of the name list. +The purpose of this +entry is to support incremental backups; a program restoring from +such an archive may wish to delete files on disk that did not exist +in the directory when the archive was made. +.Pp +Note that the "D" typeflag specifically violates POSIX, which requires +that unrecognized typeflags be restored as normal files. +In this case, restoring the "D" entry as a file could interfere +with subsequent creation of the like-named directory. +.It "K" +The data for this entry is a long linkname for the following regular entry. +.It "L" +The data for this entry is a long pathname for the following regular entry. +.It "M" +This is a continuation of the last file on the previous volume. +GNU multi-volume archives guarantee that each volume begins with a valid +entry header. +To ensure this, a file may be split, with part stored at the end of one volume, +and part stored at the beginning of the next volume. +The "M" typeflag indicates that this entry continues an existing file. +Such entries can only occur as the first or second entry +in an archive (the latter only if the first entry is a volume label). +The +.Va size +field specifies the size of this entry. +The +.Va offset +field at bytes 369-380 specifies the offset where this file fragment +begins. +The +.Va realsize +field specifies the total size of the file (which must equal +.Va size +plus +.Va offset ) . +When extracting, GNU tar checks that the header file name is the one it is +expecting, that the header offset is in the correct sequence, and that +the sum of offset and size is equal to realsize. +FreeBSD's version of GNU tar does not handle the corner case of an +archive's being continued in the middle of a long name or other +extension header. +.It "N" +Type "N" records are no longer generated by GNU tar. +They contained a +list of files to be renamed or symlinked after extraction; this was +originally used to support long names. +The contents of this record +are a text description of the operations to be done, in the form +.Dq Rename %s to %s\en +or +.Dq Symlink %s to %s\en ; +in either case, both +filenames are escaped using K&R C syntax. +.It "S" +This is a +.Dq sparse +regular file. +Sparse files are stored as a series of fragments. +The header contains a list of fragment offset/length pairs. +If more than four such entries are required, the header is +extended as necessary with +.Dq extra +header extensions (an older format that is no longer used), or +.Dq sparse +extensions. +.It "V" +The +.Va name +field should be interpreted as a tape/volume header name. +This entry should generally be ignored on extraction. +.El +.It Va magic +The magic field holds the five characters +.Dq ustar +followed by a space. +Note that POSIX ustar archives have a trailing null. +.It Va version +The version field holds a space character followed by a null. +Note that POSIX ustar archives use two copies of the ASCII digit +.Dq 0 . +.It Va atime , Va ctime +The time the file was last accessed and the time of +last change of file information, stored in octal as with +.Va mtime . +.It Va longnames +This field is apparently no longer used. +.It Sparse Va offset / Va numbytes +Each such structure specifies a single fragment of a sparse +file. +The two fields store values as octal numbers. +The fragments are each padded to a multiple of 512 bytes +in the archive. +On extraction, the list of fragments is collected from the +header (including any extension headers), and the data +is then read and written to the file at appropriate offsets. +.It Va isextended +If this is set to non-zero, the header will be followed by additional +.Dq sparse header +records. +Each such record contains information about as many as 21 additional +sparse blocks as shown here: +.Bd -literal -offset indent +struct gnu_sparse_header { + struct { + char offset[12]; + char numbytes[12]; + } sparse[21]; + char isextended[1]; + char padding[7]; +}; +.Ed +.It Va realsize +A binary representation of the file's complete size, with a much larger range +than the POSIX file size. +In particular, with +.Cm M +type files, the current entry is only a portion of the file. +In that case, the POSIX size field will indicate the size of this +entry; the +.Va realsize +field will indicate the total size of the file. +.El +.Ss Solaris Tar +XXX More Details Needed XXX +.Pp +Solaris tar (beginning with SunOS XXX 5.7 ?? XXX) supports an +.Dq extended +format that is fundamentally similar to pax interchange format, +with the following differences: +.Bl -bullet -compact -width indent +.It +Extended attributes are stored in an entry whose type is +.Cm X , +not +.Cm x , +as used by pax interchange format. +The detailed format of this entry appears to be the same +as detailed above for the +.Cm x +entry. +.It +An additional +.Cm A +entry is used to store an ACL for the following regular entry. +The body of this entry contains a seven-digit octal number +(whose value is 01000000 plus the number of ACL entries) +followed by a zero byte, followed by the +textual ACL description. +.El +.Ss Other Extensions +One common extension, utilized by GNU tar, star, and other newer +.Nm +implementations, permits binary numbers in the standard numeric +fields. +This is flagged by setting the high bit of the first character. +This permits 95-bit values for the length and time fields +and 63-bit values for the uid, gid, and device numbers. +GNU tar supports this extension for the +length, mtime, ctime, and atime fields. +Joerg Schilling's star program supports this extension for +all numeric fields. +Note that this extension is largely obsoleted by the extended attribute +record provided by the pax interchange format. +.Pp +Another early GNU extension allowed base-64 values rather +than octal. +This extension was short-lived and such archives are almost never seen. +However, there is still code in GNU tar to support them; this code is +responsible for a very cryptic warning message that is sometimes seen when +GNU tar encounters a damaged archive. +.Sh SEE ALSO +.Xr ar 1 , +.Xr pax 1 , +.Xr tar 1 +.Sh STANDARDS +The +.Nm tar +utility is no longer a part of POSIX or the Single Unix Standard. +It last appeared in +.St -susv2 . +It has been supplanted in subsequent standards by +.Xr pax 1 . +The ustar format is currently part of the specification for the +.Xr pax 1 +utility. +The pax interchange file format is new with +.St -p1003.1-2001 . +.Sh HISTORY +A +.Nm tar +command appeared in Seventh Edition Unix, which was released in January, 1979. +It replaced the +.Nm tp +program from Fourth Edition Unix which in turn replaced the +.Nm tap +program from First Edition Unix. +John Gilmore's +.Nm pdtar +public-domain implementation (circa 1987) was highly influential +and formed the basis of +.Nm GNU tar . +Joerg Shilling's +.Nm star +archiver is another open-source (GPL) archiver (originally developed +circa 1985) which features complete support for pax interchange +format. diff --git a/contrib/libarchive-2.1/tar/bsdtar.1 b/contrib/libarchive-2.1/tar/bsdtar.1 new file mode 100644 index 0000000000..f2237f06b6 --- /dev/null +++ b/contrib/libarchive-2.1/tar/bsdtar.1 @@ -0,0 +1,763 @@ +.\" 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. +.\" +.\" $FreeBSD: src/usr.bin/tar/bsdtar.1,v 1.33 2007/01/09 08:12:17 kientzle Exp $ +.\" +.Dd April 13, 2004 +.Dt BSDTAR 1 +.Os +.Sh NAME +.Nm tar +.Nd manipulate tape archives +.Sh SYNOPSIS +.Nm +.Op Ar bundled-flags Ao args Ac +.Op Ao Ar file Ac | Ao Ar pattern Ac ... +.Nm +.Brq Fl c +.Op Ar options +.Op Ar files | directories +.Nm +.Brq Fl r | Fl u +.Fl f Ar archive-file +.Op Ar options +.Op Ar files | directories +.Nm +.Brq Fl t | Fl x +.Op Ar options +.Op Ar patterns +.Sh DESCRIPTION +.Nm +creates and manipulates streaming archive files. +.Pp +The first synopsis form shows a +.Dq bundled +option word. +This usage is provided for compatibility with historical implementations. +See COMPATIBILITY below for details. +.Pp +The other synopsis forms show the preferred usage. +The first option to +.Nm +is a mode indicator from the following list: +.Bl -tag -compact -width indent +.It Fl c +Create a new archive containing the specified items. +.It Fl r +Like +.Fl c , +but new entries are appended to the archive. +Note that this only works on uncompressed archives stored in regular files. +The +.Fl f +option is required. +.It Fl t +List archive contents to stdout. +.It Fl u +Like +.Fl r , +but new entries are added only if they have a modification date +newer than the corresponding entry in the archive. +Note that this only works on uncompressed archives stored in regular files. +The +.Fl f +option is required. +.It Fl x +Extract to disk from the archive. +If a file with the same name appears more than once in the archive, +each copy will be extracted, with later copies overwriting (replacing) +earlier copies. +.El +.Pp +In +.Fl c , +.Fl r , +or +.Fl u +mode, each specified file or directory is added to the +archive in the order specified on the command line. +By default, the contents of each directory are also archived. +.Pp +In extract or list mode, the entire command line +is read and parsed before the archive is opened. +The pathnames or patterns on the command line indicate +which items in the archive should be processed. +Patterns are shell-style globbing patterns as +documented in +.Xr tcsh 1 . +.Sh OPTIONS +Unless specifically stated otherwise, options are applicable in +all operating modes. +.Bl -tag -width indent +.It Cm @ Ns Pa archive +(c and r mode only) +The specified archive is opened and the entries +in it will be appended to the current archive. +As a simple example, +.Dl Nm Fl c Fl f Pa - Pa newfile Cm @ Ns Pa original.tar +writes a new archive to standard output containing a file +.Pa newfile +and all of the entries from +.Pa original.tar . +In contrast, +.Dl Nm Fl c Fl f Pa - Pa newfile Pa original.tar +creates a new archive with only two entries. +Similarly, +.Dl Nm Fl czf Pa - Fl -format Cm pax Cm @ Ns Pa - +reads an archive from standard input (whose format will be determined +automatically) and converts it into a gzip-compressed +pax-format archive on stdout. +In this way, +.Nm +can be used to convert archives from one format to another. +.It Fl b Ar blocksize +Specify the block size, in 512-byte records, for tape drive I/O. +As a rule, this argument is only needed when reading from or writing +to tape drives, and usually not even then as the default block size of +20 records (10240 bytes) is very common. +.It Fl C Ar directory +In c and r mode, this changes the directory before adding +the following files. +In x mode, change directories after opening the archive +but before extracting entries from the archive. +.It Fl -check-links ( Fl W Cm check-links ) +(c and r modes only) +Issue a warning message unless all links to each file are archived. +.It Fl -exclude Ar pattern ( Fl W Cm exclude Ns = Ns Ar pattern ) +Do not process files or directories that match the +specified pattern. +Note that exclusions take precedence over patterns or filenames +specified on the command line. +.It Fl -format Ar format ( Fl W Cm format Ns = Ns Ar format ) +(c mode only) +Use the specified format for the created archive. +Supported formats include +.Dq cpio , +.Dq pax , +.Dq shar , +and +.Dq ustar . +Other formats may also be supported; see +.Xr libarchive-formats 5 +for more information about currently-supported formats. +.It Fl f Ar file +Read the archive from or write the archive to the specified file. +The filename can be +.Pa - +for standard input or standard output. +If not specified, the default tape device will be used. +(On +.Fx , +the default tape device is +.Pa /dev/sa0 . ) +.It Fl -fast-read ( Fl W Cm fast-read ) +(x and t mode only) +Extract or list only the first archive entry that matches each pattern +or filename operand. +Exit as soon as each specified pattern or filename has been matched. +By default, the archive is always read to the very end, since +there can be multiple entries with the same name and, by convention, +later entries overwrite earlier entries. +This option is provided as a performance optimization. +.It Fl H +(c and r mode only) +Symbolic links named on the command line will be followed; the +target of the link will be archived, not the link itself. +.It Fl h +(c and r mode only) +Synonym for +.Fl L . +.It Fl I +Synonym for +.Fl T . +.It Fl -include Ar pattern ( Fl W Cm include Ns = Ns Ar pattern ) +Process only files or directories that match the specified pattern. +Note that exclusions specified with +.Fl -exclude +take precedence over inclusions. +If no inclusions are explicitly specified, all entries are processed by +default. +The +.Fl -include +option is especially useful when filtering archives. +For example, the command +.Dl Nm Fl c Fl f Pa new.tar Fl -include='*foo*' Cm @ Ns Pa old.tgz +creates a new archive +.Pa new.tar +containing only the entries from +.Pa old.tgz +containing the string +.Sq foo . +.It Fl j +(c mode only) +Compress the resulting archive with +.Xr bzip2 1 . +In extract or list modes, this option is ignored. +Note that, unlike other +.Nm tar +implementations, this implementation recognizes bzip2 compression +automatically when reading archives. +.It Fl k +(x mode only) +Do not overwrite existing files. +In particular, if a file appears more than once in an archive, +later copies will not overwrite earlier copies. +.It Fl L +(c and r mode only) +All symbolic links will be followed. +Normally, symbolic links are archived as such. +With this option, the target of the link will be archived instead. +.It Fl l +If +.Ev POSIXLY_CORRECT +is specified in the environment, this is a synonym for the +.Fl -check-links +option. +Otherwise, an error will be displayed. +Users who desire behavior compatible with GNU tar should use +the +.Fl -one-file-system +option instead. +.It Fl m +(x mode only) +Do not extract modification time. +By default, the modification time is set to the time stored in the archive. +.It Fl n +(c, r, u modes only) +Do not recursively archive the contents of directories. +.It Fl -newer Ar date ( Fl W Cm newer Ns = Ns Ar date ) +(c, r, u modes only) +Only include files and directories newer than the specified date. +This compares ctime entries. +.It Fl -newer-mtime Ar date ( Fl W Cm newer-mtime Ns = Ns Ar date ) +(c, r, u modes only) +Like +.Fl -newer , +except it compares mtime entries instead of ctime entries. +.It Fl -newer-than Pa file ( Fl W Cm newer-than Ns = Ns Pa file ) +(c, r, u modes only) +Only include files and directories newer than the specified file. +This compares ctime entries. +.It Fl -newer-mtime-than Pa file ( Fl W Cm newer-mtime-than Ns = Ns Pa file ) +(c, r, u modes only) +Like +.Fl -newer-than , +except it compares mtime entries instead of ctime entries. +.It Fl -nodump ( Fl W Cm nodump ) +(c and r modes only) +Honor the nodump file flag by skipping this file. +.It Fl -null ( Fl W Cm null ) +(use with +.Fl I , +.Fl T , +or +.Fl X ) +Filenames or patterns are separated by null characters, +not by newlines. +This is often used to read filenames output by the +.Fl print0 +option to +.Xr find 1 . +.It Fl O +(x, t modes only) +In extract (-x) mode, files will be written to standard out rather than +being extracted to disk. +In list (-t) mode, the file listing will be written to stderr rather than +the usual stdout. +.It Fl o +(x mode only) +Use the user and group of the user running the program rather +than those specified in the archive. +Note that this has no significance unless +.Fl p +is specified, and the program is being run by the root user. +In this case, the file modes and flags from +the archive will be restored, but ACLs or owner information in +the archive will be discarded. +.It Fl -one-file-system ( Fl W Cm one-file-system ) +(c, r, and u modes) +Do not cross mount points. +.It Fl P +Preserve pathnames. +By default, absolute pathnames (those that begin with a / +character) have the leading slash removed both when creating archives +and extracting from them. +Also, +.Nm +will refuse to extract archive entries whose pathnames contain +.Pa .. +or whose target directory would be altered by a symlink. +This option suppresses these behaviors. +.It Fl p +(x mode only) +Preserve file permissions. +Attempt to restore the full permissions, including owner, file modes, file +flags and ACLs, if available, for each item extracted from the archive. +By default, newly-created files are owned by the user running +.Nm , +the file mode is restored for newly-created regular files, and +all other types of entries receive default permissions. +If +.Nm +is being run by root, the default is to restore the owner unless the +.Fl o +option is also specified. +.It Fl -strip-components Ar count ( Fl W Cm strip-components Ns = Ns Ar count ) +(x and t mode only) +Remove the specified number of leading path elements. +Pathnames with fewer elements will be silently skipped. +Note that the pathname is edited after checking inclusion/exclusion patterns +but before security checks. +.It Fl T Ar filename +In x or t mode, +.Nm +will read the list of names to be extracted from +.Pa filename . +In c mode, +.Nm +will read names to be archived from +.Pa filename . +The special name +.Dq -C +on a line by itself will cause the current directory to be changed to +the directory specified on the following line. +Names are terminated by newlines unless +.Fl -null +is specified. +Note that +.Fl -null +also disables the special handling of lines containing +.Dq -C . +.It Fl U +(x mode only) +Unlink files before creating them. +Without this option, +.Nm +overwrites existing files, which preserves existing hardlinks. +With this option, existing hardlinks will be broken, as will any +symlink that would affect the location of an extracted file. +.It Fl -use-compress-program Ar program +Pipe the input (in x or t mode) or the output (in c mode) through +.Pa program +instead of using the builtin compression support. +.It Fl v +Produce verbose output. +In create and extract modes, +.Nm +will list each file name as it is read from or written to +the archive. +In list mode, +.Nm +will produce output similar to that of +.Xr ls 1 . +Additional +.Fl v +options will provide additional detail. +.It Fl W Ar longopt=value +Long options (preceded by +.Fl - ) +are only supported directly on systems that have the +.Xr getopt_long 3 +function. +The +.Fl W +option can be used to access long options on systems that +do not support this function. +.It Fl w +Ask for confirmation for every action. +.It Fl X Ar filename +Read a list of exclusion patterns from the specified file. +See +.Fl -exclude +for more information about the handling of exclusions. +.It Fl y +(c mode only) +Compress the resulting archive with +.Xr bzip2 1 . +In extract or list modes, this option is ignored. +Note that, unlike other +.Nm tar +implementations, this implementation recognizes bzip2 compression +automatically when reading archives. +.It Fl z +(c mode only) +Compress the resulting archive with +.Xr gzip 1 . +In extract or list modes, this option is ignored. +Note that, unlike other +.Nm tar +implementations, this implementation recognizes gzip compression +automatically when reading archives. +.El +.Sh ENVIRONMENT +The following environment variables affect the execution of +.Nm : +.Bl -tag -width ".Ev BLOCKSIZE" +.It Ev LANG +The locale to use. +See +.Xr environ 7 +for more information. +.It Ev POSIXLY_CORRECT +If this environment variable is defined, the +.Fl l +option will be interpreted in accordance with +.St -p1003.1-96 . +.It Ev TAPE +The default tape device. +The +.Fl f +option overrides this. +.It Ev TZ +The timezone to use when displaying dates. +See +.Xr environ 7 +for more information. +.El +.Sh FILES +.Bl -tag -width ".Ev BLOCKSIZE" +.It Pa /dev/sa0 +The default tape device, if not overridden by the +.Ev TAPE +environment variable or the +.Fl f +option. +.El +.Sh EXIT STATUS +.Ex -std +.Sh EXAMPLES +The following creates a new archive +called +.Ar file.tar.gz +that contains two files +.Ar source.c +and +.Ar source.h : +.Dl Nm Fl czf Pa file.tar.gz Pa source.c Pa source.h +.Pp +To view a detailed table of contents for this +archive: +.Dl Nm Fl tvf Pa file.tar.gz +.Pp +To extract all entries from the archive on +the default tape drive: +.Dl Nm Fl x +.Pp +To move file hierarchies, invoke +.Nm +as +.Dl Nm Fl cf Pa - Fl C Pa srcdir\ . | Nm Fl xpf Pa - Fl C Pa destdir +or more traditionally +.Dl cd srcdir \&; Nm Fl cf Pa -\ . | ( cd destdir \&; Nm Fl xpf Pa - ) +.Pp +In create mode, the list of files and directories to be archived +can also include directory change instructions of the form +.Cm -C Ns Pa foo/baz +and archive inclusions of the form +.Cm @ Ns Pa archive-file . +For example, the command line +.Dl Nm Fl c Fl f Pa new.tar Pa foo1 Cm @ Ns Pa old.tgz Cm -C Ns Pa /tmp Pa foo2 +will create a new archive +.Pa new.tar . +.Nm +will read the file +.Pa foo1 +from the current directory and add it to the output archive. +It will then read each entry from +.Pa old.tgz +and add those entries to the output archive. +Finally, it will switch to the +.Pa /tmp +directory and add +.Pa foo2 +to the output archive. +.Pp +The +.Fl -newer +and +.Fl -newer-mtime +switches accept a variety of common date and time specifications, including +.Dq 12 Mar 2005 7:14:29pm , +.Dq 2005-03-12 19:14 , +.Dq 5 minutes ago , +and +.Dq 19:14 PST May 1 . +.Sh COMPATIBILITY +The bundled-arguments format is supported for compatibility +with historic implementations. +It consists of an initial word (with no leading - character) in which +each character indicates an option. +Arguments follow as separate words. +The order of the arguments must match the order +of the corresponding characters in the bundled command word. +For example, +.Dl Nm Cm tbf 32 Pa file.tar +specifies three flags +.Cm t , +.Cm b , +and +.Cm f . +The +.Cm b +and +.Cm f +flags both require arguments, +so there must be two additional items +on the command line. +The +.Ar 32 +is the argument to the +.Cm b +flag, and +.Ar file.tar +is the argument to the +.Cm f +flag. +.Pp +The mode options c, r, t, u, and x and the options +b, f, l, m, o, v, and w comply with SUSv2. +.Pp +For maximum portability, scripts that invoke +.Nm tar +should use the bundled-argument format above, should limit +themselves to the +.Cm c , +.Cm t , +and +.Cm x +modes, and the +.Cm b , +.Cm f , +.Cm m , +.Cm v , +and +.Cm w +options. +.Pp +On systems that support getopt_long(), additional long options +are available to improve compatibility with other tar implementations. +.Sh SECURITY +Certain security issues are common to many archiving programs, including +.Nm . +In particular, carefully-crafted archives can request that +.Nm +extract files to locations outside of the target directory. +This can potentially be used to cause unwitting users to overwrite +files they did not intend to overwrite. +If the archive is being extracted by the superuser, any file +on the system can potentially be overwritten. +There are three ways this can happen. +Although +.Nm +has mechanisms to protect against each one, +savvy users should be aware of the implications: +.Bl -bullet -width indent +.It +Archive entries can have absolute pathnames. +By default, +.Nm +removes the leading +.Pa / +character from filenames before restoring them to guard against this problem. +.It +Archive entries can have pathnames that include +.Pa .. +components. +By default, +.Nm +will not extract files containing +.Pa .. +components in their pathname. +.It +Archive entries can exploit symbolic links to restore +files to other directories. +An archive can restore a symbolic link to another directory, +then use that link to restore a file into that directory. +To guard against this, +.Nm +checks each extracted path for symlinks. +If the final path element is a symlink, it will be removed +and replaced with the archive entry. +If +.Fl U +is specified, any intermediate symlink will also be unconditionally removed. +If neither +.Fl U +nor +.Fl P +is specified, +.Nm +will refuse to extract the entry. +.El +To protect yourself, you should be wary of any archives that +come from untrusted sources. +You should examine the contents of an archive with +.Dl Nm Fl tf Pa filename +before extraction. +You should use the +.Fl k +option to ensure that +.Nm +will not overwrite any existing files or the +.Fl U +option to remove any pre-existing files. +You should generally not extract archives while running with super-user +privileges. +Note that the +.Fl P +option to +.Nm +disables the security checks above and allows you to extract +an archive while preserving any absolute pathnames, +.Pa .. +components, or symlinks to other directories. +.Sh SEE ALSO +.Xr bzip2 1 , +.Xr cpio 1 , +.Xr gzip 1 , +.Xr mt 1 , +.Xr pax 1 , +.Xr shar 1 , +.Xr libarchive 3 , +.Xr libarchive-formats 5 , +.Xr tar 5 +.Sh STANDARDS +There is no current POSIX standard for the tar command; it appeared +in +.St -p1003.1-96 +but was dropped from +.St -p1003.1-2001 . +The options used by this implementation were developed by surveying a +number of existing tar implementations as well as the old POSIX specification +for tar and the current POSIX specification for pax. +.Pp +The ustar and pax interchange file formats are defined by +.St -p1003.1-2001 +for the pax command. +.Sh HISTORY +A +.Nm tar +command appeared in Seventh Edition Unix. +There have been numerous other implementations, +many of which extended the file format. +John Gilmore's +.Nm pdtar +public-domain implementation (circa November, 1987) +was quite influential, and formed the basis of GNU tar. +GNU tar was included as the standard system tar +in +.Fx +beginning with +.Fx 1.0 . +.Pp +This is a complete re-implementation based on the +.Xr libarchive 3 +library. +.Sh BUGS +POSIX and GNU violently disagree about the meaning of the +.Fl l +option. +Because of the potential for disaster if someone expects +one behavior and gets the other, the +.Fl l +option is deliberately broken in this implementation. +.Pp +The +.Fl C Pa dir +option may differ from historic implementations. +.Pp +All archive output is written in correctly-sized blocks, even +if the output is being compressed. +Whether or not the last output block is padded to a full +block size varies depending on the format and the +output device. +For tar and cpio formats, the last block of output is padded +to a full block size if the output is being +written to standard output or to a character or block device such as +a tape drive. +If the output is being written to a regular file, the last block +will not be padded. +Many compressors, including +.Xr gzip 1 +and +.Xr bzip2 1 , +complain about the null padding when decompressing an archive created by +.Nm , +although they still extract it correctly. +.Pp +The compression and decompression is implemented internally, so +there may be insignificant differences between the compressed output +generated by +.Dl Nm Fl czf Pa - file +and that generated by +.Dl Nm Fl cf Pa - file | Nm gzip +.Pp +The default should be to read and write archives to the standard I/O paths, +but tradition (and POSIX) dictates otherwise. +.Pp +The +.Cm r +and +.Cm u +modes require that the archive be uncompressed +and located in a regular file on disk. +Other archives can be modified using +.Cm c +mode with the +.Pa @archive-file +extension. +.Pp +To archive a file called +.Pa @foo +or +.Pa -foo +you must specify it as +.Pa ./@foo +or +.Pa ./-foo , +respectively. +.Pp +In create mode, a leading +.Pa ./ +is always removed. +A leading +.Pa / +is stripped unless the +.Fl P +option is specified. +.Pp +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. +.Pp +Converting between dissimilar archive formats (such as tar and cpio) using the +.Cm @ Ns Pa - +convention can cause hard link information to be lost. +(This is a consequence of the incompatible ways that different archive +formats store hardlink information.) +.Pp +There are alternative long options for many of the short options that +are deliberately not documented. diff --git a/contrib/libarchive-2.1/tar/bsdtar.c b/contrib/libarchive-2.1/tar/bsdtar.c new file mode 100644 index 0000000000..4303b94da1 --- /dev/null +++ b/contrib/libarchive-2.1/tar/bsdtar.c @@ -0,0 +1,922 @@ +/*- + * 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: src/usr.bin/tar/bsdtar.c,v 1.74 2007/03/24 03:25:49 kientzle Exp $"); + +#ifdef HAVE_SYS_PARAM_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_GETOPT_LONG +#include +#else +struct option { + const char *name; + int has_arg; + int *flag; + int val; +}; +#define no_argument 0 +#define required_argument 1 +#endif +#ifdef HAVE_LANGINFO_H +#include +#endif +#ifdef HAVE_LOCALE_H +#include +#endif +#ifdef HAVE_PATHS_H +#include +#endif +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_TIME_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#if HAVE_ZLIB_H +#include +#endif + +#include "bsdtar.h" + +/* + * Per POSIX.1-1988, tar defaults to reading/writing archives to/from + * the default tape device for the system. Pick something reasonable here. + */ +#ifdef __linux +#define _PATH_DEFTAPE "/dev/st0" +#endif + +#ifndef _PATH_DEFTAPE +#define _PATH_DEFTAPE "/dev/tape" +#endif + +/* External function to parse a date/time string (from getdate.y) */ +time_t get_date(const char *); + +static int bsdtar_getopt(struct bsdtar *, const char *optstring, + const struct option **poption); +static void long_help(struct bsdtar *); +static void only_mode(struct bsdtar *, const char *opt, + const char *valid); +static char ** rewrite_argv(struct bsdtar *, + int *argc, char ** src_argv, + const char *optstring); +static void set_mode(struct bsdtar *, char opt); +static void version(void); + +/* + * The leading '+' here forces the GNU version of getopt() (as well as + * both the GNU and BSD versions of getopt_long) to stop at the first + * non-option. Otherwise, GNU getopt() permutes the arguments and + * screws up -C processing. + */ +static const char *tar_opts = "+Bb:C:cf:HhI:jkLlmnOoPprtT:UuvW:wX:xyZz"; + +/* + * Most of these long options are deliberately not documented. They + * are provided only to make life easier for people who also use GNU tar. + * The only long options documented in the manual page are the ones + * with no corresponding short option, such as --exclude, --nodump, + * and --fast-read. + * + * On systems that lack getopt_long, long options can be specified + * using -W longopt and -W longopt=value, e.g. "-W nodump" is the same + * as "--nodump" and "-W exclude=pattern" is the same as "--exclude + * pattern". This does not rely the GNU getopt() "W;" extension, so + * should work correctly on any system with a POSIX-compliant getopt(). + */ + +/* Fake short equivalents for long options that otherwise lack them. */ +enum { + OPTION_CHECK_LINKS=1, + OPTION_EXCLUDE, + OPTION_FAST_READ, + OPTION_FORMAT, + OPTION_HELP, + OPTION_INCLUDE, + OPTION_NEWER_CTIME, + OPTION_NEWER_CTIME_THAN, + OPTION_NEWER_MTIME, + OPTION_NEWER_MTIME_THAN, + OPTION_NODUMP, + OPTION_NO_SAME_OWNER, + OPTION_NO_SAME_PERMISSIONS, + OPTION_NULL, + OPTION_ONE_FILE_SYSTEM, + OPTION_STRIP_COMPONENTS, + OPTION_TOTALS, + OPTION_USE_COMPRESS_PROGRAM, + OPTION_VERSION +}; + +/* + * If you add anything, be very careful to keep this list properly + * sorted, as the -W logic relies on it. + */ +static const struct option tar_longopts[] = { + { "absolute-paths", no_argument, NULL, 'P' }, + { "append", no_argument, NULL, 'r' }, + { "block-size", required_argument, NULL, 'b' }, + { "bunzip2", no_argument, NULL, 'j' }, + { "bzip", no_argument, NULL, 'j' }, + { "bzip2", no_argument, NULL, 'j' }, + { "cd", required_argument, NULL, 'C' }, + { "check-links", no_argument, NULL, OPTION_CHECK_LINKS }, + { "confirmation", no_argument, NULL, 'w' }, + { "create", no_argument, NULL, 'c' }, + { "dereference", no_argument, NULL, 'L' }, + { "directory", required_argument, NULL, 'C' }, + { "exclude", required_argument, NULL, OPTION_EXCLUDE }, + { "exclude-from", required_argument, NULL, 'X' }, + { "extract", no_argument, NULL, 'x' }, + { "fast-read", no_argument, NULL, OPTION_FAST_READ }, + { "file", required_argument, NULL, 'f' }, + { "files-from", required_argument, NULL, 'T' }, + { "format", required_argument, NULL, OPTION_FORMAT }, + { "gunzip", no_argument, NULL, 'z' }, + { "gzip", no_argument, NULL, 'z' }, + { "help", no_argument, NULL, OPTION_HELP }, + { "include", required_argument, NULL, OPTION_INCLUDE }, + { "interactive", no_argument, NULL, 'w' }, + { "keep-old-files", no_argument, NULL, 'k' }, + { "list", no_argument, NULL, 't' }, + { "modification-time", no_argument, NULL, 'm' }, + { "newer", required_argument, NULL, OPTION_NEWER_CTIME }, + { "newer-ctime", required_argument, NULL, OPTION_NEWER_CTIME }, + { "newer-ctime-than", required_argument, NULL, OPTION_NEWER_CTIME_THAN }, + { "newer-mtime", required_argument, NULL, OPTION_NEWER_MTIME }, + { "newer-mtime-than", required_argument, NULL, OPTION_NEWER_MTIME_THAN }, + { "newer-than", required_argument, NULL, OPTION_NEWER_CTIME_THAN }, + { "nodump", no_argument, NULL, OPTION_NODUMP }, + { "norecurse", no_argument, NULL, 'n' }, + { "no-recursion", no_argument, NULL, 'n' }, + { "no-same-owner", no_argument, NULL, OPTION_NO_SAME_OWNER }, + { "no-same-permissions",no_argument, NULL, OPTION_NO_SAME_PERMISSIONS }, + { "null", no_argument, NULL, OPTION_NULL }, + { "one-file-system", no_argument, NULL, OPTION_ONE_FILE_SYSTEM }, + { "preserve-permissions", no_argument, NULL, 'p' }, + { "read-full-blocks", no_argument, NULL, 'B' }, + { "same-permissions", no_argument, NULL, 'p' }, + { "strip-components", required_argument, NULL, OPTION_STRIP_COMPONENTS }, + { "to-stdout", no_argument, NULL, 'O' }, + { "totals", no_argument, NULL, OPTION_TOTALS }, + { "unlink", no_argument, NULL, 'U' }, + { "unlink-first", no_argument, NULL, 'U' }, + { "update", no_argument, NULL, 'u' }, + { "use-compress-program", + required_argument, NULL, OPTION_USE_COMPRESS_PROGRAM }, + { "verbose", no_argument, NULL, 'v' }, + { "version", no_argument, NULL, OPTION_VERSION }, + { NULL, 0, NULL, 0 } +}; + +/* A basic set of security flags to request from libarchive. */ +#define SECURITY \ + (ARCHIVE_EXTRACT_SECURE_SYMLINKS \ + | ARCHIVE_EXTRACT_SECURE_NODOTDOT) + +int +main(int argc, char **argv) +{ + struct bsdtar *bsdtar, bsdtar_storage; + const struct option *option; + int opt, t; + char option_o; + char possible_help_request; + char buff[16]; + + /* + * Use a pointer for consistency, but stack-allocated storage + * for ease of cleanup. + */ + bsdtar = &bsdtar_storage; + memset(bsdtar, 0, sizeof(*bsdtar)); + bsdtar->fd = -1; /* Mark as "unused" */ + option_o = 0; + + /* Need bsdtar->progname before calling bsdtar_warnc. */ + if (*argv == NULL) + bsdtar->progname = "bsdtar"; + else { + bsdtar->progname = strrchr(*argv, '/'); + if (bsdtar->progname != NULL) + bsdtar->progname++; + else + bsdtar->progname = *argv; + } + + if (setlocale(LC_ALL, "") == NULL) + bsdtar_warnc(bsdtar, 0, "Failed to set default locale"); +#if defined(HAVE_NL_LANGINFO) && defined(HAVE_D_MD_ORDER) + bsdtar->day_first = (*nl_langinfo(D_MD_ORDER) == 'd'); +#endif + possible_help_request = 0; + + /* Look up uid of current user for future reference */ + bsdtar->user_uid = geteuid(); + + /* Default: open tape drive. */ + bsdtar->filename = getenv("TAPE"); + if (bsdtar->filename == NULL) + bsdtar->filename = _PATH_DEFTAPE; + + /* Default: preserve mod time on extract */ + bsdtar->extract_flags = ARCHIVE_EXTRACT_TIME; + + /* Default: Perform basic security checks. */ + bsdtar->extract_flags |= SECURITY; + + /* Defaults for root user: */ + if (bsdtar->user_uid == 0) { + /* --same-owner */ + bsdtar->extract_flags |= ARCHIVE_EXTRACT_OWNER; + /* -p */ + bsdtar->extract_flags |= ARCHIVE_EXTRACT_PERM; + bsdtar->extract_flags |= ARCHIVE_EXTRACT_ACL; + bsdtar->extract_flags |= ARCHIVE_EXTRACT_XATTR; + bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS; + } + + /* Rewrite traditional-style tar arguments, if used. */ + argv = rewrite_argv(bsdtar, &argc, argv, tar_opts); + + bsdtar->argv = argv; + bsdtar->argc = argc; + + /* Process all remaining arguments now. */ + /* + * Comments following each option indicate where that option + * originated: SUSv2, POSIX, GNU tar, star, etc. If there's + * no such comment, then I don't know of anyone else who + * implements that option. + */ + while ((opt = bsdtar_getopt(bsdtar, tar_opts, &option)) != -1) { + switch (opt) { + case 'B': /* GNU tar */ + /* libarchive doesn't need this; just ignore it. */ + break; + case 'b': /* SUSv2 */ + t = atoi(optarg); + if (t <= 0 || t > 1024) + bsdtar_errc(bsdtar, 1, 0, + "Argument to -b is out of range (1..1024)"); + bsdtar->bytes_per_block = 512 * t; + break; + case 'C': /* GNU tar */ + set_chdir(bsdtar, optarg); + break; + case 'c': /* SUSv2 */ + set_mode(bsdtar, opt); + break; + case OPTION_CHECK_LINKS: /* GNU tar */ + bsdtar->option_warn_links = 1; + break; + case OPTION_EXCLUDE: /* GNU tar */ + if (exclude(bsdtar, optarg)) + bsdtar_errc(bsdtar, 1, 0, + "Couldn't exclude %s\n", optarg); + break; + case OPTION_FORMAT: /* GNU tar, others */ + bsdtar->create_format = optarg; + break; + case 'f': /* SUSv2 */ + bsdtar->filename = optarg; + if (strcmp(bsdtar->filename, "-") == 0) + bsdtar->filename = NULL; + break; + case OPTION_FAST_READ: /* GNU tar */ + bsdtar->option_fast_read = 1; + break; + case 'H': /* BSD convention */ + bsdtar->symlink_mode = 'H'; + break; + case 'h': /* Linux Standards Base, gtar; synonym for -L */ + bsdtar->symlink_mode = 'L'; + /* Hack: -h by itself is the "help" command. */ + possible_help_request = 1; + break; + case OPTION_HELP: /* GNU tar, others */ + long_help(bsdtar); + exit(0); + break; + case 'I': /* GNU tar */ + /* + * TODO: Allow 'names' to come from an archive, + * not just a text file. Design a good UI for + * allowing names and mode/owner to be read + * from an archive, with contents coming from + * disk. This can be used to "refresh" an + * archive or to design archives with special + * permissions without having to create those + * permissions on disk. + */ + bsdtar->names_from_file = optarg; + break; + case OPTION_INCLUDE: + /* + * Noone else has the @archive extension, so + * noone else needs this to filter entries + * when transforming archives. + */ + if (include(bsdtar, optarg)) + bsdtar_errc(bsdtar, 1, 0, + "Failed to add %s to inclusion list", + optarg); + break; + case 'j': /* GNU tar */ +#if HAVE_LIBBZ2 + if (bsdtar->create_compression != '\0') + bsdtar_errc(bsdtar, 1, 0, + "Can't specify both -%c and -%c", opt, + bsdtar->create_compression); + bsdtar->create_compression = opt; +#else + bsdtar_warnc(bsdtar, 0, "-j compression not supported by this version of bsdtar"); + usage(bsdtar); +#endif + break; + case 'k': /* GNU tar */ + bsdtar->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE; + break; + case 'L': /* BSD convention */ + bsdtar->symlink_mode = 'L'; + break; + case 'l': /* SUSv2 and GNU conflict badly here */ + if (getenv("POSIXLY_CORRECT") != NULL) { + /* User has asked for POSIX/SUS behavior. */ + bsdtar->option_warn_links = 1; + } else { + fprintf(stderr, +"Error: -l has different behaviors in different tar programs.\n"); + fprintf(stderr, +" For the GNU behavior, use --one-file-system instead.\n"); + fprintf(stderr, +" For the POSIX behavior, use --check-links instead.\n"); + usage(bsdtar); + } + break; + case 'm': /* SUSv2 */ + bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_TIME; + break; + case 'n': /* GNU tar */ + bsdtar->option_no_subdirs = 1; + break; + /* + * Selecting files by time: + * --newer-?time='date' Only files newer than 'date' + * --newer-?time-than='file' Only files newer than time + * on specified file (useful for incremental backups) + * TODO: Add corresponding "older" options to reverse these. + */ + case OPTION_NEWER_CTIME: /* GNU tar */ + bsdtar->newer_ctime_sec = get_date(optarg); + break; + case OPTION_NEWER_CTIME_THAN: + { + struct stat st; + if (stat(optarg, &st) != 0) + bsdtar_errc(bsdtar, 1, 0, + "Can't open file %s", optarg); + bsdtar->newer_ctime_sec = st.st_ctime; + bsdtar->newer_ctime_nsec = + ARCHIVE_STAT_CTIME_NANOS(&st); + } + break; + case OPTION_NEWER_MTIME: /* GNU tar */ + bsdtar->newer_mtime_sec = get_date(optarg); + break; + case OPTION_NEWER_MTIME_THAN: + { + struct stat st; + if (stat(optarg, &st) != 0) + bsdtar_errc(bsdtar, 1, 0, + "Can't open file %s", optarg); + bsdtar->newer_mtime_sec = st.st_mtime; + bsdtar->newer_mtime_nsec = + ARCHIVE_STAT_MTIME_NANOS(&st); + } + break; + case OPTION_NODUMP: /* star */ + bsdtar->option_honor_nodump = 1; + break; + case OPTION_NO_SAME_OWNER: /* GNU tar */ + bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_OWNER; + break; + case OPTION_NO_SAME_PERMISSIONS: /* GNU tar */ + bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_PERM; + bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_ACL; + bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_XATTR; + bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_FFLAGS; + break; + case OPTION_NULL: /* GNU tar */ + bsdtar->option_null++; + break; + case 'O': /* GNU tar */ + bsdtar->option_stdout = 1; + break; + case 'o': /* SUSv2 and GNU conflict here, but not fatally */ + option_o = 1; /* Record it and resolve it later. */ + break; + case OPTION_ONE_FILE_SYSTEM: /* GNU tar */ + bsdtar->option_dont_traverse_mounts = 1; + break; +#if 0 + /* + * The common BSD -P option is not necessary, since + * our default is to archive symlinks, not follow + * them. This is convenient, as -P conflicts with GNU + * tar anyway. + */ + case 'P': /* BSD convention */ + /* Default behavior, no option necessary. */ + break; +#endif + case 'P': /* GNU tar */ + bsdtar->extract_flags &= ~SECURITY; + bsdtar->option_absolute_paths = 1; + break; + case 'p': /* GNU tar, star */ + bsdtar->extract_flags |= ARCHIVE_EXTRACT_PERM; + bsdtar->extract_flags |= ARCHIVE_EXTRACT_ACL; + bsdtar->extract_flags |= ARCHIVE_EXTRACT_XATTR; + bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS; + break; + case 'r': /* SUSv2 */ + set_mode(bsdtar, opt); + break; + case OPTION_STRIP_COMPONENTS: /* GNU tar 1.15 */ + bsdtar->strip_components = atoi(optarg); + break; + case 'T': /* GNU tar */ + bsdtar->names_from_file = optarg; + break; + case 't': /* SUSv2 */ + set_mode(bsdtar, opt); + bsdtar->verbose++; + break; + case OPTION_TOTALS: /* GNU tar */ + bsdtar->option_totals++; + break; + case 'U': /* GNU tar */ + bsdtar->extract_flags |= ARCHIVE_EXTRACT_UNLINK; + bsdtar->option_unlink_first = 1; + break; + case 'u': /* SUSv2 */ + set_mode(bsdtar, opt); + break; + case 'v': /* SUSv2 */ + bsdtar->verbose++; + break; + case OPTION_VERSION: /* GNU convention */ + version(); + break; +#if 0 + /* + * The -W longopt feature is handled inside of + * bsdtar_getop(), so -W is not available here. + */ + case 'W': /* Obscure, but useful GNU convention. */ + break; +#endif + case 'w': /* SUSv2 */ + bsdtar->option_interactive = 1; + break; + case 'X': /* GNU tar */ + if (exclude_from_file(bsdtar, optarg)) + bsdtar_errc(bsdtar, 1, 0, + "failed to process exclusions from file %s", + optarg); + break; + case 'x': /* SUSv2 */ + set_mode(bsdtar, opt); + break; + case 'y': /* FreeBSD version of GNU tar */ +#if HAVE_LIBBZ2 + if (bsdtar->create_compression != '\0') + bsdtar_errc(bsdtar, 1, 0, + "Can't specify both -%c and -%c", opt, + bsdtar->create_compression); + bsdtar->create_compression = opt; +#else + bsdtar_warnc(bsdtar, 0, "-y compression not supported by this version of bsdtar"); + usage(bsdtar); +#endif + break; + case 'Z': /* GNU tar */ + if (bsdtar->create_compression != '\0') + bsdtar_errc(bsdtar, 1, 0, + "Can't specify both -%c and -%c", opt, + bsdtar->create_compression); + bsdtar->create_compression = opt; + break; + case 'z': /* GNU tar, star, many others */ +#if HAVE_LIBZ + if (bsdtar->create_compression != '\0') + bsdtar_errc(bsdtar, 1, 0, + "Can't specify both -%c and -%c", opt, + bsdtar->create_compression); + bsdtar->create_compression = opt; +#else + bsdtar_warnc(bsdtar, 0, "-z compression not supported by this version of bsdtar"); + usage(bsdtar); +#endif + break; + case OPTION_USE_COMPRESS_PROGRAM: + bsdtar->compress_program = optarg; + break; + default: + usage(bsdtar); + } + } + + /* + * Sanity-check options. + */ + + /* If no "real" mode was specified, treat -h as --help. */ + if ((bsdtar->mode == '\0') && possible_help_request) { + long_help(bsdtar); + exit(0); + } + + /* Otherwise, a mode is required. */ + if (bsdtar->mode == '\0') + bsdtar_errc(bsdtar, 1, 0, + "Must specify one of -c, -r, -t, -u, -x"); + + /* Check boolean options only permitted in certain modes. */ + if (bsdtar->option_dont_traverse_mounts) + only_mode(bsdtar, "--one-file-system", "cru"); + if (bsdtar->option_fast_read) + only_mode(bsdtar, "--fast-read", "xt"); + if (bsdtar->option_honor_nodump) + only_mode(bsdtar, "--nodump", "cru"); + if (option_o > 0) { + switch (bsdtar->mode) { + case 'c': + /* + * In GNU tar, -o means "old format." The + * "ustar" format is the closest thing + * supported by libarchive. + */ + bsdtar->create_format = "ustar"; + /* TODO: bsdtar->create_format = "v7"; */ + break; + case 'x': + /* POSIX-compatible behavior. */ + bsdtar->option_no_owner = 1; + bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_OWNER; + break; + default: + only_mode(bsdtar, "-o", "xc"); + break; + } + } + if (bsdtar->option_no_subdirs) + only_mode(bsdtar, "-n", "cru"); + if (bsdtar->option_stdout) + only_mode(bsdtar, "-O", "xt"); + if (bsdtar->option_unlink_first) + only_mode(bsdtar, "-U", "x"); + if (bsdtar->option_warn_links) + only_mode(bsdtar, "--check-links", "cr"); + + /* Check other parameters only permitted in certain modes. */ + if (bsdtar->create_compression == 'Z' && bsdtar->mode == 'c') { + bsdtar_warnc(bsdtar, 0, ".Z compression not supported"); + usage(bsdtar); + } + if (bsdtar->create_compression != '\0') { + strcpy(buff, "-?"); + buff[1] = bsdtar->create_compression; + only_mode(bsdtar, buff, "cxt"); + } + if (bsdtar->create_format != NULL) + only_mode(bsdtar, "--format", "c"); + if (bsdtar->symlink_mode != '\0') { + strcpy(buff, "-?"); + buff[1] = bsdtar->symlink_mode; + only_mode(bsdtar, buff, "cru"); + } + if (bsdtar->strip_components != 0) + only_mode(bsdtar, "--strip-components", "xt"); + + bsdtar->argc -= optind; + bsdtar->argv += optind; + + switch(bsdtar->mode) { + case 'c': + tar_mode_c(bsdtar); + break; + case 'r': + tar_mode_r(bsdtar); + break; + case 't': + tar_mode_t(bsdtar); + break; + case 'u': + tar_mode_u(bsdtar); + break; + case 'x': + tar_mode_x(bsdtar); + break; + } + + cleanup_exclusions(bsdtar); + if (bsdtar->return_value != 0) + bsdtar_warnc(bsdtar, 0, + "Error exit delayed from previous errors."); + return (bsdtar->return_value); +} + +static void +set_mode(struct bsdtar *bsdtar, char opt) +{ + if (bsdtar->mode != '\0' && bsdtar->mode != opt) + bsdtar_errc(bsdtar, 1, 0, + "Can't specify both -%c and -%c", opt, bsdtar->mode); + bsdtar->mode = opt; +} + +/* + * Verify that the mode is correct. + */ +static void +only_mode(struct bsdtar *bsdtar, const char *opt, const char *valid_modes) +{ + if (strchr(valid_modes, bsdtar->mode) == NULL) + bsdtar_errc(bsdtar, 1, 0, + "Option %s is not permitted in mode -%c", + opt, bsdtar->mode); +} + + +/*- + * Convert traditional tar arguments into new-style. + * For example, + * tar tvfb file.tar 32 --exclude FOO + * will be converted to + * tar -t -v -f file.tar -b 32 --exclude FOO + * + * This requires building a new argv array. The initial bundled word + * gets expanded into a new string that looks like "-t\0-v\0-f\0-b\0". + * The new argv array has pointers into this string intermingled with + * pointers to the existing arguments. Arguments are moved to + * immediately follow their options. + * + * The optstring argument here is the same one passed to getopt(3). + * It is used to determine which option letters have trailing arguments. + */ +char ** +rewrite_argv(struct bsdtar *bsdtar, int *argc, char **src_argv, + const char *optstring) +{ + char **new_argv, **dest_argv; + const char *p; + char *src, *dest; + + if (src_argv[0] == NULL || + src_argv[1] == NULL || src_argv[1][0] == '-') + return (src_argv); + + *argc += strlen(src_argv[1]) - 1; + new_argv = malloc((*argc + 1) * sizeof(new_argv[0])); + if (new_argv == NULL) + bsdtar_errc(bsdtar, 1, errno, "No Memory"); + + dest_argv = new_argv; + *dest_argv++ = *src_argv++; + + dest = malloc(strlen(*src_argv) * 3); + if (dest == NULL) + bsdtar_errc(bsdtar, 1, errno, "No memory"); + for (src = *src_argv++; *src != '\0'; src++) { + *dest_argv++ = dest; + *dest++ = '-'; + *dest++ = *src; + *dest++ = '\0'; + /* If option takes an argument, insert that into the list. */ + for (p = optstring; p != NULL && *p != '\0'; p++) { + if (*p != *src) + continue; + if (p[1] != ':') /* No arg required, done. */ + break; + if (*src_argv == NULL) /* No arg available? Error. */ + bsdtar_errc(bsdtar, 1, 0, + "Option %c requires an argument", + *src); + *dest_argv++ = *src_argv++; + break; + } + } + + /* Copy remaining arguments, including trailing NULL. */ + while ((*dest_argv++ = *src_argv++) != NULL) + ; + + return (new_argv); +} + +void +usage(struct bsdtar *bsdtar) +{ + const char *p; + + p = bsdtar->progname; + + fprintf(stderr, "Usage:\n"); + fprintf(stderr, " List: %s -tf \n", p); + fprintf(stderr, " Extract: %s -xf \n", p); + fprintf(stderr, " Create: %s -cf [filenames...]\n", p); +#ifdef HAVE_GETOPT_LONG + fprintf(stderr, " Help: %s --help\n", p); +#else + fprintf(stderr, " Help: %s -h\n", p); +#endif + exit(1); +} + +static void +version(void) +{ + printf("bsdtar %s - %s\n", PACKAGE_VERSION, archive_version()); + exit(1); +} + +static const char *long_help_msg = + "First option must be a mode specifier:\n" + " -c Create -r Add/Replace -t List -u Update -x Extract\n" + "Common Options:\n" + " -b # Use # 512-byte records per I/O block\n" + " -f Location of archive (default " _PATH_DEFTAPE ")\n" + " -v Verbose\n" + " -w Interactive\n" + "Create: %p -c [options] [ | | @ | -C ]\n" + " , add these items to archive\n" + " -z, -j Compress archive with gzip/bzip2\n" + " --format {ustar|pax|cpio|shar} Select archive format\n" +#ifdef HAVE_GETOPT_LONG + " --exclude Skip files that match pattern\n" +#else + " -W exclude= Skip files that match pattern\n" +#endif + " -C Change to before processing remaining files\n" + " @ Add entries from to output\n" + "List: %p -t [options] []\n" + " If specified, list only entries that match\n" + "Extract: %p -x [options] []\n" + " If specified, extract only entries that match\n" + " -k Keep (don't overwrite) existing files\n" + " -m Don't restore modification times\n" + " -O Write entries to stdout, don't restore to disk\n" + " -p Restore permissions (including ACLs, owner, file flags)\n"; + + +/* + * Note that the word 'bsdtar' will always appear in the first line + * of output. + * + * In particular, /bin/sh scripts that need to test for the presence + * of bsdtar can use the following template: + * + * if (tar --help 2>&1 | grep bsdtar >/dev/null 2>&1 ) then \ + * echo bsdtar; else echo not bsdtar; fi + */ +static void +long_help(struct bsdtar *bsdtar) +{ + const char *prog; + const char *p; + + prog = bsdtar->progname; + + fflush(stderr); + + p = (strcmp(prog,"bsdtar") != 0) ? "(bsdtar)" : ""; + printf("%s%s: manipulate archive files\n", prog, p); + + for (p = long_help_msg; *p != '\0'; p++) { + if (*p == '%') { + if (p[1] == 'p') { + fputs(prog, stdout); + p++; + } else + putchar('%'); + } else + putchar(*p); + } + version(); +} + +static int +bsdtar_getopt(struct bsdtar *bsdtar, const char *optstring, + const struct option **poption) +{ + char *p, *q; + const struct option *option; + int opt; + int option_index; + size_t option_length; + + option_index = -1; + *poption = NULL; + +#ifdef HAVE_GETOPT_LONG + opt = getopt_long(bsdtar->argc, bsdtar->argv, optstring, + tar_longopts, &option_index); + if (option_index > -1) + *poption = tar_longopts + option_index; +#else + opt = getopt(bsdtar->argc, bsdtar->argv, optstring); +#endif + + /* Support long options through -W longopt=value */ + if (opt == 'W') { + p = optarg; + q = strchr(optarg, '='); + if (q != NULL) { + option_length = (size_t)(q - p); + optarg = q + 1; + } else { + option_length = strlen(p); + optarg = NULL; + } + option = tar_longopts; + while (option->name != NULL && + (strlen(option->name) < option_length || + strncmp(p, option->name, option_length) != 0 )) { + option++; + } + + if (option->name != NULL) { + *poption = option; + opt = option->val; + + /* If the first match was exact, we're done. */ + if (strncmp(p, option->name, strlen(option->name)) == 0) { + while (option->name != NULL) + option++; + } else { + /* Check if there's another match. */ + option++; + while (option->name != NULL && + (strlen(option->name) < option_length || + strncmp(p, option->name, option_length) != 0)) { + option++; + } + } + if (option->name != NULL) + bsdtar_errc(bsdtar, 1, 0, + "Ambiguous option %s " + "(matches both %s and %s)", + p, (*poption)->name, option->name); + + } else { + opt = '?'; + /* TODO: Set up a fake 'struct option' for + * error reporting... ? ? ? */ + } + } + + return (opt); +} diff --git a/contrib/libarchive-2.1/tar/bsdtar.h b/contrib/libarchive-2.1/tar/bsdtar.h new file mode 100644 index 0000000000..d99c23d984 --- /dev/null +++ b/contrib/libarchive-2.1/tar/bsdtar.h @@ -0,0 +1,125 @@ +/*- + * 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. + * + * $FreeBSD: src/usr.bin/tar/bsdtar.h,v 1.27 2007/03/11 10:36:42 kientzle Exp $ + */ + +#include "bsdtar_platform.h" +#include + +#define DEFAULT_BYTES_PER_BLOCK (20*512) + +/* + * The internal state for the "bsdtar" program. + * + * Keeping all of the state in a structure like this simplifies memory + * leak testing (at exit, anything left on the heap is suspect). A + * pointer to this structure is passed to most bsdtar internal + * functions. + */ +struct bsdtar { + /* Options */ + const char *filename; /* -f filename */ + const char *create_format; /* -F format */ + char *pending_chdir; /* -C dir */ + const char *names_from_file; /* -T file */ + time_t newer_ctime_sec; /* --newer/--newer-than */ + long newer_ctime_nsec; /* --newer/--newer-than */ + time_t newer_mtime_sec; /* --newer-mtime */ + long newer_mtime_nsec; /* --newer-mtime-than */ + int bytes_per_block; /* -b block_size */ + int verbose; /* -v */ + int extract_flags; /* Flags for extract operation */ + int strip_components; /* Remove this many leading dirs */ + char mode; /* Program mode: 'c', 't', 'r', 'u', 'x' */ + char symlink_mode; /* H or L, per BSD conventions */ + char create_compression; /* j, y, or z */ + const char *compress_program; + char option_absolute_paths; /* -P */ + char option_dont_traverse_mounts; /* --one-file-system */ + char option_fast_read; /* --fast-read */ + char option_honor_nodump; /* --nodump */ + char option_interactive; /* -w */ + char option_no_owner; /* -o */ + char option_no_subdirs; /* -n */ + char option_null; /* --null */ + char option_stdout; /* -O */ + char option_totals; /* --totals */ + char option_unlink_first; /* -U */ + char option_warn_links; /* --check-links */ + char day_first; /* show day before month in -tv output */ + + /* If >= 0, then close this when done. */ + int fd; + + /* Miscellaneous state information */ + struct archive *archive; + const char *progname; + int argc; + char **argv; + size_t gs_width; /* For 'list_item' in read.c */ + size_t u_width; /* for 'list_item' in read.c */ + uid_t user_uid; /* UID running this program */ + int return_value; /* Value returned by main() */ + char warned_lead_slash; /* Already displayed warning */ + char next_line_is_dir; /* Used for -C parsing in -cT */ + + /* + * Data for various subsystems. Full definitions are located in + * the file where they are used. + */ + struct archive_dir *archive_dir; /* for write.c */ + struct name_cache *gname_cache; /* for write.c */ + struct links_cache *links_cache; /* for write.c */ + struct matching *matching; /* for matching.c */ + struct security *security; /* for read.c */ + struct name_cache *uname_cache; /* for write.c */ +}; + +void bsdtar_errc(struct bsdtar *, int _eval, int _code, + const char *fmt, ...); +void bsdtar_strmode(struct archive_entry *entry, char *bp); +void bsdtar_warnc(struct bsdtar *, int _code, const char *fmt, ...); +void cleanup_exclusions(struct bsdtar *); +void do_chdir(struct bsdtar *); +int edit_pathname(struct bsdtar *, struct archive_entry *); +int exclude(struct bsdtar *, const char *pattern); +int exclude_from_file(struct bsdtar *, const char *pathname); +int excluded(struct bsdtar *, const char *pathname); +int include(struct bsdtar *, const char *pattern); +int include_from_file(struct bsdtar *, const char *pathname); +int pathcmp(const char *a, const char *b); +int process_lines(struct bsdtar *bsdtar, const char *pathname, + int (*process)(struct bsdtar *, const char *)); +void safe_fprintf(FILE *, const char *fmt, ...); +void set_chdir(struct bsdtar *, const char *newdir); +void tar_mode_c(struct bsdtar *bsdtar); +void tar_mode_r(struct bsdtar *bsdtar); +void tar_mode_t(struct bsdtar *bsdtar); +void tar_mode_u(struct bsdtar *bsdtar); +void tar_mode_x(struct bsdtar *bsdtar); +int unmatched_inclusions(struct bsdtar *bsdtar); +void usage(struct bsdtar *); +int yes(const char *fmt, ...); + diff --git a/contrib/libarchive-2.1/tar/bsdtar_platform.h b/contrib/libarchive-2.1/tar/bsdtar_platform.h new file mode 100644 index 0000000000..707abcbda2 --- /dev/null +++ b/contrib/libarchive-2.1/tar/bsdtar_platform.h @@ -0,0 +1,149 @@ +/*- + * 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. + * + * $FreeBSD: src/usr.bin/tar/bsdtar_platform.h,v 1.24 2007/04/12 04:45:32 kientzle Exp $ + */ + +/* + * This header is the first thing included in any of the bsdtar + * source files. As far as possible, platform-specific issues should + * be dealt with here and not within individual source files. + */ + +#ifndef BSDTAR_PLATFORM_H_INCLUDED +#define BSDTAR_PLATFORM_H_INCLUDED + +#if defined(PLATFORM_CONFIG_H) +/* Use hand-built config.h in environments that need it. */ +#include PLATFORM_CONFIG_H +#elif defined(HAVE_CONFIG_H) +/* Most POSIX platforms use the 'configure' script to build config.h */ +#include "../config.h" +#else +/* Warn if bsdtar hasn't been (automatically or manually) configured. */ +#error Oops: No config.h and no built-in configuration in bsdtar_platform.h. +#endif /* !HAVE_CONFIG_H */ + +/* No non-FreeBSD platform will have __FBSDID, so just define it here. */ +#ifdef __FreeBSD__ +#include /* For __FBSDID */ +#else +#define __FBSDID(a) /* null */ +#endif + +#ifdef HAVE_LIBARCHIVE +/* If we're using the platform libarchive, include system headers. */ +#include +#include +#else +/* Otherwise, include user headers. */ +#include "archive.h" +#include "archive_entry.h" +#endif + +/* + * Does this platform have complete-looking POSIX-style ACL support, + * including some variant of the acl_get_perm() function (which was + * omitted from the POSIX.1e draft)? + */ +#if HAVE_SYS_ACL_H && HAVE_ACL_PERMSET_T && HAVE_ACL_USER +#if HAVE_ACL_GET_PERM || HAVE_ACL_GET_PERM_NP +#define HAVE_POSIX_ACL 1 +#endif +#endif + +#ifdef HAVE_LIBACL +#include +#endif + +#if HAVE_ACL_GET_PERM +#define ACL_GET_PERM acl_get_perm +#else +#if HAVE_ACL_GET_PERM_NP +#define ACL_GET_PERM acl_get_perm_np +#endif +#endif + +/* + * Include "dirent.h" (or it's equivalent on several different platforms). + * + * This is slightly modified from the GNU autoconf recipe. + * In particular, FreeBSD includes d_namlen in it's dirent structure, + * so my configure script includes an explicit test for the d_namlen + * field. + */ +#if HAVE_DIRENT_H +# include +# if HAVE_DIRENT_D_NAMLEN +# define DIRENT_NAMLEN(dirent) (dirent)->d_namlen +# else +# define DIRENT_NAMLEN(dirent) strlen((dirent)->d_name) +# endif +#else +# define dirent direct +# define DIRENT_NAMLEN(dirent) (dirent)->d_namlen +# if HAVE_SYS_NDIR_H +# include +# endif +# if HAVE_SYS_DIR_H +# include +# endif +# if HAVE_NDIR_H +# include +# endif +#endif + + +/* + * We need to be able to display a filesize using printf(). The type + * and format string here must be compatible with one another and + * large enough for any file. + */ +#if HAVE_UINTMAX_T +#define BSDTAR_FILESIZE_TYPE uintmax_t +#define BSDTAR_FILESIZE_PRINTF "%ju" +#else +#if HAVE_UNSIGNED_LONG_LONG +#define BSDTAR_FILESIZE_TYPE unsigned long long +#define BSDTAR_FILESIZE_PRINTF "%llu" +#else +#define BSDTAR_FILESIZE_TYPE unsigned long +#define BSDTAR_FILESIZE_PRINTF "%lu" +#endif +#endif + +#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC +#define ARCHIVE_STAT_CTIME_NANOS(st) (st)->st_ctimespec.tv_nsec +#define ARCHIVE_STAT_MTIME_NANOS(st) (st)->st_mtimespec.tv_nsec +#else +#if HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC +#define ARCHIVE_STAT_CTIME_NANOS(st) (st)->st_ctim.tv_nsec +#define ARCHIVE_STAT_MTIME_NANOS(st) (st)->st_mtim.tv_nsec +#else +#define ARCHIVE_STAT_CTIME_NANOS(st) (0) +#define ARCHIVE_STAT_MTIME_NANOS(st) (0) +#endif +#endif + +#endif /* !BSDTAR_PLATFORM_H_INCLUDED */ diff --git a/contrib/libarchive-2.1/tar/getdate.y b/contrib/libarchive-2.1/tar/getdate.y new file mode 100644 index 0000000000..84348f8d3f --- /dev/null +++ b/contrib/libarchive-2.1/tar/getdate.y @@ -0,0 +1,811 @@ +%{ +/* + * March 2005: Further modified and simplified by Tim Kientzle: + * Eliminate minutes-based calculations (just do everything in + * seconds), have lexer only recognize unsigned integers (handle '+' + * and '-' characters in grammar), combine tables into one table with + * explicit abbreviation notes, do am/pm adjustments in the grammar + * (eliminate some state variables and post-processing). Among other + * things, these changes eliminated two shift/reduce conflicts. (Went + * from 10 to 8.) + * All of Tim Kientzle's changes to this file are public domain. + */ + +/* +** Originally written by Steven M. Bellovin while +** at the University of North Carolina at Chapel Hill. Later tweaked by +** a couple of people on Usenet. Completely overhauled by Rich $alz +** and Jim Berets in August, 1990; +** +** This grammar has 10 shift/reduce conflicts. +** +** This code is in the public domain and has no copyright. +*/ +/* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */ +/* SUPPRESS 288 on yyerrlab *//* Label unused */ + +#ifdef __FreeBSD__ +#include +__FBSDID("$FreeBSD: src/usr.bin/tar/getdate.y,v 1.8 2007/03/11 10:36:42 kientzle Exp $"); +#endif + +#include +#include +#include +#include +#include + +#define yyparse getdate_yyparse +#define yylex getdate_yylex +#define yyerror getdate_yyerror + +static int yyparse(void); +static int yylex(void); +static int yyerror(const char *); + +time_t get_date(char *); + +#define EPOCH 1970 +#define HOUR(x) ((time_t)(x) * 60) +#define SECSPERDAY (24L * 60L * 60L) + +/* +** Daylight-savings mode: on, off, or not yet known. +*/ +typedef enum _DSTMODE { + DSTon, DSToff, DSTmaybe +} DSTMODE; + +/* +** Meridian: am or pm. +*/ +enum { tAM, tPM }; + +/* +** Global variables. We could get rid of most of these by using a good +** union as the yacc stack. (This routine was originally written before +** yacc had the %union construct.) Maybe someday; right now we only use +** the %union very rarely. +*/ +static char *yyInput; + +static DSTMODE yyDSTmode; +static time_t yyDayOrdinal; +static time_t yyDayNumber; +static int yyHaveDate; +static int yyHaveDay; +static int yyHaveRel; +static int yyHaveTime; +static int yyHaveZone; +static time_t yyTimezone; +static time_t yyDay; +static time_t yyHour; +static time_t yyMinutes; +static time_t yyMonth; +static time_t yySeconds; +static time_t yyYear; +static time_t yyRelMonth; +static time_t yyRelSeconds; + +%} + +%union { + time_t Number; +} + +%token tAGO tDAY tDAYZONE tAMPM tMONTH tMONTH_UNIT tSEC_UNIT tUNUMBER +%token tZONE tDST + +%type tDAY tDAYZONE tMONTH tMONTH_UNIT +%type tSEC_UNIT tUNUMBER tZONE tAMPM + +%% + +spec : /* NULL */ + | spec item + ; + +item : time { yyHaveTime++; } + | zone { yyHaveZone++; } + | date { yyHaveDate++; } + | day { yyHaveDay++; } + | rel { yyHaveRel++; } + | number + ; + +time : tUNUMBER tAMPM { + /* "7am" */ + yyHour = $1; + if (yyHour == 12) + yyHour = 0; + yyMinutes = 0; + yySeconds = 0; + if ($2 == tPM) + yyHour += 12; + } + | bare_time { + /* "7:12:18" "19:17" */ + } + | bare_time tAMPM { + /* "7:12pm", "12:20:13am" */ + if (yyHour == 12) + yyHour = 0; + if ($2 == tPM) + yyHour += 12; + } + | bare_time '+' tUNUMBER { + /* "7:14+0700" */ + yyDSTmode = DSToff; + yyTimezone = - ($3 % 100 + ($3 / 100) * 60); + } + | bare_time '-' tUNUMBER { + /* "19:14:12-0530" */ + yyDSTmode = DSToff; + yyTimezone = + ($3 % 100 + ($3 / 100) * 60); + } + ; + +bare_time : tUNUMBER ':' tUNUMBER { + yyHour = $1; + yyMinutes = $3; + yySeconds = 0; + } + | tUNUMBER ':' tUNUMBER ':' tUNUMBER { + yyHour = $1; + yyMinutes = $3; + yySeconds = $5; + } + ; + +zone : tZONE { + yyTimezone = $1; + yyDSTmode = DSToff; + } + | tDAYZONE { + yyTimezone = $1; + yyDSTmode = DSTon; + } + | tZONE tDST { + yyTimezone = $1; + yyDSTmode = DSTon; + } + ; + +day : tDAY { + yyDayOrdinal = 1; + yyDayNumber = $1; + } + | tDAY ',' { + /* "tue," "wednesday," */ + yyDayOrdinal = 1; + yyDayNumber = $1; + } + | tUNUMBER tDAY { + /* "second tues" "3 wed" */ + yyDayOrdinal = $1; + yyDayNumber = $2; + } + ; + +date : tUNUMBER '/' tUNUMBER { + /* "1/15" */ + yyMonth = $1; + yyDay = $3; + } + | tUNUMBER '/' tUNUMBER '/' tUNUMBER { + if ($1 >= 13) { + /* First number is big: 2004/01/29, 99/02/17 */ + yyYear = $1; + yyMonth = $3; + yyDay = $5; + } else if (($5 >= 13) || ($3 >= 13)) { + /* Last number is big: 01/07/98 */ + /* Middle number is big: 01/29/04 */ + yyMonth = $1; + yyDay = $3; + yyYear = $5; + } else { + /* No significant clues: 02/03/04 */ + yyMonth = $1; + yyDay = $3; + yyYear = $5; + } + } + | tUNUMBER '-' tUNUMBER '-' tUNUMBER { + /* ISO 8601 format. yyyy-mm-dd. */ + yyYear = $1; + yyMonth = $3; + yyDay = $5; + } + | tUNUMBER '-' tMONTH '-' tUNUMBER { + if ($1 > 31) { + /* e.g. 1992-Jun-17 */ + yyYear = $1; + yyMonth = $3; + yyDay = $5; + } else { + /* e.g. 17-JUN-1992. */ + yyDay = $1; + yyMonth = $3; + yyYear = $5; + } + } + | tMONTH tUNUMBER { + /* "May 3" */ + yyMonth = $1; + yyDay = $2; + } + | tMONTH tUNUMBER ',' tUNUMBER { + /* "June 17, 2001" */ + yyMonth = $1; + yyDay = $2; + yyYear = $4; + } + | tUNUMBER tMONTH { + /* "12 Sept" */ + yyDay = $1; + yyMonth = $2; + } + | tUNUMBER tMONTH tUNUMBER { + /* "12 Sept 1997" */ + yyDay = $1; + yyMonth = $2; + yyYear = $3; + } + ; + +rel : relunit tAGO { + yyRelSeconds = -yyRelSeconds; + yyRelMonth = -yyRelMonth; + } + | relunit + ; + +relunit : '-' tUNUMBER tSEC_UNIT { + /* "-3 hours" */ + yyRelSeconds -= $2 * $3; + } + | '+' tUNUMBER tSEC_UNIT { + /* "+1 minute" */ + yyRelSeconds += $2 * $3; + } + | tUNUMBER tSEC_UNIT { + /* "1 day" */ + yyRelSeconds += $1 * $2; + } + | tSEC_UNIT { + /* "hour" */ + yyRelSeconds += $1; + } + | '-' tUNUMBER tMONTH_UNIT { + /* "-3 months" */ + yyRelMonth -= $2 * $3; + } + | '+' tUNUMBER tMONTH_UNIT { + /* "+5 years" */ + yyRelMonth += $2 * $3; + } + | tUNUMBER tMONTH_UNIT { + /* "2 years" */ + yyRelMonth += $1 * $2; + } + | tMONTH_UNIT { + /* "6 months" */ + yyRelMonth += $1; + } + ; + +number : tUNUMBER { + if (yyHaveTime && yyHaveDate && !yyHaveRel) + yyYear = $1; + else { + if($1>10000) { + /* "20040301" */ + yyHaveDate++; + yyDay= ($1)%100; + yyMonth= ($1/100)%100; + yyYear = $1/10000; + } + else { + /* "513" is same as "5:13" */ + yyHaveTime++; + if ($1 < 100) { + yyHour = $1; + yyMinutes = 0; + } + else { + yyHour = $1 / 100; + yyMinutes = $1 % 100; + } + yySeconds = 0; + } + } + } + ; + + +%% + +static struct TABLE { + size_t abbrev; + const char *name; + int type; + time_t value; +} const TimeWords[] = { + /* am/pm */ + { 0, "am", tAMPM, tAM }, + { 0, "pm", tAMPM, tPM }, + + /* Month names. */ + { 3, "january", tMONTH, 1 }, + { 3, "february", tMONTH, 2 }, + { 3, "march", tMONTH, 3 }, + { 3, "april", tMONTH, 4 }, + { 3, "may", tMONTH, 5 }, + { 3, "june", tMONTH, 6 }, + { 3, "july", tMONTH, 7 }, + { 3, "august", tMONTH, 8 }, + { 3, "september", tMONTH, 9 }, + { 3, "october", tMONTH, 10 }, + { 3, "november", tMONTH, 11 }, + { 3, "december", tMONTH, 12 }, + + /* Days of the week. */ + { 2, "sunday", tDAY, 0 }, + { 3, "monday", tDAY, 1 }, + { 2, "tuesday", tDAY, 2 }, + { 3, "wednesday", tDAY, 3 }, + { 2, "thursday", tDAY, 4 }, + { 2, "friday", tDAY, 5 }, + { 2, "saturday", tDAY, 6 }, + + /* Timezones: Offsets are in minutes. */ + { 0, "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */ + { 0, "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */ + { 0, "utc", tZONE, HOUR( 0) }, + { 0, "wet", tZONE, HOUR( 0) }, /* Western European */ + { 0, "bst", tDAYZONE, HOUR( 0) }, /* British Summer */ + { 0, "wat", tZONE, HOUR( 1) }, /* West Africa */ + { 0, "at", tZONE, HOUR( 2) }, /* Azores */ + /* { 0, "bst", tZONE, HOUR( 3) }, */ /* Brazil Standard: Conflict */ + /* { 0, "gst", tZONE, HOUR( 3) }, */ /* Greenland Standard: Conflict*/ + { 0, "nft", tZONE, HOUR(3)+30 }, /* Newfoundland */ + { 0, "nst", tZONE, HOUR(3)+30 }, /* Newfoundland Standard */ + { 0, "ndt", tDAYZONE, HOUR(3)+30 }, /* Newfoundland Daylight */ + { 0, "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */ + { 0, "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */ + { 0, "est", tZONE, HOUR( 5) }, /* Eastern Standard */ + { 0, "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */ + { 0, "cst", tZONE, HOUR( 6) }, /* Central Standard */ + { 0, "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */ + { 0, "mst", tZONE, HOUR( 7) }, /* Mountain Standard */ + { 0, "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */ + { 0, "pst", tZONE, HOUR( 8) }, /* Pacific Standard */ + { 0, "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */ + { 0, "yst", tZONE, HOUR( 9) }, /* Yukon Standard */ + { 0, "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */ + { 0, "hst", tZONE, HOUR(10) }, /* Hawaii Standard */ + { 0, "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */ + { 0, "cat", tZONE, HOUR(10) }, /* Central Alaska */ + { 0, "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */ + { 0, "nt", tZONE, HOUR(11) }, /* Nome */ + { 0, "idlw", tZONE, HOUR(12) }, /* Intl Date Line West */ + { 0, "cet", tZONE, -HOUR(1) }, /* Central European */ + { 0, "met", tZONE, -HOUR(1) }, /* Middle European */ + { 0, "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */ + { 0, "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */ + { 0, "swt", tZONE, -HOUR(1) }, /* Swedish Winter */ + { 0, "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */ + { 0, "fwt", tZONE, -HOUR(1) }, /* French Winter */ + { 0, "fst", tDAYZONE, -HOUR(1) }, /* French Summer */ + { 0, "eet", tZONE, -HOUR(2) }, /* Eastern Eur, USSR Zone 1 */ + { 0, "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */ + { 0, "it", tZONE, -HOUR(3)-30 },/* Iran */ + { 0, "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */ + { 0, "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */ + { 0, "ist", tZONE, -HOUR(5)-30 },/* Indian Standard */ + { 0, "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */ + /* { 0, "nst", tZONE, -HOUR(6.5) }, */ /* North Sumatra: Conflict */ + /* { 0, "sst", tZONE, -HOUR(7) }, */ /* So Sumatra, USSR 6: Conflict */ + { 0, "wast", tZONE, -HOUR(7) }, /* West Australian Standard */ + { 0, "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */ + { 0, "jt", tZONE, -HOUR(7)-30 },/* Java (3pm in Cronusland!)*/ + { 0, "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */ + { 0, "jst", tZONE, -HOUR(9) }, /* Japan Std, USSR Zone 8 */ + { 0, "cast", tZONE, -HOUR(9)-30 },/* Central Australian Std */ + { 0, "cadt", tDAYZONE, -HOUR(9)-30 },/* Central Australian Daylt */ + { 0, "east", tZONE, -HOUR(10) }, /* Eastern Australian Std */ + { 0, "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylt */ + { 0, "gst", tZONE, -HOUR(10) }, /* Guam Std, USSR Zone 9 */ + { 0, "nzt", tZONE, -HOUR(12) }, /* New Zealand */ + { 0, "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */ + { 0, "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */ + { 0, "idle", tZONE, -HOUR(12) }, /* Intl Date Line East */ + + { 0, "dst", tDST, 0 }, + + /* Time units. */ + { 4, "years", tMONTH_UNIT, 12 }, + { 5, "months", tMONTH_UNIT, 1 }, + { 9, "fortnights", tSEC_UNIT, 14 * 24 * 60 * 60 }, + { 4, "weeks", tSEC_UNIT, 7 * 24 * 60 * 60 }, + { 3, "days", tSEC_UNIT, 1 * 24 * 60 * 60 }, + { 4, "hours", tSEC_UNIT, 60 * 60 }, + { 3, "minutes", tSEC_UNIT, 60 }, + { 3, "seconds", tSEC_UNIT, 1 }, + + /* Relative-time words. */ + { 0, "tomorrow", tSEC_UNIT, 1 * 24 * 60 * 60 }, + { 0, "yesterday", tSEC_UNIT, -1 * 24 * 60 * 60 }, + { 0, "today", tSEC_UNIT, 0 }, + { 0, "now", tSEC_UNIT, 0 }, + { 0, "last", tUNUMBER, -1 }, + { 0, "this", tSEC_UNIT, 0 }, + { 0, "next", tUNUMBER, 2 }, + { 0, "first", tUNUMBER, 1 }, + { 0, "1st", tUNUMBER, 1 }, +/* { 0, "second", tUNUMBER, 2 }, */ + { 0, "2nd", tUNUMBER, 2 }, + { 0, "third", tUNUMBER, 3 }, + { 0, "3rd", tUNUMBER, 3 }, + { 0, "fourth", tUNUMBER, 4 }, + { 0, "4th", tUNUMBER, 4 }, + { 0, "fifth", tUNUMBER, 5 }, + { 0, "5th", tUNUMBER, 5 }, + { 0, "sixth", tUNUMBER, 6 }, + { 0, "seventh", tUNUMBER, 7 }, + { 0, "eighth", tUNUMBER, 8 }, + { 0, "ninth", tUNUMBER, 9 }, + { 0, "tenth", tUNUMBER, 10 }, + { 0, "eleventh", tUNUMBER, 11 }, + { 0, "twelfth", tUNUMBER, 12 }, + { 0, "ago", tAGO, 1 }, + + /* Military timezones. */ + { 0, "a", tZONE, HOUR( 1) }, + { 0, "b", tZONE, HOUR( 2) }, + { 0, "c", tZONE, HOUR( 3) }, + { 0, "d", tZONE, HOUR( 4) }, + { 0, "e", tZONE, HOUR( 5) }, + { 0, "f", tZONE, HOUR( 6) }, + { 0, "g", tZONE, HOUR( 7) }, + { 0, "h", tZONE, HOUR( 8) }, + { 0, "i", tZONE, HOUR( 9) }, + { 0, "k", tZONE, HOUR( 10) }, + { 0, "l", tZONE, HOUR( 11) }, + { 0, "m", tZONE, HOUR( 12) }, + { 0, "n", tZONE, HOUR(- 1) }, + { 0, "o", tZONE, HOUR(- 2) }, + { 0, "p", tZONE, HOUR(- 3) }, + { 0, "q", tZONE, HOUR(- 4) }, + { 0, "r", tZONE, HOUR(- 5) }, + { 0, "s", tZONE, HOUR(- 6) }, + { 0, "t", tZONE, HOUR(- 7) }, + { 0, "u", tZONE, HOUR(- 8) }, + { 0, "v", tZONE, HOUR(- 9) }, + { 0, "w", tZONE, HOUR(-10) }, + { 0, "x", tZONE, HOUR(-11) }, + { 0, "y", tZONE, HOUR(-12) }, + { 0, "z", tZONE, HOUR( 0) }, + + /* End of table. */ + { 0, NULL, 0, 0 } +}; + + + + +/* ARGSUSED */ +static int +yyerror(const char *s) +{ + (void)s; + return 0; +} + +static time_t +ToSeconds(time_t Hours, time_t Minutes, time_t Seconds) +{ + if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59) + return -1; + if (Hours < 0 || Hours > 23) + return -1; + return (Hours * 60L + Minutes) * 60L + Seconds; +} + + +/* Year is either + * A number from 0 to 99, which means a year from 1970 to 2069, or + * The actual year (>=100). */ +static time_t +Convert(time_t Month, time_t Day, time_t Year, + time_t Hours, time_t Minutes, time_t Seconds, DSTMODE DSTmode) +{ + static int DaysInMonth[12] = { + 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + time_t tod; + time_t Julian; + int i; + + if (Year < 69) + Year += 2000; + else if (Year < 100) + Year += 1900; + DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) + ? 29 : 28; + /* Checking for 2038 bogusly assumes that time_t is 32 bits. But + I'm too lazy to try to check for time_t overflow in another way. */ + if (Year < EPOCH || Year > 2038 + || Month < 1 || Month > 12 + /* Lint fluff: "conversion from long may lose accuracy" */ + || Day < 1 || Day > DaysInMonth[(int)--Month]) + return -1; + + Julian = Day - 1; + for (i = 0; i < Month; i++) + Julian += DaysInMonth[i]; + for (i = EPOCH; i < Year; i++) + Julian += 365 + (i % 4 == 0); + Julian *= SECSPERDAY; + Julian += yyTimezone * 60L; + if ((tod = ToSeconds(Hours, Minutes, Seconds)) < 0) + return -1; + Julian += tod; + if (DSTmode == DSTon + || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst)) + Julian -= 60 * 60; + return Julian; +} + + +static time_t +DSTcorrect(time_t Start, time_t Future) +{ + time_t StartDay; + time_t FutureDay; + + StartDay = (localtime(&Start)->tm_hour + 1) % 24; + FutureDay = (localtime(&Future)->tm_hour + 1) % 24; + return (Future - Start) + (StartDay - FutureDay) * 60L * 60L; +} + + +static time_t +RelativeDate(time_t Start, time_t DayOrdinal, time_t DayNumber) +{ + struct tm *tm; + time_t now; + + now = Start; + tm = localtime(&now); + now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7); + now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1); + return DSTcorrect(Start, now); +} + + +static time_t +RelativeMonth(time_t Start, time_t RelMonth) +{ + struct tm *tm; + time_t Month; + time_t Year; + + if (RelMonth == 0) + return 0; + tm = localtime(&Start); + Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth; + Year = Month / 12; + Month = Month % 12 + 1; + return DSTcorrect(Start, + Convert(Month, (time_t)tm->tm_mday, Year, + (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec, + DSTmaybe)); +} + +static int +yylex(void) +{ + char c; + char buff[64]; + + for ( ; ; ) { + while (isspace(*yyInput)) + yyInput++; + + /* Skip parenthesized comments. */ + if (*yyInput == '(') { + int Count = 0; + do { + c = *yyInput++; + if (c == '\0') + return c; + if (c == '(') + Count++; + else if (c == ')') + Count--; + } while (Count > 0); + continue; + } + + /* Try the next token in the word table first. */ + /* This allows us to match "2nd", for example. */ + { + char *src = yyInput; + const struct TABLE *tp; + unsigned i = 0; + + /* Force to lowercase and strip '.' characters. */ + while (*src != '\0' + && (isalnum(*src) || *src == '.') + && i < sizeof(buff)-1) { + if (*src != '.') { + if (isupper(*src)) + buff[i++] = tolower(*src); + else + buff[i++] = *src; + } + src++; + } + buff[i++] = '\0'; + + /* + * Find the first match. If the word can be + * abbreviated, make sure we match at least + * the minimum abbreviation. + */ + for (tp = TimeWords; tp->name; tp++) { + size_t abbrev = tp->abbrev; + if (abbrev == 0) + abbrev = strlen(tp->name); + if (strlen(buff) >= abbrev + && strncmp(tp->name, buff, strlen(buff)) + == 0) { + /* Skip over token. */ + yyInput = src; + /* Return the match. */ + yylval.Number = tp->value; + return tp->type; + } + } + } + + /* + * Not in the word table, maybe it's a number. Note: + * Because '-' and '+' have other special meanings, I + * don't deal with signed numbers here. + */ + if (isdigit(c = *yyInput)) { + for (yylval.Number = 0; isdigit(c = *yyInput++); ) + yylval.Number = 10 * yylval.Number + c - '0'; + yyInput--; + return (tUNUMBER); + } + + return (*yyInput++); + } +} + +#define TM_YEAR_ORIGIN 1900 + +/* Yield A - B, measured in seconds. */ +static long +difftm (struct tm *a, struct tm *b) +{ + int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); + int by = b->tm_year + (TM_YEAR_ORIGIN - 1); + int days = ( + /* difference in day of year */ + a->tm_yday - b->tm_yday + /* + intervening leap days */ + + ((ay >> 2) - (by >> 2)) + - (ay/100 - by/100) + + ((ay/100 >> 2) - (by/100 >> 2)) + /* + difference in years * 365 */ + + (long)(ay-by) * 365 + ); + return (60*(60*(24*days + (a->tm_hour - b->tm_hour)) + + (a->tm_min - b->tm_min)) + + (a->tm_sec - b->tm_sec)); +} + +time_t +get_date(char *p) +{ + struct tm *tm; + struct tm gmt, *gmt_ptr; + time_t Start; + time_t tod; + time_t nowtime; + long tzone; + + memset(&gmt, 0, sizeof(gmt)); + yyInput = p; + + (void)time (&nowtime); + + gmt_ptr = gmtime (&nowtime); + if (gmt_ptr != NULL) { + /* Copy, in case localtime and gmtime use the same buffer. */ + gmt = *gmt_ptr; + } + + if (! (tm = localtime (&nowtime))) + return -1; + + if (gmt_ptr != NULL) + tzone = difftm (&gmt, tm) / 60; + else + /* This system doesn't understand timezones; fake it. */ + tzone = 0; + if(tm->tm_isdst) + tzone += 60; + + yyYear = tm->tm_year + 1900; + yyMonth = tm->tm_mon + 1; + yyDay = tm->tm_mday; + yyTimezone = tzone; + yyDSTmode = DSTmaybe; + yyHour = 0; + yyMinutes = 0; + yySeconds = 0; + yyRelSeconds = 0; + yyRelMonth = 0; + yyHaveDate = 0; + yyHaveDay = 0; + yyHaveRel = 0; + yyHaveTime = 0; + yyHaveZone = 0; + + if (yyparse() + || yyHaveTime > 1 || yyHaveZone > 1 + || yyHaveDate > 1 || yyHaveDay > 1) + return -1; + + if (yyHaveDate || yyHaveTime || yyHaveDay) { + Start = Convert(yyMonth, yyDay, yyYear, + yyHour, yyMinutes, yySeconds, yyDSTmode); + if (Start < 0) + return -1; + } else { + Start = nowtime; + if (!yyHaveRel) + Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec; + } + + Start += yyRelSeconds; + Start += RelativeMonth(Start, yyRelMonth); + + if (yyHaveDay && !yyHaveDate) { + tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber); + Start += tod; + } + + /* Have to do *something* with a legitimate -1 so it's + * distinguishable from the error return value. (Alternately + * could set errno on error.) */ + return Start == -1 ? 0 : Start; +} + + +#if defined(TEST) + +/* ARGSUSED */ +int +main(int argc, char **argv) +{ + time_t d; + + while (*++argv != NULL) { + (void)printf("Input: %s\n", *argv); + d = get_date(*argv); + if (d == -1) + (void)printf("Bad format - couldn't convert.\n"); + else + (void)printf("Output: %s\n", ctime(&d)); + } + exit(0); + /* NOTREACHED */ +} +#endif /* defined(TEST) */ diff --git a/contrib/libarchive-2.1/tar/matching.c b/contrib/libarchive-2.1/tar/matching.c new file mode 100644 index 0000000000..f0ca0c9abb --- /dev/null +++ b/contrib/libarchive-2.1/tar/matching.c @@ -0,0 +1,421 @@ +/*- + * 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: src/usr.bin/tar/matching.c,v 1.11 2007/03/11 10:36:42 kientzle Exp $"); + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "bsdtar.h" + +struct match { + struct match *next; + int matches; + char pattern[1]; +}; + +struct matching { + struct match *exclusions; + int exclusions_count; + struct match *inclusions; + int inclusions_count; + int inclusions_unmatched_count; +}; + + +static void add_pattern(struct bsdtar *, struct match **list, + const char *pattern); +static int bsdtar_fnmatch(const char *p, const char *s); +static void initialize_matching(struct bsdtar *); +static int match_exclusion(struct match *, const char *pathname); +static int match_inclusion(struct match *, const char *pathname); + +/* + * The matching logic here needs to be re-thought. I started out to + * try to mimic gtar's matching logic, but it's not entirely + * consistent. In particular 'tar -t' and 'tar -x' interpret patterns + * on the command line as anchored, but --exclude doesn't. + */ + +/* + * Utility functions to manage exclusion/inclusion patterns + */ + +int +exclude(struct bsdtar *bsdtar, const char *pattern) +{ + struct matching *matching; + + if (bsdtar->matching == NULL) + initialize_matching(bsdtar); + matching = bsdtar->matching; + add_pattern(bsdtar, &(matching->exclusions), pattern); + matching->exclusions_count++; + return (0); +} + +int +exclude_from_file(struct bsdtar *bsdtar, const char *pathname) +{ + return (process_lines(bsdtar, pathname, &exclude)); +} + +int +include(struct bsdtar *bsdtar, const char *pattern) +{ + struct matching *matching; + + if (bsdtar->matching == NULL) + initialize_matching(bsdtar); + matching = bsdtar->matching; + add_pattern(bsdtar, &(matching->inclusions), pattern); + matching->inclusions_count++; + matching->inclusions_unmatched_count++; + return (0); +} + +int +include_from_file(struct bsdtar *bsdtar, const char *pathname) +{ + return (process_lines(bsdtar, pathname, &include)); +} + +static void +add_pattern(struct bsdtar *bsdtar, struct match **list, const char *pattern) +{ + struct match *match; + + match = malloc(sizeof(*match) + strlen(pattern) + 1); + if (match == NULL) + bsdtar_errc(bsdtar, 1, errno, "Out of memory"); + if (pattern[0] == '/') + pattern++; + strcpy(match->pattern, pattern); + /* Both "foo/" and "foo" should match "foo/bar". */ + if (match->pattern[strlen(match->pattern)-1] == '/') + match->pattern[strlen(match->pattern)-1] = '\0'; + match->next = *list; + *list = match; + match->matches = 0; +} + + +int +excluded(struct bsdtar *bsdtar, const char *pathname) +{ + struct matching *matching; + struct match *match; + struct match *matched; + + matching = bsdtar->matching; + if (matching == NULL) + return (0); + + /* Exclusions take priority */ + for (match = matching->exclusions; match != NULL; match = match->next){ + if (match_exclusion(match, pathname)) + return (1); + } + + /* Then check for inclusions */ + matched = NULL; + for (match = matching->inclusions; match != NULL; match = match->next){ + if (match_inclusion(match, pathname)) { + /* + * If this pattern has never been matched, + * then we're done. + */ + if (match->matches == 0) { + match->matches++; + matching->inclusions_unmatched_count++; + return (0); + } + /* + * Otherwise, remember the match but keep checking + * in case we can tick off an unmatched pattern. + */ + matched = match; + } + } + /* + * We didn't find a pattern that had never been matched, but + * we did find a match, so count it and exit. + */ + if (matched != NULL) { + matched->matches++; + return (0); + } + + /* If there were inclusions, default is to exclude. */ + if (matching->inclusions != NULL) + return (1); + + /* No explicit inclusions, default is to match. */ + return (0); +} + +/* + * This is a little odd, but it matches the default behavior of + * gtar. In particular, 'a*b' will match 'foo/a1111/222b/bar' + * + */ +int +match_exclusion(struct match *match, const char *pathname) +{ + const char *p; + + if (*match->pattern == '*' || *match->pattern == '/') + return (bsdtar_fnmatch(match->pattern, pathname) == 0); + + for (p = pathname; p != NULL; p = strchr(p, '/')) { + if (*p == '/') + p++; + if (bsdtar_fnmatch(match->pattern, p) == 0) + return (1); + } + return (0); +} + +/* + * Again, mimic gtar: inclusions are always anchored (have to match + * the beginning of the path) even though exclusions are not anchored. + */ +int +match_inclusion(struct match *match, const char *pathname) +{ + return (bsdtar_fnmatch(match->pattern, pathname) == 0); +} + +void +cleanup_exclusions(struct bsdtar *bsdtar) +{ + struct match *p, *q; + + if (bsdtar->matching) { + p = bsdtar->matching->inclusions; + while (p != NULL) { + q = p; + p = p->next; + free(q); + } + p = bsdtar->matching->exclusions; + while (p != NULL) { + q = p; + p = p->next; + free(q); + } + free(bsdtar->matching); + } +} + +static void +initialize_matching(struct bsdtar *bsdtar) +{ + bsdtar->matching = malloc(sizeof(*bsdtar->matching)); + if (bsdtar->matching == NULL) + bsdtar_errc(bsdtar, 1, errno, "No memory"); + memset(bsdtar->matching, 0, sizeof(*bsdtar->matching)); +} + +int +unmatched_inclusions(struct bsdtar *bsdtar) +{ + struct matching *matching; + + matching = bsdtar->matching; + if (matching == NULL) + return (0); + return (matching->inclusions_unmatched_count); +} + + + +#if defined(HAVE_FNMATCH) && defined(HAVE_FNM_LEADING_DIR) + +/* Use system fnmatch() if it suits our needs. */ +/* On Linux, _GNU_SOURCE must be defined to get FNM_LEADING_DIR. */ +#define _GNU_SOURCE +#include +static int +bsdtar_fnmatch(const char *pattern, const char *string) +{ + return (fnmatch(pattern, string, FNM_LEADING_DIR)); +} + +#else +/* + * The following was hacked from BSD C library + * code: src/lib/libc/gen/fnmatch.c,v 1.15 2002/02/01 + * + * In particular, most of the flags were ripped out: this always + * behaves like FNM_LEADING_DIR is set and other flags specified + * by POSIX are unset. + * + * Normally, I would not conditionally compile something like this: If + * I have to support it anyway, everyone may as well use it. ;-) + * However, the full POSIX spec for fnmatch() includes a lot of + * advanced character handling that I'm not ready to put in here, so + * it's probably best if people use a local version when it's available. + */ + +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Guido van Rossum. + * + * 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. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. + */ + +static int +bsdtar_fnmatch(const char *pattern, const char *string) +{ + const char *saved_pattern; + int negate, matched; + char c; + + for (;;) { + switch (c = *pattern++) { + case '\0': + if (*string == '/' || *string == '\0') + return (0); + return (1); + case '?': + if (*string == '\0') + return (1); + ++string; + break; + case '*': + c = *pattern; + /* Collapse multiple stars. */ + while (c == '*') + c = *++pattern; + + /* Optimize for pattern with * at end. */ + if (c == '\0') + return (0); + + /* General case, use recursion. */ + while (*string != '\0') { + if (!bsdtar_fnmatch(pattern, string)) + return (0); + ++string; + } + return (1); + case '[': + if (*string == '\0') + return (1); + saved_pattern = pattern; + if (*pattern == '!' || *pattern == '^') { + negate = 1; + ++pattern; + } else + negate = 0; + matched = 0; + c = *pattern++; + do { + if (c == '\\') + c = *pattern++; + if (c == '\0') { + pattern = saved_pattern; + c = '['; + goto norm; + } + if (*pattern == '-') { + char c2 = *(pattern + 1); + if (c2 == '\0') { + pattern = saved_pattern; + c = '['; + goto norm; + } + if (c2 == ']') { + /* [a-] is not a range. */ + if (c == *string + || '-' == *string) + matched = 1; + pattern ++; + } else { + if (c <= *string + && *string <= c2) + matched = 1; + pattern += 2; + } + } else if (c == *string) + matched = 1; + c = *pattern++; + } while (c != ']'); + if (matched == negate) + return (1); + ++string; + break; + case '\\': + if ((c = *pattern++) == '\0') { + c = '\\'; + --pattern; + } + /* FALLTHROUGH */ + default: + norm: + if (c != *string) + return (1); + string++; + break; + } + } + /* NOTREACHED */ +} + +#endif diff --git a/contrib/libarchive-2.1/tar/read.c b/contrib/libarchive-2.1/tar/read.c new file mode 100644 index 0000000000..e84725b758 --- /dev/null +++ b/contrib/libarchive-2.1/tar/read.c @@ -0,0 +1,356 @@ +/*- + * 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: src/usr.bin/tar/read.c,v 1.31 2007/04/16 04:04:50 cperciva Exp $"); + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef MAJOR_IN_MKDEV +#include +#elif defined(MAJOR_IN_SYSMACROS) +#include +#endif +#ifdef HAVE_SYS_PARAM_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_GRP_H +#include +#endif +#ifdef HAVE_LIMITS_H +#include +#endif +#ifdef HAVE_PWD_H +#include +#endif +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_TIME_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "bsdtar.h" + +static void list_item_verbose(struct bsdtar *, FILE *, + struct archive_entry *); +static void read_archive(struct bsdtar *bsdtar, char mode); + +void +tar_mode_t(struct bsdtar *bsdtar) +{ + read_archive(bsdtar, 't'); +} + +void +tar_mode_x(struct bsdtar *bsdtar) +{ + read_archive(bsdtar, 'x'); +} + +/* + * Handle 'x' and 't' modes. + */ +static void +read_archive(struct bsdtar *bsdtar, char mode) +{ + FILE *out; + struct archive *a; + struct archive_entry *entry; + const struct stat *st; + int r; + + while (*bsdtar->argv) { + include(bsdtar, *bsdtar->argv); + bsdtar->argv++; + } + + if (bsdtar->names_from_file != NULL) + include_from_file(bsdtar, bsdtar->names_from_file); + + a = archive_read_new(); + if (bsdtar->compress_program != NULL) + archive_read_support_compression_program(a, bsdtar->compress_program); + else + archive_read_support_compression_all(a); + archive_read_support_format_all(a); + if (archive_read_open_file(a, bsdtar->filename, + bsdtar->bytes_per_block != 0 ? bsdtar->bytes_per_block : + DEFAULT_BYTES_PER_BLOCK)) + bsdtar_errc(bsdtar, 1, 0, "Error opening archive: %s", + archive_error_string(a)); + + do_chdir(bsdtar); + for (;;) { + /* Support --fast-read option */ + if (bsdtar->option_fast_read && + unmatched_inclusions(bsdtar) == 0) + break; + + r = archive_read_next_header(a, &entry); + if (r == ARCHIVE_EOF) + break; + if (r < ARCHIVE_OK) + bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); + if (r == ARCHIVE_RETRY) { + /* Retryable error: try again */ + bsdtar_warnc(bsdtar, 0, "Retrying..."); + continue; + } + if (r != ARCHIVE_OK) { + bsdtar->return_value = 1; + break; + } + + /* + * Exclude entries that are too old. + */ + st = archive_entry_stat(entry); + if (bsdtar->newer_ctime_sec > 0) { + if (st->st_ctime < bsdtar->newer_ctime_sec) + continue; /* Too old, skip it. */ + if (st->st_ctime == bsdtar->newer_ctime_sec + && ARCHIVE_STAT_CTIME_NANOS(st) + <= bsdtar->newer_ctime_nsec) + continue; /* Too old, skip it. */ + } + if (bsdtar->newer_mtime_sec > 0) { + if (st->st_mtime < bsdtar->newer_mtime_sec) + continue; /* Too old, skip it. */ + if (st->st_mtime == bsdtar->newer_mtime_sec + && ARCHIVE_STAT_MTIME_NANOS(st) + <= bsdtar->newer_mtime_nsec) + continue; /* Too old, skip it. */ + } + + /* + * Note that pattern exclusions are checked before + * pathname rewrites are handled. This gives more + * control over exclusions, since rewrites always lose + * information. (For example, consider a rewrite + * s/foo[0-9]/foo/. If we check exclusions after the + * rewrite, there would be no way to exclude foo1/bar + * while allowing foo2/bar.) + */ + if (excluded(bsdtar, archive_entry_pathname(entry))) + continue; /* Excluded by a pattern test. */ + + /* + * Modify the pathname as requested by the user. We + * do this for -t as well to give users a way to + * preview the effects of their rewrites. We also do + * this before extraction security checks (including + * leading '/' removal). Note that some rewrite + * failures prevent extraction. + */ + if (edit_pathname(bsdtar, entry)) + continue; /* Excluded by a rewrite failure. */ + + if (mode == 't') { + /* Perversely, gtar uses -O to mean "send to stderr" + * when used with -t. */ + out = bsdtar->option_stdout ? stderr : stdout; + + if (bsdtar->verbose < 2) + safe_fprintf(out, "%s", + archive_entry_pathname(entry)); + else + list_item_verbose(bsdtar, out, entry); + fflush(out); + r = archive_read_data_skip(a); + if (r == ARCHIVE_WARN) { + fprintf(out, "\n"); + bsdtar_warnc(bsdtar, 0, "%s", + archive_error_string(a)); + } + if (r == ARCHIVE_RETRY) { + fprintf(out, "\n"); + bsdtar_warnc(bsdtar, 0, "%s", + archive_error_string(a)); + } + if (r == ARCHIVE_FATAL) { + fprintf(out, "\n"); + bsdtar_warnc(bsdtar, 0, "%s", + archive_error_string(a)); + break; + } + fprintf(out, "\n"); + } else { + if (bsdtar->option_interactive && + !yes("extract '%s'", archive_entry_pathname(entry))) + continue; + + /* + * Format here is from SUSv2, including the + * deferred '\n'. + */ + if (bsdtar->verbose) { + safe_fprintf(stderr, "x %s", + archive_entry_pathname(entry)); + fflush(stderr); + } + if (bsdtar->option_stdout) + r = archive_read_data_into_fd(a, 1); + else + r = archive_read_extract(a, entry, + bsdtar->extract_flags); + if (r != ARCHIVE_OK) { + if (!bsdtar->verbose) + safe_fprintf(stderr, "%s", + archive_entry_pathname(entry)); + safe_fprintf(stderr, ": %s", + archive_error_string(a)); + if (!bsdtar->verbose) + fprintf(stderr, "\n"); + bsdtar->return_value = 1; + } + if (bsdtar->verbose) + fprintf(stderr, "\n"); + if (r == ARCHIVE_FATAL) + break; + } + } + + if (bsdtar->verbose > 2) + fprintf(stdout, "Archive Format: %s, Compression: %s\n", + archive_format_name(a), archive_compression_name(a)); + + archive_read_finish(a); +} + + +/* + * 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) +{ + const struct stat *st; + char tmp[100]; + size_t w; + const char *p; + const char *fmt; + time_t tim; + static time_t now; + + st = archive_entry_stat(entry); + + /* + * 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); + bsdtar_strmode(entry, tmp); + fprintf(out, "%s %d ", tmp, (int)(st->st_nlink)); + + /* Use uname if it's present, else uid. */ + p = archive_entry_uname(entry); + if ((p == NULL) || (*p == '\0')) { + sprintf(tmp, "%lu ", (unsigned long)st->st_uid); + 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)st->st_gid); + 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 (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) { + sprintf(tmp, "%lu,%lu", + (unsigned long)major(st->st_rdev), + (unsigned long)minor(st->st_rdev)); /* ls(1) also casts here. */ + } else { + /* + * Note the use of platform-dependent macros to format + * the filesize here. We need the format string and the + * corresponding type for the cast. + */ + sprintf(tmp, BSDTAR_FILESIZE_PRINTF, + (BSDTAR_FILESIZE_TYPE)st->st_size); + } + 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 = (time_t)st->st_mtime; + if (abs(tim - now) > (365/2)*86400) + fmt = bsdtar->day_first ? "%e %b %Y" : "%b %e %Y"; + else + fmt = bsdtar->day_first ? "%e %b %R" : "%b %e %R"; + 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 (S_ISLNK(st->st_mode)) /* Symbolic link */ + safe_fprintf(out, " -> %s", archive_entry_symlink(entry)); +} diff --git a/contrib/libarchive-2.1/tar/tree.c b/contrib/libarchive-2.1/tar/tree.c new file mode 100644 index 0000000000..23e64e3da0 --- /dev/null +++ b/contrib/libarchive-2.1/tar/tree.c @@ -0,0 +1,542 @@ +/*- + * 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. + */ + +/*- + * This is a new directory-walking system that addresses a number + * of problems I've had with fts(3). In particular, it has no + * pathname-length limits (other than the size of 'int'), handles + * deep logical traversals, uses considerably less memory, and has + * an opaque interface (easier to modify in the future). + * + * Internally, it keeps a single list of "tree_entry" items that + * represent filesystem objects that require further attention. + * Non-directories are not kept in memory: they are pulled from + * readdir(), returned to the client, then freed as soon as possible. + * Any directory entry to be traversed gets pushed onto the stack. + * + * There is surprisingly little information that needs to be kept for + * each item on the stack. Just the name, depth (represented here as the + * string length of the parent directory's pathname), and some markers + * indicating how to get back to the parent (via chdir("..") for a + * regular dir or via fchdir(2) for a symlink). + */ +#include "bsdtar_platform.h" +__FBSDID("$FreeBSD: src/usr.bin/tar/tree.c,v 1.8 2007/03/11 10:36:42 kientzle Exp $"); + +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_DIRENT_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "tree.h" + +/* + * TODO: + * 1) Loop checking. + * 3) Arbitrary logical traversals by closing/reopening intermediate fds. + */ + +struct tree_entry { + struct tree_entry *next; + struct tree_entry *parent; + char *name; + size_t dirname_length; + dev_t dev; + ino_t ino; + int fd; + int flags; +}; + +/* Definitions for tree_entry.flags bitmap. */ +#define isDir 1 /* This entry is a regular directory. */ +#define isDirLink 2 /* This entry is a symbolic link to a directory. */ +#define needsPreVisit 4 /* This entry needs to be previsited. */ +#define needsPostVisit 8 /* This entry needs to be postvisited. */ + +/* + * Local data for this package. + */ +struct tree { + struct tree_entry *stack; + struct tree_entry *current; + DIR *d; + int initialDirFd; + int flags; + int visit_type; + int tree_errno; /* Error code from last failed operation. */ + + char *buff; + const char *basename; + size_t buff_length; + size_t path_length; + size_t dirname_length; + + int depth; + int openCount; + int maxOpenCount; + + struct stat lst; + struct stat st; +}; + +/* Definitions for tree.flags bitmap. */ +#define needsReturn 8 /* Marks first entry as not having been returned yet. */ +#define hasStat 16 /* The st entry is set. */ +#define hasLstat 32 /* The lst entry is set. */ + + +#ifdef HAVE_DIRENT_D_NAMLEN +/* BSD extension; avoids need for a strlen() call. */ +#define D_NAMELEN(dp) (dp)->d_namlen +#else +#define D_NAMELEN(dp) (strlen((dp)->d_name)) +#endif + +#if 0 +#include +void +tree_dump(struct tree *t, FILE *out) +{ + struct tree_entry *te; + + fprintf(out, "\tdepth: %d\n", t->depth); + fprintf(out, "\tbuff: %s\n", t->buff); + fprintf(out, "\tpwd: "); fflush(stdout); system("pwd"); + fprintf(out, "\taccess: %s\n", t->basename); + fprintf(out, "\tstack:\n"); + for (te = t->stack; te != NULL; te = te->next) { + fprintf(out, "\t\tte->name: %s%s%s\n", te->name, + te->flags & needsPreVisit ? "" : " *", + t->current == te ? " (current)" : ""); + } +} +#endif + +/* + * Add a directory path to the current stack. + */ +static void +tree_push(struct tree *t, const char *path) +{ + struct tree_entry *te; + + te = malloc(sizeof(*te)); + memset(te, 0, sizeof(*te)); + te->next = t->stack; + t->stack = te; + te->fd = -1; + te->name = strdup(path); + te->flags = needsPreVisit | needsPostVisit; + te->dirname_length = t->dirname_length; +} + +/* + * Append a name to the current path. + */ +static void +tree_append(struct tree *t, const char *name, size_t name_length) +{ + char *p; + + if (t->buff != NULL) + t->buff[t->dirname_length] = '\0'; + /* Strip trailing '/' from name, unless entire name is "/". */ + while (name_length > 1 && name[name_length - 1] == '/') + name_length--; + + /* Resize pathname buffer as needed. */ + while (name_length + 1 + t->dirname_length >= t->buff_length) { + t->buff_length *= 2; + if (t->buff_length < 1024) + t->buff_length = 1024; + t->buff = realloc(t->buff, t->buff_length); + } + p = t->buff + t->dirname_length; + t->path_length = t->dirname_length + name_length; + /* Add a separating '/' if it's needed. */ + if (t->dirname_length > 0 && p[-1] != '/') { + *p++ = '/'; + t->path_length ++; + } + strncpy(p, name, name_length); + p[name_length] = '\0'; + t->basename = p; +} + +/* + * Open a directory tree for traversal. + */ +struct tree * +tree_open(const char *path) +{ + struct tree *t; + + t = malloc(sizeof(*t)); + memset(t, 0, sizeof(*t)); + tree_append(t, path, strlen(path)); + t->initialDirFd = open(".", O_RDONLY); + /* + * During most of the traversal, items are set up and then + * returned immediately from tree_next(). That doesn't work + * for the very first entry, so we set a flag for this special + * case. + */ + t->flags = needsReturn; + return (t); +} + +/* + * We've finished a directory; ascend back to the parent. + */ +static void +tree_ascend(struct tree *t) +{ + struct tree_entry *te; + + te = t->stack; + t->depth--; + if (te->flags & isDirLink) { + fchdir(te->fd); + close(te->fd); + t->openCount--; + } else { + chdir(".."); + } +} + +/* + * Pop the working stack. + */ +static void +tree_pop(struct tree *t) +{ + struct tree_entry *te; + + t->buff[t->dirname_length] = '\0'; + if (t->stack == t->current && t->current != NULL) + t->current = t->current->parent; + te = t->stack; + t->stack = te->next; + t->dirname_length = te->dirname_length; + t->basename = t->buff + t->dirname_length; + /* Special case: starting dir doesn't skip leading '/'. */ + if (t->dirname_length > 0) + t->basename++; + free(te->name); + free(te); +} + +/* + * Get the next item in the tree traversal. + */ +int +tree_next(struct tree *t) +{ + struct dirent *de = NULL; + + /* Handle the startup case by returning the initial entry. */ + if (t->flags & needsReturn) { + t->flags &= ~needsReturn; + return (t->visit_type = TREE_REGULAR); + } + + while (t->stack != NULL) { + /* If there's an open dir, get the next entry from there. */ + while (t->d != NULL) { + de = readdir(t->d); + if (de == NULL) { + closedir(t->d); + t->d = NULL; + } else if (de->d_name[0] == '.' + && de->d_name[1] == '\0') { + /* Skip '.' */ + } else if (de->d_name[0] == '.' + && de->d_name[1] == '.' + && de->d_name[2] == '\0') { + /* Skip '..' */ + } else { + /* + * Append the path to the current path + * and return it. + */ + tree_append(t, de->d_name, D_NAMELEN(de)); + t->flags &= ~hasLstat; + t->flags &= ~hasStat; + return (t->visit_type = TREE_REGULAR); + } + } + + /* If the current dir needs to be visited, set it up. */ + if (t->stack->flags & needsPreVisit) { + t->current = t->stack; + tree_append(t, t->stack->name, strlen(t->stack->name)); + t->stack->flags &= ~needsPreVisit; + /* If it is a link, set up fd for the ascent. */ + if (t->stack->flags & isDirLink) { + t->stack->fd = open(".", O_RDONLY); + t->openCount++; + if (t->openCount > t->maxOpenCount) + t->maxOpenCount = t->openCount; + } + t->dirname_length = t->path_length; + if (chdir(t->stack->name) != 0) { + /* chdir() failed; return error */ + tree_pop(t); + t->tree_errno = errno; + return (t->visit_type = TREE_ERROR_DIR); + } + t->depth++; + t->d = opendir("."); + if (t->d == NULL) { + tree_ascend(t); /* Undo "chdir" */ + tree_pop(t); + t->tree_errno = errno; + return (t->visit_type = TREE_ERROR_DIR); + } + t->flags &= ~hasLstat; + t->flags &= ~hasStat; + t->basename = "."; + return (t->visit_type = TREE_POSTDESCENT); + } + + /* We've done everything necessary for the top stack entry. */ + if (t->stack->flags & needsPostVisit) { + tree_ascend(t); + tree_pop(t); + t->flags &= ~hasLstat; + t->flags &= ~hasStat; + return (t->visit_type = TREE_POSTASCENT); + } + } + return (t->visit_type = 0); +} + +/* + * Return error code. + */ +int +tree_errno(struct tree *t) +{ + return (t->tree_errno); +} + +/* + * Called by the client to mark the directory just returned from + * tree_next() as needing to be visited. + */ +void +tree_descend(struct tree *t) +{ + if (t->visit_type != TREE_REGULAR) + return; + + if (tree_current_is_physical_dir(t)) { + tree_push(t, t->basename); + t->stack->flags |= isDir; + } else if (tree_current_is_dir(t)) { + tree_push(t, t->basename); + t->stack->flags |= isDirLink; + } +} + +/* + * Get the stat() data for the entry just returned from tree_next(). + */ +const struct stat * +tree_current_stat(struct tree *t) +{ + if (!(t->flags & hasStat)) { + if (stat(t->basename, &t->st) != 0) + return NULL; + t->flags |= hasStat; + } + return (&t->st); +} + +/* + * Get the lstat() data for the entry just returned from tree_next(). + */ +const struct stat * +tree_current_lstat(struct tree *t) +{ + if (!(t->flags & hasLstat)) { + if (lstat(t->basename, &t->lst) != 0) + return NULL; + t->flags |= hasLstat; + } + return (&t->lst); +} + +/* + * Test whether current entry is a dir or link to a dir. + */ +int +tree_current_is_dir(struct tree *t) +{ + const struct stat *st; + + /* + * If we already have lstat() info, then try some + * cheap tests to determine if this is a dir. + */ + if (t->flags & hasLstat) { + /* If lstat() says it's a dir, it must be a dir. */ + if (S_ISDIR(tree_current_lstat(t)->st_mode)) + return 1; + /* Not a dir; might be a link to a dir. */ + /* If it's not a link, then it's not a link to a dir. */ + if (!S_ISLNK(tree_current_lstat(t)->st_mode)) + return 0; + /* + * It's a link, but we don't know what it's a link to, + * so we'll have to use stat(). + */ + } + + st = tree_current_stat(t); + /* If we can't stat it, it's not a dir. */ + if (st == NULL) + return 0; + /* Use the definitive test. Hopefully this is cached. */ + return (S_ISDIR(st->st_mode)); +} + +/* + * Test whether current entry is a physical directory. Usually, we + * already have at least one of stat() or lstat() in memory, so we + * use tricks to try to avoid an extra trip to the disk. + */ +int +tree_current_is_physical_dir(struct tree *t) +{ + const struct stat *st; + + /* + * If stat() says it isn't a dir, then it's not a dir. + * If stat() data is cached, this check is free, so do it first. + */ + if ((t->flags & hasStat) + && (!S_ISDIR(tree_current_stat(t)->st_mode))) + return 0; + + /* + * Either stat() said it was a dir (in which case, we have + * to determine whether it's really a link to a dir) or + * stat() info wasn't available. So we use lstat(), which + * hopefully is already cached. + */ + + st = tree_current_lstat(t); + /* If we can't stat it, it's not a dir. */ + if (st == NULL) + return 0; + /* Use the definitive test. Hopefully this is cached. */ + return (S_ISDIR(st->st_mode)); +} + +/* + * Test whether current entry is a symbolic link. + */ +int +tree_current_is_physical_link(struct tree *t) +{ + const struct stat *st = tree_current_lstat(t); + if (st == NULL) + return 0; + return (S_ISLNK(st->st_mode)); +} + +/* + * Return the access path for the entry just returned from tree_next(). + */ +const char * +tree_current_access_path(struct tree *t) +{ + return (t->basename); +} + +/* + * Return the full path for the entry just returned from tree_next(). + */ +const char * +tree_current_path(struct tree *t) +{ + return (t->buff); +} + +/* + * Return the length of the path for the entry just returned from tree_next(). + */ +size_t +tree_current_pathlen(struct tree *t) +{ + return (t->path_length); +} + +/* + * Return the nesting depth of the entry just returned from tree_next(). + */ +int +tree_current_depth(struct tree *t) +{ + return (t->depth); +} + +/* + * Terminate the traversal and release any resources. + */ +void +tree_close(struct tree *t) +{ + /* Release anything remaining in the stack. */ + while (t->stack != NULL) + tree_pop(t); + if (t->buff) + free(t->buff); + /* chdir() back to where we started. */ + if (t->initialDirFd >= 0) { + fchdir(t->initialDirFd); + close(t->initialDirFd); + t->initialDirFd = -1; + } + free(t); +} diff --git a/contrib/libarchive-2.1/tar/tree.h b/contrib/libarchive-2.1/tar/tree.h new file mode 100644 index 0000000000..a32a15f8f3 --- /dev/null +++ b/contrib/libarchive-2.1/tar/tree.h @@ -0,0 +1,115 @@ +/*- + * 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. + * + * $FreeBSD: src/usr.bin/tar/tree.h,v 1.3 2007/01/09 08:12:17 kientzle Exp $ + */ + +/*- + * A set of routines for traversing directory trees. + * Similar in concept to the fts library, but with a few + * important differences: + * * Uses less memory. In particular, fts stores an entire directory + * in memory at a time. This package only keeps enough subdirectory + * information in memory to track the traversal. Information + * about non-directories is discarded as soon as possible. + * * Supports very deep logical traversals. The fts package + * uses "non-chdir" approach for logical traversals. This + * package does use a chdir approach for logical traversals + * and can therefore handle pathnames much longer than + * PATH_MAX. + * * Supports deep physical traversals "out of the box." + * Due to the memory optimizations above, there's no need to + * limit dir names to 32k. + */ + +#include +#include + +struct tree; + +/* Initiate/terminate a tree traversal. */ +struct tree *tree_open(const char * /* pathname */); +void tree_close(struct tree *); + +/* + * tree_next() returns Zero if there is no next entry, non-zero if there is. + * Note that directories are potentially visited three times. The first + * time as "regular" file. If tree_descend() is invoked at that time, + * the directory is added to a work list and will be visited two more + * times: once just after descending into the directory and again + * just after ascending back to the parent. + * + * TREE_ERROR is returned if the descent failed (because the + * directory couldn't be opened, for instance). This is returned + * instead of TREE_PREVISIT/TREE_POSTVISIT. + */ +#define TREE_REGULAR 1 +#define TREE_POSTDESCENT 2 +#define TREE_POSTASCENT 3 +#define TREE_ERROR_DIR -1 +int tree_next(struct tree *); + +int tree_errno(struct tree *); + +/* + * Request that current entry be visited. If you invoke it on every + * directory, you'll get a physical traversal. This is ignored if the + * current entry isn't a directory or a link to a directory. So, if + * you invoke this on every returned path, you'll get a full logical + * traversal. + */ +void tree_descend(struct tree *); + +/* + * Return information about the current entry. + */ + +int tree_current_depth(struct tree *); +/* + * The current full pathname, length of the full pathname, + * and a name that can be used to access the file. + * Because tree does use chdir extensively, the access path is + * almost never the same as the full current path. + */ +const char *tree_current_path(struct tree *); +size_t tree_current_pathlen(struct tree *); +const char *tree_current_access_path(struct tree *); +/* + * Request the lstat() or stat() data for the current path. Since the + * tree package needs to do some of this anyway, and caches the + * results, you should take advantage of it here if you need it rather + * than make a redundant stat() or lstat() call of your own. + */ +const struct stat *tree_current_stat(struct tree *); +const struct stat *tree_current_lstat(struct tree *); +/* The following tests may use mechanisms much faster than stat()/lstat(). */ +/* "is_physical_dir" is equivalent to S_ISDIR(tree_current_lstat()->st_mode) */ +int tree_current_is_physical_dir(struct tree *); +/* "is_physical_link" is equivalent to S_ISLNK(tree_current_lstat()->st_mode) */ +int tree_current_is_physical_link(struct tree *); +/* "is_dir" is equivalent to S_ISDIR(tree_current_stat()->st_mode) */ +int tree_current_is_dir(struct tree *); + +/* For testing/debugging: Dump the internal status to the given filehandle. */ +void tree_dump(struct tree *, FILE *); diff --git a/contrib/libarchive-2.1/tar/util.c b/contrib/libarchive-2.1/tar/util.c new file mode 100644 index 0000000000..d4a92c9bef --- /dev/null +++ b/contrib/libarchive-2.1/tar/util.c @@ -0,0 +1,487 @@ +/*- + * 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: src/usr.bin/tar/util.c,v 1.17 2007/04/18 04:36:11 kientzle Exp $"); + +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include /* Linux doesn't define mode_t, etc. in sys/stat.h. */ +#endif +#include +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDARG_H +#include +#endif +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "bsdtar.h" + +static void bsdtar_vwarnc(struct bsdtar *, int code, + const char *fmt, va_list ap); + +/* + * Print a string, taking care with any non-printable characters. + */ + +void +safe_fprintf(FILE *f, const char *fmt, ...) +{ + char *buff; + char *buff_heap; + int buff_length; + int length; + va_list ap; + char *p; + unsigned i; + char buff_stack[256]; + char copy_buff[256]; + + /* Use a stack-allocated buffer if we can, for speed and safety. */ + buff_heap = NULL; + buff_length = sizeof(buff_stack); + buff = buff_stack; + + va_start(ap, fmt); + length = vsnprintf(buff, buff_length, fmt, ap); + va_end(ap); + /* If the result is too large, allocate a buffer on the heap. */ + if (length >= buff_length) { + buff_length = length+1; + buff_heap = malloc(buff_length); + /* Failsafe: use the truncated string if malloc fails. */ + if (buff_heap != NULL) { + buff = buff_heap; + va_start(ap, fmt); + length = vsnprintf(buff, buff_length, fmt, ap); + va_end(ap); + } + } + + /* Write data, expanding unprintable characters. */ + p = buff; + i = 0; + while (*p != '\0') { + unsigned char c = *p++; + + if (isprint(c) && c != '\\') + copy_buff[i++] = c; + else { + copy_buff[i++] = '\\'; + switch (c) { + case '\a': copy_buff[i++] = 'a'; break; + case '\b': copy_buff[i++] = 'b'; break; + case '\f': copy_buff[i++] = 'f'; break; + case '\n': copy_buff[i++] = 'n'; break; +#if '\r' != '\n' + /* On some platforms, \n and \r are the same. */ + case '\r': copy_buff[i++] = 'r'; break; +#endif + case '\t': copy_buff[i++] = 't'; break; + case '\v': copy_buff[i++] = 'v'; break; + case '\\': copy_buff[i++] = '\\'; break; + default: + sprintf(copy_buff + i, "%03o", c); + i += 3; + } + } + + /* If our temp buffer is full, dump it and keep going. */ + if (i > (sizeof(copy_buff) - 8)) { + copy_buff[i++] = '\0'; + fprintf(f, "%s", copy_buff); + i = 0; + } + } + copy_buff[i++] = '\0'; + fprintf(f, "%s", copy_buff); + + /* If we allocated a heap-based buffer, free it now. */ + if (buff_heap != NULL) + free(buff_heap); +} + +static void +bsdtar_vwarnc(struct bsdtar *bsdtar, int code, const char *fmt, va_list ap) +{ + fprintf(stderr, "%s: ", bsdtar->progname); + vfprintf(stderr, fmt, ap); + if (code != 0) + fprintf(stderr, ": %s", strerror(code)); + fprintf(stderr, "\n"); +} + +void +bsdtar_warnc(struct bsdtar *bsdtar, int code, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + bsdtar_vwarnc(bsdtar, code, fmt, ap); + va_end(ap); +} + +void +bsdtar_errc(struct bsdtar *bsdtar, int eval, int code, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + bsdtar_vwarnc(bsdtar, code, fmt, ap); + va_end(ap); + exit(eval); +} + +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)); + if (l <= 0) + return (0); + buff[l] = 0; + + for (p = buff; *p != '\0'; p++) { + if (isspace(0xff & (int)*p)) + continue; + switch(*p) { + case 'y': case 'Y': + return (1); + case 'n': case 'N': + return (0); + default: + return (0); + } + } + + return (0); +} + +void +bsdtar_strmode(struct archive_entry *entry, char *bp) +{ + static const char *perms = "?rwxrwxrwx "; + static const mode_t permbits[] = + { S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP, + S_IROTH, S_IWOTH, S_IXOTH }; + mode_t mode; + int i; + + /* Fill in a default string, then selectively override. */ + strcpy(bp, perms); + + mode = archive_entry_mode(entry); + switch (mode & S_IFMT) { + case S_IFREG: bp[0] = '-'; break; + case S_IFBLK: bp[0] = 'b'; break; + case S_IFCHR: bp[0] = 'c'; break; + case S_IFDIR: bp[0] = 'd'; break; + case S_IFLNK: bp[0] = 'l'; break; + case S_IFSOCK: bp[0] = 's'; break; +#ifdef S_IFIFO + case S_IFIFO: bp[0] = 'p'; break; +#endif +#ifdef S_IFWHT + case S_IFWHT: bp[0] = 'w'; break; +#endif + } + + for (i = 0; i < 9; i++) + if (!(mode & permbits[i])) + bp[i+1] = '-'; + + if (mode & S_ISUID) { + if (mode & S_IXUSR) bp[3] = 's'; + else bp[3] = 'S'; + } + if (mode & S_ISGID) { + if (mode & S_IXGRP) bp[6] = 's'; + else bp[6] = 'S'; + } + if (mode & S_ISVTX) { + if (mode & S_IXOTH) bp[9] = 't'; + else bp[9] = 'T'; + } + if (archive_entry_acl_count(entry, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)) + bp[10] = '+'; +} + + +/* + * Read lines from file and do something with each one. If option_null + * is set, lines are terminated with zero bytes; otherwise, they're + * terminated with newlines. + * + * This uses a self-sizing buffer to handle arbitrarily-long lines. + * If the "process" function returns non-zero for any line, this + * function will return non-zero after attempting to process all + * remaining lines. + */ +int +process_lines(struct bsdtar *bsdtar, const char *pathname, + int (*process)(struct bsdtar *, const char *)) +{ + FILE *f; + char *buff, *buff_end, *line_start, *line_end, *p; + size_t buff_length, bytes_read, bytes_wanted; + int separator; + int ret; + + separator = bsdtar->option_null ? '\0' : '\n'; + ret = 0; + + if (strcmp(pathname, "-") == 0) + f = stdin; + else + f = fopen(pathname, "r"); + if (f == NULL) + bsdtar_errc(bsdtar, 1, errno, "Couldn't open %s", pathname); + buff_length = 8192; + buff = malloc(buff_length); + if (buff == NULL) + bsdtar_errc(bsdtar, 1, ENOMEM, "Can't read %s", pathname); + line_start = line_end = buff_end = buff; + for (;;) { + /* Get some more data into the buffer. */ + bytes_wanted = buff + buff_length - buff_end; + bytes_read = fread(buff_end, 1, bytes_wanted, f); + buff_end += bytes_read; + /* Process all complete lines in the buffer. */ + while (line_end < buff_end) { + if (*line_end == separator) { + *line_end = '\0'; + if ((*process)(bsdtar, line_start) != 0) + ret = -1; + line_start = line_end + 1; + line_end = line_start; + } else + line_end++; + } + if (feof(f)) + break; + if (ferror(f)) + bsdtar_errc(bsdtar, 1, errno, + "Can't read %s", pathname); + if (line_start > buff) { + /* Move a leftover fractional line to the beginning. */ + memmove(buff, line_start, buff_end - line_start); + buff_end -= line_start - buff; + line_end -= line_start - buff; + line_start = buff; + } else { + /* Line is too big; enlarge the buffer. */ + p = realloc(buff, buff_length *= 2); + if (p == NULL) + bsdtar_errc(bsdtar, 1, ENOMEM, + "Line too long in %s", pathname); + buff_end = p + (buff_end - buff); + line_end = p + (line_end - buff); + line_start = buff = p; + } + } + /* At end-of-file, handle the final line. */ + if (line_end > line_start) { + *line_end = '\0'; + if ((*process)(bsdtar, line_start) != 0) + ret = -1; + } + free(buff); + if (f != stdin) + fclose(f); + return (ret); +} + +/*- + * The logic here for -C 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 (newdir[0] == '/') { + /* 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); + if (old_pending[old_len - 1] == '/') + old_pending[old_len - 1] = '\0'; + if (bsdtar->pending_chdir != NULL) + sprintf(bsdtar->pending_chdir, "%s/%s", + old_pending, newdir); + free(old_pending); + } + if (bsdtar->pending_chdir == NULL) + bsdtar_errc(bsdtar, 1, errno, "No memory"); +} + +void +do_chdir(struct bsdtar *bsdtar) +{ + if (bsdtar->pending_chdir == NULL) + return; + + if (chdir(bsdtar->pending_chdir) != 0) { + bsdtar_errc(bsdtar, 1, 0, "could not chdir to '%s'\n", + bsdtar->pending_chdir); + } + free(bsdtar->pending_chdir); + bsdtar->pending_chdir = NULL; +} + +/* + * Handle --strip-components and any future path-rewriting options. + * Returns non-zero if the pathname should not be extracted. + * + * TODO: Support pax-style regex path rewrites. + */ +int +edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry) +{ + const char *name = archive_entry_pathname(entry); + + /* Strip leading dir names as per --strip-components option. */ + if (bsdtar->strip_components > 0) { + int r = bsdtar->strip_components; + const char *p = name; + + while (r > 0) { + switch (*p++) { + case '/': + r--; + name = p; + break; + case '\0': + /* Path is too short, skip it. */ + return (1); + } + } + } + + /* Strip redundant leading '/' characters. */ + while (name[0] == '/' && name[1] == '/') + name++; + + /* Strip leading '/' unless user has asked us not to. */ + if (name[0] == '/' && !bsdtar->option_absolute_paths) { + /* Generate a warning the first time this happens. */ + if (!bsdtar->warned_lead_slash) { + bsdtar_warnc(bsdtar, 0, + "Removing leading '/' from member names"); + bsdtar->warned_lead_slash = 1; + } + name++; + /* Special case: Stripping leading '/' from "/" yields ".". */ + if (*name == '\0') + 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); + } + return (0); +} + +/* + * 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. + */ + +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); +} diff --git a/contrib/libarchive-2.1/tar/write.c b/contrib/libarchive-2.1/tar/write.c new file mode 100644 index 0000000000..869da0f6be --- /dev/null +++ b/contrib/libarchive-2.1/tar/write.c @@ -0,0 +1,1540 @@ +/*- + * 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: src/usr.bin/tar/write.c,v 1.61 2007/04/09 08:22:34 cperciva Exp $"); + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_ACL_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_ATTR_XATTR_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_EXT2FS_EXT2_FS_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_FNMATCH_H +#include +#endif +#ifdef HAVE_GRP_H +#include +#endif +#ifdef HAVE_LIMITS_H +#include +#endif +#ifdef HAVE_LINUX_FS_H +#include /* for Linux file flags */ +#endif +#ifdef HAVE_LINUX_EXT2_FS_H +#include /* for Linux file flags */ +#endif +#ifdef HAVE_PWD_H +#include +#endif +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "bsdtar.h" +#include "tree.h" + +/* Fixed size of uname/gname caches. */ +#define name_cache_size 101 + +static const char * const NO_NAME = "(noname)"; + +/* Initial size of link cache. */ +#define links_cache_initial_size 1024 + +struct archive_dir_entry { + struct archive_dir_entry *next; + time_t mtime_sec; + int mtime_nsec; + char *name; +}; + +struct archive_dir { + struct archive_dir_entry *head, *tail; +}; + +struct links_cache { + unsigned long number_entries; + size_t number_buckets; + struct links_entry **buckets; +}; + +struct links_entry { + struct links_entry *next; + struct links_entry *previous; + int links; + dev_t dev; + ino_t ino; + char *name; +}; + +struct name_cache { + int probes; + int hits; + size_t size; + struct { + id_t id; + const char *name; + } cache[name_cache_size]; +}; + +static void add_dir_list(struct bsdtar *bsdtar, const char *path, + time_t mtime_sec, int mtime_nsec); +static int append_archive(struct bsdtar *, struct archive *, + struct archive *ina); +static int append_archive_filename(struct bsdtar *, + struct archive *, const char *fname); +static void archive_names_from_file(struct bsdtar *bsdtar, + struct archive *a); +static int archive_names_from_file_helper(struct bsdtar *bsdtar, + const char *line); +static int copy_file_data(struct bsdtar *bsdtar, + struct archive *a, struct archive *ina); +static void create_cleanup(struct bsdtar *); +static void free_buckets(struct bsdtar *, struct links_cache *); +static void free_cache(struct name_cache *cache); +static const char * lookup_gname(struct bsdtar *bsdtar, gid_t gid); +static int lookup_gname_helper(struct bsdtar *bsdtar, + const char **name, id_t gid); +static void lookup_hardlink(struct bsdtar *, + struct archive_entry *entry, const struct stat *); +static const char * lookup_uname(struct bsdtar *bsdtar, uid_t uid); +static int lookup_uname_helper(struct bsdtar *bsdtar, + const char **name, id_t uid); +static int new_enough(struct bsdtar *, const char *path, + const struct stat *); +static void setup_acls(struct bsdtar *, struct archive_entry *, + const char *path); +static void setup_xattrs(struct bsdtar *, struct archive_entry *, + const char *path); +static void test_for_append(struct bsdtar *); +static void write_archive(struct archive *, struct bsdtar *); +static void write_entry(struct bsdtar *, struct archive *, + const struct stat *, const char *pathname, + const char *accpath); +static int write_file_data(struct bsdtar *, struct archive *, + int fd); +static void write_hierarchy(struct bsdtar *, struct archive *, + const char *); + +void +tar_mode_c(struct bsdtar *bsdtar) +{ + struct archive *a; + int r; + + if (*bsdtar->argv == NULL && bsdtar->names_from_file == NULL) + bsdtar_errc(bsdtar, 1, 0, "no files or directories specified"); + + a = archive_write_new(); + + /* Support any format that the library supports. */ + if (bsdtar->create_format == NULL) { + r = archive_write_set_format_pax_restricted(a); + bsdtar->create_format = "pax restricted"; + } else { + r = archive_write_set_format_by_name(a, bsdtar->create_format); + } + if (r != ARCHIVE_OK) { + fprintf(stderr, "Can't use format %s: %s\n", + bsdtar->create_format, + archive_error_string(a)); + usage(bsdtar); + } + + /* + * If user explicitly set the block size, then assume they + * want the last block padded as well. Otherwise, use the + * default block size and accept archive_write_open_file()'s + * default padding decisions. + */ + if (bsdtar->bytes_per_block != 0) { + archive_write_set_bytes_per_block(a, bsdtar->bytes_per_block); + archive_write_set_bytes_in_last_block(a, + bsdtar->bytes_per_block); + } else + archive_write_set_bytes_per_block(a, DEFAULT_BYTES_PER_BLOCK); + + if (bsdtar->compress_program) { + archive_write_set_compression_program(a, bsdtar->compress_program); + } else { + switch (bsdtar->create_compression) { + case 0: + archive_write_set_compression_none(a); + break; +#ifdef HAVE_LIBBZ2 + case 'j': case 'y': + archive_write_set_compression_bzip2(a); + break; +#endif +#ifdef HAVE_LIBZ + case 'z': + archive_write_set_compression_gzip(a); + break; +#endif + default: + bsdtar_errc(bsdtar, 1, 0, + "Unrecognized compression option -%c", + bsdtar->create_compression); + } + } + + r = archive_write_open_file(a, bsdtar->filename); + if (r != ARCHIVE_OK) + bsdtar_errc(bsdtar, 1, 0, archive_error_string(a)); + + write_archive(a, bsdtar); + + if (bsdtar->option_totals) { + fprintf(stderr, "Total bytes written: " BSDTAR_FILESIZE_PRINTF "\n", + (BSDTAR_FILESIZE_TYPE)archive_position_compressed(a)); + } + + archive_write_finish(a); +} + +/* + * Same as 'c', except we only support tar or empty formats in + * uncompressed files on disk. + */ +void +tar_mode_r(struct bsdtar *bsdtar) +{ + off_t end_offset; + int format; + struct archive *a; + struct archive_entry *entry; + int r; + + /* Sanity-test some arguments and the file. */ + test_for_append(bsdtar); + + format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED; + + bsdtar->fd = open(bsdtar->filename, O_RDWR | O_CREAT, 0666); + if (bsdtar->fd < 0) + bsdtar_errc(bsdtar, 1, errno, + "Cannot open %s", bsdtar->filename); + + a = archive_read_new(); + archive_read_support_compression_all(a); + archive_read_support_format_tar(a); + archive_read_support_format_gnutar(a); + r = archive_read_open_fd(a, bsdtar->fd, 10240); + if (r != ARCHIVE_OK) + bsdtar_errc(bsdtar, 1, archive_errno(a), + "Can't read archive %s: %s", bsdtar->filename, + archive_error_string(a)); + while (0 == archive_read_next_header(a, &entry)) { + if (archive_compression(a) != ARCHIVE_COMPRESSION_NONE) { + archive_read_finish(a); + close(bsdtar->fd); + bsdtar_errc(bsdtar, 1, 0, + "Cannot append to compressed archive."); + } + /* Keep going until we hit end-of-archive */ + format = archive_format(a); + } + + end_offset = archive_read_header_position(a); + archive_read_finish(a); + + /* Re-open archive for writing */ + a = archive_write_new(); + archive_write_set_compression_none(a); + /* + * Set the format to be used for writing. To allow people to + * extend empty files, we need to allow them to specify the format, + * which opens the possibility that they will specify a format that + * doesn't match the existing format. Hence, the following bit + * of arcane ugliness. + */ + + if (bsdtar->create_format != NULL) { + /* If the user requested a format, use that, but ... */ + archive_write_set_format_by_name(a, + bsdtar->create_format); + /* ... complain if it's not compatible. */ + format &= ARCHIVE_FORMAT_BASE_MASK; + if (format != (int)(archive_format(a) & ARCHIVE_FORMAT_BASE_MASK) + && format != ARCHIVE_FORMAT_EMPTY) { + bsdtar_errc(bsdtar, 1, 0, + "Format %s is incompatible with the archive %s.", + bsdtar->create_format, bsdtar->filename); + } + } else { + /* + * Just preserve the current format, with a little care + * for formats that libarchive can't write. + */ + if (format == ARCHIVE_FORMAT_TAR_GNUTAR) + /* TODO: When gtar supports pax, use pax restricted. */ + format = ARCHIVE_FORMAT_TAR_USTAR; + if (format == ARCHIVE_FORMAT_EMPTY) + format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED; + archive_write_set_format(a, format); + } + lseek(bsdtar->fd, end_offset, SEEK_SET); /* XXX check return val XXX */ + archive_write_open_fd(a, bsdtar->fd); /* XXX check return val XXX */ + + write_archive(a, bsdtar); /* XXX check return val XXX */ + + if (bsdtar->option_totals) { + fprintf(stderr, "Total bytes written: " BSDTAR_FILESIZE_PRINTF "\n", + (BSDTAR_FILESIZE_TYPE)archive_position_compressed(a)); + } + + archive_write_finish(a); + close(bsdtar->fd); + bsdtar->fd = -1; +} + +void +tar_mode_u(struct bsdtar *bsdtar) +{ + off_t end_offset; + struct archive *a; + struct archive_entry *entry; + int format; + struct archive_dir_entry *p; + struct archive_dir archive_dir; + + bsdtar->archive_dir = &archive_dir; + memset(&archive_dir, 0, sizeof(archive_dir)); + + format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED; + + /* Sanity-test some arguments and the file. */ + test_for_append(bsdtar); + + bsdtar->fd = open(bsdtar->filename, O_RDWR); + if (bsdtar->fd < 0) + bsdtar_errc(bsdtar, 1, errno, + "Cannot open %s", bsdtar->filename); + + a = archive_read_new(); + archive_read_support_compression_all(a); + archive_read_support_format_tar(a); + archive_read_support_format_gnutar(a); + if (archive_read_open_fd(a, bsdtar->fd, + bsdtar->bytes_per_block != 0 ? bsdtar->bytes_per_block : + DEFAULT_BYTES_PER_BLOCK) != ARCHIVE_OK) { + bsdtar_errc(bsdtar, 1, 0, + "Can't open %s: %s", bsdtar->filename, + archive_error_string(a)); + } + + /* Build a list of all entries and their recorded mod times. */ + while (0 == archive_read_next_header(a, &entry)) { + if (archive_compression(a) != ARCHIVE_COMPRESSION_NONE) { + archive_read_finish(a); + close(bsdtar->fd); + bsdtar_errc(bsdtar, 1, 0, + "Cannot append to compressed archive."); + } + add_dir_list(bsdtar, archive_entry_pathname(entry), + archive_entry_mtime(entry), + archive_entry_mtime_nsec(entry)); + /* Record the last format determination we see */ + format = archive_format(a); + /* Keep going until we hit end-of-archive */ + } + + end_offset = archive_read_header_position(a); + archive_read_finish(a); + + /* Re-open archive for writing. */ + a = archive_write_new(); + archive_write_set_compression_none(a); + /* + * Set format to same one auto-detected above, except that + * we don't write GNU tar format, so use ustar instead. + */ + if (format == ARCHIVE_FORMAT_TAR_GNUTAR) + format = ARCHIVE_FORMAT_TAR_USTAR; + archive_write_set_format(a, format); + if (bsdtar->bytes_per_block != 0) { + archive_write_set_bytes_per_block(a, bsdtar->bytes_per_block); + archive_write_set_bytes_in_last_block(a, + bsdtar->bytes_per_block); + } else + archive_write_set_bytes_per_block(a, DEFAULT_BYTES_PER_BLOCK); + lseek(bsdtar->fd, end_offset, SEEK_SET); + ftruncate(bsdtar->fd, end_offset); + archive_write_open_fd(a, bsdtar->fd); + + write_archive(a, bsdtar); + + if (bsdtar->option_totals) { + fprintf(stderr, "Total bytes written: " BSDTAR_FILESIZE_PRINTF "\n", + (BSDTAR_FILESIZE_TYPE)archive_position_compressed(a)); + } + + archive_write_finish(a); + close(bsdtar->fd); + bsdtar->fd = -1; + + while (bsdtar->archive_dir->head != NULL) { + p = bsdtar->archive_dir->head->next; + free(bsdtar->archive_dir->head->name); + free(bsdtar->archive_dir->head); + bsdtar->archive_dir->head = p; + } + bsdtar->archive_dir->tail = NULL; +} + + +/* + * Write user-specified files/dirs to opened archive. + */ +static void +write_archive(struct archive *a, struct bsdtar *bsdtar) +{ + const char *arg; + + if (bsdtar->names_from_file != NULL) + archive_names_from_file(bsdtar, a); + + while (*bsdtar->argv) { + arg = *bsdtar->argv; + if (arg[0] == '-' && arg[1] == 'C') { + arg += 2; + if (*arg == '\0') { + bsdtar->argv++; + arg = *bsdtar->argv; + if (arg == NULL) { + bsdtar_warnc(bsdtar, 1, 0, + "Missing argument for -C"); + bsdtar->return_value = 1; + return; + } + } + set_chdir(bsdtar, arg); + } else { + if (*arg != '/' && (arg[0] != '@' || arg[1] != '/')) + do_chdir(bsdtar); /* Handle a deferred -C */ + if (*arg == '@') { + if (append_archive_filename(bsdtar, a, + arg + 1) != 0) + break; + } else + write_hierarchy(bsdtar, a, arg); + } + bsdtar->argv++; + } + + create_cleanup(bsdtar); + if (archive_write_close(a)) { + bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); + bsdtar->return_value = 1; + } +} + +/* + * Archive names specified in file. + * + * Unless --null was specified, a line containing exactly "-C" will + * cause the next line to be a directory to pass to chdir(). If + * --null is specified, then a line "-C" is just another filename. + */ +void +archive_names_from_file(struct bsdtar *bsdtar, struct archive *a) +{ + bsdtar->archive = a; + + bsdtar->next_line_is_dir = 0; + process_lines(bsdtar, bsdtar->names_from_file, + archive_names_from_file_helper); + if (bsdtar->next_line_is_dir) + bsdtar_errc(bsdtar, 1, errno, + "Unexpected end of filename list; " + "directory expected after -C"); +} + +static int +archive_names_from_file_helper(struct bsdtar *bsdtar, const char *line) +{ + if (bsdtar->next_line_is_dir) { + set_chdir(bsdtar, line); + bsdtar->next_line_is_dir = 0; + } else if (!bsdtar->option_null && strcmp(line, "-C") == 0) + bsdtar->next_line_is_dir = 1; + else { + if (*line != '/') + do_chdir(bsdtar); /* Handle a deferred -C */ + write_hierarchy(bsdtar, bsdtar->archive, line); + } + return (0); +} + +/* + * Copy from specified archive to current archive. Returns non-zero + * for write errors (which force us to terminate the entire archiving + * operation). If there are errors reading the input archive, we set + * bsdtar->return_value but return zero, so the overall archiving + * operation will complete and return non-zero. + */ +static int +append_archive_filename(struct bsdtar *bsdtar, struct archive *a, + const char *filename) +{ + struct archive *ina; + int rc; + + if (strcmp(filename, "-") == 0) + filename = NULL; /* Library uses NULL for stdio. */ + + ina = archive_read_new(); + archive_read_support_format_all(ina); + archive_read_support_compression_all(ina); + if (archive_read_open_file(ina, filename, 10240)) { + bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(ina)); + bsdtar->return_value = 1; + return (0); + } + + rc = append_archive(bsdtar, a, ina); + + if (archive_errno(ina)) { + bsdtar_warnc(bsdtar, 0, "Error reading archive %s: %s", + filename, archive_error_string(ina)); + bsdtar->return_value = 1; + } + archive_read_finish(ina); + + return (rc); +} + +static int +append_archive(struct bsdtar *bsdtar, struct archive *a, struct archive *ina) +{ + struct archive_entry *in_entry; + int e; + + while (0 == archive_read_next_header(ina, &in_entry)) { + if (!new_enough(bsdtar, archive_entry_pathname(in_entry), + archive_entry_stat(in_entry))) + continue; + if (excluded(bsdtar, archive_entry_pathname(in_entry))) + continue; + if (bsdtar->option_interactive && + !yes("copy '%s'", archive_entry_pathname(in_entry))) + continue; + if (bsdtar->verbose) + safe_fprintf(stderr, "a %s", + archive_entry_pathname(in_entry)); + + e = archive_write_header(a, in_entry); + if (e != ARCHIVE_OK) { + if (!bsdtar->verbose) + bsdtar_warnc(bsdtar, 0, "%s: %s", + archive_entry_pathname(in_entry), + archive_error_string(a)); + else + fprintf(stderr, ": %s", archive_error_string(a)); + } + if (e == ARCHIVE_FATAL) + exit(1); + + if (e >= ARCHIVE_WARN) + if (copy_file_data(bsdtar, a, ina)) + exit(1); + + if (bsdtar->verbose) + fprintf(stderr, "\n"); + } + + /* Note: If we got here, we saw no write errors, so return success. */ + return (0); +} + +/* Helper function to copy data between archives. */ +static int +copy_file_data(struct bsdtar *bsdtar, struct archive *a, struct archive *ina) +{ + char buff[64*1024]; + ssize_t bytes_read; + ssize_t bytes_written; + + bytes_read = archive_read_data(ina, buff, sizeof(buff)); + while (bytes_read > 0) { + bytes_written = archive_write_data(a, buff, bytes_read); + if (bytes_written < bytes_read) { + bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); + return (-1); + } + bytes_read = archive_read_data(ina, buff, sizeof(buff)); + } + + return (0); +} + +/* + * Add the file or dir hierarchy named by 'path' to the archive + */ +static void +write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) +{ + struct tree *tree; + char symlink_mode = bsdtar->symlink_mode; + dev_t first_dev = 0; + int dev_recorded = 0; + int tree_ret; +#ifdef __linux + int fd, r; + unsigned long fflags; +#endif + + tree = tree_open(path); + + if (!tree) { + bsdtar_warnc(bsdtar, errno, "%s: Cannot open", path); + bsdtar->return_value = 1; + return; + } + + while ((tree_ret = tree_next(tree))) { + const char *name = tree_current_path(tree); + const struct stat *st = NULL, *lst = NULL; + int descend; + + if (tree_ret == TREE_ERROR_DIR) + bsdtar_warnc(bsdtar, errno, "%s: Couldn't visit directory", name); + if (tree_ret != TREE_REGULAR) + continue; + lst = tree_current_lstat(tree); + if (lst == NULL) { + /* Couldn't lstat(); must not exist. */ + bsdtar_warnc(bsdtar, errno, "%s: Cannot stat", name); + continue; + } + if (S_ISLNK(lst->st_mode)) + st = tree_current_stat(tree); + /* Default: descend into any dir or symlink to dir. */ + /* We'll adjust this later on. */ + descend = 0; + if ((st != NULL) && S_ISDIR(st->st_mode)) + descend = 1; + if ((lst != NULL) && S_ISDIR(lst->st_mode)) + descend = 1; + + /* + * If user has asked us not to cross mount points, + * then don't descend into into a dir on a different + * device. + */ + if (!dev_recorded) { + first_dev = lst->st_dev; + dev_recorded = 1; + } + if (bsdtar->option_dont_traverse_mounts) { + if (lst != NULL && lst->st_dev != first_dev) + descend = 0; + } + + /* + * If this file/dir is flagged "nodump" and we're + * honoring such flags, skip this file/dir. + */ +#ifdef HAVE_CHFLAGS + if (bsdtar->option_honor_nodump && + (lst->st_flags & UF_NODUMP)) + continue; +#endif + +#ifdef __linux + /* + * Linux has a nodump flag too but to read it + * we have to open() the file/dir and do an ioctl on it... + */ + if (bsdtar->option_honor_nodump && + ((fd = open(name, O_RDONLY|O_NONBLOCK)) >= 0) && + ((r = ioctl(fd, EXT2_IOC_GETFLAGS, &fflags)), + close(fd), r) >= 0 && + (fflags & EXT2_NODUMP_FL)) + continue; +#endif + + /* + * If this file/dir is excluded by a filename + * pattern, skip it. + */ + if (excluded(bsdtar, name)) + continue; + + /* + * If the user vetoes this file/directory, skip it. + */ + if (bsdtar->option_interactive && + !yes("add '%s'", name)) + continue; + + /* + * If this is a dir, decide whether or not to recurse. + */ + if (bsdtar->option_no_subdirs) + descend = 0; + + /* + * Distinguish 'L'/'P'/'H' symlink following. + */ + switch(symlink_mode) { + case 'H': + /* 'H': After the first item, rest like 'P'. */ + symlink_mode = 'P'; + /* 'H': First item (from command line) like 'L'. */ + /* FALLTHROUGH */ + case 'L': + /* 'L': Do descend through a symlink to dir. */ + /* 'L': Archive symlink to file as file. */ + lst = tree_current_stat(tree); + /* If stat fails, we have a broken symlink; + * in that case, archive the link as such. */ + if (lst == NULL) + lst = tree_current_lstat(tree); + break; + default: + /* 'P': Don't descend through a symlink to dir. */ + if (!S_ISDIR(lst->st_mode)) + descend = 0; + /* 'P': Archive symlink to file as symlink. */ + /* lst = tree_current_lstat(tree); */ + break; + } + + if (descend) + tree_descend(tree); + + /* + * Write the entry. Note that write_entry() handles + * pathname editing and newness testing. + */ + write_entry(bsdtar, a, lst, name, + tree_current_access_path(tree)); + } + tree_close(tree); +} + +/* + * Add a single filesystem object to the archive. + */ +static void +write_entry(struct bsdtar *bsdtar, struct archive *a, const struct stat *st, + const char *pathname, const char *accpath) +{ + struct archive_entry *entry; + int e; + int fd; +#ifdef __linux + int r; + unsigned long stflags; +#endif + static char linkbuffer[PATH_MAX+1]; + + fd = -1; + entry = archive_entry_new(); + + archive_entry_set_pathname(entry, pathname); + + /* + * Rewrite the pathname to be archived. If rewrite + * fails, skip the entry. + */ + if (edit_pathname(bsdtar, entry)) + goto abort; + + /* + * In -u mode, check that the file is newer than what's + * already in the archive; in all modes, obey --newerXXX flags. + */ + if (!new_enough(bsdtar, archive_entry_pathname(entry), st)) + goto abort; + + if (!S_ISDIR(st->st_mode) && (st->st_nlink > 1)) + lookup_hardlink(bsdtar, entry, st); + + /* Display entry as we process it. This format is required by SUSv2. */ + if (bsdtar->verbose) + safe_fprintf(stderr, "a %s", archive_entry_pathname(entry)); + + /* Read symbolic link information. */ + if ((st->st_mode & S_IFMT) == S_IFLNK) { + int lnklen; + + lnklen = readlink(accpath, linkbuffer, PATH_MAX); + if (lnklen < 0) { + if (!bsdtar->verbose) + bsdtar_warnc(bsdtar, errno, + "%s: Couldn't read symbolic link", + pathname); + else + safe_fprintf(stderr, + ": Couldn't read symbolic link: %s", + strerror(errno)); + goto cleanup; + } + linkbuffer[lnklen] = 0; + archive_entry_set_symlink(entry, linkbuffer); + } + + /* Look up username and group name. */ + archive_entry_set_uname(entry, lookup_uname(bsdtar, st->st_uid)); + archive_entry_set_gname(entry, lookup_gname(bsdtar, st->st_gid)); + +#ifdef HAVE_CHFLAGS + if (st->st_flags != 0) + archive_entry_set_fflags(entry, st->st_flags, 0); +#endif + +#ifdef __linux + if ((S_ISREG(st->st_mode) || S_ISDIR(st->st_mode)) && + ((fd = open(accpath, O_RDONLY|O_NONBLOCK)) >= 0) && + ((r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags)), close(fd), (fd = -1), r) >= 0 && + stflags) { + archive_entry_set_fflags(entry, stflags, 0); + } +#endif + + archive_entry_copy_stat(entry, st); + setup_acls(bsdtar, entry, accpath); + setup_xattrs(bsdtar, entry, accpath); + + /* + * If it's a regular file (and non-zero in size) make sure we + * can open it before we start to write. In particular, note + * that we can always archive a zero-length file, even if we + * can't read it. + */ + if (S_ISREG(st->st_mode) && st->st_size > 0) { + fd = open(accpath, O_RDONLY); + if (fd < 0) { + if (!bsdtar->verbose) + bsdtar_warnc(bsdtar, errno, "%s: could not open file", pathname); + else + fprintf(stderr, ": %s", strerror(errno)); + goto cleanup; + } + } + + /* Non-regular files get archived with zero size. */ + if (!S_ISREG(st->st_mode)) + archive_entry_set_size(entry, 0); + + e = archive_write_header(a, entry); + if (e != ARCHIVE_OK) { + if (!bsdtar->verbose) + bsdtar_warnc(bsdtar, 0, "%s: %s", pathname, + archive_error_string(a)); + else + fprintf(stderr, ": %s", archive_error_string(a)); + } + + if (e == ARCHIVE_FATAL) + exit(1); + + /* + * If we opened a file earlier, write it out now. Note that + * the format handler might have reset the size field to zero + * to inform us that the archive body won't get stored. In + * that case, just skip the write. + */ + if (e >= ARCHIVE_WARN && fd >= 0 && archive_entry_size(entry) > 0) + if (write_file_data(bsdtar, a, fd)) + exit(1); + +cleanup: + if (bsdtar->verbose) + fprintf(stderr, "\n"); + +abort: + if (fd >= 0) + close(fd); + + if (entry != NULL) + archive_entry_free(entry); +} + + +/* Helper function to copy file to archive, with stack-allocated buffer. */ +static int +write_file_data(struct bsdtar *bsdtar, struct archive *a, int fd) +{ + char buff[64*1024]; + ssize_t bytes_read; + ssize_t bytes_written; + + /* XXX TODO: Allocate buffer on heap and store pointer to + * it in bsdtar structure; arrange cleanup as well. XXX */ + + bytes_read = read(fd, buff, sizeof(buff)); + while (bytes_read > 0) { + bytes_written = archive_write_data(a, buff, bytes_read); + if (bytes_written < 0) { + /* Write failed; this is bad */ + bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); + return (-1); + } + if (bytes_written < bytes_read) { + /* Write was truncated; warn but continue. */ + bsdtar_warnc(bsdtar, 0, + "Truncated write; file may have grown while being archived."); + return (0); + } + bytes_read = read(fd, buff, sizeof(buff)); + } + return 0; +} + + +static void +create_cleanup(struct bsdtar *bsdtar) +{ + /* Free inode->pathname map used for hardlink detection. */ + if (bsdtar->links_cache != NULL) { + free_buckets(bsdtar, bsdtar->links_cache); + free(bsdtar->links_cache); + bsdtar->links_cache = NULL; + } + + free_cache(bsdtar->uname_cache); + bsdtar->uname_cache = NULL; + free_cache(bsdtar->gname_cache); + bsdtar->gname_cache = NULL; +} + + +static void +free_buckets(struct bsdtar *bsdtar, struct links_cache *links_cache) +{ + size_t i; + + if (links_cache->buckets == NULL) + return; + + for (i = 0; i < links_cache->number_buckets; i++) { + while (links_cache->buckets[i] != NULL) { + struct links_entry *lp = links_cache->buckets[i]->next; + if (bsdtar->option_warn_links) + bsdtar_warnc(bsdtar, 0, "Missing links to %s", + links_cache->buckets[i]->name); + if (links_cache->buckets[i]->name != NULL) + free(links_cache->buckets[i]->name); + free(links_cache->buckets[i]); + links_cache->buckets[i] = lp; + } + } + free(links_cache->buckets); + links_cache->buckets = NULL; +} + +static void +lookup_hardlink(struct bsdtar *bsdtar, struct archive_entry *entry, + const struct stat *st) +{ + struct links_cache *links_cache; + struct links_entry *le, **new_buckets; + int hash; + size_t i, new_size; + + /* If necessary, initialize the links cache. */ + links_cache = bsdtar->links_cache; + if (links_cache == NULL) { + bsdtar->links_cache = malloc(sizeof(struct links_cache)); + if (bsdtar->links_cache == NULL) + bsdtar_errc(bsdtar, 1, ENOMEM, + "No memory for hardlink detection."); + links_cache = bsdtar->links_cache; + memset(links_cache, 0, sizeof(struct links_cache)); + links_cache->number_buckets = links_cache_initial_size; + links_cache->buckets = malloc(links_cache->number_buckets * + sizeof(links_cache->buckets[0])); + if (links_cache->buckets == NULL) { + bsdtar_errc(bsdtar, 1, ENOMEM, + "No memory for hardlink detection."); + } + for (i = 0; i < links_cache->number_buckets; i++) + links_cache->buckets[i] = NULL; + } + + /* If the links cache overflowed and got flushed, don't bother. */ + if (links_cache->buckets == NULL) + return; + + /* If the links cache is getting too full, enlarge the hash table. */ + if (links_cache->number_entries > links_cache->number_buckets * 2) + { + new_size = links_cache->number_buckets * 2; + new_buckets = malloc(new_size * sizeof(struct links_entry *)); + + if (new_buckets != NULL) { + memset(new_buckets, 0, + new_size * sizeof(struct links_entry *)); + for (i = 0; i < links_cache->number_buckets; i++) { + while (links_cache->buckets[i] != NULL) { + /* Remove entry from old bucket. */ + le = links_cache->buckets[i]; + links_cache->buckets[i] = le->next; + + /* Add entry to new bucket. */ + hash = (le->dev ^ le->ino) % new_size; + + if (new_buckets[hash] != NULL) + new_buckets[hash]->previous = + le; + le->next = new_buckets[hash]; + le->previous = NULL; + new_buckets[hash] = le; + } + } + free(links_cache->buckets); + links_cache->buckets = new_buckets; + links_cache->number_buckets = new_size; + } else { + free_buckets(bsdtar, links_cache); + bsdtar_warnc(bsdtar, ENOMEM, + "No more memory for recording hard links"); + bsdtar_warnc(bsdtar, 0, + "Remaining links will be dumped as full files"); + } + } + + /* Try to locate this entry in the links cache. */ + hash = ( st->st_dev ^ st->st_ino ) % links_cache->number_buckets; + for (le = links_cache->buckets[hash]; le != NULL; le = le->next) { + if (le->dev == st->st_dev && le->ino == st->st_ino) { + archive_entry_copy_hardlink(entry, le->name); + + /* + * Decrement link count each time and release + * the entry if it hits zero. This saves + * memory and is necessary for proper -l + * implementation. + */ + if (--le->links <= 0) { + if (le->previous != NULL) + le->previous->next = le->next; + if (le->next != NULL) + le->next->previous = le->previous; + if (le->name != NULL) + free(le->name); + if (links_cache->buckets[hash] == le) + links_cache->buckets[hash] = le->next; + links_cache->number_entries--; + free(le); + } + + return; + } + } + + /* Add this entry to the links cache. */ + le = malloc(sizeof(struct links_entry)); + if (le != NULL) + le->name = strdup(archive_entry_pathname(entry)); + if ((le == NULL) || (le->name == NULL)) { + free_buckets(bsdtar, links_cache); + bsdtar_warnc(bsdtar, ENOMEM, + "No more memory for recording hard links"); + bsdtar_warnc(bsdtar, 0, + "Remaining hard links will be dumped as full files"); + if (le != NULL) + free(le); + return; + } + if (links_cache->buckets[hash] != NULL) + links_cache->buckets[hash]->previous = le; + links_cache->number_entries++; + le->next = links_cache->buckets[hash]; + le->previous = NULL; + links_cache->buckets[hash] = le; + le->dev = st->st_dev; + le->ino = st->st_ino; + le->links = st->st_nlink - 1; +} + +#ifdef HAVE_POSIX_ACL +static void setup_acl(struct bsdtar *bsdtar, + struct archive_entry *entry, const char *accpath, + int acl_type, int archive_entry_acl_type); + +static void +setup_acls(struct bsdtar *bsdtar, struct archive_entry *entry, + const char *accpath) +{ + archive_entry_acl_clear(entry); + + setup_acl(bsdtar, entry, accpath, + ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_TYPE_ACCESS); + /* Only directories can have default ACLs. */ + if (S_ISDIR(archive_entry_mode(entry))) + setup_acl(bsdtar, entry, accpath, + ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); +} + +static void +setup_acl(struct bsdtar *bsdtar, struct archive_entry *entry, + const char *accpath, int acl_type, int archive_entry_acl_type) +{ + acl_t acl; + acl_tag_t acl_tag; + acl_entry_t acl_entry; + acl_permset_t acl_permset; + int s, ae_id, ae_tag, ae_perm; + const char *ae_name; + + /* Retrieve access ACL from file. */ + acl = acl_get_file(accpath, acl_type); + if (acl != NULL) { + s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry); + while (s == 1) { + ae_id = -1; + ae_name = NULL; + + acl_get_tag_type(acl_entry, &acl_tag); + if (acl_tag == ACL_USER) { + ae_id = (int)*(uid_t *)acl_get_qualifier(acl_entry); + ae_name = lookup_uname(bsdtar, ae_id); + ae_tag = ARCHIVE_ENTRY_ACL_USER; + } else if (acl_tag == ACL_GROUP) { + ae_id = (int)*(gid_t *)acl_get_qualifier(acl_entry); + ae_name = lookup_gname(bsdtar, ae_id); + ae_tag = ARCHIVE_ENTRY_ACL_GROUP; + } else if (acl_tag == ACL_MASK) { + ae_tag = ARCHIVE_ENTRY_ACL_MASK; + } else if (acl_tag == ACL_USER_OBJ) { + ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ; + } else if (acl_tag == ACL_GROUP_OBJ) { + ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; + } else if (acl_tag == ACL_OTHER) { + ae_tag = ARCHIVE_ENTRY_ACL_OTHER; + } else { + /* Skip types that libarchive can't support. */ + continue; + } + + acl_get_permset(acl_entry, &acl_permset); + ae_perm = 0; + /* + * acl_get_perm() is spelled differently on different + * platforms; see bsdtar_platform.h for details. + */ + if (ACL_GET_PERM(acl_permset, ACL_EXECUTE)) + ae_perm |= ARCHIVE_ENTRY_ACL_EXECUTE; + if (ACL_GET_PERM(acl_permset, ACL_READ)) + ae_perm |= ARCHIVE_ENTRY_ACL_READ; + if (ACL_GET_PERM(acl_permset, ACL_WRITE)) + ae_perm |= ARCHIVE_ENTRY_ACL_WRITE; + + archive_entry_acl_add_entry(entry, + archive_entry_acl_type, ae_perm, ae_tag, + ae_id, ae_name); + + s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); + } + acl_free(acl); + } +} +#else +static void +setup_acls(struct bsdtar *bsdtar, struct archive_entry *entry, + const char *accpath) +{ + (void)bsdtar; + (void)entry; + (void)accpath; +} +#endif + +#if HAVE_LISTXATTR && HAVE_LLISTXATTR && HAVE_GETXATTR && HAVE_LGETXATTR + +static void +setup_xattr(struct bsdtar *bsdtar, struct archive_entry *entry, + const char *accpath, const char *name) +{ + size_t size; + void *value = NULL; + char symlink_mode = bsdtar->symlink_mode; + + if (symlink_mode == 'H') + size = getxattr(accpath, name, NULL, 0); + else + size = lgetxattr(accpath, name, NULL, 0); + + if (size == -1) { + bsdtar_warnc(bsdtar, errno, "Couldn't get extended attribute"); + return; + } + + if (size > 0 && (value = malloc(size)) == NULL) { + bsdtar_errc(bsdtar, 1, errno, "Out of memory"); + return; + } + + if (symlink_mode == 'H') + size = getxattr(accpath, name, value, size); + else + size = lgetxattr(accpath, name, value, size); + + if (size == -1) { + bsdtar_warnc(bsdtar, errno, "Couldn't get extended attribute"); + return; + } + + archive_entry_xattr_add_entry(entry, name, value, size); + + free(value); +} + +/* + * Linux extended attribute support + */ +static void +setup_xattrs(struct bsdtar *bsdtar, struct archive_entry *entry, + const char *accpath) +{ + char *list, *p; + size_t list_size; + char symlink_mode = bsdtar->symlink_mode; + + if (symlink_mode == 'H') + list_size = listxattr(accpath, NULL, 0); + else + list_size = llistxattr(accpath, NULL, 0); + + if (list_size == -1) { + bsdtar_warnc(bsdtar, errno, + "Couldn't list extended attributes"); + return; + } else if (list_size == 0) + return; + + if ((list = malloc(list_size)) == NULL) { + bsdtar_errc(bsdtar, 1, errno, "Out of memory"); + return; + } + + if (symlink_mode == 'H') + list_size = listxattr(accpath, list, list_size); + else + list_size = llistxattr(accpath, list, list_size); + + if (list_size == -1) { + bsdtar_warnc(bsdtar, errno, + "Couldn't list extended attributes"); + free(list); + return; + } + + for (p = list; (p - list) < list_size; p += strlen(p) + 1) { + if (strncmp(p, "system.", 7) == 0 || + strncmp(p, "xfsroot.", 8) == 0) + continue; + + setup_xattr(bsdtar, entry, accpath, p); + } + + free(list); +} + +#else + +/* + * Generic (stub) extended attribute support. + */ +static void +setup_xattrs(struct bsdtar *bsdtar, struct archive_entry *entry, + const char *accpath) +{ + (void)bsdtar; /* UNUSED */ + (void)entry; /* UNUSED */ + (void)accpath; /* UNUSED */ +} + +#endif + +static void +free_cache(struct name_cache *cache) +{ + size_t i; + + if (cache != NULL) { + for (i = 0; i < cache->size; i++) { + if (cache->cache[i].name != NULL && + cache->cache[i].name != NO_NAME) + free((void *)(uintptr_t)cache->cache[i].name); + } + free(cache); + } +} + +/* + * Lookup uid/gid from uname/gname, return NULL if no match. + */ +static const char * +lookup_name(struct bsdtar *bsdtar, struct name_cache **name_cache_variable, + int (*lookup_fn)(struct bsdtar *, const char **, id_t), id_t id) +{ + struct name_cache *cache; + const char *name; + int slot; + + + if (*name_cache_variable == NULL) { + *name_cache_variable = malloc(sizeof(struct name_cache)); + if (*name_cache_variable == NULL) + bsdtar_errc(bsdtar, 1, ENOMEM, "No more memory"); + memset(*name_cache_variable, 0, sizeof(struct name_cache)); + (*name_cache_variable)->size = name_cache_size; + } + + cache = *name_cache_variable; + cache->probes++; + + slot = id % cache->size; + if (cache->cache[slot].name != NULL) { + if (cache->cache[slot].id == id) { + cache->hits++; + if (cache->cache[slot].name == NO_NAME) + return (NULL); + return (cache->cache[slot].name); + } + if (cache->cache[slot].name != NO_NAME) + free((void *)(uintptr_t)cache->cache[slot].name); + cache->cache[slot].name = NULL; + } + + if (lookup_fn(bsdtar, &name, id) == 0) { + if (name == NULL || name[0] == '\0') { + /* Cache the negative response. */ + cache->cache[slot].name = NO_NAME; + cache->cache[slot].id = id; + } else { + cache->cache[slot].name = strdup(name); + if (cache->cache[slot].name != NULL) { + cache->cache[slot].id = id; + return (cache->cache[slot].name); + } + /* + * Conveniently, NULL marks an empty slot, so + * if the strdup() fails, we've just failed to + * cache it. No recovery necessary. + */ + } + } + return (NULL); +} + +static const char * +lookup_uname(struct bsdtar *bsdtar, uid_t uid) +{ + return (lookup_name(bsdtar, &bsdtar->uname_cache, + &lookup_uname_helper, (id_t)uid)); +} + +static int +lookup_uname_helper(struct bsdtar *bsdtar, const char **name, id_t id) +{ + struct passwd *pwent; + + (void)bsdtar; /* UNUSED */ + + errno = 0; + pwent = getpwuid((uid_t)id); + if (pwent == NULL) { + *name = NULL; + if (errno != 0) + bsdtar_warnc(bsdtar, errno, "getpwuid(%d) failed", id); + return (errno); + } + + *name = pwent->pw_name; + return (0); +} + +static const char * +lookup_gname(struct bsdtar *bsdtar, gid_t gid) +{ + return (lookup_name(bsdtar, &bsdtar->gname_cache, + &lookup_gname_helper, (id_t)gid)); +} + +static int +lookup_gname_helper(struct bsdtar *bsdtar, const char **name, id_t id) +{ + struct group *grent; + + (void)bsdtar; /* UNUSED */ + + errno = 0; + grent = getgrgid((gid_t)id); + if (grent == NULL) { + *name = NULL; + if (errno != 0) + bsdtar_warnc(bsdtar, errno, "getgrgid(%d) failed", id); + return (errno); + } + + *name = grent->gr_name; + return (0); +} + +/* + * Test if the specified file is new enough to include in the archive. + */ +int +new_enough(struct bsdtar *bsdtar, const char *path, const struct stat *st) +{ + struct archive_dir_entry *p; + + /* + * If this file/dir is excluded by a time comparison, skip it. + */ + if (bsdtar->newer_ctime_sec > 0) { + if (st->st_ctime < bsdtar->newer_ctime_sec) + return (0); /* Too old, skip it. */ + if (st->st_ctime == bsdtar->newer_ctime_sec + && ARCHIVE_STAT_CTIME_NANOS(st) + <= bsdtar->newer_ctime_nsec) + return (0); /* Too old, skip it. */ + } + if (bsdtar->newer_mtime_sec > 0) { + if (st->st_mtime < bsdtar->newer_mtime_sec) + return (0); /* Too old, skip it. */ + if (st->st_mtime == bsdtar->newer_mtime_sec + && ARCHIVE_STAT_MTIME_NANOS(st) + <= bsdtar->newer_mtime_nsec) + return (0); /* Too old, skip it. */ + } + + /* + * In -u mode, we only write an entry if it's newer than + * what was already in the archive. + */ + if (bsdtar->archive_dir != NULL && + bsdtar->archive_dir->head != NULL) { + for (p = bsdtar->archive_dir->head; p != NULL; p = p->next) { + if (pathcmp(path, p->name)==0) + return (p->mtime_sec < st->st_mtime || + (p->mtime_sec == st->st_mtime && + p->mtime_nsec + < ARCHIVE_STAT_MTIME_NANOS(st))); + } + } + + /* If the file wasn't rejected, include it. */ + return (1); +} + +/* + * Add an entry to the dir list for 'u' mode. + * + * XXX TODO: Make this fast. + */ +static void +add_dir_list(struct bsdtar *bsdtar, const char *path, + time_t mtime_sec, int mtime_nsec) +{ + struct archive_dir_entry *p; + + /* + * Search entire list to see if this file has appeared before. + * If it has, override the timestamp data. + */ + p = bsdtar->archive_dir->head; + while (p != NULL) { + if (strcmp(path, p->name)==0) { + p->mtime_sec = mtime_sec; + p->mtime_nsec = mtime_nsec; + return; + } + p = p->next; + } + + p = malloc(sizeof(*p)); + if (p == NULL) + bsdtar_errc(bsdtar, 1, ENOMEM, "Can't read archive directory"); + + p->name = strdup(path); + if (p->name == NULL) + bsdtar_errc(bsdtar, 1, ENOMEM, "Can't read archive directory"); + p->mtime_sec = mtime_sec; + p->mtime_nsec = mtime_nsec; + p->next = NULL; + if (bsdtar->archive_dir->tail == NULL) { + bsdtar->archive_dir->head = bsdtar->archive_dir->tail = p; + } else { + bsdtar->archive_dir->tail->next = p; + bsdtar->archive_dir->tail = p; + } +} + +void +test_for_append(struct bsdtar *bsdtar) +{ + struct stat s; + + if (*bsdtar->argv == NULL) + bsdtar_errc(bsdtar, 1, 0, "no files or directories specified"); + if (bsdtar->filename == NULL) + bsdtar_errc(bsdtar, 1, 0, "Cannot append to stdout."); + + if (bsdtar->create_compression != 0) + bsdtar_errc(bsdtar, 1, 0, + "Cannot append to %s with compression", bsdtar->filename); + + if (stat(bsdtar->filename, &s) != 0) + return; + + if (!S_ISREG(s.st_mode)) + bsdtar_errc(bsdtar, 1, 0, + "Cannot append to %s: not a regular file.", + bsdtar->filename); +} diff --git a/contrib/libarchive-2.1/version b/contrib/libarchive-2.1/version new file mode 100644 index 0000000000..b88e5006fb --- /dev/null +++ b/contrib/libarchive-2.1/version @@ -0,0 +1 @@ +2.1.9 \ No newline at end of file