mirror of
https://git.FreeBSD.org/src.git
synced 2024-10-19 02:29:40 +00:00
Use contrib sources for building libarchive, tar and cpio.
Make "make test" fully operational. MFC after: 2 weeks
This commit is contained in:
parent
e2ee19e346
commit
45d3b226a6
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=228797
@ -1,36 +0,0 @@
|
||||
All of the C source code, header files, and documentation in this
|
||||
package are covered by the following:
|
||||
|
||||
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.
|
||||
|
||||
===========================================================================
|
||||
|
||||
Shell scripts, makefiles, and certain other files may be covered by
|
||||
other licenses. In particular, some distributions of this library
|
||||
contain Makefiles and/or shell scripts that are generated
|
||||
automatically by GNU autoconf and GNU automake. Those generated files
|
||||
are controlled by the relevant licenses.
|
||||
|
||||
$FreeBSD$
|
||||
|
@ -1,6 +1,8 @@
|
||||
# $FreeBSD$
|
||||
.include <bsd.own.mk>
|
||||
|
||||
LIBARCHIVEDIR= ${.CURDIR}/../../contrib/libarchive
|
||||
|
||||
LIB= archive
|
||||
DPADD= ${LIBZ}
|
||||
LDADD= -lz
|
||||
@ -20,7 +22,7 @@ LDADD+= -lbsdxml
|
||||
# It has no real relation to the libarchive version number.
|
||||
SHLIB_MAJOR= 5
|
||||
|
||||
CFLAGS+= -DPLATFORM_CONFIG_H=\"config_freebsd.h\"
|
||||
CFLAGS+= -DPLATFORM_CONFIG_H=\"${.CURDIR}/config_freebsd.h\"
|
||||
CFLAGS+= -I${.OBJDIR}
|
||||
|
||||
.if ${MK_OPENSSL} != "no"
|
||||
@ -32,6 +34,8 @@ DPADD+= ${LIBMD}
|
||||
LDADD+= -lmd
|
||||
.endif
|
||||
|
||||
.PATH: ${LIBARCHIVEDIR}/libarchive
|
||||
|
||||
# Headers to be installed in /usr/include
|
||||
INCS= archive.h archive_entry.h
|
||||
|
||||
@ -273,8 +277,11 @@ MLINKS+= archive_write_disk.3 archive_write_disk_set_standard_lookup.3
|
||||
MLINKS+= archive_write_disk.3 archive_write_disk_set_user_lookup.3
|
||||
MLINKS+= libarchive.3 archive.3
|
||||
|
||||
.PHONY: check test
|
||||
.PHONY: check test clean-test
|
||||
check test:
|
||||
cd ${.CURDIR}/test && make test
|
||||
cd ${.CURDIR}/test && make obj && make test
|
||||
|
||||
clean-test:
|
||||
cd ${.CURDIR}/test && make clean
|
||||
|
||||
.include <bsd.lib.mk>
|
||||
|
@ -1,102 +0,0 @@
|
||||
$FreeBSD$
|
||||
|
||||
libarchive: a library for reading and writing streaming archives
|
||||
|
||||
This is all under a BSD license. Use, enjoy, but don't blame me if it breaks!
|
||||
|
||||
Documentation:
|
||||
* 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" and "minitar" programs 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
|
||||
* lzma and xz 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 or Joliet extensions)
|
||||
* ZIP archives (with uncompressed or "deflate" compressed entries)
|
||||
* GNU and BSD 'ar' archives
|
||||
* 'mtree' format
|
||||
|
||||
The library can write:
|
||||
* gzip compression
|
||||
* bzip2 compression
|
||||
* compress/LZW compression
|
||||
* lzma and xz 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
|
||||
* SVR4 "newc" cpio
|
||||
* shar archives
|
||||
* ZIP archives (with uncompressed or "deflate" compressed entries)
|
||||
* GNU and BSD 'ar' archives
|
||||
* 'mtree' format
|
||||
|
||||
Notes:
|
||||
* 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, the archive_write_disk interface
|
||||
treats a directory as if it were an archive so you can copy
|
||||
from archive->disk using the same code you use for archive->archive
|
||||
transfers.
|
||||
|
||||
* Note: "pax interchange format" is really an extended tar format,
|
||||
despite what the name says.
|
@ -1,746 +0,0 @@
|
||||
/*-
|
||||
* 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$
|
||||
*/
|
||||
|
||||
#ifndef ARCHIVE_H_INCLUDED
|
||||
#define ARCHIVE_H_INCLUDED
|
||||
|
||||
/*
|
||||
* Note: archive.h is for use outside of libarchive; the configuration
|
||||
* headers (config.h, archive_platform.h, etc.) are purely internal.
|
||||
* Do NOT use HAVE_XXX configuration macros to control the behavior of
|
||||
* this header! If you must conditionalize, use predefined compiler and/or
|
||||
* platform macros.
|
||||
*/
|
||||
#if defined(__BORLANDC__) && __BORLANDC__ >= 0x560
|
||||
# define __LA_STDINT_H <stdint.h>
|
||||
#elif !defined(__WATCOMC__) && !defined(_MSC_VER) && !defined(__INTERIX) && !defined(__BORLANDC__)
|
||||
# define __LA_STDINT_H <inttypes.h>
|
||||
#endif
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h> /* Linux requires this for off_t */
|
||||
#ifdef __LA_STDINT_H
|
||||
# include __LA_STDINT_H /* int64_t, etc. */
|
||||
#endif
|
||||
#include <stdio.h> /* For FILE * */
|
||||
|
||||
/* Get appropriate definitions of standard POSIX-style types. */
|
||||
/* These should match the types used in 'struct stat' */
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
#define __LA_INT64_T __int64
|
||||
# if defined(_SSIZE_T_DEFINED) || defined(_SSIZE_T_)
|
||||
# define __LA_SSIZE_T ssize_t
|
||||
# elif defined(_WIN64)
|
||||
# define __LA_SSIZE_T __int64
|
||||
# else
|
||||
# define __LA_SSIZE_T long
|
||||
# endif
|
||||
# if defined(__BORLANDC__)
|
||||
# define __LA_UID_T uid_t
|
||||
# define __LA_GID_T gid_t
|
||||
# else
|
||||
# define __LA_UID_T short
|
||||
# define __LA_GID_T short
|
||||
# endif
|
||||
#else
|
||||
#include <unistd.h> /* ssize_t, uid_t, and gid_t */
|
||||
#define __LA_INT64_T int64_t
|
||||
#define __LA_SSIZE_T ssize_t
|
||||
#define __LA_UID_T uid_t
|
||||
#define __LA_GID_T gid_t
|
||||
#endif
|
||||
|
||||
/*
|
||||
* On Windows, define LIBARCHIVE_STATIC if you're building or using a
|
||||
* .lib. The default here assumes you're building a DLL. Only
|
||||
* libarchive source should ever define __LIBARCHIVE_BUILD.
|
||||
*/
|
||||
#if ((defined __WIN32__) || (defined _WIN32) || defined(__CYGWIN__)) && (!defined LIBARCHIVE_STATIC)
|
||||
# ifdef __LIBARCHIVE_BUILD
|
||||
# ifdef __GNUC__
|
||||
# define __LA_DECL __attribute__((dllexport)) extern
|
||||
# else
|
||||
# define __LA_DECL __declspec(dllexport)
|
||||
# endif
|
||||
# else
|
||||
# ifdef __GNUC__
|
||||
# define __LA_DECL __attribute__((dllimport)) extern
|
||||
# else
|
||||
# define __LA_DECL __declspec(dllimport)
|
||||
# endif
|
||||
# endif
|
||||
#else
|
||||
/* Static libraries or non-Windows needs no special declaration. */
|
||||
# define __LA_DECL
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) && __GNUC__ >= 3
|
||||
#define __LA_PRINTF(fmtarg, firstvararg) \
|
||||
__attribute__((__format__ (__printf__, fmtarg, firstvararg)))
|
||||
#else
|
||||
#define __LA_PRINTF(fmtarg, firstvararg) /* nothing */
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The version number is provided as both a macro and a function.
|
||||
* The macro identifies the installed header; the function identifies
|
||||
* the library version (which may not be the same if you're using a
|
||||
* dynamically-linked version of the library). Of course, if the
|
||||
* header and library are very different, you should expect some
|
||||
* strangeness. Don't do that.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The version number is expressed as a single integer that makes it
|
||||
* easy to compare versions at build time: for version a.b.c, the
|
||||
* version number is printf("%d%03d%03d",a,b,c). For example, if you
|
||||
* know your application requires version 2.12.108 or later, you can
|
||||
* assert that ARCHIVE_VERSION >= 2012108.
|
||||
*
|
||||
* This single-number format was introduced with libarchive 1.9.0 in
|
||||
* the libarchive 1.x family and libarchive 2.2.4 in the libarchive
|
||||
* 2.x family. The following may be useful if you really want to do
|
||||
* feature detection for earlier libarchive versions (which defined
|
||||
* ARCHIVE_API_VERSION and ARCHIVE_API_FEATURE instead):
|
||||
*
|
||||
* #ifndef ARCHIVE_VERSION_NUMBER
|
||||
* #define ARCHIVE_VERSION_NUMBER \
|
||||
* (ARCHIVE_API_VERSION * 1000000 + ARCHIVE_API_FEATURE * 1000)
|
||||
* #endif
|
||||
*/
|
||||
#define ARCHIVE_VERSION_NUMBER 2008005
|
||||
__LA_DECL int archive_version_number(void);
|
||||
|
||||
/*
|
||||
* Textual name/version of the library, useful for version displays.
|
||||
*/
|
||||
#define ARCHIVE_VERSION_STRING "libarchive 2.8.5"
|
||||
__LA_DECL const char * archive_version_string(void);
|
||||
|
||||
#if ARCHIVE_VERSION_NUMBER < 3000000
|
||||
/*
|
||||
* Deprecated; these are older names that will be removed in favor of
|
||||
* the simpler definitions above.
|
||||
*/
|
||||
#define ARCHIVE_VERSION_STAMP ARCHIVE_VERSION_NUMBER
|
||||
__LA_DECL int archive_version_stamp(void);
|
||||
#define ARCHIVE_LIBRARY_VERSION ARCHIVE_VERSION_STRING
|
||||
__LA_DECL const char * archive_version(void);
|
||||
#define ARCHIVE_API_VERSION (ARCHIVE_VERSION_NUMBER / 1000000)
|
||||
__LA_DECL int archive_api_version(void);
|
||||
#define ARCHIVE_API_FEATURE ((ARCHIVE_VERSION_NUMBER / 1000) % 1000)
|
||||
__LA_DECL int archive_api_feature(void);
|
||||
#endif
|
||||
|
||||
#if ARCHIVE_VERSION_NUMBER < 3000000
|
||||
/* This should never have been here in the first place. */
|
||||
/* Legacy of old tar assumptions, will be removed in libarchive 3.0. */
|
||||
#define ARCHIVE_BYTES_PER_RECORD 512
|
||||
#define ARCHIVE_DEFAULT_BYTES_PER_BLOCK 10240
|
||||
#endif
|
||||
|
||||
/* 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. */
|
||||
/* But if write_header is "fatal," then this archive is dead and useless. */
|
||||
#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 __LA_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_VERSION_NUMBER < 2000000
|
||||
/* Libarchive 1.0 used ssize_t for the return, which is only 32 bits
|
||||
* on most 32-bit platforms; not large enough. */
|
||||
typedef __LA_SSIZE_T archive_skip_callback(struct archive *,
|
||||
void *_client_data, size_t request);
|
||||
#elif ARCHIVE_VERSION_NUMBER < 3000000
|
||||
/* Libarchive 2.0 used off_t here, but that is a bad idea on Linux and a
|
||||
* few other platforms where off_t varies with build settings. */
|
||||
typedef off_t archive_skip_callback(struct archive *,
|
||||
void *_client_data, off_t request);
|
||||
#else
|
||||
/* Libarchive 3.0 uses int64_t here, which is actually guaranteed to be
|
||||
* 64 bits on every platform. */
|
||||
typedef __LA_INT64_T archive_skip_callback(struct archive *,
|
||||
void *_client_data, __LA_INT64_T request);
|
||||
#endif
|
||||
|
||||
/* Returns size actually written, zero on EOF, -1 on error. */
|
||||
typedef __LA_SSIZE_T archive_write_callback(struct archive *,
|
||||
void *_client_data,
|
||||
const void *_buffer, size_t _length);
|
||||
|
||||
#if ARCHIVE_VERSION_NUMBER < 3000000
|
||||
/* Open callback is actually never needed; remove it in libarchive 3.0. */
|
||||
typedef int archive_open_callback(struct archive *, void *_client_data);
|
||||
#endif
|
||||
|
||||
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
|
||||
#define ARCHIVE_COMPRESSION_LZMA 5
|
||||
#define ARCHIVE_COMPRESSION_XZ 6
|
||||
#define ARCHIVE_COMPRESSION_UU 7
|
||||
#define ARCHIVE_COMPRESSION_RPM 8
|
||||
|
||||
/*
|
||||
* 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)
|
||||
#define ARCHIVE_FORMAT_MTREE 0x80000
|
||||
#define ARCHIVE_FORMAT_RAW 0x90000
|
||||
#define ARCHIVE_FORMAT_XAR 0xA0000
|
||||
|
||||
/*-
|
||||
* 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.
|
||||
*/
|
||||
__LA_DECL 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.
|
||||
*/
|
||||
__LA_DECL int archive_read_support_compression_all(struct archive *);
|
||||
__LA_DECL int archive_read_support_compression_bzip2(struct archive *);
|
||||
__LA_DECL int archive_read_support_compression_compress(struct archive *);
|
||||
__LA_DECL int archive_read_support_compression_gzip(struct archive *);
|
||||
__LA_DECL int archive_read_support_compression_lzma(struct archive *);
|
||||
__LA_DECL int archive_read_support_compression_none(struct archive *);
|
||||
__LA_DECL int archive_read_support_compression_program(struct archive *,
|
||||
const char *command);
|
||||
__LA_DECL int archive_read_support_compression_program_signature
|
||||
(struct archive *, const char *,
|
||||
const void * /* match */, size_t);
|
||||
|
||||
__LA_DECL int archive_read_support_compression_rpm(struct archive *);
|
||||
__LA_DECL int archive_read_support_compression_uu(struct archive *);
|
||||
__LA_DECL int archive_read_support_compression_xz(struct archive *);
|
||||
|
||||
__LA_DECL int archive_read_support_format_all(struct archive *);
|
||||
__LA_DECL int archive_read_support_format_ar(struct archive *);
|
||||
__LA_DECL int archive_read_support_format_cpio(struct archive *);
|
||||
__LA_DECL int archive_read_support_format_empty(struct archive *);
|
||||
__LA_DECL int archive_read_support_format_gnutar(struct archive *);
|
||||
__LA_DECL int archive_read_support_format_iso9660(struct archive *);
|
||||
__LA_DECL int archive_read_support_format_mtree(struct archive *);
|
||||
__LA_DECL int archive_read_support_format_raw(struct archive *);
|
||||
__LA_DECL int archive_read_support_format_tar(struct archive *);
|
||||
__LA_DECL int archive_read_support_format_xar(struct archive *);
|
||||
__LA_DECL int archive_read_support_format_zip(struct archive *);
|
||||
|
||||
|
||||
/* Open the archive using callbacks for archive I/O. */
|
||||
__LA_DECL int archive_read_open(struct archive *, void *_client_data,
|
||||
archive_open_callback *, archive_read_callback *,
|
||||
archive_close_callback *);
|
||||
__LA_DECL 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. */
|
||||
__LA_DECL 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(). */
|
||||
__LA_DECL int archive_read_open_file(struct archive *,
|
||||
const char *_filename, size_t _block_size);
|
||||
/* Read an archive that's stored in memory. */
|
||||
__LA_DECL int archive_read_open_memory(struct archive *,
|
||||
void * buff, size_t size);
|
||||
/* A more involved version that is only used for internal testing. */
|
||||
__LA_DECL 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. */
|
||||
__LA_DECL 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. */
|
||||
__LA_DECL int archive_read_open_FILE(struct archive *, FILE *_file);
|
||||
|
||||
/* Parses and returns next entry header. */
|
||||
__LA_DECL int archive_read_next_header(struct archive *,
|
||||
struct archive_entry **);
|
||||
|
||||
/* Parses and returns next entry header using the archive_entry passed in */
|
||||
__LA_DECL int archive_read_next_header2(struct archive *,
|
||||
struct archive_entry *);
|
||||
|
||||
/*
|
||||
* Retrieve the byte offset in UNCOMPRESSED data where last-read
|
||||
* header started.
|
||||
*/
|
||||
__LA_DECL __LA_INT64_T archive_read_header_position(struct archive *);
|
||||
|
||||
/* Read data from the body of an entry. Similar to read(2). */
|
||||
__LA_DECL __LA_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.
|
||||
*/
|
||||
#if ARCHIVE_VERSION_NUMBER < 3000000
|
||||
__LA_DECL int archive_read_data_block(struct archive *a,
|
||||
const void **buff, size_t *size, off_t *offset);
|
||||
#else
|
||||
__LA_DECL int archive_read_data_block(struct archive *a,
|
||||
const void **buff, size_t *size,
|
||||
__LA_INT64_T *offset);
|
||||
#endif
|
||||
|
||||
/*-
|
||||
* 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
|
||||
*/
|
||||
__LA_DECL int archive_read_data_skip(struct archive *);
|
||||
__LA_DECL int archive_read_data_into_buffer(struct archive *,
|
||||
void *buffer, __LA_SSIZE_T len);
|
||||
__LA_DECL int archive_read_data_into_fd(struct archive *, int fd);
|
||||
|
||||
/*
|
||||
* Set read options.
|
||||
*/
|
||||
/* Apply option string to the format only. */
|
||||
__LA_DECL int archive_read_set_format_options(struct archive *_a,
|
||||
const char *s);
|
||||
/* Apply option string to the filter only. */
|
||||
__LA_DECL int archive_read_set_filter_options(struct archive *_a,
|
||||
const char *s);
|
||||
/* Apply option string to both the format and the filter. */
|
||||
__LA_DECL int archive_read_set_options(struct archive *_a,
|
||||
const char *s);
|
||||
|
||||
/*-
|
||||
* 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 (0x0001)
|
||||
/* Default: Do obey umask, do not restore SUID/SGID/SVTX bits. */
|
||||
#define ARCHIVE_EXTRACT_PERM (0x0002)
|
||||
/* Default: Do not restore mtime/atime. */
|
||||
#define ARCHIVE_EXTRACT_TIME (0x0004)
|
||||
/* Default: Replace existing files. */
|
||||
#define ARCHIVE_EXTRACT_NO_OVERWRITE (0x0008)
|
||||
/* Default: Try create first, unlink only if create fails with EEXIST. */
|
||||
#define ARCHIVE_EXTRACT_UNLINK (0x0010)
|
||||
/* Default: Do not restore ACLs. */
|
||||
#define ARCHIVE_EXTRACT_ACL (0x0020)
|
||||
/* Default: Do not restore fflags. */
|
||||
#define ARCHIVE_EXTRACT_FFLAGS (0x0040)
|
||||
/* Default: Do not restore xattrs. */
|
||||
#define ARCHIVE_EXTRACT_XATTR (0x0080)
|
||||
/* 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 (0x0100)
|
||||
/* Default: Do not reject entries with '..' as path elements. */
|
||||
#define ARCHIVE_EXTRACT_SECURE_NODOTDOT (0x0200)
|
||||
/* Default: Create parent directories as needed. */
|
||||
#define ARCHIVE_EXTRACT_NO_AUTODIR (0x0400)
|
||||
/* Default: Overwrite files, even if one on disk is newer. */
|
||||
#define ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER (0x0800)
|
||||
/* Detect blocks of 0 and write holes instead. */
|
||||
#define ARCHIVE_EXTRACT_SPARSE (0x1000)
|
||||
|
||||
__LA_DECL int archive_read_extract(struct archive *, struct archive_entry *,
|
||||
int flags);
|
||||
__LA_DECL int archive_read_extract2(struct archive *, struct archive_entry *,
|
||||
struct archive * /* dest */);
|
||||
__LA_DECL 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. */
|
||||
__LA_DECL void archive_read_extract_set_skip_file(struct archive *,
|
||||
dev_t, ino_t);
|
||||
|
||||
/* Close the file and release most resources. */
|
||||
__LA_DECL int archive_read_close(struct archive *);
|
||||
/* Release all resources and destroy the object. */
|
||||
/* Note that archive_read_free will call archive_read_close for you. */
|
||||
__LA_DECL int archive_read_free(struct archive *);
|
||||
#if ARCHIVE_VERSION_NUMBER < 4000000
|
||||
/* Synonym for archive_read_free() for backwards compatibility. */
|
||||
__LA_DECL int 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_free to cleanup the writer and release resources
|
||||
*/
|
||||
__LA_DECL struct archive *archive_write_new(void);
|
||||
__LA_DECL int archive_write_set_bytes_per_block(struct archive *,
|
||||
int bytes_per_block);
|
||||
__LA_DECL int archive_write_get_bytes_per_block(struct archive *);
|
||||
/* XXX This is badly misnamed; suggestions appreciated. XXX */
|
||||
__LA_DECL int archive_write_set_bytes_in_last_block(struct archive *,
|
||||
int bytes_in_last_block);
|
||||
__LA_DECL 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. */
|
||||
__LA_DECL int archive_write_set_skip_file(struct archive *, dev_t, ino_t);
|
||||
|
||||
__LA_DECL int archive_write_set_compression_bzip2(struct archive *);
|
||||
__LA_DECL int archive_write_set_compression_compress(struct archive *);
|
||||
__LA_DECL int archive_write_set_compression_gzip(struct archive *);
|
||||
__LA_DECL int archive_write_set_compression_lzma(struct archive *);
|
||||
__LA_DECL int archive_write_set_compression_none(struct archive *);
|
||||
__LA_DECL int archive_write_set_compression_program(struct archive *,
|
||||
const char *cmd);
|
||||
__LA_DECL int archive_write_set_compression_xz(struct archive *);
|
||||
/* A convenience function to set the format based on the code or name. */
|
||||
__LA_DECL int archive_write_set_format(struct archive *, int format_code);
|
||||
__LA_DECL int archive_write_set_format_by_name(struct archive *,
|
||||
const char *name);
|
||||
/* To minimize link pollution, use one or more of the following. */
|
||||
__LA_DECL int archive_write_set_format_ar_bsd(struct archive *);
|
||||
__LA_DECL int archive_write_set_format_ar_svr4(struct archive *);
|
||||
__LA_DECL int archive_write_set_format_cpio(struct archive *);
|
||||
__LA_DECL int archive_write_set_format_cpio_newc(struct archive *);
|
||||
__LA_DECL int archive_write_set_format_mtree(struct archive *);
|
||||
/* TODO: int archive_write_set_format_old_tar(struct archive *); */
|
||||
__LA_DECL int archive_write_set_format_pax(struct archive *);
|
||||
__LA_DECL int archive_write_set_format_pax_restricted(struct archive *);
|
||||
__LA_DECL int archive_write_set_format_shar(struct archive *);
|
||||
__LA_DECL int archive_write_set_format_shar_dump(struct archive *);
|
||||
__LA_DECL int archive_write_set_format_ustar(struct archive *);
|
||||
__LA_DECL int archive_write_set_format_zip(struct archive *);
|
||||
__LA_DECL int archive_write_open(struct archive *, void *,
|
||||
archive_open_callback *, archive_write_callback *,
|
||||
archive_close_callback *);
|
||||
__LA_DECL int archive_write_open_fd(struct archive *, int _fd);
|
||||
__LA_DECL int archive_write_open_filename(struct archive *, const char *_file);
|
||||
/* A deprecated synonym for archive_write_open_filename() */
|
||||
__LA_DECL int archive_write_open_file(struct archive *, const char *_file);
|
||||
__LA_DECL 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. */
|
||||
__LA_DECL 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.
|
||||
*/
|
||||
__LA_DECL int archive_write_header(struct archive *,
|
||||
struct archive_entry *);
|
||||
#if ARCHIVE_VERSION_NUMBER < 2000000
|
||||
/* This was erroneously declared to return "int" in libarchive 1.x. */
|
||||
__LA_DECL int archive_write_data(struct archive *,
|
||||
const void *, size_t);
|
||||
#else
|
||||
/* Libarchive 2.0 and later return ssize_t here. */
|
||||
__LA_DECL __LA_SSIZE_T archive_write_data(struct archive *,
|
||||
const void *, size_t);
|
||||
#endif
|
||||
|
||||
#if ARCHIVE_VERSION_NUMBER < 3000000
|
||||
/* Libarchive 1.x and 2.x use off_t for the argument, but that's not
|
||||
* stable on Linux. */
|
||||
__LA_DECL __LA_SSIZE_T archive_write_data_block(struct archive *,
|
||||
const void *, size_t, off_t);
|
||||
#else
|
||||
/* Libarchive 3.0 uses explicit int64_t to ensure consistent 64-bit support. */
|
||||
__LA_DECL __LA_SSIZE_T archive_write_data_block(struct archive *,
|
||||
const void *, size_t, __LA_INT64_T);
|
||||
#endif
|
||||
__LA_DECL int archive_write_finish_entry(struct archive *);
|
||||
__LA_DECL int archive_write_close(struct archive *);
|
||||
|
||||
/* This can fail if the archive wasn't already closed, in which case
|
||||
* archive_write_free() will implicitly call archive_write_close(). */
|
||||
__LA_DECL int archive_write_free(struct archive *);
|
||||
#if ARCHIVE_VERSION_NUMBER < 4000000
|
||||
/* Synonym for archive_write_free() for backwards compatibility. */
|
||||
__LA_DECL int archive_write_finish(struct archive *);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Set write options.
|
||||
*/
|
||||
/* Apply option string to the format only. */
|
||||
__LA_DECL int archive_write_set_format_options(struct archive *_a,
|
||||
const char *s);
|
||||
/* Apply option string to the compressor only. */
|
||||
__LA_DECL int archive_write_set_compressor_options(struct archive *_a,
|
||||
const char *s);
|
||||
/* Apply option string to both the format and the compressor. */
|
||||
__LA_DECL int archive_write_set_options(struct archive *_a,
|
||||
const char *s);
|
||||
|
||||
|
||||
/*-
|
||||
* ARCHIVE_WRITE_DISK API
|
||||
*
|
||||
* 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 probably
|
||||
* want to set the options.
|
||||
* 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_free 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.
|
||||
*/
|
||||
__LA_DECL struct archive *archive_write_disk_new(void);
|
||||
/* This file will not be overwritten. */
|
||||
__LA_DECL int archive_write_disk_set_skip_file(struct archive *,
|
||||
dev_t, ino_t);
|
||||
/* Set flags to control how the next item gets created.
|
||||
* This accepts a bitmask of ARCHIVE_EXTRACT_XXX flags defined above. */
|
||||
__LA_DECL 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".
|
||||
*/
|
||||
__LA_DECL 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.
|
||||
*/
|
||||
__LA_DECL int archive_write_disk_set_group_lookup(struct archive *,
|
||||
void * /* private_data */,
|
||||
__LA_GID_T (*)(void *, const char *, __LA_GID_T),
|
||||
void (* /* cleanup */)(void *));
|
||||
__LA_DECL int archive_write_disk_set_user_lookup(struct archive *,
|
||||
void * /* private_data */,
|
||||
__LA_UID_T (*)(void *, const char *, __LA_UID_T),
|
||||
void (* /* cleanup */)(void *));
|
||||
|
||||
/*
|
||||
* ARCHIVE_READ_DISK API
|
||||
*
|
||||
* This is still evolving and somewhat experimental.
|
||||
*/
|
||||
__LA_DECL struct archive *archive_read_disk_new(void);
|
||||
/* The names for symlink modes here correspond to an old BSD
|
||||
* command-line argument convention: -L, -P, -H */
|
||||
/* Follow all symlinks. */
|
||||
__LA_DECL int archive_read_disk_set_symlink_logical(struct archive *);
|
||||
/* Follow no symlinks. */
|
||||
__LA_DECL int archive_read_disk_set_symlink_physical(struct archive *);
|
||||
/* Follow symlink initially, then not. */
|
||||
__LA_DECL int archive_read_disk_set_symlink_hybrid(struct archive *);
|
||||
/* TODO: Handle Linux stat32/stat64 ugliness. <sigh> */
|
||||
__LA_DECL int archive_read_disk_entry_from_file(struct archive *,
|
||||
struct archive_entry *, int /* fd */, const struct stat *);
|
||||
/* Look up gname for gid or uname for uid. */
|
||||
/* Default implementations are very, very stupid. */
|
||||
__LA_DECL const char *archive_read_disk_gname(struct archive *, __LA_GID_T);
|
||||
__LA_DECL const char *archive_read_disk_uname(struct archive *, __LA_UID_T);
|
||||
/* "Standard" implementation uses getpwuid_r, getgrgid_r and caches the
|
||||
* results for performance. */
|
||||
__LA_DECL int archive_read_disk_set_standard_lookup(struct archive *);
|
||||
/* You can install your own lookups if you like. */
|
||||
__LA_DECL int archive_read_disk_set_gname_lookup(struct archive *,
|
||||
void * /* private_data */,
|
||||
const char *(* /* lookup_fn */)(void *, __LA_GID_T),
|
||||
void (* /* cleanup_fn */)(void *));
|
||||
__LA_DECL int archive_read_disk_set_uname_lookup(struct archive *,
|
||||
void * /* private_data */,
|
||||
const char *(* /* lookup_fn */)(void *, __LA_UID_T),
|
||||
void (* /* cleanup_fn */)(void *));
|
||||
|
||||
/*
|
||||
* Accessor functions to read/set various information in
|
||||
* the struct archive object:
|
||||
*/
|
||||
/* Bytes written after compression or read before decompression. */
|
||||
__LA_DECL __LA_INT64_T archive_position_compressed(struct archive *);
|
||||
/* Bytes written to compressor or read from decompressor. */
|
||||
__LA_DECL __LA_INT64_T archive_position_uncompressed(struct archive *);
|
||||
|
||||
__LA_DECL const char *archive_compression_name(struct archive *);
|
||||
__LA_DECL int archive_compression(struct archive *);
|
||||
__LA_DECL int archive_errno(struct archive *);
|
||||
__LA_DECL const char *archive_error_string(struct archive *);
|
||||
__LA_DECL const char *archive_format_name(struct archive *);
|
||||
__LA_DECL int archive_format(struct archive *);
|
||||
__LA_DECL void archive_clear_error(struct archive *);
|
||||
__LA_DECL void archive_set_error(struct archive *, int _err,
|
||||
const char *fmt, ...) __LA_PRINTF(3, 4);
|
||||
__LA_DECL void archive_copy_error(struct archive *dest,
|
||||
struct archive *src);
|
||||
__LA_DECL int archive_file_count(struct archive *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/* These are meaningless outside of this header. */
|
||||
#undef __LA_DECL
|
||||
#undef __LA_GID_T
|
||||
#undef __LA_UID_T
|
||||
|
||||
/* These need to remain defined because they're used in the
|
||||
* callback type definitions. XXX Fix this. This is ugly. XXX */
|
||||
/* #undef __LA_INT64_T */
|
||||
/* #undef __LA_SSIZE_T */
|
||||
|
||||
#endif /* !ARCHIVE_H_INCLUDED */
|
@ -1,134 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
#include <windows.h>
|
||||
#include <winbase.h>
|
||||
#endif
|
||||
|
||||
#include "archive_private.h"
|
||||
|
||||
static void
|
||||
errmsg(const char *m)
|
||||
{
|
||||
size_t s = strlen(m);
|
||||
ssize_t written;
|
||||
|
||||
while (s > 0) {
|
||||
written = write(2, m, strlen(m));
|
||||
if (written <= 0)
|
||||
return;
|
||||
m += written;
|
||||
s -= written;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
diediedie(void)
|
||||
{
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG)
|
||||
/* Cause a breakpoint exception */
|
||||
DebugBreak();
|
||||
#endif
|
||||
abort(); /* Terminate the program abnormally. */
|
||||
}
|
||||
|
||||
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 & (1 + ~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();
|
||||
}
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2009 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef __LIBARCHIVE_BUILD
|
||||
#error This header is only to be used internally to libarchive.
|
||||
#endif
|
||||
|
||||
/*
|
||||
* When zlib is unavailable, we should still be able to validate
|
||||
* uncompressed zip archives. That requires us to be able to compute
|
||||
* the CRC32 check value. This is a drop-in compatible replacement
|
||||
* for crc32() from zlib. It's slower than the zlib implementation,
|
||||
* but still pretty fast: This runs about 300MB/s on my 3GHz P4
|
||||
* compared to about 800MB/s for the zlib implementation.
|
||||
*/
|
||||
static unsigned long
|
||||
crc32(unsigned long crc, const void *_p, size_t len)
|
||||
{
|
||||
unsigned long crc2, b, i;
|
||||
const unsigned char *p = _p;
|
||||
static volatile int crc_tbl_inited = 0;
|
||||
static unsigned long crc_tbl[256];
|
||||
|
||||
if (!crc_tbl_inited) {
|
||||
for (b = 0; b < 256; ++b) {
|
||||
crc2 = b;
|
||||
for (i = 8; i > 0; --i) {
|
||||
if (crc2 & 1)
|
||||
crc2 = (crc2 >> 1) ^ 0xedb88320UL;
|
||||
else
|
||||
crc2 = (crc2 >> 1);
|
||||
}
|
||||
crc_tbl[b] = crc2;
|
||||
}
|
||||
crc_tbl_inited = 1;
|
||||
}
|
||||
|
||||
crc = crc ^ 0xffffffffUL;
|
||||
while (len--)
|
||||
crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
|
||||
return (crc ^ 0xffffffffUL);
|
||||
}
|
@ -1,162 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2002 Thomas Moestl <tmm@FreeBSD.org>
|
||||
* 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$
|
||||
*
|
||||
* Borrowed from FreeBSD's <sys/endian.h>
|
||||
*/
|
||||
|
||||
#ifndef __LIBARCHIVE_BUILD
|
||||
#error This header is only to be used internally to libarchive.
|
||||
#endif
|
||||
|
||||
/* Note: This is a purely internal header! */
|
||||
/* Do not use this outside of libarchive internal code! */
|
||||
|
||||
#ifndef ARCHIVE_ENDIAN_H_INCLUDED
|
||||
#define ARCHIVE_ENDIAN_H_INCLUDED
|
||||
|
||||
|
||||
/*
|
||||
* Disabling inline keyword for compilers known to choke on it:
|
||||
* - Watcom C++ in C code. (For any version?)
|
||||
* - SGI MIPSpro
|
||||
* - Microsoft Visual C++ 6.0 (supposedly newer versions too)
|
||||
*/
|
||||
#if defined(__WATCOMC__) || defined(__sgi) || defined(__hpux) || defined(__BORLANDC__)
|
||||
#define inline
|
||||
#elif defined(_MSC_VER)
|
||||
#define inline __inline
|
||||
#endif
|
||||
|
||||
/* Alignment-agnostic encode/decode bytestream to/from little/big endian. */
|
||||
|
||||
static inline uint16_t
|
||||
archive_be16dec(const void *pp)
|
||||
{
|
||||
unsigned char const *p = (unsigned char const *)pp;
|
||||
|
||||
return ((p[0] << 8) | p[1]);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
archive_be32dec(const void *pp)
|
||||
{
|
||||
unsigned char const *p = (unsigned char const *)pp;
|
||||
|
||||
return ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
archive_be64dec(const void *pp)
|
||||
{
|
||||
unsigned char const *p = (unsigned char const *)pp;
|
||||
|
||||
return (((uint64_t)archive_be32dec(p) << 32) | archive_be32dec(p + 4));
|
||||
}
|
||||
|
||||
static inline uint16_t
|
||||
archive_le16dec(const void *pp)
|
||||
{
|
||||
unsigned char const *p = (unsigned char const *)pp;
|
||||
|
||||
return ((p[1] << 8) | p[0]);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
archive_le32dec(const void *pp)
|
||||
{
|
||||
unsigned char const *p = (unsigned char const *)pp;
|
||||
|
||||
return ((p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]);
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
archive_le64dec(const void *pp)
|
||||
{
|
||||
unsigned char const *p = (unsigned char const *)pp;
|
||||
|
||||
return (((uint64_t)archive_le32dec(p + 4) << 32) | archive_le32dec(p));
|
||||
}
|
||||
|
||||
static inline void
|
||||
archive_be16enc(void *pp, uint16_t u)
|
||||
{
|
||||
unsigned char *p = (unsigned char *)pp;
|
||||
|
||||
p[0] = (u >> 8) & 0xff;
|
||||
p[1] = u & 0xff;
|
||||
}
|
||||
|
||||
static inline void
|
||||
archive_be32enc(void *pp, uint32_t u)
|
||||
{
|
||||
unsigned char *p = (unsigned char *)pp;
|
||||
|
||||
p[0] = (u >> 24) & 0xff;
|
||||
p[1] = (u >> 16) & 0xff;
|
||||
p[2] = (u >> 8) & 0xff;
|
||||
p[3] = u & 0xff;
|
||||
}
|
||||
|
||||
static inline void
|
||||
archive_be64enc(void *pp, uint64_t u)
|
||||
{
|
||||
unsigned char *p = (unsigned char *)pp;
|
||||
|
||||
archive_be32enc(p, u >> 32);
|
||||
archive_be32enc(p + 4, u & 0xffffffff);
|
||||
}
|
||||
|
||||
static inline void
|
||||
archive_le16enc(void *pp, uint16_t u)
|
||||
{
|
||||
unsigned char *p = (unsigned char *)pp;
|
||||
|
||||
p[0] = u & 0xff;
|
||||
p[1] = (u >> 8) & 0xff;
|
||||
}
|
||||
|
||||
static inline void
|
||||
archive_le32enc(void *pp, uint32_t u)
|
||||
{
|
||||
unsigned char *p = (unsigned char *)pp;
|
||||
|
||||
p[0] = u & 0xff;
|
||||
p[1] = (u >> 8) & 0xff;
|
||||
p[2] = (u >> 16) & 0xff;
|
||||
p[3] = (u >> 24) & 0xff;
|
||||
}
|
||||
|
||||
static inline void
|
||||
archive_le64enc(void *pp, uint64_t u)
|
||||
{
|
||||
unsigned char *p = (unsigned char *)pp;
|
||||
|
||||
archive_le32enc(p, u & 0xffffffff);
|
||||
archive_le32enc(p + 4, u >> 32);
|
||||
}
|
||||
|
||||
#endif
|
@ -1,433 +0,0 @@
|
||||
.\" 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 May 12, 2008
|
||||
.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 ,
|
||||
.Nm archive_entry_copy_fflags_text_w ,
|
||||
.Nm archive_entry_copy_gname ,
|
||||
.Nm archive_entry_copy_gname_w ,
|
||||
.Nm archive_entry_copy_hardlink ,
|
||||
.Nm archive_entry_copy_hardlink_w ,
|
||||
.Nm archive_entry_copy_link ,
|
||||
.Nm archive_entry_copy_link_w ,
|
||||
.Nm archive_entry_copy_pathname_w ,
|
||||
.Nm archive_entry_copy_sourcepath ,
|
||||
.Nm archive_entry_copy_stat ,
|
||||
.Nm archive_entry_copy_symlink ,
|
||||
.Nm archive_entry_copy_symlink_w ,
|
||||
.Nm archive_entry_copy_uname ,
|
||||
.Nm archive_entry_copy_uname_w ,
|
||||
.Nm archive_entry_dev ,
|
||||
.Nm archive_entry_devmajor ,
|
||||
.Nm archive_entry_devminor ,
|
||||
.Nm archive_entry_filetype ,
|
||||
.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_nlink ,
|
||||
.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_atime ,
|
||||
.Nm archive_entry_set_ctime ,
|
||||
.Nm archive_entry_set_dev ,
|
||||
.Nm archive_entry_set_devmajor ,
|
||||
.Nm archive_entry_set_devminor ,
|
||||
.Nm archive_entry_set_filetype ,
|
||||
.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_sourcepath ,
|
||||
.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
|
||||
.Fo archive_entry_acl_add_entry
|
||||
.Fa "struct archive_entry *"
|
||||
.Fa "int type"
|
||||
.Fa "int permset"
|
||||
.Fa "int tag"
|
||||
.Fa "int qual"
|
||||
.Fa "const char *name"
|
||||
.Fc
|
||||
.Ft void
|
||||
.Fo archive_entry_acl_add_entry_w
|
||||
.Fa "struct archive_entry *"
|
||||
.Fa "int type"
|
||||
.Fa "int permset"
|
||||
.Fa "int tag"
|
||||
.Fa "int qual"
|
||||
.Fa "const wchar_t *name"
|
||||
.Fc
|
||||
.Ft void
|
||||
.Fn archive_entry_acl_clear "struct archive_entry *"
|
||||
.Ft int
|
||||
.Fn archive_entry_acl_count "struct archive_entry *" "int type"
|
||||
.Ft int
|
||||
.Fo archive_entry_acl_next
|
||||
.Fa "struct archive_entry *"
|
||||
.Fa "int want_type"
|
||||
.Fa "int *type"
|
||||
.Fa "int *permset"
|
||||
.Fa "int *tag"
|
||||
.Fa "int *qual"
|
||||
.Fa "const char **name"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fo archive_entry_acl_next_w
|
||||
.Fa "struct archive_entry *"
|
||||
.Fa "int want_type"
|
||||
.Fa "int *type"
|
||||
.Fa "int *permset"
|
||||
.Fa "int *tag"
|
||||
.Fa "int *qual"
|
||||
.Fa "const wchar_t **name"
|
||||
.Fc
|
||||
.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 char * *
|
||||
.Fn archive_entry_copy_fflags_text_w "struct archive_entry *" "const char *"
|
||||
.Ft const wchar_t *
|
||||
.Fn archive_entry_copy_fflags_text_w "struct archive_entry *" "const wchar_t *"
|
||||
.Ft void
|
||||
.Fn archive_entry_copy_gname "struct archive_entry *" "const char *"
|
||||
.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_sourcepath "struct archive_entry *" "const char *"
|
||||
.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 "struct archive_entry *" "const char *"
|
||||
.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 dev_t
|
||||
.Fn archive_entry_devmajor "struct archive_entry *"
|
||||
.Ft dev_t
|
||||
.Fn archive_entry_devminor "struct archive_entry *"
|
||||
.Ft mode_t
|
||||
.Fn archive_entry_filetype "struct archive_entry *"
|
||||
.Ft void
|
||||
.Fo archive_entry_fflags
|
||||
.Fa "struct archive_entry *"
|
||||
.Fa "unsigned long *set"
|
||||
.Fa "unsigned long *clear"
|
||||
.Fc
|
||||
.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 unsigned int
|
||||
.Fn archive_entry_nlink "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_dev "struct archive_entry *" "dev_t"
|
||||
.Ft void
|
||||
.Fn archive_entry_set_devmajor "struct archive_entry *" "dev_t"
|
||||
.Ft void
|
||||
.Fn archive_entry_set_devminor "struct archive_entry *" "dev_t"
|
||||
.Ft void
|
||||
.Fn archive_entry_set_filetype "struct archive_entry *" "unsigned int"
|
||||
.Ft void
|
||||
.Fo archive_entry_set_fflags
|
||||
.Fa "struct archive_entry *"
|
||||
.Fa "unsigned long set"
|
||||
.Fa "unsigned long clear"
|
||||
.Fc
|
||||
.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_ino "struct archive_entry *" "unsigned long"
|
||||
.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_nlink "struct archive_entry *" "unsigned int"
|
||||
.Ft void
|
||||
.Fn archive_entry_set_pathname "struct archive_entry *" "const char *"
|
||||
.Ft void
|
||||
.Fn archive_entry_set_rdev "struct archive_entry *" "dev_t"
|
||||
.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 char *
|
||||
.Fn archive_entry_sourcepath "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
|
||||
and
|
||||
.Fn archive_entry_copy_fflags_text_w
|
||||
functions parse 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
|
File diff suppressed because it is too large
Load Diff
@ -1,524 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2008 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$
|
||||
*/
|
||||
|
||||
#ifndef ARCHIVE_ENTRY_H_INCLUDED
|
||||
#define ARCHIVE_ENTRY_H_INCLUDED
|
||||
|
||||
/*
|
||||
* Note: archive_entry.h is for use outside of libarchive; the
|
||||
* configuration headers (config.h, archive_platform.h, etc.) are
|
||||
* purely internal. Do NOT use HAVE_XXX configuration macros to
|
||||
* control the behavior of this header! If you must conditionalize,
|
||||
* use predefined compiler and/or platform macros.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stddef.h> /* for wchar_t */
|
||||
#include <time.h>
|
||||
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
/* Get appropriate definitions of standard POSIX-style types. */
|
||||
/* These should match the types used in 'struct stat' */
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
#define __LA_INT64_T __int64
|
||||
# if defined(__BORLANDC__)
|
||||
# define __LA_UID_T uid_t
|
||||
# define __LA_GID_T gid_t
|
||||
# define __LA_DEV_T dev_t
|
||||
# define __LA_MODE_T mode_t
|
||||
# else
|
||||
# define __LA_UID_T short
|
||||
# define __LA_GID_T short
|
||||
# define __LA_DEV_T unsigned int
|
||||
# define __LA_MODE_T unsigned short
|
||||
# endif
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#define __LA_INT64_T int64_t
|
||||
#define __LA_UID_T uid_t
|
||||
#define __LA_GID_T gid_t
|
||||
#define __LA_DEV_T dev_t
|
||||
#define __LA_MODE_T mode_t
|
||||
#endif
|
||||
|
||||
/*
|
||||
* XXX Is this defined for all Windows compilers? If so, in what
|
||||
* header? It would be nice to remove the __LA_INO_T indirection and
|
||||
* just use plain ino_t everywhere. Likewise for the other types just
|
||||
* above.
|
||||
*/
|
||||
#define __LA_INO_T ino_t
|
||||
|
||||
|
||||
/*
|
||||
* On Windows, define LIBARCHIVE_STATIC if you're building or using a
|
||||
* .lib. The default here assumes you're building a DLL. Only
|
||||
* libarchive source should ever define __LIBARCHIVE_BUILD.
|
||||
*/
|
||||
#if ((defined __WIN32__) || (defined _WIN32) || defined(__CYGWIN__)) && (!defined LIBARCHIVE_STATIC)
|
||||
# ifdef __LIBARCHIVE_BUILD
|
||||
# ifdef __GNUC__
|
||||
# define __LA_DECL __attribute__((dllexport)) extern
|
||||
# else
|
||||
# define __LA_DECL __declspec(dllexport)
|
||||
# endif
|
||||
# else
|
||||
# ifdef __GNUC__
|
||||
# define __LA_DECL __attribute__((dllimport)) extern
|
||||
# else
|
||||
# define __LA_DECL __declspec(dllimport)
|
||||
# endif
|
||||
# endif
|
||||
#else
|
||||
/* Static libraries on all platforms and shared libraries on non-Windows. */
|
||||
# define __LA_DECL
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Description of an archive entry.
|
||||
*
|
||||
* You can think of this as "struct stat" with some 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()
|
||||
* and passed to archive_entry_set_filetype().
|
||||
*
|
||||
* These values match S_XXX defines on every platform I've checked,
|
||||
* including Windows, AIX, Linux, Solaris, and BSD. They're
|
||||
* (re)defined here because platforms generally don't define the ones
|
||||
* they don't support. For example, Windows doesn't define S_IFLNK or
|
||||
* S_IFBLK. Instead of having a mass of conditional logic and system
|
||||
* checks to define any S_XXX values that aren't supported locally,
|
||||
* I've just defined a new set of such constants so that
|
||||
* libarchive-based applications can manipulate and identify archive
|
||||
* entries properly even if the hosting platform can't store them on
|
||||
* disk.
|
||||
*
|
||||
* These values are also used directly within some portable formats,
|
||||
* such as cpio. If you find a platform that varies from these, the
|
||||
* correct solution is to leave these alone and translate from these
|
||||
* portable values to platform-native values when entries are read from
|
||||
* or written to disk.
|
||||
*/
|
||||
#define AE_IFMT 0170000
|
||||
#define AE_IFREG 0100000
|
||||
#define AE_IFLNK 0120000
|
||||
#define AE_IFSOCK 0140000
|
||||
#define AE_IFCHR 0020000
|
||||
#define AE_IFBLK 0060000
|
||||
#define AE_IFDIR 0040000
|
||||
#define AE_IFIFO 0010000
|
||||
|
||||
/*
|
||||
* Basic object manipulation
|
||||
*/
|
||||
|
||||
__LA_DECL struct archive_entry *archive_entry_clear(struct archive_entry *);
|
||||
/* The 'clone' function does a deep copy; all of the strings are copied too. */
|
||||
__LA_DECL struct archive_entry *archive_entry_clone(struct archive_entry *);
|
||||
__LA_DECL void archive_entry_free(struct archive_entry *);
|
||||
__LA_DECL struct archive_entry *archive_entry_new(void);
|
||||
|
||||
/*
|
||||
* Retrieve fields from an archive_entry.
|
||||
*
|
||||
* There are a number of implicit conversions among these fields. For
|
||||
* example, if a regular string field is set and you read the _w wide
|
||||
* character field, the entry will implicitly convert narrow-to-wide
|
||||
* using the current locale. Similarly, dev values are automatically
|
||||
* updated when you write devmajor or devminor and vice versa.
|
||||
*
|
||||
* In addition, fields can be "set" or "unset." Unset string fields
|
||||
* return NULL, non-string fields have _is_set() functions to test
|
||||
* whether they've been set. You can "unset" a string field by
|
||||
* assigning NULL; non-string fields have _unset() functions to
|
||||
* unset them.
|
||||
*
|
||||
* Note: There is one ambiguity in the above; string fields will
|
||||
* also return NULL when implicit character set conversions fail.
|
||||
* This is usually what you want.
|
||||
*/
|
||||
__LA_DECL time_t archive_entry_atime(struct archive_entry *);
|
||||
__LA_DECL long archive_entry_atime_nsec(struct archive_entry *);
|
||||
__LA_DECL int archive_entry_atime_is_set(struct archive_entry *);
|
||||
__LA_DECL time_t archive_entry_birthtime(struct archive_entry *);
|
||||
__LA_DECL long archive_entry_birthtime_nsec(struct archive_entry *);
|
||||
__LA_DECL int archive_entry_birthtime_is_set(struct archive_entry *);
|
||||
__LA_DECL time_t archive_entry_ctime(struct archive_entry *);
|
||||
__LA_DECL long archive_entry_ctime_nsec(struct archive_entry *);
|
||||
__LA_DECL int archive_entry_ctime_is_set(struct archive_entry *);
|
||||
__LA_DECL dev_t archive_entry_dev(struct archive_entry *);
|
||||
__LA_DECL dev_t archive_entry_devmajor(struct archive_entry *);
|
||||
__LA_DECL dev_t archive_entry_devminor(struct archive_entry *);
|
||||
__LA_DECL __LA_MODE_T archive_entry_filetype(struct archive_entry *);
|
||||
__LA_DECL void archive_entry_fflags(struct archive_entry *,
|
||||
unsigned long * /* set */,
|
||||
unsigned long * /* clear */);
|
||||
__LA_DECL const char *archive_entry_fflags_text(struct archive_entry *);
|
||||
__LA_DECL __LA_GID_T archive_entry_gid(struct archive_entry *);
|
||||
__LA_DECL const char *archive_entry_gname(struct archive_entry *);
|
||||
__LA_DECL const wchar_t *archive_entry_gname_w(struct archive_entry *);
|
||||
__LA_DECL const char *archive_entry_hardlink(struct archive_entry *);
|
||||
__LA_DECL const wchar_t *archive_entry_hardlink_w(struct archive_entry *);
|
||||
__LA_DECL __LA_INO_T archive_entry_ino(struct archive_entry *);
|
||||
__LA_DECL __LA_INT64_T archive_entry_ino64(struct archive_entry *);
|
||||
__LA_DECL __LA_MODE_T archive_entry_mode(struct archive_entry *);
|
||||
__LA_DECL time_t archive_entry_mtime(struct archive_entry *);
|
||||
__LA_DECL long archive_entry_mtime_nsec(struct archive_entry *);
|
||||
__LA_DECL int archive_entry_mtime_is_set(struct archive_entry *);
|
||||
__LA_DECL unsigned int archive_entry_nlink(struct archive_entry *);
|
||||
__LA_DECL const char *archive_entry_pathname(struct archive_entry *);
|
||||
__LA_DECL const wchar_t *archive_entry_pathname_w(struct archive_entry *);
|
||||
__LA_DECL dev_t archive_entry_rdev(struct archive_entry *);
|
||||
__LA_DECL dev_t archive_entry_rdevmajor(struct archive_entry *);
|
||||
__LA_DECL dev_t archive_entry_rdevminor(struct archive_entry *);
|
||||
__LA_DECL const char *archive_entry_sourcepath(struct archive_entry *);
|
||||
__LA_DECL __LA_INT64_T archive_entry_size(struct archive_entry *);
|
||||
__LA_DECL int archive_entry_size_is_set(struct archive_entry *);
|
||||
__LA_DECL const char *archive_entry_strmode(struct archive_entry *);
|
||||
__LA_DECL const char *archive_entry_symlink(struct archive_entry *);
|
||||
__LA_DECL const wchar_t *archive_entry_symlink_w(struct archive_entry *);
|
||||
__LA_DECL __LA_UID_T archive_entry_uid(struct archive_entry *);
|
||||
__LA_DECL const char *archive_entry_uname(struct archive_entry *);
|
||||
__LA_DECL 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.
|
||||
*
|
||||
* Note: As of libarchive 2.4, 'set' functions do copy the string and
|
||||
* are therefore exact synonyms for the 'copy' versions. The 'copy'
|
||||
* names will be retired in libarchive 3.0.
|
||||
*/
|
||||
|
||||
__LA_DECL void archive_entry_set_atime(struct archive_entry *, time_t, long);
|
||||
__LA_DECL void archive_entry_unset_atime(struct archive_entry *);
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
__LA_DECL void archive_entry_copy_bhfi(struct archive_entry *,
|
||||
BY_HANDLE_FILE_INFORMATION *);
|
||||
#endif
|
||||
__LA_DECL void archive_entry_set_birthtime(struct archive_entry *, time_t, long);
|
||||
__LA_DECL void archive_entry_unset_birthtime(struct archive_entry *);
|
||||
__LA_DECL void archive_entry_set_ctime(struct archive_entry *, time_t, long);
|
||||
__LA_DECL void archive_entry_unset_ctime(struct archive_entry *);
|
||||
__LA_DECL void archive_entry_set_dev(struct archive_entry *, dev_t);
|
||||
__LA_DECL void archive_entry_set_devmajor(struct archive_entry *, dev_t);
|
||||
__LA_DECL void archive_entry_set_devminor(struct archive_entry *, dev_t);
|
||||
__LA_DECL void archive_entry_set_filetype(struct archive_entry *, unsigned int);
|
||||
__LA_DECL 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. */
|
||||
__LA_DECL const char *archive_entry_copy_fflags_text(struct archive_entry *,
|
||||
const char *);
|
||||
__LA_DECL const wchar_t *archive_entry_copy_fflags_text_w(struct archive_entry *,
|
||||
const wchar_t *);
|
||||
__LA_DECL void archive_entry_set_gid(struct archive_entry *, __LA_GID_T);
|
||||
__LA_DECL void archive_entry_set_gname(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_copy_gname(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_copy_gname_w(struct archive_entry *, const wchar_t *);
|
||||
__LA_DECL int archive_entry_update_gname_utf8(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_set_hardlink(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_copy_hardlink(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_copy_hardlink_w(struct archive_entry *, const wchar_t *);
|
||||
__LA_DECL int archive_entry_update_hardlink_utf8(struct archive_entry *, const char *);
|
||||
#if ARCHIVE_VERSION_NUMBER >= 3000000
|
||||
/* Starting with libarchive 3.0, this will be synonym for ino64. */
|
||||
__LA_DECL void archive_entry_set_ino(struct archive_entry *, __LA_INT64_T);
|
||||
#else
|
||||
__LA_DECL void archive_entry_set_ino(struct archive_entry *, unsigned long);
|
||||
#endif
|
||||
__LA_DECL void archive_entry_set_ino64(struct archive_entry *, __LA_INT64_T);
|
||||
__LA_DECL void archive_entry_set_link(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_copy_link(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_copy_link_w(struct archive_entry *, const wchar_t *);
|
||||
__LA_DECL int archive_entry_update_link_utf8(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_set_mode(struct archive_entry *, __LA_MODE_T);
|
||||
__LA_DECL void archive_entry_set_mtime(struct archive_entry *, time_t, long);
|
||||
__LA_DECL void archive_entry_unset_mtime(struct archive_entry *);
|
||||
__LA_DECL void archive_entry_set_nlink(struct archive_entry *, unsigned int);
|
||||
__LA_DECL void archive_entry_set_pathname(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_copy_pathname(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_copy_pathname_w(struct archive_entry *, const wchar_t *);
|
||||
__LA_DECL int archive_entry_update_pathname_utf8(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_set_perm(struct archive_entry *, __LA_MODE_T);
|
||||
__LA_DECL void archive_entry_set_rdev(struct archive_entry *, dev_t);
|
||||
__LA_DECL void archive_entry_set_rdevmajor(struct archive_entry *, dev_t);
|
||||
__LA_DECL void archive_entry_set_rdevminor(struct archive_entry *, dev_t);
|
||||
__LA_DECL void archive_entry_set_size(struct archive_entry *, __LA_INT64_T);
|
||||
__LA_DECL void archive_entry_unset_size(struct archive_entry *);
|
||||
__LA_DECL void archive_entry_copy_sourcepath(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_set_symlink(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_copy_symlink(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_copy_symlink_w(struct archive_entry *, const wchar_t *);
|
||||
__LA_DECL int archive_entry_update_symlink_utf8(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_set_uid(struct archive_entry *, __LA_UID_T);
|
||||
__LA_DECL void archive_entry_set_uname(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_copy_uname(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_copy_uname_w(struct archive_entry *, const wchar_t *);
|
||||
__LA_DECL int archive_entry_update_uname_utf8(struct archive_entry *, const char *);
|
||||
/*
|
||||
* Routines to bulk copy fields to/from a platform-native "struct
|
||||
* stat." Libarchive used to just store a struct stat inside of each
|
||||
* archive_entry object, but this created issues when trying to
|
||||
* manipulate archives on systems different than the ones they were
|
||||
* created on.
|
||||
*
|
||||
* TODO: On Linux, provide both stat32 and stat64 versions of these functions.
|
||||
*/
|
||||
__LA_DECL const struct stat *archive_entry_stat(struct archive_entry *);
|
||||
__LA_DECL void archive_entry_copy_stat(struct archive_entry *, const struct stat *);
|
||||
|
||||
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* This last point, in particular, forces me to implement a reasonably
|
||||
* complete set of ACL support routines.
|
||||
*
|
||||
* TODO: Extend this to support NFSv4/NTFS permissions. That should
|
||||
* allow full ACL support on Mac OS, in particular, which uses
|
||||
* POSIX.1e-style interfaces to manipulate NFSv4/NTFS permissions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
__LA_DECL void archive_entry_acl_clear(struct archive_entry *);
|
||||
__LA_DECL void archive_entry_acl_add_entry(struct archive_entry *,
|
||||
int /* type */, int /* permset */, int /* tag */,
|
||||
int /* qual */, const char * /* name */);
|
||||
__LA_DECL 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.
|
||||
*/
|
||||
__LA_DECL int archive_entry_acl_reset(struct archive_entry *, int /* want_type */);
|
||||
__LA_DECL int archive_entry_acl_next(struct archive_entry *, int /* want_type */,
|
||||
int * /* type */, int * /* permset */, int * /* tag */,
|
||||
int * /* qual */, const char ** /* name */);
|
||||
__LA_DECL 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
|
||||
__LA_DECL const wchar_t *archive_entry_acl_text_w(struct archive_entry *,
|
||||
int /* flags */);
|
||||
|
||||
/* Return a count of entries matching 'want_type' */
|
||||
__LA_DECL 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!
|
||||
*
|
||||
* TODO: Move this declaration out of the public header and into
|
||||
* a private header. Warnings above are silly.
|
||||
*/
|
||||
__LA_DECL int __archive_entry_acl_parse_w(struct archive_entry *,
|
||||
const wchar_t *, int /* type */);
|
||||
|
||||
/*
|
||||
* extended attributes
|
||||
*/
|
||||
|
||||
__LA_DECL void archive_entry_xattr_clear(struct archive_entry *);
|
||||
__LA_DECL 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.
|
||||
*/
|
||||
|
||||
__LA_DECL int archive_entry_xattr_count(struct archive_entry *);
|
||||
__LA_DECL int archive_entry_xattr_reset(struct archive_entry *);
|
||||
__LA_DECL int archive_entry_xattr_next(struct archive_entry *,
|
||||
const char ** /* name */, const void ** /* value */, size_t *);
|
||||
|
||||
/*
|
||||
* Utility to match up hardlinks.
|
||||
*
|
||||
* The 'struct archive_entry_linkresolver' is a cache of archive entries
|
||||
* for files with multiple links. Here's how to use it:
|
||||
* 1. Create a lookup object with archive_entry_linkresolver_new()
|
||||
* 2. Tell it the archive format you're using.
|
||||
* 3. Hand each archive_entry to archive_entry_linkify().
|
||||
* That function will return 0, 1, or 2 entries that should
|
||||
* be written.
|
||||
* 4. Call archive_entry_linkify(resolver, NULL) until
|
||||
* no more entries are returned.
|
||||
* 5. Call archive_entry_link_resolver_free(resolver) to free resources.
|
||||
*
|
||||
* The entries returned have their hardlink and size fields updated
|
||||
* appropriately. If an entry is passed in that does not refer to
|
||||
* a file with multiple links, it is returned unchanged. The intention
|
||||
* is that you should be able to simply filter all entries through
|
||||
* this machine.
|
||||
*
|
||||
* To make things more efficient, be sure that each entry has a valid
|
||||
* nlinks value. The hardlink cache uses this to track when all links
|
||||
* have been found. If the nlinks value is zero, it will keep every
|
||||
* name in the cache indefinitely, which can use a lot of memory.
|
||||
*
|
||||
* Note that archive_entry_size() is reset to zero if the file
|
||||
* body should not be written to the archive. Pay attention!
|
||||
*/
|
||||
struct archive_entry_linkresolver;
|
||||
|
||||
/*
|
||||
* There are three different strategies for marking hardlinks.
|
||||
* The descriptions below name them after the best-known
|
||||
* formats that rely on each strategy:
|
||||
*
|
||||
* "Old cpio" is the simplest, it always returns any entry unmodified.
|
||||
* As far as I know, only cpio formats use this. Old cpio archives
|
||||
* store every link with the full body; the onus is on the dearchiver
|
||||
* to detect and properly link the files as they are restored.
|
||||
* "tar" is also pretty simple; it caches a copy the first time it sees
|
||||
* any link. Subsequent appearances are modified to be hardlink
|
||||
* references to the first one without any body. Used by all tar
|
||||
* formats, although the newest tar formats permit the "old cpio" strategy
|
||||
* as well. This strategy is very simple for the dearchiver,
|
||||
* and reasonably straightforward for the archiver.
|
||||
* "new cpio" is trickier. It stores the body only with the last
|
||||
* occurrence. The complication is that we might not
|
||||
* see every link to a particular file in a single session, so
|
||||
* there's no easy way to know when we've seen the last occurrence.
|
||||
* The solution here is to queue one link until we see the next.
|
||||
* At the end of the session, you can enumerate any remaining
|
||||
* entries by calling archive_entry_linkify(NULL) and store those
|
||||
* bodies. If you have a file with three links l1, l2, and l3,
|
||||
* you'll get the following behavior if you see all three links:
|
||||
* linkify(l1) => NULL (the resolver stores l1 internally)
|
||||
* linkify(l2) => l1 (resolver stores l2, you write l1)
|
||||
* linkify(l3) => l2, l3 (all links seen, you can write both).
|
||||
* If you only see l1 and l2, you'll get this behavior:
|
||||
* linkify(l1) => NULL
|
||||
* linkify(l2) => l1
|
||||
* linkify(NULL) => l2 (at end, you retrieve remaining links)
|
||||
* As the name suggests, this strategy is used by newer cpio variants.
|
||||
* It's noticably more complex for the archiver, slightly more complex
|
||||
* for the dearchiver than the tar strategy, but makes it straightforward
|
||||
* to restore a file using any link by simply continuing to scan until
|
||||
* you see a link that is stored with a body. In contrast, the tar
|
||||
* strategy requires you to rescan the archive from the beginning to
|
||||
* correctly extract an arbitrary link.
|
||||
*/
|
||||
|
||||
__LA_DECL struct archive_entry_linkresolver *archive_entry_linkresolver_new(void);
|
||||
__LA_DECL void archive_entry_linkresolver_set_strategy(
|
||||
struct archive_entry_linkresolver *, int /* format_code */);
|
||||
__LA_DECL void archive_entry_linkresolver_free(struct archive_entry_linkresolver *);
|
||||
__LA_DECL void archive_entry_linkify(struct archive_entry_linkresolver *,
|
||||
struct archive_entry **, struct archive_entry **);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/* This is meaningless outside of this header. */
|
||||
#undef __LA_DECL
|
||||
|
||||
#endif /* !ARCHIVE_ENTRY_H_INCLUDED */
|
@ -1,77 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
#include "archive_entry.h"
|
||||
|
||||
void
|
||||
archive_entry_copy_stat(struct archive_entry *entry, const struct stat *st)
|
||||
{
|
||||
#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
|
||||
archive_entry_set_atime(entry, st->st_atime, st->st_atimespec.tv_nsec);
|
||||
archive_entry_set_ctime(entry, st->st_ctime, st->st_ctimespec.tv_nsec);
|
||||
archive_entry_set_mtime(entry, st->st_mtime, st->st_mtimespec.tv_nsec);
|
||||
#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
|
||||
archive_entry_set_atime(entry, st->st_atime, st->st_atim.tv_nsec);
|
||||
archive_entry_set_ctime(entry, st->st_ctime, st->st_ctim.tv_nsec);
|
||||
archive_entry_set_mtime(entry, st->st_mtime, st->st_mtim.tv_nsec);
|
||||
#elif HAVE_STRUCT_STAT_ST_MTIME_N
|
||||
archive_entry_set_atime(entry, st->st_atime, st->st_atime_n);
|
||||
archive_entry_set_ctime(entry, st->st_ctime, st->st_ctime_n);
|
||||
archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_n);
|
||||
#elif HAVE_STRUCT_STAT_ST_UMTIME
|
||||
archive_entry_set_atime(entry, st->st_atime, st->st_uatime * 1000);
|
||||
archive_entry_set_ctime(entry, st->st_ctime, st->st_uctime * 1000);
|
||||
archive_entry_set_mtime(entry, st->st_mtime, st->st_umtime * 1000);
|
||||
#elif HAVE_STRUCT_STAT_ST_MTIME_USEC
|
||||
archive_entry_set_atime(entry, st->st_atime, st->st_atime_usec * 1000);
|
||||
archive_entry_set_ctime(entry, st->st_ctime, st->st_ctime_usec * 1000);
|
||||
archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_usec * 1000);
|
||||
#else
|
||||
archive_entry_set_atime(entry, st->st_atime, 0);
|
||||
archive_entry_set_ctime(entry, st->st_ctime, 0);
|
||||
archive_entry_set_mtime(entry, st->st_mtime, 0);
|
||||
#if HAVE_STRUCT_STAT_ST_BIRTHTIME
|
||||
archive_entry_set_birthtime(entry, st->st_birthtime, 0);
|
||||
#endif
|
||||
#endif
|
||||
#if HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC
|
||||
archive_entry_set_birthtime(entry, st->st_birthtime, st->st_birthtimespec.tv_nsec);
|
||||
#endif
|
||||
archive_entry_set_dev(entry, st->st_dev);
|
||||
archive_entry_set_gid(entry, st->st_gid);
|
||||
archive_entry_set_uid(entry, st->st_uid);
|
||||
archive_entry_set_ino(entry, st->st_ino);
|
||||
archive_entry_set_nlink(entry, st->st_nlink);
|
||||
archive_entry_set_rdev(entry, st->st_rdev);
|
||||
archive_entry_set_size(entry, st->st_size);
|
||||
archive_entry_set_mode(entry, st->st_mode);
|
||||
}
|
@ -1,405 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_entry.h"
|
||||
|
||||
/*
|
||||
* This is mostly a pretty straightforward hash table implementation.
|
||||
* The only interesting bit is the different strategies used to
|
||||
* match up links. These strategies match those used by various
|
||||
* archiving formats:
|
||||
* tar - content stored with first link, remainder refer back to it.
|
||||
* This requires us to match each subsequent link up with the
|
||||
* first appearance.
|
||||
* cpio - Old cpio just stored body with each link, match-ups were
|
||||
* implicit. This is trivial.
|
||||
* new cpio - New cpio only stores body with last link, match-ups
|
||||
* are implicit. This is actually quite tricky; see the notes
|
||||
* below.
|
||||
*/
|
||||
|
||||
/* Users pass us a format code, we translate that into a strategy here. */
|
||||
#define ARCHIVE_ENTRY_LINKIFY_LIKE_TAR 0
|
||||
#define ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE 1
|
||||
#define ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO 2
|
||||
#define ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO 3
|
||||
|
||||
/* Initial size of link cache. */
|
||||
#define links_cache_initial_size 1024
|
||||
|
||||
struct links_entry {
|
||||
struct links_entry *next;
|
||||
struct links_entry *previous;
|
||||
int links; /* # links not yet seen */
|
||||
int hash;
|
||||
struct archive_entry *canonical;
|
||||
struct archive_entry *entry;
|
||||
};
|
||||
|
||||
struct archive_entry_linkresolver {
|
||||
struct links_entry **buckets;
|
||||
struct links_entry *spare;
|
||||
unsigned long number_entries;
|
||||
size_t number_buckets;
|
||||
int strategy;
|
||||
};
|
||||
|
||||
static struct links_entry *find_entry(struct archive_entry_linkresolver *,
|
||||
struct archive_entry *);
|
||||
static void grow_hash(struct archive_entry_linkresolver *);
|
||||
static struct links_entry *insert_entry(struct archive_entry_linkresolver *,
|
||||
struct archive_entry *);
|
||||
static struct links_entry *next_entry(struct archive_entry_linkresolver *);
|
||||
|
||||
struct archive_entry_linkresolver *
|
||||
archive_entry_linkresolver_new(void)
|
||||
{
|
||||
struct archive_entry_linkresolver *res;
|
||||
size_t i;
|
||||
|
||||
res = malloc(sizeof(struct archive_entry_linkresolver));
|
||||
if (res == NULL)
|
||||
return (NULL);
|
||||
memset(res, 0, sizeof(struct archive_entry_linkresolver));
|
||||
res->number_buckets = links_cache_initial_size;
|
||||
res->buckets = malloc(res->number_buckets *
|
||||
sizeof(res->buckets[0]));
|
||||
if (res->buckets == NULL) {
|
||||
free(res);
|
||||
return (NULL);
|
||||
}
|
||||
for (i = 0; i < res->number_buckets; i++)
|
||||
res->buckets[i] = NULL;
|
||||
return (res);
|
||||
}
|
||||
|
||||
void
|
||||
archive_entry_linkresolver_set_strategy(struct archive_entry_linkresolver *res,
|
||||
int fmt)
|
||||
{
|
||||
int fmtbase = fmt & ARCHIVE_FORMAT_BASE_MASK;
|
||||
|
||||
switch (fmtbase) {
|
||||
case ARCHIVE_FORMAT_CPIO:
|
||||
switch (fmt) {
|
||||
case ARCHIVE_FORMAT_CPIO_SVR4_NOCRC:
|
||||
case ARCHIVE_FORMAT_CPIO_SVR4_CRC:
|
||||
res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO;
|
||||
break;
|
||||
default:
|
||||
res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ARCHIVE_FORMAT_MTREE:
|
||||
res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE;
|
||||
break;
|
||||
case ARCHIVE_FORMAT_TAR:
|
||||
res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_TAR;
|
||||
break;
|
||||
default:
|
||||
res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_TAR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
archive_entry_linkresolver_free(struct archive_entry_linkresolver *res)
|
||||
{
|
||||
struct links_entry *le;
|
||||
|
||||
if (res == NULL)
|
||||
return;
|
||||
|
||||
if (res->buckets != NULL) {
|
||||
while ((le = next_entry(res)) != NULL)
|
||||
archive_entry_free(le->entry);
|
||||
free(res->buckets);
|
||||
res->buckets = NULL;
|
||||
}
|
||||
free(res);
|
||||
}
|
||||
|
||||
void
|
||||
archive_entry_linkify(struct archive_entry_linkresolver *res,
|
||||
struct archive_entry **e, struct archive_entry **f)
|
||||
{
|
||||
struct links_entry *le;
|
||||
struct archive_entry *t;
|
||||
|
||||
*f = NULL; /* Default: Don't return a second entry. */
|
||||
|
||||
if (*e == NULL) {
|
||||
le = next_entry(res);
|
||||
if (le != NULL) {
|
||||
*e = le->entry;
|
||||
le->entry = NULL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* If it has only one link, then we're done. */
|
||||
if (archive_entry_nlink(*e) == 1)
|
||||
return;
|
||||
/* Directories, devices never have hardlinks. */
|
||||
if (archive_entry_filetype(*e) == AE_IFDIR
|
||||
|| archive_entry_filetype(*e) == AE_IFBLK
|
||||
|| archive_entry_filetype(*e) == AE_IFCHR)
|
||||
return;
|
||||
|
||||
switch (res->strategy) {
|
||||
case ARCHIVE_ENTRY_LINKIFY_LIKE_TAR:
|
||||
le = find_entry(res, *e);
|
||||
if (le != NULL) {
|
||||
archive_entry_unset_size(*e);
|
||||
archive_entry_copy_hardlink(*e,
|
||||
archive_entry_pathname(le->canonical));
|
||||
} else
|
||||
insert_entry(res, *e);
|
||||
return;
|
||||
case ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE:
|
||||
le = find_entry(res, *e);
|
||||
if (le != NULL) {
|
||||
archive_entry_copy_hardlink(*e,
|
||||
archive_entry_pathname(le->canonical));
|
||||
} else
|
||||
insert_entry(res, *e);
|
||||
return;
|
||||
case ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO:
|
||||
/* This one is trivial. */
|
||||
return;
|
||||
case ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO:
|
||||
le = find_entry(res, *e);
|
||||
if (le != NULL) {
|
||||
/*
|
||||
* Put the new entry in le, return the
|
||||
* old entry from le.
|
||||
*/
|
||||
t = *e;
|
||||
*e = le->entry;
|
||||
le->entry = t;
|
||||
/* Make the old entry into a hardlink. */
|
||||
archive_entry_unset_size(*e);
|
||||
archive_entry_copy_hardlink(*e,
|
||||
archive_entry_pathname(le->canonical));
|
||||
/* If we ran out of links, return the
|
||||
* final entry as well. */
|
||||
if (le->links == 0) {
|
||||
*f = le->entry;
|
||||
le->entry = NULL;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* If we haven't seen it, tuck it away
|
||||
* for future use.
|
||||
*/
|
||||
le = insert_entry(res, *e);
|
||||
le->entry = *e;
|
||||
*e = NULL;
|
||||
}
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static struct links_entry *
|
||||
find_entry(struct archive_entry_linkresolver *res,
|
||||
struct archive_entry *entry)
|
||||
{
|
||||
struct links_entry *le;
|
||||
int hash, bucket;
|
||||
dev_t dev;
|
||||
int64_t ino;
|
||||
|
||||
/* Free a held entry. */
|
||||
if (res->spare != NULL) {
|
||||
archive_entry_free(res->spare->canonical);
|
||||
archive_entry_free(res->spare->entry);
|
||||
free(res->spare);
|
||||
res->spare = NULL;
|
||||
}
|
||||
|
||||
/* If the links cache overflowed and got flushed, don't bother. */
|
||||
if (res->buckets == NULL)
|
||||
return (NULL);
|
||||
|
||||
dev = archive_entry_dev(entry);
|
||||
ino = archive_entry_ino64(entry);
|
||||
hash = (int)(dev ^ ino);
|
||||
|
||||
/* Try to locate this entry in the links cache. */
|
||||
bucket = hash % res->number_buckets;
|
||||
for (le = res->buckets[bucket]; le != NULL; le = le->next) {
|
||||
if (le->hash == hash
|
||||
&& dev == archive_entry_dev(le->canonical)
|
||||
&& ino == archive_entry_ino64(le->canonical)) {
|
||||
/*
|
||||
* Decrement link count each time and release
|
||||
* the entry if it hits zero. This saves
|
||||
* memory and is necessary for detecting
|
||||
* missed links.
|
||||
*/
|
||||
--le->links;
|
||||
if (le->links > 0)
|
||||
return (le);
|
||||
/* Remove it from this hash bucket. */
|
||||
if (le->previous != NULL)
|
||||
le->previous->next = le->next;
|
||||
if (le->next != NULL)
|
||||
le->next->previous = le->previous;
|
||||
if (res->buckets[bucket] == le)
|
||||
res->buckets[bucket] = le->next;
|
||||
res->number_entries--;
|
||||
/* Defer freeing this entry. */
|
||||
res->spare = le;
|
||||
return (le);
|
||||
}
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static struct links_entry *
|
||||
next_entry(struct archive_entry_linkresolver *res)
|
||||
{
|
||||
struct links_entry *le;
|
||||
size_t bucket;
|
||||
|
||||
/* Free a held entry. */
|
||||
if (res->spare != NULL) {
|
||||
archive_entry_free(res->spare->canonical);
|
||||
free(res->spare);
|
||||
res->spare = NULL;
|
||||
}
|
||||
|
||||
/* If the links cache overflowed and got flushed, don't bother. */
|
||||
if (res->buckets == NULL)
|
||||
return (NULL);
|
||||
|
||||
/* Look for next non-empty bucket in the links cache. */
|
||||
for (bucket = 0; bucket < res->number_buckets; bucket++) {
|
||||
le = res->buckets[bucket];
|
||||
if (le != NULL) {
|
||||
/* Remove it from this hash bucket. */
|
||||
if (le->next != NULL)
|
||||
le->next->previous = le->previous;
|
||||
res->buckets[bucket] = le->next;
|
||||
res->number_entries--;
|
||||
/* Defer freeing this entry. */
|
||||
res->spare = le;
|
||||
return (le);
|
||||
}
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static struct links_entry *
|
||||
insert_entry(struct archive_entry_linkresolver *res,
|
||||
struct archive_entry *entry)
|
||||
{
|
||||
struct links_entry *le;
|
||||
int hash, bucket;
|
||||
|
||||
/* Add this entry to the links cache. */
|
||||
le = malloc(sizeof(struct links_entry));
|
||||
if (le == NULL)
|
||||
return (NULL);
|
||||
memset(le, 0, sizeof(*le));
|
||||
le->canonical = archive_entry_clone(entry);
|
||||
|
||||
/* If the links cache is getting too full, enlarge the hash table. */
|
||||
if (res->number_entries > res->number_buckets * 2)
|
||||
grow_hash(res);
|
||||
|
||||
hash = archive_entry_dev(entry) ^ archive_entry_ino64(entry);
|
||||
bucket = hash % res->number_buckets;
|
||||
|
||||
/* If we could allocate the entry, record it. */
|
||||
if (res->buckets[bucket] != NULL)
|
||||
res->buckets[bucket]->previous = le;
|
||||
res->number_entries++;
|
||||
le->next = res->buckets[bucket];
|
||||
le->previous = NULL;
|
||||
res->buckets[bucket] = le;
|
||||
le->hash = hash;
|
||||
le->links = archive_entry_nlink(entry) - 1;
|
||||
return (le);
|
||||
}
|
||||
|
||||
static void
|
||||
grow_hash(struct archive_entry_linkresolver *res)
|
||||
{
|
||||
struct links_entry *le, **new_buckets;
|
||||
size_t new_size;
|
||||
size_t i, bucket;
|
||||
|
||||
/* Try to enlarge the bucket list. */
|
||||
new_size = res->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 < res->number_buckets; i++) {
|
||||
while (res->buckets[i] != NULL) {
|
||||
/* Remove entry from old bucket. */
|
||||
le = res->buckets[i];
|
||||
res->buckets[i] = le->next;
|
||||
|
||||
/* Add entry to new bucket. */
|
||||
bucket = le->hash % new_size;
|
||||
|
||||
if (new_buckets[bucket] != NULL)
|
||||
new_buckets[bucket]->previous =
|
||||
le;
|
||||
le->next = new_buckets[bucket];
|
||||
le->previous = NULL;
|
||||
new_buckets[bucket] = le;
|
||||
}
|
||||
}
|
||||
free(res->buckets);
|
||||
res->buckets = new_buckets;
|
||||
res->number_buckets = new_size;
|
||||
}
|
||||
}
|
@ -1,184 +0,0 @@
|
||||
/*-
|
||||
* 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$
|
||||
*/
|
||||
|
||||
#ifndef __LIBARCHIVE_BUILD
|
||||
#error This header is only to be used internally to libarchive.
|
||||
#endif
|
||||
|
||||
#ifndef ARCHIVE_ENTRY_PRIVATE_H_INCLUDED
|
||||
#define ARCHIVE_ENTRY_PRIVATE_H_INCLUDED
|
||||
|
||||
#include "archive_string.h"
|
||||
|
||||
/*
|
||||
* Handle wide character (i.e., Unicode) and non-wide character
|
||||
* strings transparently.
|
||||
*/
|
||||
|
||||
struct aes {
|
||||
struct archive_string aes_mbs;
|
||||
struct archive_string aes_utf8;
|
||||
const wchar_t *aes_wcs;
|
||||
/* Bitmap of which of the above are valid. Because we're lazy
|
||||
* about malloc-ing and reusing the underlying storage, we
|
||||
* can't rely on NULL pointers to indicate whether a string
|
||||
* has been set. */
|
||||
int aes_set;
|
||||
#define AES_SET_MBS 1
|
||||
#define AES_SET_UTF8 2
|
||||
#define AES_SET_WCS 4
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
/*
|
||||
* 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 & AE_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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Read archive_entry_copy_stat.c for an explanation of why I
|
||||
* don't just use "struct stat" instead of "struct aest" here
|
||||
* and why I have this odd pointer to a separately-allocated
|
||||
* struct stat.
|
||||
*/
|
||||
void *stat;
|
||||
int stat_valid; /* Set to 0 whenever a field in aest changes. */
|
||||
|
||||
struct aest {
|
||||
int64_t aest_atime;
|
||||
uint32_t aest_atime_nsec;
|
||||
int64_t aest_ctime;
|
||||
uint32_t aest_ctime_nsec;
|
||||
int64_t aest_mtime;
|
||||
uint32_t aest_mtime_nsec;
|
||||
int64_t aest_birthtime;
|
||||
uint32_t aest_birthtime_nsec;
|
||||
gid_t aest_gid;
|
||||
int64_t aest_ino;
|
||||
mode_t aest_mode;
|
||||
uint32_t aest_nlink;
|
||||
uint64_t aest_size;
|
||||
uid_t aest_uid;
|
||||
/*
|
||||
* Because converting between device codes and
|
||||
* major/minor values is platform-specific and
|
||||
* inherently a bit risky, we only do that conversion
|
||||
* lazily. That way, we will do a better job of
|
||||
* preserving information in those cases where no
|
||||
* conversion is actually required.
|
||||
*/
|
||||
int aest_dev_is_broken_down;
|
||||
dev_t aest_dev;
|
||||
dev_t aest_devmajor;
|
||||
dev_t aest_devminor;
|
||||
int aest_rdev_is_broken_down;
|
||||
dev_t aest_rdev;
|
||||
dev_t aest_rdevmajor;
|
||||
dev_t aest_rdevminor;
|
||||
} ae_stat;
|
||||
|
||||
int ae_set; /* bitmap of fields that are currently set */
|
||||
#define AE_SET_HARDLINK 1
|
||||
#define AE_SET_SYMLINK 2
|
||||
#define AE_SET_ATIME 4
|
||||
#define AE_SET_CTIME 8
|
||||
#define AE_SET_MTIME 16
|
||||
#define AE_SET_BIRTHTIME 32
|
||||
#define AE_SET_SIZE 64
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
|
||||
/* Not used within libarchive; useful for some clients. */
|
||||
struct aes ae_sourcepath; /* Path this entry is sourced from. */
|
||||
|
||||
/* ACL support. */
|
||||
struct ae_acl *acl_head;
|
||||
struct ae_acl *acl_p;
|
||||
int acl_state; /* See acl_next for details. */
|
||||
wchar_t *acl_text_w;
|
||||
|
||||
/* extattr support. */
|
||||
struct ae_xattr *xattr_head;
|
||||
struct ae_xattr *xattr_p;
|
||||
|
||||
/* Miscellaneous. */
|
||||
char strmode[12];
|
||||
};
|
||||
|
||||
|
||||
#endif /* ARCHIVE_ENTRY_PRIVATE_H_INCLUDED */
|
@ -1,118 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#include "archive_entry.h"
|
||||
#include "archive_entry_private.h"
|
||||
|
||||
const struct stat *
|
||||
archive_entry_stat(struct archive_entry *entry)
|
||||
{
|
||||
struct stat *st;
|
||||
if (entry->stat == NULL) {
|
||||
entry->stat = malloc(sizeof(*st));
|
||||
if (entry->stat == NULL)
|
||||
return (NULL);
|
||||
entry->stat_valid = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If none of the underlying fields have been changed, we
|
||||
* don't need to regenerate. In theory, we could use a bitmap
|
||||
* here to flag only those items that have changed, but the
|
||||
* extra complexity probably isn't worth it. It will be very
|
||||
* rare for anyone to change just one field then request a new
|
||||
* stat structure.
|
||||
*/
|
||||
if (entry->stat_valid)
|
||||
return (entry->stat);
|
||||
|
||||
st = entry->stat;
|
||||
/*
|
||||
* Use the public interfaces to extract items, so that
|
||||
* the appropriate conversions get invoked.
|
||||
*/
|
||||
st->st_atime = archive_entry_atime(entry);
|
||||
#if HAVE_STRUCT_STAT_ST_BIRTHTIME
|
||||
st->st_birthtime = archive_entry_birthtime(entry);
|
||||
#endif
|
||||
st->st_ctime = archive_entry_ctime(entry);
|
||||
st->st_mtime = archive_entry_mtime(entry);
|
||||
st->st_dev = archive_entry_dev(entry);
|
||||
st->st_gid = archive_entry_gid(entry);
|
||||
st->st_uid = archive_entry_uid(entry);
|
||||
st->st_ino = archive_entry_ino64(entry);
|
||||
st->st_nlink = archive_entry_nlink(entry);
|
||||
st->st_rdev = archive_entry_rdev(entry);
|
||||
st->st_size = archive_entry_size(entry);
|
||||
st->st_mode = archive_entry_mode(entry);
|
||||
|
||||
/*
|
||||
* On systems that support high-res timestamps, copy that
|
||||
* information into struct stat.
|
||||
*/
|
||||
#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
|
||||
st->st_atimespec.tv_nsec = archive_entry_atime_nsec(entry);
|
||||
st->st_ctimespec.tv_nsec = archive_entry_ctime_nsec(entry);
|
||||
st->st_mtimespec.tv_nsec = archive_entry_mtime_nsec(entry);
|
||||
#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
|
||||
st->st_atim.tv_nsec = archive_entry_atime_nsec(entry);
|
||||
st->st_ctim.tv_nsec = archive_entry_ctime_nsec(entry);
|
||||
st->st_mtim.tv_nsec = archive_entry_mtime_nsec(entry);
|
||||
#elif HAVE_STRUCT_STAT_ST_MTIME_N
|
||||
st->st_atime_n = archive_entry_atime_nsec(entry);
|
||||
st->st_ctime_n = archive_entry_ctime_nsec(entry);
|
||||
st->st_mtime_n = archive_entry_mtime_nsec(entry);
|
||||
#elif HAVE_STRUCT_STAT_ST_UMTIME
|
||||
st->st_uatime = archive_entry_atime_nsec(entry) / 1000;
|
||||
st->st_uctime = archive_entry_ctime_nsec(entry) / 1000;
|
||||
st->st_umtime = archive_entry_mtime_nsec(entry) / 1000;
|
||||
#elif HAVE_STRUCT_STAT_ST_MTIME_USEC
|
||||
st->st_atime_usec = archive_entry_atime_nsec(entry) / 1000;
|
||||
st->st_ctime_usec = archive_entry_ctime_nsec(entry) / 1000;
|
||||
st->st_mtime_usec = archive_entry_mtime_nsec(entry) / 1000;
|
||||
#endif
|
||||
#if HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC
|
||||
st->st_birthtimespec.tv_nsec = archive_entry_birthtime_nsec(entry);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TODO: On Linux, store 32 or 64 here depending on whether
|
||||
* the cached stat structure is a stat32 or a stat64. This
|
||||
* will allow us to support both variants interchangably.
|
||||
*/
|
||||
entry->stat_valid = 1;
|
||||
|
||||
return (st);
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "archive_entry.h"
|
||||
#include "archive_entry_private.h"
|
||||
|
||||
const char *
|
||||
archive_entry_strmode(struct archive_entry *entry)
|
||||
{
|
||||
static const mode_t permbits[] =
|
||||
{ 0400, 0200, 0100, 0040, 0020, 0010, 0004, 0002, 0001 };
|
||||
char *bp = entry->strmode;
|
||||
mode_t mode;
|
||||
int i;
|
||||
|
||||
/* Fill in a default string, then selectively override. */
|
||||
strcpy(bp, "?rwxrwxrwx ");
|
||||
|
||||
mode = archive_entry_mode(entry);
|
||||
switch (archive_entry_filetype(entry)) {
|
||||
case AE_IFREG: bp[0] = '-'; break;
|
||||
case AE_IFBLK: bp[0] = 'b'; break;
|
||||
case AE_IFCHR: bp[0] = 'c'; break;
|
||||
case AE_IFDIR: bp[0] = 'd'; break;
|
||||
case AE_IFLNK: bp[0] = 'l'; break;
|
||||
case AE_IFSOCK: bp[0] = 's'; break;
|
||||
case AE_IFIFO: bp[0] = 'p'; break;
|
||||
default:
|
||||
if (archive_entry_hardlink(entry) != NULL) {
|
||||
bp[0] = 'h';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 9; i++)
|
||||
if (!(mode & permbits[i]))
|
||||
bp[i+1] = '-';
|
||||
|
||||
if (mode & S_ISUID) {
|
||||
if (mode & 0100) bp[3] = 's';
|
||||
else bp[3] = 'S';
|
||||
}
|
||||
if (mode & S_ISGID) {
|
||||
if (mode & 0010) bp[6] = 's';
|
||||
else bp[6] = 'S';
|
||||
}
|
||||
if (mode & S_ISVTX) {
|
||||
if (mode & 0001) bp[9] = 't';
|
||||
else bp[9] = 'T';
|
||||
}
|
||||
if (archive_entry_acl_count(entry, ARCHIVE_ENTRY_ACL_TYPE_ACCESS))
|
||||
bp[10] = '+';
|
||||
|
||||
return (bp);
|
||||
}
|
@ -1,158 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
#ifdef HAVE_LIMITS_H
|
||||
#include <limits.h>
|
||||
#endif
|
||||
#ifdef HAVE_LINUX_FS_H
|
||||
#include <linux/fs.h> /* for Linux file flags */
|
||||
#endif
|
||||
/*
|
||||
* Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.
|
||||
* As the include guards don't agree, the order of include is important.
|
||||
*/
|
||||
#ifdef HAVE_LINUX_EXT2_FS_H
|
||||
#include <linux/ext2_fs.h> /* for Linux file flags */
|
||||
#endif
|
||||
#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
|
||||
#include <ext2fs/ext2_fs.h> /* for Linux file flags */
|
||||
#endif
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_WCHAR_H
|
||||
#include <wchar.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_entry.h"
|
||||
#include "archive_private.h"
|
||||
#include "archive_entry_private.h"
|
||||
|
||||
/*
|
||||
* 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;
|
||||
*value = NULL;
|
||||
*size = (size_t)0;
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* end of xattr handling
|
||||
*/
|
@ -1,309 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2009 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef __LIBARCHIVE_BUILD
|
||||
#error This header is only to be used internally to libarchive.
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Hash function support in various Operating Systems:
|
||||
*
|
||||
* NetBSD:
|
||||
* - MD5 and SHA1 in libc: without _ after algorithm name
|
||||
* - SHA2 in libc: with _ after algorithm name
|
||||
*
|
||||
* OpenBSD:
|
||||
* - MD5, SHA1 and SHA2 in libc: without _ after algorithm name
|
||||
* - OpenBSD 4.4 and earlier have SHA2 in libc with _ after algorithm name
|
||||
*
|
||||
* DragonFly and FreeBSD (XXX not used yet):
|
||||
* - MD5 and SHA1 in libmd: without _ after algorithm name
|
||||
* - SHA256: with _ after algorithm name
|
||||
*
|
||||
* Mac OS X (10.4 and later):
|
||||
* - MD5, SHA1 and SHA2 in libSystem: with CC_ prefix and _ after algorithm name
|
||||
*
|
||||
* OpenSSL:
|
||||
* - MD5, SHA1 and SHA2 in libcrypto: with _ after algorithm name
|
||||
*
|
||||
* Windows:
|
||||
* - MD5, SHA1 and SHA2 in archive_windows.c: without algorithm name
|
||||
* and with __la_ prefix.
|
||||
*/
|
||||
#if defined(ARCHIVE_HASH_MD5_WIN) ||\
|
||||
defined(ARCHIVE_HASH_SHA1_WIN) || defined(ARCHIVE_HASH_SHA256_WIN) ||\
|
||||
defined(ARCHIVE_HASH_SHA384_WIN) || defined(ARCHIVE_HASH_SHA512_WIN)
|
||||
#include <wincrypt.h>
|
||||
typedef struct {
|
||||
int valid;
|
||||
HCRYPTPROV cryptProv;
|
||||
HCRYPTHASH hash;
|
||||
} Digest_CTX;
|
||||
extern void __la_hash_Init(Digest_CTX *, ALG_ID);
|
||||
extern void __la_hash_Final(unsigned char *, size_t, Digest_CTX *);
|
||||
extern void __la_hash_Update(Digest_CTX *, const unsigned char *, size_t);
|
||||
#endif
|
||||
|
||||
#if defined(ARCHIVE_HASH_MD5_LIBC)
|
||||
# include <md5.h>
|
||||
# define ARCHIVE_HAS_MD5
|
||||
typedef MD5_CTX archive_md5_ctx;
|
||||
# define archive_md5_init(ctx) MD5Init(ctx)
|
||||
# define archive_md5_final(ctx, buf) MD5Final(buf, ctx)
|
||||
# define archive_md5_update(ctx, buf, n) MD5Update(ctx, buf, n)
|
||||
#elif defined(ARCHIVE_HASH_MD5_LIBMD)
|
||||
# include <md5.h>
|
||||
# define ARCHIVE_HAS_MD5
|
||||
typedef MD5_CTX archive_md5_ctx;
|
||||
# define archive_md5_init(ctx) MD5Init(ctx)
|
||||
# define archive_md5_final(ctx, buf) MD5Final(buf, ctx)
|
||||
# define archive_md5_update(ctx, buf, n) MD5Update(ctx, buf, n)
|
||||
#elif defined(ARCHIVE_HASH_MD5_LIBSYSTEM)
|
||||
# include <CommonCrypto/CommonDigest.h>
|
||||
# define ARCHIVE_HAS_MD5
|
||||
typedef CC_MD5_CTX archive_md5_ctx;
|
||||
# define archive_md5_init(ctx) CC_MD5_Init(ctx)
|
||||
# define archive_md5_final(ctx, buf) CC_MD5_Final(buf, ctx)
|
||||
# define archive_md5_update(ctx, buf, n) CC_MD5_Update(ctx, buf, n)
|
||||
#elif defined(ARCHIVE_HASH_MD5_OPENSSL)
|
||||
# include <openssl/md5.h>
|
||||
# define ARCHIVE_HAS_MD5
|
||||
typedef MD5_CTX archive_md5_ctx;
|
||||
# define archive_md5_init(ctx) MD5_Init(ctx)
|
||||
# define archive_md5_final(ctx, buf) MD5_Final(buf, ctx)
|
||||
# define archive_md5_update(ctx, buf, n) MD5_Update(ctx, buf, n)
|
||||
#elif defined(ARCHIVE_HASH_MD5_WIN)
|
||||
# define ARCHIVE_HAS_MD5
|
||||
# define MD5_DIGEST_LENGTH 16
|
||||
typedef Digest_CTX archive_md5_ctx;
|
||||
# define archive_md5_init(ctx) __la_hash_Init(ctx, CALG_MD5)
|
||||
# define archive_md5_final(ctx, buf) __la_hash_Final(buf, MD5_DIGEST_LENGTH, ctx)
|
||||
# define archive_md5_update(ctx, buf, n) __la_hash_Update(ctx, buf, n)
|
||||
#endif
|
||||
|
||||
#if defined(ARCHIVE_HASH_RMD160_LIBC)
|
||||
# include <rmd160.h>
|
||||
# define ARCHIVE_HAS_RMD160
|
||||
typedef RMD160_CTX archive_rmd160_ctx;
|
||||
# define archive_rmd160_init(ctx) RMD160Init(ctx)
|
||||
# define archive_rmd160_final(ctx, buf) RMD160Final(buf, ctx)
|
||||
# define archive_rmd160_update(ctx, buf, n) RMD160Update(ctx, buf, n)
|
||||
#elif defined(ARCHIVE_HASH_RMD160_OPENSSL)
|
||||
# include <openssl/ripemd.h>
|
||||
# define ARCHIVE_HAS_RMD160
|
||||
typedef RIPEMD160_CTX archive_rmd160_ctx;
|
||||
# define archive_rmd160_init(ctx) RIPEMD160_Init(ctx)
|
||||
# define archive_rmd160_final(ctx, buf) RIPEMD160_Final(buf, ctx)
|
||||
# define archive_rmd160_update(ctx, buf, n) RIPEMD160_Update(ctx, buf, n)
|
||||
#endif
|
||||
|
||||
#if defined(ARCHIVE_HASH_SHA1_LIBC)
|
||||
# include <sha1.h>
|
||||
# define ARCHIVE_HAS_SHA1
|
||||
typedef SHA1_CTX archive_sha1_ctx;
|
||||
# define archive_sha1_init(ctx) SHA1Init(ctx)
|
||||
# define archive_sha1_final(ctx, buf) SHA1Final(buf, ctx)
|
||||
# define archive_sha1_update(ctx, buf, n) SHA1Update(ctx, buf, n)
|
||||
#elif defined(ARCHIVE_HASH_SHA1_LIBMD)
|
||||
# include <sha.h>
|
||||
# define ARCHIVE_HAS_SHA1
|
||||
typedef SHA1_CTX archive_sha1_ctx;
|
||||
# define archive_sha1_init(ctx) SHA1_Init(ctx)
|
||||
# define archive_sha1_final(ctx, buf) SHA1_Final(buf, ctx)
|
||||
# define archive_sha1_update(ctx, buf, n) SHA1_Update(ctx, buf, n)
|
||||
#elif defined(ARCHIVE_HASH_SHA1_LIBSYSTEM)
|
||||
# include <CommonCrypto/CommonDigest.h>
|
||||
# define ARCHIVE_HAS_SHA1
|
||||
typedef CC_SHA1_CTX archive_sha1_ctx;
|
||||
# define archive_sha1_init(ctx) CC_SHA1_Init(ctx)
|
||||
# define archive_sha1_final(ctx, buf) CC_SHA1_Final(buf, ctx)
|
||||
# define archive_sha1_update(ctx, buf, n) CC_SHA1_Update(ctx, buf, n)
|
||||
#elif defined(ARCHIVE_HASH_SHA1_OPENSSL)
|
||||
# include <openssl/sha.h>
|
||||
# define ARCHIVE_HAS_SHA1
|
||||
typedef SHA_CTX archive_sha1_ctx;
|
||||
# define archive_sha1_init(ctx) SHA1_Init(ctx)
|
||||
# define archive_sha1_final(ctx, buf) SHA1_Final(buf, ctx)
|
||||
# define archive_sha1_update(ctx, buf, n) SHA1_Update(ctx, buf, n)
|
||||
#elif defined(ARCHIVE_HASH_SHA1_WIN)
|
||||
# define ARCHIVE_HAS_SHA1
|
||||
# define SHA1_DIGEST_LENGTH 20
|
||||
typedef Digest_CTX archive_sha1_ctx;
|
||||
# define archive_sha1_init(ctx) __la_hash_Init(ctx, CALG_SHA1)
|
||||
# define archive_sha1_final(ctx, buf) __la_hash_Final(buf, SHA1_DIGEST_LENGTH, ctx)
|
||||
# define archive_sha1_update(ctx, buf, n) __la_hash_Update(ctx, buf, n)
|
||||
#endif
|
||||
|
||||
#if defined(ARCHIVE_HASH_SHA256_LIBC)
|
||||
# include <sha2.h>
|
||||
# define ARCHIVE_HAS_SHA256
|
||||
typedef SHA256_CTX archive_sha256_ctx;
|
||||
# define archive_sha256_init(ctx) SHA256_Init(ctx)
|
||||
# define archive_sha256_final(ctx, buf) SHA256_Final(buf, ctx)
|
||||
# define archive_sha256_update(ctx, buf, n) SHA256_Update(ctx, buf, n)
|
||||
#elif defined(ARCHIVE_HASH_SHA256_LIBC2)
|
||||
# include <sha2.h>
|
||||
# define ARCHIVE_HAS_SHA256
|
||||
typedef SHA256_CTX archive_sha256_ctx;
|
||||
# define archive_sha256_init(ctx) SHA256Init(ctx)
|
||||
# define archive_sha256_final(ctx, buf) SHA256Final(buf, ctx)
|
||||
# define archive_sha256_update(ctx, buf, n) SHA256Update(ctx, buf, n)
|
||||
#elif defined(ARCHIVE_HASH_SHA256_LIBC3)
|
||||
# include <sha2.h>
|
||||
# define ARCHIVE_HAS_SHA256
|
||||
typedef SHA2_CTX archive_sha256_ctx;
|
||||
# define archive_sha256_init(ctx) SHA256Init(ctx)
|
||||
# define archive_sha256_final(ctx, buf) SHA256Final(buf, ctx)
|
||||
# define archive_sha256_update(ctx, buf, n) SHA256Update(ctx, buf, n)
|
||||
#elif defined(ARCHIVE_HASH_SHA256_LIBMD)
|
||||
# include <sha256.h>
|
||||
# define ARCHIVE_HAS_SHA256
|
||||
typedef SHA256_CTX archive_sha256_ctx;
|
||||
# define archive_sha256_init(ctx) SHA256_Init(ctx)
|
||||
# define archive_sha256_final(ctx, buf) SHA256_Final(buf, ctx)
|
||||
# define archive_sha256_update(ctx, buf, n) SHA256_Update(ctx, buf, n)
|
||||
#elif defined(ARCHIVE_HASH_SHA256_LIBSYSTEM)
|
||||
# include <CommonCrypto/CommonDigest.h>
|
||||
# define ARCHIVE_HAS_SHA256
|
||||
typedef CC_SHA256_CTX archive_shs256_ctx;
|
||||
# define archive_shs256_init(ctx) CC_SHA256_Init(ctx)
|
||||
# define archive_shs256_final(ctx, buf) CC_SHA256_Final(buf, ctx)
|
||||
# define archive_shs256_update(ctx, buf, n) CC_SHA256_Update(ctx, buf, n)
|
||||
#elif defined(ARCHIVE_HASH_SHA256_OPENSSL)
|
||||
# include <openssl/sha.h>
|
||||
# define ARCHIVE_HAS_SHA256
|
||||
typedef SHA256_CTX archive_sha256_ctx;
|
||||
# define archive_sha256_init(ctx) SHA256_Init(ctx)
|
||||
# define archive_sha256_final(ctx, buf) SHA256_Final(buf, ctx)
|
||||
# define archive_sha256_update(ctx, buf, n) SHA256_Update(ctx, buf, n)
|
||||
#elif defined(ARCHIVE_HASH_SHA256_WIN)
|
||||
# define ARCHIVE_HAS_SHA256
|
||||
# define SHA256_DIGEST_LENGTH 32
|
||||
typedef Digest_CTX archive_sha256_ctx;
|
||||
# define archive_sha256_init(ctx) __la_hash_Init(ctx, CALG_SHA_256)
|
||||
# define archive_sha256_final(ctx, buf) __la_hash_Final(buf, SHA256_DIGEST_LENGTH, ctx)
|
||||
# define archive_sha256_update(ctx, buf, n) __la_hash_Update(ctx, buf, n)
|
||||
#endif
|
||||
|
||||
#if defined(ARCHIVE_HASH_SHA384_LIBC)
|
||||
# include <sha2.h>
|
||||
# define ARCHIVE_HAS_SHA384
|
||||
typedef SHA384_CTX archive_sha384_ctx;
|
||||
# define archive_sha384_init(ctx) SHA384_Init(ctx)
|
||||
# define archive_sha384_final(ctx, buf) SHA384_Final(buf, ctx)
|
||||
# define archive_sha384_update(ctx, buf, n) SHA384_Update(ctx, buf, n)
|
||||
#elif defined(ARCHIVE_HASH_SHA384_LIBC2)
|
||||
# include <sha2.h>
|
||||
# define ARCHIVE_HAS_SHA384
|
||||
typedef SHA384_CTX archive_sha384_ctx;
|
||||
# define archive_sha384_init(ctx) SHA384Init(ctx)
|
||||
# define archive_sha384_final(ctx, buf) SHA384Final(buf, ctx)
|
||||
# define archive_sha384_update(ctx, buf, n) SHA384Update(ctx, buf, n)
|
||||
#elif defined(ARCHIVE_HASH_SHA384_LIBC3)
|
||||
# include <sha2.h>
|
||||
# define ARCHIVE_HAS_SHA384
|
||||
typedef SHA2_CTX archive_sha384_ctx;
|
||||
# define archive_sha384_init(ctx) SHA384Init(ctx)
|
||||
# define archive_sha384_final(ctx, buf) SHA384Final(buf, ctx)
|
||||
# define archive_sha384_update(ctx, buf, n) SHA384Update(ctx, buf, n)
|
||||
#elif defined(ARCHIVE_HASH_SHA384_LIBSYSTEM)
|
||||
# include <CommonCrypto/CommonDigest.h>
|
||||
# define ARCHIVE_HAS_SHA384
|
||||
typedef CC_SHA512_CTX archive_shs384_ctx;
|
||||
# define archive_shs384_init(ctx) CC_SHA384_Init(ctx)
|
||||
# define archive_shs384_final(ctx, buf) CC_SHA384_Final(buf, ctx)
|
||||
# define archive_shs384_update(ctx, buf, n) CC_SHA384_Update(ctx, buf, n)
|
||||
#elif defined(ARCHIVE_HASH_SHA384_OPENSSL)
|
||||
# include <openssl/sha.h>
|
||||
# define ARCHIVE_HAS_SHA384
|
||||
typedef SHA512_CTX archive_sha384_ctx;
|
||||
# define archive_sha384_init(ctx) SHA384_Init(ctx)
|
||||
# define archive_sha384_final(ctx, buf) SHA384_Final(buf, ctx)
|
||||
# define archive_sha384_update(ctx, buf, n) SHA384_Update(ctx, buf, n)
|
||||
#elif defined(ARCHIVE_HASH_SHA384_WIN)
|
||||
# define ARCHIVE_HAS_SHA384
|
||||
# define SHA384_DIGEST_LENGTH 48
|
||||
typedef Digest_CTX archive_sha384_ctx;
|
||||
# define archive_sha384_init(ctx) __la_hash_Init(ctx, CALG_SHA_384)
|
||||
# define archive_sha384_final(ctx, buf) __la_hash_Final(buf, SHA384_DIGEST_LENGTH, ctx)
|
||||
# define archive_sha384_update(ctx, buf, n) __la_hash_Update(ctx, buf, n)
|
||||
#endif
|
||||
|
||||
#if defined(ARCHIVE_HASH_SHA512_LIBC)
|
||||
# include <sha2.h>
|
||||
# define ARCHIVE_HAS_SHA512
|
||||
typedef SHA512_CTX archive_sha512_ctx;
|
||||
# define archive_sha512_init(ctx) SHA512_Init(ctx)
|
||||
# define archive_sha512_final(ctx, buf) SHA512_Final(buf, ctx)
|
||||
# define archive_sha512_update(ctx, buf, n) SHA512_Update(ctx, buf, n)
|
||||
#elif defined(ARCHIVE_HASH_SHA512_LIBC2)
|
||||
# include <sha2.h>
|
||||
# define ARCHIVE_HAS_SHA512
|
||||
typedef SHA512_CTX archive_sha512_ctx;
|
||||
# define archive_sha512_init(ctx) SHA512Init(ctx)
|
||||
# define archive_sha512_final(ctx, buf) SHA512Final(buf, ctx)
|
||||
# define archive_sha512_update(ctx, buf, n) SHA512Update(ctx, buf, n)
|
||||
#elif defined(ARCHIVE_HASH_SHA512_LIBC3)
|
||||
# include <sha2.h>
|
||||
# define ARCHIVE_HAS_SHA512
|
||||
typedef SHA2_CTX archive_sha512_ctx;
|
||||
# define archive_sha512_init(ctx) SHA512Init(ctx)
|
||||
# define archive_sha512_final(ctx, buf) SHA512Final(buf, ctx)
|
||||
# define archive_sha512_update(ctx, buf, n) SHA512Update(ctx, buf, n)
|
||||
#elif defined(ARCHIVE_HASH_SHA512_LIBMD)
|
||||
# include <sha512.h>
|
||||
# define ARCHIVE_HAS_SHA512
|
||||
typedef SHA512_CTX archive_sha512_ctx;
|
||||
# define archive_sha512_init(ctx) SHA512_Init(ctx)
|
||||
# define archive_sha512_final(ctx, buf) SHA512_Final(buf, ctx)
|
||||
# define archive_sha512_update(ctx, buf, n) SHA512_Update(ctx, buf, n)
|
||||
#elif defined(ARCHIVE_HASH_SHA512_LIBSYSTEM)
|
||||
# include <CommonCrypto/CommonDigest.h>
|
||||
# define ARCHIVE_HAS_SHA512
|
||||
typedef CC_SHA512_CTX archive_shs512_ctx;
|
||||
# define archive_shs512_init(ctx) CC_SHA512_Init(ctx)
|
||||
# define archive_shs512_final(ctx, buf) CC_SHA512_Final(buf, ctx)
|
||||
# define archive_shs512_update(ctx, buf, n) CC_SHA512_Update(ctx, buf, n)
|
||||
#elif defined(ARCHIVE_HASH_SHA512_OPENSSL)
|
||||
# include <openssl/sha.h>
|
||||
# define ARCHIVE_HAS_SHA512
|
||||
typedef SHA512_CTX archive_sha512_ctx;
|
||||
# define archive_sha512_init(ctx) SHA512_Init(ctx)
|
||||
# define archive_sha512_final(ctx, buf) SHA512_Final(buf, ctx)
|
||||
# define archive_sha512_update(ctx, buf, n) SHA512_Update(ctx, buf, n)
|
||||
#elif defined(ARCHIVE_HASH_SHA512_WIN)
|
||||
# define ARCHIVE_HAS_SHA512
|
||||
# define SHA512_DIGEST_LENGTH 64
|
||||
typedef Digest_CTX archive_sha512_ctx;
|
||||
# define archive_sha512_init(ctx) __la_hash_Init(ctx, CALG_SHA_512)
|
||||
# define archive_sha512_final(ctx, buf) __la_hash_Final(buf, SHA512_DIGEST_LENGTH, ctx)
|
||||
# define archive_sha512_update(ctx, buf, n) __la_hash_Update(ctx, buf, n)
|
||||
#endif
|
@ -1,165 +0,0 @@
|
||||
/*-
|
||||
* 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$
|
||||
*/
|
||||
|
||||
/* !!ONLY FOR USE INTERNALLY TO LIBARCHIVE!! */
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
||||
/* archive.h and archive_entry.h require this. */
|
||||
#define __LIBARCHIVE_BUILD 1
|
||||
|
||||
#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
|
||||
|
||||
/* It should be possible to get rid of this by extending the feature-test
|
||||
* macros to cover Windows API functions, probably along with non-trivial
|
||||
* refactoring of code to find structures that sit more cleanly on top of
|
||||
* either Windows or Posix APIs. */
|
||||
#if (defined(__WIN32__) || defined(_WIN32) || defined(__WIN32)) && !defined(__CYGWIN__)
|
||||
#include "archive_windows.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.
|
||||
*/
|
||||
|
||||
/* Get a real definition for __FBSDID if we can */
|
||||
#if HAVE_SYS_CDEFS_H
|
||||
#include <sys/cdefs.h>
|
||||
#endif
|
||||
|
||||
/* If not, define it so as to avoid dangling semicolons. */
|
||||
#ifndef __FBSDID
|
||||
#define __FBSDID(a) struct _undefined_hack
|
||||
#endif
|
||||
|
||||
/* Try to get standard C99-style integer type definitions. */
|
||||
#if HAVE_INTTYPES_H
|
||||
#include <inttypes.h>
|
||||
#endif
|
||||
#if HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
/* Borland warns about its own constants! */
|
||||
#if defined(__BORLANDC__)
|
||||
# if HAVE_DECL_UINT64_MAX
|
||||
# undef UINT64_MAX
|
||||
# undef HAVE_DECL_UINT64_MAX
|
||||
# endif
|
||||
# if HAVE_DECL_UINT64_MIN
|
||||
# undef UINT64_MIN
|
||||
# undef HAVE_DECL_UINT64_MIN
|
||||
# endif
|
||||
# if HAVE_DECL_INT64_MAX
|
||||
# undef INT64_MAX
|
||||
# undef HAVE_DECL_INT64_MAX
|
||||
# endif
|
||||
# if HAVE_DECL_INT64_MIN
|
||||
# undef INT64_MIN
|
||||
# undef HAVE_DECL_INT64_MIN
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Some platforms lack the standard *_MAX definitions. */
|
||||
#if !HAVE_DECL_SIZE_MAX
|
||||
#define SIZE_MAX (~(size_t)0)
|
||||
#endif
|
||||
#if !HAVE_DECL_SSIZE_MAX
|
||||
#define SSIZE_MAX ((ssize_t)(SIZE_MAX >> 1))
|
||||
#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 <sys/acl.h>, 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
|
||||
|
||||
#endif /* !ARCHIVE_PLATFORM_H_INCLUDED */
|
@ -1,124 +0,0 @@
|
||||
/*-
|
||||
* 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$
|
||||
*/
|
||||
|
||||
#ifndef __LIBARCHIVE_BUILD
|
||||
#error This header is only to be used internally to libarchive.
|
||||
#endif
|
||||
|
||||
#ifndef ARCHIVE_PRIVATE_H_INCLUDED
|
||||
#define ARCHIVE_PRIVATE_H_INCLUDED
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_string.h"
|
||||
|
||||
#if defined(__GNUC__) && (__GNUC__ > 2 || \
|
||||
(__GNUC__ == 2 && __GNUC_MINOR__ >= 5))
|
||||
#define __LA_DEAD __attribute__((__noreturn__))
|
||||
#else
|
||||
#define __LA_DEAD
|
||||
#endif
|
||||
|
||||
#define ARCHIVE_WRITE_MAGIC (0xb0c5c0deU)
|
||||
#define ARCHIVE_READ_MAGIC (0xdeb0c5U)
|
||||
#define ARCHIVE_WRITE_DISK_MAGIC (0xc001b0c5U)
|
||||
#define ARCHIVE_READ_DISK_MAGIC (0xbadb0c5U)
|
||||
|
||||
#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_close)(struct archive *);
|
||||
int (*archive_free)(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. */
|
||||
int64_t file_position;
|
||||
/* Position in COMPRESSED data stream. */
|
||||
int64_t raw_position;
|
||||
/* Number of file entries processed. */
|
||||
int file_count;
|
||||
|
||||
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) __LA_DEAD;
|
||||
|
||||
int __archive_parse_options(const char *p, const char *fn,
|
||||
int keysize, char *key, int valsize, char *val);
|
||||
|
||||
#define err_combine(a,b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
#if defined(__BORLANDC__) || (defined(_MSC_VER) && _MSC_VER <= 1300)
|
||||
# define ARCHIVE_LITERAL_LL(x) x##i64
|
||||
# define ARCHIVE_LITERAL_ULL(x) x##ui64
|
||||
#else
|
||||
# define ARCHIVE_LITERAL_LL(x) x##ll
|
||||
# define ARCHIVE_LITERAL_ULL(x) x##ull
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,714 +0,0 @@
|
||||
.\" 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 13, 2009
|
||||
.Dt ARCHIVE_READ 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm archive_read_new ,
|
||||
.Nm archive_read_set_filter_options ,
|
||||
.Nm archive_read_set_format_options ,
|
||||
.Nm archive_read_set_options ,
|
||||
.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_lzma ,
|
||||
.Nm archive_read_support_compression_none ,
|
||||
.Nm archive_read_support_compression_xz ,
|
||||
.Nm archive_read_support_compression_program ,
|
||||
.Nm archive_read_support_compression_program_signature ,
|
||||
.Nm archive_read_support_format_all ,
|
||||
.Nm archive_read_support_format_ar ,
|
||||
.Nm archive_read_support_format_cpio ,
|
||||
.Nm archive_read_support_format_empty ,
|
||||
.Nm archive_read_support_format_iso9660 ,
|
||||
.Nm archive_read_support_format_mtree,
|
||||
.Nm archive_read_support_format_raw,
|
||||
.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_next_header2 ,
|
||||
.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_extract2 ,
|
||||
.Nm archive_read_extract_set_progress_callback ,
|
||||
.Nm archive_read_close ,
|
||||
.Nm archive_read_free
|
||||
.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_lzma "struct archive *"
|
||||
.Ft int
|
||||
.Fn archive_read_support_compression_none "struct archive *"
|
||||
.Ft int
|
||||
.Fn archive_read_support_compression_xz "struct archive *"
|
||||
.Ft int
|
||||
.Fo archive_read_support_compression_program
|
||||
.Fa "struct archive *"
|
||||
.Fa "const char *cmd"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fo archive_read_support_compression_program_signature
|
||||
.Fa "struct archive *"
|
||||
.Fa "const char *cmd"
|
||||
.Fa "const void *signature"
|
||||
.Fa "size_t signature_length"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fn archive_read_support_format_all "struct archive *"
|
||||
.Ft int
|
||||
.Fn archive_read_support_format_ar "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_mtree "struct archive *"
|
||||
.Ft int
|
||||
.Fn archive_read_support_format_raw "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_set_filter_options "struct archive *" "const char *"
|
||||
.Ft int
|
||||
.Fn archive_read_set_format_options "struct archive *" "const char *"
|
||||
.Ft int
|
||||
.Fn archive_read_set_options "struct archive *" "const char *"
|
||||
.Ft int
|
||||
.Fo archive_read_open
|
||||
.Fa "struct archive *"
|
||||
.Fa "void *client_data"
|
||||
.Fa "archive_open_callback *"
|
||||
.Fa "archive_read_callback *"
|
||||
.Fa "archive_close_callback *"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fo archive_read_open2
|
||||
.Fa "struct archive *"
|
||||
.Fa "void *client_data"
|
||||
.Fa "archive_open_callback *"
|
||||
.Fa "archive_read_callback *"
|
||||
.Fa "archive_skip_callback *"
|
||||
.Fa "archive_close_callback *"
|
||||
.Fc
|
||||
.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
|
||||
.Fo archive_read_open_filename
|
||||
.Fa "struct archive *"
|
||||
.Fa "const char *filename"
|
||||
.Fa "size_t block_size"
|
||||
.Fc
|
||||
.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 int
|
||||
.Fn archive_read_next_header2 "struct archive *" "struct archive_entry *"
|
||||
.Ft ssize_t
|
||||
.Fn archive_read_data "struct archive *" "void *buff" "size_t len"
|
||||
.Ft int
|
||||
.Fo archive_read_data_block
|
||||
.Fa "struct archive *"
|
||||
.Fa "const void **buff"
|
||||
.Fa "size_t *len"
|
||||
.Fa "off_t *offset"
|
||||
.Fc
|
||||
.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
|
||||
.Fo archive_read_extract
|
||||
.Fa "struct archive *"
|
||||
.Fa "struct archive_entry *"
|
||||
.Fa "int flags"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fo archive_read_extract2
|
||||
.Fa "struct archive *src"
|
||||
.Fa "struct archive_entry *"
|
||||
.Fa "struct archive *dest"
|
||||
.Fc
|
||||
.Ft void
|
||||
.Fo archive_read_extract_set_progress_callback
|
||||
.Fa "struct archive *"
|
||||
.Fa "void (*func)(void *)"
|
||||
.Fa "void *user_data"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fn archive_read_close "struct archive *"
|
||||
.Ft int
|
||||
.Fn archive_read_free "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 Xo
|
||||
.Fn archive_read_support_compression_bzip2 ,
|
||||
.Fn archive_read_support_compression_compress ,
|
||||
.Fn archive_read_support_compression_gzip ,
|
||||
.Fn archive_read_support_compression_lzma ,
|
||||
.Fn archive_read_support_compression_none ,
|
||||
.Fn archive_read_support_compression_xz
|
||||
.Xc
|
||||
Enables auto-detection code and decompression support for the
|
||||
specified compression.
|
||||
Returns
|
||||
.Cm ARCHIVE_OK
|
||||
if the compression is fully supported, or
|
||||
.Cm ARCHIVE_WARN
|
||||
if the compression is supported only through an external program.
|
||||
Note that decompression using an external program is usually slower than
|
||||
decompression through built-in libraries.
|
||||
Note that
|
||||
.Dq none
|
||||
is always enabled by default.
|
||||
.It Fn archive_read_support_compression_all
|
||||
Enables all available decompression filters.
|
||||
.It Fn archive_read_support_compression_program
|
||||
Data is fed through the specified external program before being dearchived.
|
||||
Note that this disables automatic detection of the compression format,
|
||||
so it makes no sense to specify this in conjunction with any other
|
||||
decompression option.
|
||||
.It Fn archive_read_support_compression_program_signature
|
||||
This feeds data through the specified external program
|
||||
but only if the initial bytes of the data match the specified
|
||||
signature value.
|
||||
.It Xo
|
||||
.Fn archive_read_support_format_all ,
|
||||
.Fn archive_read_support_format_ar ,
|
||||
.Fn archive_read_support_format_cpio ,
|
||||
.Fn archive_read_support_format_empty ,
|
||||
.Fn archive_read_support_format_iso9660 ,
|
||||
.Fn archive_read_support_format_mtree ,
|
||||
.Fn archive_read_support_format_tar ,
|
||||
.Fn archive_read_support_format_zip
|
||||
.Xc
|
||||
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_support_format_raw
|
||||
The
|
||||
.Dq raw
|
||||
format handler allows libarchive to be used to read arbitrary data.
|
||||
It treats any data stream as an archive with a single entry.
|
||||
The pathname of this entry is
|
||||
.Dq data ;
|
||||
all other entry fields are unset.
|
||||
This is not enabled by
|
||||
.Fn archive_read_support_format_all
|
||||
in order to avoid erroneous handling of damaged archives.
|
||||
.It Xo
|
||||
.Fn archive_read_set_filter_options ,
|
||||
.Fn archive_read_set_format_options ,
|
||||
.Fn archive_read_set_options
|
||||
.Xc
|
||||
Specifies options that will be passed to currently-registered
|
||||
filters (including decompression filters) and/or format readers.
|
||||
The argument is a comma-separated list of individual options.
|
||||
Individual options have one of the following forms:
|
||||
.Bl -tag -compact -width indent
|
||||
.It Ar option=value
|
||||
The option/value pair will be provided to every module.
|
||||
Modules that do not accept an option with this name will ignore it.
|
||||
.It Ar option
|
||||
The option will be provided to every module with a value of
|
||||
.Dq 1 .
|
||||
.It Ar !option
|
||||
The option will be provided to every module with a NULL value.
|
||||
.It Ar module:option=value , Ar module:option , Ar module:!option
|
||||
As above, but the corresponding option and value will be provided
|
||||
only to modules whose name matches
|
||||
.Ar module .
|
||||
.El
|
||||
The return value will be
|
||||
.Cm ARCHIVE_OK
|
||||
if any module accepts the option, or
|
||||
.Cm ARCHIVE_WARN
|
||||
if no module accepted the option, or
|
||||
.Cm ARCHIVE_FATAL
|
||||
if there was a fatal error while attempting to process the option.
|
||||
.Pp
|
||||
The currently supported options are:
|
||||
.Bl -tag -compact -width indent
|
||||
.It Format iso9660
|
||||
.Bl -tag -compact -width indent
|
||||
.It Cm joliet
|
||||
Support Joliet extensions.
|
||||
Defaults to enabled, use
|
||||
.Cm !joliet
|
||||
to disable.
|
||||
.El
|
||||
.El
|
||||
.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 .
|
||||
This is a convenience wrapper around
|
||||
.Fn archive_read_next_header2
|
||||
that reuses an internal
|
||||
.Tn struct archive_entry
|
||||
object for each request.
|
||||
.It Fn archive_read_next_header2
|
||||
Read the header for the next entry and populate the provided
|
||||
.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 archive_write_disk_set_options 3 .
|
||||
.It Fn archive_read_extract2
|
||||
This is another version of
|
||||
.Fn archive_read_extract
|
||||
that allows you to provide your own restore object.
|
||||
In particular, this allows you to override the standard lookup functions
|
||||
using
|
||||
.Xr archive_write_disk_set_group_lookup 3 ,
|
||||
and
|
||||
.Xr archive_write_disk_set_user_lookup 3 .
|
||||
Note that
|
||||
.Fn archive_read_extract2
|
||||
does not accept a
|
||||
.Va flags
|
||||
argument; you should use
|
||||
.Fn archive_write_disk_set_options
|
||||
to set the restore options yourself.
|
||||
.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_free
|
||||
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
|
||||
.Fo archive_read_callback
|
||||
.Fa "struct archive *"
|
||||
.Fa "void *client_data"
|
||||
.Fa "const void **buffer"
|
||||
.Fc
|
||||
.It
|
||||
.\" #if ARCHIVE_API_VERSION < 2
|
||||
.Ft typedef int
|
||||
.Fo archive_skip_callback
|
||||
.Fa "struct archive *"
|
||||
.Fa "void *client_data"
|
||||
.Fa "size_t request"
|
||||
.Fc
|
||||
.\" #else
|
||||
.\" .Ft typedef off_t
|
||||
.\" .Fo archive_skip_callback
|
||||
.\" .Fa "struct archive *"
|
||||
.\" .Fa "void *client_data"
|
||||
.\" .Fa "off_t request"
|
||||
.\" .Fc
|
||||
.\" #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\en",archive_entry_pathname(entry));
|
||||
archive_read_data_skip(a);
|
||||
}
|
||||
archive_read_free(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.
|
File diff suppressed because it is too large
Load Diff
@ -1,93 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#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) {
|
||||
output_offset = lseek(fd,
|
||||
offset - output_offset, SEEK_CUR);
|
||||
if (output_offset != offset) {
|
||||
archive_set_error(a, errno, "Seek error");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
}
|
||||
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 (ARCHIVE_FATAL);
|
||||
}
|
||||
output_offset += bytes_written;
|
||||
total_written += bytes_written;
|
||||
p += bytes_written;
|
||||
size -= bytes_written;
|
||||
}
|
||||
}
|
||||
|
||||
if (r != ARCHIVE_EOF)
|
||||
return (r);
|
||||
return (ARCHIVE_OK);
|
||||
}
|
@ -1,308 +0,0 @@
|
||||
.\" Copyright (c) 2003-2009 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 March 10, 2009
|
||||
.Dt ARCHIVE_READ_DISK 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm archive_read_disk_new ,
|
||||
.Nm archive_read_disk_set_symlink_logical ,
|
||||
.Nm archive_read_disk_set_symlink_physical ,
|
||||
.Nm archive_read_disk_set_symlink_hybrid ,
|
||||
.Nm archive_read_disk_entry_from_file ,
|
||||
.Nm archive_read_disk_gname ,
|
||||
.Nm archive_read_disk_uname ,
|
||||
.Nm archive_read_disk_set_uname_lookup ,
|
||||
.Nm archive_read_disk_set_gname_lookup ,
|
||||
.Nm archive_read_disk_set_standard_lookup ,
|
||||
.Nm archive_read_close ,
|
||||
.Nm archive_read_free
|
||||
.Nd functions for reading objects from disk
|
||||
.Sh SYNOPSIS
|
||||
.In archive.h
|
||||
.Ft struct archive *
|
||||
.Fn archive_read_disk_new "void"
|
||||
.Ft int
|
||||
.Fn archive_read_disk_set_symlink_logical "struct archive *"
|
||||
.Ft int
|
||||
.Fn archive_read_disk_set_symlink_physical "struct archive *"
|
||||
.Ft int
|
||||
.Fn archive_read_disk_set_symlink_hybrid "struct archive *"
|
||||
.Ft int
|
||||
.Fn archive_read_disk_gname "struct archive *" "gid_t"
|
||||
.Ft int
|
||||
.Fn archive_read_disk_uname "struct archive *" "uid_t"
|
||||
.Ft int
|
||||
.Fo archive_read_disk_set_gname_lookup
|
||||
.Fa "struct archive *"
|
||||
.Fa "void *"
|
||||
.Fa "const char *(*lookup)(void *, gid_t)"
|
||||
.Fa "void (*cleanup)(void *)"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fo archive_read_disk_set_uname_lookup
|
||||
.Fa "struct archive *"
|
||||
.Fa "void *"
|
||||
.Fa "const char *(*lookup)(void *, uid_t)"
|
||||
.Fa "void (*cleanup)(void *)"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fn archive_read_disk_set_standard_lookup "struct archive *"
|
||||
.Ft int
|
||||
.Fo archive_read_disk_entry_from_file
|
||||
.Fa "struct archive *"
|
||||
.Fa "struct archive_entry *"
|
||||
.Fa "int fd"
|
||||
.Fa "const struct stat *"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fn archive_read_close "struct archive *"
|
||||
.Ft int
|
||||
.Fn archive_read_free "struct archive *"
|
||||
.Sh DESCRIPTION
|
||||
These functions provide an API for reading information about
|
||||
objects on disk.
|
||||
In particular, they provide an interface for populating
|
||||
.Tn struct archive_entry
|
||||
objects.
|
||||
.Bl -tag -width indent
|
||||
.It Fn archive_read_disk_new
|
||||
Allocates and initializes a
|
||||
.Tn struct archive
|
||||
object suitable for reading object information from disk.
|
||||
.It Xo
|
||||
.Fn archive_read_disk_set_symlink_logical ,
|
||||
.Fn archive_read_disk_set_symlink_physical ,
|
||||
.Fn archive_read_disk_set_symlink_hybrid
|
||||
.Xc
|
||||
This sets the mode used for handling symbolic links.
|
||||
The
|
||||
.Dq logical
|
||||
mode follows all symbolic links.
|
||||
The
|
||||
.Dq physical
|
||||
mode does not follow any symbolic links.
|
||||
The
|
||||
.Dq hybrid
|
||||
mode currently behaves identically to the
|
||||
.Dq logical
|
||||
mode.
|
||||
.It Xo
|
||||
.Fn archive_read_disk_gname ,
|
||||
.Fn archive_read_disk_uname
|
||||
.Xc
|
||||
Returns a user or group name given a gid or uid value.
|
||||
By default, these always return a NULL string.
|
||||
.It Xo
|
||||
.Fn archive_read_disk_set_gname_lookup ,
|
||||
.Fn archive_read_disk_set_uname_lookup
|
||||
.Xc
|
||||
These allow you to override the functions used for
|
||||
user and group name lookups.
|
||||
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 or when new lookup functions are registered.
|
||||
.It Fn archive_read_disk_set_standard_lookup
|
||||
This convenience function installs a standard set of user
|
||||
and group name lookup functions.
|
||||
These functions use
|
||||
.Xr getpwuid 3
|
||||
and
|
||||
.Xr getgrgid 3
|
||||
to convert ids to names, defaulting to NULL if the names cannot
|
||||
be looked up.
|
||||
These functions also implement a simple memory cache to reduce
|
||||
the number of calls to
|
||||
.Xr getpwuid 3
|
||||
and
|
||||
.Xr getgrgid 3 .
|
||||
.It Fn archive_read_disk_entry_from_file
|
||||
Populates a
|
||||
.Tn struct archive_entry
|
||||
object with information about a particular file.
|
||||
The
|
||||
.Tn archive_entry
|
||||
object must have already been created with
|
||||
.Xr archive_entry_new 3
|
||||
and at least one of the source path or path fields must already be set.
|
||||
(If both are set, the source path will be used.)
|
||||
.Pp
|
||||
Information is read from disk using the path name from the
|
||||
.Tn struct archive_entry
|
||||
object.
|
||||
If a file descriptor is provided, some information will be obtained using
|
||||
that file descriptor, on platforms that support the appropriate
|
||||
system calls.
|
||||
.Pp
|
||||
If a pointer to a
|
||||
.Tn struct stat
|
||||
is provided, information from that structure will be used instead
|
||||
of reading from the disk where appropriate.
|
||||
This can provide performance benefits in scenarios where
|
||||
.Tn struct stat
|
||||
information has already been read from the disk as a side effect
|
||||
of some other operation.
|
||||
(For example, directory traversal libraries often provide this information.)
|
||||
.Pp
|
||||
Where necessary, user and group ids are converted to user and group names
|
||||
using the currently registered lookup functions above.
|
||||
This affects the file ownership fields and ACL values in the
|
||||
.Tn struct archive_entry
|
||||
object.
|
||||
.It Fn archive_read_close
|
||||
This currently does nothing.
|
||||
.It Fn archive_read_free
|
||||
Invokes
|
||||
.Fn archive_read_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.
|
||||
.Sh EXAMPLE
|
||||
The following illustrates basic usage of the library by
|
||||
showing how to use it to copy an item on disk into an archive.
|
||||
.Bd -literal -offset indent
|
||||
void
|
||||
file_to_archive(struct archive *a, const char *name)
|
||||
{
|
||||
char buff[8192];
|
||||
size_t bytes_read;
|
||||
struct archive *ard;
|
||||
struct archive_entry *entry;
|
||||
int fd;
|
||||
|
||||
ard = archive_read_disk_new();
|
||||
archive_read_disk_set_standard_lookup(ard);
|
||||
entry = archive_entry_new();
|
||||
fd = open(name, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return;
|
||||
archive_entry_copy_sourcepath(entry, name);
|
||||
archive_read_disk_entry_from_file(ard, entry, fd, NULL);
|
||||
archive_write_header(a, entry);
|
||||
while ((bytes_read = read(fd, buff, sizeof(buff))) > 0)
|
||||
archive_write_data(a, buff, bytes_read);
|
||||
archive_write_finish_entry(a);
|
||||
archive_read_free(ard);
|
||||
archive_entry_free(entry);
|
||||
}
|
||||
.Ed
|
||||
.Sh RETURN VALUES
|
||||
Most functions return
|
||||
.Cm ARCHIVE_OK
|
||||
(zero) on success, or one of several negative
|
||||
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
|
||||
.Xr archive_errno 3
|
||||
and
|
||||
.Xr archive_error_string 3
|
||||
functions can be used to retrieve an appropriate error code and a
|
||||
textual error message.
|
||||
(See
|
||||
.Xr archive_util 3
|
||||
for details.)
|
||||
.Pp
|
||||
.Fn archive_read_disk_new
|
||||
returns a pointer to a newly-allocated
|
||||
.Tn struct archive
|
||||
object or NULL if the allocation failed for any reason.
|
||||
.Pp
|
||||
.Fn archive_read_disk_gname
|
||||
and
|
||||
.Fn archive_read_disk_uname
|
||||
return
|
||||
.Tn const char *
|
||||
pointers to the textual name or NULL if the lookup failed for any reason.
|
||||
The returned pointer points to internal storage that
|
||||
may be reused on the next call to either of these functions;
|
||||
callers should copy the string if they need to continue accessing it.
|
||||
.Pp
|
||||
.Sh SEE ALSO
|
||||
.Xr archive_read 3 ,
|
||||
.Xr archive_write 3 ,
|
||||
.Xr archive_write_disk 3 ,
|
||||
.Xr tar 1 ,
|
||||
.Xr libarchive 3
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm libarchive
|
||||
library first appeared in
|
||||
.Fx 5.3 .
|
||||
The
|
||||
.Nm archive_read_disk
|
||||
interface was added to
|
||||
.Nm libarchive 2.6
|
||||
and first appeared in
|
||||
.Fx 8.0 .
|
||||
.Sh AUTHORS
|
||||
.An -nosplit
|
||||
The
|
||||
.Nm libarchive
|
||||
library was written by
|
||||
.An Tim Kientzle Aq kientzle@FreeBSD.org .
|
||||
.Sh BUGS
|
||||
The
|
||||
.Dq standard
|
||||
user name and group name lookup functions are not the defaults because
|
||||
.Xr getgrgid 3
|
||||
and
|
||||
.Xr getpwuid 3
|
||||
are sometimes too large for particular applications.
|
||||
The current design allows the application author to use a more
|
||||
compact implementation when appropriate.
|
||||
.Pp
|
||||
The full list of metadata read from disk by
|
||||
.Fn archive_read_disk_entry_from_file
|
||||
is necessarily system-dependent.
|
||||
.Pp
|
||||
The
|
||||
.Fn archive_read_disk_entry_from_file
|
||||
function reads as much information as it can from disk.
|
||||
Some method should be provided to limit this so that clients who
|
||||
do not need ACLs, for instance, can avoid the extra work needed
|
||||
to look up such information.
|
||||
.Pp
|
||||
This API should provide a set of methods for walking a directory tree.
|
||||
That would make it a direct parallel of the
|
||||
.Xr archive_read 3
|
||||
API.
|
||||
When such methods are implemented, the
|
||||
.Dq hybrid
|
||||
symbolic link mode will make sense.
|
@ -1,198 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2009 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$");
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_string.h"
|
||||
#include "archive_entry.h"
|
||||
#include "archive_private.h"
|
||||
#include "archive_read_disk_private.h"
|
||||
|
||||
static int _archive_read_free(struct archive *);
|
||||
static int _archive_read_close(struct archive *);
|
||||
static const char *trivial_lookup_gname(void *, gid_t gid);
|
||||
static const char *trivial_lookup_uname(void *, uid_t uid);
|
||||
|
||||
static struct archive_vtable *
|
||||
archive_read_disk_vtable(void)
|
||||
{
|
||||
static struct archive_vtable av;
|
||||
static int inited = 0;
|
||||
|
||||
if (!inited) {
|
||||
av.archive_free = _archive_read_free;
|
||||
av.archive_close = _archive_read_close;
|
||||
}
|
||||
return (&av);
|
||||
}
|
||||
|
||||
const char *
|
||||
archive_read_disk_gname(struct archive *_a, gid_t gid)
|
||||
{
|
||||
struct archive_read_disk *a = (struct archive_read_disk *)_a;
|
||||
if (a->lookup_gname != NULL)
|
||||
return ((*a->lookup_gname)(a->lookup_gname_data, gid));
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
const char *
|
||||
archive_read_disk_uname(struct archive *_a, uid_t uid)
|
||||
{
|
||||
struct archive_read_disk *a = (struct archive_read_disk *)_a;
|
||||
if (a->lookup_uname != NULL)
|
||||
return ((*a->lookup_uname)(a->lookup_uname_data, uid));
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
int
|
||||
archive_read_disk_set_gname_lookup(struct archive *_a,
|
||||
void *private_data,
|
||||
const char * (*lookup_gname)(void *private, gid_t gid),
|
||||
void (*cleanup_gname)(void *private))
|
||||
{
|
||||
struct archive_read_disk *a = (struct archive_read_disk *)_a;
|
||||
__archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC,
|
||||
ARCHIVE_STATE_ANY, "archive_read_disk_set_gname_lookup");
|
||||
|
||||
if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL)
|
||||
(a->cleanup_gname)(a->lookup_gname_data);
|
||||
|
||||
a->lookup_gname = lookup_gname;
|
||||
a->cleanup_gname = cleanup_gname;
|
||||
a->lookup_gname_data = private_data;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
int
|
||||
archive_read_disk_set_uname_lookup(struct archive *_a,
|
||||
void *private_data,
|
||||
const char * (*lookup_uname)(void *private, uid_t uid),
|
||||
void (*cleanup_uname)(void *private))
|
||||
{
|
||||
struct archive_read_disk *a = (struct archive_read_disk *)_a;
|
||||
__archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC,
|
||||
ARCHIVE_STATE_ANY, "archive_read_disk_set_uname_lookup");
|
||||
|
||||
if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL)
|
||||
(a->cleanup_uname)(a->lookup_uname_data);
|
||||
|
||||
a->lookup_uname = lookup_uname;
|
||||
a->cleanup_uname = cleanup_uname;
|
||||
a->lookup_uname_data = private_data;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new archive_read_disk object and initialize it with global state.
|
||||
*/
|
||||
struct archive *
|
||||
archive_read_disk_new(void)
|
||||
{
|
||||
struct archive_read_disk *a;
|
||||
|
||||
a = (struct archive_read_disk *)malloc(sizeof(*a));
|
||||
if (a == NULL)
|
||||
return (NULL);
|
||||
memset(a, 0, sizeof(*a));
|
||||
a->archive.magic = ARCHIVE_READ_DISK_MAGIC;
|
||||
/* We're ready to write a header immediately. */
|
||||
a->archive.state = ARCHIVE_STATE_HEADER;
|
||||
a->archive.vtable = archive_read_disk_vtable();
|
||||
a->lookup_uname = trivial_lookup_uname;
|
||||
a->lookup_gname = trivial_lookup_gname;
|
||||
return (&a->archive);
|
||||
}
|
||||
|
||||
static int
|
||||
_archive_read_free(struct archive *_a)
|
||||
{
|
||||
struct archive_read_disk *a = (struct archive_read_disk *)_a;
|
||||
|
||||
if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL)
|
||||
(a->cleanup_gname)(a->lookup_gname_data);
|
||||
if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL)
|
||||
(a->cleanup_uname)(a->lookup_uname_data);
|
||||
archive_string_free(&a->archive.error_string);
|
||||
free(a);
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
_archive_read_close(struct archive *_a)
|
||||
{
|
||||
(void)_a; /* UNUSED */
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
int
|
||||
archive_read_disk_set_symlink_logical(struct archive *_a)
|
||||
{
|
||||
struct archive_read_disk *a = (struct archive_read_disk *)_a;
|
||||
a->symlink_mode = 'L';
|
||||
a->follow_symlinks = 1;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
int
|
||||
archive_read_disk_set_symlink_physical(struct archive *_a)
|
||||
{
|
||||
struct archive_read_disk *a = (struct archive_read_disk *)_a;
|
||||
a->symlink_mode = 'P';
|
||||
a->follow_symlinks = 0;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
int
|
||||
archive_read_disk_set_symlink_hybrid(struct archive *_a)
|
||||
{
|
||||
struct archive_read_disk *a = (struct archive_read_disk *)_a;
|
||||
a->symlink_mode = 'H';
|
||||
a->follow_symlinks = 1; /* Follow symlinks initially. */
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Trivial implementations of gname/uname lookup functions.
|
||||
* These are normally overridden by the client, but these stub
|
||||
* versions ensure that we always have something that works.
|
||||
*/
|
||||
static const char *
|
||||
trivial_lookup_gname(void *private_data, gid_t gid)
|
||||
{
|
||||
(void)private_data; /* UNUSED */
|
||||
(void)gid; /* UNUSED */
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static const char *
|
||||
trivial_lookup_uname(void *private_data, uid_t uid)
|
||||
{
|
||||
(void)private_data; /* UNUSED */
|
||||
(void)uid; /* UNUSED */
|
||||
return (NULL);
|
||||
}
|
@ -1,570 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2009 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$");
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
/* Mac OSX requires sys/types.h before sys/acl.h. */
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_ACL_H
|
||||
#include <sys/acl.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_EXTATTR_H
|
||||
#include <sys/extattr.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_PARAM_H
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_XATTR_H
|
||||
#include <sys/xattr.h>
|
||||
#endif
|
||||
#ifdef HAVE_ACL_LIBACL_H
|
||||
#include <acl/libacl.h>
|
||||
#endif
|
||||
#ifdef HAVE_ATTR_XATTR_H
|
||||
#include <attr/xattr.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_LIMITS_H
|
||||
#include <limits.h>
|
||||
#endif
|
||||
#ifdef HAVE_WINDOWS_H
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_entry.h"
|
||||
#include "archive_private.h"
|
||||
#include "archive_read_disk_private.h"
|
||||
|
||||
/*
|
||||
* Linux and FreeBSD plug this obvious hole in POSIX.1e in
|
||||
* different ways.
|
||||
*/
|
||||
#if HAVE_ACL_GET_PERM
|
||||
#define ACL_GET_PERM acl_get_perm
|
||||
#elif HAVE_ACL_GET_PERM_NP
|
||||
#define ACL_GET_PERM acl_get_perm_np
|
||||
#endif
|
||||
|
||||
static int setup_acls_posix1e(struct archive_read_disk *,
|
||||
struct archive_entry *, int fd);
|
||||
static int setup_xattrs(struct archive_read_disk *,
|
||||
struct archive_entry *, int fd);
|
||||
|
||||
int
|
||||
archive_read_disk_entry_from_file(struct archive *_a,
|
||||
struct archive_entry *entry,
|
||||
int fd, const struct stat *st)
|
||||
{
|
||||
struct archive_read_disk *a = (struct archive_read_disk *)_a;
|
||||
const char *path, *name;
|
||||
struct stat s;
|
||||
int initial_fd = fd;
|
||||
int r, r1;
|
||||
|
||||
archive_clear_error(_a);
|
||||
path = archive_entry_sourcepath(entry);
|
||||
if (path == NULL)
|
||||
path = archive_entry_pathname(entry);
|
||||
|
||||
#ifdef EXT2_IOC_GETFLAGS
|
||||
/* Linux requires an extra ioctl to pull the flags. Although
|
||||
* this is an extra step, it has a nice side-effect: We get an
|
||||
* open file descriptor which we can use in the subsequent lookups. */
|
||||
if ((S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) {
|
||||
if (fd < 0)
|
||||
fd = open(pathname, O_RDONLY | O_NONBLOCK | O_BINARY);
|
||||
if (fd >= 0) {
|
||||
unsigned long stflags;
|
||||
int r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags);
|
||||
if (r == 0 && stflags != 0)
|
||||
archive_entry_set_fflags(entry, stflags, 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (st == NULL) {
|
||||
/* TODO: On Windows, use GetFileInfoByHandle() here.
|
||||
* Using Windows stat() call is badly broken, but
|
||||
* even the stat() wrapper has problems because
|
||||
* 'struct stat' is broken on Windows.
|
||||
*/
|
||||
#if HAVE_FSTAT
|
||||
if (fd >= 0) {
|
||||
if (fstat(fd, &s) != 0) {
|
||||
archive_set_error(&a->archive, errno,
|
||||
"Can't fstat");
|
||||
return (ARCHIVE_FAILED);
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
#if HAVE_LSTAT
|
||||
if (!a->follow_symlinks) {
|
||||
if (lstat(path, &s) != 0) {
|
||||
archive_set_error(&a->archive, errno,
|
||||
"Can't lstat %s", path);
|
||||
return (ARCHIVE_FAILED);
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
if (stat(path, &s) != 0) {
|
||||
archive_set_error(&a->archive, errno,
|
||||
"Can't stat %s", path);
|
||||
return (ARCHIVE_FAILED);
|
||||
}
|
||||
st = &s;
|
||||
}
|
||||
archive_entry_copy_stat(entry, st);
|
||||
|
||||
/* Lookup uname/gname */
|
||||
name = archive_read_disk_uname(_a, archive_entry_uid(entry));
|
||||
if (name != NULL)
|
||||
archive_entry_copy_uname(entry, name);
|
||||
name = archive_read_disk_gname(_a, archive_entry_gid(entry));
|
||||
if (name != NULL)
|
||||
archive_entry_copy_gname(entry, name);
|
||||
|
||||
#ifdef HAVE_STRUCT_STAT_ST_FLAGS
|
||||
/* On FreeBSD, we get flags for free with the stat. */
|
||||
/* TODO: Does this belong in copy_stat()? */
|
||||
if (st->st_flags != 0)
|
||||
archive_entry_set_fflags(entry, st->st_flags, 0);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_READLINK
|
||||
if (S_ISLNK(st->st_mode)) {
|
||||
char linkbuffer[PATH_MAX + 1];
|
||||
int lnklen = readlink(path, linkbuffer, PATH_MAX);
|
||||
if (lnklen < 0) {
|
||||
archive_set_error(&a->archive, errno,
|
||||
"Couldn't read link data");
|
||||
return (ARCHIVE_FAILED);
|
||||
}
|
||||
linkbuffer[lnklen] = 0;
|
||||
archive_entry_set_symlink(entry, linkbuffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
r = setup_acls_posix1e(a, entry, fd);
|
||||
r1 = setup_xattrs(a, entry, fd);
|
||||
if (r1 < r)
|
||||
r = r1;
|
||||
/* If we opened the file earlier in this function, close it. */
|
||||
if (initial_fd != fd)
|
||||
close(fd);
|
||||
return (r);
|
||||
}
|
||||
|
||||
#ifdef HAVE_POSIX_ACL
|
||||
static void setup_acl_posix1e(struct archive_read_disk *a,
|
||||
struct archive_entry *entry, acl_t acl, int archive_entry_acl_type);
|
||||
|
||||
static int
|
||||
setup_acls_posix1e(struct archive_read_disk *a,
|
||||
struct archive_entry *entry, int fd)
|
||||
{
|
||||
const char *accpath;
|
||||
acl_t acl;
|
||||
|
||||
accpath = archive_entry_sourcepath(entry);
|
||||
if (accpath == NULL)
|
||||
accpath = archive_entry_pathname(entry);
|
||||
|
||||
archive_entry_acl_clear(entry);
|
||||
|
||||
/* Retrieve access ACL from file. */
|
||||
if (fd >= 0)
|
||||
acl = acl_get_fd(fd);
|
||||
#if HAVE_ACL_GET_LINK_NP
|
||||
else if (!a->follow_symlinks)
|
||||
acl = acl_get_link_np(accpath, ACL_TYPE_ACCESS);
|
||||
#else
|
||||
else if ((!a->follow_symlinks)
|
||||
&& (archive_entry_filetype(entry) == AE_IFLNK))
|
||||
/* We can't get the ACL of a symlink, so we assume it can't
|
||||
have one. */
|
||||
acl = NULL;
|
||||
#endif
|
||||
else
|
||||
acl = acl_get_file(accpath, ACL_TYPE_ACCESS);
|
||||
if (acl != NULL) {
|
||||
setup_acl_posix1e(a, entry, acl,
|
||||
ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
|
||||
acl_free(acl);
|
||||
}
|
||||
|
||||
/* Only directories can have default ACLs. */
|
||||
if (S_ISDIR(archive_entry_mode(entry))) {
|
||||
acl = acl_get_file(accpath, ACL_TYPE_DEFAULT);
|
||||
if (acl != NULL) {
|
||||
setup_acl_posix1e(a, entry, acl,
|
||||
ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);
|
||||
acl_free(acl);
|
||||
}
|
||||
}
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Translate POSIX.1e ACL into libarchive internal structure.
|
||||
*/
|
||||
static void
|
||||
setup_acl_posix1e(struct archive_read_disk *a,
|
||||
struct archive_entry *entry, acl_t acl, int archive_entry_acl_type)
|
||||
{
|
||||
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;
|
||||
|
||||
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 = archive_read_disk_uname(&a->archive, 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 = archive_read_disk_gname(&a->archive, 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 above.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
#else
|
||||
static int
|
||||
setup_acls_posix1e(struct archive_read_disk *a,
|
||||
struct archive_entry *entry, int fd)
|
||||
{
|
||||
(void)a; /* UNUSED */
|
||||
(void)entry; /* UNUSED */
|
||||
(void)fd; /* UNUSED */
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAVE_LISTXATTR && HAVE_LLISTXATTR && HAVE_GETXATTR && HAVE_LGETXATTR
|
||||
|
||||
/*
|
||||
* Linux extended attribute support.
|
||||
*
|
||||
* TODO: By using a stack-allocated buffer for the first
|
||||
* call to getxattr(), we might be able to avoid the second
|
||||
* call entirely. We only need the second call if the
|
||||
* stack-allocated buffer is too small. But a modest buffer
|
||||
* of 1024 bytes or so will often be big enough. Same applies
|
||||
* to listxattr().
|
||||
*/
|
||||
|
||||
|
||||
static int
|
||||
setup_xattr(struct archive_read_disk *a,
|
||||
struct archive_entry *entry, const char *name, int fd)
|
||||
{
|
||||
ssize_t size;
|
||||
void *value = NULL;
|
||||
const char *accpath;
|
||||
|
||||
(void)fd; /* UNUSED */
|
||||
|
||||
accpath = archive_entry_sourcepath(entry);
|
||||
if (accpath == NULL)
|
||||
accpath = archive_entry_pathname(entry);
|
||||
|
||||
if (!a->follow_symlinks)
|
||||
size = lgetxattr(accpath, name, NULL, 0);
|
||||
else
|
||||
size = getxattr(accpath, name, NULL, 0);
|
||||
|
||||
if (size == -1) {
|
||||
archive_set_error(&a->archive, errno,
|
||||
"Couldn't query extended attribute");
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
|
||||
if (size > 0 && (value = malloc(size)) == NULL) {
|
||||
archive_set_error(&a->archive, errno, "Out of memory");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
if (!a->follow_symlinks)
|
||||
size = lgetxattr(accpath, name, value, size);
|
||||
else
|
||||
size = getxattr(accpath, name, value, size);
|
||||
|
||||
if (size == -1) {
|
||||
archive_set_error(&a->archive, errno,
|
||||
"Couldn't read extended attribute");
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
|
||||
archive_entry_xattr_add_entry(entry, name, value, size);
|
||||
|
||||
free(value);
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
setup_xattrs(struct archive_read_disk *a,
|
||||
struct archive_entry *entry, int fd)
|
||||
{
|
||||
char *list, *p;
|
||||
const char *path;
|
||||
ssize_t list_size;
|
||||
|
||||
|
||||
path = archive_entry_sourcepath(entry);
|
||||
if (path == NULL)
|
||||
path = archive_entry_pathname(entry);
|
||||
|
||||
if (!a->follow_symlinks)
|
||||
list_size = llistxattr(path, NULL, 0);
|
||||
else
|
||||
list_size = listxattr(path, NULL, 0);
|
||||
|
||||
if (list_size == -1) {
|
||||
if (errno == ENOTSUP)
|
||||
return (ARCHIVE_OK);
|
||||
archive_set_error(&a->archive, errno,
|
||||
"Couldn't list extended attributes");
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
|
||||
if (list_size == 0)
|
||||
return (ARCHIVE_OK);
|
||||
|
||||
if ((list = malloc(list_size)) == NULL) {
|
||||
archive_set_error(&a->archive, errno, "Out of memory");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
if (!a->follow_symlinks)
|
||||
list_size = llistxattr(path, list, list_size);
|
||||
else
|
||||
list_size = listxattr(path, list, list_size);
|
||||
|
||||
if (list_size == -1) {
|
||||
archive_set_error(&a->archive, errno,
|
||||
"Couldn't retrieve extended attributes");
|
||||
free(list);
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
|
||||
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(a, entry, p, fd);
|
||||
}
|
||||
|
||||
free(list);
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
#elif HAVE_EXTATTR_GET_FILE && HAVE_EXTATTR_LIST_FILE && \
|
||||
HAVE_DECL_EXTATTR_NAMESPACE_USER
|
||||
|
||||
/*
|
||||
* FreeBSD extattr interface.
|
||||
*/
|
||||
|
||||
/* TODO: Implement this. Follow the Linux model above, but
|
||||
* with FreeBSD-specific system calls, of course. Be careful
|
||||
* to not include the system extattrs that hold ACLs; we handle
|
||||
* those separately.
|
||||
*/
|
||||
static int
|
||||
setup_xattr(struct archive_read_disk *a, struct archive_entry *entry,
|
||||
int namespace, const char *name, const char *fullname, int fd);
|
||||
|
||||
static int
|
||||
setup_xattr(struct archive_read_disk *a, struct archive_entry *entry,
|
||||
int namespace, const char *name, const char *fullname, int fd)
|
||||
{
|
||||
ssize_t size;
|
||||
void *value = NULL;
|
||||
const char *accpath;
|
||||
|
||||
(void)fd; /* UNUSED */
|
||||
|
||||
accpath = archive_entry_sourcepath(entry);
|
||||
if (accpath == NULL)
|
||||
accpath = archive_entry_pathname(entry);
|
||||
|
||||
if (!a->follow_symlinks)
|
||||
size = extattr_get_link(accpath, namespace, name, NULL, 0);
|
||||
else
|
||||
size = extattr_get_file(accpath, namespace, name, NULL, 0);
|
||||
|
||||
if (size == -1) {
|
||||
archive_set_error(&a->archive, errno,
|
||||
"Couldn't query extended attribute");
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
|
||||
if (size > 0 && (value = malloc(size)) == NULL) {
|
||||
archive_set_error(&a->archive, errno, "Out of memory");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
if (!a->follow_symlinks)
|
||||
size = extattr_get_link(accpath, namespace, name, value, size);
|
||||
else
|
||||
size = extattr_get_file(accpath, namespace, name, value, size);
|
||||
|
||||
if (size == -1) {
|
||||
archive_set_error(&a->archive, errno,
|
||||
"Couldn't read extended attribute");
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
|
||||
archive_entry_xattr_add_entry(entry, fullname, value, size);
|
||||
|
||||
free(value);
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
setup_xattrs(struct archive_read_disk *a,
|
||||
struct archive_entry *entry, int fd)
|
||||
{
|
||||
char buff[512];
|
||||
char *list, *p;
|
||||
ssize_t list_size;
|
||||
const char *path;
|
||||
int namespace = EXTATTR_NAMESPACE_USER;
|
||||
|
||||
path = archive_entry_sourcepath(entry);
|
||||
if (path == NULL)
|
||||
path = archive_entry_pathname(entry);
|
||||
|
||||
if (!a->follow_symlinks)
|
||||
list_size = extattr_list_link(path, namespace, NULL, 0);
|
||||
else
|
||||
list_size = extattr_list_file(path, namespace, NULL, 0);
|
||||
|
||||
if (list_size == -1 && errno == EOPNOTSUPP)
|
||||
return (ARCHIVE_OK);
|
||||
if (list_size == -1) {
|
||||
archive_set_error(&a->archive, errno,
|
||||
"Couldn't list extended attributes");
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
|
||||
if (list_size == 0)
|
||||
return (ARCHIVE_OK);
|
||||
|
||||
if ((list = malloc(list_size)) == NULL) {
|
||||
archive_set_error(&a->archive, errno, "Out of memory");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
if (!a->follow_symlinks)
|
||||
list_size = extattr_list_link(path, namespace, list, list_size);
|
||||
else
|
||||
list_size = extattr_list_file(path, namespace, list, list_size);
|
||||
|
||||
if (list_size == -1) {
|
||||
archive_set_error(&a->archive, errno,
|
||||
"Couldn't retrieve extended attributes");
|
||||
free(list);
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
|
||||
p = list;
|
||||
while ((p - list) < list_size) {
|
||||
size_t len = 255 & (int)*p;
|
||||
char *name;
|
||||
|
||||
strcpy(buff, "user.");
|
||||
name = buff + strlen(buff);
|
||||
memcpy(name, p + 1, len);
|
||||
name[len] = '\0';
|
||||
setup_xattr(a, entry, namespace, name, buff, fd);
|
||||
p += 1 + len;
|
||||
}
|
||||
|
||||
free(list);
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Generic (stub) extended attribute support.
|
||||
*/
|
||||
static int
|
||||
setup_xattrs(struct archive_read_disk *a,
|
||||
struct archive_entry *entry, int fd)
|
||||
{
|
||||
(void)a; /* UNUSED */
|
||||
(void)entry; /* UNUSED */
|
||||
(void)fd; /* UNUSED */
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
#endif
|
@ -1,62 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2009 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$
|
||||
*/
|
||||
|
||||
#ifndef __LIBARCHIVE_BUILD
|
||||
#error This header is only to be used internally to libarchive.
|
||||
#endif
|
||||
|
||||
#ifndef ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED
|
||||
#define ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED
|
||||
|
||||
struct archive_read_disk {
|
||||
struct archive archive;
|
||||
|
||||
/*
|
||||
* Symlink mode is one of 'L'ogical, 'P'hysical, or 'H'ybrid,
|
||||
* following an old BSD convention. 'L' follows all symlinks,
|
||||
* 'P' follows none, 'H' follows symlinks only for the first
|
||||
* item.
|
||||
*/
|
||||
char symlink_mode;
|
||||
|
||||
/*
|
||||
* Since symlink interaction changes, we need to track whether
|
||||
* we're following symlinks for the current item. 'L' mode above
|
||||
* sets this true, 'P' sets it false, 'H' changes it as we traverse.
|
||||
*/
|
||||
char follow_symlinks; /* Either 'L' or 'P'. */
|
||||
|
||||
const char * (*lookup_gname)(void *private, gid_t gid);
|
||||
void (*cleanup_gname)(void *private);
|
||||
void *lookup_gname_data;
|
||||
const char * (*lookup_uname)(void *private, gid_t gid);
|
||||
void (*cleanup_uname)(void *private);
|
||||
void *lookup_uname_data;
|
||||
};
|
||||
|
||||
#endif
|
@ -1,303 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_GRP_H
|
||||
#include <grp.h>
|
||||
#endif
|
||||
#ifdef HAVE_PWD_H
|
||||
#include <pwd.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
int
|
||||
archive_read_disk_set_standard_lookup(struct archive *a)
|
||||
{
|
||||
archive_set_error(a, -1, "Standard lookups not available on Windows");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
#else /* ! (_WIN32 && !__CYGWIN__) */
|
||||
#define name_cache_size 127
|
||||
|
||||
static const char * const NO_NAME = "(noname)";
|
||||
|
||||
struct name_cache {
|
||||
struct archive *archive;
|
||||
char *buff;
|
||||
size_t buff_size;
|
||||
int probes;
|
||||
int hits;
|
||||
size_t size;
|
||||
struct {
|
||||
id_t id;
|
||||
const char *name;
|
||||
} cache[name_cache_size];
|
||||
};
|
||||
|
||||
static const char * lookup_gname(void *, gid_t);
|
||||
static const char * lookup_uname(void *, uid_t);
|
||||
static void cleanup(void *);
|
||||
static const char * lookup_gname_helper(struct name_cache *, id_t gid);
|
||||
static const char * lookup_uname_helper(struct name_cache *, id_t uid);
|
||||
|
||||
/*
|
||||
* Installs functions that use getpwuid()/getgrgid()---along with
|
||||
* a simple cache to accelerate such lookups---into the archive_read_disk
|
||||
* object. This is in a separate file because getpwuid()/getgrgid()
|
||||
* 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_read_disk.c) that just
|
||||
* use the uid/gid without the lookup. Or define your own custom functions
|
||||
* if you prefer.
|
||||
*/
|
||||
int
|
||||
archive_read_disk_set_standard_lookup(struct archive *a)
|
||||
{
|
||||
struct name_cache *ucache = malloc(sizeof(struct name_cache));
|
||||
struct name_cache *gcache = malloc(sizeof(struct name_cache));
|
||||
|
||||
if (ucache == NULL || gcache == NULL) {
|
||||
archive_set_error(a, ENOMEM,
|
||||
"Can't allocate uname/gname lookup cache");
|
||||
free(ucache);
|
||||
free(gcache);
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
memset(ucache, 0, sizeof(*ucache));
|
||||
ucache->archive = a;
|
||||
ucache->size = name_cache_size;
|
||||
memset(gcache, 0, sizeof(*gcache));
|
||||
gcache->archive = a;
|
||||
gcache->size = name_cache_size;
|
||||
|
||||
archive_read_disk_set_gname_lookup(a, gcache, lookup_gname, cleanup);
|
||||
archive_read_disk_set_uname_lookup(a, ucache, lookup_uname, cleanup);
|
||||
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static void
|
||||
cleanup(void *data)
|
||||
{
|
||||
struct name_cache *cache = (struct name_cache *)data;
|
||||
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->buff);
|
||||
free(cache);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Lookup uid/gid from uname/gname, return NULL if no match.
|
||||
*/
|
||||
static const char *
|
||||
lookup_name(struct name_cache *cache,
|
||||
const char * (*lookup_fn)(struct name_cache *, id_t), id_t id)
|
||||
{
|
||||
const char *name;
|
||||
int slot;
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
name = (lookup_fn)(cache, id);
|
||||
if (name == NULL) {
|
||||
/* Cache and return the negative response. */
|
||||
cache->cache[slot].name = NO_NAME;
|
||||
cache->cache[slot].id = id;
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
cache->cache[slot].name = name;
|
||||
cache->cache[slot].id = id;
|
||||
return (cache->cache[slot].name);
|
||||
}
|
||||
|
||||
static const char *
|
||||
lookup_uname(void *data, uid_t uid)
|
||||
{
|
||||
struct name_cache *uname_cache = (struct name_cache *)data;
|
||||
return (lookup_name(uname_cache,
|
||||
&lookup_uname_helper, (id_t)uid));
|
||||
}
|
||||
|
||||
#if HAVE_GETPWUID_R
|
||||
static const char *
|
||||
lookup_uname_helper(struct name_cache *cache, id_t id)
|
||||
{
|
||||
struct passwd pwent, *result;
|
||||
int r;
|
||||
|
||||
if (cache->buff_size == 0) {
|
||||
cache->buff_size = 256;
|
||||
cache->buff = malloc(cache->buff_size);
|
||||
}
|
||||
if (cache->buff == NULL)
|
||||
return (NULL);
|
||||
for (;;) {
|
||||
result = &pwent; /* Old getpwuid_r ignores last arg. */
|
||||
r = getpwuid_r((uid_t)id, &pwent,
|
||||
cache->buff, cache->buff_size, &result);
|
||||
if (r == 0)
|
||||
break;
|
||||
if (r != ERANGE)
|
||||
break;
|
||||
/* ERANGE means our buffer was too small, but POSIX
|
||||
* doesn't tell us how big the buffer should be, so
|
||||
* we just double it and try again. Because the buffer
|
||||
* is kept around in the cache object, we shouldn't
|
||||
* have to do this very often. */
|
||||
cache->buff_size *= 2;
|
||||
cache->buff = realloc(cache->buff, cache->buff_size);
|
||||
if (cache->buff == NULL)
|
||||
break;
|
||||
}
|
||||
if (r != 0) {
|
||||
archive_set_error(cache->archive, errno,
|
||||
"Can't lookup user for id %d", (int)id);
|
||||
return (NULL);
|
||||
}
|
||||
if (result == NULL)
|
||||
return (NULL);
|
||||
|
||||
return strdup(result->pw_name);
|
||||
}
|
||||
#else
|
||||
static const char *
|
||||
lookup_uname_helper(struct name_cache *cache, id_t id)
|
||||
{
|
||||
struct passwd *result;
|
||||
|
||||
result = getpwuid((uid_t)id);
|
||||
|
||||
if (result == NULL)
|
||||
return (NULL);
|
||||
|
||||
return strdup(result->pw_name);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const char *
|
||||
lookup_gname(void *data, gid_t gid)
|
||||
{
|
||||
struct name_cache *gname_cache = (struct name_cache *)data;
|
||||
return (lookup_name(gname_cache,
|
||||
&lookup_gname_helper, (id_t)gid));
|
||||
}
|
||||
|
||||
#if HAVE_GETGRGID_R
|
||||
static const char *
|
||||
lookup_gname_helper(struct name_cache *cache, id_t id)
|
||||
{
|
||||
struct group grent, *result;
|
||||
int r;
|
||||
|
||||
if (cache->buff_size == 0) {
|
||||
cache->buff_size = 256;
|
||||
cache->buff = malloc(cache->buff_size);
|
||||
}
|
||||
if (cache->buff == NULL)
|
||||
return (NULL);
|
||||
for (;;) {
|
||||
result = &grent; /* Old getgrgid_r ignores last arg. */
|
||||
r = getgrgid_r((gid_t)id, &grent,
|
||||
cache->buff, cache->buff_size, &result);
|
||||
if (r == 0)
|
||||
break;
|
||||
if (r != ERANGE)
|
||||
break;
|
||||
/* ERANGE means our buffer was too small, but POSIX
|
||||
* doesn't tell us how big the buffer should be, so
|
||||
* we just double it and try again. */
|
||||
cache->buff_size *= 2;
|
||||
cache->buff = realloc(cache->buff, cache->buff_size);
|
||||
if (cache->buff == NULL)
|
||||
break;
|
||||
}
|
||||
if (r != 0) {
|
||||
archive_set_error(cache->archive, errno,
|
||||
"Can't lookup group for id %d", (int)id);
|
||||
return (NULL);
|
||||
}
|
||||
if (result == NULL)
|
||||
return (NULL);
|
||||
|
||||
return strdup(result->gr_name);
|
||||
}
|
||||
#else
|
||||
static const char *
|
||||
lookup_gname_helper(struct name_cache *cache, id_t id)
|
||||
{
|
||||
struct group *result;
|
||||
|
||||
result = getgrgid((gid_t)id);
|
||||
|
||||
if (result == NULL)
|
||||
return (NULL);
|
||||
|
||||
return strdup(result->gr_name);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ! (_WIN32 && !__CYGWIN__) */
|
@ -1,180 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_entry.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);
|
||||
}
|
||||
memset(a->extract, 0, sizeof(*a->extract));
|
||||
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 extract *extract;
|
||||
|
||||
extract = get_extract((struct archive_read *)_a);
|
||||
if (extract == NULL)
|
||||
return (ARCHIVE_FATAL);
|
||||
archive_write_disk_set_options(extract->ad, flags);
|
||||
return (archive_read_extract2(_a, entry, extract->ad));
|
||||
}
|
||||
|
||||
int
|
||||
archive_read_extract2(struct archive *_a, struct archive_entry *entry,
|
||||
struct archive *ad)
|
||||
{
|
||||
struct archive_read *a = (struct archive_read *)_a;
|
||||
int r, r2;
|
||||
|
||||
/* Set up for this particular entry. */
|
||||
archive_write_disk_set_skip_file(ad,
|
||||
a->skip_file_dev, a->skip_file_ino);
|
||||
r = archive_write_header(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, ad);
|
||||
else if (!archive_entry_size_is_set(entry) || archive_entry_size(entry) > 0)
|
||||
/* Otherwise, pour data into the entry. */
|
||||
r = copy_data(_a, ad);
|
||||
r2 = archive_write_finish_entry(ad);
|
||||
if (r2 < ARCHIVE_WARN)
|
||||
r2 = ARCHIVE_WARN;
|
||||
/* Use the first message. */
|
||||
if (r2 != ARCHIVE_OK && r == ARCHIVE_OK)
|
||||
archive_copy_error(&a->archive, 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)
|
||||
{
|
||||
off_t offset;
|
||||
const void *buff;
|
||||
struct extract *extract;
|
||||
size_t size;
|
||||
int r;
|
||||
|
||||
extract = get_extract((struct archive_read *)ar);
|
||||
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);
|
||||
}
|
||||
if (extract->extract_progress)
|
||||
(extract->extract_progress)
|
||||
(extract->extract_progress_user_data);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Cleanup function for archive_extract.
|
||||
*/
|
||||
static int
|
||||
archive_read_extract_cleanup(struct archive_read *a)
|
||||
{
|
||||
int ret = ARCHIVE_OK;
|
||||
|
||||
ret = archive_write_free(a->extract->ad);
|
||||
free(a->extract);
|
||||
a->extract = NULL;
|
||||
return (ret);
|
||||
}
|
@ -1,190 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
#ifdef HAVE_IO_H
|
||||
#include <io.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
|
||||
struct read_fd_data {
|
||||
int fd;
|
||||
size_t block_size;
|
||||
char can_skip;
|
||||
void *buffer;
|
||||
};
|
||||
|
||||
static int file_close(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 stat st;
|
||||
struct read_fd_data *mine;
|
||||
void *b;
|
||||
|
||||
archive_clear_error(a);
|
||||
if (fstat(fd, &st) != 0) {
|
||||
archive_set_error(a, errno, "Can't stat fd %d", fd);
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
mine = (struct read_fd_data *)malloc(sizeof(*mine));
|
||||
b = malloc(block_size);
|
||||
if (mine == NULL || b == NULL) {
|
||||
archive_set_error(a, ENOMEM, "No memory");
|
||||
free(mine);
|
||||
free(b);
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
mine->block_size = block_size;
|
||||
mine->buffer = b;
|
||||
mine->fd = fd;
|
||||
/*
|
||||
* Skip support is a performance optimization for anything
|
||||
* that supports lseek(). On FreeBSD, only regular files and
|
||||
* raw disk devices support lseek() and there's no portable
|
||||
* way to determine if a device is a raw disk device, so we
|
||||
* only enable this optimization for regular files.
|
||||
*/
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
|
||||
mine->can_skip = 1;
|
||||
} else
|
||||
mine->can_skip = 0;
|
||||
#if defined(__CYGWIN__) || defined(_WIN32)
|
||||
setmode(mine->fd, O_BINARY);
|
||||
#endif
|
||||
|
||||
return (archive_read_open2(a, mine,
|
||||
NULL, file_read, file_skip, file_close));
|
||||
}
|
||||
|
||||
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;
|
||||
for (;;) {
|
||||
bytes_read = read(mine->fd, mine->buffer, mine->block_size);
|
||||
if (bytes_read < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
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;
|
||||
|
||||
if (!mine->can_skip)
|
||||
return (0);
|
||||
|
||||
/* Reduce request to the next smallest multiple of block_size */
|
||||
request = (request / mine->block_size) * mine->block_size;
|
||||
if (request == 0)
|
||||
return (0);
|
||||
|
||||
/*
|
||||
* 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 seek failed once, it will probably fail again. */
|
||||
mine->can_skip = 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 */
|
||||
free(mine->buffer);
|
||||
free(mine);
|
||||
return (ARCHIVE_OK);
|
||||
}
|
@ -1,165 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
#ifdef HAVE_IO_H
|
||||
#include <io.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
|
||||
struct read_FILE_data {
|
||||
FILE *f;
|
||||
size_t block_size;
|
||||
void *buffer;
|
||||
char can_skip;
|
||||
};
|
||||
|
||||
static int file_close(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 stat st;
|
||||
struct read_FILE_data *mine;
|
||||
size_t block_size = 128 * 1024;
|
||||
void *b;
|
||||
|
||||
archive_clear_error(a);
|
||||
mine = (struct read_FILE_data *)malloc(sizeof(*mine));
|
||||
b = malloc(block_size);
|
||||
if (mine == NULL || b == NULL) {
|
||||
archive_set_error(a, ENOMEM, "No memory");
|
||||
free(mine);
|
||||
free(b);
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
mine->block_size = block_size;
|
||||
mine->buffer = b;
|
||||
mine->f = f;
|
||||
/*
|
||||
* 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, some of which don't support fileno()).)
|
||||
*/
|
||||
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);
|
||||
/* Enable the seek optimization only for regular files. */
|
||||
mine->can_skip = 1;
|
||||
} else
|
||||
mine->can_skip = 0;
|
||||
|
||||
#if defined(__CYGWIN__) || defined(_WIN32)
|
||||
setmode(fileno(mine->f), O_BINARY);
|
||||
#endif
|
||||
|
||||
return (archive_read_open2(a, mine, NULL, file_read,
|
||||
file_skip, file_close));
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
(void)a; /* UNUSED */
|
||||
|
||||
/*
|
||||
* If we can't skip, return 0 as the amount we did step and
|
||||
* the caller will work around by reading and discarding.
|
||||
*/
|
||||
if (!mine->can_skip)
|
||||
return (0);
|
||||
if (request == 0)
|
||||
return (0);
|
||||
|
||||
#if HAVE_FSEEKO
|
||||
if (fseeko(mine->f, request, SEEK_CUR) != 0)
|
||||
#else
|
||||
if (fseek(mine->f, request, SEEK_CUR) != 0)
|
||||
#endif
|
||||
{
|
||||
mine->can_skip = 0;
|
||||
return (0);
|
||||
}
|
||||
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);
|
||||
}
|
@ -1,272 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
#ifdef HAVE_IO_H
|
||||
#include <io.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
|
||||
#ifndef O_BINARY
|
||||
#define O_BINARY 0
|
||||
#endif
|
||||
|
||||
struct read_file_data {
|
||||
int fd;
|
||||
size_t block_size;
|
||||
void *buffer;
|
||||
mode_t st_mode; /* Mode bits for opened file. */
|
||||
char can_skip; /* This file supports skipping. */
|
||||
char filename[1]; /* Must be last! */
|
||||
};
|
||||
|
||||
static int file_close(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 stat st;
|
||||
struct read_file_data *mine;
|
||||
void *b;
|
||||
int fd;
|
||||
|
||||
archive_clear_error(a);
|
||||
if (filename == NULL || filename[0] == '\0') {
|
||||
/* We used to invoke archive_read_open_fd(a,0,block_size)
|
||||
* here, but that doesn't (and shouldn't) handle the
|
||||
* end-of-file flush when reading stdout from a pipe.
|
||||
* Basically, read_open_fd() is intended for folks who
|
||||
* are willing to handle such details themselves. This
|
||||
* API is intended to be a little smarter for folks who
|
||||
* want easy handling of the common case.
|
||||
*/
|
||||
filename = ""; /* Normalize NULL to "" */
|
||||
fd = 0;
|
||||
#if defined(__CYGWIN__) || defined(_WIN32)
|
||||
setmode(0, O_BINARY);
|
||||
#endif
|
||||
} else {
|
||||
fd = open(filename, O_RDONLY | O_BINARY);
|
||||
if (fd < 0) {
|
||||
archive_set_error(a, errno,
|
||||
"Failed to open '%s'", filename);
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
}
|
||||
if (fstat(fd, &st) != 0) {
|
||||
archive_set_error(a, errno, "Can't stat '%s'", filename);
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
mine = (struct read_file_data *)calloc(1,
|
||||
sizeof(*mine) + strlen(filename));
|
||||
b = malloc(block_size);
|
||||
if (mine == NULL || b == NULL) {
|
||||
archive_set_error(a, ENOMEM, "No memory");
|
||||
free(mine);
|
||||
free(b);
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
strcpy(mine->filename, filename);
|
||||
mine->block_size = block_size;
|
||||
mine->buffer = b;
|
||||
mine->fd = fd;
|
||||
/* Remember mode so close can decide whether to flush. */
|
||||
mine->st_mode = st.st_mode;
|
||||
/* 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);
|
||||
/*
|
||||
* Enabling skip here is a performance optimization
|
||||
* for anything that supports lseek(). On FreeBSD
|
||||
* (and probably many other systems), only regular
|
||||
* files and raw disk devices support lseek() (on
|
||||
* other input types, lseek() returns success but
|
||||
* doesn't actually change the file pointer, which
|
||||
* just completely screws up the position-tracking
|
||||
* logic). In addition, I've yet to find a portable
|
||||
* way to determine if a device is a raw disk device.
|
||||
* So I don't see a way to do much better than to only
|
||||
* enable this optimization for regular files.
|
||||
*/
|
||||
mine->can_skip = 1;
|
||||
}
|
||||
return (archive_read_open2(a, mine,
|
||||
NULL, file_read, file_skip, file_close));
|
||||
}
|
||||
|
||||
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;
|
||||
for (;;) {
|
||||
bytes_read = read(mine->fd, mine->buffer, mine->block_size);
|
||||
if (bytes_read < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
else 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;
|
||||
|
||||
if (!mine->can_skip) /* We can't skip, so ... */
|
||||
return (0); /* ... skip zero bytes. */
|
||||
|
||||
/* Reduce request to the next smallest multiple of block_size */
|
||||
request = (request / mine->block_size) * mine->block_size;
|
||||
if (request == 0)
|
||||
return (0);
|
||||
|
||||
/*
|
||||
* 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 skip failed once, it will probably fail again. */
|
||||
mine->can_skip = 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 */
|
||||
|
||||
/* Only flush and close if open succeeded. */
|
||||
if (mine->fd >= 0) {
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
free(mine->buffer);
|
||||
free(mine);
|
||||
return (ARCHIVE_OK);
|
||||
}
|
@ -1,156 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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 ((off_t)skip > (off_t)(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);
|
||||
}
|
@ -1,199 +0,0 @@
|
||||
/*-
|
||||
* 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$
|
||||
*/
|
||||
|
||||
#ifndef __LIBARCHIVE_BUILD
|
||||
#error This header is only to be used internally to libarchive.
|
||||
#endif
|
||||
|
||||
#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_read_filter_bidder;
|
||||
struct archive_read_filter;
|
||||
|
||||
/*
|
||||
* How bidding works for filters:
|
||||
* * The bid manager reads the first block from the current source.
|
||||
* * It shows that block to each registered bidder.
|
||||
* * The bid manager creates a new filter structure for the winning
|
||||
* bidder and gives the winning bidder a chance to initialize it.
|
||||
* * The new filter becomes the top filter in the archive_read structure
|
||||
* and we repeat the process.
|
||||
* This ends only when no bidder provides a non-zero bid.
|
||||
*/
|
||||
struct archive_read_filter_bidder {
|
||||
/* Configuration data for the bidder. */
|
||||
void *data;
|
||||
/* Taste the upstream filter to see if we handle this. */
|
||||
int (*bid)(struct archive_read_filter_bidder *,
|
||||
struct archive_read_filter *);
|
||||
/* Initialize a newly-created filter. */
|
||||
int (*init)(struct archive_read_filter *);
|
||||
/* Set an option for the filter bidder. */
|
||||
int (*options)(struct archive_read_filter_bidder *,
|
||||
const char *key, const char *value);
|
||||
/* Release the bidder's configuration data. */
|
||||
int (*free)(struct archive_read_filter_bidder *);
|
||||
};
|
||||
|
||||
/*
|
||||
* This structure is allocated within the archive_read core
|
||||
* and initialized by archive_read and the init() method of the
|
||||
* corresponding bidder above.
|
||||
*/
|
||||
struct archive_read_filter {
|
||||
/* Essentially all filters will need these values, so
|
||||
* just declare them here. */
|
||||
struct archive_read_filter_bidder *bidder; /* My bidder. */
|
||||
struct archive_read_filter *upstream; /* Who I read from. */
|
||||
struct archive_read *archive; /* Associated archive. */
|
||||
/* Return next block. */
|
||||
ssize_t (*read)(struct archive_read_filter *, const void **);
|
||||
/* Skip forward this many bytes. */
|
||||
int64_t (*skip)(struct archive_read_filter *self, int64_t request);
|
||||
/* Close (just this filter) and free(self). */
|
||||
int (*close)(struct archive_read_filter *self);
|
||||
/* My private data. */
|
||||
void *data;
|
||||
|
||||
const char *name;
|
||||
int code;
|
||||
|
||||
/* Used by reblocking logic. */
|
||||
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;
|
||||
int64_t position;
|
||||
char end_of_file;
|
||||
char fatal;
|
||||
};
|
||||
|
||||
/*
|
||||
* The client looks a lot like a filter, so we just wrap it here.
|
||||
*
|
||||
* TODO: Make archive_read_filter and archive_read_client identical so
|
||||
* that users of the library can easily register their own
|
||||
* transformation filters. This will probably break the API/ABI and
|
||||
* so should be deferred at least until libarchive 3.0.
|
||||
*/
|
||||
struct archive_read_client {
|
||||
archive_read_callback *reader;
|
||||
archive_skip_callback *skipper;
|
||||
archive_close_callback *closer;
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
/*
|
||||
* 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 client archive stream. */
|
||||
struct archive_read_client client;
|
||||
|
||||
/* Registered filter bidders. */
|
||||
struct archive_read_filter_bidder bidders[8];
|
||||
|
||||
/* Last filter in chain */
|
||||
struct archive_read_filter *filter;
|
||||
|
||||
/* File offset of beginning of most recently-read header. */
|
||||
off_t header_position;
|
||||
|
||||
/*
|
||||
* Format detection is mostly the same as compression
|
||||
* detection, with one significant difference: 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.
|
||||
*/
|
||||
|
||||
struct archive_format_descriptor {
|
||||
void *data;
|
||||
const char *name;
|
||||
int (*bid)(struct archive_read *);
|
||||
int (*options)(struct archive_read *, const char *key,
|
||||
const char *value);
|
||||
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[9];
|
||||
struct archive_format_descriptor *format; /* Active format. */
|
||||
|
||||
/*
|
||||
* 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,
|
||||
const char *name,
|
||||
int (*bid)(struct archive_read *),
|
||||
int (*options)(struct archive_read *, const char *, const char *),
|
||||
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 archive_read_filter_bidder
|
||||
*__archive_read_get_bidder(struct archive_read *a);
|
||||
|
||||
const void *__archive_read_ahead(struct archive_read *, size_t, ssize_t *);
|
||||
const void *__archive_read_filter_ahead(struct archive_read_filter *,
|
||||
size_t, ssize_t *);
|
||||
ssize_t __archive_read_consume(struct archive_read *, size_t);
|
||||
ssize_t __archive_read_filter_consume(struct archive_read_filter *, size_t);
|
||||
int64_t __archive_read_skip(struct archive_read *, int64_t);
|
||||
int64_t __archive_read_skip_lenient(struct archive_read *, int64_t);
|
||||
int64_t __archive_read_filter_skip(struct archive_read_filter *, int64_t);
|
||||
int __archive_read_program(struct archive_read_filter *, const char *);
|
||||
#endif
|
@ -1,60 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#include "archive.h"
|
||||
|
||||
int
|
||||
archive_read_support_compression_all(struct archive *a)
|
||||
{
|
||||
/* Bzip falls back to "bunzip2" command-line */
|
||||
archive_read_support_compression_bzip2(a);
|
||||
/* The decompress code doesn't use an outside library. */
|
||||
archive_read_support_compression_compress(a);
|
||||
/* Gzip decompress falls back to "gunzip" command-line. */
|
||||
archive_read_support_compression_gzip(a);
|
||||
/* The LZMA file format has a very weak signature, so it
|
||||
* may not be feasible to keep this here, but we'll try.
|
||||
* This will come back out if there are problems. */
|
||||
/* Lzma falls back to "unlzma" command-line program. */
|
||||
archive_read_support_compression_lzma(a);
|
||||
/* Xz falls back to "unxz" command-line program. */
|
||||
archive_read_support_compression_xz(a);
|
||||
/* The decode code doesn't use an outside library. */
|
||||
archive_read_support_compression_uu(a);
|
||||
/* The decode code doesn't use an outside library. */
|
||||
archive_read_support_compression_rpm(a);
|
||||
|
||||
/* Note: We always return ARCHIVE_OK here, even if some of the
|
||||
* above return ARCHIVE_WARN. The intent here is to enable
|
||||
* "as much as possible." Clients who need specific
|
||||
* compression should enable those individually so they can
|
||||
* verify the level of support. */
|
||||
/* Clear any warning messages set by the above functions. */
|
||||
archive_clear_error(a);
|
||||
return (ARCHIVE_OK);
|
||||
}
|
@ -1,353 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_BZLIB_H
|
||||
#include <bzlib.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_private.h"
|
||||
#include "archive_read_private.h"
|
||||
|
||||
#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
|
||||
struct private_data {
|
||||
bz_stream stream;
|
||||
char *out_block;
|
||||
size_t out_block_size;
|
||||
char valid; /* True = decompressor is initialized */
|
||||
char eof; /* True = found end of compressed data. */
|
||||
};
|
||||
|
||||
/* Bzip2 filter */
|
||||
static ssize_t bzip2_filter_read(struct archive_read_filter *, const void **);
|
||||
static int bzip2_filter_close(struct archive_read_filter *);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Note that we can detect bzip2 archives even if we can't decompress
|
||||
* them. (In fact, we like detecting them because we can give better
|
||||
* error messages.) So the bid framework here gets compiled even
|
||||
* if bzlib is unavailable.
|
||||
*/
|
||||
static int bzip2_reader_bid(struct archive_read_filter_bidder *, struct archive_read_filter *);
|
||||
static int bzip2_reader_init(struct archive_read_filter *);
|
||||
static int bzip2_reader_free(struct archive_read_filter_bidder *);
|
||||
|
||||
int
|
||||
archive_read_support_compression_bzip2(struct archive *_a)
|
||||
{
|
||||
struct archive_read *a = (struct archive_read *)_a;
|
||||
struct archive_read_filter_bidder *reader = __archive_read_get_bidder(a);
|
||||
|
||||
if (reader == NULL)
|
||||
return (ARCHIVE_FATAL);
|
||||
|
||||
reader->data = NULL;
|
||||
reader->bid = bzip2_reader_bid;
|
||||
reader->init = bzip2_reader_init;
|
||||
reader->options = NULL;
|
||||
reader->free = bzip2_reader_free;
|
||||
#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
|
||||
return (ARCHIVE_OK);
|
||||
#else
|
||||
archive_set_error(_a, ARCHIVE_ERRNO_MISC,
|
||||
"Using external bunzip2 program");
|
||||
return (ARCHIVE_WARN);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
bzip2_reader_free(struct archive_read_filter_bidder *self){
|
||||
(void)self; /* UNUSED */
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
bzip2_reader_bid(struct archive_read_filter_bidder *self, struct archive_read_filter *filter)
|
||||
{
|
||||
const unsigned char *buffer;
|
||||
ssize_t avail;
|
||||
int bits_checked;
|
||||
|
||||
(void)self; /* UNUSED */
|
||||
|
||||
/* Minimal bzip2 archive is 14 bytes. */
|
||||
buffer = __archive_read_filter_ahead(filter, 14, &avail);
|
||||
if (buffer == NULL)
|
||||
return (0);
|
||||
|
||||
/* First three bytes must be "BZh" */
|
||||
bits_checked = 0;
|
||||
if (buffer[0] != 'B' || buffer[1] != 'Z' || buffer[2] != 'h')
|
||||
return (0);
|
||||
bits_checked += 24;
|
||||
|
||||
/* Next follows a compression flag which must be an ASCII digit. */
|
||||
if (buffer[3] < '1' || buffer[3] > '9')
|
||||
return (0);
|
||||
bits_checked += 5;
|
||||
|
||||
/* After BZh[1-9], there must be either a data block
|
||||
* which begins with 0x314159265359 or an end-of-data
|
||||
* marker of 0x177245385090. */
|
||||
if (memcmp(buffer + 4, "\x31\x41\x59\x26\x53\x59", 6) == 0)
|
||||
bits_checked += 48;
|
||||
else if (memcmp(buffer + 4, "\x17\x72\x45\x38\x50\x90", 6) == 0)
|
||||
bits_checked += 48;
|
||||
else
|
||||
return (0);
|
||||
|
||||
return (bits_checked);
|
||||
}
|
||||
|
||||
#if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR)
|
||||
|
||||
/*
|
||||
* If we don't have the library on this system, we can't actually do the
|
||||
* decompression. We can, however, still detect compressed archives
|
||||
* and emit a useful message.
|
||||
*/
|
||||
static int
|
||||
bzip2_reader_init(struct archive_read_filter *self)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = __archive_read_program(self, "bunzip2");
|
||||
/* Note: We set the format here even if __archive_read_program()
|
||||
* above fails. We do, after all, know what the format is
|
||||
* even if we weren't able to read it. */
|
||||
self->code = ARCHIVE_COMPRESSION_BZIP2;
|
||||
self->name = "bzip2";
|
||||
return (r);
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Setup the callbacks.
|
||||
*/
|
||||
static int
|
||||
bzip2_reader_init(struct archive_read_filter *self)
|
||||
{
|
||||
static const size_t out_block_size = 64 * 1024;
|
||||
void *out_block;
|
||||
struct private_data *state;
|
||||
|
||||
self->code = ARCHIVE_COMPRESSION_BZIP2;
|
||||
self->name = "bzip2";
|
||||
|
||||
state = (struct private_data *)calloc(sizeof(*state), 1);
|
||||
out_block = (unsigned char *)malloc(out_block_size);
|
||||
if (self == NULL || state == NULL || out_block == NULL) {
|
||||
archive_set_error(&self->archive->archive, ENOMEM,
|
||||
"Can't allocate data for bzip2 decompression");
|
||||
free(out_block);
|
||||
free(state);
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
self->data = state;
|
||||
state->out_block_size = out_block_size;
|
||||
state->out_block = out_block;
|
||||
self->read = bzip2_filter_read;
|
||||
self->skip = NULL; /* not supported */
|
||||
self->close = bzip2_filter_close;
|
||||
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the next block of decompressed data.
|
||||
*/
|
||||
static ssize_t
|
||||
bzip2_filter_read(struct archive_read_filter *self, const void **p)
|
||||
{
|
||||
struct private_data *state;
|
||||
size_t decompressed;
|
||||
const char *read_buf;
|
||||
ssize_t ret;
|
||||
|
||||
state = (struct private_data *)self->data;
|
||||
|
||||
if (state->eof) {
|
||||
*p = NULL;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Empty our output buffer. */
|
||||
state->stream.next_out = state->out_block;
|
||||
state->stream.avail_out = state->out_block_size;
|
||||
|
||||
/* Try to fill the output buffer. */
|
||||
for (;;) {
|
||||
if (!state->valid) {
|
||||
if (bzip2_reader_bid(self->bidder, self->upstream) == 0) {
|
||||
state->eof = 1;
|
||||
*p = state->out_block;
|
||||
decompressed = state->stream.next_out
|
||||
- state->out_block;
|
||||
return (decompressed);
|
||||
}
|
||||
/* Initialize compression library. */
|
||||
ret = BZ2_bzDecompressInit(&(state->stream),
|
||||
0 /* library verbosity */,
|
||||
0 /* don't use low-mem algorithm */);
|
||||
|
||||
/* If init fails, try low-memory algorithm instead. */
|
||||
if (ret == BZ_MEM_ERROR)
|
||||
ret = BZ2_bzDecompressInit(&(state->stream),
|
||||
0 /* library verbosity */,
|
||||
1 /* do use low-mem algo */);
|
||||
|
||||
if (ret != BZ_OK) {
|
||||
const char *detail = NULL;
|
||||
int err = ARCHIVE_ERRNO_MISC;
|
||||
switch (ret) {
|
||||
case BZ_PARAM_ERROR:
|
||||
detail = "invalid setup parameter";
|
||||
break;
|
||||
case BZ_MEM_ERROR:
|
||||
err = ENOMEM;
|
||||
detail = "out of memory";
|
||||
break;
|
||||
case BZ_CONFIG_ERROR:
|
||||
detail = "mis-compiled library";
|
||||
break;
|
||||
}
|
||||
archive_set_error(&self->archive->archive, err,
|
||||
"Internal error initializing decompressor%s%s",
|
||||
detail == NULL ? "" : ": ",
|
||||
detail);
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
state->valid = 1;
|
||||
}
|
||||
|
||||
/* stream.next_in is really const, but bzlib
|
||||
* doesn't declare it so. <sigh> */
|
||||
read_buf =
|
||||
__archive_read_filter_ahead(self->upstream, 1, &ret);
|
||||
if (read_buf == NULL)
|
||||
return (ARCHIVE_FATAL);
|
||||
state->stream.next_in = (char *)(uintptr_t)read_buf;
|
||||
state->stream.avail_in = ret;
|
||||
/* There is no more data, return whatever we have. */
|
||||
if (ret == 0) {
|
||||
state->eof = 1;
|
||||
*p = state->out_block;
|
||||
decompressed = state->stream.next_out
|
||||
- state->out_block;
|
||||
return (decompressed);
|
||||
}
|
||||
|
||||
/* Decompress as much as we can in one pass. */
|
||||
ret = BZ2_bzDecompress(&(state->stream));
|
||||
__archive_read_filter_consume(self->upstream,
|
||||
state->stream.next_in - read_buf);
|
||||
|
||||
switch (ret) {
|
||||
case BZ_STREAM_END: /* Found end of stream. */
|
||||
switch (BZ2_bzDecompressEnd(&(state->stream))) {
|
||||
case BZ_OK:
|
||||
break;
|
||||
default:
|
||||
archive_set_error(&(self->archive->archive),
|
||||
ARCHIVE_ERRNO_MISC,
|
||||
"Failed to clean up decompressor");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
state->valid = 0;
|
||||
/* FALLTHROUGH */
|
||||
case BZ_OK: /* Decompressor made some progress. */
|
||||
/* If we filled our buffer, update stats and return. */
|
||||
if (state->stream.avail_out == 0) {
|
||||
*p = state->out_block;
|
||||
decompressed = state->stream.next_out
|
||||
- state->out_block;
|
||||
return (decompressed);
|
||||
}
|
||||
break;
|
||||
default: /* Return an error. */
|
||||
archive_set_error(&self->archive->archive,
|
||||
ARCHIVE_ERRNO_MISC, "bzip decompression failed");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up the decompressor.
|
||||
*/
|
||||
static int
|
||||
bzip2_filter_close(struct archive_read_filter *self)
|
||||
{
|
||||
struct private_data *state;
|
||||
int ret = ARCHIVE_OK;
|
||||
|
||||
state = (struct private_data *)self->data;
|
||||
|
||||
if (state->valid) {
|
||||
switch (BZ2_bzDecompressEnd(&state->stream)) {
|
||||
case BZ_OK:
|
||||
break;
|
||||
default:
|
||||
archive_set_error(&self->archive->archive,
|
||||
ARCHIVE_ERRNO_MISC,
|
||||
"Failed to clean up decompressor");
|
||||
ret = ARCHIVE_FATAL;
|
||||
}
|
||||
}
|
||||
|
||||
free(state->out_block);
|
||||
free(state);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
#endif /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */
|
@ -1,444 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#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 out_block_size;
|
||||
void *out_block;
|
||||
|
||||
/* 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 compress_bidder_bid(struct archive_read_filter_bidder *, struct archive_read_filter *);
|
||||
static int compress_bidder_init(struct archive_read_filter *);
|
||||
static int compress_bidder_free(struct archive_read_filter_bidder *);
|
||||
|
||||
static ssize_t compress_filter_read(struct archive_read_filter *, const void **);
|
||||
static int compress_filter_close(struct archive_read_filter *);
|
||||
|
||||
static int getbits(struct archive_read_filter *, int n);
|
||||
static int next_code(struct archive_read_filter *);
|
||||
|
||||
int
|
||||
archive_read_support_compression_compress(struct archive *_a)
|
||||
{
|
||||
struct archive_read *a = (struct archive_read *)_a;
|
||||
struct archive_read_filter_bidder *bidder = __archive_read_get_bidder(a);
|
||||
|
||||
if (bidder == NULL)
|
||||
return (ARCHIVE_FATAL);
|
||||
|
||||
bidder->data = NULL;
|
||||
bidder->bid = compress_bidder_bid;
|
||||
bidder->init = compress_bidder_init;
|
||||
bidder->options = NULL;
|
||||
bidder->free = compress_bidder_free;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
compress_bidder_bid(struct archive_read_filter_bidder *self,
|
||||
struct archive_read_filter *filter)
|
||||
{
|
||||
const unsigned char *buffer;
|
||||
ssize_t avail;
|
||||
int bits_checked;
|
||||
|
||||
(void)self; /* UNUSED */
|
||||
|
||||
buffer = __archive_read_filter_ahead(filter, 2, &avail);
|
||||
|
||||
if (buffer == NULL)
|
||||
return (0);
|
||||
|
||||
bits_checked = 0;
|
||||
if (buffer[0] != 037) /* Verify first ID byte. */
|
||||
return (0);
|
||||
bits_checked += 8;
|
||||
|
||||
if (buffer[1] != 0235) /* Verify second ID byte. */
|
||||
return (0);
|
||||
bits_checked += 8;
|
||||
|
||||
/*
|
||||
* TODO: Verify more.
|
||||
*/
|
||||
|
||||
return (bits_checked);
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup the callbacks.
|
||||
*/
|
||||
static int
|
||||
compress_bidder_init(struct archive_read_filter *self)
|
||||
{
|
||||
struct private_data *state;
|
||||
static const size_t out_block_size = 64 * 1024;
|
||||
void *out_block;
|
||||
int code;
|
||||
|
||||
self->code = ARCHIVE_COMPRESSION_COMPRESS;
|
||||
self->name = "compress (.Z)";
|
||||
|
||||
state = (struct private_data *)calloc(sizeof(*state), 1);
|
||||
out_block = malloc(out_block_size);
|
||||
if (state == NULL || out_block == NULL) {
|
||||
free(out_block);
|
||||
free(state);
|
||||
archive_set_error(&self->archive->archive, ENOMEM,
|
||||
"Can't allocate data for %s decompression",
|
||||
self->name);
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
self->data = state;
|
||||
state->out_block_size = out_block_size;
|
||||
state->out_block = out_block;
|
||||
self->read = compress_filter_read;
|
||||
self->skip = NULL; /* not supported */
|
||||
self->close = compress_filter_close;
|
||||
|
||||
/* XXX MOVE THE FOLLOWING OUT OF INIT() XXX */
|
||||
|
||||
(void)getbits(self, 8); /* Skip first signature byte. */
|
||||
(void)getbits(self, 8); /* Skip second signature byte. */
|
||||
|
||||
code = getbits(self, 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<<state->bits) - 1;
|
||||
state->oldcode = -1;
|
||||
for (code = 255; code >= 0; code--) {
|
||||
state->prefix[code] = 0;
|
||||
state->suffix[code] = code;
|
||||
}
|
||||
next_code(self);
|
||||
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a block of data from the decompression buffer. Decompress more
|
||||
* as necessary.
|
||||
*/
|
||||
static ssize_t
|
||||
compress_filter_read(struct archive_read_filter *self, const void **pblock)
|
||||
{
|
||||
struct private_data *state;
|
||||
unsigned char *p, *start, *end;
|
||||
int ret;
|
||||
|
||||
state = (struct private_data *)self->data;
|
||||
if (state->end_of_stream) {
|
||||
*pblock = NULL;
|
||||
return (0);
|
||||
}
|
||||
p = start = (unsigned char *)state->out_block;
|
||||
end = start + state->out_block_size;
|
||||
|
||||
while (p < end && !state->end_of_stream) {
|
||||
if (state->stackp > state->stack) {
|
||||
*p++ = *--state->stackp;
|
||||
} else {
|
||||
ret = next_code(self);
|
||||
if (ret == -1)
|
||||
state->end_of_stream = ret;
|
||||
else if (ret != ARCHIVE_OK)
|
||||
return (ret);
|
||||
}
|
||||
}
|
||||
|
||||
*pblock = start;
|
||||
return (p - start);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up the reader.
|
||||
*/
|
||||
static int
|
||||
compress_bidder_free(struct archive_read_filter_bidder *self)
|
||||
{
|
||||
self->data = NULL;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Close and release the filter.
|
||||
*/
|
||||
static int
|
||||
compress_filter_close(struct archive_read_filter *self)
|
||||
{
|
||||
struct private_data *state = (struct private_data *)self->data;
|
||||
|
||||
free(state->out_block);
|
||||
free(state);
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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_filter *self)
|
||||
{
|
||||
struct private_data *state = (struct private_data *)self->data;
|
||||
int code, newcode;
|
||||
|
||||
static int debug_buff[1024];
|
||||
static unsigned debug_index;
|
||||
|
||||
code = newcode = getbits(self, 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(self, 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(self));
|
||||
}
|
||||
|
||||
if (code > state->free_ent) {
|
||||
/* An invalid code is a fatal error. */
|
||||
archive_set_error(&(self->archive->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_filter *self, int n)
|
||||
{
|
||||
struct private_data *state = (struct private_data *)self->data;
|
||||
int code;
|
||||
ssize_t ret;
|
||||
static const int mask[] = {
|
||||
0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff,
|
||||
0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff, 0x7fff, 0xffff
|
||||
};
|
||||
|
||||
while (state->bits_avail < n) {
|
||||
if (state->avail_in <= 0) {
|
||||
state->next_in
|
||||
= __archive_read_filter_ahead(self->upstream,
|
||||
1, &ret);
|
||||
if (ret == 0)
|
||||
return (-1);
|
||||
if (ret < 0 || state->next_in == NULL)
|
||||
return (ARCHIVE_FATAL);
|
||||
state->avail_in = ret;
|
||||
__archive_read_filter_consume(self->upstream, 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]);
|
||||
}
|
@ -1,465 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_ZLIB_H
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_private.h"
|
||||
#include "archive_read_private.h"
|
||||
|
||||
#ifdef HAVE_ZLIB_H
|
||||
struct private_data {
|
||||
z_stream stream;
|
||||
char in_stream;
|
||||
unsigned char *out_block;
|
||||
size_t out_block_size;
|
||||
int64_t total_out;
|
||||
unsigned long crc;
|
||||
char eof; /* True = found end of compressed data. */
|
||||
};
|
||||
|
||||
/* Gzip Filter. */
|
||||
static ssize_t gzip_filter_read(struct archive_read_filter *, const void **);
|
||||
static int gzip_filter_close(struct archive_read_filter *);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Note that we can detect gzip archives even if we can't decompress
|
||||
* them. (In fact, we like detecting them because we can give better
|
||||
* error messages.) So the bid framework here gets compiled even
|
||||
* if zlib is unavailable.
|
||||
*
|
||||
* TODO: If zlib is unavailable, gzip_bidder_init() should
|
||||
* use the compress_program framework to try to fire up an external
|
||||
* gunzip program.
|
||||
*/
|
||||
static int gzip_bidder_bid(struct archive_read_filter_bidder *,
|
||||
struct archive_read_filter *);
|
||||
static int gzip_bidder_init(struct archive_read_filter *);
|
||||
|
||||
int
|
||||
archive_read_support_compression_gzip(struct archive *_a)
|
||||
{
|
||||
struct archive_read *a = (struct archive_read *)_a;
|
||||
struct archive_read_filter_bidder *bidder = __archive_read_get_bidder(a);
|
||||
|
||||
if (bidder == NULL)
|
||||
return (ARCHIVE_FATAL);
|
||||
|
||||
bidder->data = NULL;
|
||||
bidder->bid = gzip_bidder_bid;
|
||||
bidder->init = gzip_bidder_init;
|
||||
bidder->options = NULL;
|
||||
bidder->free = NULL; /* No data, so no cleanup necessary. */
|
||||
/* Signal the extent of gzip support with the return value here. */
|
||||
#if HAVE_ZLIB_H
|
||||
return (ARCHIVE_OK);
|
||||
#else
|
||||
archive_set_error(_a, ARCHIVE_ERRNO_MISC,
|
||||
"Using external gunzip program");
|
||||
return (ARCHIVE_WARN);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Read and verify the header.
|
||||
*
|
||||
* Returns zero if the header couldn't be validated, else returns
|
||||
* number of bytes in header. If pbits is non-NULL, it receives a
|
||||
* count of bits verified, suitable for use by bidder.
|
||||
*/
|
||||
static int
|
||||
peek_at_header(struct archive_read_filter *filter, int *pbits)
|
||||
{
|
||||
const unsigned char *p;
|
||||
ssize_t avail, len;
|
||||
int bits = 0;
|
||||
int header_flags;
|
||||
|
||||
/* Start by looking at the first ten bytes of the header, which
|
||||
* is all fixed layout. */
|
||||
len = 10;
|
||||
p = __archive_read_filter_ahead(filter, len, &avail);
|
||||
if (p == NULL || avail == 0)
|
||||
return (0);
|
||||
if (p[0] != 037)
|
||||
return (0);
|
||||
bits += 8;
|
||||
if (p[1] != 0213)
|
||||
return (0);
|
||||
bits += 8;
|
||||
if (p[2] != 8) /* We only support deflation. */
|
||||
return (0);
|
||||
bits += 8;
|
||||
if ((p[3] & 0xE0)!= 0) /* No reserved flags set. */
|
||||
return (0);
|
||||
bits += 3;
|
||||
header_flags = p[3];
|
||||
/* Bytes 4-7 are mod time. */
|
||||
/* Byte 8 is deflate flags. */
|
||||
/* XXXX TODO: return deflate flags back to consume_header for use
|
||||
in initializing the decompressor. */
|
||||
/* Byte 9 is OS. */
|
||||
|
||||
/* Optional extra data: 2 byte length plus variable body. */
|
||||
if (header_flags & 4) {
|
||||
p = __archive_read_filter_ahead(filter, len + 2, &avail);
|
||||
if (p == NULL)
|
||||
return (0);
|
||||
len += ((int)p[len + 1] << 8) | (int)p[len];
|
||||
len += 2;
|
||||
}
|
||||
|
||||
/* Null-terminated optional filename. */
|
||||
if (header_flags & 8) {
|
||||
do {
|
||||
++len;
|
||||
if (avail < len)
|
||||
p = __archive_read_filter_ahead(filter,
|
||||
len, &avail);
|
||||
if (p == NULL)
|
||||
return (0);
|
||||
} while (p[len - 1] != 0);
|
||||
}
|
||||
|
||||
/* Null-terminated optional comment. */
|
||||
if (header_flags & 16) {
|
||||
do {
|
||||
++len;
|
||||
if (avail < len)
|
||||
p = __archive_read_filter_ahead(filter,
|
||||
len, &avail);
|
||||
if (p == NULL)
|
||||
return (0);
|
||||
} while (p[len - 1] != 0);
|
||||
}
|
||||
|
||||
/* Optional header CRC */
|
||||
if ((header_flags & 2)) {
|
||||
p = __archive_read_filter_ahead(filter, len + 2, &avail);
|
||||
if (p == NULL)
|
||||
return (0);
|
||||
#if 0
|
||||
int hcrc = ((int)p[len + 1] << 8) | (int)p[len];
|
||||
int crc = /* XXX TODO: Compute header CRC. */;
|
||||
if (crc != hcrc)
|
||||
return (0);
|
||||
bits += 16;
|
||||
#endif
|
||||
len += 2;
|
||||
}
|
||||
|
||||
if (pbits != NULL)
|
||||
*pbits = bits;
|
||||
return (len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Bidder just verifies the header and returns the number of verified bits.
|
||||
*/
|
||||
static int
|
||||
gzip_bidder_bid(struct archive_read_filter_bidder *self,
|
||||
struct archive_read_filter *filter)
|
||||
{
|
||||
int bits_checked;
|
||||
|
||||
(void)self; /* UNUSED */
|
||||
|
||||
if (peek_at_header(filter, &bits_checked))
|
||||
return (bits_checked);
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
#ifndef HAVE_ZLIB_H
|
||||
|
||||
/*
|
||||
* If we don't have the library on this system, we can't do the
|
||||
* decompression directly. We can, however, try to run gunzip
|
||||
* in case that's available.
|
||||
*/
|
||||
static int
|
||||
gzip_bidder_init(struct archive_read_filter *self)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = __archive_read_program(self, "gunzip");
|
||||
/* Note: We set the format here even if __archive_read_program()
|
||||
* above fails. We do, after all, know what the format is
|
||||
* even if we weren't able to read it. */
|
||||
self->code = ARCHIVE_COMPRESSION_GZIP;
|
||||
self->name = "gzip";
|
||||
return (r);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Initialize the filter object.
|
||||
*/
|
||||
static int
|
||||
gzip_bidder_init(struct archive_read_filter *self)
|
||||
{
|
||||
struct private_data *state;
|
||||
static const size_t out_block_size = 64 * 1024;
|
||||
void *out_block;
|
||||
|
||||
self->code = ARCHIVE_COMPRESSION_GZIP;
|
||||
self->name = "gzip";
|
||||
|
||||
state = (struct private_data *)calloc(sizeof(*state), 1);
|
||||
out_block = (unsigned char *)malloc(out_block_size);
|
||||
if (state == NULL || out_block == NULL) {
|
||||
free(out_block);
|
||||
free(state);
|
||||
archive_set_error(&self->archive->archive, ENOMEM,
|
||||
"Can't allocate data for gzip decompression");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
self->data = state;
|
||||
state->out_block_size = out_block_size;
|
||||
state->out_block = out_block;
|
||||
self->read = gzip_filter_read;
|
||||
self->skip = NULL; /* not supported */
|
||||
self->close = gzip_filter_close;
|
||||
|
||||
state->in_stream = 0; /* We're not actually within a stream yet. */
|
||||
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
consume_header(struct archive_read_filter *self)
|
||||
{
|
||||
struct private_data *state;
|
||||
ssize_t avail;
|
||||
size_t len;
|
||||
int ret;
|
||||
|
||||
state = (struct private_data *)self->data;
|
||||
|
||||
/* If this is a real header, consume it. */
|
||||
len = peek_at_header(self->upstream, NULL);
|
||||
if (len == 0)
|
||||
return (ARCHIVE_EOF);
|
||||
__archive_read_filter_consume(self->upstream, len);
|
||||
|
||||
/* Initialize CRC accumulator. */
|
||||
state->crc = crc32(0L, NULL, 0);
|
||||
|
||||
/* Initialize compression library. */
|
||||
state->stream.next_in = (unsigned char *)(uintptr_t)
|
||||
__archive_read_filter_ahead(self->upstream, 1, &avail);
|
||||
state->stream.avail_in = avail;
|
||||
ret = inflateInit2(&(state->stream),
|
||||
-15 /* Don't check for zlib header */);
|
||||
|
||||
/* Decipher the error code. */
|
||||
switch (ret) {
|
||||
case Z_OK:
|
||||
state->in_stream = 1;
|
||||
return (ARCHIVE_OK);
|
||||
case Z_STREAM_ERROR:
|
||||
archive_set_error(&self->archive->archive,
|
||||
ARCHIVE_ERRNO_MISC,
|
||||
"Internal error initializing compression library: "
|
||||
"invalid setup parameter");
|
||||
break;
|
||||
case Z_MEM_ERROR:
|
||||
archive_set_error(&self->archive->archive, ENOMEM,
|
||||
"Internal error initializing compression library: "
|
||||
"out of memory");
|
||||
break;
|
||||
case Z_VERSION_ERROR:
|
||||
archive_set_error(&self->archive->archive,
|
||||
ARCHIVE_ERRNO_MISC,
|
||||
"Internal error initializing compression library: "
|
||||
"invalid library version");
|
||||
break;
|
||||
default:
|
||||
archive_set_error(&self->archive->archive,
|
||||
ARCHIVE_ERRNO_MISC,
|
||||
"Internal error initializing compression library: "
|
||||
" Zlib error %d", ret);
|
||||
break;
|
||||
}
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
static int
|
||||
consume_trailer(struct archive_read_filter *self)
|
||||
{
|
||||
struct private_data *state;
|
||||
const unsigned char *p;
|
||||
ssize_t avail;
|
||||
|
||||
state = (struct private_data *)self->data;
|
||||
|
||||
state->in_stream = 0;
|
||||
switch (inflateEnd(&(state->stream))) {
|
||||
case Z_OK:
|
||||
break;
|
||||
default:
|
||||
archive_set_error(&self->archive->archive,
|
||||
ARCHIVE_ERRNO_MISC,
|
||||
"Failed to clean up gzip decompressor");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
/* GZip trailer is a fixed 8 byte structure. */
|
||||
p = __archive_read_filter_ahead(self->upstream, 8, &avail);
|
||||
if (p == NULL || avail == 0)
|
||||
return (ARCHIVE_FATAL);
|
||||
|
||||
/* XXX TODO: Verify the length and CRC. */
|
||||
|
||||
/* We've verified the trailer, so consume it now. */
|
||||
__archive_read_filter_consume(self->upstream, 8);
|
||||
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
gzip_filter_read(struct archive_read_filter *self, const void **p)
|
||||
{
|
||||
struct private_data *state;
|
||||
size_t decompressed;
|
||||
ssize_t avail_in;
|
||||
int ret;
|
||||
|
||||
state = (struct private_data *)self->data;
|
||||
|
||||
/* Empty our output buffer. */
|
||||
state->stream.next_out = state->out_block;
|
||||
state->stream.avail_out = state->out_block_size;
|
||||
|
||||
/* Try to fill the output buffer. */
|
||||
while (state->stream.avail_out > 0 && !state->eof) {
|
||||
/* If we're not in a stream, read a header
|
||||
* and initialize the decompression library. */
|
||||
if (!state->in_stream) {
|
||||
ret = consume_header(self);
|
||||
if (ret == ARCHIVE_EOF) {
|
||||
state->eof = 1;
|
||||
break;
|
||||
}
|
||||
if (ret < ARCHIVE_OK)
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/* Peek at the next available data. */
|
||||
/* ZLib treats stream.next_in as const but doesn't declare
|
||||
* it so, hence this ugly cast. */
|
||||
state->stream.next_in = (unsigned char *)(uintptr_t)
|
||||
__archive_read_filter_ahead(self->upstream, 1, &avail_in);
|
||||
if (state->stream.next_in == NULL)
|
||||
return (ARCHIVE_FATAL);
|
||||
state->stream.avail_in = avail_in;
|
||||
|
||||
/* Decompress and consume some of that data. */
|
||||
ret = inflate(&(state->stream), 0);
|
||||
switch (ret) {
|
||||
case Z_OK: /* Decompressor made some progress. */
|
||||
__archive_read_filter_consume(self->upstream,
|
||||
avail_in - state->stream.avail_in);
|
||||
break;
|
||||
case Z_STREAM_END: /* Found end of stream. */
|
||||
__archive_read_filter_consume(self->upstream,
|
||||
avail_in - state->stream.avail_in);
|
||||
/* Consume the stream trailer; release the
|
||||
* decompression library. */
|
||||
ret = consume_trailer(self);
|
||||
if (ret < ARCHIVE_OK)
|
||||
return (ret);
|
||||
break;
|
||||
default:
|
||||
/* Return an error. */
|
||||
archive_set_error(&self->archive->archive,
|
||||
ARCHIVE_ERRNO_MISC,
|
||||
"gzip decompression failed");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
}
|
||||
|
||||
/* We've read as much as we can. */
|
||||
decompressed = state->stream.next_out - state->out_block;
|
||||
state->total_out += decompressed;
|
||||
if (decompressed == 0)
|
||||
*p = NULL;
|
||||
else
|
||||
*p = state->out_block;
|
||||
return (decompressed);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up the decompressor.
|
||||
*/
|
||||
static int
|
||||
gzip_filter_close(struct archive_read_filter *self)
|
||||
{
|
||||
struct private_data *state;
|
||||
int ret;
|
||||
|
||||
state = (struct private_data *)self->data;
|
||||
ret = ARCHIVE_OK;
|
||||
|
||||
if (state->in_stream) {
|
||||
switch (inflateEnd(&(state->stream))) {
|
||||
case Z_OK:
|
||||
break;
|
||||
default:
|
||||
archive_set_error(&(self->archive->archive),
|
||||
ARCHIVE_ERRNO_MISC,
|
||||
"Failed to clean up gzip compressor");
|
||||
ret = ARCHIVE_FATAL;
|
||||
}
|
||||
}
|
||||
|
||||
free(state->out_block);
|
||||
free(state);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
#endif /* HAVE_ZLIB_H */
|
@ -1,40 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#include "archive.h"
|
||||
|
||||
/*
|
||||
* Uncompressed streams are handled implicitly by the read core,
|
||||
* so this is now a no-op.
|
||||
*/
|
||||
int
|
||||
archive_read_support_compression_none(struct archive *a)
|
||||
{
|
||||
(void)a; /* UNUSED */
|
||||
return (ARCHIVE_OK);
|
||||
}
|
@ -1,459 +0,0 @@
|
||||
/*-
|
||||
* 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 <sys/wait.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
# include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_FCNTL_H
|
||||
# include <fcntl.h>
|
||||
#endif
|
||||
#ifdef HAVE_LIMITS_H
|
||||
# include <limits.h>
|
||||
#endif
|
||||
#ifdef HAVE_SIGNAL_H
|
||||
# include <signal.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
# include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
# include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_private.h"
|
||||
#include "archive_read_private.h"
|
||||
|
||||
int
|
||||
archive_read_support_compression_program(struct archive *a, const char *cmd)
|
||||
{
|
||||
return (archive_read_support_compression_program_signature(a, cmd, NULL, 0));
|
||||
}
|
||||
|
||||
|
||||
/* This capability is only available on POSIX systems. */
|
||||
#if (!defined(HAVE_PIPE) || !defined(HAVE_FCNTL) || \
|
||||
!(defined(HAVE_FORK) || defined(HAVE_VFORK))) && (!defined(_WIN32) || defined(__CYGWIN__))
|
||||
|
||||
/*
|
||||
* On non-Posix systems, allow the program to build, but choke if
|
||||
* this function is actually invoked.
|
||||
*/
|
||||
int
|
||||
archive_read_support_compression_program_signature(struct archive *_a,
|
||||
const char *cmd, void *signature, size_t signature_len)
|
||||
{
|
||||
(void)_a; /* UNUSED */
|
||||
(void)cmd; /* UNUSED */
|
||||
(void)signature; /* UNUSED */
|
||||
(void)signature_len; /* UNUSED */
|
||||
|
||||
archive_set_error(_a, -1,
|
||||
"External compression programs not supported on this platform");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
int
|
||||
__archive_read_program(struct archive_read_filter *self, const char *cmd)
|
||||
{
|
||||
(void)self; /* UNUSED */
|
||||
(void)cmd; /* UNUSED */
|
||||
|
||||
archive_set_error(&self->archive->archive, -1,
|
||||
"External compression programs not supported on this platform");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include "filter_fork.h"
|
||||
|
||||
/*
|
||||
* The bidder object stores the command and the signature to watch for.
|
||||
* The 'inhibit' entry here is used to ensure that unchecked filters never
|
||||
* bid twice in the same pipeline.
|
||||
*/
|
||||
struct program_bidder {
|
||||
char *cmd;
|
||||
void *signature;
|
||||
size_t signature_len;
|
||||
int inhibit;
|
||||
};
|
||||
|
||||
static int program_bidder_bid(struct archive_read_filter_bidder *,
|
||||
struct archive_read_filter *upstream);
|
||||
static int program_bidder_init(struct archive_read_filter *);
|
||||
static int program_bidder_free(struct archive_read_filter_bidder *);
|
||||
|
||||
/*
|
||||
* The actual filter needs to track input and output data.
|
||||
*/
|
||||
struct program_filter {
|
||||
char *description;
|
||||
pid_t child;
|
||||
int exit_status;
|
||||
int waitpid_return;
|
||||
int child_stdin, child_stdout;
|
||||
|
||||
char *out_buf;
|
||||
size_t out_buf_len;
|
||||
};
|
||||
|
||||
static ssize_t program_filter_read(struct archive_read_filter *,
|
||||
const void **);
|
||||
static int program_filter_close(struct archive_read_filter *);
|
||||
|
||||
int
|
||||
archive_read_support_compression_program_signature(struct archive *_a,
|
||||
const char *cmd, const void *signature, size_t signature_len)
|
||||
{
|
||||
struct archive_read *a = (struct archive_read *)_a;
|
||||
struct archive_read_filter_bidder *bidder;
|
||||
struct program_bidder *state;
|
||||
|
||||
/*
|
||||
* Get a bidder object from the read core.
|
||||
*/
|
||||
bidder = __archive_read_get_bidder(a);
|
||||
if (bidder == NULL)
|
||||
return (ARCHIVE_FATAL);
|
||||
|
||||
/*
|
||||
* Allocate our private state.
|
||||
*/
|
||||
state = (struct program_bidder *)calloc(sizeof (*state), 1);
|
||||
if (state == NULL)
|
||||
return (ARCHIVE_FATAL);
|
||||
state->cmd = strdup(cmd);
|
||||
if (signature != NULL && signature_len > 0) {
|
||||
state->signature_len = signature_len;
|
||||
state->signature = malloc(signature_len);
|
||||
memcpy(state->signature, signature, signature_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill in the bidder object.
|
||||
*/
|
||||
bidder->data = state;
|
||||
bidder->bid = program_bidder_bid;
|
||||
bidder->init = program_bidder_init;
|
||||
bidder->options = NULL;
|
||||
bidder->free = program_bidder_free;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
program_bidder_free(struct archive_read_filter_bidder *self)
|
||||
{
|
||||
struct program_bidder *state = (struct program_bidder *)self->data;
|
||||
free(state->cmd);
|
||||
free(state->signature);
|
||||
free(self->data);
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we do have a signature, bid only if that matches.
|
||||
*
|
||||
* If there's no signature, we bid INT_MAX the first time
|
||||
* we're called, then never bid again.
|
||||
*/
|
||||
static int
|
||||
program_bidder_bid(struct archive_read_filter_bidder *self,
|
||||
struct archive_read_filter *upstream)
|
||||
{
|
||||
struct program_bidder *state = self->data;
|
||||
const char *p;
|
||||
|
||||
/* If we have a signature, use that to match. */
|
||||
if (state->signature_len > 0) {
|
||||
p = __archive_read_filter_ahead(upstream,
|
||||
state->signature_len, NULL);
|
||||
if (p == NULL)
|
||||
return (0);
|
||||
/* No match, so don't bid. */
|
||||
if (memcmp(p, state->signature, state->signature_len) != 0)
|
||||
return (0);
|
||||
return ((int)state->signature_len * 8);
|
||||
}
|
||||
|
||||
/* Otherwise, bid once and then never bid again. */
|
||||
if (state->inhibit)
|
||||
return (0);
|
||||
state->inhibit = 1;
|
||||
return (INT_MAX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Shut down the child, return ARCHIVE_OK if it exited normally.
|
||||
*
|
||||
* Note that the return value is sticky; if we're called again,
|
||||
* we won't reap the child again, but we will return the same status
|
||||
* (including error message if the child came to a bad end).
|
||||
*/
|
||||
static int
|
||||
child_stop(struct archive_read_filter *self, struct program_filter *state)
|
||||
{
|
||||
/* Close our side of the I/O with the child. */
|
||||
if (state->child_stdin != -1) {
|
||||
close(state->child_stdin);
|
||||
state->child_stdin = -1;
|
||||
}
|
||||
if (state->child_stdout != -1) {
|
||||
close(state->child_stdout);
|
||||
state->child_stdout = -1;
|
||||
}
|
||||
|
||||
if (state->child != 0) {
|
||||
/* Reap the child. */
|
||||
do {
|
||||
state->waitpid_return
|
||||
= waitpid(state->child, &state->exit_status, 0);
|
||||
} while (state->waitpid_return == -1 && errno == EINTR);
|
||||
state->child = 0;
|
||||
}
|
||||
|
||||
if (state->waitpid_return < 0) {
|
||||
/* waitpid() failed? This is ugly. */
|
||||
archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Child process exited badly");
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
|
||||
#if !defined(_WIN32) || defined(__CYGWIN__)
|
||||
if (WIFSIGNALED(state->exit_status)) {
|
||||
#ifdef SIGPIPE
|
||||
/* If the child died because we stopped reading before
|
||||
* it was done, that's okay. Some archive formats
|
||||
* have padding at the end that we routinely ignore. */
|
||||
/* The alternative to this would be to add a step
|
||||
* before close(child_stdout) above to read from the
|
||||
* child until the child has no more to write. */
|
||||
if (WTERMSIG(state->exit_status) == SIGPIPE)
|
||||
return (ARCHIVE_OK);
|
||||
#endif
|
||||
archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Child process exited with signal %d",
|
||||
WTERMSIG(state->exit_status));
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
#endif /* !_WIN32 || __CYGWIN__ */
|
||||
|
||||
if (WIFEXITED(state->exit_status)) {
|
||||
if (WEXITSTATUS(state->exit_status) == 0)
|
||||
return (ARCHIVE_OK);
|
||||
|
||||
archive_set_error(&self->archive->archive,
|
||||
ARCHIVE_ERRNO_MISC,
|
||||
"Child process exited with status %d",
|
||||
WEXITSTATUS(state->exit_status));
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
|
||||
/*
|
||||
* Use select() to decide whether the child is ready for read or write.
|
||||
*/
|
||||
static ssize_t
|
||||
child_read(struct archive_read_filter *self, char *buf, size_t buf_len)
|
||||
{
|
||||
struct program_filter *state = self->data;
|
||||
ssize_t ret, requested, avail;
|
||||
const char *p;
|
||||
|
||||
requested = buf_len > SSIZE_MAX ? SSIZE_MAX : buf_len;
|
||||
|
||||
for (;;) {
|
||||
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))
|
||||
/* Child has closed its output; reap the child
|
||||
* and return the status. */
|
||||
return (child_stop(self, state));
|
||||
if (ret == -1 && errno != EAGAIN)
|
||||
return (-1);
|
||||
|
||||
if (state->child_stdin == -1) {
|
||||
/* Block until child has some I/O ready. */
|
||||
__archive_check_child(state->child_stdin,
|
||||
state->child_stdout);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Get some more data from upstream. */
|
||||
p = __archive_read_filter_ahead(self->upstream, 1, &avail);
|
||||
if (p == NULL) {
|
||||
close(state->child_stdin);
|
||||
state->child_stdin = -1;
|
||||
fcntl(state->child_stdout, F_SETFL, 0);
|
||||
if (avail < 0)
|
||||
return (avail);
|
||||
continue;
|
||||
}
|
||||
|
||||
do {
|
||||
ret = write(state->child_stdin, p, avail);
|
||||
} while (ret == -1 && errno == EINTR);
|
||||
|
||||
if (ret > 0) {
|
||||
/* Consume whatever we managed to write. */
|
||||
__archive_read_filter_consume(self->upstream, ret);
|
||||
} else if (ret == -1 && errno == EAGAIN) {
|
||||
/* Block until child has some I/O ready. */
|
||||
__archive_check_child(state->child_stdin,
|
||||
state->child_stdout);
|
||||
} else {
|
||||
/* Write failed. */
|
||||
close(state->child_stdin);
|
||||
state->child_stdin = -1;
|
||||
fcntl(state->child_stdout, F_SETFL, 0);
|
||||
/* If it was a bad error, we're done; otherwise
|
||||
* it was EPIPE or EOF, and we can still read
|
||||
* from the child. */
|
||||
if (ret == -1 && errno != EPIPE)
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
__archive_read_program(struct archive_read_filter *self, const char *cmd)
|
||||
{
|
||||
struct program_filter *state;
|
||||
static const size_t out_buf_len = 65536;
|
||||
char *out_buf;
|
||||
char *description;
|
||||
const char *prefix = "Program: ";
|
||||
|
||||
state = (struct program_filter *)calloc(1, sizeof(*state));
|
||||
out_buf = (char *)malloc(out_buf_len);
|
||||
description = (char *)malloc(strlen(prefix) + strlen(cmd) + 1);
|
||||
if (state == NULL || out_buf == NULL || description == NULL) {
|
||||
archive_set_error(&self->archive->archive, ENOMEM,
|
||||
"Can't allocate input data");
|
||||
free(state);
|
||||
free(out_buf);
|
||||
free(description);
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
self->code = ARCHIVE_COMPRESSION_PROGRAM;
|
||||
state->description = description;
|
||||
strcpy(state->description, prefix);
|
||||
strcat(state->description, cmd);
|
||||
self->name = state->description;
|
||||
|
||||
state->out_buf = out_buf;
|
||||
state->out_buf_len = out_buf_len;
|
||||
|
||||
if ((state->child = __archive_create_child(cmd,
|
||||
&state->child_stdin, &state->child_stdout)) == -1) {
|
||||
free(state->out_buf);
|
||||
free(state);
|
||||
archive_set_error(&self->archive->archive, EINVAL,
|
||||
"Can't initialise filter");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
self->data = state;
|
||||
self->read = program_filter_read;
|
||||
self->skip = NULL;
|
||||
self->close = program_filter_close;
|
||||
|
||||
/* XXX Check that we can read at least one byte? */
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
program_bidder_init(struct archive_read_filter *self)
|
||||
{
|
||||
struct program_bidder *bidder_state;
|
||||
|
||||
bidder_state = (struct program_bidder *)self->bidder->data;
|
||||
return (__archive_read_program(self, bidder_state->cmd));
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
program_filter_read(struct archive_read_filter *self, const void **buff)
|
||||
{
|
||||
struct program_filter *state;
|
||||
ssize_t bytes;
|
||||
size_t total;
|
||||
char *p;
|
||||
|
||||
state = (struct program_filter *)self->data;
|
||||
|
||||
total = 0;
|
||||
p = state->out_buf;
|
||||
while (state->child_stdout != -1 && total < state->out_buf_len) {
|
||||
bytes = child_read(self, p, state->out_buf_len - total);
|
||||
if (bytes < 0)
|
||||
/* No recovery is possible if we can no longer
|
||||
* read from the child. */
|
||||
return (ARCHIVE_FATAL);
|
||||
if (bytes == 0)
|
||||
/* We got EOF from the child. */
|
||||
break;
|
||||
total += bytes;
|
||||
p += bytes;
|
||||
}
|
||||
|
||||
*buff = state->out_buf;
|
||||
return (total);
|
||||
}
|
||||
|
||||
static int
|
||||
program_filter_close(struct archive_read_filter *self)
|
||||
{
|
||||
struct program_filter *state;
|
||||
int e;
|
||||
|
||||
state = (struct program_filter *)self->data;
|
||||
e = child_stop(self, state);
|
||||
|
||||
/* Release our private data. */
|
||||
free(state->out_buf);
|
||||
free(state->description);
|
||||
free(state);
|
||||
|
||||
return (e);
|
||||
}
|
||||
|
||||
#endif /* !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) */
|
@ -1,288 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2009 Michihiro NAKAJIMA
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "archive_platform.h"
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_endian.h"
|
||||
#include "archive_private.h"
|
||||
#include "archive_read_private.h"
|
||||
|
||||
struct rpm {
|
||||
int64_t total_in;
|
||||
size_t hpos;
|
||||
size_t hlen;
|
||||
unsigned char header[16];
|
||||
enum {
|
||||
ST_LEAD, /* Skipping 'Lead' section. */
|
||||
ST_HEADER, /* Reading 'Header' section;
|
||||
* first 16 bytes. */
|
||||
ST_HEADER_DATA, /* Skipping 'Header' section. */
|
||||
ST_PADDING, /* Skipping padding data after the
|
||||
* 'Header' section. */
|
||||
ST_ARCHIVE /* Reading 'Archive' section. */
|
||||
} state;
|
||||
int first_header;
|
||||
};
|
||||
#define RPM_LEAD_SIZE 96 /* Size of 'Lead' section. */
|
||||
|
||||
static int rpm_bidder_bid(struct archive_read_filter_bidder *,
|
||||
struct archive_read_filter *);
|
||||
static int rpm_bidder_init(struct archive_read_filter *);
|
||||
|
||||
static ssize_t rpm_filter_read(struct archive_read_filter *,
|
||||
const void **);
|
||||
static int rpm_filter_close(struct archive_read_filter *);
|
||||
|
||||
int
|
||||
archive_read_support_compression_rpm(struct archive *_a)
|
||||
{
|
||||
struct archive_read *a = (struct archive_read *)_a;
|
||||
struct archive_read_filter_bidder *bidder;
|
||||
|
||||
bidder = __archive_read_get_bidder(a);
|
||||
archive_clear_error(_a);
|
||||
if (bidder == NULL)
|
||||
return (ARCHIVE_FATAL);
|
||||
|
||||
bidder->data = NULL;
|
||||
bidder->bid = rpm_bidder_bid;
|
||||
bidder->init = rpm_bidder_init;
|
||||
bidder->options = NULL;
|
||||
bidder->free = NULL;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
rpm_bidder_bid(struct archive_read_filter_bidder *self,
|
||||
struct archive_read_filter *filter)
|
||||
{
|
||||
const unsigned char *b;
|
||||
ssize_t avail;
|
||||
int bits_checked;
|
||||
|
||||
(void)self; /* UNUSED */
|
||||
|
||||
b = __archive_read_filter_ahead(filter, 8, &avail);
|
||||
if (b == NULL)
|
||||
return (0);
|
||||
|
||||
bits_checked = 0;
|
||||
/*
|
||||
* Verify Header Magic Bytes : 0xed 0xab 0xee 0xdb
|
||||
*/
|
||||
if (b[0] != 0xed)
|
||||
return (0);
|
||||
bits_checked += 8;
|
||||
if (b[1] != 0xab)
|
||||
return (0);
|
||||
bits_checked += 8;
|
||||
if (b[2] != 0xee)
|
||||
return (0);
|
||||
bits_checked += 8;
|
||||
if (b[3] != 0xdb)
|
||||
return (0);
|
||||
bits_checked += 8;
|
||||
/*
|
||||
* Check major version.
|
||||
*/
|
||||
if (b[4] != 3 && b[4] != 4)
|
||||
return (0);
|
||||
bits_checked += 8;
|
||||
/*
|
||||
* Check package type; binary or source.
|
||||
*/
|
||||
if (b[6] != 0)
|
||||
return (0);
|
||||
bits_checked += 8;
|
||||
if (b[7] != 0 && b[7] != 1)
|
||||
return (0);
|
||||
bits_checked += 8;
|
||||
|
||||
return (bits_checked);
|
||||
}
|
||||
|
||||
static int
|
||||
rpm_bidder_init(struct archive_read_filter *self)
|
||||
{
|
||||
struct rpm *rpm;
|
||||
|
||||
self->code = ARCHIVE_COMPRESSION_RPM;
|
||||
self->name = "rpm";
|
||||
self->read = rpm_filter_read;
|
||||
self->skip = NULL; /* not supported */
|
||||
self->close = rpm_filter_close;
|
||||
|
||||
rpm = (struct rpm *)calloc(sizeof(*rpm), 1);
|
||||
if (rpm == NULL) {
|
||||
archive_set_error(&self->archive->archive, ENOMEM,
|
||||
"Can't allocate data for rpm");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
self->data = rpm;
|
||||
rpm->state = ST_LEAD;
|
||||
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
rpm_filter_read(struct archive_read_filter *self, const void **buff)
|
||||
{
|
||||
struct rpm *rpm;
|
||||
const unsigned char *b;
|
||||
ssize_t avail_in, total;
|
||||
size_t used, n;
|
||||
uint32_t section;
|
||||
uint32_t bytes;
|
||||
|
||||
rpm = (struct rpm *)self->data;
|
||||
*buff = NULL;
|
||||
total = avail_in = 0;
|
||||
b = NULL;
|
||||
used = 0;
|
||||
do {
|
||||
if (b == NULL) {
|
||||
b = __archive_read_filter_ahead(self->upstream, 1,
|
||||
&avail_in);
|
||||
if (b == NULL) {
|
||||
if (avail_in < 0)
|
||||
return (ARCHIVE_FATAL);
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (rpm->state) {
|
||||
case ST_LEAD:
|
||||
if (rpm->total_in + avail_in < RPM_LEAD_SIZE)
|
||||
used += avail_in;
|
||||
else {
|
||||
n = RPM_LEAD_SIZE - rpm->total_in;
|
||||
used += n;
|
||||
b += n;
|
||||
rpm->state = ST_HEADER;
|
||||
rpm->hpos = 0;
|
||||
rpm->hlen = 0;
|
||||
rpm->first_header = 1;
|
||||
}
|
||||
break;
|
||||
case ST_HEADER:
|
||||
n = 16 - rpm->hpos;
|
||||
if (n > avail_in - used)
|
||||
n = avail_in - used;
|
||||
memcpy(rpm->header+rpm->hpos, b, n);
|
||||
b += n;
|
||||
used += n;
|
||||
rpm->hpos += n;
|
||||
|
||||
if (rpm->hpos == 16) {
|
||||
if (rpm->header[0] != 0x8e ||
|
||||
rpm->header[1] != 0xad ||
|
||||
rpm->header[2] != 0xe8 ||
|
||||
rpm->header[3] != 0x01) {
|
||||
if (rpm->first_header) {
|
||||
archive_set_error(
|
||||
&self->archive->archive,
|
||||
ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Unrecoginized rpm header");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
rpm->state = ST_ARCHIVE;
|
||||
*buff = rpm->header;
|
||||
total = rpm->hpos;
|
||||
break;
|
||||
}
|
||||
/* Calculate 'Header' length. */
|
||||
section = archive_be32dec(rpm->header+8);
|
||||
bytes = archive_be32dec(rpm->header+12);
|
||||
rpm->hlen = 16 + section * 16 + bytes;
|
||||
rpm->state = ST_HEADER_DATA;
|
||||
rpm->first_header = 0;
|
||||
}
|
||||
break;
|
||||
case ST_HEADER_DATA:
|
||||
n = rpm->hlen - rpm->hpos;
|
||||
if (n > avail_in - used)
|
||||
n = avail_in - used;
|
||||
b += n;
|
||||
used += n;
|
||||
rpm->hpos += n;
|
||||
if (rpm->hpos == rpm->hlen)
|
||||
rpm->state = ST_PADDING;
|
||||
break;
|
||||
case ST_PADDING:
|
||||
while (used < (size_t)avail_in) {
|
||||
if (*b != 0) {
|
||||
/* Read next header. */
|
||||
rpm->state = ST_HEADER;
|
||||
rpm->hpos = 0;
|
||||
rpm->hlen = 0;
|
||||
break;
|
||||
}
|
||||
b++;
|
||||
used++;
|
||||
}
|
||||
break;
|
||||
case ST_ARCHIVE:
|
||||
*buff = b;
|
||||
total = avail_in;
|
||||
used = avail_in;
|
||||
break;
|
||||
}
|
||||
if (used == (size_t)avail_in) {
|
||||
rpm->total_in += used;
|
||||
__archive_read_filter_consume(self->upstream, used);
|
||||
b = NULL;
|
||||
used = 0;
|
||||
}
|
||||
} while (total == 0 && avail_in > 0);
|
||||
|
||||
if (used > 0 && b != NULL) {
|
||||
rpm->total_in += used;
|
||||
__archive_read_filter_consume(self->upstream, used);
|
||||
}
|
||||
return (total);
|
||||
}
|
||||
|
||||
static int
|
||||
rpm_filter_close(struct archive_read_filter *self)
|
||||
{
|
||||
struct rpm *rpm;
|
||||
|
||||
rpm = (struct rpm *)self->data;
|
||||
free(rpm);
|
||||
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
@ -1,637 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2009 Michihiro NAKAJIMA
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "archive_platform.h"
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_private.h"
|
||||
#include "archive_read_private.h"
|
||||
|
||||
struct uudecode {
|
||||
int64_t total;
|
||||
unsigned char *in_buff;
|
||||
#define IN_BUFF_SIZE (1024)
|
||||
int in_cnt;
|
||||
size_t in_allocated;
|
||||
unsigned char *out_buff;
|
||||
#define OUT_BUFF_SIZE (64 * 1024)
|
||||
int state;
|
||||
#define ST_FIND_HEAD 0
|
||||
#define ST_READ_UU 1
|
||||
#define ST_UUEND 2
|
||||
#define ST_READ_BASE64 3
|
||||
};
|
||||
|
||||
static int uudecode_bidder_bid(struct archive_read_filter_bidder *,
|
||||
struct archive_read_filter *filter);
|
||||
static int uudecode_bidder_init(struct archive_read_filter *);
|
||||
|
||||
static ssize_t uudecode_filter_read(struct archive_read_filter *,
|
||||
const void **);
|
||||
static int uudecode_filter_close(struct archive_read_filter *);
|
||||
|
||||
int
|
||||
archive_read_support_compression_uu(struct archive *_a)
|
||||
{
|
||||
struct archive_read *a = (struct archive_read *)_a;
|
||||
struct archive_read_filter_bidder *bidder;
|
||||
|
||||
bidder = __archive_read_get_bidder(a);
|
||||
archive_clear_error(_a);
|
||||
if (bidder == NULL)
|
||||
return (ARCHIVE_FATAL);
|
||||
|
||||
bidder->data = NULL;
|
||||
bidder->bid = uudecode_bidder_bid;
|
||||
bidder->init = uudecode_bidder_init;
|
||||
bidder->options = NULL;
|
||||
bidder->free = NULL;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static const unsigned char ascii[256] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\n', 0, 0, '\r', 0, 0, /* 00 - 0F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 - 3F */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 70 - 7F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
|
||||
};
|
||||
|
||||
static const unsigned char uuchar[256] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 - 3F */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */
|
||||
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 70 - 7F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
|
||||
};
|
||||
|
||||
static const unsigned char base64[256] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, /* 20 - 2F */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, /* 30 - 3F */
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 50 - 5F */
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 70 - 7F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
|
||||
};
|
||||
|
||||
static const int base64num[128] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 62, 0, 0, 0, 63, /* 20 - 2F */
|
||||
52, 53, 54, 55, 56, 57, 58, 59,
|
||||
60, 61, 0, 0, 0, 0, 0, 0, /* 30 - 3F */
|
||||
0, 0, 1, 2, 3, 4, 5, 6,
|
||||
7, 8, 9, 10, 11, 12, 13, 14, /* 40 - 4F */
|
||||
15, 16, 17, 18, 19, 20, 21, 22,
|
||||
23, 24, 25, 0, 0, 0, 0, 0, /* 50 - 5F */
|
||||
0, 26, 27, 28, 29, 30, 31, 32,
|
||||
33, 34, 35, 36, 37, 38, 39, 40, /* 60 - 6F */
|
||||
41, 42, 43, 44, 45, 46, 47, 48,
|
||||
49, 50, 51, 0, 0, 0, 0, 0, /* 70 - 7F */
|
||||
};
|
||||
|
||||
static ssize_t
|
||||
get_line(const unsigned char *b, ssize_t avail, ssize_t *nlsize)
|
||||
{
|
||||
ssize_t len;
|
||||
|
||||
len = 0;
|
||||
while (len < avail) {
|
||||
switch (ascii[*b]) {
|
||||
case 0: /* Non-ascii character or control character. */
|
||||
if (nlsize != NULL)
|
||||
*nlsize = 0;
|
||||
return (-1);
|
||||
case '\r':
|
||||
if (avail-len > 1 && b[1] == '\n') {
|
||||
if (nlsize != NULL)
|
||||
*nlsize = 2;
|
||||
return (len+2);
|
||||
}
|
||||
/* FALL THROUGH */
|
||||
case '\n':
|
||||
if (nlsize != NULL)
|
||||
*nlsize = 1;
|
||||
return (len+1);
|
||||
case 1:
|
||||
b++;
|
||||
len++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (nlsize != NULL)
|
||||
*nlsize = 0;
|
||||
return (avail);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
bid_get_line(struct archive_read_filter *filter,
|
||||
const unsigned char **b, ssize_t *avail, ssize_t *ravail, ssize_t *nl)
|
||||
{
|
||||
ssize_t len;
|
||||
int quit;
|
||||
|
||||
quit = 0;
|
||||
if (*avail == 0) {
|
||||
*nl = 0;
|
||||
len = 0;
|
||||
} else
|
||||
len = get_line(*b, *avail, nl);
|
||||
/*
|
||||
* Read bytes more while it does not reach the end of line.
|
||||
*/
|
||||
while (*nl == 0 && len == *avail && !quit) {
|
||||
ssize_t diff = *ravail - *avail;
|
||||
|
||||
*b = __archive_read_filter_ahead(filter, 160 + *ravail, avail);
|
||||
if (*b == NULL) {
|
||||
if (*ravail >= *avail)
|
||||
return (0);
|
||||
/* Reading bytes reaches the end of file. */
|
||||
*b = __archive_read_filter_ahead(filter, *avail, avail);
|
||||
quit = 1;
|
||||
}
|
||||
*ravail = *avail;
|
||||
*b += diff;
|
||||
*avail -= diff;
|
||||
len = get_line(*b, *avail, nl);
|
||||
}
|
||||
return (len);
|
||||
}
|
||||
|
||||
#define UUDECODE(c) (((c) - 0x20) & 0x3f)
|
||||
|
||||
static int
|
||||
uudecode_bidder_bid(struct archive_read_filter_bidder *self,
|
||||
struct archive_read_filter *filter)
|
||||
{
|
||||
const unsigned char *b;
|
||||
ssize_t avail, ravail;
|
||||
ssize_t len, nl;
|
||||
int l;
|
||||
int firstline;
|
||||
|
||||
(void)self; /* UNUSED */
|
||||
|
||||
b = __archive_read_filter_ahead(filter, 1, &avail);
|
||||
if (b == NULL)
|
||||
return (0);
|
||||
|
||||
firstline = 20;
|
||||
ravail = avail;
|
||||
for (;;) {
|
||||
len = bid_get_line(filter, &b, &avail, &ravail, &nl);
|
||||
if (len < 0 || nl == 0)
|
||||
return (0);/* Binary data. */
|
||||
if (memcmp(b, "begin ", 6) == 0 && len - nl >= 11)
|
||||
l = 6;
|
||||
else if (memcmp(b, "begin-base64 ", 13) == 0 && len - nl >= 18)
|
||||
l = 13;
|
||||
else
|
||||
l = 0;
|
||||
|
||||
if (l > 0 && (b[l] < '0' || b[l] > '7' ||
|
||||
b[l+1] < '0' || b[l+1] > '7' ||
|
||||
b[l+2] < '0' || b[l+2] > '7' || b[l+3] != ' '))
|
||||
l = 0;
|
||||
|
||||
b += len;
|
||||
avail -= len;
|
||||
if (l)
|
||||
break;
|
||||
firstline = 0;
|
||||
}
|
||||
if (!avail)
|
||||
return (0);
|
||||
len = bid_get_line(filter, &b, &avail, &ravail, &nl);
|
||||
if (len < 0 || nl == 0)
|
||||
return (0);/* There are non-ascii characters. */
|
||||
avail -= len;
|
||||
|
||||
if (l == 6) {
|
||||
if (!uuchar[*b])
|
||||
return (0);
|
||||
/* Get a length of decoded bytes. */
|
||||
l = UUDECODE(*b++); len--;
|
||||
if (l > 45)
|
||||
/* Normally, maximum length is 45(character 'M'). */
|
||||
return (0);
|
||||
while (l && len-nl > 0) {
|
||||
if (l > 0) {
|
||||
if (!uuchar[*b++])
|
||||
return (0);
|
||||
if (!uuchar[*b++])
|
||||
return (0);
|
||||
len -= 2;
|
||||
--l;
|
||||
}
|
||||
if (l > 0) {
|
||||
if (!uuchar[*b++])
|
||||
return (0);
|
||||
--len;
|
||||
--l;
|
||||
}
|
||||
if (l > 0) {
|
||||
if (!uuchar[*b++])
|
||||
return (0);
|
||||
--len;
|
||||
--l;
|
||||
}
|
||||
}
|
||||
if (len-nl < 0)
|
||||
return (0);
|
||||
if (len-nl == 1 &&
|
||||
(uuchar[*b] || /* Check sum. */
|
||||
(*b >= 'a' && *b <= 'z'))) {/* Padding data(MINIX). */
|
||||
++b;
|
||||
--len;
|
||||
}
|
||||
b += nl;
|
||||
if (avail && uuchar[*b])
|
||||
return (firstline+30);
|
||||
}
|
||||
if (l == 13) {
|
||||
while (len-nl > 0) {
|
||||
if (!base64[*b++])
|
||||
return (0);
|
||||
--len;
|
||||
}
|
||||
b += nl;
|
||||
|
||||
if (avail >= 5 && memcmp(b, "====\n", 5) == 0)
|
||||
return (firstline+40);
|
||||
if (avail >= 6 && memcmp(b, "====\r\n", 6) == 0)
|
||||
return (firstline+40);
|
||||
if (avail > 0 && base64[*b])
|
||||
return (firstline+30);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
uudecode_bidder_init(struct archive_read_filter *self)
|
||||
{
|
||||
struct uudecode *uudecode;
|
||||
void *out_buff;
|
||||
void *in_buff;
|
||||
|
||||
self->code = ARCHIVE_COMPRESSION_UU;
|
||||
self->name = "uu";
|
||||
self->read = uudecode_filter_read;
|
||||
self->skip = NULL; /* not supported */
|
||||
self->close = uudecode_filter_close;
|
||||
|
||||
uudecode = (struct uudecode *)calloc(sizeof(*uudecode), 1);
|
||||
out_buff = malloc(OUT_BUFF_SIZE);
|
||||
in_buff = malloc(IN_BUFF_SIZE);
|
||||
if (uudecode == NULL || out_buff == NULL || in_buff == NULL) {
|
||||
archive_set_error(&self->archive->archive, ENOMEM,
|
||||
"Can't allocate data for uudecode");
|
||||
free(uudecode);
|
||||
free(out_buff);
|
||||
free(in_buff);
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
self->data = uudecode;
|
||||
uudecode->in_buff = in_buff;
|
||||
uudecode->in_cnt = 0;
|
||||
uudecode->in_allocated = IN_BUFF_SIZE;
|
||||
uudecode->out_buff = out_buff;
|
||||
uudecode->state = ST_FIND_HEAD;
|
||||
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
ensure_in_buff_size(struct archive_read_filter *self,
|
||||
struct uudecode *uudecode, size_t size)
|
||||
{
|
||||
|
||||
if (size > uudecode->in_allocated) {
|
||||
unsigned char *ptr;
|
||||
size_t newsize;
|
||||
|
||||
/*
|
||||
* Calculate a new buffer size for in_buff.
|
||||
* Increase its value until it has enough size we need.
|
||||
*/
|
||||
newsize = uudecode->in_allocated;
|
||||
do {
|
||||
if (newsize < IN_BUFF_SIZE*32)
|
||||
newsize <<= 1;
|
||||
else
|
||||
newsize += IN_BUFF_SIZE;
|
||||
} while (size > newsize);
|
||||
ptr = malloc(newsize);
|
||||
if (ptr == NULL ||
|
||||
newsize < uudecode->in_allocated) {
|
||||
free(ptr);
|
||||
archive_set_error(&self->archive->archive,
|
||||
ENOMEM,
|
||||
"Can't allocate data for uudecode");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
if (uudecode->in_cnt)
|
||||
memmove(ptr, uudecode->in_buff,
|
||||
uudecode->in_cnt);
|
||||
free(uudecode->in_buff);
|
||||
uudecode->in_buff = ptr;
|
||||
uudecode->in_allocated = newsize;
|
||||
}
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
uudecode_filter_read(struct archive_read_filter *self, const void **buff)
|
||||
{
|
||||
struct uudecode *uudecode;
|
||||
const unsigned char *b, *d;
|
||||
unsigned char *out;
|
||||
ssize_t avail_in, ravail;
|
||||
ssize_t used;
|
||||
ssize_t total;
|
||||
ssize_t len, llen, nl;
|
||||
|
||||
uudecode = (struct uudecode *)self->data;
|
||||
|
||||
read_more:
|
||||
d = __archive_read_filter_ahead(self->upstream, 1, &avail_in);
|
||||
if (d == NULL && avail_in < 0)
|
||||
return (ARCHIVE_FATAL);
|
||||
/* Quiet a code analyzer; make sure avail_in must be zero
|
||||
* when d is NULL. */
|
||||
if (d == NULL)
|
||||
avail_in = 0;
|
||||
used = 0;
|
||||
total = 0;
|
||||
out = uudecode->out_buff;
|
||||
ravail = avail_in;
|
||||
if (uudecode->in_cnt) {
|
||||
/*
|
||||
* If there is remaining data which is saved by
|
||||
* previous calling, use it first.
|
||||
*/
|
||||
if (ensure_in_buff_size(self, uudecode,
|
||||
avail_in + uudecode->in_cnt) != ARCHIVE_OK)
|
||||
return (ARCHIVE_FATAL);
|
||||
memcpy(uudecode->in_buff + uudecode->in_cnt,
|
||||
d, avail_in);
|
||||
d = uudecode->in_buff;
|
||||
avail_in += uudecode->in_cnt;
|
||||
uudecode->in_cnt = 0;
|
||||
}
|
||||
for (;used < avail_in; d += llen, used += llen) {
|
||||
int l, body;
|
||||
|
||||
b = d;
|
||||
len = get_line(b, avail_in - used, &nl);
|
||||
if (len < 0) {
|
||||
/* Non-ascii character is found. */
|
||||
archive_set_error(&self->archive->archive,
|
||||
ARCHIVE_ERRNO_MISC,
|
||||
"Insufficient compressed data");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
llen = len;
|
||||
if (nl == 0) {
|
||||
/*
|
||||
* Save remaining data which does not contain
|
||||
* NL('\n','\r').
|
||||
*/
|
||||
if (ensure_in_buff_size(self, uudecode, len)
|
||||
!= ARCHIVE_OK)
|
||||
return (ARCHIVE_FATAL);
|
||||
if (uudecode->in_buff != b)
|
||||
memmove(uudecode->in_buff, b, len);
|
||||
uudecode->in_cnt = len;
|
||||
if (total == 0) {
|
||||
/* Do not return 0; it means end-of-file.
|
||||
* We should try to read bytes more. */
|
||||
__archive_read_filter_consume(
|
||||
self->upstream, ravail);
|
||||
goto read_more;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (total + len * 2 > OUT_BUFF_SIZE)
|
||||
break;
|
||||
switch (uudecode->state) {
|
||||
default:
|
||||
case ST_FIND_HEAD:
|
||||
if (len - nl >= 11 && memcmp(b, "begin ", 6) == 0)
|
||||
l = 6;
|
||||
else if (len - nl >= 18 &&
|
||||
memcmp(b, "begin-base64 ", 13) == 0)
|
||||
l = 13;
|
||||
else
|
||||
l = 0;
|
||||
if (l != 0 && b[l] >= '0' && b[l] <= '7' &&
|
||||
b[l+1] >= '0' && b[l+1] <= '7' &&
|
||||
b[l+2] >= '0' && b[l+2] <= '7' && b[l+3] == ' ') {
|
||||
if (l == 6)
|
||||
uudecode->state = ST_READ_UU;
|
||||
else
|
||||
uudecode->state = ST_READ_BASE64;
|
||||
}
|
||||
break;
|
||||
case ST_READ_UU:
|
||||
body = len - nl;
|
||||
if (!uuchar[*b] || body <= 0) {
|
||||
archive_set_error(&self->archive->archive,
|
||||
ARCHIVE_ERRNO_MISC,
|
||||
"Insufficient compressed data");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
/* Get length of undecoded bytes of curent line. */
|
||||
l = UUDECODE(*b++);
|
||||
body--;
|
||||
if (l > body) {
|
||||
archive_set_error(&self->archive->archive,
|
||||
ARCHIVE_ERRNO_MISC,
|
||||
"Insufficient compressed data");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
if (l == 0) {
|
||||
uudecode->state = ST_UUEND;
|
||||
break;
|
||||
}
|
||||
while (l > 0) {
|
||||
int n = 0;
|
||||
|
||||
if (l > 0) {
|
||||
if (!uuchar[b[0]] || !uuchar[b[1]])
|
||||
break;
|
||||
n = UUDECODE(*b++) << 18;
|
||||
n |= UUDECODE(*b++) << 12;
|
||||
*out++ = n >> 16; total++;
|
||||
--l;
|
||||
}
|
||||
if (l > 0) {
|
||||
if (!uuchar[b[0]])
|
||||
break;
|
||||
n |= UUDECODE(*b++) << 6;
|
||||
*out++ = (n >> 8) & 0xFF; total++;
|
||||
--l;
|
||||
}
|
||||
if (l > 0) {
|
||||
if (!uuchar[b[0]])
|
||||
break;
|
||||
n |= UUDECODE(*b++);
|
||||
*out++ = n & 0xFF; total++;
|
||||
--l;
|
||||
}
|
||||
}
|
||||
if (l) {
|
||||
archive_set_error(&self->archive->archive,
|
||||
ARCHIVE_ERRNO_MISC,
|
||||
"Insufficient compressed data");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
break;
|
||||
case ST_UUEND:
|
||||
if (len - nl == 3 && memcmp(b, "end ", 3) == 0)
|
||||
uudecode->state = ST_FIND_HEAD;
|
||||
else {
|
||||
archive_set_error(&self->archive->archive,
|
||||
ARCHIVE_ERRNO_MISC,
|
||||
"Insufficient compressed data");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
break;
|
||||
case ST_READ_BASE64:
|
||||
l = len - nl;
|
||||
if (l >= 3 && b[0] == '=' && b[1] == '=' &&
|
||||
b[2] == '=') {
|
||||
uudecode->state = ST_FIND_HEAD;
|
||||
break;
|
||||
}
|
||||
while (l > 0) {
|
||||
int n = 0;
|
||||
|
||||
if (l > 0) {
|
||||
if (!base64[b[0]] || !base64[b[1]])
|
||||
break;
|
||||
n = base64num[*b++] << 18;
|
||||
n |= base64num[*b++] << 12;
|
||||
*out++ = n >> 16; total++;
|
||||
l -= 2;
|
||||
}
|
||||
if (l > 0) {
|
||||
if (*b == '=')
|
||||
break;
|
||||
if (!base64[*b])
|
||||
break;
|
||||
n |= base64num[*b++] << 6;
|
||||
*out++ = (n >> 8) & 0xFF; total++;
|
||||
--l;
|
||||
}
|
||||
if (l > 0) {
|
||||
if (*b == '=')
|
||||
break;
|
||||
if (!base64[*b])
|
||||
break;
|
||||
n |= base64num[*b++];
|
||||
*out++ = n & 0xFF; total++;
|
||||
--l;
|
||||
}
|
||||
}
|
||||
if (l && *b != '=') {
|
||||
archive_set_error(&self->archive->archive,
|
||||
ARCHIVE_ERRNO_MISC,
|
||||
"Insufficient compressed data");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
__archive_read_filter_consume(self->upstream, ravail);
|
||||
|
||||
*buff = uudecode->out_buff;
|
||||
uudecode->total += total;
|
||||
return (total);
|
||||
}
|
||||
|
||||
static int
|
||||
uudecode_filter_close(struct archive_read_filter *self)
|
||||
{
|
||||
struct uudecode *uudecode;
|
||||
|
||||
uudecode = (struct uudecode *)self->data;
|
||||
free(uudecode->in_buff);
|
||||
free(uudecode->out_buff);
|
||||
free(uudecode);
|
||||
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
@ -1,708 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2009 Michihiro NAKAJIMA
|
||||
* Copyright (c) 2003-2008 Tim Kientzle and Miklos Vajna
|
||||
* 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_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#if HAVE_LZMA_H
|
||||
#include <lzma.h>
|
||||
#elif HAVE_LZMADEC_H
|
||||
#include <lzmadec.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_endian.h"
|
||||
#include "archive_private.h"
|
||||
#include "archive_read_private.h"
|
||||
|
||||
#if HAVE_LZMA_H && HAVE_LIBLZMA
|
||||
|
||||
struct private_data {
|
||||
lzma_stream stream;
|
||||
unsigned char *out_block;
|
||||
size_t out_block_size;
|
||||
int64_t total_out;
|
||||
char eof; /* True = found end of compressed data. */
|
||||
};
|
||||
|
||||
/* Combined lzma/xz filter */
|
||||
static ssize_t xz_filter_read(struct archive_read_filter *, const void **);
|
||||
static int xz_filter_close(struct archive_read_filter *);
|
||||
static int xz_lzma_bidder_init(struct archive_read_filter *);
|
||||
|
||||
#elif HAVE_LZMADEC_H && HAVE_LIBLZMADEC
|
||||
|
||||
struct private_data {
|
||||
lzmadec_stream stream;
|
||||
unsigned char *out_block;
|
||||
size_t out_block_size;
|
||||
int64_t total_out;
|
||||
char eof; /* True = found end of compressed data. */
|
||||
};
|
||||
|
||||
/* Lzma-only filter */
|
||||
static ssize_t lzma_filter_read(struct archive_read_filter *, const void **);
|
||||
static int lzma_filter_close(struct archive_read_filter *);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Note that we can detect xz and lzma compressed files even if we
|
||||
* can't decompress them. (In fact, we like detecting them because we
|
||||
* can give better error messages.) So the bid framework here gets
|
||||
* compiled even if no lzma library is available.
|
||||
*/
|
||||
static int xz_bidder_bid(struct archive_read_filter_bidder *,
|
||||
struct archive_read_filter *);
|
||||
static int xz_bidder_init(struct archive_read_filter *);
|
||||
static int lzma_bidder_bid(struct archive_read_filter_bidder *,
|
||||
struct archive_read_filter *);
|
||||
static int lzma_bidder_init(struct archive_read_filter *);
|
||||
|
||||
int
|
||||
archive_read_support_compression_xz(struct archive *_a)
|
||||
{
|
||||
struct archive_read *a = (struct archive_read *)_a;
|
||||
struct archive_read_filter_bidder *bidder = __archive_read_get_bidder(a);
|
||||
|
||||
archive_clear_error(_a);
|
||||
if (bidder == NULL)
|
||||
return (ARCHIVE_FATAL);
|
||||
|
||||
bidder->data = NULL;
|
||||
bidder->bid = xz_bidder_bid;
|
||||
bidder->init = xz_bidder_init;
|
||||
bidder->options = NULL;
|
||||
bidder->free = NULL;
|
||||
#if HAVE_LZMA_H && HAVE_LIBLZMA
|
||||
return (ARCHIVE_OK);
|
||||
#else
|
||||
archive_set_error(_a, ARCHIVE_ERRNO_MISC,
|
||||
"Using external unxz program for xz decompression");
|
||||
return (ARCHIVE_WARN);
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
archive_read_support_compression_lzma(struct archive *_a)
|
||||
{
|
||||
struct archive_read *a = (struct archive_read *)_a;
|
||||
struct archive_read_filter_bidder *bidder = __archive_read_get_bidder(a);
|
||||
|
||||
archive_clear_error(_a);
|
||||
if (bidder == NULL)
|
||||
return (ARCHIVE_FATAL);
|
||||
|
||||
bidder->data = NULL;
|
||||
bidder->bid = lzma_bidder_bid;
|
||||
bidder->init = lzma_bidder_init;
|
||||
bidder->options = NULL;
|
||||
bidder->free = NULL;
|
||||
#if HAVE_LZMA_H && HAVE_LIBLZMA
|
||||
return (ARCHIVE_OK);
|
||||
#elif HAVE_LZMADEC_H && HAVE_LIBLZMADEC
|
||||
return (ARCHIVE_OK);
|
||||
#else
|
||||
archive_set_error(_a, ARCHIVE_ERRNO_MISC,
|
||||
"Using external unlzma program for lzma decompression");
|
||||
return (ARCHIVE_WARN);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Test whether we can handle this data.
|
||||
*/
|
||||
static int
|
||||
xz_bidder_bid(struct archive_read_filter_bidder *self,
|
||||
struct archive_read_filter *filter)
|
||||
{
|
||||
const unsigned char *buffer;
|
||||
ssize_t avail;
|
||||
int bits_checked;
|
||||
|
||||
(void)self; /* UNUSED */
|
||||
|
||||
buffer = __archive_read_filter_ahead(filter, 6, &avail);
|
||||
if (buffer == NULL)
|
||||
return (0);
|
||||
|
||||
/*
|
||||
* Verify Header Magic Bytes : FD 37 7A 58 5A 00
|
||||
*/
|
||||
bits_checked = 0;
|
||||
if (buffer[0] != 0xFD)
|
||||
return (0);
|
||||
bits_checked += 8;
|
||||
if (buffer[1] != 0x37)
|
||||
return (0);
|
||||
bits_checked += 8;
|
||||
if (buffer[2] != 0x7A)
|
||||
return (0);
|
||||
bits_checked += 8;
|
||||
if (buffer[3] != 0x58)
|
||||
return (0);
|
||||
bits_checked += 8;
|
||||
if (buffer[4] != 0x5A)
|
||||
return (0);
|
||||
bits_checked += 8;
|
||||
if (buffer[5] != 0x00)
|
||||
return (0);
|
||||
bits_checked += 8;
|
||||
|
||||
return (bits_checked);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test whether we can handle this data.
|
||||
*
|
||||
* <sigh> LZMA has a rather poor file signature. Zeros do not
|
||||
* make good signature bytes as a rule, and the only non-zero byte
|
||||
* here is an ASCII character. For example, an uncompressed tar
|
||||
* archive whose first file is ']' would satisfy this check. It may
|
||||
* be necessary to exclude LZMA from compression_all() because of
|
||||
* this. Clients of libarchive would then have to explicitly enable
|
||||
* LZMA checking instead of (or in addition to) compression_all() when
|
||||
* they have other evidence (file name, command-line option) to go on.
|
||||
*/
|
||||
static int
|
||||
lzma_bidder_bid(struct archive_read_filter_bidder *self,
|
||||
struct archive_read_filter *filter)
|
||||
{
|
||||
const unsigned char *buffer;
|
||||
ssize_t avail;
|
||||
uint32_t dicsize;
|
||||
uint64_t uncompressed_size;
|
||||
int bits_checked;
|
||||
|
||||
(void)self; /* UNUSED */
|
||||
|
||||
buffer = __archive_read_filter_ahead(filter, 14, &avail);
|
||||
if (buffer == NULL)
|
||||
return (0);
|
||||
|
||||
/* First byte of raw LZMA stream is commonly 0x5d.
|
||||
* The first byte is a special number, which consists of
|
||||
* three parameters of LZMA compression, a number of literal
|
||||
* context bits(which is from 0 to 8, default is 3), a number
|
||||
* of literal pos bits(which is from 0 to 4, default is 0),
|
||||
* a number of pos bits(which is from 0 to 4, default is 2).
|
||||
* The first byte is made by
|
||||
* (pos bits * 5 + literal pos bit) * 9 + * literal contest bit,
|
||||
* and so the default value in this field is
|
||||
* (2 * 5 + 0) * 9 + 3 = 0x5d.
|
||||
* lzma of LZMA SDK has options to change those parameters.
|
||||
* It means a range of this field is from 0 to 224. And lzma of
|
||||
* XZ Utils with option -e records 0x5e in this field. */
|
||||
/* NOTE: If this checking of the first byte increases false
|
||||
* recognition, we should allow only 0x5d and 0x5e for the first
|
||||
* byte of LZMA stream. */
|
||||
bits_checked = 0;
|
||||
if (buffer[0] > (4 * 5 + 4) * 9 + 8)
|
||||
return (0);
|
||||
/* Most likely value in the first byte of LZMA stream. */
|
||||
if (buffer[0] == 0x5d || buffer[0] == 0x5e)
|
||||
bits_checked += 8;
|
||||
|
||||
/* Sixth through fourteenth bytes are uncompressed size,
|
||||
* stored in little-endian order. `-1' means uncompressed
|
||||
* size is unknown and lzma of XZ Utils always records `-1'
|
||||
* in this field. */
|
||||
uncompressed_size = archive_le64dec(buffer+5);
|
||||
if (uncompressed_size == (uint64_t)ARCHIVE_LITERAL_LL(-1))
|
||||
bits_checked += 64;
|
||||
|
||||
/* Second through fifth bytes are dictionary size, stored in
|
||||
* little-endian order. The minimum dictionary size is
|
||||
* 1 << 12(4KiB) which the lzma of LZMA SDK uses with option
|
||||
* -d12 and the maxinam dictionary size is 1 << 27(128MiB)
|
||||
* which the one uses with option -d27.
|
||||
* NOTE: A comment of LZMA SDK source code says this dictionary
|
||||
* range is from 1 << 12 to 1 << 30. */
|
||||
dicsize = archive_le32dec(buffer+1);
|
||||
switch (dicsize) {
|
||||
case 0x00001000:/* lzma of LZMA SDK option -d12. */
|
||||
case 0x00002000:/* lzma of LZMA SDK option -d13. */
|
||||
case 0x00004000:/* lzma of LZMA SDK option -d14. */
|
||||
case 0x00008000:/* lzma of LZMA SDK option -d15. */
|
||||
case 0x00010000:/* lzma of XZ Utils option -0 and -1.
|
||||
* lzma of LZMA SDK option -d16. */
|
||||
case 0x00020000:/* lzma of LZMA SDK option -d17. */
|
||||
case 0x00040000:/* lzma of LZMA SDK option -d18. */
|
||||
case 0x00080000:/* lzma of XZ Utils option -2.
|
||||
* lzma of LZMA SDK option -d19. */
|
||||
case 0x00100000:/* lzma of XZ Utils option -3.
|
||||
* lzma of LZMA SDK option -d20. */
|
||||
case 0x00200000:/* lzma of XZ Utils option -4.
|
||||
* lzma of LZMA SDK option -d21. */
|
||||
case 0x00400000:/* lzma of XZ Utils option -5.
|
||||
* lzma of LZMA SDK option -d22. */
|
||||
case 0x00800000:/* lzma of XZ Utils option -6.
|
||||
* lzma of LZMA SDK option -d23. */
|
||||
case 0x01000000:/* lzma of XZ Utils option -7.
|
||||
* lzma of LZMA SDK option -d24. */
|
||||
case 0x02000000:/* lzma of XZ Utils option -8.
|
||||
* lzma of LZMA SDK option -d25. */
|
||||
case 0x04000000:/* lzma of XZ Utils option -9.
|
||||
* lzma of LZMA SDK option -d26. */
|
||||
case 0x08000000:/* lzma of LZMA SDK option -d27. */
|
||||
bits_checked += 32;
|
||||
break;
|
||||
default:
|
||||
/* If a memory usage for encoding was not enough on
|
||||
* the platform where LZMA stream was made, lzma of
|
||||
* XZ Utils automatically decreased the dictionary
|
||||
* size to enough memory for encoding by 1Mi bytes
|
||||
* (1 << 20).*/
|
||||
if (dicsize <= 0x03F00000 && dicsize >= 0x00300000 &&
|
||||
(dicsize & ((1 << 20)-1)) == 0 &&
|
||||
bits_checked == 8 + 64) {
|
||||
bits_checked += 32;
|
||||
break;
|
||||
}
|
||||
/* Otherwise dictionary size is unlikely. But it is
|
||||
* possible that someone makes lzma stream with
|
||||
* liblzma/LZMA SDK in one's dictionary size. */
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* TODO: The above test is still very weak. It would be
|
||||
* good to do better. */
|
||||
|
||||
return (bits_checked);
|
||||
}
|
||||
|
||||
#if HAVE_LZMA_H && HAVE_LIBLZMA
|
||||
|
||||
/*
|
||||
* liblzma 4.999.7 and later support both lzma and xz streams.
|
||||
*/
|
||||
static int
|
||||
xz_bidder_init(struct archive_read_filter *self)
|
||||
{
|
||||
self->code = ARCHIVE_COMPRESSION_XZ;
|
||||
self->name = "xz";
|
||||
return (xz_lzma_bidder_init(self));
|
||||
}
|
||||
|
||||
static int
|
||||
lzma_bidder_init(struct archive_read_filter *self)
|
||||
{
|
||||
self->code = ARCHIVE_COMPRESSION_LZMA;
|
||||
self->name = "lzma";
|
||||
return (xz_lzma_bidder_init(self));
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup the callbacks.
|
||||
*/
|
||||
static int
|
||||
xz_lzma_bidder_init(struct archive_read_filter *self)
|
||||
{
|
||||
static const size_t out_block_size = 64 * 1024;
|
||||
void *out_block;
|
||||
struct private_data *state;
|
||||
int ret;
|
||||
|
||||
state = (struct private_data *)calloc(sizeof(*state), 1);
|
||||
out_block = (unsigned char *)malloc(out_block_size);
|
||||
if (state == NULL || out_block == NULL) {
|
||||
archive_set_error(&self->archive->archive, ENOMEM,
|
||||
"Can't allocate data for xz decompression");
|
||||
free(out_block);
|
||||
free(state);
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
self->data = state;
|
||||
state->out_block_size = out_block_size;
|
||||
state->out_block = out_block;
|
||||
self->read = xz_filter_read;
|
||||
self->skip = NULL; /* not supported */
|
||||
self->close = xz_filter_close;
|
||||
|
||||
state->stream.avail_in = 0;
|
||||
|
||||
state->stream.next_out = state->out_block;
|
||||
state->stream.avail_out = state->out_block_size;
|
||||
|
||||
/* Initialize compression library.
|
||||
* TODO: I don't know what value is best for memlimit.
|
||||
* maybe, it needs to check memory size which
|
||||
* running system has.
|
||||
*/
|
||||
if (self->code == ARCHIVE_COMPRESSION_XZ)
|
||||
ret = lzma_stream_decoder(&(state->stream),
|
||||
(1U << 30),/* memlimit */
|
||||
LZMA_CONCATENATED);
|
||||
else
|
||||
ret = lzma_alone_decoder(&(state->stream),
|
||||
(1U << 30));/* memlimit */
|
||||
|
||||
if (ret == LZMA_OK)
|
||||
return (ARCHIVE_OK);
|
||||
|
||||
/* Library setup failed: Choose an error message and clean up. */
|
||||
switch (ret) {
|
||||
case LZMA_MEM_ERROR:
|
||||
archive_set_error(&self->archive->archive, ENOMEM,
|
||||
"Internal error initializing compression library: "
|
||||
"Cannot allocate memory");
|
||||
break;
|
||||
case LZMA_OPTIONS_ERROR:
|
||||
archive_set_error(&self->archive->archive,
|
||||
ARCHIVE_ERRNO_MISC,
|
||||
"Internal error initializing compression library: "
|
||||
"Invalid or unsupported options");
|
||||
break;
|
||||
default:
|
||||
archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Internal error initializing lzma library");
|
||||
break;
|
||||
}
|
||||
|
||||
free(state->out_block);
|
||||
free(state);
|
||||
self->data = NULL;
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the next block of decompressed data.
|
||||
*/
|
||||
static ssize_t
|
||||
xz_filter_read(struct archive_read_filter *self, const void **p)
|
||||
{
|
||||
struct private_data *state;
|
||||
size_t decompressed;
|
||||
ssize_t avail_in;
|
||||
int ret;
|
||||
|
||||
state = (struct private_data *)self->data;
|
||||
|
||||
/* Empty our output buffer. */
|
||||
state->stream.next_out = state->out_block;
|
||||
state->stream.avail_out = state->out_block_size;
|
||||
|
||||
/* Try to fill the output buffer. */
|
||||
while (state->stream.avail_out > 0 && !state->eof) {
|
||||
state->stream.next_in =
|
||||
__archive_read_filter_ahead(self->upstream, 1, &avail_in);
|
||||
if (state->stream.next_in == NULL && avail_in < 0)
|
||||
return (ARCHIVE_FATAL);
|
||||
state->stream.avail_in = avail_in;
|
||||
|
||||
/* Decompress as much as we can in one pass. */
|
||||
ret = lzma_code(&(state->stream),
|
||||
(state->stream.avail_in == 0)? LZMA_FINISH: LZMA_RUN);
|
||||
switch (ret) {
|
||||
case LZMA_STREAM_END: /* Found end of stream. */
|
||||
state->eof = 1;
|
||||
/* FALL THROUGH */
|
||||
case LZMA_OK: /* Decompressor made some progress. */
|
||||
__archive_read_filter_consume(self->upstream,
|
||||
avail_in - state->stream.avail_in);
|
||||
break;
|
||||
case LZMA_MEM_ERROR:
|
||||
archive_set_error(&self->archive->archive, ENOMEM,
|
||||
"Lzma library error: Cannot allocate memory");
|
||||
return (ARCHIVE_FATAL);
|
||||
case LZMA_MEMLIMIT_ERROR:
|
||||
archive_set_error(&self->archive->archive, ENOMEM,
|
||||
"Lzma library error: Out of memory");
|
||||
return (ARCHIVE_FATAL);
|
||||
case LZMA_FORMAT_ERROR:
|
||||
archive_set_error(&self->archive->archive,
|
||||
ARCHIVE_ERRNO_MISC,
|
||||
"Lzma library error: format not recognized");
|
||||
return (ARCHIVE_FATAL);
|
||||
case LZMA_OPTIONS_ERROR:
|
||||
archive_set_error(&self->archive->archive,
|
||||
ARCHIVE_ERRNO_MISC,
|
||||
"Lzma library error: Invalid options");
|
||||
return (ARCHIVE_FATAL);
|
||||
case LZMA_DATA_ERROR:
|
||||
archive_set_error(&self->archive->archive,
|
||||
ARCHIVE_ERRNO_MISC,
|
||||
"Lzma library error: Corrupted input data");
|
||||
return (ARCHIVE_FATAL);
|
||||
case LZMA_BUF_ERROR:
|
||||
archive_set_error(&self->archive->archive,
|
||||
ARCHIVE_ERRNO_MISC,
|
||||
"Lzma library error: No progress is possible");
|
||||
return (ARCHIVE_FATAL);
|
||||
default:
|
||||
/* Return an error. */
|
||||
archive_set_error(&self->archive->archive,
|
||||
ARCHIVE_ERRNO_MISC,
|
||||
"Lzma decompression failed: Unknown error");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
}
|
||||
|
||||
decompressed = state->stream.next_out - state->out_block;
|
||||
state->total_out += decompressed;
|
||||
if (decompressed == 0)
|
||||
*p = NULL;
|
||||
else
|
||||
*p = state->out_block;
|
||||
return (decompressed);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up the decompressor.
|
||||
*/
|
||||
static int
|
||||
xz_filter_close(struct archive_read_filter *self)
|
||||
{
|
||||
struct private_data *state;
|
||||
|
||||
state = (struct private_data *)self->data;
|
||||
lzma_end(&(state->stream));
|
||||
free(state->out_block);
|
||||
free(state);
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#if HAVE_LZMADEC_H && HAVE_LIBLZMADEC
|
||||
|
||||
/*
|
||||
* If we have the older liblzmadec library, then we can handle
|
||||
* LZMA streams but not XZ streams.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Setup the callbacks.
|
||||
*/
|
||||
static int
|
||||
lzma_bidder_init(struct archive_read_filter *self)
|
||||
{
|
||||
static const size_t out_block_size = 64 * 1024;
|
||||
void *out_block;
|
||||
struct private_data *state;
|
||||
ssize_t ret, avail_in;
|
||||
|
||||
self->code = ARCHIVE_COMPRESSION_LZMA;
|
||||
self->name = "lzma";
|
||||
|
||||
state = (struct private_data *)calloc(sizeof(*state), 1);
|
||||
out_block = (unsigned char *)malloc(out_block_size);
|
||||
if (state == NULL || out_block == NULL) {
|
||||
archive_set_error(&self->archive->archive, ENOMEM,
|
||||
"Can't allocate data for lzma decompression");
|
||||
free(out_block);
|
||||
free(state);
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
self->data = state;
|
||||
state->out_block_size = out_block_size;
|
||||
state->out_block = out_block;
|
||||
self->read = lzma_filter_read;
|
||||
self->skip = NULL; /* not supported */
|
||||
self->close = lzma_filter_close;
|
||||
|
||||
/* Prime the lzma library with 18 bytes of input. */
|
||||
state->stream.next_in = (unsigned char *)(uintptr_t)
|
||||
__archive_read_filter_ahead(self->upstream, 18, &avail_in);
|
||||
if (state->stream.next_in == NULL)
|
||||
return (ARCHIVE_FATAL);
|
||||
state->stream.avail_in = avail_in;
|
||||
state->stream.next_out = state->out_block;
|
||||
state->stream.avail_out = state->out_block_size;
|
||||
|
||||
/* Initialize compression library. */
|
||||
ret = lzmadec_init(&(state->stream));
|
||||
__archive_read_filter_consume(self->upstream,
|
||||
avail_in - state->stream.avail_in);
|
||||
if (ret == LZMADEC_OK)
|
||||
return (ARCHIVE_OK);
|
||||
|
||||
/* Library setup failed: Clean up. */
|
||||
archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Internal error initializing lzma library");
|
||||
|
||||
/* Override the error message if we know what really went wrong. */
|
||||
switch (ret) {
|
||||
case LZMADEC_HEADER_ERROR:
|
||||
archive_set_error(&self->archive->archive,
|
||||
ARCHIVE_ERRNO_MISC,
|
||||
"Internal error initializing compression library: "
|
||||
"invalid header");
|
||||
break;
|
||||
case LZMADEC_MEM_ERROR:
|
||||
archive_set_error(&self->archive->archive, ENOMEM,
|
||||
"Internal error initializing compression library: "
|
||||
"out of memory");
|
||||
break;
|
||||
}
|
||||
|
||||
free(state->out_block);
|
||||
free(state);
|
||||
self->data = NULL;
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the next block of decompressed data.
|
||||
*/
|
||||
static ssize_t
|
||||
lzma_filter_read(struct archive_read_filter *self, const void **p)
|
||||
{
|
||||
struct private_data *state;
|
||||
size_t decompressed;
|
||||
ssize_t avail_in, ret;
|
||||
|
||||
state = (struct private_data *)self->data;
|
||||
|
||||
/* Empty our output buffer. */
|
||||
state->stream.next_out = state->out_block;
|
||||
state->stream.avail_out = state->out_block_size;
|
||||
|
||||
/* Try to fill the output buffer. */
|
||||
while (state->stream.avail_out > 0 && !state->eof) {
|
||||
state->stream.next_in = (unsigned char *)(uintptr_t)
|
||||
__archive_read_filter_ahead(self->upstream, 1, &avail_in);
|
||||
if (state->stream.next_in == NULL && avail_in < 0)
|
||||
return (ARCHIVE_FATAL);
|
||||
state->stream.avail_in = avail_in;
|
||||
|
||||
/* Decompress as much as we can in one pass. */
|
||||
ret = lzmadec_decode(&(state->stream), avail_in == 0);
|
||||
switch (ret) {
|
||||
case LZMADEC_STREAM_END: /* Found end of stream. */
|
||||
state->eof = 1;
|
||||
/* FALL THROUGH */
|
||||
case LZMADEC_OK: /* Decompressor made some progress. */
|
||||
__archive_read_filter_consume(self->upstream,
|
||||
avail_in - state->stream.avail_in);
|
||||
break;
|
||||
case LZMADEC_BUF_ERROR: /* Insufficient input data? */
|
||||
archive_set_error(&self->archive->archive,
|
||||
ARCHIVE_ERRNO_MISC,
|
||||
"Insufficient compressed data");
|
||||
return (ARCHIVE_FATAL);
|
||||
default:
|
||||
/* Return an error. */
|
||||
archive_set_error(&self->archive->archive,
|
||||
ARCHIVE_ERRNO_MISC,
|
||||
"Lzma decompression failed");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
}
|
||||
|
||||
decompressed = state->stream.next_out - state->out_block;
|
||||
state->total_out += decompressed;
|
||||
if (decompressed == 0)
|
||||
*p = NULL;
|
||||
else
|
||||
*p = state->out_block;
|
||||
return (decompressed);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up the decompressor.
|
||||
*/
|
||||
static int
|
||||
lzma_filter_close(struct archive_read_filter *self)
|
||||
{
|
||||
struct private_data *state;
|
||||
int ret;
|
||||
|
||||
state = (struct private_data *)self->data;
|
||||
ret = ARCHIVE_OK;
|
||||
switch (lzmadec_end(&(state->stream))) {
|
||||
case LZMADEC_OK:
|
||||
break;
|
||||
default:
|
||||
archive_set_error(&(self->archive->archive),
|
||||
ARCHIVE_ERRNO_MISC,
|
||||
"Failed to clean up %s compressor",
|
||||
self->archive->archive.compression_name);
|
||||
ret = ARCHIVE_FATAL;
|
||||
}
|
||||
|
||||
free(state->out_block);
|
||||
free(state);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
*
|
||||
* If we have no suitable library on this system, we can't actually do
|
||||
* the decompression. We can, however, still detect compressed
|
||||
* archives and emit a useful message.
|
||||
*
|
||||
*/
|
||||
static int
|
||||
lzma_bidder_init(struct archive_read_filter *self)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = __archive_read_program(self, "unlzma");
|
||||
/* Note: We set the format here even if __archive_read_program()
|
||||
* above fails. We do, after all, know what the format is
|
||||
* even if we weren't able to read it. */
|
||||
self->code = ARCHIVE_COMPRESSION_LZMA;
|
||||
self->name = "lzma";
|
||||
return (r);
|
||||
}
|
||||
|
||||
#endif /* HAVE_LZMADEC_H */
|
||||
|
||||
|
||||
static int
|
||||
xz_bidder_init(struct archive_read_filter *self)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = __archive_read_program(self, "unxz");
|
||||
/* Note: We set the format here even if __archive_read_program()
|
||||
* above fails. We do, after all, know what the format is
|
||||
* even if we weren't able to read it. */
|
||||
self->code = ARCHIVE_COMPRESSION_XZ;
|
||||
self->name = "xz";
|
||||
return (r);
|
||||
}
|
||||
|
||||
|
||||
#endif /* HAVE_LZMA_H */
|
@ -1,51 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2011 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$");
|
||||
|
||||
#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_mtree(a);
|
||||
archive_read_support_format_tar(a);
|
||||
archive_read_support_format_xar(a);
|
||||
archive_read_support_format_zip(a);
|
||||
|
||||
/* Note: We always return ARCHIVE_OK here, even if some of the
|
||||
* above return ARCHIVE_WARN. The intent here is to enable
|
||||
* "as much as possible." Clients who need specific
|
||||
* compression should enable those individually so they can
|
||||
* verify the level of support. */
|
||||
/* Clear any warning messages set by the above functions. */
|
||||
archive_clear_error(a);
|
||||
return (ARCHIVE_OK);
|
||||
}
|
@ -1,584 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_LIMITS_H
|
||||
#include <limits.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_entry.h"
|
||||
#include "archive_private.h"
|
||||
#include "archive_read_private.h"
|
||||
|
||||
struct ar {
|
||||
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
|
||||
|
||||
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 *a);
|
||||
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->strtab = NULL;
|
||||
|
||||
r = __archive_read_register_format(a,
|
||||
ar,
|
||||
"ar",
|
||||
archive_read_format_ar_bid,
|
||||
NULL,
|
||||
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)
|
||||
{
|
||||
const void *h;
|
||||
|
||||
if (a->archive.archive_format != 0 &&
|
||||
(a->archive.archive_format & ARCHIVE_FORMAT_BASE_MASK) !=
|
||||
ARCHIVE_FORMAT_AR)
|
||||
return(0);
|
||||
|
||||
/*
|
||||
* Verify the 8-byte file signature.
|
||||
* TODO: Do we need to check more than this?
|
||||
*/
|
||||
if ((h = __archive_read_ahead(a, 8, NULL)) == NULL)
|
||||
return (-1);
|
||||
if (strncmp((const char*)h, "!<arch>\n", 8) == 0) {
|
||||
return (64);
|
||||
}
|
||||
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, *st;
|
||||
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.
|
||||
*/
|
||||
__archive_read_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. */
|
||||
if ((b = __archive_read_ahead(a, 60, &bytes_read)) == NULL)
|
||||
/* Broken header. */
|
||||
return (ARCHIVE_EOF);
|
||||
__archive_read_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,
|
||||
"Incorrect file header signature");
|
||||
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_filetype(entry, AE_IFREG);
|
||||
/* 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;
|
||||
if (entry_size == 0) {
|
||||
archive_set_error(&a->archive, EINVAL,
|
||||
"Invalid string table");
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
if (ar->strtab != NULL) {
|
||||
archive_set_error(&a->archive, EINVAL,
|
||||
"More than one string tables exist");
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
|
||||
/* Read the filename table into memory. */
|
||||
st = malloc(entry_size);
|
||||
if (st == NULL) {
|
||||
archive_set_error(&a->archive, ENOMEM,
|
||||
"Can't allocate filename table buffer");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
ar->strtab = st;
|
||||
ar->strtab_size = entry_size;
|
||||
if ((b = __archive_read_ahead(a, entry_size, NULL)) == NULL)
|
||||
return (ARCHIVE_FATAL);
|
||||
memcpy(st, b, entry_size);
|
||||
__archive_read_consume(a, entry_size);
|
||||
/* All contents are consumed. */
|
||||
ar->entry_bytes_remaining = 0;
|
||||
archive_entry_set_size(entry, ar->entry_bytes_remaining);
|
||||
|
||||
/* Parse the filename table. */
|
||||
return (ar_parse_gnu_filename_table(a));
|
||||
}
|
||||
|
||||
/*
|
||||
* GNU variant handles long filenames by storing /<number>
|
||||
* to indicate a name stored in the filename table.
|
||||
* XXX TODO: Verify that it's all digits... Don't be fooled
|
||||
* by "/9xyz" XXX
|
||||
*/
|
||||
if (filename[0] == '/' && filename[1] >= '0' && filename[1] <= '9') {
|
||||
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);
|
||||
bsd_name_length = (size_t)number;
|
||||
/* Guard against the filename + trailing NUL
|
||||
* overflowing a size_t and against the filename size
|
||||
* being larger than the entire entry. */
|
||||
if (number > (uint64_t)(bsd_name_length + 1)
|
||||
|| (off_t)bsd_name_length > ar->entry_bytes_remaining) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Bad input file size");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
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. */
|
||||
if ((b = __archive_read_ahead(a, bsd_name_length, NULL)) == NULL) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Truncated input file");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
__archive_read_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_filetype(entry, AE_IFREG);
|
||||
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) {
|
||||
*buff = __archive_read_ahead(a, 1, &bytes_read);
|
||||
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;
|
||||
__archive_read_consume(a, (size_t)bytes_read);
|
||||
return (ARCHIVE_OK);
|
||||
} else {
|
||||
while (ar->entry_padding > 0) {
|
||||
*buff = __archive_read_ahead(a, 1, &bytes_read);
|
||||
if (bytes_read <= 0)
|
||||
return (ARCHIVE_FATAL);
|
||||
if (bytes_read > ar->entry_padding)
|
||||
bytes_read = (ssize_t)ar->entry_padding;
|
||||
__archive_read_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;
|
||||
|
||||
ar = (struct ar *)(a->format->data);
|
||||
|
||||
bytes_skipped = __archive_read_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;
|
||||
char *p;
|
||||
size_t size;
|
||||
|
||||
ar = (struct ar*)(a->format->data);
|
||||
size = ar->strtab_size;
|
||||
|
||||
for (p = ar->strtab; p < ar->strtab + size - 1; ++p) {
|
||||
if (*p == '/') {
|
||||
*p++ = '\0';
|
||||
if (*p != '\n')
|
||||
goto bad_string_table;
|
||||
*p = '\0';
|
||||
}
|
||||
}
|
||||
/*
|
||||
* GNU ar always pads the table to an even size.
|
||||
* The pad character is either '\n' or '`'.
|
||||
*/
|
||||
if (p != ar->strtab + size && *p != '\n' && *p != '`')
|
||||
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);
|
||||
}
|
@ -1,777 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
/* #include <stdint.h> */ /* See archive_platform.h */
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#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];
|
||||
} __packed;
|
||||
|
||||
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];
|
||||
} __packed;
|
||||
|
||||
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];
|
||||
} __packed;
|
||||
|
||||
struct links_entry {
|
||||
struct links_entry *next;
|
||||
struct links_entry *previous;
|
||||
int links;
|
||||
dev_t dev;
|
||||
int64_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 find_odc_header(struct archive_read *);
|
||||
static int find_newc_header(struct archive_read *);
|
||||
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 is_octal(const char *, size_t);
|
||||
static int is_hex(const char *, 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,
|
||||
"cpio",
|
||||
archive_read_format_cpio_bid,
|
||||
NULL,
|
||||
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)
|
||||
{
|
||||
const void *h;
|
||||
const unsigned char *p;
|
||||
struct cpio *cpio;
|
||||
int bid;
|
||||
|
||||
cpio = (struct cpio *)(a->format->data);
|
||||
|
||||
if ((h = __archive_read_ahead(a, 6, NULL)) == NULL)
|
||||
return (-1);
|
||||
|
||||
p = (const unsigned char *)h;
|
||||
bid = 0;
|
||||
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;
|
||||
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_WARN)
|
||||
return (r);
|
||||
|
||||
/* Read name from buffer. */
|
||||
h = __archive_read_ahead(a, namelength + name_pad, NULL);
|
||||
if (h == NULL)
|
||||
return (ARCHIVE_FATAL);
|
||||
__archive_read_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) {
|
||||
h = __archive_read_ahead(a, cpio->entry_bytes_remaining, NULL);
|
||||
if (h == NULL)
|
||||
return (ARCHIVE_FATAL);
|
||||
__archive_read_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;
|
||||
}
|
||||
|
||||
/* XXX TODO: If the full mode is 0160200, then this is a Solaris
|
||||
* ACL description for the following entry. Read this body
|
||||
* and parse it as a Solaris-style ACL, then read the next
|
||||
* header. XXX */
|
||||
|
||||
/* 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_clear_error(&a->archive);
|
||||
return (ARCHIVE_EOF);
|
||||
}
|
||||
|
||||
/* Detect and record hardlinks to previously-extracted entries. */
|
||||
record_hardlink(cpio, entry);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
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) {
|
||||
*buff = __archive_read_ahead(a, 1, &bytes_read);
|
||||
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;
|
||||
__archive_read_consume(a, bytes_read);
|
||||
return (ARCHIVE_OK);
|
||||
} else {
|
||||
while (cpio->entry_padding > 0) {
|
||||
*buff = __archive_read_ahead(a, 1, &bytes_read);
|
||||
if (bytes_read <= 0)
|
||||
return (ARCHIVE_FATAL);
|
||||
if (bytes_read > cpio->entry_padding)
|
||||
bytes_read = cpio->entry_padding;
|
||||
__archive_read_consume(a, bytes_read);
|
||||
cpio->entry_padding -= bytes_read;
|
||||
}
|
||||
*buff = NULL;
|
||||
*size = 0;
|
||||
*offset = cpio->entry_offset;
|
||||
return (ARCHIVE_EOF);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Skip forward to the next cpio newc header by searching for the
|
||||
* 07070[12] string. This should be generalized and merged with
|
||||
* find_odc_header below.
|
||||
*/
|
||||
static int
|
||||
is_hex(const char *p, size_t len)
|
||||
{
|
||||
while (len-- > 0) {
|
||||
if ((*p >= '0' && *p <= '9')
|
||||
|| (*p >= 'a' && *p <= 'f')
|
||||
|| (*p >= 'A' && *p <= 'F'))
|
||||
++p;
|
||||
else
|
||||
return (0);
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
static int
|
||||
find_newc_header(struct archive_read *a)
|
||||
{
|
||||
const void *h;
|
||||
const char *p, *q;
|
||||
size_t skip, skipped = 0;
|
||||
ssize_t bytes;
|
||||
|
||||
for (;;) {
|
||||
h = __archive_read_ahead(a, sizeof(struct cpio_newc_header), &bytes);
|
||||
if (h == NULL)
|
||||
return (ARCHIVE_FATAL);
|
||||
p = h;
|
||||
q = p + bytes;
|
||||
|
||||
/* Try the typical case first, then go into the slow search.*/
|
||||
if (memcmp("07070", p, 5) == 0
|
||||
&& (p[5] == '1' || p[5] == '2')
|
||||
&& is_hex(p, sizeof(struct cpio_newc_header)))
|
||||
return (ARCHIVE_OK);
|
||||
|
||||
/*
|
||||
* Scan ahead until we find something that looks
|
||||
* like an odc header.
|
||||
*/
|
||||
while (p + sizeof(struct cpio_newc_header) <= q) {
|
||||
switch (p[5]) {
|
||||
case '1':
|
||||
case '2':
|
||||
if (memcmp("07070", p, 5) == 0
|
||||
&& is_hex(p, sizeof(struct cpio_newc_header))) {
|
||||
skip = p - (const char *)h;
|
||||
__archive_read_consume(a, skip);
|
||||
skipped += skip;
|
||||
if (skipped > 0) {
|
||||
archive_set_error(&a->archive,
|
||||
0,
|
||||
"Skipped %d bytes before "
|
||||
"finding valid header",
|
||||
(int)skipped);
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
p += 2;
|
||||
break;
|
||||
case '0':
|
||||
p++;
|
||||
break;
|
||||
default:
|
||||
p += 6;
|
||||
break;
|
||||
}
|
||||
}
|
||||
skip = p - (const char *)h;
|
||||
__archive_read_consume(a, skip);
|
||||
skipped += skip;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
int r;
|
||||
|
||||
r = find_newc_header(a);
|
||||
if (r < ARCHIVE_WARN)
|
||||
return (r);
|
||||
|
||||
/* Read fixed-size portion of header. */
|
||||
h = __archive_read_ahead(a, sizeof(struct cpio_newc_header), NULL);
|
||||
if (h == NULL)
|
||||
return (ARCHIVE_FATAL);
|
||||
__archive_read_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 (r);
|
||||
}
|
||||
|
||||
/*
|
||||
* Skip forward to the next cpio odc header by searching for the
|
||||
* 070707 string. This is a hand-optimized search that could
|
||||
* probably be easily generalized to handle all character-based
|
||||
* cpio variants.
|
||||
*/
|
||||
static int
|
||||
is_octal(const char *p, size_t len)
|
||||
{
|
||||
while (len-- > 0) {
|
||||
if (*p < '0' || *p > '7')
|
||||
return (0);
|
||||
++p;
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
static int
|
||||
find_odc_header(struct archive_read *a)
|
||||
{
|
||||
const void *h;
|
||||
const char *p, *q;
|
||||
size_t skip, skipped = 0;
|
||||
ssize_t bytes;
|
||||
|
||||
for (;;) {
|
||||
h = __archive_read_ahead(a, sizeof(struct cpio_odc_header), &bytes);
|
||||
if (h == NULL)
|
||||
return (ARCHIVE_FATAL);
|
||||
p = h;
|
||||
q = p + bytes;
|
||||
|
||||
/* Try the typical case first, then go into the slow search.*/
|
||||
if (memcmp("070707", p, 6) == 0
|
||||
&& is_octal(p, sizeof(struct cpio_odc_header)))
|
||||
return (ARCHIVE_OK);
|
||||
|
||||
/*
|
||||
* Scan ahead until we find something that looks
|
||||
* like an odc header.
|
||||
*/
|
||||
while (p + sizeof(struct cpio_odc_header) <= q) {
|
||||
switch (p[5]) {
|
||||
case '7':
|
||||
if (memcmp("070707", p, 6) == 0
|
||||
&& is_octal(p, sizeof(struct cpio_odc_header))) {
|
||||
skip = p - (const char *)h;
|
||||
__archive_read_consume(a, skip);
|
||||
skipped += skip;
|
||||
if (skipped > 0) {
|
||||
archive_set_error(&a->archive,
|
||||
0,
|
||||
"Skipped %d bytes before "
|
||||
"finding valid header",
|
||||
(int)skipped);
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
p += 2;
|
||||
break;
|
||||
case '0':
|
||||
p++;
|
||||
break;
|
||||
default:
|
||||
p += 6;
|
||||
break;
|
||||
}
|
||||
}
|
||||
skip = p - (const char *)h;
|
||||
__archive_read_consume(a, skip);
|
||||
skipped += skip;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
int r;
|
||||
const struct cpio_odc_header *header;
|
||||
|
||||
a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX;
|
||||
a->archive.archive_format_name = "POSIX octet-oriented cpio";
|
||||
|
||||
/* Find the start of the next header. */
|
||||
r = find_odc_header(a);
|
||||
if (r < ARCHIVE_WARN)
|
||||
return (r);
|
||||
|
||||
/* Read fixed-size portion of header. */
|
||||
h = __archive_read_ahead(a, sizeof(struct cpio_odc_header), NULL);
|
||||
if (h == NULL)
|
||||
return (ARCHIVE_FATAL);
|
||||
__archive_read_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 (r);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_LE;
|
||||
a->archive.archive_format_name = "cpio (little-endian binary)";
|
||||
|
||||
/* Read fixed-size portion of header. */
|
||||
h = __archive_read_ahead(a, sizeof(struct cpio_bin_header), NULL);
|
||||
if (h == NULL)
|
||||
return (ARCHIVE_FATAL);
|
||||
__archive_read_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;
|
||||
|
||||
a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_BE;
|
||||
a->archive.archive_format_name = "cpio (big-endian binary)";
|
||||
|
||||
/* Read fixed-size portion of header. */
|
||||
h = __archive_read_ahead(a, sizeof(struct cpio_bin_header), NULL);
|
||||
if (h == NULL)
|
||||
return (ARCHIVE_FATAL);
|
||||
__archive_read_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]<<24) + (p[1]<<16) + (p[2]<<8) + (p[3]));
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
int64_t ino;
|
||||
|
||||
if (archive_entry_nlink(entry) <= 1)
|
||||
return;
|
||||
|
||||
dev = archive_entry_dev(entry);
|
||||
ino = archive_entry_ino64(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_copy_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->name);
|
||||
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");
|
||||
}
|
@ -1,93 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#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,
|
||||
NULL,
|
||||
archive_read_format_empty_bid,
|
||||
NULL,
|
||||
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)
|
||||
{
|
||||
ssize_t avail;
|
||||
|
||||
(void)__archive_read_ahead(a, 1, &avail);
|
||||
if (avail != 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);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,185 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2009 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$");
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_entry.h"
|
||||
#include "archive_private.h"
|
||||
#include "archive_read_private.h"
|
||||
|
||||
struct raw_info {
|
||||
int64_t offset; /* Current position in the file. */
|
||||
int end_of_file;
|
||||
};
|
||||
|
||||
static int archive_read_format_raw_bid(struct archive_read *);
|
||||
static int archive_read_format_raw_cleanup(struct archive_read *);
|
||||
static int archive_read_format_raw_read_data(struct archive_read *,
|
||||
const void **, size_t *, off_t *);
|
||||
static int archive_read_format_raw_read_data_skip(struct archive_read *);
|
||||
static int archive_read_format_raw_read_header(struct archive_read *,
|
||||
struct archive_entry *);
|
||||
|
||||
int
|
||||
archive_read_support_format_raw(struct archive *_a)
|
||||
{
|
||||
struct raw_info *info;
|
||||
struct archive_read *a = (struct archive_read *)_a;
|
||||
int r;
|
||||
|
||||
info = (struct raw_info *)calloc(1, sizeof(*info));
|
||||
if (info == NULL) {
|
||||
archive_set_error(&a->archive, ENOMEM,
|
||||
"Can't allocate raw_info data");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
r = __archive_read_register_format(a,
|
||||
info,
|
||||
"raw",
|
||||
archive_read_format_raw_bid,
|
||||
NULL,
|
||||
archive_read_format_raw_read_header,
|
||||
archive_read_format_raw_read_data,
|
||||
archive_read_format_raw_read_data_skip,
|
||||
archive_read_format_raw_cleanup);
|
||||
if (r != ARCHIVE_OK)
|
||||
free(info);
|
||||
return (r);
|
||||
}
|
||||
|
||||
/*
|
||||
* Bid 1 if this is a non-empty file. Anyone who can really support
|
||||
* this should outbid us, so it should generally be safe to use "raw"
|
||||
* in conjunction with other formats. But, this could really confuse
|
||||
* folks if there are bid errors or minor file damage, so we don't
|
||||
* include "raw" as part of support_format_all().
|
||||
*/
|
||||
static int
|
||||
archive_read_format_raw_bid(struct archive_read *a)
|
||||
{
|
||||
|
||||
if (__archive_read_ahead(a, 1, NULL) == NULL)
|
||||
return (-1);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Mock up a fake header.
|
||||
*/
|
||||
static int
|
||||
archive_read_format_raw_read_header(struct archive_read *a,
|
||||
struct archive_entry *entry)
|
||||
{
|
||||
struct raw_info *info;
|
||||
|
||||
info = (struct raw_info *)(a->format->data);
|
||||
if (info->end_of_file)
|
||||
return (ARCHIVE_EOF);
|
||||
|
||||
a->archive.archive_format = ARCHIVE_FORMAT_RAW;
|
||||
a->archive.archive_format_name = "Raw data";
|
||||
archive_entry_set_pathname(entry, "data");
|
||||
/* XXX should we set mode to mimic a regular file? XXX */
|
||||
/* I'm deliberately leaving most fields unset here. */
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
archive_read_format_raw_read_data(struct archive_read *a,
|
||||
const void **buff, size_t *size, off_t *offset)
|
||||
{
|
||||
struct raw_info *info;
|
||||
ssize_t avail;
|
||||
|
||||
info = (struct raw_info *)(a->format->data);
|
||||
if (info->end_of_file)
|
||||
return (ARCHIVE_EOF);
|
||||
|
||||
/* Get whatever bytes are immediately available. */
|
||||
*buff = __archive_read_ahead(a, 1, &avail);
|
||||
if (avail > 0) {
|
||||
/* Consume and return the bytes we just read */
|
||||
__archive_read_consume(a, avail);
|
||||
*size = avail;
|
||||
*offset = info->offset;
|
||||
info->offset += *size;
|
||||
return (ARCHIVE_OK);
|
||||
} else if (0 == avail) {
|
||||
/* Record and return end-of-file. */
|
||||
info->end_of_file = 1;
|
||||
*size = 0;
|
||||
*offset = info->offset;
|
||||
return (ARCHIVE_EOF);
|
||||
} else {
|
||||
/* Record and return an error. */
|
||||
*size = 0;
|
||||
*offset = info->offset;
|
||||
return (avail);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
archive_read_format_raw_read_data_skip(struct archive_read *a)
|
||||
{
|
||||
struct raw_info *info;
|
||||
off_t bytes_skipped;
|
||||
int64_t request = 1024 * 1024 * 1024UL; /* Skip 1 GB at a time. */
|
||||
|
||||
info = (struct raw_info *)(a->format->data);
|
||||
if (info->end_of_file)
|
||||
return (ARCHIVE_EOF);
|
||||
info->end_of_file = 1;
|
||||
|
||||
for (;;) {
|
||||
bytes_skipped = __archive_read_skip_lenient(a, request);
|
||||
if (bytes_skipped < 0)
|
||||
return (ARCHIVE_FATAL);
|
||||
if (bytes_skipped < request)
|
||||
return (ARCHIVE_OK);
|
||||
/* We skipped all the bytes we asked for. There might
|
||||
* be more, so try again. */
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
archive_read_format_raw_cleanup(struct archive_read *a)
|
||||
{
|
||||
struct raw_info *info;
|
||||
|
||||
info = (struct raw_info *)(a->format->data);
|
||||
free(info);
|
||||
a->format->data = NULL;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,950 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#include <time.h>
|
||||
#ifdef HAVE_ZLIB_H
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_entry.h"
|
||||
#include "archive_private.h"
|
||||
#include "archive_read_private.h"
|
||||
#include "archive_endian.h"
|
||||
|
||||
#ifndef HAVE_ZLIB_H
|
||||
#include "archive_crc32.h"
|
||||
#endif
|
||||
|
||||
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;
|
||||
|
||||
/* Running CRC32 of the decompressed data */
|
||||
unsigned long entry_crc32;
|
||||
|
||||
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;
|
||||
|
||||
unsigned 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;
|
||||
char stream_valid;
|
||||
#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 search_next_signature(struct archive_read *);
|
||||
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,
|
||||
"zip",
|
||||
archive_read_format_zip_bid,
|
||||
NULL,
|
||||
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)
|
||||
{
|
||||
const char *p;
|
||||
const void *buff;
|
||||
ssize_t bytes_avail, offset;
|
||||
|
||||
if ((p = __archive_read_ahead(a, 4, NULL)) == NULL)
|
||||
return (-1);
|
||||
|
||||
/*
|
||||
* Bid of 30 here is: 16 bits for "PK",
|
||||
* next 16-bit field has four options (-2 bits).
|
||||
* 16 + 16-2 = 30.
|
||||
*/
|
||||
if (p[0] == 'P' && p[1] == 'K') {
|
||||
if ((p[2] == '\001' && p[3] == '\002')
|
||||
|| (p[2] == '\003' && p[3] == '\004')
|
||||
|| (p[2] == '\005' && p[3] == '\006')
|
||||
|| (p[2] == '\007' && p[3] == '\010')
|
||||
|| (p[2] == '0' && p[3] == '0'))
|
||||
return (30);
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to handle self-extracting archives
|
||||
* by noting a PE header and searching forward
|
||||
* up to 128k for a 'PK\003\004' marker.
|
||||
*/
|
||||
if (p[0] == 'M' && p[1] == 'Z') {
|
||||
/*
|
||||
* TODO: Optimize by initializing 'offset' to an
|
||||
* estimate of the likely start of the archive data
|
||||
* based on values in the PE header. Note that we
|
||||
* don't need to be exact, but we mustn't skip too
|
||||
* far. The search below will compensate if we
|
||||
* undershoot.
|
||||
*/
|
||||
offset = 0;
|
||||
while (offset < 124000) {
|
||||
/* Get 4k of data beyond where we stopped. */
|
||||
buff = __archive_read_ahead(a, offset + 4096,
|
||||
&bytes_avail);
|
||||
if (buff == NULL)
|
||||
break;
|
||||
p = (const char *)buff + offset;
|
||||
while (p + 9 < (const char *)buff + bytes_avail) {
|
||||
if (p[0] == 'P' && p[1] == 'K' /* signature */
|
||||
&& p[2] == 3 && p[3] == 4 /* File entry */
|
||||
&& p[8] == 8 /* compression == deflate */
|
||||
&& p[9] == 0 /* High byte of compression */
|
||||
)
|
||||
{
|
||||
return (30);
|
||||
}
|
||||
++p;
|
||||
}
|
||||
offset = p - (const char *)buff;
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Search forward for a "PK\003\004" file header. This handles the
|
||||
* case of self-extracting archives, where there is an executable
|
||||
* prepended to the ZIP archive.
|
||||
*/
|
||||
static int
|
||||
skip_sfx(struct archive_read *a)
|
||||
{
|
||||
const void *h;
|
||||
const char *p, *q;
|
||||
size_t skip;
|
||||
ssize_t bytes;
|
||||
|
||||
/*
|
||||
* TODO: We should be able to skip forward by a bunch
|
||||
* by lifting some values from the PE header. We don't
|
||||
* need to be exact (we're still going to search forward
|
||||
* to find the header), but it will speed things up and
|
||||
* reduce the chance of a false positive.
|
||||
*/
|
||||
for (;;) {
|
||||
h = __archive_read_ahead(a, 4, &bytes);
|
||||
if (bytes < 4)
|
||||
return (ARCHIVE_FATAL);
|
||||
p = h;
|
||||
q = p + bytes;
|
||||
|
||||
/*
|
||||
* Scan ahead until we find something that looks
|
||||
* like the zip header.
|
||||
*/
|
||||
while (p + 4 < q) {
|
||||
switch (p[3]) {
|
||||
case '\004':
|
||||
/* TODO: Additional verification here. */
|
||||
if (memcmp("PK\003\004", p, 4) == 0) {
|
||||
skip = p - (const char *)h;
|
||||
__archive_read_consume(a, skip);
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
p += 4;
|
||||
break;
|
||||
case '\003': p += 1; break;
|
||||
case 'K': p += 2; break;
|
||||
case 'P': p += 3; break;
|
||||
default: p += 4; break;
|
||||
}
|
||||
}
|
||||
skip = p - (const char *)h;
|
||||
__archive_read_consume(a, skip);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
archive_read_format_zip_read_header(struct archive_read *a,
|
||||
struct archive_entry *entry)
|
||||
{
|
||||
const void *h;
|
||||
const char *signature;
|
||||
struct zip *zip;
|
||||
int r = ARCHIVE_OK, r1;
|
||||
|
||||
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->entry_uncompressed_bytes_read = 0;
|
||||
zip->entry_compressed_bytes_read = 0;
|
||||
zip->entry_crc32 = crc32(0, NULL, 0);
|
||||
if ((h = __archive_read_ahead(a, 4, NULL)) == NULL)
|
||||
return (ARCHIVE_FATAL);
|
||||
|
||||
signature = (const char *)h;
|
||||
if (signature[0] == 'M' && signature[1] == 'Z') {
|
||||
/* This is an executable? Must be self-extracting... */
|
||||
r = skip_sfx(a);
|
||||
if (r < ARCHIVE_WARN)
|
||||
return (r);
|
||||
if ((h = __archive_read_ahead(a, 4, NULL)) == NULL)
|
||||
return (ARCHIVE_FATAL);
|
||||
signature = (const char *)h;
|
||||
}
|
||||
|
||||
/* If we don't see a PK signature here, scan forward. */
|
||||
if (signature[0] != 'P' || signature[1] != 'K') {
|
||||
r = search_next_signature(a);
|
||||
if (r != ARCHIVE_OK) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Bad ZIP file");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
if ((h = __archive_read_ahead(a, 4, NULL)) == NULL)
|
||||
return (ARCHIVE_FATAL);
|
||||
signature = (const char *)h;
|
||||
}
|
||||
|
||||
/*
|
||||
* "PK00" signature is used for "split" archives that
|
||||
* only have a single segment. This means we can just
|
||||
* skip the PK00; the first real file header should follow.
|
||||
*/
|
||||
if (signature[2] == '0' && signature[3] == '0') {
|
||||
__archive_read_consume(a, 4);
|
||||
if ((h = __archive_read_ahead(a, 4, NULL)) == NULL)
|
||||
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. */
|
||||
r1 = zip_read_file_header(a, entry, zip);
|
||||
if (r1 != ARCHIVE_OK)
|
||||
return (r1);
|
||||
return (r);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static int
|
||||
search_next_signature(struct archive_read *a)
|
||||
{
|
||||
const void *h;
|
||||
const char *p, *q;
|
||||
size_t skip;
|
||||
ssize_t bytes;
|
||||
int64_t skipped = 0;
|
||||
|
||||
for (;;) {
|
||||
h = __archive_read_ahead(a, 4, &bytes);
|
||||
if (h == NULL)
|
||||
return (ARCHIVE_FATAL);
|
||||
p = h;
|
||||
q = p + bytes;
|
||||
|
||||
while (p + 4 <= q) {
|
||||
if (p[0] == 'P' && p[1] == 'K') {
|
||||
if ((p[2] == '\001' && p[3] == '\002')
|
||||
|| (p[2] == '\003' && p[3] == '\004')
|
||||
|| (p[2] == '\005' && p[3] == '\006')
|
||||
|| (p[2] == '\007' && p[3] == '\010')
|
||||
|| (p[2] == '0' && p[3] == '0')) {
|
||||
skip = p - (const char *)h;
|
||||
__archive_read_consume(a, skip);
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
}
|
||||
++p;
|
||||
}
|
||||
skip = p - (const char *)h;
|
||||
__archive_read_consume(a, skip);
|
||||
skipped += skip;
|
||||
}
|
||||
}
|
||||
|
||||
static 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;
|
||||
|
||||
if ((p = __archive_read_ahead(a, sizeof *p, NULL)) == NULL) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Truncated ZIP file header");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
zip->version = p->version[0];
|
||||
zip->system = p->version[1];
|
||||
zip->flags = archive_le16dec(p->flags);
|
||||
zip->compression = archive_le16dec(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 = archive_le32dec(p->crc32);
|
||||
zip->filename_length = archive_le16dec(p->filename_length);
|
||||
zip->extra_length = archive_le16dec(p->extra_length);
|
||||
zip->uncompressed_size = archive_le32dec(p->uncompressed_size);
|
||||
zip->compressed_size = archive_le32dec(p->compressed_size);
|
||||
|
||||
__archive_read_consume(a, sizeof(struct zip_file_header));
|
||||
|
||||
|
||||
/* Read the filename. */
|
||||
if ((h = __archive_read_ahead(a, zip->filename_length, NULL)) == NULL) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Truncated ZIP file header");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
if (archive_string_ensure(&zip->pathname, zip->filename_length) == NULL)
|
||||
__archive_errx(1, "Out of memory");
|
||||
archive_strncpy(&zip->pathname, h, zip->filename_length);
|
||||
__archive_read_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. */
|
||||
if ((h = __archive_read_ahead(a, zip->extra_length, NULL)) == NULL) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Truncated ZIP file header");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
process_extra(h, zip);
|
||||
__archive_read_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);
|
||||
/* Set the size only if it's meaningful. */
|
||||
if (0 == (zip->flags & ZIP_LENGTH_AT_END))
|
||||
archive_entry_set_size(entry, zip->uncompressed_size);
|
||||
|
||||
zip->entry_bytes_remaining = zip->compressed_size;
|
||||
zip->entry_offset = 0;
|
||||
|
||||
/* If there's no body, force read_data() to return EOF immediately. */
|
||||
if (0 == (zip->flags & ZIP_LENGTH_AT_END)
|
||||
&& zip->entry_bytes_remaining < 1)
|
||||
zip->end_of_entry = 1;
|
||||
|
||||
/* 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) {
|
||||
*offset = zip->entry_uncompressed_bytes_read;
|
||||
*size = 0;
|
||||
*buff = NULL;
|
||||
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 can't decompress this entry, but we will
|
||||
* be able to skip() it and try the next entry. */
|
||||
r = ARCHIVE_WARN;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (r != ARCHIVE_OK)
|
||||
return (r);
|
||||
/* Update checksum */
|
||||
if (*size)
|
||||
zip->entry_crc32 = crc32(zip->entry_crc32, *buff, *size);
|
||||
/* If we hit the end, swallow any end-of-data marker. */
|
||||
if (zip->end_of_entry) {
|
||||
if (zip->flags & ZIP_LENGTH_AT_END) {
|
||||
const char *p;
|
||||
|
||||
if ((p = __archive_read_ahead(a, 16, NULL)) == NULL) {
|
||||
archive_set_error(&a->archive,
|
||||
ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Truncated ZIP end-of-file record");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
zip->crc32 = archive_le32dec(p + 4);
|
||||
zip->compressed_size = archive_le32dec(p + 8);
|
||||
zip->uncompressed_size = archive_le32dec(p + 12);
|
||||
__archive_read_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);
|
||||
}
|
||||
/* Check computed CRC against header */
|
||||
if (zip->crc32 != zip->entry_crc32) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"ZIP bad CRC: 0x%lx should be 0x%lx",
|
||||
zip->entry_crc32, zip->crc32);
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
}
|
||||
|
||||
/* Return EOF immediately if this is a non-regular file. */
|
||||
if (AE_IFREG != (zip->mode & AE_IFMT))
|
||||
return (ARCHIVE_EOF);
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
*buff = __archive_read_ahead(a, 1, &bytes_avail);
|
||||
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;
|
||||
__archive_read_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) {
|
||||
if (zip->stream_valid)
|
||||
r = inflateReset(&zip->stream);
|
||||
else
|
||||
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);
|
||||
}
|
||||
/* Stream structure has been set up. */
|
||||
zip->stream_valid = 1;
|
||||
/* We've initialized decompression for this stream. */
|
||||
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.
|
||||
*/
|
||||
compressed_buff = __archive_read_ahead(a, 1, &bytes_avail);
|
||||
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;
|
||||
__archive_read_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;
|
||||
off_t bytes_skipped;
|
||||
|
||||
zip = (struct zip *)(a->format->data);
|
||||
|
||||
/* If we've already read to end of data, we're done. */
|
||||
if (zip->end_of_entry)
|
||||
return (ARCHIVE_OK);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
bytes_skipped = __archive_read_skip(a, zip->entry_bytes_remaining);
|
||||
if (bytes_skipped < 0)
|
||||
return (ARCHIVE_FATAL);
|
||||
|
||||
/* This entry is finished and done. */
|
||||
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);
|
||||
#ifdef HAVE_ZLIB_H
|
||||
if (zip->stream_valid)
|
||||
inflateEnd(&zip->stream);
|
||||
#endif
|
||||
free(zip->uncompressed_buffer);
|
||||
archive_string_free(&(zip->pathname));
|
||||
archive_string_free(&(zip->extra));
|
||||
free(zip);
|
||||
(a->format->data) = NULL;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 = archive_le16dec(p + offset);
|
||||
unsigned short datasize = archive_le16dec(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 = archive_le64dec(p + offset);
|
||||
if (datasize >= 16)
|
||||
zip->compressed_size = archive_le64dec(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: %lld -> %d\n",
|
||||
(long long)zip->mtime,
|
||||
archive_le32dec(p + offset));
|
||||
#endif
|
||||
if (datasize < 4)
|
||||
break;
|
||||
zip->mtime = archive_le32dec(p + offset);
|
||||
offset += 4;
|
||||
datasize -= 4;
|
||||
}
|
||||
if (flags & 0x02)
|
||||
{
|
||||
if (datasize < 4)
|
||||
break;
|
||||
zip->atime = archive_le32dec(p + offset);
|
||||
offset += 4;
|
||||
datasize -= 4;
|
||||
}
|
||||
if (flags & 0x04)
|
||||
{
|
||||
if (datasize < 4)
|
||||
break;
|
||||
zip->ctime = archive_le32dec(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",
|
||||
archive_le16dec(p + offset),
|
||||
archive_le16dec(p + offset + 2));
|
||||
#endif
|
||||
if (datasize >= 2)
|
||||
zip->uid = archive_le16dec(p + offset);
|
||||
if (datasize >= 4)
|
||||
zip->gid = archive_le16dec(p + offset + 2);
|
||||
break;
|
||||
case 0x7875:
|
||||
/* Info-Zip Unix Extra Field (type 3) "ux". */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
offset += datasize;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
if (offset != zip->extra_length)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Extra data field contents do not match reported size!");
|
||||
}
|
||||
#endif
|
||||
}
|
@ -1,453 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
/*
|
||||
* Basic resizable string support, to simplify manipulating arbitrary-sized
|
||||
* strings while minimizing heap activity.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_WCHAR_H
|
||||
#include <wchar.h>
|
||||
#endif
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
#include <windows.h>
|
||||
#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)
|
||||
{
|
||||
if (__archive_string_ensure(as, as->length + s + 1) == NULL)
|
||||
__archive_errx(1, "Out of memory");
|
||||
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)
|
||||
{
|
||||
if (src->length == 0)
|
||||
dest->length = 0;
|
||||
else {
|
||||
if (__archive_string_ensure(dest, src->length + 1) == NULL)
|
||||
__archive_errx(1, "Out of memory");
|
||||
memcpy(dest->s, src->s, src->length);
|
||||
dest->length = src->length;
|
||||
dest->s[dest->length] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
__archive_string_concat(struct archive_string *dest, struct archive_string *src)
|
||||
{
|
||||
if (src->length > 0) {
|
||||
if (__archive_string_ensure(dest, dest->length + src->length + 1) == NULL)
|
||||
__archive_errx(1, "Out of memory");
|
||||
memcpy(dest->s + dest->length, 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);
|
||||
as->s = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns NULL on any allocation failure. */
|
||||
struct archive_string *
|
||||
__archive_string_ensure(struct archive_string *as, size_t s)
|
||||
{
|
||||
/* If buffer is already big enough, don't reallocate. */
|
||||
if (as->s && (s <= as->buffer_length))
|
||||
return (as);
|
||||
|
||||
/*
|
||||
* Growing the buffer at least exponentially ensures that
|
||||
* append operations are always linear in the number of
|
||||
* characters appended. Using a smaller growth rate for
|
||||
* larger buffers reduces memory waste somewhat at the cost of
|
||||
* a larger constant factor.
|
||||
*/
|
||||
if (as->buffer_length < 32)
|
||||
/* Start with a minimum 32-character buffer. */
|
||||
as->buffer_length = 32;
|
||||
else if (as->buffer_length < 8192)
|
||||
/* Buffers under 8k are doubled for speed. */
|
||||
as->buffer_length += as->buffer_length;
|
||||
else {
|
||||
/* Buffers 8k and over grow by at least 25% each time. */
|
||||
size_t old_length = as->buffer_length;
|
||||
as->buffer_length += as->buffer_length / 4;
|
||||
/* Be safe: If size wraps, release buffer and return NULL. */
|
||||
if (as->buffer_length < old_length) {
|
||||
free(as->s);
|
||||
as->s = NULL;
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* The computation above is a lower limit to how much we'll
|
||||
* grow the buffer. In any case, we have to grow it enough to
|
||||
* hold the request.
|
||||
*/
|
||||
if (as->buffer_length < s)
|
||||
as->buffer_length = s;
|
||||
/* Now we can reallocate the buffer. */
|
||||
as->s = (char *)realloc(as->s, as->buffer_length);
|
||||
if (as->s == NULL)
|
||||
return (NULL);
|
||||
return (as);
|
||||
}
|
||||
|
||||
struct archive_string *
|
||||
__archive_strncat(struct archive_string *as, const void *_p, size_t n)
|
||||
{
|
||||
size_t s;
|
||||
const char *p, *pp;
|
||||
|
||||
p = (const char *)_p;
|
||||
|
||||
/* Like strlen(p), except won't examine positions beyond p[n]. */
|
||||
s = 0;
|
||||
pp = p;
|
||||
while (s < n && *pp) {
|
||||
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));
|
||||
}
|
||||
|
||||
/*
|
||||
* Translates a wide character string into UTF-8 and appends
|
||||
* to the archive_string. Note: returns NULL if conversion fails,
|
||||
* but still leaves a best-effort conversion in the argument as.
|
||||
*/
|
||||
struct archive_string *
|
||||
__archive_strappend_w_utf8(struct archive_string *as, const wchar_t *w)
|
||||
{
|
||||
char *p;
|
||||
unsigned wc;
|
||||
char buff[256];
|
||||
struct archive_string *return_val = as;
|
||||
|
||||
/*
|
||||
* Convert one wide char at a time into 'buff', whenever that
|
||||
* fills, append it to the string.
|
||||
*/
|
||||
p = buff;
|
||||
while (*w != L'\0') {
|
||||
/* Flush the buffer when we have <=16 bytes free. */
|
||||
/* (No encoding has a single character >16 bytes.) */
|
||||
if ((size_t)(p - buff) >= (size_t)(sizeof(buff) - 16)) {
|
||||
*p = '\0';
|
||||
archive_strcat(as, buff);
|
||||
p = buff;
|
||||
}
|
||||
wc = *w++;
|
||||
/* If this is a surrogate pair, assemble the full code point.*/
|
||||
/* Note: wc must not be wchar_t here, because the full code
|
||||
* point can be more than 16 bits! */
|
||||
if (wc >= 0xD800 && wc <= 0xDBff
|
||||
&& *w >= 0xDC00 && *w <= 0xDFFF) {
|
||||
wc -= 0xD800;
|
||||
wc *= 0x400;
|
||||
wc += (*w - 0xDC00);
|
||||
wc += 0x10000;
|
||||
++w;
|
||||
}
|
||||
/* Translate code point to UTF8 */
|
||||
if (wc <= 0x7f) {
|
||||
*p++ = (char)wc;
|
||||
} else if (wc <= 0x7ff) {
|
||||
*p++ = 0xc0 | ((wc >> 6) & 0x1f);
|
||||
*p++ = 0x80 | (wc & 0x3f);
|
||||
} else if (wc <= 0xffff) {
|
||||
*p++ = 0xe0 | ((wc >> 12) & 0x0f);
|
||||
*p++ = 0x80 | ((wc >> 6) & 0x3f);
|
||||
*p++ = 0x80 | (wc & 0x3f);
|
||||
} else if (wc <= 0x1fffff) {
|
||||
*p++ = 0xf0 | ((wc >> 18) & 0x07);
|
||||
*p++ = 0x80 | ((wc >> 12) & 0x3f);
|
||||
*p++ = 0x80 | ((wc >> 6) & 0x3f);
|
||||
*p++ = 0x80 | (wc & 0x3f);
|
||||
} else {
|
||||
/* Unicode has no codes larger than 0x1fffff. */
|
||||
/* TODO: use \uXXXX escape here instead of ? */
|
||||
*p++ = '?';
|
||||
return_val = NULL;
|
||||
}
|
||||
}
|
||||
*p = '\0';
|
||||
archive_strcat(as, buff);
|
||||
return (return_val);
|
||||
}
|
||||
|
||||
static int
|
||||
utf8_to_unicode(int *pwc, const char *s, size_t n)
|
||||
{
|
||||
int ch;
|
||||
|
||||
/*
|
||||
* Decode 1-4 bytes depending on the value of the first byte.
|
||||
*/
|
||||
ch = (unsigned char)*s;
|
||||
if (ch == 0) {
|
||||
return (0); /* Standard: return 0 for end-of-string. */
|
||||
}
|
||||
if ((ch & 0x80) == 0) {
|
||||
*pwc = ch & 0x7f;
|
||||
return (1);
|
||||
}
|
||||
if ((ch & 0xe0) == 0xc0) {
|
||||
if (n < 2)
|
||||
return (-1);
|
||||
if ((s[1] & 0xc0) != 0x80) return (-1);
|
||||
*pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f);
|
||||
return (2);
|
||||
}
|
||||
if ((ch & 0xf0) == 0xe0) {
|
||||
if (n < 3)
|
||||
return (-1);
|
||||
if ((s[1] & 0xc0) != 0x80) return (-1);
|
||||
if ((s[2] & 0xc0) != 0x80) return (-1);
|
||||
*pwc = ((ch & 0x0f) << 12)
|
||||
| ((s[1] & 0x3f) << 6)
|
||||
| (s[2] & 0x3f);
|
||||
return (3);
|
||||
}
|
||||
if ((ch & 0xf8) == 0xf0) {
|
||||
if (n < 4)
|
||||
return (-1);
|
||||
if ((s[1] & 0xc0) != 0x80) return (-1);
|
||||
if ((s[2] & 0xc0) != 0x80) return (-1);
|
||||
if ((s[3] & 0xc0) != 0x80) return (-1);
|
||||
*pwc = ((ch & 0x07) << 18)
|
||||
| ((s[1] & 0x3f) << 12)
|
||||
| ((s[2] & 0x3f) << 6)
|
||||
| (s[3] & 0x3f);
|
||||
return (4);
|
||||
}
|
||||
/* Invalid first byte. */
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a wide-character Unicode string by converting this archive_string
|
||||
* from UTF-8. We assume that systems with 16-bit wchar_t always use
|
||||
* UTF16 and systems with 32-bit wchar_t can accept UCS4.
|
||||
*/
|
||||
wchar_t *
|
||||
__archive_string_utf8_w(struct archive_string *as)
|
||||
{
|
||||
wchar_t *ws, *dest;
|
||||
int wc, wc2;/* Must be large enough for a 21-bit Unicode code point. */
|
||||
const char *src;
|
||||
int n;
|
||||
|
||||
ws = (wchar_t *)malloc((as->length + 1) * sizeof(wchar_t));
|
||||
if (ws == NULL)
|
||||
__archive_errx(1, "Out of memory");
|
||||
dest = ws;
|
||||
src = as->s;
|
||||
while (*src != '\0') {
|
||||
n = utf8_to_unicode(&wc, src, 8);
|
||||
if (n == 0)
|
||||
break;
|
||||
if (n < 0) {
|
||||
free(ws);
|
||||
return (NULL);
|
||||
}
|
||||
src += n;
|
||||
if (wc >= 0xDC00 && wc <= 0xDBFF) {
|
||||
/* This is a leading surrogate; some idiot
|
||||
* has translated UTF16 to UTF8 without combining
|
||||
* surrogates; rebuild the full code point before
|
||||
* continuing. */
|
||||
n = utf8_to_unicode(&wc2, src, 8);
|
||||
if (n < 0) {
|
||||
free(ws);
|
||||
return (NULL);
|
||||
}
|
||||
if (n == 0) /* Ignore the leading surrogate */
|
||||
break;
|
||||
if (wc2 < 0xDC00 || wc2 > 0xDFFF) {
|
||||
/* If the second character isn't a
|
||||
* trailing surrogate, then someone
|
||||
* has really screwed up and this is
|
||||
* invalid. */
|
||||
free(ws);
|
||||
return (NULL);
|
||||
} else {
|
||||
src += n;
|
||||
wc -= 0xD800;
|
||||
wc *= 0x400;
|
||||
wc += wc2 - 0xDC00;
|
||||
wc += 0x10000;
|
||||
}
|
||||
}
|
||||
if ((sizeof(wchar_t) < 4) && (wc > 0xffff)) {
|
||||
/* We have a code point that won't fit into a
|
||||
* wchar_t; convert it to a surrogate pair. */
|
||||
wc -= 0x10000;
|
||||
*dest++ = ((wc >> 10) & 0x3ff) + 0xD800;
|
||||
*dest++ = (wc & 0x3ff) + 0xDC00;
|
||||
} else
|
||||
*dest++ = wc;
|
||||
}
|
||||
*dest = L'\0';
|
||||
return (ws);
|
||||
}
|
||||
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
|
||||
/*
|
||||
* Translates a wide character string into current locale character set
|
||||
* and appends to the archive_string. Note: returns NULL if conversion
|
||||
* fails.
|
||||
*
|
||||
* Win32 builds use WideCharToMultiByte from the Windows API.
|
||||
* (Maybe Cygwin should too? WideCharToMultiByte will know a
|
||||
* lot more about local character encodings than the wcrtomb()
|
||||
* wrapper is going to know.)
|
||||
*/
|
||||
struct archive_string *
|
||||
__archive_strappend_w_mbs(struct archive_string *as, const wchar_t *w)
|
||||
{
|
||||
char *p;
|
||||
int l, wl;
|
||||
BOOL useDefaultChar = FALSE;
|
||||
|
||||
wl = (int)wcslen(w);
|
||||
l = wl * 4 + 4;
|
||||
p = malloc(l);
|
||||
if (p == NULL)
|
||||
__archive_errx(1, "Out of memory");
|
||||
/* To check a useDefaultChar is to simulate error handling of
|
||||
* the my_wcstombs() which is running on non Windows system with
|
||||
* wctomb().
|
||||
* And to set NULL for last argument is necessary when a codepage
|
||||
* is not CP_ACP(current locale).
|
||||
*/
|
||||
l = WideCharToMultiByte(CP_ACP, 0, w, wl, p, l, NULL, &useDefaultChar);
|
||||
if (l == 0) {
|
||||
free(p);
|
||||
return (NULL);
|
||||
}
|
||||
__archive_string_append(as, p, l);
|
||||
free(p);
|
||||
return (as);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Translates a wide character string into current locale character set
|
||||
* and appends to the archive_string. Note: returns NULL if conversion
|
||||
* fails.
|
||||
*
|
||||
* Non-Windows uses ISO C wcrtomb() or wctomb() to perform the conversion
|
||||
* one character at a time. If a non-Windows platform doesn't have
|
||||
* either of these, fall back to the built-in UTF8 conversion.
|
||||
*/
|
||||
struct archive_string *
|
||||
__archive_strappend_w_mbs(struct archive_string *as, const wchar_t *w)
|
||||
{
|
||||
#if !defined(HAVE_WCTOMB) && !defined(HAVE_WCRTOMB)
|
||||
/* If there's no built-in locale support, fall back to UTF8 always. */
|
||||
return __archive_strappend_w_utf8(as, w);
|
||||
#else
|
||||
/* We cannot use the standard wcstombs() here because it
|
||||
* cannot tell us how big the output buffer should be. So
|
||||
* I've built a loop around wcrtomb() or wctomb() that
|
||||
* converts a character at a time and resizes the string as
|
||||
* needed. We prefer wcrtomb() when it's available because
|
||||
* it's thread-safe. */
|
||||
int n;
|
||||
char *p;
|
||||
char buff[256];
|
||||
#if HAVE_WCRTOMB
|
||||
mbstate_t shift_state;
|
||||
|
||||
memset(&shift_state, 0, sizeof(shift_state));
|
||||
#else
|
||||
/* Clear the shift state before starting. */
|
||||
wctomb(NULL, L'\0');
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Convert one wide char at a time into 'buff', whenever that
|
||||
* fills, append it to the string.
|
||||
*/
|
||||
p = buff;
|
||||
while (*w != L'\0') {
|
||||
/* Flush the buffer when we have <=16 bytes free. */
|
||||
/* (No encoding has a single character >16 bytes.) */
|
||||
if ((size_t)(p - buff) >= (size_t)(sizeof(buff) - MB_CUR_MAX)) {
|
||||
*p = '\0';
|
||||
archive_strcat(as, buff);
|
||||
p = buff;
|
||||
}
|
||||
#if HAVE_WCRTOMB
|
||||
n = wcrtomb(p, *w++, &shift_state);
|
||||
#else
|
||||
n = wctomb(p, *w++);
|
||||
#endif
|
||||
if (n == -1)
|
||||
return (NULL);
|
||||
p += n;
|
||||
}
|
||||
*p = '\0';
|
||||
archive_strcat(as, buff);
|
||||
return (as);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* _WIN32 && ! __CYGWIN__ */
|
@ -1,151 +0,0 @@
|
||||
/*-
|
||||
* 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$
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __LIBARCHIVE_BUILD
|
||||
#error This header is only to be used internally to libarchive.
|
||||
#endif
|
||||
|
||||
#ifndef ARCHIVE_STRING_H_INCLUDED
|
||||
#define ARCHIVE_STRING_H_INCLUDED
|
||||
|
||||
#include <stdarg.h>
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h> /* required for wchar_t on some systems */
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_WCHAR_H
|
||||
#include <wchar.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
||||
/* Convert a wide-char string to UTF-8 and append the result. */
|
||||
struct archive_string *
|
||||
__archive_strappend_w_utf8(struct archive_string *, const wchar_t *);
|
||||
#define archive_strappend_w_utf8 __archive_strappend_w_utf8
|
||||
|
||||
/* Convert a wide-char string to current locale and append the result. */
|
||||
/* Returns NULL if conversion fails. */
|
||||
struct archive_string *
|
||||
__archive_strappend_w_mbs(struct archive_string *, const wchar_t *);
|
||||
#define archive_strappend_w_mbs __archive_strappend_w_mbs
|
||||
|
||||
/* 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)
|
||||
|
||||
/* Concatenate one archive_string to another */
|
||||
void
|
||||
__archive_string_concat(struct archive_string *dest, struct archive_string *src);
|
||||
#define archive_string_concat(dest, src) \
|
||||
__archive_string_concat(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. */
|
||||
/* The source is declared void * here because this gets used with
|
||||
* "signed char *", "unsigned char *" and "char *" arguments.
|
||||
* Declaring it "char *" as with some of the other functions just
|
||||
* leads to a lot of extra casts. */
|
||||
struct archive_string *
|
||||
__archive_strncat(struct archive_string *, const void *, 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), p == NULL ? 0 : 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) __LA_PRINTF(2, 0);
|
||||
#define archive_string_vsprintf __archive_string_vsprintf
|
||||
|
||||
void __archive_string_sprintf(struct archive_string *, const char *, ...)
|
||||
__LA_PRINTF(2, 3);
|
||||
#define archive_string_sprintf __archive_string_sprintf
|
||||
|
||||
/* Allocates a fresh buffer and converts as (assumed to be UTF-8) into it.
|
||||
* Returns NULL if conversion failed in any way. */
|
||||
wchar_t *__archive_string_utf8_w(struct archive_string *as);
|
||||
|
||||
|
||||
#endif
|
@ -1,164 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
/*
|
||||
* 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. <sigh>
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
|
||||
#include "archive_string.h"
|
||||
#include "archive_private.h"
|
||||
|
||||
/*
|
||||
* Utility functions to format signed/unsigned integers and append
|
||||
* them to an archive_string.
|
||||
*/
|
||||
static void
|
||||
append_uint(struct archive_string *as, uintmax_t d, unsigned base)
|
||||
{
|
||||
static const char *digits = "0123456789abcdef";
|
||||
if (d >= base)
|
||||
append_uint(as, d/base, base);
|
||||
archive_strappend_char(as, digits[d % base]);
|
||||
}
|
||||
|
||||
static void
|
||||
append_int(struct archive_string *as, intmax_t d, unsigned base)
|
||||
{
|
||||
if (d < 0) {
|
||||
archive_strappend_char(as, '-');
|
||||
d = -d;
|
||||
}
|
||||
append_uint(as, d, base);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
__archive_string_sprintf(struct archive_string *as, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
archive_string_vsprintf(as, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
if (__archive_string_ensure(as, 64) == NULL)
|
||||
__archive_errx(1, "Out of memory");
|
||||
|
||||
if (fmt == NULL) {
|
||||
as->s[0] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
for (p = fmt; *p != '\0'; p++) {
|
||||
const char *saved_p = p;
|
||||
|
||||
if (*p != '%') {
|
||||
archive_strappend_char(as, *p);
|
||||
continue;
|
||||
}
|
||||
|
||||
p++;
|
||||
|
||||
long_flag = '\0';
|
||||
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;
|
||||
}
|
||||
append_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': append_uint(as, u, 8); break;
|
||||
case 'u': append_uint(as, u, 10); break;
|
||||
default: append_uint(as, u, 16); break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* Rewind and print the initial '%' literally. */
|
||||
p = saved_p;
|
||||
archive_strappend_char(as, *p);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,160 +0,0 @@
|
||||
.\" 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 January 8, 2005
|
||||
.Dt ARCHIVE_UTIL 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm archive_clear_error ,
|
||||
.Nm archive_compression ,
|
||||
.Nm archive_compression_name ,
|
||||
.Nm archive_copy_error ,
|
||||
.Nm archive_errno ,
|
||||
.Nm archive_error_string ,
|
||||
.Nm archive_file_count ,
|
||||
.Nm archive_format ,
|
||||
.Nm archive_format_name ,
|
||||
.Nm archive_set_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 void
|
||||
.Fn archive_copy_error "struct archive *" "struct archive *"
|
||||
.Ft int
|
||||
.Fn archive_errno "struct archive *"
|
||||
.Ft const char *
|
||||
.Fn archive_error_string "struct archive *"
|
||||
.Ft int
|
||||
.Fn archive_file_count "struct archive *"
|
||||
.Ft int
|
||||
.Fn archive_format "struct archive *"
|
||||
.Ft const char *
|
||||
.Fn archive_format_name "struct archive *"
|
||||
.Ft void
|
||||
.Fo archive_set_error
|
||||
.Fa "struct archive *"
|
||||
.Fa "int error_code"
|
||||
.Fa "const char *fmt"
|
||||
.Fa "..."
|
||||
.Fc
|
||||
.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_copy_error
|
||||
Copies error information from one archive to another.
|
||||
.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_file_count
|
||||
Returns a count of the number of files processed by this archive object.
|
||||
The count is incremented by calls to
|
||||
.Xr archive_write_header
|
||||
or
|
||||
.Xr archive_read_next_header .
|
||||
.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.
|
||||
.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 .
|
@ -1,392 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2009 Michihiro NAKAJIMA
|
||||
* 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$");
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_private.h"
|
||||
#include "archive_string.h"
|
||||
|
||||
#if ARCHIVE_VERSION_NUMBER < 3000000
|
||||
/* These disappear in libarchive 3.0 */
|
||||
/* Deprecated. */
|
||||
int
|
||||
archive_api_feature(void)
|
||||
{
|
||||
return (ARCHIVE_API_FEATURE);
|
||||
}
|
||||
|
||||
/* Deprecated. */
|
||||
int
|
||||
archive_api_version(void)
|
||||
{
|
||||
return (ARCHIVE_API_VERSION);
|
||||
}
|
||||
|
||||
/* Deprecated synonym for archive_version_number() */
|
||||
int
|
||||
archive_version_stamp(void)
|
||||
{
|
||||
return (archive_version_number());
|
||||
}
|
||||
|
||||
/* Deprecated synonym for archive_version_string() */
|
||||
const char *
|
||||
archive_version(void)
|
||||
{
|
||||
return (archive_version_string());
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
archive_version_number(void)
|
||||
{
|
||||
return (ARCHIVE_VERSION_NUMBER);
|
||||
}
|
||||
|
||||
const char *
|
||||
archive_version_string(void)
|
||||
{
|
||||
return (ARCHIVE_VERSION_STRING);
|
||||
}
|
||||
|
||||
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_file_count(struct archive *a)
|
||||
{
|
||||
return (a->file_count);
|
||||
}
|
||||
|
||||
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;
|
||||
a->archive_error_number = 0;
|
||||
}
|
||||
|
||||
void
|
||||
archive_set_error(struct archive *a, int error_number, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
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);
|
||||
va_end(ap);
|
||||
a->error = a->error_string.s;
|
||||
}
|
||||
|
||||
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: ";
|
||||
size_t s;
|
||||
|
||||
s = write(2, msg1, strlen(msg1));
|
||||
(void)s; /* UNUSED */
|
||||
s = write(2, msg, strlen(msg));
|
||||
(void)s; /* UNUSED */
|
||||
s = write(2, "\n", 1);
|
||||
(void)s; /* UNUSED */
|
||||
exit(retvalue);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse option strings
|
||||
* Detail of option format.
|
||||
* - The option can accept:
|
||||
* "opt-name", "!opt-name", "opt-name=value".
|
||||
*
|
||||
* - The option entries are separated by comma.
|
||||
* e.g "compression=9,opt=XXX,opt-b=ZZZ"
|
||||
*
|
||||
* - The name of option string consist of '-' and alphabet
|
||||
* but character '-' cannot be used for the first character.
|
||||
* (Regular expression is [a-z][-a-z]+)
|
||||
*
|
||||
* - For a specfic format/filter, using the format name with ':'.
|
||||
* e.g "zip:compression=9"
|
||||
* (This "compression=9" option entry is for "zip" format only)
|
||||
*
|
||||
* If another entries follow it, those are not for
|
||||
* the specfic format/filter.
|
||||
* e.g handle "zip:compression=9,opt=XXX,opt-b=ZZZ"
|
||||
* "zip" format/filter handler will get "compression=9"
|
||||
* all format/filter handler will get "opt=XXX"
|
||||
* all format/filter handler will get "opt-b=ZZZ"
|
||||
*
|
||||
* - Whitespace and tab are bypassed.
|
||||
*
|
||||
*/
|
||||
int
|
||||
__archive_parse_options(const char *p, const char *fn, int keysize, char *key,
|
||||
int valsize, char *val)
|
||||
{
|
||||
const char *p_org;
|
||||
int apply;
|
||||
int kidx, vidx;
|
||||
int negative;
|
||||
enum {
|
||||
/* Requested for initialization. */
|
||||
INIT,
|
||||
/* Finding format/filter-name and option-name. */
|
||||
F_BOTH,
|
||||
/* Finding option-name only.
|
||||
* (already detected format/filter-name) */
|
||||
F_NAME,
|
||||
/* Getting option-value. */
|
||||
G_VALUE,
|
||||
} state;
|
||||
|
||||
p_org = p;
|
||||
state = INIT;
|
||||
kidx = vidx = negative = 0;
|
||||
apply = 1;
|
||||
while (*p) {
|
||||
switch (state) {
|
||||
case INIT:
|
||||
kidx = vidx = 0;
|
||||
negative = 0;
|
||||
apply = 1;
|
||||
state = F_BOTH;
|
||||
break;
|
||||
case F_BOTH:
|
||||
case F_NAME:
|
||||
if ((*p >= 'a' && *p <= 'z') ||
|
||||
(*p >= '0' && *p <= '9') || *p == '-') {
|
||||
if (kidx == 0 && !(*p >= 'a' && *p <= 'z'))
|
||||
/* Illegal sequence. */
|
||||
return (-1);
|
||||
if (kidx >= keysize -1)
|
||||
/* Too many characters. */
|
||||
return (-1);
|
||||
key[kidx++] = *p++;
|
||||
} else if (*p == '!') {
|
||||
if (kidx != 0)
|
||||
/* Illegal sequence. */
|
||||
return (-1);
|
||||
negative = 1;
|
||||
++p;
|
||||
} else if (*p == ',') {
|
||||
if (kidx == 0)
|
||||
/* Illegal sequence. */
|
||||
return (-1);
|
||||
if (!negative)
|
||||
val[vidx++] = '1';
|
||||
/* We have got boolean option data. */
|
||||
++p;
|
||||
if (apply)
|
||||
goto complete;
|
||||
else
|
||||
/* This option does not apply to the
|
||||
* format which the fn variable
|
||||
* indicate. */
|
||||
state = INIT;
|
||||
} else if (*p == ':') {
|
||||
/* obuf data is format name */
|
||||
if (state == F_NAME)
|
||||
/* We already found it. */
|
||||
return (-1);
|
||||
if (kidx == 0)
|
||||
/* Illegal sequence. */
|
||||
return (-1);
|
||||
if (negative)
|
||||
/* We cannot accept "!format-name:". */
|
||||
return (-1);
|
||||
key[kidx] = '\0';
|
||||
if (strcmp(fn, key) != 0)
|
||||
/* This option does not apply to the
|
||||
* format which the fn variable
|
||||
* indicate. */
|
||||
apply = 0;
|
||||
kidx = 0;
|
||||
++p;
|
||||
state = F_NAME;
|
||||
} else if (*p == '=') {
|
||||
if (kidx == 0)
|
||||
/* Illegal sequence. */
|
||||
return (-1);
|
||||
if (negative)
|
||||
/* We cannot accept "!opt-name=value". */
|
||||
return (-1);
|
||||
++p;
|
||||
state = G_VALUE;
|
||||
} else if (*p == ' ') {
|
||||
/* Pass the space character */
|
||||
++p;
|
||||
} else {
|
||||
/* Illegal character. */
|
||||
return (-1);
|
||||
}
|
||||
break;
|
||||
case G_VALUE:
|
||||
if (*p == ',') {
|
||||
if (vidx == 0)
|
||||
/* Illegal sequence. */
|
||||
return (-1);
|
||||
/* We have got option data. */
|
||||
++p;
|
||||
if (apply)
|
||||
goto complete;
|
||||
else
|
||||
/* This option does not apply to the
|
||||
* format which the fn variable
|
||||
* indicate. */
|
||||
state = INIT;
|
||||
} else if (*p == ' ') {
|
||||
/* Pass the space character */
|
||||
++p;
|
||||
} else {
|
||||
if (vidx >= valsize -1)
|
||||
/* Too many characters. */
|
||||
return (-1);
|
||||
val[vidx++] = *p++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
case F_BOTH:
|
||||
case F_NAME:
|
||||
if (kidx != 0) {
|
||||
if (!negative)
|
||||
val[vidx++] = '1';
|
||||
/* We have got boolean option. */
|
||||
if (apply)
|
||||
/* This option apply to the format which the
|
||||
* fn variable indicate. */
|
||||
goto complete;
|
||||
}
|
||||
break;
|
||||
case G_VALUE:
|
||||
if (vidx == 0)
|
||||
/* Illegal sequence. */
|
||||
return (-1);
|
||||
/* We have got option value. */
|
||||
if (apply)
|
||||
/* This option apply to the format which the fn
|
||||
* variable indicate. */
|
||||
goto complete;
|
||||
break;
|
||||
case INIT:/* nothing */
|
||||
break;
|
||||
}
|
||||
|
||||
/* End of Option string. */
|
||||
return (0);
|
||||
|
||||
complete:
|
||||
key[kidx] = '\0';
|
||||
val[vidx] = '\0';
|
||||
/* Return a size which we've consumed for detecting option */
|
||||
return ((int)(p - p_org));
|
||||
}
|
@ -1,98 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_entry.h"
|
||||
#include "archive_private.h"
|
||||
|
||||
int
|
||||
archive_write_close(struct archive *a)
|
||||
{
|
||||
return ((a->vtable->archive_close)(a));
|
||||
}
|
||||
|
||||
int
|
||||
archive_read_close(struct archive *a)
|
||||
{
|
||||
return ((a->vtable->archive_close)(a));
|
||||
}
|
||||
|
||||
int
|
||||
archive_write_free(struct archive *a)
|
||||
{
|
||||
return ((a->vtable->archive_free)(a));
|
||||
}
|
||||
|
||||
#if ARCHIVE_VERSION_NUMBER < 4000000
|
||||
/* For backwards compatibility; will be removed with libarchive 4.0. */
|
||||
int
|
||||
archive_write_finish(struct archive *a)
|
||||
{
|
||||
return ((a->vtable->archive_free)(a));
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
archive_read_free(struct archive *a)
|
||||
{
|
||||
return ((a->vtable->archive_free)(a));
|
||||
}
|
||||
|
||||
#if ARCHIVE_VERSION_NUMBER < 4000000
|
||||
/* For backwards compatibility; will be removed with libarchive 4.0. */
|
||||
int
|
||||
archive_read_finish(struct archive *a)
|
||||
{
|
||||
return ((a->vtable->archive_free)(a));
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
archive_write_header(struct archive *a, struct archive_entry *entry)
|
||||
{
|
||||
++a->file_count;
|
||||
return ((a->vtable->archive_write_header)(a, entry));
|
||||
}
|
||||
|
||||
int
|
||||
archive_write_finish_entry(struct archive *a)
|
||||
{
|
||||
return ((a->vtable->archive_write_finish_entry)(a));
|
||||
}
|
||||
|
||||
ssize_t
|
||||
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));
|
||||
}
|
@ -1,629 +0,0 @@
|
||||
.\" 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 May 11, 2008
|
||||
.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_compression_bzip2 ,
|
||||
.Nm archive_write_set_compression_compress ,
|
||||
.Nm archive_write_set_compression_gzip ,
|
||||
.Nm archive_write_set_compression_none ,
|
||||
.Nm archive_write_set_compression_program ,
|
||||
.Nm archive_write_set_compressor_options ,
|
||||
.Nm archive_write_set_format_options ,
|
||||
.Nm archive_write_set_options ,
|
||||
.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_free
|
||||
.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_compression_bzip2 "struct archive *"
|
||||
.Ft int
|
||||
.Fn archive_write_set_compression_compress "struct archive *"
|
||||
.Ft int
|
||||
.Fn archive_write_set_compression_gzip "struct archive *"
|
||||
.Ft int
|
||||
.Fn archive_write_set_compression_none "struct archive *"
|
||||
.Ft int
|
||||
.Fn archive_write_set_compression_program "struct archive *" "const char * cmd"
|
||||
.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_set_format_options "struct archive *" "const char *"
|
||||
.Ft int
|
||||
.Fn archive_write_set_compressor_options "struct archive *" "const char *"
|
||||
.Ft int
|
||||
.Fn archive_write_set_options "struct archive *" "const char *"
|
||||
.Ft int
|
||||
.Fo archive_write_open
|
||||
.Fa "struct archive *"
|
||||
.Fa "void *client_data"
|
||||
.Fa "archive_open_callback *"
|
||||
.Fa "archive_write_callback *"
|
||||
.Fa "archive_close_callback *"
|
||||
.Fc
|
||||
.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
|
||||
.Fo archive_write_open_memory
|
||||
.Fa "struct archive *"
|
||||
.Fa "void *buffer"
|
||||
.Fa "size_t bufferSize"
|
||||
.Fa "size_t *outUsed"
|
||||
.Fc
|
||||
.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_free "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 Xo
|
||||
.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
|
||||
.Xc
|
||||
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 Xo
|
||||
.Fn archive_write_set_compression_bzip2 ,
|
||||
.Fn archive_write_set_compression_compress ,
|
||||
.Fn archive_write_set_compression_gzip ,
|
||||
.Fn archive_write_set_compression_none
|
||||
.Xc
|
||||
The resulting archive will be compressed as specified.
|
||||
Note that the compressed output is always properly blocked.
|
||||
.It Fn archive_write_set_compression_program
|
||||
The archive will be fed into the specified compression program.
|
||||
The output of that program is blocked and written to the client
|
||||
write callbacks.
|
||||
.It Xo
|
||||
.Fn archive_write_set_compressor_options ,
|
||||
.Fn archive_write_set_format_options ,
|
||||
.Fn archive_write_set_options
|
||||
.Xc
|
||||
Specifies options that will be passed to the currently-enabled
|
||||
compressor and/or format writer.
|
||||
The argument is a comma-separated list of individual options.
|
||||
Individual options have one of the following forms:
|
||||
.Bl -tag -compact -width indent
|
||||
.It Ar option=value
|
||||
The option/value pair will be provided to every module.
|
||||
Modules that do not accept an option with this name will ignore it.
|
||||
.It Ar option
|
||||
The option will be provided to every module with a value of
|
||||
.Dq 1 .
|
||||
.It Ar !option
|
||||
The option will be provided to every module with a NULL value.
|
||||
.It Ar module:option=value , Ar module:option , Ar module:!option
|
||||
As above, but the corresponding option and value will be provided
|
||||
only to modules whose name matches
|
||||
.Ar module .
|
||||
.El
|
||||
The return value will be
|
||||
.Cm ARCHIVE_OK
|
||||
if any module accepts the option, or
|
||||
.Cm ARCHIVE_WARN
|
||||
if no module accepted the option, or
|
||||
.Cm ARCHIVE_FATAL
|
||||
if there was a fatal error while attempting to process the option.
|
||||
.Pp
|
||||
The currently supported options are:
|
||||
.Bl -tag -compact -width indent
|
||||
.It Compressor gzip
|
||||
.Bl -tag -compact -width indent
|
||||
.It Cm compression-level
|
||||
The value is interpreted as a decimal integer specifying the
|
||||
gzip compression level.
|
||||
.El
|
||||
.It Compressor xz
|
||||
.Bl -tag -compact -width indent
|
||||
.It Cm compression-level
|
||||
The value is interpreted as a decimal integer specifying the
|
||||
compression level.
|
||||
.El
|
||||
.It Format mtree
|
||||
.Bl -tag -compact -width indent
|
||||
.It Cm cksum , Cm device , Cm flags , Cm gid , Cm gname , Cm indent , Cm link , Cm md5 , Cm mode , Cm nlink , Cm rmd160 , Cm sha1 , Cm sha256 , Cm sha384 , Cm sha512 , Cm size , Cm time , Cm uid , Cm uname
|
||||
Enable a particular keyword in the mtree output.
|
||||
Prefix with an exclamation mark to disable the corresponding keyword.
|
||||
The default is equivalent to
|
||||
.Dq device, flags, gid, gname, link, mode, nlink, size, time, type, uid, uname .
|
||||
.It Cm all
|
||||
Enables all of the above keywords.
|
||||
.It Cm use-set
|
||||
Enables generation of
|
||||
.Cm /set
|
||||
lines that specify default values for the following files and/or directories.
|
||||
.It Cm indent
|
||||
XXX needs explanation XXX
|
||||
.El
|
||||
.El
|
||||
.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_free
|
||||
Invokes
|
||||
.Fn archive_write_close
|
||||
if necessary, then releases all resources.
|
||||
If you need detailed information about
|
||||
.Fn archive_write_close
|
||||
failures, you should be careful to call it separately, as
|
||||
you cannot obtain error information after
|
||||
.Fn archive_write_free
|
||||
returns.
|
||||
.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
|
||||
.Fo archive_write_callback
|
||||
.Fa "struct archive *"
|
||||
.Fa "void *client_data"
|
||||
.Fa "const void *buffer"
|
||||
.Fa "size_t length"
|
||||
.Fc
|
||||
.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
|
||||
#ifdef __linux__
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
#include <archive.h>
|
||||
#include <archive_entry.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
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, const 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_free(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_free .
|
||||
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.
|
@ -1,466 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
/*
|
||||
* 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 <sys/wait.h>
|
||||
#endif
|
||||
#ifdef HAVE_LIMITS_H
|
||||
#include <limits.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#include <time.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#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_free(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_close = _archive_write_close;
|
||||
av.archive_free = _archive_write_free;
|
||||
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();
|
||||
/*
|
||||
* The value 10240 here matches the traditional tar default,
|
||||
* but is otherwise arbitrary.
|
||||
* TODO: Set the default block size from the format selected.
|
||||
*/
|
||||
a->bytes_per_block = 10240;
|
||||
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 write options for the format. Returns 0 if successful.
|
||||
*/
|
||||
int
|
||||
archive_write_set_format_options(struct archive *_a, const char *s)
|
||||
{
|
||||
struct archive_write *a = (struct archive_write *)_a;
|
||||
char key[64], val[64];
|
||||
int len, r, ret = ARCHIVE_OK;
|
||||
|
||||
__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
|
||||
ARCHIVE_STATE_NEW, "archive_write_set_format_options");
|
||||
archive_clear_error(&a->archive);
|
||||
|
||||
if (s == NULL || *s == '\0')
|
||||
return (ARCHIVE_OK);
|
||||
if (a->format_options == NULL)
|
||||
/* This format does not support option. */
|
||||
return (ARCHIVE_OK);
|
||||
|
||||
while ((len = __archive_parse_options(s, a->format_name,
|
||||
sizeof(key), key, sizeof(val), val)) > 0) {
|
||||
if (val[0] == '\0')
|
||||
r = a->format_options(a, key, NULL);
|
||||
else
|
||||
r = a->format_options(a, key, val);
|
||||
if (r == ARCHIVE_FATAL)
|
||||
return (r);
|
||||
if (r < ARCHIVE_OK) { /* This key was not handled. */
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Unsupported option ``%s''", key);
|
||||
ret = ARCHIVE_WARN;
|
||||
}
|
||||
s += len;
|
||||
}
|
||||
if (len < 0) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Malformed options string.");
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set write options for the compressor. Returns 0 if successful.
|
||||
*/
|
||||
int
|
||||
archive_write_set_compressor_options(struct archive *_a, const char *s)
|
||||
{
|
||||
struct archive_write *a = (struct archive_write *)_a;
|
||||
char key[64], val[64];
|
||||
int len, r;
|
||||
int ret = ARCHIVE_OK;
|
||||
|
||||
__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
|
||||
ARCHIVE_STATE_NEW, "archive_write_set_compressor_options");
|
||||
archive_clear_error(&a->archive);
|
||||
|
||||
if (s == NULL || *s == '\0')
|
||||
return (ARCHIVE_OK);
|
||||
if (a->compressor.options == NULL) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Unsupported option ``%s''", s);
|
||||
/* This compressor does not support option. */
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
|
||||
while ((len = __archive_parse_options(s, a->archive.compression_name,
|
||||
sizeof(key), key, sizeof(val), val)) > 0) {
|
||||
if (val[0] == '\0')
|
||||
r = a->compressor.options(a, key, NULL);
|
||||
else
|
||||
r = a->compressor.options(a, key, val);
|
||||
if (r == ARCHIVE_FATAL)
|
||||
return (r);
|
||||
if (r < ARCHIVE_OK) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Unsupported option ``%s''", key);
|
||||
ret = ARCHIVE_WARN;
|
||||
}
|
||||
s += len;
|
||||
}
|
||||
if (len < 0) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Illegal format options.");
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set write options for the format and the compressor. Returns 0 if successful.
|
||||
*/
|
||||
int
|
||||
archive_write_set_options(struct archive *_a, const char *s)
|
||||
{
|
||||
int r1, r2;
|
||||
|
||||
r1 = archive_write_set_format_options(_a, s);
|
||||
if (r1 < ARCHIVE_WARN)
|
||||
return (r1);
|
||||
r2 = archive_write_set_compressor_options(_a, s);
|
||||
if (r2 < ARCHIVE_WARN)
|
||||
return (r2);
|
||||
if (r1 == ARCHIVE_WARN && r2 == ARCHIVE_WARN)
|
||||
return (ARCHIVE_WARN);
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
__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_free(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_free");
|
||||
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_ino64(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));
|
||||
}
|
@ -1,375 +0,0 @@
|
||||
.\" 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 August 5, 2008
|
||||
.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_free
|
||||
.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
|
||||
.Fo archive_write_disk_set_group_lookup
|
||||
.Fa "struct archive *"
|
||||
.Fa "void *"
|
||||
.Fa "gid_t (*)(void *, const char *gname, gid_t gid)"
|
||||
.Fa "void (*cleanup)(void *)"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fn archive_write_disk_set_standard_lookup "struct archive *"
|
||||
.Ft int
|
||||
.Fo archive_write_disk_set_user_lookup
|
||||
.Fa "struct archive *"
|
||||
.Fa "void *"
|
||||
.Fa "uid_t (*)(void *, const char *uname, uid_t uid)"
|
||||
.Fa "void (*cleanup)(void *)"
|
||||
.Fc
|
||||
.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_free "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.
|
||||
.It Cm ARCHIVE_EXTRACT_SPARSE
|
||||
Scan data for blocks of NUL bytes and try to recreate them with holes.
|
||||
This results in sparse files, independent of whether the archive format
|
||||
supports or uses them.
|
||||
.El
|
||||
.It Xo
|
||||
.Fn archive_write_disk_set_group_lookup ,
|
||||
.Fn archive_write_disk_set_user_lookup
|
||||
.Xc
|
||||
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_free
|
||||
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 hierarchy
|
||||
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 hierarchy and returns archive
|
||||
entry objects.
|
File diff suppressed because it is too large
Load Diff
@ -1,38 +0,0 @@
|
||||
/*-
|
||||
* 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$
|
||||
*/
|
||||
|
||||
#ifndef __LIBARCHIVE_BUILD
|
||||
#error This header is only to be used internally to libarchive.
|
||||
#endif
|
||||
|
||||
#ifndef ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED
|
||||
#define ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED
|
||||
|
||||
struct archive_write_disk;
|
||||
|
||||
#endif
|
@ -1,262 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_GRP_H
|
||||
#include <grp.h>
|
||||
#endif
|
||||
#ifdef HAVE_PWD_H
|
||||
#include <pwd.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#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(cache_size * sizeof(struct bucket));
|
||||
struct bucket *gcache = malloc(cache_size * sizeof(struct bucket));
|
||||
memset(ucache, 0, cache_size * sizeof(struct bucket));
|
||||
memset(gcache, 0, cache_size * sizeof(struct bucket));
|
||||
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
|
||||
# if HAVE_GETGRNAM_R
|
||||
{
|
||||
char _buffer[128];
|
||||
size_t bufsize = 128;
|
||||
char *buffer = _buffer;
|
||||
struct group grent, *result;
|
||||
int r;
|
||||
|
||||
for (;;) {
|
||||
result = &grent; /* Old getgrnam_r ignores last arg. */
|
||||
r = getgrnam_r(gname, &grent, buffer, bufsize, &result);
|
||||
if (r == 0)
|
||||
break;
|
||||
if (r != ERANGE)
|
||||
break;
|
||||
bufsize *= 2;
|
||||
if (buffer != _buffer)
|
||||
free(buffer);
|
||||
buffer = malloc(bufsize);
|
||||
if (buffer == NULL)
|
||||
break;
|
||||
}
|
||||
if (result != NULL)
|
||||
gid = result->gr_gid;
|
||||
if (buffer != _buffer)
|
||||
free(buffer);
|
||||
}
|
||||
# else /* HAVE_GETGRNAM_R */
|
||||
{
|
||||
struct group *result;
|
||||
|
||||
result = getgrnam(gname);
|
||||
if (result != NULL)
|
||||
gid = result->gr_gid;
|
||||
}
|
||||
# endif /* HAVE_GETGRNAM_R */
|
||||
#elif defined(_WIN32) && !defined(__CYGWIN__)
|
||||
/* TODO: do a gname->gid lookup for Windows. */
|
||||
#else
|
||||
#error No way to perform gid lookups on this platform
|
||||
#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
|
||||
# if HAVE_GETPWNAM_R
|
||||
{
|
||||
char _buffer[128];
|
||||
size_t bufsize = 128;
|
||||
char *buffer = _buffer;
|
||||
struct passwd pwent, *result;
|
||||
int r;
|
||||
|
||||
for (;;) {
|
||||
result = &pwent; /* Old getpwnam_r ignores last arg. */
|
||||
r = getpwnam_r(uname, &pwent, buffer, bufsize, &result);
|
||||
if (r == 0)
|
||||
break;
|
||||
if (r != ERANGE)
|
||||
break;
|
||||
bufsize *= 2;
|
||||
if (buffer != _buffer)
|
||||
free(buffer);
|
||||
buffer = malloc(bufsize);
|
||||
if (buffer == NULL)
|
||||
break;
|
||||
}
|
||||
if (result != NULL)
|
||||
uid = result->pw_uid;
|
||||
if (buffer != _buffer)
|
||||
free(buffer);
|
||||
}
|
||||
# else /* HAVE_GETPWNAM_R */
|
||||
{
|
||||
struct passwd *result;
|
||||
|
||||
result = getpwnam(uname);
|
||||
if (result != NULL)
|
||||
uid = result->pw_uid;
|
||||
}
|
||||
#endif /* HAVE_GETPWNAM_R */
|
||||
#elif defined(_WIN32) && !defined(__CYGWIN__)
|
||||
/* TODO: do a uname->uid lookup for Windows. */
|
||||
#else
|
||||
#error No way to look up uids on this platform
|
||||
#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) != 0) {
|
||||
h ^= g >> 24;
|
||||
h &= 0x0FFFFFFF;
|
||||
}
|
||||
}
|
||||
return h;
|
||||
}
|
@ -1,144 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
#ifdef HAVE_IO_H
|
||||
#include <io.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
|
||||
struct write_fd_data {
|
||||
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;
|
||||
#if defined(__CYGWIN__) || defined(_WIN32)
|
||||
setmode(mine->fd, O_BINARY);
|
||||
#endif
|
||||
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;
|
||||
for (;;) {
|
||||
bytesWritten = write(mine->fd, buff, length);
|
||||
if (bytesWritten <= 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
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);
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#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;
|
||||
for (;;) {
|
||||
bytesWritten = fwrite(buff, 1, length, mine->f);
|
||||
if (bytesWritten <= 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
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);
|
||||
}
|
@ -1,166 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
|
||||
#ifndef O_BINARY
|
||||
#define O_BINARY 0
|
||||
#endif
|
||||
|
||||
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')
|
||||
return (archive_write_open_fd(a, 1));
|
||||
|
||||
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 | O_BINARY;
|
||||
|
||||
/*
|
||||
* Open the file.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
for (;;) {
|
||||
bytesWritten = write(mine->fd, buff, length);
|
||||
if (bytesWritten <= 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
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 */
|
||||
close(mine->fd);
|
||||
free(mine);
|
||||
return (ARCHIVE_OK);
|
||||
}
|
@ -1,126 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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);
|
||||
}
|
@ -1,122 +0,0 @@
|
||||
/*-
|
||||
* 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$
|
||||
*/
|
||||
|
||||
#ifndef __LIBARCHIVE_BUILD
|
||||
#error This header is only to be used internally to libarchive.
|
||||
#endif
|
||||
|
||||
#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;
|
||||
int64_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 (*options)(struct archive_write *,
|
||||
const char *key, const char *value);
|
||||
int (*finish)(struct archive_write *);
|
||||
int (*write)(struct archive_write *, const void *, size_t);
|
||||
} compressor;
|
||||
|
||||
/*
|
||||
* Pointers to format-specific functions for writing. They're
|
||||
* initialized by archive_write_set_format_XXX() calls.
|
||||
*/
|
||||
void *format_data;
|
||||
const char *format_name;
|
||||
int (*format_init)(struct archive_write *);
|
||||
int (*format_options)(struct archive_write *,
|
||||
const char *key, const char *value);
|
||||
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
|
@ -1,408 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_BZLIB_H
|
||||
#include <bzlib.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_private.h"
|
||||
#include "archive_write_private.h"
|
||||
|
||||
#if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR)
|
||||
int
|
||||
archive_write_set_compression_bzip2(struct archive *a)
|
||||
{
|
||||
archive_set_error(a, ARCHIVE_ERRNO_MISC,
|
||||
"bzip2 compression not supported on this platform");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
#else
|
||||
/* Don't compile this if we don't have bzlib. */
|
||||
|
||||
struct private_data {
|
||||
bz_stream stream;
|
||||
int64_t total_in;
|
||||
char *compressed;
|
||||
size_t compressed_buffer_size;
|
||||
};
|
||||
|
||||
struct private_config {
|
||||
int compression_level;
|
||||
};
|
||||
|
||||
/*
|
||||
* 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_options(struct archive_write *,
|
||||
const char *, const char *);
|
||||
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;
|
||||
struct private_config *config;
|
||||
__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
|
||||
ARCHIVE_STATE_NEW, "archive_write_set_compression_bzip2");
|
||||
config = malloc(sizeof(*config));
|
||||
if (config == NULL) {
|
||||
archive_set_error(&a->archive, ENOMEM, "Out of memory");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
a->compressor.config = config;
|
||||
a->compressor.finish = archive_compressor_bzip2_finish;
|
||||
config->compression_level = 9; /* default */
|
||||
a->compressor.init = &archive_compressor_bzip2_init;
|
||||
a->compressor.options = &archive_compressor_bzip2_options;
|
||||
a->archive.compression_code = ARCHIVE_COMPRESSION_BZIP2;
|
||||
a->archive.compression_name = "bzip2";
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup callback.
|
||||
*/
|
||||
static int
|
||||
archive_compressor_bzip2_init(struct archive_write *a)
|
||||
{
|
||||
int ret;
|
||||
struct private_data *state;
|
||||
struct private_config *config;
|
||||
|
||||
config = (struct private_config *)a->compressor.config;
|
||||
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;
|
||||
|
||||
/* Initialize compression library */
|
||||
ret = BZ2_bzCompressInit(&(state->stream),
|
||||
config->compression_level, 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);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Set write options.
|
||||
*/
|
||||
static int
|
||||
archive_compressor_bzip2_options(struct archive_write *a, const char *key,
|
||||
const char *value)
|
||||
{
|
||||
struct private_config *config;
|
||||
|
||||
config = (struct private_config *)a->compressor.config;
|
||||
if (strcmp(key, "compression-level") == 0) {
|
||||
if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
|
||||
value[1] != '\0')
|
||||
return (ARCHIVE_WARN);
|
||||
config->compression_level = value[0] - '0';
|
||||
/* Make '0' be a synonym for '1'. */
|
||||
/* This way, bzip2 compressor supports the same 0..9
|
||||
* range of levels as gzip. */
|
||||
if (config->compression_level < 1)
|
||||
config->compression_level = 1;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
ret = ARCHIVE_OK;
|
||||
state = (struct private_data *)a->compressor.data;
|
||||
if (state != NULL) {
|
||||
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. */
|
||||
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);
|
||||
}
|
||||
/* Free configuration data even if we were never fully initialized. */
|
||||
free(a->compressor.config);
|
||||
a->compressor.config = NULL;
|
||||
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;
|
||||
}
|
||||
|
||||
/* If there's nothing to do, we're done. */
|
||||
if (!finishing && state->stream.avail_in == 0)
|
||||
return (ARCHIVE_OK);
|
||||
|
||||
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;"
|
||||
" BZ2_bzCompress() returned %d",
|
||||
ret);
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */
|
@ -1,492 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2008 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.
|
||||
*/
|
||||
|
||||
/*-
|
||||
* 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.
|
||||
* 3. 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$");
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_private.h"
|
||||
#include "archive_write_private.h"
|
||||
|
||||
#define HSIZE 69001 /* 95% occupancy */
|
||||
#define HSHIFT 8 /* 8 - trunc(log2(HSIZE / 65536)) */
|
||||
#define CHECK_GAP 10000 /* Ratio check interval. */
|
||||
|
||||
#define MAXCODE(bits) ((1 << (bits)) - 1)
|
||||
|
||||
/*
|
||||
* the next two codes should not be changed lightly, as they must not
|
||||
* lie within the contiguous general code space.
|
||||
*/
|
||||
#define FIRST 257 /* First free entry. */
|
||||
#define CLEAR 256 /* Table clear output code. */
|
||||
|
||||
struct private_data {
|
||||
off_t in_count, out_count, checkpoint;
|
||||
|
||||
int code_len; /* Number of bits/code. */
|
||||
int cur_maxcode; /* Maximum code, given n_bits. */
|
||||
int max_maxcode; /* Should NEVER generate this code. */
|
||||
int hashtab [HSIZE];
|
||||
unsigned short codetab [HSIZE];
|
||||
int first_free; /* First unused entry. */
|
||||
int compress_ratio;
|
||||
|
||||
int cur_code, cur_fcode;
|
||||
|
||||
int bit_offset;
|
||||
unsigned char bit_buf;
|
||||
|
||||
unsigned char *compressed;
|
||||
size_t compressed_buffer_size;
|
||||
size_t compressed_offset;
|
||||
};
|
||||
|
||||
static int archive_compressor_compress_finish(struct archive_write *);
|
||||
static int archive_compressor_compress_init(struct archive_write *);
|
||||
static int archive_compressor_compress_write(struct archive_write *,
|
||||
const void *, size_t);
|
||||
|
||||
/*
|
||||
* Allocate, initialize and return a archive object.
|
||||
*/
|
||||
int
|
||||
archive_write_set_compression_compress(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_compress");
|
||||
a->compressor.init = &archive_compressor_compress_init;
|
||||
a->archive.compression_code = ARCHIVE_COMPRESSION_COMPRESS;
|
||||
a->archive.compression_name = "compress";
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup callback.
|
||||
*/
|
||||
static int
|
||||
archive_compressor_compress_init(struct archive_write *a)
|
||||
{
|
||||
int ret;
|
||||
struct private_data *state;
|
||||
|
||||
a->archive.compression_code = ARCHIVE_COMPRESSION_COMPRESS;
|
||||
a->archive.compression_name = "compress";
|
||||
|
||||
if (a->bytes_per_block < 4) {
|
||||
archive_set_error(&a->archive, EINVAL,
|
||||
"Can't write Compress header as single block");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
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 = 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);
|
||||
}
|
||||
|
||||
a->compressor.write = archive_compressor_compress_write;
|
||||
a->compressor.finish = archive_compressor_compress_finish;
|
||||
|
||||
state->max_maxcode = 0x10000; /* Should NEVER generate this code. */
|
||||
state->in_count = 0; /* Length of input. */
|
||||
state->bit_buf = 0;
|
||||
state->bit_offset = 0;
|
||||
state->out_count = 3; /* Includes 3-byte header mojo. */
|
||||
state->compress_ratio = 0;
|
||||
state->checkpoint = CHECK_GAP;
|
||||
state->code_len = 9;
|
||||
state->cur_maxcode = MAXCODE(state->code_len);
|
||||
state->first_free = FIRST;
|
||||
|
||||
memset(state->hashtab, 0xff, sizeof(state->hashtab));
|
||||
|
||||
/* Prime output buffer with a gzip header. */
|
||||
state->compressed[0] = 0x1f; /* Compress */
|
||||
state->compressed[1] = 0x9d;
|
||||
state->compressed[2] = 0x90; /* Block mode, 16bit max */
|
||||
state->compressed_offset = 3;
|
||||
|
||||
a->compressor.data = state;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*-
|
||||
* Output the given code.
|
||||
* Inputs:
|
||||
* code: A n_bits-bit integer. If == -1, then EOF. This assumes
|
||||
* that n_bits =< (long)wordsize - 1.
|
||||
* Outputs:
|
||||
* Outputs code to the file.
|
||||
* Assumptions:
|
||||
* Chars are 8 bits long.
|
||||
* Algorithm:
|
||||
* Maintain a BITS character long buffer (so that 8 codes will
|
||||
* fit in it exactly). Use the VAX insv instruction to insert each
|
||||
* code in turn. When the buffer fills up empty it and start over.
|
||||
*/
|
||||
|
||||
static unsigned char rmask[9] =
|
||||
{0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
|
||||
|
||||
static int
|
||||
output_byte(struct archive_write *a, unsigned char c)
|
||||
{
|
||||
struct private_data *state = a->compressor.data;
|
||||
ssize_t bytes_written;
|
||||
|
||||
state->compressed[state->compressed_offset++] = c;
|
||||
++state->out_count;
|
||||
|
||||
if (state->compressed_buffer_size == state->compressed_offset) {
|
||||
bytes_written = (a->client_writer)(&a->archive,
|
||||
a->client_data,
|
||||
state->compressed, state->compressed_buffer_size);
|
||||
if (bytes_written <= 0)
|
||||
return ARCHIVE_FATAL;
|
||||
a->archive.raw_position += bytes_written;
|
||||
state->compressed_offset = 0;
|
||||
}
|
||||
|
||||
return ARCHIVE_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
output_code(struct archive_write *a, int ocode)
|
||||
{
|
||||
struct private_data *state = a->compressor.data;
|
||||
int bits, ret, clear_flg, bit_offset;
|
||||
|
||||
clear_flg = ocode == CLEAR;
|
||||
|
||||
/*
|
||||
* Since ocode is always >= 8 bits, only need to mask the first
|
||||
* hunk on the left.
|
||||
*/
|
||||
bit_offset = state->bit_offset % 8;
|
||||
state->bit_buf |= (ocode << bit_offset) & 0xff;
|
||||
output_byte(a, state->bit_buf);
|
||||
|
||||
bits = state->code_len - (8 - bit_offset);
|
||||
ocode >>= 8 - bit_offset;
|
||||
/* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
|
||||
if (bits >= 8) {
|
||||
output_byte(a, ocode & 0xff);
|
||||
ocode >>= 8;
|
||||
bits -= 8;
|
||||
}
|
||||
/* Last bits. */
|
||||
state->bit_offset += state->code_len;
|
||||
state->bit_buf = ocode & rmask[bits];
|
||||
if (state->bit_offset == state->code_len * 8)
|
||||
state->bit_offset = 0;
|
||||
|
||||
/*
|
||||
* If the next entry is going to be too big for the ocode size,
|
||||
* then increase it, if possible.
|
||||
*/
|
||||
if (clear_flg || state->first_free > state->cur_maxcode) {
|
||||
/*
|
||||
* Write the whole buffer, because the input side won't
|
||||
* discover the size increase until after it has read it.
|
||||
*/
|
||||
if (state->bit_offset > 0) {
|
||||
while (state->bit_offset < state->code_len * 8) {
|
||||
ret = output_byte(a, state->bit_buf);
|
||||
if (ret != ARCHIVE_OK)
|
||||
return ret;
|
||||
state->bit_offset += 8;
|
||||
state->bit_buf = 0;
|
||||
}
|
||||
}
|
||||
state->bit_buf = 0;
|
||||
state->bit_offset = 0;
|
||||
|
||||
if (clear_flg) {
|
||||
state->code_len = 9;
|
||||
state->cur_maxcode = MAXCODE(state->code_len);
|
||||
} else {
|
||||
state->code_len++;
|
||||
if (state->code_len == 16)
|
||||
state->cur_maxcode = state->max_maxcode;
|
||||
else
|
||||
state->cur_maxcode = MAXCODE(state->code_len);
|
||||
}
|
||||
}
|
||||
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
output_flush(struct archive_write *a)
|
||||
{
|
||||
struct private_data *state = a->compressor.data;
|
||||
int ret;
|
||||
|
||||
/* At EOF, write the rest of the buffer. */
|
||||
if (state->bit_offset % 8) {
|
||||
state->code_len = (state->bit_offset % 8 + 7) / 8;
|
||||
ret = output_byte(a, state->bit_buf);
|
||||
if (ret != ARCHIVE_OK)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write data to the compressed stream.
|
||||
*/
|
||||
static int
|
||||
archive_compressor_compress_write(struct archive_write *a, const void *buff,
|
||||
size_t length)
|
||||
{
|
||||
struct private_data *state;
|
||||
int i;
|
||||
int ratio;
|
||||
int c, disp, ret;
|
||||
const unsigned char *bp;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (length == 0)
|
||||
return ARCHIVE_OK;
|
||||
|
||||
bp = buff;
|
||||
|
||||
if (state->in_count == 0) {
|
||||
state->cur_code = *bp++;
|
||||
++state->in_count;
|
||||
--length;
|
||||
}
|
||||
|
||||
while (length--) {
|
||||
c = *bp++;
|
||||
state->in_count++;
|
||||
state->cur_fcode = (c << 16) + state->cur_code;
|
||||
i = ((c << HSHIFT) ^ state->cur_code); /* Xor hashing. */
|
||||
|
||||
if (state->hashtab[i] == state->cur_fcode) {
|
||||
state->cur_code = state->codetab[i];
|
||||
continue;
|
||||
}
|
||||
if (state->hashtab[i] < 0) /* Empty slot. */
|
||||
goto nomatch;
|
||||
/* Secondary hash (after G. Knott). */
|
||||
if (i == 0)
|
||||
disp = 1;
|
||||
else
|
||||
disp = HSIZE - i;
|
||||
probe:
|
||||
if ((i -= disp) < 0)
|
||||
i += HSIZE;
|
||||
|
||||
if (state->hashtab[i] == state->cur_fcode) {
|
||||
state->cur_code = state->codetab[i];
|
||||
continue;
|
||||
}
|
||||
if (state->hashtab[i] >= 0)
|
||||
goto probe;
|
||||
nomatch:
|
||||
ret = output_code(a, state->cur_code);
|
||||
if (ret != ARCHIVE_OK)
|
||||
return ret;
|
||||
state->cur_code = c;
|
||||
if (state->first_free < state->max_maxcode) {
|
||||
state->codetab[i] = state->first_free++; /* code -> hashtable */
|
||||
state->hashtab[i] = state->cur_fcode;
|
||||
continue;
|
||||
}
|
||||
if (state->in_count < state->checkpoint)
|
||||
continue;
|
||||
|
||||
state->checkpoint = state->in_count + CHECK_GAP;
|
||||
|
||||
if (state->in_count <= 0x007fffff)
|
||||
ratio = state->in_count * 256 / state->out_count;
|
||||
else if ((ratio = state->out_count / 256) == 0)
|
||||
ratio = 0x7fffffff;
|
||||
else
|
||||
ratio = state->in_count / ratio;
|
||||
|
||||
if (ratio > state->compress_ratio)
|
||||
state->compress_ratio = ratio;
|
||||
else {
|
||||
state->compress_ratio = 0;
|
||||
memset(state->hashtab, 0xff, sizeof(state->hashtab));
|
||||
state->first_free = FIRST;
|
||||
ret = output_code(a, CLEAR);
|
||||
if (ret != ARCHIVE_OK)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Finish the compression...
|
||||
*/
|
||||
static int
|
||||
archive_compressor_compress_finish(struct archive_write *a)
|
||||
{
|
||||
ssize_t block_length, target_block_length, bytes_written;
|
||||
int ret;
|
||||
struct private_data *state;
|
||||
size_t tocopy;
|
||||
|
||||
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.");
|
||||
ret = ARCHIVE_FATAL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* By default, always pad the uncompressed data. */
|
||||
if (a->pad_uncompressed) {
|
||||
while (state->in_count % a->bytes_per_block != 0) {
|
||||
tocopy = a->bytes_per_block -
|
||||
(state->in_count % a->bytes_per_block);
|
||||
if (tocopy > a->null_length)
|
||||
tocopy = a->null_length;
|
||||
ret = archive_compressor_compress_write(a, a->nulls,
|
||||
tocopy);
|
||||
if (ret != ARCHIVE_OK)
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
ret = output_code(a, state->cur_code);
|
||||
if (ret != ARCHIVE_OK)
|
||||
goto cleanup;
|
||||
ret = output_flush(a);
|
||||
if (ret != ARCHIVE_OK)
|
||||
goto cleanup;
|
||||
|
||||
/* Optionally, pad the final compressed block. */
|
||||
block_length = state->compressed_offset;
|
||||
|
||||
/* Tricky calculation to determine size of last block. */
|
||||
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->compressed + state->compressed_offset, 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;
|
||||
else
|
||||
a->archive.raw_position += bytes_written;
|
||||
|
||||
cleanup:
|
||||
free(state->compressed);
|
||||
free(state);
|
||||
return (ret);
|
||||
}
|
@ -1,477 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#include <time.h>
|
||||
#ifdef HAVE_ZLIB_H
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_private.h"
|
||||
#include "archive_write_private.h"
|
||||
|
||||
#ifndef HAVE_ZLIB_H
|
||||
int
|
||||
archive_write_set_compression_gzip(struct archive *a)
|
||||
{
|
||||
archive_set_error(a, ARCHIVE_ERRNO_MISC,
|
||||
"gzip compression not supported on this platform");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
#else
|
||||
/* Don't compile this if we don't have zlib. */
|
||||
|
||||
struct private_data {
|
||||
z_stream stream;
|
||||
int64_t total_in;
|
||||
unsigned char *compressed;
|
||||
size_t compressed_buffer_size;
|
||||
unsigned long crc;
|
||||
};
|
||||
|
||||
struct private_config {
|
||||
int compression_level;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* 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_options(struct archive_write *,
|
||||
const char *, const char *);
|
||||
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;
|
||||
struct private_config *config;
|
||||
__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
|
||||
ARCHIVE_STATE_NEW, "archive_write_set_compression_gzip");
|
||||
config = malloc(sizeof(*config));
|
||||
if (config == NULL) {
|
||||
archive_set_error(&a->archive, ENOMEM, "Out of memory");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
a->compressor.config = config;
|
||||
a->compressor.finish = &archive_compressor_gzip_finish;
|
||||
config->compression_level = Z_DEFAULT_COMPRESSION;
|
||||
a->compressor.init = &archive_compressor_gzip_init;
|
||||
a->compressor.options = &archive_compressor_gzip_options;
|
||||
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;
|
||||
struct private_config *config;
|
||||
time_t t;
|
||||
|
||||
config = (struct private_config *)a->compressor.config;
|
||||
|
||||
if (a->client_opener != NULL) {
|
||||
ret = (a->client_opener)(&a->archive, a->client_data);
|
||||
if (ret != ARCHIVE_OK)
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* The next check is a temporary workaround until the gzip
|
||||
* code can be overhauled some. The code should not require
|
||||
* that compressed_buffer_size == bytes_per_block. Removing
|
||||
* this assumption will allow us to compress larger chunks at
|
||||
* a time, which should improve overall performance
|
||||
* marginally. As a minor side-effect, such a cleanup would
|
||||
* allow us to support truly arbitrary block sizes.
|
||||
*/
|
||||
if (a->bytes_per_block < 10) {
|
||||
archive_set_error(&a->archive, EINVAL,
|
||||
"GZip compressor requires a minimum 10 byte block size");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
/*
|
||||
* See comment above. We should set compressed_buffer_size to
|
||||
* max(bytes_per_block, 65536), but the code can't handle that yet.
|
||||
*/
|
||||
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;
|
||||
|
||||
/* Initialize compression library. */
|
||||
ret = deflateInit2(&(state->stream),
|
||||
config->compression_level,
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set write options.
|
||||
*/
|
||||
static int
|
||||
archive_compressor_gzip_options(struct archive_write *a, const char *key,
|
||||
const char *value)
|
||||
{
|
||||
struct private_config *config;
|
||||
|
||||
config = (struct private_config *)a->compressor.config;
|
||||
if (strcmp(key, "compression-level") == 0) {
|
||||
if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
|
||||
value[1] != '\0')
|
||||
return (ARCHIVE_WARN);
|
||||
config->compression_level = value[0] - '0';
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 (state != NULL) {
|
||||
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. */
|
||||
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);
|
||||
}
|
||||
/* Clean up config area even if we never initialized. */
|
||||
free(a->compressor.config);
|
||||
a->compressor.config = NULL;
|
||||
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;
|
||||
}
|
||||
|
||||
/* If there's nothing to do, we're done. */
|
||||
if (!finishing && state->stream.avail_in == 0)
|
||||
return (ARCHIVE_OK);
|
||||
|
||||
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:"
|
||||
" deflate() call returned status %d",
|
||||
ret);
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HAVE_ZLIB_H */
|
@ -1,257 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#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);
|
||||
}
|
||||
|
||||
/* If the copy buffer isn't empty, try to fill it. */
|
||||
if (state->avail < state->buffer_size) {
|
||||
/* If buffer is not empty... */
|
||||
/* ... copy data into buffer ... */
|
||||
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;
|
||||
/* ... if it's full, write it out. */
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
while (remaining > state->buffer_size) {
|
||||
/* Write out full blocks directly to client. */
|
||||
bytes_written = (a->client_writer)(&a->archive,
|
||||
a->client_data, buff, state->buffer_size);
|
||||
if (bytes_written <= 0)
|
||||
return (ARCHIVE_FATAL);
|
||||
a->archive.raw_position += bytes_written;
|
||||
buff += bytes_written;
|
||||
remaining -= bytes_written;
|
||||
}
|
||||
|
||||
if (remaining > 0) {
|
||||
/* Copy last bit into copy buffer. */
|
||||
memcpy(state->next, buff, remaining);
|
||||
state->next += remaining;
|
||||
state->avail -= remaining;
|
||||
}
|
||||
|
||||
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;
|
||||
struct archive_none *state;
|
||||
|
||||
state = (struct archive_none *)a->compressor.data;
|
||||
ret = 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 */
|
||||
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);
|
||||
}
|
@ -1,347 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
/* This capability is only available on POSIX systems. */
|
||||
#if (!defined(HAVE_PIPE) || !defined(HAVE_FCNTL) || \
|
||||
!(defined(HAVE_FORK) || defined(HAVE_VFORK))) && (!defined(_WIN32) || defined(__CYGWIN__))
|
||||
#include "archive.h"
|
||||
|
||||
/*
|
||||
* On non-Posix systems, allow the program to build, but choke if
|
||||
* this function is actually invoked.
|
||||
*/
|
||||
int
|
||||
archive_write_set_compression_program(struct archive *_a, const char *cmd)
|
||||
{
|
||||
archive_set_error(_a, -1,
|
||||
"External compression programs not supported on this platform");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#ifdef HAVE_SYS_WAIT_H
|
||||
# include <sys/wait.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
# include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_FCNTL_H
|
||||
# include <fcntl.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
# include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
# include <string.h>
|
||||
#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);
|
||||
|
||||
if (state->child_stdout == -1) {
|
||||
fcntl(state->child_stdin, F_SETFL, 0);
|
||||
__archive_check_child(state->child_stdin, state->child_stdout);
|
||||
goto restart_write;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
ssize_t ret;
|
||||
const char *buf;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#endif /* !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) */
|
@ -1,439 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2009 Michihiro NAKAJIMA
|
||||
* 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$");
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#include <time.h>
|
||||
#ifdef HAVE_LZMA_H
|
||||
#include <lzma.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_private.h"
|
||||
#include "archive_write_private.h"
|
||||
|
||||
#ifndef HAVE_LZMA_H
|
||||
int
|
||||
archive_write_set_compression_xz(struct archive *a)
|
||||
{
|
||||
archive_set_error(a, ARCHIVE_ERRNO_MISC,
|
||||
"xz compression not supported on this platform");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
int
|
||||
archive_write_set_compression_lzma(struct archive *a)
|
||||
{
|
||||
archive_set_error(a, ARCHIVE_ERRNO_MISC,
|
||||
"lzma compression not supported on this platform");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
#else
|
||||
/* Don't compile this if we don't have liblzma. */
|
||||
|
||||
struct private_data {
|
||||
lzma_stream stream;
|
||||
lzma_filter lzmafilters[2];
|
||||
lzma_options_lzma lzma_opt;
|
||||
int64_t total_in;
|
||||
unsigned char *compressed;
|
||||
size_t compressed_buffer_size;
|
||||
};
|
||||
|
||||
struct private_config {
|
||||
int compression_level;
|
||||
};
|
||||
|
||||
static int archive_compressor_xz_init(struct archive_write *);
|
||||
static int archive_compressor_xz_options(struct archive_write *,
|
||||
const char *, const char *);
|
||||
static int archive_compressor_xz_finish(struct archive_write *);
|
||||
static int archive_compressor_xz_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_xz(struct archive *_a)
|
||||
{
|
||||
struct private_config *config;
|
||||
struct archive_write *a = (struct archive_write *)_a;
|
||||
__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
|
||||
ARCHIVE_STATE_NEW, "archive_write_set_compression_xz");
|
||||
config = calloc(1, sizeof(*config));
|
||||
if (config == NULL) {
|
||||
archive_set_error(&a->archive, ENOMEM, "Out of memory");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
a->compressor.config = config;
|
||||
a->compressor.finish = archive_compressor_xz_finish;
|
||||
config->compression_level = LZMA_PRESET_DEFAULT;
|
||||
a->compressor.init = &archive_compressor_xz_init;
|
||||
a->compressor.options = &archive_compressor_xz_options;
|
||||
a->archive.compression_code = ARCHIVE_COMPRESSION_XZ;
|
||||
a->archive.compression_name = "xz";
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
/* LZMA is handled identically, we just need a different compression
|
||||
* code set. (The liblzma setup looks at the code to determine
|
||||
* the one place that XZ and LZMA require different handling.) */
|
||||
int
|
||||
archive_write_set_compression_lzma(struct archive *_a)
|
||||
{
|
||||
struct archive_write *a = (struct archive_write *)_a;
|
||||
int r = archive_write_set_compression_xz(_a);
|
||||
if (r != ARCHIVE_OK)
|
||||
return (r);
|
||||
a->archive.compression_code = ARCHIVE_COMPRESSION_LZMA;
|
||||
a->archive.compression_name = "lzma";
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
archive_compressor_xz_init_stream(struct archive_write *a,
|
||||
struct private_data *state)
|
||||
{
|
||||
static const lzma_stream lzma_stream_init_data = LZMA_STREAM_INIT;
|
||||
int ret;
|
||||
|
||||
state->stream = lzma_stream_init_data;
|
||||
state->stream.next_out = state->compressed;
|
||||
state->stream.avail_out = state->compressed_buffer_size;
|
||||
if (a->archive.compression_code == ARCHIVE_COMPRESSION_XZ)
|
||||
ret = lzma_stream_encoder(&(state->stream),
|
||||
state->lzmafilters, LZMA_CHECK_CRC64);
|
||||
else
|
||||
ret = lzma_alone_encoder(&(state->stream), &state->lzma_opt);
|
||||
if (ret == LZMA_OK)
|
||||
return (ARCHIVE_OK);
|
||||
|
||||
switch (ret) {
|
||||
case LZMA_MEM_ERROR:
|
||||
archive_set_error(&a->archive, ENOMEM,
|
||||
"Internal error initializing compression library: "
|
||||
"Cannot allocate memory");
|
||||
break;
|
||||
default:
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Internal error initializing compression library: "
|
||||
"It's a bug in liblzma");
|
||||
break;
|
||||
}
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup callback.
|
||||
*/
|
||||
static int
|
||||
archive_compressor_xz_init(struct archive_write *a)
|
||||
{
|
||||
int ret;
|
||||
struct private_data *state;
|
||||
struct private_config *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));
|
||||
config = a->compressor.config;
|
||||
|
||||
/*
|
||||
* See comment above. We should set compressed_buffer_size to
|
||||
* max(bytes_per_block, 65536), but the code can't handle that yet.
|
||||
*/
|
||||
state->compressed_buffer_size = a->bytes_per_block;
|
||||
state->compressed = (unsigned 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);
|
||||
}
|
||||
a->compressor.write = archive_compressor_xz_write;
|
||||
|
||||
/* Initialize compression library. */
|
||||
if (lzma_lzma_preset(&state->lzma_opt, config->compression_level)) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Internal error initializing compression library");
|
||||
free(state->compressed);
|
||||
free(state);
|
||||
}
|
||||
state->lzmafilters[0].id = LZMA_FILTER_LZMA2;
|
||||
state->lzmafilters[0].options = &state->lzma_opt;
|
||||
state->lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */
|
||||
ret = archive_compressor_xz_init_stream(a, state);
|
||||
if (ret == LZMA_OK) {
|
||||
a->compressor.data = state;
|
||||
return (0);
|
||||
}
|
||||
/* Library setup failed: clean up. */
|
||||
free(state->compressed);
|
||||
free(state);
|
||||
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set write options.
|
||||
*/
|
||||
static int
|
||||
archive_compressor_xz_options(struct archive_write *a, const char *key,
|
||||
const char *value)
|
||||
{
|
||||
struct private_config *config;
|
||||
|
||||
config = (struct private_config *)a->compressor.config;
|
||||
if (strcmp(key, "compression-level") == 0) {
|
||||
if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
|
||||
value[1] != '\0')
|
||||
return (ARCHIVE_WARN);
|
||||
config->compression_level = value[0] - '0';
|
||||
if (config->compression_level > 6)
|
||||
config->compression_level = 6;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write data to the compressed stream.
|
||||
*/
|
||||
static int
|
||||
archive_compressor_xz_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->total_in += length;
|
||||
|
||||
/* Compress input data to output buffer */
|
||||
state->stream.next_in = 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_xz_finish(struct archive_write *a)
|
||||
{
|
||||
ssize_t block_length, target_block_length, bytes_written;
|
||||
int ret;
|
||||
struct private_data *state;
|
||||
unsigned tocopy;
|
||||
|
||||
ret = ARCHIVE_OK;
|
||||
state = (struct private_data *)a->compressor.data;
|
||||
if (state != NULL) {
|
||||
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) {
|
||||
state->stream.next_in = 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))) != ARCHIVE_OK)
|
||||
goto cleanup;
|
||||
|
||||
/* Optionally, pad the final compressed block. */
|
||||
block_length = state->stream.next_out - state->compressed;
|
||||
|
||||
/* Tricky calculation to determine size of last block. */
|
||||
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:
|
||||
lzma_end(&(state->stream));
|
||||
free(state->compressed);
|
||||
free(state);
|
||||
}
|
||||
free(a->compressor.config);
|
||||
a->compressor.config = NULL;
|
||||
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;
|
||||
}
|
||||
|
||||
/* If there's nothing to do, we're done. */
|
||||
if (!finishing && state->stream.avail_in == 0)
|
||||
return (ARCHIVE_OK);
|
||||
|
||||
ret = lzma_code(&(state->stream),
|
||||
finishing ? LZMA_FINISH : LZMA_RUN );
|
||||
|
||||
switch (ret) {
|
||||
case LZMA_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 LZMA_STREAM_END:
|
||||
/* This return can only occur in finishing case. */
|
||||
if (finishing)
|
||||
return (ARCHIVE_OK);
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"lzma compression data error");
|
||||
return (ARCHIVE_FATAL);
|
||||
case LZMA_MEMLIMIT_ERROR:
|
||||
archive_set_error(&a->archive, ENOMEM,
|
||||
"lzma compression error: "
|
||||
"%ju MiB would have been needed",
|
||||
(uintmax_t)((lzma_memusage(&(state->stream)) + 1024 * 1024 -1)
|
||||
/ (1024 * 1024)));
|
||||
return (ARCHIVE_FATAL);
|
||||
default:
|
||||
/* Any other return value indicates an error. */
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"lzma compression failed:"
|
||||
" lzma_code() call returned status %d",
|
||||
ret);
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HAVE_LZMA_H */
|
@ -1,72 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#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_SVR4_NOCRC, archive_write_set_format_cpio_newc },
|
||||
{ ARCHIVE_FORMAT_CPIO_POSIX, archive_write_set_format_cpio },
|
||||
{ ARCHIVE_FORMAT_MTREE, archive_write_set_format_mtree },
|
||||
{ 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 },
|
||||
{ ARCHIVE_FORMAT_ZIP, archive_write_set_format_zip },
|
||||
{ 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);
|
||||
}
|
@ -1,550 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#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(struct archive_write *);
|
||||
static int archive_write_ar_finish_entry(struct archive_write *);
|
||||
static const char *ar_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.archive_format = ARCHIVE_FORMAT_AR_BSD;
|
||||
a->archive.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.archive_format = ARCHIVE_FORMAT_AR_GNU;
|
||||
a->archive.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_name = "ar";
|
||||
a->format_write_header = archive_write_ar_header;
|
||||
a->format_write_data = archive_write_ar_data;
|
||||
a->format_finish = archive_write_ar_finish;
|
||||
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;
|
||||
int64_t size;
|
||||
|
||||
append_fn = 0;
|
||||
ar = (struct ar_w *)a->format_data;
|
||||
ar->is_strtab = 0;
|
||||
filename = NULL;
|
||||
size = archive_entry_size(entry);
|
||||
|
||||
|
||||
/*
|
||||
* 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, "!<arch>\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 = ar_basename(pathname)) == NULL) {
|
||||
/* Reject filenames with trailing "/" */
|
||||
archive_set_error(&a->archive, EINVAL,
|
||||
"Invalid filename");
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
|
||||
if (a->archive.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.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;
|
||||
size += 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(size, 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 = size;
|
||||
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 == NULL)
|
||||
return (ARCHIVE_OK);
|
||||
|
||||
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(struct archive_write *a)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* If we haven't written anything yet, we need to write
|
||||
* the ar global header now to make it a valid ar archive.
|
||||
*/
|
||||
if (a->archive.file_position == 0) {
|
||||
ret = (a->compressor.write)(a, "!<arch>\n", 8);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
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",
|
||||
(int)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 *
|
||||
ar_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);
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#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[] =
|
||||
{
|
||||
{ "ar", archive_write_set_format_ar_bsd },
|
||||
{ "arbsd", 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 },
|
||||
{ "mtree", archive_write_set_format_mtree },
|
||||
{ "newc", archive_write_set_format_cpio_newc },
|
||||
{ "odc", 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 },
|
||||
{ "zip", archive_write_set_format_zip },
|
||||
{ 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);
|
||||
}
|
@ -1,344 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#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;
|
||||
|
||||
int64_t ino_next;
|
||||
|
||||
struct { int64_t old; int new;} *ino_list;
|
||||
size_t ino_list_size;
|
||||
size_t ino_list_next;
|
||||
};
|
||||
|
||||
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];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* 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_name = "cpio";
|
||||
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.archive_format = ARCHIVE_FORMAT_CPIO_POSIX;
|
||||
a->archive.archive_format_name = "POSIX cpio";
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ino values are as long as 64 bits on some systems; cpio format
|
||||
* only allows 18 bits and relies on the ino values to identify hardlinked
|
||||
* files. So, we can't merely "hash" the ino numbers since collisions
|
||||
* would corrupt the archive. Instead, we generate synthetic ino values
|
||||
* to store in the archive and maintain a map of original ino values to
|
||||
* synthetic ones so we can preserve hardlink information.
|
||||
*
|
||||
* TODO: Make this more efficient. It's not as bad as it looks (most
|
||||
* files don't have any hardlinks and we don't do any work here for those),
|
||||
* but it wouldn't be hard to do better.
|
||||
*
|
||||
* TODO: Work with dev/ino pairs here instead of just ino values.
|
||||
*/
|
||||
static int
|
||||
synthesize_ino_value(struct cpio *cpio, struct archive_entry *entry)
|
||||
{
|
||||
int64_t ino = archive_entry_ino64(entry);
|
||||
int ino_new;
|
||||
size_t i;
|
||||
|
||||
/*
|
||||
* If no index number was given, don't assign one. In
|
||||
* particular, this handles the end-of-archive marker
|
||||
* correctly by giving it a zero index value. (This is also
|
||||
* why we start our synthetic index numbers with one below.)
|
||||
*/
|
||||
if (ino == 0)
|
||||
return (0);
|
||||
|
||||
/* Don't store a mapping if we don't need to. */
|
||||
if (archive_entry_nlink(entry) < 2) {
|
||||
return ++cpio->ino_next;
|
||||
}
|
||||
|
||||
/* Look up old ino; if we have it, this is a hardlink
|
||||
* and we reuse the same value. */
|
||||
for (i = 0; i < cpio->ino_list_next; ++i) {
|
||||
if (cpio->ino_list[i].old == ino)
|
||||
return (cpio->ino_list[i].new);
|
||||
}
|
||||
|
||||
/* Assign a new index number. */
|
||||
ino_new = ++cpio->ino_next;
|
||||
|
||||
/* Ensure space for the new mapping. */
|
||||
if (cpio->ino_list_size <= cpio->ino_list_next) {
|
||||
size_t newsize = cpio->ino_list_size < 512
|
||||
? 512 : cpio->ino_list_size * 2;
|
||||
void *newlist = realloc(cpio->ino_list,
|
||||
sizeof(cpio->ino_list[0]) * newsize);
|
||||
if (newlist == NULL)
|
||||
return (-1);
|
||||
|
||||
cpio->ino_list_size = newsize;
|
||||
cpio->ino_list = newlist;
|
||||
}
|
||||
|
||||
/* Record and return the new value. */
|
||||
cpio->ino_list[cpio->ino_list_next].old = ino;
|
||||
cpio->ino_list[cpio->ino_list_next].new = ino_new;
|
||||
++cpio->ino_list_next;
|
||||
return (ino_new);
|
||||
}
|
||||
|
||||
static int
|
||||
archive_write_cpio_header(struct archive_write *a, struct archive_entry *entry)
|
||||
{
|
||||
struct cpio *cpio;
|
||||
const char *p, *path;
|
||||
int pathlength, ret, ret2;
|
||||
int64_t ino;
|
||||
struct cpio_header h;
|
||||
|
||||
cpio = (struct cpio *)a->format_data;
|
||||
ret2 = ARCHIVE_OK;
|
||||
|
||||
path = archive_entry_pathname(entry);
|
||||
pathlength = (int)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));
|
||||
|
||||
ino = synthesize_ino_value(cpio, entry);
|
||||
if (ino < 0) {
|
||||
archive_set_error(&a->archive, ENOMEM,
|
||||
"No memory for ino translation table");
|
||||
return (ARCHIVE_FATAL);
|
||||
} else if (ino > 0777777) {
|
||||
archive_set_error(&a->archive, ERANGE,
|
||||
"Too many files for this cpio format");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
format_octal(ino & 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));
|
||||
|
||||
/* Non-regular files don't store bodies. */
|
||||
if (archive_entry_filetype(entry) != AE_IFREG)
|
||||
archive_entry_set_size(entry, 0);
|
||||
|
||||
/* 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));
|
||||
|
||||
if (ret == ARCHIVE_OK)
|
||||
ret = ret2;
|
||||
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)
|
||||
{
|
||||
int er;
|
||||
struct archive_entry *trailer;
|
||||
|
||||
trailer = archive_entry_new();
|
||||
/* nlink = 1 here for GNU cpio compat. */
|
||||
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->ino_list);
|
||||
free(cpio);
|
||||
a->format_data = NULL;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
archive_write_cpio_finish_entry(struct archive_write *a)
|
||||
{
|
||||
struct cpio *cpio;
|
||||
size_t to_write;
|
||||
int 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);
|
||||
}
|
@ -1,295 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2007 Tim Kientzle
|
||||
* Copyright (c) 2006 Rudolf Marek SYSGO s.r.o.
|
||||
* 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_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_entry.h"
|
||||
#include "archive_private.h"
|
||||
#include "archive_write_private.h"
|
||||
|
||||
static ssize_t archive_write_newc_data(struct archive_write *,
|
||||
const void *buff, size_t s);
|
||||
static int archive_write_newc_finish(struct archive_write *);
|
||||
static int archive_write_newc_destroy(struct archive_write *);
|
||||
static int archive_write_newc_finish_entry(struct archive_write *);
|
||||
static int archive_write_newc_header(struct archive_write *,
|
||||
struct archive_entry *);
|
||||
static int format_hex(int64_t, void *, int);
|
||||
static int64_t format_hex_recursive(int64_t, char *, int);
|
||||
|
||||
struct cpio {
|
||||
uint64_t entry_bytes_remaining;
|
||||
int padding;
|
||||
};
|
||||
|
||||
struct cpio_header_newc {
|
||||
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_checksum[8];
|
||||
};
|
||||
|
||||
/* Logic trick: difference between 'n' and next multiple of 4 */
|
||||
#define PAD4(n) (3 & (1 + ~(n)))
|
||||
|
||||
/*
|
||||
* Set output format to 'cpio' format.
|
||||
*/
|
||||
int
|
||||
archive_write_set_format_cpio_newc(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_name = "cpio";
|
||||
a->format_write_header = archive_write_newc_header;
|
||||
a->format_write_data = archive_write_newc_data;
|
||||
a->format_finish_entry = archive_write_newc_finish_entry;
|
||||
a->format_finish = archive_write_newc_finish;
|
||||
a->format_destroy = archive_write_newc_destroy;
|
||||
a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_NOCRC;
|
||||
a->archive.archive_format_name = "SVR4 cpio nocrc";
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
archive_write_newc_header(struct archive_write *a, struct archive_entry *entry)
|
||||
{
|
||||
int64_t ino;
|
||||
struct cpio *cpio;
|
||||
const char *p, *path;
|
||||
int pathlength, ret, ret2;
|
||||
struct cpio_header_newc h;
|
||||
int pad;
|
||||
|
||||
cpio = (struct cpio *)a->format_data;
|
||||
ret2 = ARCHIVE_OK;
|
||||
|
||||
path = archive_entry_pathname(entry);
|
||||
pathlength = (int)strlen(path) + 1; /* Include trailing null. */
|
||||
|
||||
memset(&h, 0, sizeof(h));
|
||||
format_hex(0x070701, &h.c_magic, sizeof(h.c_magic));
|
||||
format_hex(archive_entry_devmajor(entry), &h.c_devmajor,
|
||||
sizeof(h.c_devmajor));
|
||||
format_hex(archive_entry_devminor(entry), &h.c_devminor,
|
||||
sizeof(h.c_devminor));
|
||||
|
||||
ino = archive_entry_ino64(entry);
|
||||
if (ino > 0xffffffff) {
|
||||
archive_set_error(&a->archive, ERANGE,
|
||||
"large inode number truncated");
|
||||
ret2 = ARCHIVE_WARN;
|
||||
}
|
||||
|
||||
format_hex(ino & 0xffffffff, &h.c_ino, sizeof(h.c_ino));
|
||||
format_hex(archive_entry_mode(entry), &h.c_mode, sizeof(h.c_mode));
|
||||
format_hex(archive_entry_uid(entry), &h.c_uid, sizeof(h.c_uid));
|
||||
format_hex(archive_entry_gid(entry), &h.c_gid, sizeof(h.c_gid));
|
||||
format_hex(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_hex(archive_entry_rdevmajor(entry), &h.c_rdevmajor, sizeof(h.c_rdevmajor));
|
||||
format_hex(archive_entry_rdevminor(entry), &h.c_rdevminor, sizeof(h.c_rdevminor));
|
||||
} else {
|
||||
format_hex(0, &h.c_rdevmajor, sizeof(h.c_rdevmajor));
|
||||
format_hex(0, &h.c_rdevminor, sizeof(h.c_rdevminor));
|
||||
}
|
||||
format_hex(archive_entry_mtime(entry), &h.c_mtime, sizeof(h.c_mtime));
|
||||
format_hex(pathlength, &h.c_namesize, sizeof(h.c_namesize));
|
||||
format_hex(0, &h.c_checksum, sizeof(h.c_checksum));
|
||||
|
||||
/* Non-regular files don't store bodies. */
|
||||
if (archive_entry_filetype(entry) != AE_IFREG)
|
||||
archive_entry_set_size(entry, 0);
|
||||
|
||||
/* Symlinks get the link written as the body of the entry. */
|
||||
p = archive_entry_symlink(entry);
|
||||
if (p != NULL && *p != '\0')
|
||||
format_hex(strlen(p), &h.c_filesize, sizeof(h.c_filesize));
|
||||
else
|
||||
format_hex(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);
|
||||
|
||||
/* Pad pathname to even length. */
|
||||
ret = (a->compressor.write)(a, path, pathlength);
|
||||
if (ret != ARCHIVE_OK)
|
||||
return (ARCHIVE_FATAL);
|
||||
pad = PAD4(pathlength + sizeof(struct cpio_header_newc));
|
||||
if (pad)
|
||||
ret = (a->compressor.write)(a, "\0\0\0", pad);
|
||||
if (ret != ARCHIVE_OK)
|
||||
return (ARCHIVE_FATAL);
|
||||
|
||||
cpio->entry_bytes_remaining = archive_entry_size(entry);
|
||||
cpio->padding = PAD4(cpio->entry_bytes_remaining);
|
||||
|
||||
/* Write the symlink now. */
|
||||
if (p != NULL && *p != '\0') {
|
||||
ret = (a->compressor.write)(a, p, strlen(p));
|
||||
if (ret != ARCHIVE_OK)
|
||||
return (ARCHIVE_FATAL);
|
||||
pad = PAD4(strlen(p));
|
||||
ret = (a->compressor.write)(a, "\0\0\0", pad);
|
||||
}
|
||||
|
||||
if (ret == ARCHIVE_OK)
|
||||
ret = ret2;
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
archive_write_newc_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_hex(int64_t v, void *p, int digits)
|
||||
{
|
||||
int64_t max;
|
||||
int ret;
|
||||
|
||||
max = (((int64_t)1) << (digits * 4)) - 1;
|
||||
if (v >= 0 && v <= max) {
|
||||
format_hex_recursive(v, (char *)p, digits);
|
||||
ret = 0;
|
||||
} else {
|
||||
format_hex_recursive(max, (char *)p, digits);
|
||||
ret = -1;
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static int64_t
|
||||
format_hex_recursive(int64_t v, char *p, int s)
|
||||
{
|
||||
if (s == 0)
|
||||
return (v);
|
||||
v = format_hex_recursive(v, p+1, s-1);
|
||||
*p = "0123456789abcdef"[v & 0xf];
|
||||
return (v >> 4);
|
||||
}
|
||||
|
||||
static int
|
||||
archive_write_newc_finish(struct archive_write *a)
|
||||
{
|
||||
int er;
|
||||
struct archive_entry *trailer;
|
||||
|
||||
trailer = archive_entry_new();
|
||||
archive_entry_set_nlink(trailer, 1);
|
||||
archive_entry_set_pathname(trailer, "TRAILER!!!");
|
||||
er = archive_write_newc_header(a, trailer);
|
||||
archive_entry_free(trailer);
|
||||
return (er);
|
||||
}
|
||||
|
||||
static int
|
||||
archive_write_newc_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_newc_finish_entry(struct archive_write *a)
|
||||
{
|
||||
struct cpio *cpio;
|
||||
size_t to_write;
|
||||
int ret;
|
||||
|
||||
cpio = (struct cpio *)a->format_data;
|
||||
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;
|
||||
}
|
||||
ret = (a->compressor.write)(a, a->nulls, cpio->padding);
|
||||
return (ret);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,625 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2007 Tim Kientzle
|
||||
* Copyright (c) 2008 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_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#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;
|
||||
|
||||
/* Line buffer for uuencoded dump format */
|
||||
char outbuff[45];
|
||||
size_t outpos;
|
||||
|
||||
int wrote_header;
|
||||
struct archive_string work;
|
||||
struct archive_string quoted_name;
|
||||
};
|
||||
|
||||
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 *);
|
||||
|
||||
/*
|
||||
* Copy the given string to the buffer, quoting all shell meta characters
|
||||
* found.
|
||||
*/
|
||||
static void
|
||||
shar_quote(struct archive_string *buf, const char *str, int in_shell)
|
||||
{
|
||||
static const char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~";
|
||||
size_t len;
|
||||
|
||||
while (*str != '\0') {
|
||||
if ((len = strcspn(str, meta)) != 0) {
|
||||
archive_strncat(buf, str, len);
|
||||
str += len;
|
||||
} else if (*str == '\n') {
|
||||
if (in_shell)
|
||||
archive_strcat(buf, "\"\n\"");
|
||||
else
|
||||
archive_strcat(buf, "\\n");
|
||||
++str;
|
||||
} else {
|
||||
archive_strappend_char(buf, '\\');
|
||||
archive_strappend_char(buf, *str);
|
||||
++str;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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));
|
||||
archive_string_init(&shar->work);
|
||||
archive_string_init(&shar->quoted_name);
|
||||
a->format_data = shar;
|
||||
|
||||
a->pad_uncompressed = 0;
|
||||
a->format_name = "shar";
|
||||
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.archive_format = ARCHIVE_FORMAT_SHAR_BASE;
|
||||
a->archive.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.archive_format = ARCHIVE_FORMAT_SHAR_DUMP;
|
||||
a->archive.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;
|
||||
|
||||
shar = (struct shar *)a->format_data;
|
||||
if (!shar->wrote_header) {
|
||||
archive_strcat(&shar->work, "#!/bin/sh\n");
|
||||
archive_strcat(&shar->work, "# This is a shell archive\n");
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
archive_string_empty(&shar->quoted_name);
|
||||
shar_quote(&shar->quoted_name, name, 1);
|
||||
|
||||
/* Stock preparation for all file types. */
|
||||
archive_string_sprintf(&shar->work, "echo x %s\n", shar->quoted_name.s);
|
||||
|
||||
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 ." */
|
||||
free(p);
|
||||
} else if (shar->last_dir == NULL) {
|
||||
archive_strcat(&shar->work, "mkdir -p ");
|
||||
shar_quote(&shar->work, p, 1);
|
||||
archive_strcat(&shar->work,
|
||||
" > /dev/null 2>&1\n");
|
||||
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 {
|
||||
archive_strcat(&shar->work, "mkdir -p ");
|
||||
shar_quote(&shar->work, p, 1);
|
||||
archive_strcat(&shar->work,
|
||||
" > /dev/null 2>&1\n");
|
||||
shar->last_dir = p;
|
||||
}
|
||||
} else {
|
||||
free(p);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle file-type specific issues. */
|
||||
shar->has_data = 0;
|
||||
if ((linkname = archive_entry_hardlink(entry)) != NULL) {
|
||||
archive_strcat(&shar->work, "ln -f ");
|
||||
shar_quote(&shar->work, linkname, 1);
|
||||
archive_string_sprintf(&shar->work, " %s\n",
|
||||
shar->quoted_name.s);
|
||||
} else if ((linkname = archive_entry_symlink(entry)) != NULL) {
|
||||
archive_strcat(&shar->work, "ln -fs ");
|
||||
shar_quote(&shar->work, linkname, 1);
|
||||
archive_string_sprintf(&shar->work, " %s\n",
|
||||
shar->quoted_name.s);
|
||||
} else {
|
||||
switch(archive_entry_filetype(entry)) {
|
||||
case AE_IFREG:
|
||||
if (archive_entry_size(entry) == 0) {
|
||||
/* More portable than "touch." */
|
||||
archive_string_sprintf(&shar->work,
|
||||
"test -e \"%s\" || :> \"%s\"\n",
|
||||
shar->quoted_name.s, shar->quoted_name.s);
|
||||
} else {
|
||||
if (shar->dump) {
|
||||
archive_string_sprintf(&shar->work,
|
||||
"uudecode -p > %s << 'SHAR_END'\n",
|
||||
shar->quoted_name.s);
|
||||
archive_string_sprintf(&shar->work,
|
||||
"begin %o ",
|
||||
archive_entry_mode(entry) & 0777);
|
||||
shar_quote(&shar->work, name, 0);
|
||||
archive_strcat(&shar->work, "\n");
|
||||
} else {
|
||||
archive_string_sprintf(&shar->work,
|
||||
"sed 's/^X//' > %s << 'SHAR_END'\n",
|
||||
shar->quoted_name.s);
|
||||
}
|
||||
shar->has_data = 1;
|
||||
shar->end_of_line = 1;
|
||||
shar->outpos = 0;
|
||||
}
|
||||
break;
|
||||
case AE_IFDIR:
|
||||
archive_string_sprintf(&shar->work,
|
||||
"mkdir -p %s > /dev/null 2>&1\n",
|
||||
shar->quoted_name.s);
|
||||
/* 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:
|
||||
archive_string_sprintf(&shar->work,
|
||||
"mkfifo %s\n", shar->quoted_name.s);
|
||||
break;
|
||||
case AE_IFCHR:
|
||||
archive_string_sprintf(&shar->work,
|
||||
"mknod %s c %d %d\n", shar->quoted_name.s,
|
||||
archive_entry_rdevmajor(entry),
|
||||
archive_entry_rdevminor(entry));
|
||||
break;
|
||||
case AE_IFBLK:
|
||||
archive_string_sprintf(&shar->work,
|
||||
"mknod %s b %d %d\n", shar->quoted_name.s,
|
||||
archive_entry_rdevmajor(entry),
|
||||
archive_entry_rdevminor(entry));
|
||||
break;
|
||||
default:
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
}
|
||||
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
archive_write_shar_data_sed(struct archive_write *a, const void *buff, size_t n)
|
||||
{
|
||||
static const size_t ensured = 65533;
|
||||
struct shar *shar;
|
||||
const char *src;
|
||||
char *buf, *buf_end;
|
||||
int ret;
|
||||
size_t written = n;
|
||||
|
||||
shar = (struct shar *)a->format_data;
|
||||
if (!shar->has_data || n == 0)
|
||||
return (0);
|
||||
|
||||
src = (const char *)buff;
|
||||
|
||||
/*
|
||||
* ensure is the number of bytes in buffer before expanding the
|
||||
* current character. Each operation writes the current character
|
||||
* and optionally the start-of-new-line marker. This can happen
|
||||
* twice before entering the loop, so make sure three additional
|
||||
* bytes can be written.
|
||||
*/
|
||||
if (archive_string_ensure(&shar->work, ensured + 3) == NULL)
|
||||
__archive_errx(1, "Out of memory");
|
||||
|
||||
if (shar->work.length > ensured) {
|
||||
ret = (*a->compressor.write)(a, shar->work.s,
|
||||
shar->work.length);
|
||||
if (ret != ARCHIVE_OK)
|
||||
return (ARCHIVE_FATAL);
|
||||
archive_string_empty(&shar->work);
|
||||
}
|
||||
buf = shar->work.s + shar->work.length;
|
||||
buf_end = shar->work.s + ensured;
|
||||
|
||||
if (shar->end_of_line) {
|
||||
*buf++ = 'X';
|
||||
shar->end_of_line = 0;
|
||||
}
|
||||
|
||||
while (n-- != 0) {
|
||||
if ((*buf++ = *src++) == '\n') {
|
||||
if (n == 0)
|
||||
shar->end_of_line = 1;
|
||||
else
|
||||
*buf++ = 'X';
|
||||
}
|
||||
|
||||
if (buf >= buf_end) {
|
||||
shar->work.length = buf - shar->work.s;
|
||||
ret = (*a->compressor.write)(a, shar->work.s,
|
||||
shar->work.length);
|
||||
if (ret != ARCHIVE_OK)
|
||||
return (ARCHIVE_FATAL);
|
||||
archive_string_empty(&shar->work);
|
||||
buf = shar->work.s;
|
||||
}
|
||||
}
|
||||
|
||||
shar->work.length = buf - shar->work.s;
|
||||
|
||||
return (written);
|
||||
}
|
||||
|
||||
#define UUENC(c) (((c)!=0) ? ((c) & 077) + ' ': '`')
|
||||
|
||||
static void
|
||||
uuencode_group(const char _in[3], char out[4])
|
||||
{
|
||||
const unsigned char *in = (const unsigned char *)_in;
|
||||
int t;
|
||||
|
||||
t = (in[0] << 16) | (in[1] << 8) | in[2];
|
||||
out[0] = UUENC( 0x3f & (t >> 18) );
|
||||
out[1] = UUENC( 0x3f & (t >> 12) );
|
||||
out[2] = UUENC( 0x3f & (t >> 6) );
|
||||
out[3] = UUENC( 0x3f & t );
|
||||
}
|
||||
|
||||
static void
|
||||
uuencode_line(struct shar *shar, const char *inbuf, size_t len)
|
||||
{
|
||||
char tmp_buf[3], *buf;
|
||||
size_t alloc_len;
|
||||
|
||||
/* len <= 45 -> expanded to 60 + len byte + new line */
|
||||
alloc_len = shar->work.length + 62;
|
||||
if (archive_string_ensure(&shar->work, alloc_len) == NULL)
|
||||
__archive_errx(1, "Out of memory");
|
||||
|
||||
buf = shar->work.s + shar->work.length;
|
||||
*buf++ = UUENC(len);
|
||||
while (len >= 3) {
|
||||
uuencode_group(inbuf, buf);
|
||||
len -= 3;
|
||||
inbuf += 3;
|
||||
buf += 4;
|
||||
}
|
||||
if (len != 0) {
|
||||
tmp_buf[0] = inbuf[0];
|
||||
if (len == 1)
|
||||
tmp_buf[1] = '\0';
|
||||
else
|
||||
tmp_buf[1] = inbuf[1];
|
||||
tmp_buf[2] = '\0';
|
||||
uuencode_group(inbuf, buf);
|
||||
buf += 4;
|
||||
}
|
||||
*buf++ = '\n';
|
||||
if ((buf - shar->work.s) > (ptrdiff_t)(shar->work.length + 62))
|
||||
__archive_errx(1, "Buffer overflow");
|
||||
shar->work.length = buf - shar->work.s;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (shar->outpos != 0) {
|
||||
n = 45 - shar->outpos;
|
||||
if (n > length)
|
||||
n = length;
|
||||
memcpy(shar->outbuff + shar->outpos, src, n);
|
||||
if (shar->outpos + n < 45) {
|
||||
shar->outpos += n;
|
||||
return length;
|
||||
}
|
||||
uuencode_line(shar, shar->outbuff, 45);
|
||||
src += n;
|
||||
n = length - n;
|
||||
} else {
|
||||
n = length;
|
||||
}
|
||||
|
||||
while (n >= 45) {
|
||||
uuencode_line(shar, src, 45);
|
||||
src += 45;
|
||||
n -= 45;
|
||||
|
||||
if (shar->work.length < 65536)
|
||||
continue;
|
||||
ret = (*a->compressor.write)(a, shar->work.s,
|
||||
shar->work.length);
|
||||
if (ret != ARCHIVE_OK)
|
||||
return (ARCHIVE_FATAL);
|
||||
archive_string_empty(&shar->work);
|
||||
}
|
||||
if (n != 0) {
|
||||
memcpy(shar->outbuff, src, n);
|
||||
shar->outpos = n;
|
||||
}
|
||||
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->outpos > 0)
|
||||
uuencode_line(shar, shar->outbuff,
|
||||
shar->outpos);
|
||||
archive_strcat(&shar->work, "`\nend\n");
|
||||
archive_strcat(&shar->work, "SHAR_END\n");
|
||||
}
|
||||
/* Restore file mode, owner, flags. */
|
||||
/*
|
||||
* TODO: Don't immediately restore mode for
|
||||
* directories; defer that to end of script.
|
||||
*/
|
||||
archive_string_sprintf(&shar->work, "chmod %o ",
|
||||
archive_entry_mode(shar->entry) & 07777);
|
||||
shar_quote(&shar->work, archive_entry_pathname(shar->entry), 1);
|
||||
archive_strcat(&shar->work, "\n");
|
||||
|
||||
u = archive_entry_uname(shar->entry);
|
||||
g = archive_entry_gname(shar->entry);
|
||||
if (u != NULL || g != NULL) {
|
||||
archive_strcat(&shar->work, "chown ");
|
||||
if (u != NULL)
|
||||
shar_quote(&shar->work, u, 1);
|
||||
if (g != NULL) {
|
||||
archive_strcat(&shar->work, ":");
|
||||
shar_quote(&shar->work, g, 1);
|
||||
}
|
||||
shar_quote(&shar->work,
|
||||
archive_entry_pathname(shar->entry), 1);
|
||||
archive_strcat(&shar->work, "\n");
|
||||
}
|
||||
|
||||
if ((p = archive_entry_fflags_text(shar->entry)) != NULL) {
|
||||
archive_string_sprintf(&shar->work, "chflags %s ", p);
|
||||
shar_quote(&shar->work,
|
||||
archive_entry_pathname(shar->entry), 1);
|
||||
archive_strcat(&shar->work, "\n");
|
||||
}
|
||||
|
||||
/* TODO: restore ACLs */
|
||||
|
||||
} else {
|
||||
if (shar->has_data) {
|
||||
/* Finish sed-encoded data: ensure last line ends. */
|
||||
if (!shar->end_of_line)
|
||||
archive_strappend_char(&shar->work, '\n');
|
||||
archive_strcat(&shar->work, "SHAR_END\n");
|
||||
}
|
||||
}
|
||||
|
||||
archive_entry_free(shar->entry);
|
||||
shar->entry = NULL;
|
||||
|
||||
if (shar->work.length < 65536)
|
||||
return (ARCHIVE_OK);
|
||||
|
||||
ret = (*a->compressor.write)(a, shar->work.s, shar->work.length);
|
||||
if (ret != ARCHIVE_OK)
|
||||
return (ARCHIVE_FATAL);
|
||||
archive_string_empty(&shar->work);
|
||||
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
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 == 0)
|
||||
return (ARCHIVE_OK);
|
||||
|
||||
archive_strcat(&shar->work, "exit\n");
|
||||
|
||||
ret = (*a->compressor.write)(a, shar->work.s, shar->work.length);
|
||||
if (ret != ARCHIVE_OK)
|
||||
return (ARCHIVE_FATAL);
|
||||
|
||||
/* 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 == NULL)
|
||||
return (ARCHIVE_OK);
|
||||
|
||||
archive_entry_free(shar->entry);
|
||||
free(shar->last_dir);
|
||||
archive_string_free(&(shar->work));
|
||||
archive_string_free(&(shar->quoted_name));
|
||||
free(shar);
|
||||
a->format_data = NULL;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
@ -1,587 +0,0 @@
|
||||
/*-
|
||||
* 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$");
|
||||
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#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", (int)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_name = "ustar";
|
||||
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.archive_format = ARCHIVE_FORMAT_TAR_USTAR;
|
||||
a->archive.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, ret2;
|
||||
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_filetype(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) {
|
||||
archive_set_error(&a->archive, ENOMEM,
|
||||
"Can't allocate ustar data");
|
||||
return(ARCHIVE_FATAL);
|
||||
}
|
||||
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_WARN)
|
||||
return (ret);
|
||||
ret2 = (a->compressor.write)(a, buff, 512);
|
||||
if (ret2 < ARCHIVE_WARN)
|
||||
return (ret2);
|
||||
if (ret2 < ret)
|
||||
ret = ret2;
|
||||
|
||||
ustar->entry_bytes_remaining = archive_entry_size(entry);
|
||||
ustar->entry_padding = 0x1ff & (-(int64_t)ustar->entry_bytes_remaining);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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, '/');
|
||||
/*
|
||||
* Look for the next '/' if we chose the first character
|
||||
* as the separator. (ustar format doesn't permit
|
||||
* an empty prefix.)
|
||||
*/
|
||||
if (p == pp)
|
||||
p = strchr(p + 1, '/');
|
||||
/* Fail if the name won't fit. */
|
||||
if (!p) {
|
||||
/* No separator. */
|
||||
archive_set_error(&a->archive, ENAMETOOLONG,
|
||||
"Pathname too long");
|
||||
ret = ARCHIVE_FAILED;
|
||||
} else if (p[1] == '\0') {
|
||||
/*
|
||||
* The only feasible separator is a final '/';
|
||||
* this would result in a non-empty prefix and
|
||||
* an empty name, which POSIX doesn't
|
||||
* explicity forbid, but it just feels wrong.
|
||||
*/
|
||||
archive_set_error(&a->archive, ENAMETOOLONG,
|
||||
"Pathname too long");
|
||||
ret = ARCHIVE_FAILED;
|
||||
} else if (p > pp + USTAR_prefix_size) {
|
||||
/* Prefix is too long. */
|
||||
archive_set_error(&a->archive, ENAMETOOLONG,
|
||||
"Pathname too long");
|
||||
ret = ARCHIVE_FAILED;
|
||||
} 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_FAILED;
|
||||
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_FAILED;
|
||||
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_FAILED;
|
||||
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_FAILED;
|
||||
}
|
||||
|
||||
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_FAILED;
|
||||
}
|
||||
|
||||
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_FAILED;
|
||||
}
|
||||
|
||||
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_FAILED;
|
||||
}
|
||||
|
||||
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_FAILED;
|
||||
}
|
||||
|
||||
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_FAILED;
|
||||
}
|
||||
|
||||
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_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
case AE_IFSOCK:
|
||||
archive_set_error(&a->archive,
|
||||
ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"tar format cannot archive socket");
|
||||
return (ARCHIVE_FAILED);
|
||||
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_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
@ -1,670 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2008 Anselm Strauss
|
||||
* Copyright (c) 2009 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Development supported by Google Summer of Code 2008.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The current implementation is very limited:
|
||||
*
|
||||
* - No encryption support.
|
||||
* - No ZIP64 support.
|
||||
* - No support for splitting and spanning.
|
||||
* - Only supports regular file and folder entries.
|
||||
*
|
||||
* Note that generally data in ZIP files is little-endian encoded,
|
||||
* with some exceptions.
|
||||
*
|
||||
* TODO: Since Libarchive is generally 64bit oriented, but this implementation
|
||||
* does not yet support sizes exceeding 32bit, it is highly fragile for
|
||||
* big archives. This should change when ZIP64 is finally implemented, otherwise
|
||||
* some serious checking has to be done.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "archive_platform.h"
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_ZLIB_H
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_endian.h"
|
||||
#include "archive_entry.h"
|
||||
#include "archive_private.h"
|
||||
#include "archive_write_private.h"
|
||||
|
||||
#ifndef HAVE_ZLIB_H
|
||||
#include "archive_crc32.h"
|
||||
#endif
|
||||
|
||||
#define ZIP_SIGNATURE_LOCAL_FILE_HEADER 0x04034b50
|
||||
#define ZIP_SIGNATURE_DATA_DESCRIPTOR 0x08074b50
|
||||
#define ZIP_SIGNATURE_FILE_HEADER 0x02014b50
|
||||
#define ZIP_SIGNATURE_CENTRAL_DIRECTORY_END 0x06054b50
|
||||
#define ZIP_SIGNATURE_EXTRA_TIMESTAMP 0x5455
|
||||
#define ZIP_SIGNATURE_EXTRA_UNIX 0x7855
|
||||
#define ZIP_VERSION_EXTRACT 0x0014 /* ZIP version 2.0 is needed. */
|
||||
#define ZIP_VERSION_BY 0x0314 /* Made by UNIX, using ZIP version 2.0. */
|
||||
#define ZIP_FLAGS 0x08 /* Flagging bit 3 (count from 0) for using data descriptor. */
|
||||
|
||||
enum compression {
|
||||
COMPRESSION_STORE = 0
|
||||
#ifdef HAVE_ZLIB_H
|
||||
,
|
||||
COMPRESSION_DEFLATE = 8
|
||||
#endif
|
||||
};
|
||||
|
||||
static ssize_t archive_write_zip_data(struct archive_write *, const void *buff, size_t s);
|
||||
static int archive_write_zip_finish(struct archive_write *);
|
||||
static int archive_write_zip_destroy(struct archive_write *);
|
||||
static int archive_write_zip_finish_entry(struct archive_write *);
|
||||
static int archive_write_zip_header(struct archive_write *, struct archive_entry *);
|
||||
static unsigned int dos_time(const time_t);
|
||||
static size_t path_length(struct archive_entry *);
|
||||
static int write_path(struct archive_entry *, struct archive_write *);
|
||||
|
||||
struct zip_local_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];
|
||||
};
|
||||
|
||||
struct zip_file_header {
|
||||
char signature[4];
|
||||
char version_by[2];
|
||||
char version_extract[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];
|
||||
char comment_length[2];
|
||||
char disk_number[2];
|
||||
char attributes_internal[2];
|
||||
char attributes_external[4];
|
||||
char offset[4];
|
||||
};
|
||||
|
||||
struct zip_data_descriptor {
|
||||
char signature[4]; /* Not mandatory, but recommended by specification. */
|
||||
char crc32[4];
|
||||
char compressed_size[4];
|
||||
char uncompressed_size[4];
|
||||
};
|
||||
|
||||
struct zip_extra_data_local {
|
||||
char time_id[2];
|
||||
char time_size[2];
|
||||
char time_flag[1];
|
||||
char mtime[4];
|
||||
char atime[4];
|
||||
char ctime[4];
|
||||
char unix_id[2];
|
||||
char unix_size[2];
|
||||
char unix_uid[2];
|
||||
char unix_gid[2];
|
||||
};
|
||||
|
||||
struct zip_extra_data_central {
|
||||
char time_id[2];
|
||||
char time_size[2];
|
||||
char time_flag[1];
|
||||
char mtime[4];
|
||||
char unix_id[2];
|
||||
char unix_size[2];
|
||||
};
|
||||
|
||||
struct zip_file_header_link {
|
||||
struct zip_file_header_link *next;
|
||||
struct archive_entry *entry;
|
||||
off_t offset;
|
||||
unsigned long crc32;
|
||||
off_t compressed_size;
|
||||
enum compression compression;
|
||||
};
|
||||
|
||||
struct zip {
|
||||
struct zip_data_descriptor data_descriptor;
|
||||
struct zip_file_header_link *central_directory;
|
||||
struct zip_file_header_link *central_directory_end;
|
||||
int64_t offset;
|
||||
int64_t written_bytes;
|
||||
int64_t remaining_data_bytes;
|
||||
enum compression compression;
|
||||
|
||||
#ifdef HAVE_ZLIB_H
|
||||
z_stream stream;
|
||||
size_t len_buf;
|
||||
unsigned char *buf;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct zip_central_directory_end {
|
||||
char signature[4];
|
||||
char disk[2];
|
||||
char start_disk[2];
|
||||
char entries_disk[2];
|
||||
char entries[2];
|
||||
char size[4];
|
||||
char offset[4];
|
||||
char comment_length[2];
|
||||
};
|
||||
|
||||
static int
|
||||
archive_write_zip_options(struct archive_write *a, const char *key,
|
||||
const char *value)
|
||||
{
|
||||
struct zip *zip = a->format_data;
|
||||
|
||||
if (strcmp(key, "compression") == 0) {
|
||||
if (strcmp(value, "deflate") == 0) {
|
||||
#ifdef HAVE_ZLIB_H
|
||||
zip->compression = COMPRESSION_DEFLATE;
|
||||
#else
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"deflate compression not supported");
|
||||
return ARCHIVE_WARN;
|
||||
#endif
|
||||
} else if (strcmp(value, "store") == 0)
|
||||
zip->compression = COMPRESSION_STORE;
|
||||
else
|
||||
return (ARCHIVE_WARN);
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
|
||||
int
|
||||
archive_write_set_format_zip(struct archive *_a)
|
||||
{
|
||||
struct archive_write *a = (struct archive_write *)_a;
|
||||
struct zip *zip;
|
||||
|
||||
/* If another format was already registered, unregister it. */
|
||||
if (a->format_destroy != NULL)
|
||||
(a->format_destroy)(a);
|
||||
|
||||
zip = (struct zip *) calloc(1, sizeof(*zip));
|
||||
if (zip == NULL) {
|
||||
archive_set_error(&a->archive, ENOMEM, "Can't allocate zip data");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
zip->central_directory = NULL;
|
||||
zip->central_directory_end = NULL;
|
||||
zip->offset = 0;
|
||||
zip->written_bytes = 0;
|
||||
zip->remaining_data_bytes = 0;
|
||||
|
||||
#ifdef HAVE_ZLIB_H
|
||||
zip->compression = COMPRESSION_DEFLATE;
|
||||
zip->len_buf = 65536;
|
||||
zip->buf = malloc(zip->len_buf);
|
||||
if (zip->buf == NULL) {
|
||||
archive_set_error(&a->archive, ENOMEM, "Can't allocate compression buffer");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
#else
|
||||
zip->compression = COMPRESSION_STORE;
|
||||
#endif
|
||||
|
||||
a->format_data = zip;
|
||||
|
||||
a->pad_uncompressed = 0; /* Actually not needed for now, since no compression support yet. */
|
||||
a->format_name = "zip";
|
||||
a->format_options = archive_write_zip_options;
|
||||
a->format_write_header = archive_write_zip_header;
|
||||
a->format_write_data = archive_write_zip_data;
|
||||
a->format_finish_entry = archive_write_zip_finish_entry;
|
||||
a->format_finish = archive_write_zip_finish;
|
||||
a->format_destroy = archive_write_zip_destroy;
|
||||
a->archive.archive_format = ARCHIVE_FORMAT_ZIP;
|
||||
a->archive.archive_format_name = "ZIP";
|
||||
|
||||
archive_le32enc(&zip->data_descriptor.signature,
|
||||
ZIP_SIGNATURE_DATA_DESCRIPTOR);
|
||||
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
archive_write_zip_header(struct archive_write *a, struct archive_entry *entry)
|
||||
{
|
||||
struct zip *zip;
|
||||
struct zip_local_file_header h;
|
||||
struct zip_extra_data_local e;
|
||||
struct zip_data_descriptor *d;
|
||||
struct zip_file_header_link *l;
|
||||
int ret;
|
||||
int64_t size;
|
||||
mode_t type;
|
||||
|
||||
/* Entries other than a regular file or a folder are skipped. */
|
||||
type = archive_entry_filetype(entry);
|
||||
if ((type != AE_IFREG) & (type != AE_IFDIR)) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Filetype not supported");
|
||||
return ARCHIVE_FAILED;
|
||||
};
|
||||
|
||||
/* Directory entries should have a size of 0. */
|
||||
if (type == AE_IFDIR)
|
||||
archive_entry_set_size(entry, 0);
|
||||
|
||||
zip = a->format_data;
|
||||
d = &zip->data_descriptor;
|
||||
size = archive_entry_size(entry);
|
||||
zip->remaining_data_bytes = size;
|
||||
|
||||
/* Append archive entry to the central directory data. */
|
||||
l = (struct zip_file_header_link *) malloc(sizeof(*l));
|
||||
if (l == NULL) {
|
||||
archive_set_error(&a->archive, ENOMEM, "Can't allocate zip header data");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
l->entry = archive_entry_clone(entry);
|
||||
/* Initialize the CRC variable and potentially the local crc32(). */
|
||||
l->crc32 = crc32(0, NULL, 0);
|
||||
l->compression = zip->compression;
|
||||
l->compressed_size = 0;
|
||||
l->next = NULL;
|
||||
if (zip->central_directory == NULL) {
|
||||
zip->central_directory = l;
|
||||
} else {
|
||||
zip->central_directory_end->next = l;
|
||||
}
|
||||
zip->central_directory_end = l;
|
||||
|
||||
/* Store the offset of this header for later use in central directory. */
|
||||
l->offset = zip->written_bytes;
|
||||
|
||||
memset(&h, 0, sizeof(h));
|
||||
archive_le32enc(&h.signature, ZIP_SIGNATURE_LOCAL_FILE_HEADER);
|
||||
archive_le16enc(&h.version, ZIP_VERSION_EXTRACT);
|
||||
archive_le16enc(&h.flags, ZIP_FLAGS);
|
||||
archive_le16enc(&h.compression, zip->compression);
|
||||
archive_le32enc(&h.timedate, dos_time(archive_entry_mtime(entry)));
|
||||
archive_le16enc(&h.filename_length, (uint16_t)path_length(entry));
|
||||
|
||||
switch (zip->compression) {
|
||||
case COMPRESSION_STORE:
|
||||
/* Setting compressed and uncompressed sizes even when specification says
|
||||
* to set to zero when using data descriptors. Otherwise the end of the
|
||||
* data for an entry is rather difficult to find. */
|
||||
archive_le32enc(&h.compressed_size, size);
|
||||
archive_le32enc(&h.uncompressed_size, size);
|
||||
break;
|
||||
#ifdef HAVE_ZLIB_H
|
||||
case COMPRESSION_DEFLATE:
|
||||
archive_le32enc(&h.uncompressed_size, size);
|
||||
|
||||
zip->stream.zalloc = Z_NULL;
|
||||
zip->stream.zfree = Z_NULL;
|
||||
zip->stream.opaque = Z_NULL;
|
||||
zip->stream.next_out = zip->buf;
|
||||
zip->stream.avail_out = zip->len_buf;
|
||||
if (deflateInit2(&zip->stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
|
||||
-15, 8, Z_DEFAULT_STRATEGY) != Z_OK) {
|
||||
archive_set_error(&a->archive, ENOMEM, "Can't init deflate compressor");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Formatting extra data. */
|
||||
archive_le16enc(&h.extra_length, sizeof(e));
|
||||
archive_le16enc(&e.time_id, ZIP_SIGNATURE_EXTRA_TIMESTAMP);
|
||||
archive_le16enc(&e.time_size, sizeof(e.time_flag) +
|
||||
sizeof(e.mtime) + sizeof(e.atime) + sizeof(e.ctime));
|
||||
e.time_flag[0] = 0x07;
|
||||
archive_le32enc(&e.mtime, archive_entry_mtime(entry));
|
||||
archive_le32enc(&e.atime, archive_entry_atime(entry));
|
||||
archive_le32enc(&e.ctime, archive_entry_ctime(entry));
|
||||
|
||||
archive_le16enc(&e.unix_id, ZIP_SIGNATURE_EXTRA_UNIX);
|
||||
archive_le16enc(&e.unix_size, sizeof(e.unix_uid) + sizeof(e.unix_gid));
|
||||
archive_le16enc(&e.unix_uid, archive_entry_uid(entry));
|
||||
archive_le16enc(&e.unix_gid, archive_entry_gid(entry));
|
||||
|
||||
archive_le32enc(&d->uncompressed_size, size);
|
||||
|
||||
ret = (a->compressor.write)(a, &h, sizeof(h));
|
||||
if (ret != ARCHIVE_OK)
|
||||
return (ARCHIVE_FATAL);
|
||||
zip->written_bytes += sizeof(h);
|
||||
|
||||
ret = write_path(entry, a);
|
||||
if (ret <= ARCHIVE_OK)
|
||||
return (ARCHIVE_FATAL);
|
||||
zip->written_bytes += ret;
|
||||
|
||||
ret = (a->compressor.write)(a, &e, sizeof(e));
|
||||
if (ret != ARCHIVE_OK)
|
||||
return (ARCHIVE_FATAL);
|
||||
zip->written_bytes += sizeof(e);
|
||||
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
archive_write_zip_data(struct archive_write *a, const void *buff, size_t s)
|
||||
{
|
||||
int ret;
|
||||
struct zip *zip = a->format_data;
|
||||
struct zip_file_header_link *l = zip->central_directory_end;
|
||||
|
||||
if ((int64_t)s > zip->remaining_data_bytes)
|
||||
s = (size_t)zip->remaining_data_bytes;
|
||||
|
||||
if (s == 0) return 0;
|
||||
|
||||
switch (zip->compression) {
|
||||
case COMPRESSION_STORE:
|
||||
ret = (a->compressor.write)(a, buff, s);
|
||||
if (ret != ARCHIVE_OK) return (ret);
|
||||
zip->written_bytes += s;
|
||||
zip->remaining_data_bytes -= s;
|
||||
l->compressed_size += s;
|
||||
l->crc32 = crc32(l->crc32, buff, s);
|
||||
return (s);
|
||||
#if HAVE_ZLIB_H
|
||||
case COMPRESSION_DEFLATE:
|
||||
zip->stream.next_in = (unsigned char*)(uintptr_t)buff;
|
||||
zip->stream.avail_in = s;
|
||||
do {
|
||||
ret = deflate(&zip->stream, Z_NO_FLUSH);
|
||||
if (ret == Z_STREAM_ERROR)
|
||||
return (ARCHIVE_FATAL);
|
||||
if (zip->stream.avail_out == 0) {
|
||||
ret = (a->compressor.write)(a, zip->buf, zip->len_buf);
|
||||
if (ret != ARCHIVE_OK)
|
||||
return (ret);
|
||||
l->compressed_size += zip->len_buf;
|
||||
zip->written_bytes += zip->len_buf;
|
||||
zip->stream.next_out = zip->buf;
|
||||
zip->stream.avail_out = zip->len_buf;
|
||||
}
|
||||
} while (zip->stream.avail_in != 0);
|
||||
zip->remaining_data_bytes -= s;
|
||||
/* If we have it, use zlib's fast crc32() */
|
||||
l->crc32 = crc32(l->crc32, buff, s);
|
||||
return (s);
|
||||
#endif
|
||||
|
||||
default:
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Invalid ZIP compression type");
|
||||
return ARCHIVE_FATAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
archive_write_zip_finish_entry(struct archive_write *a)
|
||||
{
|
||||
/* Write the data descripter after file data has been written. */
|
||||
int ret;
|
||||
struct zip *zip = a->format_data;
|
||||
struct zip_data_descriptor *d = &zip->data_descriptor;
|
||||
struct zip_file_header_link *l = zip->central_directory_end;
|
||||
#if HAVE_ZLIB_H
|
||||
size_t reminder;
|
||||
#endif
|
||||
|
||||
switch(zip->compression) {
|
||||
case COMPRESSION_STORE:
|
||||
break;
|
||||
#if HAVE_ZLIB_H
|
||||
case COMPRESSION_DEFLATE:
|
||||
for (;;) {
|
||||
ret = deflate(&zip->stream, Z_FINISH);
|
||||
if (ret == Z_STREAM_ERROR)
|
||||
return (ARCHIVE_FATAL);
|
||||
reminder = zip->len_buf - zip->stream.avail_out;
|
||||
ret = (a->compressor.write)(a, zip->buf, reminder);
|
||||
if (ret != ARCHIVE_OK)
|
||||
return (ret);
|
||||
l->compressed_size += reminder;
|
||||
zip->written_bytes += reminder;
|
||||
zip->stream.next_out = zip->buf;
|
||||
if (zip->stream.avail_out != 0)
|
||||
break;
|
||||
zip->stream.avail_out = zip->len_buf;
|
||||
}
|
||||
deflateEnd(&zip->stream);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
archive_le32enc(&d->crc32, l->crc32);
|
||||
archive_le32enc(&d->compressed_size, l->compressed_size);
|
||||
ret = (a->compressor.write)(a, d, sizeof(*d));
|
||||
if (ret != ARCHIVE_OK)
|
||||
return (ARCHIVE_FATAL);
|
||||
zip->written_bytes += sizeof(*d);
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
archive_write_zip_finish(struct archive_write *a)
|
||||
{
|
||||
struct zip *zip;
|
||||
struct zip_file_header_link *l;
|
||||
struct zip_file_header h;
|
||||
struct zip_central_directory_end end;
|
||||
struct zip_extra_data_central e;
|
||||
off_t offset_start, offset_end;
|
||||
int entries;
|
||||
int ret;
|
||||
|
||||
if (a->compressor.write == NULL)
|
||||
return (ARCHIVE_OK);
|
||||
|
||||
zip = a->format_data;
|
||||
l = zip->central_directory;
|
||||
|
||||
/*
|
||||
* Formatting central directory file header fields that are fixed for all entries.
|
||||
* Fields not used (and therefor 0) are:
|
||||
*
|
||||
* - comment_length
|
||||
* - disk_number
|
||||
* - attributes_internal
|
||||
*/
|
||||
memset(&h, 0, sizeof(h));
|
||||
archive_le32enc(&h.signature, ZIP_SIGNATURE_FILE_HEADER);
|
||||
archive_le16enc(&h.version_by, ZIP_VERSION_BY);
|
||||
archive_le16enc(&h.version_extract, ZIP_VERSION_EXTRACT);
|
||||
archive_le16enc(&h.flags, ZIP_FLAGS);
|
||||
|
||||
entries = 0;
|
||||
offset_start = zip->written_bytes;
|
||||
|
||||
/* Formatting individual header fields per entry and
|
||||
* writing each entry. */
|
||||
while (l != NULL) {
|
||||
archive_le16enc(&h.compression, l->compression);
|
||||
archive_le32enc(&h.timedate, dos_time(archive_entry_mtime(l->entry)));
|
||||
archive_le32enc(&h.crc32, l->crc32);
|
||||
archive_le32enc(&h.compressed_size, l->compressed_size);
|
||||
archive_le32enc(&h.uncompressed_size, archive_entry_size(l->entry));
|
||||
archive_le16enc(&h.filename_length, (uint16_t)path_length(l->entry));
|
||||
archive_le16enc(&h.extra_length, sizeof(e));
|
||||
archive_le16enc(&h.attributes_external[2], archive_entry_mode(l->entry));
|
||||
archive_le32enc(&h.offset, l->offset);
|
||||
|
||||
/* Formatting extra data. */
|
||||
archive_le16enc(&e.time_id, ZIP_SIGNATURE_EXTRA_TIMESTAMP);
|
||||
archive_le16enc(&e.time_size, sizeof(e.mtime) + sizeof(e.time_flag));
|
||||
e.time_flag[0] = 0x07;
|
||||
archive_le32enc(&e.mtime, archive_entry_mtime(l->entry));
|
||||
archive_le16enc(&e.unix_id, ZIP_SIGNATURE_EXTRA_UNIX);
|
||||
archive_le16enc(&e.unix_size, 0x0000);
|
||||
|
||||
ret = (a->compressor.write)(a, &h, sizeof(h));
|
||||
if (ret != ARCHIVE_OK)
|
||||
return (ARCHIVE_FATAL);
|
||||
zip->written_bytes += sizeof(h);
|
||||
|
||||
ret = write_path(l->entry, a);
|
||||
if (ret <= ARCHIVE_OK)
|
||||
return (ARCHIVE_FATAL);
|
||||
zip->written_bytes += ret;
|
||||
|
||||
ret = (a->compressor.write)(a, &e, sizeof(e));
|
||||
if (ret != ARCHIVE_OK)
|
||||
return (ARCHIVE_FATAL);
|
||||
zip->written_bytes += sizeof(e);
|
||||
|
||||
l = l->next;
|
||||
entries++;
|
||||
}
|
||||
offset_end = zip->written_bytes;
|
||||
|
||||
/* Formatting end of central directory. */
|
||||
memset(&end, 0, sizeof(end));
|
||||
archive_le32enc(&end.signature, ZIP_SIGNATURE_CENTRAL_DIRECTORY_END);
|
||||
archive_le16enc(&end.entries_disk, entries);
|
||||
archive_le16enc(&end.entries, entries);
|
||||
archive_le32enc(&end.size, offset_end - offset_start);
|
||||
archive_le32enc(&end.offset, offset_start);
|
||||
|
||||
/* Writing end of central directory. */
|
||||
ret = (a->compressor.write)(a, &end, sizeof(end));
|
||||
if (ret != ARCHIVE_OK)
|
||||
return (ARCHIVE_FATAL);
|
||||
zip->written_bytes += sizeof(end);
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
archive_write_zip_destroy(struct archive_write *a)
|
||||
{
|
||||
struct zip *zip;
|
||||
struct zip_file_header_link *l;
|
||||
|
||||
zip = a->format_data;
|
||||
while (zip->central_directory != NULL) {
|
||||
l = zip->central_directory;
|
||||
zip->central_directory = l->next;
|
||||
archive_entry_free(l->entry);
|
||||
free(l);
|
||||
}
|
||||
#ifdef HAVE_ZLIB_H
|
||||
free(zip->buf);
|
||||
#endif
|
||||
free(zip);
|
||||
a->format_data = NULL;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
/* Convert into MSDOS-style date/time. */
|
||||
static unsigned int
|
||||
dos_time(const time_t unix_time)
|
||||
{
|
||||
struct tm *t;
|
||||
unsigned int dt;
|
||||
|
||||
/* This will not preserve time when creating/extracting the archive
|
||||
* on two systems with different time zones. */
|
||||
t = localtime(&unix_time);
|
||||
|
||||
dt = 0;
|
||||
dt += ((t->tm_year - 80) & 0x7f) << 9;
|
||||
dt += ((t->tm_mon + 1) & 0x0f) << 5;
|
||||
dt += (t->tm_mday & 0x1f);
|
||||
dt <<= 16;
|
||||
dt += (t->tm_hour & 0x1f) << 11;
|
||||
dt += (t->tm_min & 0x3f) << 5;
|
||||
dt += (t->tm_sec & 0x3e) >> 1; /* Only counting every 2 seconds. */
|
||||
return dt;
|
||||
}
|
||||
|
||||
static size_t
|
||||
path_length(struct archive_entry *entry)
|
||||
{
|
||||
mode_t type;
|
||||
const char *path;
|
||||
|
||||
type = archive_entry_filetype(entry);
|
||||
path = archive_entry_pathname(entry);
|
||||
|
||||
if ((type == AE_IFDIR) & (path[strlen(path) - 1] != '/')) {
|
||||
return strlen(path) + 1;
|
||||
} else {
|
||||
return strlen(path);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
write_path(struct archive_entry *entry, struct archive_write *archive)
|
||||
{
|
||||
int ret;
|
||||
const char *path;
|
||||
mode_t type;
|
||||
size_t written_bytes;
|
||||
|
||||
path = archive_entry_pathname(entry);
|
||||
type = archive_entry_filetype(entry);
|
||||
written_bytes = 0;
|
||||
|
||||
ret = (archive->compressor.write)(archive, path, strlen(path));
|
||||
if (ret != ARCHIVE_OK)
|
||||
return (ARCHIVE_FATAL);
|
||||
written_bytes += strlen(path);
|
||||
|
||||
/* Folders are recognized by a traling slash. */
|
||||
if ((type == AE_IFDIR) & (path[strlen(path) - 1] != '/')) {
|
||||
ret = (archive->compressor.write)(archive, "/", 1);
|
||||
if (ret != ARCHIVE_OK)
|
||||
return (ARCHIVE_FATAL);
|
||||
written_bytes += 1;
|
||||
}
|
||||
|
||||
return ((int)written_bytes);
|
||||
}
|
@ -1,325 +0,0 @@
|
||||
.\" 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.
|
||||
.\" 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 October 5, 2007
|
||||
.Dt CPIO 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm cpio
|
||||
.Nd format of cpio 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.
|
||||
.Ss General Format
|
||||
Each file system object in a
|
||||
.Nm
|
||||
archive comprises a header record with basic numeric metadata
|
||||
followed by the full pathname of the entry and the file data.
|
||||
The header record stores a series of integer values that generally
|
||||
follow the fields in
|
||||
.Va struct stat .
|
||||
(See
|
||||
.Xr stat 2
|
||||
for details.)
|
||||
The variants differ primarily in how they store those integers
|
||||
(binary, octal, or hexadecimal).
|
||||
The header is followed by the pathname of the
|
||||
entry (the length of the pathname is stored in the header)
|
||||
and any file data.
|
||||
The end of the archive is indicated by a special record with
|
||||
the pathname
|
||||
.Dq TRAILER!!! .
|
||||
.Ss PWB format
|
||||
XXX Any documentation of the original PWB/UNIX 1.0 format? XXX
|
||||
.Ss Old Binary Format
|
||||
The old binary
|
||||
.Nm
|
||||
format stores numbers as 2-byte and 4-byte binary values.
|
||||
Each entry begins with a header in the following format:
|
||||
.Bd -literal -offset indent
|
||||
struct header_old_cpio {
|
||||
unsigned short c_magic;
|
||||
unsigned short c_dev;
|
||||
unsigned short c_ino;
|
||||
unsigned short c_mode;
|
||||
unsigned short c_uid;
|
||||
unsigned short c_gid;
|
||||
unsigned short c_nlink;
|
||||
unsigned short c_rdev;
|
||||
unsigned short c_mtime[2];
|
||||
unsigned short c_namesize;
|
||||
unsigned short c_filesize[2];
|
||||
};
|
||||
.Ed
|
||||
.Pp
|
||||
The
|
||||
.Va unsigned short
|
||||
fields here are 16-bit integer values; the
|
||||
.Va unsigned int
|
||||
fields are 32-bit integer values.
|
||||
The fields are as follows
|
||||
.Bl -tag -width indent
|
||||
.It Va magic
|
||||
The integer value octal 070707.
|
||||
This value can be used to determine whether this archive is
|
||||
written with little-endian or big-endian integers.
|
||||
.It Va dev , Va ino
|
||||
The device and inode numbers from the disk.
|
||||
These are used by programs that read
|
||||
.Nm
|
||||
archives to determine when two entries refer to the same file.
|
||||
Programs that synthesize
|
||||
.Nm
|
||||
archives should be careful to set these to distinct values for each entry.
|
||||
.It Va mode
|
||||
The mode specifies both the regular permissions and the file type.
|
||||
It consists of several bit fields as follows:
|
||||
.Bl -tag -width "MMMMMMM" -compact
|
||||
.It 0170000
|
||||
This masks the file type bits.
|
||||
.It 0140000
|
||||
File type value for sockets.
|
||||
.It 0120000
|
||||
File type value for symbolic links.
|
||||
For symbolic links, the link body is stored as file data.
|
||||
.It 0100000
|
||||
File type value for regular files.
|
||||
.It 0060000
|
||||
File type value for block special devices.
|
||||
.It 0040000
|
||||
File type value for directories.
|
||||
.It 0020000
|
||||
File type value for character special devices.
|
||||
.It 0010000
|
||||
File type value for named pipes or FIFOs.
|
||||
.It 0004000
|
||||
SUID bit.
|
||||
.It 0002000
|
||||
SGID bit.
|
||||
.It 0001000
|
||||
Sticky bit.
|
||||
On some systems, this modifies the behavior of executables and/or directories.
|
||||
.It 0000777
|
||||
The lower 9 bits specify read/write/execute permissions
|
||||
for world, group, and user following standard POSIX conventions.
|
||||
.El
|
||||
.It Va uid , Va gid
|
||||
The numeric user id and group id of the owner.
|
||||
.It Va nlink
|
||||
The number of links to this file.
|
||||
Directories always have a value of at least two here.
|
||||
Note that hardlinked files include file data with every copy in the archive.
|
||||
.It Va rdev
|
||||
For block special and character special entries,
|
||||
this field contains the associated device number.
|
||||
For all other entry types, it should be set to zero by writers
|
||||
and ignored by readers.
|
||||
.It Va mtime
|
||||
Modification time of the file, indicated as the number
|
||||
of seconds since the start of the epoch,
|
||||
00:00:00 UTC January 1, 1970.
|
||||
The four-byte integer is stored with the most-significant 16 bits first
|
||||
followed by the least-significant 16 bits.
|
||||
Each of the two 16 bit values are stored in machine-native byte order.
|
||||
.It Va namesize
|
||||
The number of bytes in the pathname that follows the header.
|
||||
This count includes the trailing NUL byte.
|
||||
.It Va filesize
|
||||
The size of the file.
|
||||
Note that this archive format is limited to
|
||||
four gigabyte file sizes.
|
||||
See
|
||||
.Va mtime
|
||||
above for a description of the storage of four-byte integers.
|
||||
.El
|
||||
.Pp
|
||||
The pathname immediately follows the fixed header.
|
||||
If the
|
||||
.Cm namesize
|
||||
is odd, an additional NUL byte is added after the pathname.
|
||||
The file data is then appended, padded with NUL
|
||||
bytes to an even length.
|
||||
.Pp
|
||||
Hardlinked files are not given special treatment;
|
||||
the full file contents are included with each copy of the
|
||||
file.
|
||||
.Ss Portable ASCII Format
|
||||
.St -susv2
|
||||
standardized an ASCII variant that is portable across all
|
||||
platforms.
|
||||
It is commonly known as the
|
||||
.Dq old character
|
||||
format or as the
|
||||
.Dq odc
|
||||
format.
|
||||
It stores the same numeric fields as the old binary format, but
|
||||
represents them as 6-character or 11-character octal values.
|
||||
.Bd -literal -offset indent
|
||||
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];
|
||||
};
|
||||
.Ed
|
||||
.Pp
|
||||
The fields are identical to those in the old binary format.
|
||||
The name and file body follow the fixed header.
|
||||
Unlike the old binary format, there is no additional padding
|
||||
after the pathname or file contents.
|
||||
If the files being archived are themselves entirely ASCII, then
|
||||
the resulting archive will be entirely ASCII, except for the
|
||||
NUL byte that terminates the name field.
|
||||
.Ss New ASCII Format
|
||||
The "new" ASCII format uses 8-byte hexadecimal fields for
|
||||
all numbers and separates device numbers into separate fields
|
||||
for major and minor numbers.
|
||||
.Bd -literal -offset indent
|
||||
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_check[8];
|
||||
};
|
||||
.Ed
|
||||
.Pp
|
||||
Except as specified below, the fields here match those specified
|
||||
for the old binary format above.
|
||||
.Bl -tag -width indent
|
||||
.It Va magic
|
||||
The string
|
||||
.Dq 070701 .
|
||||
.It Va check
|
||||
This field is always set to zero by writers and ignored by readers.
|
||||
See the next section for more details.
|
||||
.El
|
||||
.Pp
|
||||
The pathname is followed by NUL bytes so that the total size
|
||||
of the fixed header plus pathname is a multiple of four.
|
||||
Likewise, the file data is padded to a multiple of four bytes.
|
||||
Note that this format supports only 4 gigabyte files (unlike the
|
||||
older ASCII format, which supports 8 gigabyte files).
|
||||
.Pp
|
||||
In this format, hardlinked files are handled by setting the
|
||||
filesize to zero for each entry except the last one that
|
||||
appears in the archive.
|
||||
.Ss New CRC Format
|
||||
The CRC format is identical to the new ASCII format described
|
||||
in the previous section except that the magic field is set
|
||||
to
|
||||
.Dq 070702
|
||||
and the
|
||||
.Va check
|
||||
field is set to the sum of all bytes in the file data.
|
||||
This sum is computed treating all bytes as unsigned values
|
||||
and using unsigned arithmetic.
|
||||
Only the least-significant 32 bits of the sum are stored.
|
||||
.Ss HP variants
|
||||
The
|
||||
.Nm cpio
|
||||
implementation distributed with HPUX used XXXX but stored
|
||||
device numbers differently XXX.
|
||||
.Ss Other Extensions and Variants
|
||||
Sun Solaris uses additional file types to store extended file
|
||||
data, including ACLs and extended attributes, as special
|
||||
entries in cpio archives.
|
||||
.Pp
|
||||
XXX Others? XXX
|
||||
.Sh SEE ALSO
|
||||
.Xr cpio 1 ,
|
||||
.Xr tar 5
|
||||
.Sh STANDARDS
|
||||
The
|
||||
.Nm cpio
|
||||
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 portable ASCII format is currently part of the specification for the
|
||||
.Xr pax 1
|
||||
utility.
|
||||
.Sh HISTORY
|
||||
The original cpio utility was written by Dick Haight
|
||||
while working in AT&T's Unix Support Group.
|
||||
It appeared in 1977 as part of PWB/UNIX 1.0, the
|
||||
.Dq Programmer's Work Bench
|
||||
derived from
|
||||
.At v6
|
||||
that was used internally at AT&T.
|
||||
Both the old binary and old character formats were in use
|
||||
by 1980, according to the System III source released
|
||||
by SCO under their
|
||||
.Dq Ancient Unix
|
||||
license.
|
||||
The character format was adopted as part of
|
||||
.St -p1003.1-88 .
|
||||
XXX when did "newc" appear? Who invented it? When did HP come out with their variant? When did Sun introduce ACLs and extended attributes? XXX
|
||||
.Sh BUGS
|
||||
The
|
||||
.Dq CRC
|
||||
format is mis-named, as it uses a simple checksum and
|
||||
not a cyclic redundancy check.
|
||||
.Pp
|
||||
The old binary format is limited to 16 bits for user id,
|
||||
group id, device, and inode numbers.
|
||||
It is limited to 4 gigabyte file sizes.
|
||||
.Pp
|
||||
The old ASCII format is limited to 18 bits for
|
||||
the user id, group id, device, and inode numbers.
|
||||
It is limited to 8 gigabyte file sizes.
|
||||
.Pp
|
||||
The new ASCII format is limited to 4 gigabyte file sizes.
|
||||
.Pp
|
||||
None of the cpio formats store user or group names,
|
||||
which are essential when moving files between systems with
|
||||
dissimilar user or group numbering.
|
||||
.Pp
|
||||
Especially when writing older cpio variants, it may be necessary
|
||||
to map actual device/inode values to synthesized values that
|
||||
fit the available fields.
|
||||
With very large filesystems, this may be necessary even for
|
||||
the newer formats.
|
@ -1,161 +0,0 @@
|
||||
/*-
|
||||
* 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"
|
||||
|
||||
/* This capability is only available on POSIX systems. */
|
||||
#if defined(HAVE_PIPE) && defined(HAVE_FCNTL) && \
|
||||
(defined(HAVE_FORK) || defined(HAVE_VFORK))
|
||||
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#if defined(HAVE_POLL) && (defined(HAVE_POLL_H) || defined(HAVE_SYS_POLL_H))
|
||||
# if defined(HAVE_POLL_H)
|
||||
# include <poll.h>
|
||||
# elif defined(HAVE_SYS_POLL_H)
|
||||
# include <sys/poll.h>
|
||||
# endif
|
||||
#elif defined(HAVE_SELECT)
|
||||
# if defined(HAVE_SYS_SELECT_H)
|
||||
# include <sys/select.h>
|
||||
# elif defined(HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
# endif
|
||||
#endif
|
||||
#ifdef HAVE_FCNTL_H
|
||||
# include <fcntl.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#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] == 1 /* stdout */) {
|
||||
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] == 0 /* stdin */) {
|
||||
if ((tmp = dup(stdout_pipe[1])) == -1)
|
||||
goto stdout_opened;
|
||||
close(stdout_pipe[1]);
|
||||
stdout_pipe[1] = tmp;
|
||||
}
|
||||
|
||||
#if HAVE_VFORK
|
||||
switch ((child = vfork())) {
|
||||
#else
|
||||
switch ((child = fork())) {
|
||||
#endif
|
||||
case -1:
|
||||
goto stdout_opened;
|
||||
case 0:
|
||||
close(stdin_pipe[1]);
|
||||
close(stdout_pipe[0]);
|
||||
if (dup2(stdin_pipe[0], 0 /* stdin */) == -1)
|
||||
_exit(254);
|
||||
if (stdin_pipe[0] != 0 /* stdin */)
|
||||
close(stdin_pipe[0]);
|
||||
if (dup2(stdout_pipe[1], 1 /* stdout */) == -1)
|
||||
_exit(254);
|
||||
if (stdout_pipe[1] != 1 /* stdout */)
|
||||
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) && (defined(HAVE_POLL_H) || defined(HAVE_SYS_POLL_H))
|
||||
struct pollfd fds[2];
|
||||
int idx;
|
||||
|
||||
idx = 0;
|
||||
if (in != -1) {
|
||||
fds[idx].fd = in;
|
||||
fds[idx].events = POLLOUT;
|
||||
++idx;
|
||||
}
|
||||
if (out != -1) {
|
||||
fds[idx].fd = out;
|
||||
fds[idx].events = POLLIN;
|
||||
++idx;
|
||||
}
|
||||
|
||||
poll(fds, idx, -1); /* -1 == INFTIM, wait forever */
|
||||
#elif defined(HAVE_SELECT)
|
||||
fd_set fds_in, fds_out, fds_error;
|
||||
|
||||
FD_ZERO(&fds_in);
|
||||
FD_ZERO(&fds_out);
|
||||
FD_ZERO(&fds_error);
|
||||
if (out != -1) {
|
||||
FD_SET(out, &fds_in);
|
||||
FD_SET(out, &fds_error);
|
||||
}
|
||||
if (in != -1) {
|
||||
FD_SET(in, &fds_out);
|
||||
FD_SET(in, &fds_error);
|
||||
}
|
||||
select(in < out ? out + 1 : in + 1, &fds_in, &fds_out, &fds_error, NULL);
|
||||
#else
|
||||
sleep(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* defined(HAVE_PIPE) && defined(HAVE_VFORK) && defined(HAVE_FCNTL) */
|
@ -1,41 +0,0 @@
|
||||
/*-
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef __LIBARCHIVE_BUILD
|
||||
#error This header is only to be used internally to libarchive.
|
||||
#endif
|
||||
|
||||
#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
|
@ -1,355 +0,0 @@
|
||||
.\" Copyright (c) 2003-2009 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 December 27, 2009
|
||||
.Dt LIBARCHIVE-FORMATS 5
|
||||
.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, though many programs do use libarchive convenience
|
||||
functions to enable all supported formats.
|
||||
.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 can read GNU sparse file entries, including the new POSIX-based
|
||||
formats, but cannot write GNU sparse file entries.
|
||||
.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 immediately before each regular entry.
|
||||
The presence of these additional entries 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 and a few LIBARCHIVE keys.
|
||||
The libarchive library can read most of the SCHILY keys
|
||||
and most of the GNU keys introduced by GNU tar.
|
||||
It silently 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 8 gigabytes in size.
|
||||
.El
|
||||
Note that the pax interchange format has none of these restrictions.
|
||||
.El
|
||||
.Pp
|
||||
The libarchive library also reads a variety of commonly-used extensions to
|
||||
the basic tar format.
|
||||
These extensions are recognized automatically whenever they appear.
|
||||
.Bl -tag -width indent
|
||||
.It Numeric extensions.
|
||||
The POSIX standards require fixed-length numeric fields to be written with
|
||||
some character position reserved for terminators.
|
||||
Libarchive allows these fields to be written without terminator characters.
|
||||
This extends the allowable range; in particular, ustar archives with this
|
||||
extension can support entries up to 64 gigabytes in size.
|
||||
Libarchive also recognizes base-256 values in most numeric fields.
|
||||
This essentially removes all limitations on file size, modification time,
|
||||
and device numbers.
|
||||
.It Solaris extensions
|
||||
Libarchive recognizes ACL and extended attribute records written
|
||||
by Solaris tar.
|
||||
Currently, libarchive only has support for old-style ACLs; the
|
||||
newer NFSv4 ACLs are recognized but discarded.
|
||||
.El
|
||||
.Pp
|
||||
The first tar program appeared in Seventh Edition Unix in 1979.
|
||||
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.
|
||||
.Ss Cpio Formats
|
||||
The libarchive library can read a number of common cpio variants and can write
|
||||
.Dq odc
|
||||
and
|
||||
.Dq newc
|
||||
format archives.
|
||||
A cpio archive stores each entry as a fixed-size header followed
|
||||
by a variable-length filename and variable-length data.
|
||||
Unlike the tar format, the cpio format does only minimal padding
|
||||
of the header or file data.
|
||||
There are several cpio variants, 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 transparently reads 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, which is officially known as the
|
||||
.Dq cpio interchange format
|
||||
or the
|
||||
.Dq octet-oriented cpio archive format
|
||||
and sometimes unofficially referred to as the
|
||||
.Dq old character 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 first appeared in PWB/UNIX 1.0, which was released within
|
||||
AT&T in 1977.
|
||||
PWB/UNIX 1.0 formed the basis of System III Unix, released outside
|
||||
of AT&T in 1981.
|
||||
This makes cpio older than tar, although cpio was not included
|
||||
in Version 7 AT&T Unix.
|
||||
As a result, the tar command became much better known in universities
|
||||
and research groups that used Version 7.
|
||||
The combination of the
|
||||
.Nm find
|
||||
and
|
||||
.Nm cpio
|
||||
utilities provided very precise control over file selection.
|
||||
Unfortunately, the format 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 with dissimilar user numbering.
|
||||
.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.
|
||||
In many cases, this can remove the need to burn a physical CDROM
|
||||
just in order to read the files contained in an ISO9660 image.
|
||||
It also avoids security and complexity issues that come with
|
||||
virtual mounts and loopback devices.
|
||||
Libarchive supports the most common Rockridge extensions and has partial
|
||||
support for Joliet extensions.
|
||||
If both extensions are present, the Joliet extensions will be
|
||||
used and the Rockridge extensions will be ignored.
|
||||
In particular, this can create problems with hardlinks and symlinks,
|
||||
which are supported by Rockridge but not by Joliet.
|
||||
.Ss Zip format
|
||||
Libarchive can read and write zip format archives that have
|
||||
uncompressed entries and entries compressed with the
|
||||
.Dq deflate
|
||||
algorithm.
|
||||
Older zip compression algorithms are not supported.
|
||||
It can extract jar archives, archives that use Zip64 extensions and many
|
||||
self-extracting zip archives.
|
||||
Libarchive reads Zip archives as they are being streamed,
|
||||
which allows it to read archives of arbitrary size.
|
||||
It currently does not use the central directory; this
|
||||
limits libarchive's ability to support some self-extracting
|
||||
archives and ones that have been modified in certain ways.
|
||||
.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.
|
||||
The two differ primarily in their handling of filenames
|
||||
longer than 15 characters:
|
||||
the GNU/SVR4 variant writes a filename table at the beginning of the archive;
|
||||
the BSD format stores each long filename in an extension
|
||||
area adjacent to the entry.
|
||||
Libarchive can read both extensions,
|
||||
including archives that may include both types of long filenames.
|
||||
Programs using libarchive can write GNU/SVR4 format
|
||||
if they provide a filename table to be written into
|
||||
the archive before any of the entries.
|
||||
Any entries whose names are not in the filename table
|
||||
will be written using BSD-style long filenames.
|
||||
This can cause problems for programs such as
|
||||
GNU ld that do not support the BSD-style long filenames.
|
||||
.Ss mtree
|
||||
Libarchive can read and write files in
|
||||
.Xr mtree 5
|
||||
format.
|
||||
This format is not a true archive format, but rather a textual description
|
||||
of a file hierarchy in which each line specifies the name of a file and
|
||||
provides specific metadata about that file.
|
||||
Libarchive can read all of the keywords supported by both
|
||||
the NetBSD and FreeBSD versions of
|
||||
.Xr mtree 1 ,
|
||||
although many of the keywords cannot currently be stored in an
|
||||
.Tn archive_entry
|
||||
object.
|
||||
When writing, libarchive supports use of the
|
||||
.Xr archive_write_set_options 3
|
||||
interface to specify which keywords should be included in the
|
||||
output.
|
||||
If libarchive was compiled with access to suitable
|
||||
cryptographic libraries (such as the OpenSSL libraries),
|
||||
it can compute hash entries such as
|
||||
.Cm sha512
|
||||
or
|
||||
.Cm md5
|
||||
from file data being written to the mtree writer.
|
||||
.Pp
|
||||
When reading an mtree file, libarchive will locate the corresponding
|
||||
files on disk using the
|
||||
.Cm contents
|
||||
keyword if present or the regular filename.
|
||||
If it can locate and open the file on disk, it will use that
|
||||
to fill in any metadata that is missing from the mtree file
|
||||
and will read the file contents and return those to the program
|
||||
using libarchive.
|
||||
If it cannot locate and open the file on disk, libarchive
|
||||
will return an error for any attempt to read the entry
|
||||
body.
|
||||
.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 cpio 5 ,
|
||||
.Xr mtree 5 ,
|
||||
.Xr tar 5
|
@ -1,371 +0,0 @@
|
||||
.\" 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 February 6, 2010
|
||||
.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 (including RockRidge and Joliet extensions),
|
||||
.It
|
||||
Zip archives.
|
||||
.El
|
||||
The library automatically detects archives compressed with
|
||||
.Xr gzip 1 ,
|
||||
.Xr bzip2 1 ,
|
||||
.Xr xz 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
|
||||
Zip archive,
|
||||
.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 a
|
||||
.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, or
|
||||
.Fn archive_read_data_block
|
||||
which provides a slightly more efficient interface.
|
||||
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_free
|
||||
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_free
|
||||
function to release all resources.
|
||||
.Pp
|
||||
The
|
||||
.Xr archive_write 3
|
||||
manual page provides more detailed calling information for this API.
|
||||
.Sh WRITING ENTRIES TO DISK
|
||||
The
|
||||
.Xr archive_write_disk 3
|
||||
API allows you to write
|
||||
.Xr archive_entry 3
|
||||
objects to disk using the same API used by
|
||||
.Xr archive_write 3 .
|
||||
The
|
||||
.Xr archive_write_disk 3
|
||||
API is used internally by
|
||||
.Fn archive_read_extract ;
|
||||
using it directly can provide greater control over how entries
|
||||
get written to disk.
|
||||
This API also makes it possible to share code between
|
||||
archive-to-archive copy and archive-to-disk extraction
|
||||
operations.
|
||||
.Sh READING ENTRIES FROM DISK
|
||||
The
|
||||
.Xr archive_read_disk 3
|
||||
provides some support for populating
|
||||
.Xr archive_entry 3
|
||||
objects from information in the filesystem.
|
||||
.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
|
||||
.Cm ARCHIVE_OK
|
||||
(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 a
|
||||
.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.
|
||||
.Pp
|
||||
The
|
||||
.Xr archive_read_disk 3
|
||||
API should support iterating over filesystems;
|
||||
that would make it possible to share code among
|
||||
disk-to-archive, archive-to-archive, archive-to-disk,
|
||||
and disk-to-disk operations.
|
||||
Currently, it only supports reading the
|
||||
information for a single file.
|
||||
(Which is still quite useful, as it hides a lot
|
||||
of system-specific details.)
|
@ -1,74 +0,0 @@
|
||||
/*-
|
||||
* 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 "lafe_platform.h"
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#ifdef HAVE_STDARG_H
|
||||
#include <stdarg.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "err.h"
|
||||
|
||||
const char *lafe_progname;
|
||||
|
||||
static void
|
||||
lafe_vwarnc(int code, const char *fmt, va_list ap)
|
||||
{
|
||||
fprintf(stderr, "%s: ", lafe_progname);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
if (code != 0)
|
||||
fprintf(stderr, ": %s", strerror(code));
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
void
|
||||
lafe_warnc(int code, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
lafe_vwarnc(code, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
lafe_errc(int eval, int code, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
lafe_vwarnc(code, fmt, ap);
|
||||
va_end(ap);
|
||||
exit(eval);
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2009 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef LAFE_ERR_H
|
||||
#define LAFE_ERR_H
|
||||
|
||||
#if defined(__GNUC__) && (__GNUC__ > 2 || \
|
||||
(__GNUC__ == 2 && __GNUC_MINOR__ >= 5))
|
||||
#define __LA_DEAD __attribute__((__noreturn__))
|
||||
#else
|
||||
#define __LA_DEAD
|
||||
#endif
|
||||
|
||||
extern const char *lafe_progname;
|
||||
|
||||
void lafe_warnc(int code, const char *fmt, ...);
|
||||
void lafe_errc(int eval, int code, const char *fmt, ...) __LA_DEAD;
|
||||
|
||||
#endif
|
@ -1,55 +0,0 @@
|
||||
/*-
|
||||
* 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$
|
||||
*/
|
||||
|
||||
/*
|
||||
* This header is the first thing included in any of the libarchive_fe
|
||||
* source files. As far as possible, platform-specific issues should
|
||||
* be dealt with here and not within individual source files.
|
||||
*/
|
||||
|
||||
#ifndef LAFE_PLATFORM_H_INCLUDED
|
||||
#define LAFE_PLATFORM_H_INCLUDED
|
||||
|
||||
#if defined(PLATFORM_CONFIG_H)
|
||||
/* Use hand-built config.h in environments that need it. */
|
||||
#include PLATFORM_CONFIG_H
|
||||
#else
|
||||
/* Read config.h or die trying. */
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
/* Get a real definition for __FBSDID if we can */
|
||||
#if HAVE_SYS_CDEFS_H
|
||||
#include <sys/cdefs.h>
|
||||
#endif
|
||||
|
||||
/* If not, define it so as to avoid dangling semicolons. */
|
||||
#ifndef __FBSDID
|
||||
#define __FBSDID(a) struct _undefined_hack
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,171 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2008 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 "lafe_platform.h"
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "err.h"
|
||||
#include "line_reader.h"
|
||||
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__BORLANDC__)
|
||||
#define strdup _strdup
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
struct lafe_line_reader {
|
||||
FILE *f;
|
||||
char *buff, *buff_end, *line_start, *line_end, *p;
|
||||
char *pathname;
|
||||
size_t buff_length;
|
||||
int nullSeparator; /* Lines separated by null, not CR/CRLF/etc. */
|
||||
int ret;
|
||||
};
|
||||
|
||||
struct lafe_line_reader *
|
||||
lafe_line_reader(const char *pathname, int nullSeparator)
|
||||
{
|
||||
struct lafe_line_reader *lr;
|
||||
|
||||
lr = calloc(1, sizeof(*lr));
|
||||
if (lr == NULL)
|
||||
lafe_errc(1, ENOMEM, "Can't open %s", pathname);
|
||||
|
||||
lr->nullSeparator = nullSeparator;
|
||||
lr->pathname = strdup(pathname);
|
||||
|
||||
if (strcmp(pathname, "-") == 0)
|
||||
lr->f = stdin;
|
||||
else
|
||||
lr->f = fopen(pathname, "r");
|
||||
if (lr->f == NULL)
|
||||
lafe_errc(1, errno, "Couldn't open %s", pathname);
|
||||
lr->buff_length = 8192;
|
||||
lr->buff = malloc(lr->buff_length);
|
||||
if (lr->buff == NULL)
|
||||
lafe_errc(1, ENOMEM, "Can't read %s", pathname);
|
||||
lr->line_start = lr->line_end = lr->buff_end = lr->buff;
|
||||
|
||||
return (lr);
|
||||
}
|
||||
|
||||
const char *
|
||||
lafe_line_reader_next(struct lafe_line_reader *lr)
|
||||
{
|
||||
size_t bytes_wanted, bytes_read, new_buff_size;
|
||||
char *line_start, *p;
|
||||
|
||||
for (;;) {
|
||||
/* If there's a line in the buffer, return it immediately. */
|
||||
while (lr->line_end < lr->buff_end) {
|
||||
if (lr->nullSeparator) {
|
||||
if (*lr->line_end == '\0') {
|
||||
line_start = lr->line_start;
|
||||
lr->line_start = lr->line_end + 1;
|
||||
lr->line_end = lr->line_start;
|
||||
return (line_start);
|
||||
}
|
||||
} else if (*lr->line_end == '\x0a' || *lr->line_end == '\x0d') {
|
||||
*lr->line_end = '\0';
|
||||
line_start = lr->line_start;
|
||||
lr->line_start = lr->line_end + 1;
|
||||
lr->line_end = lr->line_start;
|
||||
if (line_start[0] != '\0')
|
||||
return (line_start);
|
||||
}
|
||||
lr->line_end++;
|
||||
}
|
||||
|
||||
/* If we're at end-of-file, process the final data. */
|
||||
if (lr->f == NULL) {
|
||||
/* If there's more text, return one last line. */
|
||||
if (lr->line_end > lr->line_start) {
|
||||
*lr->line_end = '\0';
|
||||
line_start = lr->line_start;
|
||||
lr->line_start = lr->line_end + 1;
|
||||
lr->line_end = lr->line_start;
|
||||
return (line_start);
|
||||
}
|
||||
/* Otherwise, we're done. */
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* Buffer only has part of a line. */
|
||||
if (lr->line_start > lr->buff) {
|
||||
/* Move a leftover fractional line to the beginning. */
|
||||
memmove(lr->buff, lr->line_start,
|
||||
lr->buff_end - lr->line_start);
|
||||
lr->buff_end -= lr->line_start - lr->buff;
|
||||
lr->line_end -= lr->line_start - lr->buff;
|
||||
lr->line_start = lr->buff;
|
||||
} else {
|
||||
/* Line is too big; enlarge the buffer. */
|
||||
new_buff_size = lr->buff_length * 2;
|
||||
if (new_buff_size <= lr->buff_length)
|
||||
lafe_errc(1, ENOMEM,
|
||||
"Line too long in %s", lr->pathname);
|
||||
lr->buff_length = new_buff_size;
|
||||
p = realloc(lr->buff, new_buff_size);
|
||||
if (p == NULL)
|
||||
lafe_errc(1, ENOMEM,
|
||||
"Line too long in %s", lr->pathname);
|
||||
lr->buff_end = p + (lr->buff_end - lr->buff);
|
||||
lr->line_end = p + (lr->line_end - lr->buff);
|
||||
lr->line_start = lr->buff = p;
|
||||
}
|
||||
|
||||
/* Get some more data into the buffer. */
|
||||
bytes_wanted = lr->buff + lr->buff_length - lr->buff_end;
|
||||
bytes_read = fread(lr->buff_end, 1, bytes_wanted, lr->f);
|
||||
lr->buff_end += bytes_read;
|
||||
|
||||
if (ferror(lr->f))
|
||||
lafe_errc(1, errno, "Can't read %s", lr->pathname);
|
||||
if (feof(lr->f)) {
|
||||
if (lr->f != stdin)
|
||||
fclose(lr->f);
|
||||
lr->f = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
lafe_line_reader_free(struct lafe_line_reader *lr)
|
||||
{
|
||||
free(lr->buff);
|
||||
free(lr->pathname);
|
||||
free(lr);
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2009 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef LAFE_LINE_READER_H
|
||||
#define LAFE_LINE_READER_H
|
||||
|
||||
struct lafe_line_reader;
|
||||
|
||||
struct lafe_line_reader *lafe_line_reader(const char *, int nullSeparator);
|
||||
const char *lafe_line_reader_next(struct lafe_line_reader *);
|
||||
void lafe_line_reader_free(struct lafe_line_reader *);
|
||||
|
||||
#endif
|
@ -1,281 +0,0 @@
|
||||
/*-
|
||||
* 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 "lafe_platform.h"
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "err.h"
|
||||
#include "line_reader.h"
|
||||
#include "matching.h"
|
||||
#include "pathmatch.h"
|
||||
|
||||
struct match {
|
||||
struct match *next;
|
||||
int matches;
|
||||
char pattern[1];
|
||||
};
|
||||
|
||||
struct lafe_matching {
|
||||
struct match *exclusions;
|
||||
int exclusions_count;
|
||||
struct match *inclusions;
|
||||
int inclusions_count;
|
||||
int inclusions_unmatched_count;
|
||||
};
|
||||
|
||||
static void add_pattern(struct match **list, const char *pattern);
|
||||
static void initialize_matching(struct lafe_matching **);
|
||||
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
|
||||
lafe_exclude(struct lafe_matching **matching, const char *pattern)
|
||||
{
|
||||
|
||||
if (*matching == NULL)
|
||||
initialize_matching(matching);
|
||||
add_pattern(&((*matching)->exclusions), pattern);
|
||||
(*matching)->exclusions_count++;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
lafe_exclude_from_file(struct lafe_matching **matching, const char *pathname)
|
||||
{
|
||||
struct lafe_line_reader *lr;
|
||||
const char *p;
|
||||
int ret = 0;
|
||||
|
||||
lr = lafe_line_reader(pathname, 0);
|
||||
while ((p = lafe_line_reader_next(lr)) != NULL) {
|
||||
if (lafe_exclude(matching, p) != 0)
|
||||
ret = -1;
|
||||
}
|
||||
lafe_line_reader_free(lr);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
int
|
||||
lafe_include(struct lafe_matching **matching, const char *pattern)
|
||||
{
|
||||
|
||||
if (*matching == NULL)
|
||||
initialize_matching(matching);
|
||||
add_pattern(&((*matching)->inclusions), pattern);
|
||||
(*matching)->inclusions_count++;
|
||||
(*matching)->inclusions_unmatched_count++;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
lafe_include_from_file(struct lafe_matching **matching, const char *pathname,
|
||||
int nullSeparator)
|
||||
{
|
||||
struct lafe_line_reader *lr;
|
||||
const char *p;
|
||||
int ret = 0;
|
||||
|
||||
lr = lafe_line_reader(pathname, nullSeparator);
|
||||
while ((p = lafe_line_reader_next(lr)) != NULL) {
|
||||
if (lafe_include(matching, p) != 0)
|
||||
ret = -1;
|
||||
}
|
||||
lafe_line_reader_free(lr);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static void
|
||||
add_pattern(struct match **list, const char *pattern)
|
||||
{
|
||||
struct match *match;
|
||||
size_t len;
|
||||
|
||||
len = strlen(pattern);
|
||||
match = malloc(sizeof(*match) + len + 1);
|
||||
if (match == NULL)
|
||||
lafe_errc(1, errno, "Out of memory");
|
||||
strcpy(match->pattern, pattern);
|
||||
/* Both "foo/" and "foo" should match "foo/bar". */
|
||||
if (len && match->pattern[len - 1] == '/')
|
||||
match->pattern[strlen(match->pattern)-1] = '\0';
|
||||
match->next = *list;
|
||||
*list = match;
|
||||
match->matches = 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
lafe_excluded(struct lafe_matching *matching, const char *pathname)
|
||||
{
|
||||
struct match *match;
|
||||
struct match *matched;
|
||||
|
||||
if (matching == NULL)
|
||||
return (0);
|
||||
|
||||
/* Mark off any unmatched inclusions. */
|
||||
/* In particular, if a filename does appear in the archive and
|
||||
* is explicitly included and excluded, then we don't report
|
||||
* it as missing even though we don't extract it.
|
||||
*/
|
||||
matched = NULL;
|
||||
for (match = matching->inclusions; match != NULL; match = match->next){
|
||||
if (match->matches == 0
|
||||
&& match_inclusion(match, pathname)) {
|
||||
matching->inclusions_unmatched_count--;
|
||||
match->matches++;
|
||||
matched = match;
|
||||
}
|
||||
}
|
||||
|
||||
/* Exclusions take priority */
|
||||
for (match = matching->exclusions; match != NULL; match = match->next){
|
||||
if (match_exclusion(match, pathname))
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* It's not excluded and we found an inclusion above, so it's included. */
|
||||
if (matched != NULL)
|
||||
return (0);
|
||||
|
||||
|
||||
/* We didn't find an unmatched inclusion, check the remaining ones. */
|
||||
for (match = matching->inclusions; match != NULL; match = match->next){
|
||||
/* We looked at previously-unmatched inclusions already. */
|
||||
if (match->matches > 0
|
||||
&& match_inclusion(match, pathname)) {
|
||||
match->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'
|
||||
*
|
||||
*/
|
||||
static int
|
||||
match_exclusion(struct match *match, const char *pathname)
|
||||
{
|
||||
return (lafe_pathmatch(match->pattern,
|
||||
pathname,
|
||||
PATHMATCH_NO_ANCHOR_START | PATHMATCH_NO_ANCHOR_END));
|
||||
}
|
||||
|
||||
/*
|
||||
* Again, mimic gtar: inclusions are always anchored (have to match
|
||||
* the beginning of the path) even though exclusions are not anchored.
|
||||
*/
|
||||
static int
|
||||
match_inclusion(struct match *match, const char *pathname)
|
||||
{
|
||||
return (lafe_pathmatch(match->pattern, pathname, PATHMATCH_NO_ANCHOR_END));
|
||||
}
|
||||
|
||||
void
|
||||
lafe_cleanup_exclusions(struct lafe_matching **matching)
|
||||
{
|
||||
struct match *p, *q;
|
||||
|
||||
if (*matching == NULL)
|
||||
return;
|
||||
|
||||
for (p = (*matching)->inclusions; p != NULL; ) {
|
||||
q = p;
|
||||
p = p->next;
|
||||
free(q);
|
||||
}
|
||||
|
||||
for (p = (*matching)->exclusions; p != NULL; ) {
|
||||
q = p;
|
||||
p = p->next;
|
||||
free(q);
|
||||
}
|
||||
|
||||
free(*matching);
|
||||
*matching = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
initialize_matching(struct lafe_matching **matching)
|
||||
{
|
||||
*matching = calloc(sizeof(**matching), 1);
|
||||
if (*matching == NULL)
|
||||
lafe_errc(1, errno, "No memory");
|
||||
}
|
||||
|
||||
int
|
||||
lafe_unmatched_inclusions(struct lafe_matching *matching)
|
||||
{
|
||||
|
||||
if (matching == NULL)
|
||||
return (0);
|
||||
return (matching->inclusions_unmatched_count);
|
||||
}
|
||||
|
||||
int
|
||||
lafe_unmatched_inclusions_warn(struct lafe_matching *matching, const char *msg)
|
||||
{
|
||||
struct match *p;
|
||||
|
||||
if (matching == NULL)
|
||||
return (0);
|
||||
|
||||
for (p = matching->inclusions; p != NULL; p = p->next) {
|
||||
if (p->matches == 0)
|
||||
lafe_warnc(0, "%s: %s", p->pattern, msg);
|
||||
}
|
||||
|
||||
return (matching->inclusions_unmatched_count);
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
/*-
|
||||
* 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$
|
||||
*/
|
||||
|
||||
#ifndef MATCHING_H
|
||||
#define MATCHING_H
|
||||
|
||||
struct lafe_matching;
|
||||
|
||||
int lafe_exclude(struct lafe_matching **matching, const char *pattern);
|
||||
int lafe_exclude_from_file(struct lafe_matching **matching,
|
||||
const char *pathname);
|
||||
int lafe_include(struct lafe_matching **matching, const char *pattern);
|
||||
int lafe_include_from_file(struct lafe_matching **matching,
|
||||
const char *pathname, int nullSeparator);
|
||||
|
||||
int lafe_excluded(struct lafe_matching *, const char *pathname);
|
||||
void lafe_cleanup_exclusions(struct lafe_matching **);
|
||||
int lafe_unmatched_inclusions(struct lafe_matching *);
|
||||
int lafe_unmatched_inclusions_warn(struct lafe_matching *, const char *msg);
|
||||
|
||||
#endif
|
@ -1,255 +0,0 @@
|
||||
/*-
|
||||
* 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 "lafe_platform.h"
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "pathmatch.h"
|
||||
|
||||
/*
|
||||
* Check whether a character 'c' is matched by a list specification [...]:
|
||||
* * Leading '!' or '^' negates the class.
|
||||
* * <char>-<char> is a range of characters
|
||||
* * \<char> removes any special meaning for <char>
|
||||
*
|
||||
* Some interesting boundary cases:
|
||||
* a-d-e is one range (a-d) followed by two single characters - and e.
|
||||
* \a-\d is same as a-d
|
||||
* a\-d is three single characters: a, d, -
|
||||
* Trailing - is not special (so [a-] is two characters a and -).
|
||||
* Initial - is not special ([a-] is same as [-a] is same as [\\-a])
|
||||
* This function never sees a trailing \.
|
||||
* [] always fails
|
||||
* [!] always succeeds
|
||||
*/
|
||||
static int
|
||||
pm_list(const char *start, const char *end, const char c, int flags)
|
||||
{
|
||||
const char *p = start;
|
||||
char rangeStart = '\0', nextRangeStart;
|
||||
int match = 1, nomatch = 0;
|
||||
|
||||
/* This will be used soon... */
|
||||
(void)flags; /* UNUSED */
|
||||
|
||||
/* If this is a negated class, return success for nomatch. */
|
||||
if ((*p == '!' || *p == '^') && p < end) {
|
||||
match = 0;
|
||||
nomatch = 1;
|
||||
++p;
|
||||
}
|
||||
|
||||
while (p < end) {
|
||||
nextRangeStart = '\0';
|
||||
switch (*p) {
|
||||
case '-':
|
||||
/* Trailing or initial '-' is not special. */
|
||||
if ((rangeStart == '\0') || (p == end - 1)) {
|
||||
if (*p == c)
|
||||
return (match);
|
||||
} else {
|
||||
char rangeEnd = *++p;
|
||||
if (rangeEnd == '\\')
|
||||
rangeEnd = *++p;
|
||||
if ((rangeStart <= c) && (c <= rangeEnd))
|
||||
return (match);
|
||||
}
|
||||
break;
|
||||
case '\\':
|
||||
++p;
|
||||
/* Fall through */
|
||||
default:
|
||||
if (*p == c)
|
||||
return (match);
|
||||
nextRangeStart = *p; /* Possible start of range. */
|
||||
}
|
||||
rangeStart = nextRangeStart;
|
||||
++p;
|
||||
}
|
||||
return (nomatch);
|
||||
}
|
||||
|
||||
/*
|
||||
* If s is pointing to "./", ".//", "./././" or the like, skip it.
|
||||
*/
|
||||
static const char *
|
||||
pm_slashskip(const char *s) {
|
||||
while ((*s == '/')
|
||||
|| (s[0] == '.' && s[1] == '/')
|
||||
|| (s[0] == '.' && s[1] == '\0'))
|
||||
++s;
|
||||
return (s);
|
||||
}
|
||||
|
||||
static int
|
||||
pm(const char *p, const char *s, int flags)
|
||||
{
|
||||
const char *end;
|
||||
|
||||
/*
|
||||
* Ignore leading './', './/', '././', etc.
|
||||
*/
|
||||
if (s[0] == '.' && s[1] == '/')
|
||||
s = pm_slashskip(s + 1);
|
||||
if (p[0] == '.' && p[1] == '/')
|
||||
p = pm_slashskip(p + 1);
|
||||
|
||||
for (;;) {
|
||||
switch (*p) {
|
||||
case '\0':
|
||||
if (s[0] == '/') {
|
||||
if (flags & PATHMATCH_NO_ANCHOR_END)
|
||||
return (1);
|
||||
/* "dir" == "dir/" == "dir/." */
|
||||
s = pm_slashskip(s);
|
||||
}
|
||||
return (*s == '\0');
|
||||
case '?':
|
||||
/* ? always succeds, unless we hit end of 's' */
|
||||
if (*s == '\0')
|
||||
return (0);
|
||||
break;
|
||||
case '*':
|
||||
/* "*" == "**" == "***" ... */
|
||||
while (*p == '*')
|
||||
++p;
|
||||
/* Trailing '*' always succeeds. */
|
||||
if (*p == '\0')
|
||||
return (1);
|
||||
while (*s) {
|
||||
if (lafe_pathmatch(p, s, flags))
|
||||
return (1);
|
||||
++s;
|
||||
}
|
||||
return (0);
|
||||
case '[':
|
||||
/*
|
||||
* Find the end of the [...] character class,
|
||||
* ignoring \] that might occur within the class.
|
||||
*/
|
||||
end = p + 1;
|
||||
while (*end != '\0' && *end != ']') {
|
||||
if (*end == '\\' && end[1] != '\0')
|
||||
++end;
|
||||
++end;
|
||||
}
|
||||
if (*end == ']') {
|
||||
/* We found [...], try to match it. */
|
||||
if (!pm_list(p + 1, end, *s, flags))
|
||||
return (0);
|
||||
p = end; /* Jump to trailing ']' char. */
|
||||
break;
|
||||
} else
|
||||
/* No final ']', so just match '['. */
|
||||
if (*p != *s)
|
||||
return (0);
|
||||
break;
|
||||
case '\\':
|
||||
/* Trailing '\\' matches itself. */
|
||||
if (p[1] == '\0') {
|
||||
if (*s != '\\')
|
||||
return (0);
|
||||
} else {
|
||||
++p;
|
||||
if (*p != *s)
|
||||
return (0);
|
||||
}
|
||||
break;
|
||||
case '/':
|
||||
if (*s != '/' && *s != '\0')
|
||||
return (0);
|
||||
/* Note: pattern "/\./" won't match "/";
|
||||
* pm_slashskip() correctly stops at backslash. */
|
||||
p = pm_slashskip(p);
|
||||
s = pm_slashskip(s);
|
||||
if (*p == '\0' && (flags & PATHMATCH_NO_ANCHOR_END))
|
||||
return (1);
|
||||
--p; /* Counteract the increment below. */
|
||||
--s;
|
||||
break;
|
||||
case '$':
|
||||
/* '$' is special only at end of pattern and only
|
||||
* if PATHMATCH_NO_ANCHOR_END is specified. */
|
||||
if (p[1] == '\0' && (flags & PATHMATCH_NO_ANCHOR_END)){
|
||||
/* "dir" == "dir/" == "dir/." */
|
||||
return (*pm_slashskip(s) == '\0');
|
||||
}
|
||||
/* Otherwise, '$' is not special. */
|
||||
/* FALL THROUGH */
|
||||
default:
|
||||
if (*p != *s)
|
||||
return (0);
|
||||
break;
|
||||
}
|
||||
++p;
|
||||
++s;
|
||||
}
|
||||
}
|
||||
|
||||
/* Main entry point. */
|
||||
int
|
||||
lafe_pathmatch(const char *p, const char *s, int flags)
|
||||
{
|
||||
/* Empty pattern only matches the empty string. */
|
||||
if (p == NULL || *p == '\0')
|
||||
return (s == NULL || *s == '\0');
|
||||
|
||||
/* Leading '^' anchors the start of the pattern. */
|
||||
if (*p == '^') {
|
||||
++p;
|
||||
flags &= ~PATHMATCH_NO_ANCHOR_START;
|
||||
}
|
||||
|
||||
if (*p == '/' && *s != '/')
|
||||
return (0);
|
||||
|
||||
/* Certain patterns and file names anchor implicitly. */
|
||||
if (*p == '*' || *p == '/' || *p == '/') {
|
||||
while (*p == '/')
|
||||
++p;
|
||||
while (*s == '/')
|
||||
++s;
|
||||
return (pm(p, s, flags));
|
||||
}
|
||||
|
||||
/* If start is unanchored, try to match start of each path element. */
|
||||
if (flags & PATHMATCH_NO_ANCHOR_START) {
|
||||
for ( ; s != NULL; s = strchr(s, '/')) {
|
||||
if (*s == '/')
|
||||
s++;
|
||||
if (pm(p, s, flags))
|
||||
return (1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Default: Match from beginning. */
|
||||
return (pm(p, s, flags));
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
/*-
|
||||
* 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$
|
||||
*/
|
||||
|
||||
#ifndef LAFE_PATHMATCH_H
|
||||
#define LAFE_PATHMATCH_H
|
||||
|
||||
/* Don't anchor at beginning unless the pattern starts with "^" */
|
||||
#define PATHMATCH_NO_ANCHOR_START 1
|
||||
/* Don't anchor at end unless the pattern ends with "$" */
|
||||
#define PATHMATCH_NO_ANCHOR_END 2
|
||||
|
||||
/* Note that "^" and "$" are not special unless you set the corresponding
|
||||
* flag above. */
|
||||
|
||||
int lafe_pathmatch(const char *p, const char *s, int flags);
|
||||
|
||||
#endif
|
@ -1,365 +0,0 @@
|
||||
.\" 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 archive.
|
||||
(Prior to 2.4.0, the format bidders were invoked for each
|
||||
entry, but this design hindered error recovery.)
|
||||
.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.
|
||||
In particular, clients are allowed to return zero for any
|
||||
skip that they don't want to handle.
|
||||
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 Decompression 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 decompression 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.)
|
||||
.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 .
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user