summaryrefslogtreecommitdiffstats
path: root/libtar
diff options
context:
space:
mode:
Diffstat (limited to 'libtar')
-rw-r--r--libtar/Android.mk43
-rw-r--r--libtar/COPYRIGHT35
-rw-r--r--libtar/ChangeLog335
-rw-r--r--libtar/ChangeLog-1.0.x141
-rw-r--r--libtar/INSTALL183
-rw-r--r--libtar/README121
-rw-r--r--libtar/TODO21
-rw-r--r--libtar/android_utils.c123
-rw-r--r--libtar/android_utils.h22
-rw-r--r--libtar/append.c439
-rw-r--r--libtar/basename.c74
-rw-r--r--libtar/block.c735
-rw-r--r--libtar/compat.h257
-rw-r--r--libtar/config.h188
-rw-r--r--libtar/decode.c135
-rw-r--r--libtar/dirname.c77
-rw-r--r--libtar/encode.c239
-rw-r--r--libtar/extract.c667
-rw-r--r--libtar/fnmatch.c237
-rw-r--r--libtar/gethostbyname_r.c41
-rw-r--r--libtar/gethostname.c36
-rw-r--r--libtar/getservbyname_r.c41
-rw-r--r--libtar/glob.c873
-rw-r--r--libtar/handle.c132
-rw-r--r--libtar/inet_aton.c27
-rw-r--r--libtar/internal.h17
-rw-r--r--libtar/libtar.h352
-rw-r--r--libtar/libtar_hash.c344
-rw-r--r--libtar/libtar_list.c458
-rw-r--r--libtar/libtar_listhash.h203
-rw-r--r--libtar/output.c143
-rw-r--r--libtar/snprintf.c761
-rw-r--r--libtar/strdup.c62
-rw-r--r--libtar/strlcat.c72
-rw-r--r--libtar/strlcpy.c68
-rw-r--r--libtar/strmode.c152
-rw-r--r--libtar/strrstr.c40
-rw-r--r--libtar/strsep.c87
-rw-r--r--libtar/tar.h108
-rw-r--r--libtar/util.c221
-rw-r--r--libtar/wrapper.c181
41 files changed, 8491 insertions, 0 deletions
diff --git a/libtar/Android.mk b/libtar/Android.mk
new file mode 100644
index 000000000..90a5006c5
--- /dev/null
+++ b/libtar/Android.mk
@@ -0,0 +1,43 @@
+LOCAL_PATH := $(call my-dir)
+
+# Build shared library
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libtar
+LOCAL_MODULE_TAGS := eng optional
+LOCAL_SRC_FILES := append.c block.c decode.c encode.c extract.c handle.c output.c util.c wrapper.c basename.c strmode.c libtar_hash.c libtar_list.c dirname.c android_utils.c
+LOCAL_C_INCLUDES += $(LOCAL_PATH) \
+ external/zlib
+LOCAL_SHARED_LIBRARIES += libz libc
+
+LOCAL_C_INCLUDES += external/libselinux/include
+LOCAL_SHARED_LIBRARIES += libselinux
+
+ifeq ($(TW_INCLUDE_CRYPTO_FBE), true)
+ LOCAL_SHARED_LIBRARIES += libe4crypt
+ LOCAL_CFLAGS += -DHAVE_EXT4_CRYPT
+ LOCAL_C_INCLUDES += $(LOCAL_PATH)/../crypto/ext4crypt
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
+# Build static library
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libtar_static
+LOCAL_MODULE_TAGS := eng optional
+LOCAL_SRC_FILES := append.c block.c decode.c encode.c extract.c handle.c output.c util.c wrapper.c basename.c strmode.c libtar_hash.c libtar_list.c dirname.c android_utils.c
+LOCAL_C_INCLUDES += $(LOCAL_PATH) \
+ external/zlib
+LOCAL_STATIC_LIBRARIES += libz libc
+
+LOCAL_C_INCLUDES += external/libselinux/include
+LOCAL_STATIC_LIBRARIES += libselinux
+
+ifeq ($(TW_INCLUDE_CRYPTO_FBE), true)
+ LOCAL_SHARED_LIBRARIES += libe4crypt
+ LOCAL_CFLAGS += -DHAVE_EXT4_CRYPT
+ LOCAL_C_INCLUDES += $(LOCAL_PATH)/../crypto/ext4crypt
+endif
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/libtar/COPYRIGHT b/libtar/COPYRIGHT
new file mode 100644
index 000000000..2471ec01b
--- /dev/null
+++ b/libtar/COPYRIGHT
@@ -0,0 +1,35 @@
+Copyright (c) 1998-2003 University of Illinois Board of Trustees
+Copyright (c) 1998-2003 Mark D. Roth
+All rights reserved.
+
+Developed by: Campus Information Technologies and Educational Services,
+ University of Illinois at Urbana-Champaign
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+``Software''), to deal with the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimers.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimers in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the names of Campus Information Technologies and Educational
+ Services, University of Illinois at Urbana-Champaign, nor the names
+ of its contributors may be used to endorse or promote products derived
+ from this Software without specific prior written permission.
+
+THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+
diff --git a/libtar/ChangeLog b/libtar/ChangeLog
new file mode 100644
index 000000000..03bef6874
--- /dev/null
+++ b/libtar/ChangeLog
@@ -0,0 +1,335 @@
+ NOTE:
+ All releases below marked (Chris Frey) are maintenance releases
+ done by Chris Frey, temporarily stepping in for Mark Roth.
+ These releases are git-based only and can be found at:
+ http://repo.or.cz/w/libtar.git
+
+ Both git downloads and tarball downloads are possible at this site.
+
+
+libtar 1.2.20 - 2013/10/09 (Chris Frey)
+-------------
+ Added extern "C" protectors to listhash.h
+ Added autoconf checks for __thread compiler support
+ Fixed size_t overflow bug, as reported by Timo Warns
+ Fixed thread-safe bug in th_get_pathname() (Sergey Zhitomirsky)
+
+
+libtar 1.2.19 - 2012/12/11 (Chris Frey)
+-------------
+ Removed varargs.h and all dependencies, to avoid user compile errors
+
+ Fixed some short int / int compiler warnings in va_arg() usage
+
+ Fixed some gcc built-in compiler warnings
+
+ Changed autoconf support code from AC_RUN_ to AC_COMPILE_ to fix
+ issues reported during cross-compiling.
+
+ Applied most of Jan Cermak's const char* function argument patch.
+
+
+libtar 1.2.18 - 2012/08/02 (Chris Frey)
+-------------
+ Added more forgiving CRC checking logic when reading tar files
+
+ Note: If your application uses the macro th_crc_ok(), then to gain full
+ advantage of the changes in this version, you will need to recompile
+ your application against the new headers. Otherwise, the library is
+ drop-in replaceable, as usual.
+
+
+libtar 1.2.17 - 2012/07/24 (Chris Frey)
+-------------
+ Applied Tim Band's checksum patch from mailing list (thanks!)
+
+
+libtar 1.2.16 - 2012/05/17 (Chris Frey)
+-------------
+ Fixed build system to allow for out-of-source tree builds
+
+
+libtar 1.2.15 - 2012/05/10 (Chris Frey)
+-------------
+Chris Frey (1):
+ Fixed harmless buffer overflow which is caught by FORTIFY on some systems
+
+
+libtar 1.2.14 - 2011/12/22 (Chris Frey)
+-------------
+Chris Frey (1):
+ Fixed truncation check, so 100 char names get GNU extension support when enabled
+
+
+libtar 1.2.13 - 2011/06/13 (Chris Frey)
+-------------
+Chris Frey (10):
+ Fixed incorrect URL in readme
+ Added autoconf/ as macro dir
+ Added autogen.sh script to build a fresh configure
+ Renamed autoconf/aclocal.m4 to psg.m4 so aclocal isn't so confused
+ Removed m4 includes, and straightened out [] m4 quoting for modern autoconfs
+ Removed auto-generated files
+ Added datarootdir to Makefile.in's
+ Fixed header warnings
+ Applied Marcin Gibula's patch fixing tar_extract_glob()
+ Changed root Makefile.in to Makefile.am, which make autoreconf workable
+
+Glenn McGrath (1):
+ Use libtool to build dynamic library
+
+James Morrison (1):
+ Document stupidity of tartype_t in libtar.c.
+
+Magnus Holmgren (1):
+ Escape hyphens that should be minus signs in man pages.
+
+Per Lidén (2):
+ Fix memory leak in th_get_pathname
+ Reduce memory used by libtar when extracting files.
+
+------------------------------------------------------------------------------
+
+libtar 1.2.11 - 3/2/03
+-------------
+
+- updated autoconf macros, compat code, and listhash code
+- fixed tar_extract_regfile() to pass mode argument to open()
+ (caused EPERM on Solaris NFS clients)
+- updated README
+
+------------------------------------------------------------------------------
+
+libtar 1.2.10 - 12/15/02
+-------------
+
+- updated README
+- minor Makefile fixes
+- fixed TH_ISREG() macro to not return true for hard links
+
+------------------------------------------------------------------------------
+
+libtar 1.2.9 - 11/19/02
+------------
+
+- fixed th_read() to return 1 on EOF
+ (thanks to Yves Crespin <Crespin.Quartz@WANADOO.FR> for the bug report)
+- minor portability fixes
+ (thanks to Yves Crespin <Crespin.Quartz@WANADOO.FR> for the bug report)
+- fixed segfault on extracting filenames with 8-bit ASCII characters
+ (thanks to Per Liden <per@FUKT.BTH.SE> for the patch)
+- fixed TH_ISDIR() macro and th_get_mode() function to handle old
+ archives that don't set the typeflag field right for directories
+- use 0777 instead of 0755 in mkdirhier()
+ (thanks to Yves Crespin <Crespin.Quartz@WANADOO.FR> for the bug report)
+
+------------------------------------------------------------------------------
+
+libtar 1.2.8 - 9/13/02
+------------
+
+- added "-I../listhash" to CPPFLAGS in libtar/Makefile.in
+ (thanks to Kris Warkentin <kewarken@QNX.COM> for the bug report)
+- added .PHONY target to Makefile.in
+ (thanks to Steven Engelhardt <sengelha@YAHOO.COM> for the bug report)
+
+------------------------------------------------------------------------------
+
+libtar 1.2.7 - 9/12/02
+------------
+
+- fixed minor bugs in listhash code
+ (thanks to Jim Knoble <jmknoble@pobox.com> for the bug reports)
+
+------------------------------------------------------------------------------
+
+libtar 1.2.6 - 9/10/02
+------------
+
+- updated COPYRIGHT file
+- do not check magic field by default
+ (replaced TAR_IGNORE_MAGIC option with TAR_CHECK_MAGIC to enable check)
+- fixed th_get_mode() not to modify S_IFMT bits if they were already set
+- fixed TH_IS*() macros to check the S_IFMT mode bits in addition to typeflag
+ (this allows us to handle old tar archives that set mode bits but not
+ typeflag field for directories and other special files)
+- updated to autoconf-2.53
+- restructured autoconf macros
+- added "b" to gzoflags in gzopen_frontend() for win32 compatibility
+ (thanks to Kris Eric Warkentin <kewarken@QNX.COM> for reporting this)
+- if O_BINARY is defined (as on win32), set that bit in oflags in tar_open()
+ (thanks to Kris Eric Warkentin <kewarken@QNX.COM> for reporting this)
+- also use O_BINARY in when calling open() from tar_extract_regfile()
+ (based on patch from Graeme Peterson <gp@qnx.com>)
+- added COMPAT_FUNC_MAKEDEV macro to handle 3-arg version of makedev()
+ (based on patch from Graeme Peterson <gp@qnx.com>)
+
+------------------------------------------------------------------------------
+
+libtar 1.2.5 - 2/20/02
+------------
+
+- updated to autoconf-2.52
+- improved Makefile portability
+- fixed memory leak in hard-link detection code
+ (thanks to Michael Kamp <kamp@HITT.NL> for the bug report)
+- fixed memory leak in symlink handling code
+ (thanks to Michael Kamp <kamp@HITT.NL> for the bug report)
+- fixed memory leak in GNU long filename code
+
+------------------------------------------------------------------------------
+
+libtar 1.2.4 - 7/24/01
+------------
+
+- code cleanups to make gcc -Wall happy
+ (thanks to Jim Knoble <jmknoble@POBOX.COM> for the patch)
+- call utime() before chmod() in tar_set_file_perms() for cygwin
+ (thanks to Kris Eric Warkentin <kewarken@QNX.COM> for reporting this)
+- added "-g" flag to trigger GNU extensions in libtar binary
+- fixed buffer termination bugs in POSIX filename prefix encoding
+ (thanks to Joerg Schilling <schilling@fokus.gmd.de> for reporting this)
+- fixed bug in th_crc_calc() for filenames with 8-bit ASCII characters
+ (thanks to Hamdouni El Bachir <bach@zehc.net> for reporting the bug
+ and Antoniu-George SAVU <santoniu@libertysurf.fr> for the patch)
+- fixed backwards conditional expression in th_read()
+ (thanks to Antoniu-George SAVU <santoniu@LIBERTYSURF.FR> for the patch)
+- added new tar_open() options to replace compile-time settings:
+ TAR_IGNORE_EOT, TAR_IGNORE_MAGIC, TAR_CHECK_VERSION, TAR_IGNORE_CRC
+ (based on feedback from Kris Eric Warkentin <kewarken@QNX.COM>)
+
+------------------------------------------------------------------------------
+
+libtar 1.2.3 - 6/26/01
+------------
+
+- misc portability fixes for OpenBSD
+- fixed libtar.h to work with C++ programs
+- fixed tar_extract_file() to properly check for pre-existing symlinks
+ (based on patch from Per Lid?n <per@fukt.hk-r.se>)
+- fixed hash creation in tar_init()
+- replaced mkdirhier() with non-recursive version
+- updated autoconf macros, compat code, and listhash code
+- reformatted code for readability
+
+------------------------------------------------------------------------------
+
+libtar 1.2.2 - 1/12/01
+------------
+
+- fixed th_print_long_ls() to not truncate user and group names
+- code cleanups to make -Wall happy
+
+------------------------------------------------------------------------------
+
+libtar 1.2.1 - 1/8/01
+------------
+
+- updated WSG_ENCAP autoconf macro
+- fixed autoconf macros to behave properly when a config.cache file
+ is present
+- fixed doc/Makefile.in to create links during compilation, not
+ installation
+- fixed listhash manpage .so link lists
+
+------------------------------------------------------------------------------
+
+libtar 1.2 - 1/4/01
+----------
+
+- minor code cleanups
+
+------------------------------------------------------------------------------
+
+libtar 1.1.b8 - 1/2/01
+-------------
+
+- updated WSG_ENCAP autoconf macro
+
+------------------------------------------------------------------------------
+
+libtar 1.1.b7 - 12/13/00
+-------------
+
+- fixed autoconf snprintf() test to make sure it NUL-terminates
+
+------------------------------------------------------------------------------
+
+libtar 1.1.b6 - 11/30/00
+-------------
+
+- added $(DESTDIR) to Makefiles
+- Makefile changes to support WSG_PKG and WSG_ENCAP autoconf macros
+- changed lib/output.c to use strftime() where available
+
+------------------------------------------------------------------------------
+
+libtar 1.1.b5 - 10/29/00
+-------------
+
+- Makefile fix
+
+------------------------------------------------------------------------------
+
+libtar 1.1.b4 - 10/29/00
+-------------
+
+- more directory reorganization
+- minor Makefile cleanups
+- minor portability fixes
+- added function typecasting to avoid compiler warnings
+
+------------------------------------------------------------------------------
+
+libtar 1.1.b3 - 10/26/00
+-------------
+
+- updated aclocal.m4
+- updated README
+- updated manpages
+- minor directory structure changes because of CVS setup
+
+------------------------------------------------------------------------------
+
+libtar 1.1.b2 - 10/5/00
+-------------
+
+- added --without-zlib configure option
+- minor portability fixes
+
+------------------------------------------------------------------------------
+
+libtar 1.1.b1 - 8/21/00
+-------------
+
+- API changes:
+ - implemented tar_fdopen()
+ - implemented tar_fd()
+ - added TAR **t argument to tar_open() instead of returning dynamic memory
+ - if TAR_NOOVERWRITE is set in options and O_CREAT is set in oflags,
+ tar_open() automatically sets O_EXCL as well
+
+------------------------------------------------------------------------------
+
+libtar 1.1.b0 - 7/10/00
+-------------
+
+- API changes:
+ - replaced internal table of tar file types with a tartype_t passed to
+ tar_open() by the caller
+ (allows file access methods to be defined dynamically)
+ - fixed tar_append_tree() to grok normal files as well as directories
+ - replaced mk_dirs_for_file() with mkdirhier() from epkg
+ - replaced strtok_r() with strsep()
+ - updated list/hash code to new interface
+
+- autoconf changes:
+ - added aclocal.m4 to clean up configure.in
+ - minor portability fixes related to lib/fnmatch.c
+
+- fixed a bug in tar_open() where the result of open() was being
+ checked for 0 instead of -1 to detect error
+
+- updated libtar driver program to handle both .tar.gz and ordinary .tar
+ via the -z option
+
diff --git a/libtar/ChangeLog-1.0.x b/libtar/ChangeLog-1.0.x
new file mode 100644
index 000000000..23b06b3ac
--- /dev/null
+++ b/libtar/ChangeLog-1.0.x
@@ -0,0 +1,141 @@
+libtar 1.0.2 - 6/21/00
+------------
+
+- tar_set_file_perms() now calls chown() only if the effective user ID is 0
+ (workaround for IRIX and HP-UX, which allow file giveaways)
+
+- tar_set_file_perms() now calls chmod() or lchmod() after chown()
+ (this fixes a problem with extracting setuid files under Linux)
+
+- removed calls to fchown() and fchmod() from tar_extract_regfile()
+
+- fixed bugs in th_read() which didn't set errno properly
+
+- removed various unused variables
+
+----------------------------------------------------------------------
+
+libtar 1.0.1 - 4/1/00
+------------
+
+- removed libgen.h include from dirname and basename compat code
+
+- added lib/fnmatch.c compatability module from OpenBSD
+
+- fixed several objdirs bugs in libtar/Makefile.in
+
+- misc Makefile changes (added $CPPFLAGS support, added -o flag to compile
+ commands, use $CFLAGS on link line, etc)
+
+- removed "inline" keyword from all source files to prevent portability
+ problems
+
+- updated README
+
+----------------------------------------------------------------------
+
+libtar 1.0 - 1/2/00
+----------
+
+- various portability fixes
+
+- "make install" now runs mkencap and epkg if they're available
+
+- libmisc is now integrated into libtar
+
+----------------------------------------------------------------------
+
+libtar 0.5.6 beta - 12/16/99
+-----------------
+
+- changed API to allow better error reporting via errno
+
+- added manpages to document libtar API
+
+- replaced symbolic_mode() call with strmode() compatibility code
+
+----------------------------------------------------------------------
+
+libtar 0.5.5 beta - 11/16/99
+-----------------
+
+- fixed conditional expression in extract.c to check if we're overwriting
+ a pre-existing file
+
+- many improvements to libtar.c driver program (better error checking,
+ added -C and -v options, etc)
+
+- changed API to include list of canned file types, instead of passing
+ function pointers to tar_open()
+
+- fixed tar_set_file_perms() to not complain about chown() if not root
+ and not to call utime() on a symlink
+
+- added hash code for extracting hard links in other directory paths
+
+- fixed tar_extract_glob() to only print filenames if TAR_VERBOSE option
+ is set
+
+- replaced GNU basename(), dirname(), and strdup() compatibility code
+ with OpenBSD versions
+
+- configure performs super-anal checking of basename() and dirname()
+
+----------------------------------------------------------------------
+
+libtar 0.5.4 beta - 11/13/99
+-----------------
+
+- portability fix: use ranlib instead of ar -s
+
+- misc fixes in append.c, extract.c, and wrapper.c to do error checking
+
+- fixed a bug in tar_append_file() in append.c which added some garbage
+ characters to encoded symlink names (wasn't NULL-terminating the result
+ of readlink())
+
+- fixed a bug in symbolic_mode() in output.c concerning setuid and setgid
+ bit displaying
+
+- fixed tar_extract_all() in wrapper.c to only call print_long_ls() if
+ the TAR_VERBOSE option is set
+
+- added libtar_version constant string to handle.c for external configure
+ scripts to detect what version of libtar is installed
+
+----------------------------------------------------------------------
+
+libtar 0.5.3 beta - 09/27/99
+-----------------
+
+- fixed mk_dirs_for_file() to avoid broken dirname() implementations
+
+- misc portability fixes
+
+- merged old "compat" and "libds" directories into new "misc" directory
+ and cleaned up Makefiles
+
+----------------------------------------------------------------------
+
+libtar 0.5.2 beta - 09/10/99
+-----------------
+
+- use calloc() instead of malloc() in tar_open() to fix a bounds-checking
+ bug in tar_extract_all()
+
+- fix tar_extract_all() to properly honor the prefix argument
+
+----------------------------------------------------------------------
+
+libtar 0.5.1 beta - 08/27/99
+-----------------
+
+- misc portability fixes
+
+----------------------------------------------------------------------
+
+libtar 0.5 beta - 07/05/99
+---------------
+
+- first public release
+
diff --git a/libtar/INSTALL b/libtar/INSTALL
new file mode 100644
index 000000000..50dbe439d
--- /dev/null
+++ b/libtar/INSTALL
@@ -0,0 +1,183 @@
+Basic Installation
+==================
+
+ These are generic installation instructions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, a file
+`config.cache' that saves the results of its tests to speed up
+reconfiguring, and a file `config.log' containing compiler output
+(useful mainly for debugging `configure').
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If at some point `config.cache'
+contains results you don't want to keep, you may remove or edit it.
+
+ The file `configure.in' is used to create `configure' by a program
+called `autoconf'. You only need `configure.in' if you want to change
+it or regenerate `configure' using a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes awhile. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+Compilers and Options
+=====================
+
+ Some systems require unusual options for compilation or linking that
+the `configure' script does not know about. You can give `configure'
+initial values for variables by setting them in the environment. Using
+a Bourne-compatible shell, you can do that on the command line like
+this:
+ CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
+
+Or on systems that have the `env' program, you can do it like this:
+ env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
+
+Compiling For Multiple Architectures
+====================================
+
+ You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ If you have to use a `make' that does not supports the `VPATH'
+variable, you have to compile the package for one architecture at a time
+in the source code directory. After you have installed the package for
+one architecture, use `make distclean' before reconfiguring for another
+architecture.
+
+Installation Names
+==================
+
+ By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc. You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PATH'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+give `configure' the option `--exec-prefix=PATH', the package will use
+PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=PATH' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+ Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+ There may be some features `configure' can not figure out
+automatically, but needs to determine by the type of host the package
+will run on. Usually `configure' can figure that out, but if it prints
+a message saying it can not guess the host type, give it the
+`--host=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name with three fields:
+ CPU-COMPANY-SYSTEM
+
+See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the host type.
+
+ If you are building compiler tools for cross-compiling, you can also
+use the `--target=TYPE' option to select the type of system they will
+produce code for and the `--build=TYPE' option to select the type of
+system on which you are compiling the package.
+
+Sharing Defaults
+================
+
+ If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Operation Controls
+==================
+
+ `configure' recognizes the following options to control how it
+operates.
+
+`--cache-file=FILE'
+ Use and save the results of the tests in FILE instead of
+ `./config.cache'. Set FILE to `/dev/null' to disable caching, for
+ debugging `configure'.
+
+`--help'
+ Print a summary of the options to `configure', and exit.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`--version'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`configure' also accepts some other, not widely useful, options.
+
diff --git a/libtar/README b/libtar/README
new file mode 100644
index 000000000..91f466cbe
--- /dev/null
+++ b/libtar/README
@@ -0,0 +1,121 @@
+libtar - C library for manipulating tar files
+======
+
+libtar is a library for manipulating tar files from within C programs.
+Here are some of its features:
+
+ * Handles both POSIX tar file format and the GNU extensions.
+ * API provides functions for easy use, such as tar_extract_all().
+ * Also provides functions for more granular use, such as
+ tar_append_regfile().
+
+
+Installation
+------------
+
+To build libtar, you should be able to simply run these commands:
+
+ ./configure
+ make
+ make install
+
+
+Encap Package Support
+---------------------
+
+To build this software as an Encap package, you can pass the
+--enable-encap option to configure. This will be automatically
+enabled if the epkg or mkencap programs are detected on the system,
+but can be overridden by the --disable-encap option.
+
+When building an Encap package, the configure script will automatically
+adjust the installation prefix to use an appropriate Encap package
+directory. It does this using a heuristic algorithm which examines the
+values of the ${ENCAP_SOURCE} and ${ENCAP_TARGET} environment variables
+and the argument to configure's --prefix option.
+
+If mkencap was detected on the system, it will be automatically run during
+"make install". By default, epkg will also be run, but this can be
+inhibited with the --disable-epkg-install configure option.
+
+For information on the Encap package management system, see the WSG
+Encap Archive:
+
+ http://www.encap.org/
+
+
+zlib Support
+------------
+
+The configure script will attempt to find the zlib library on your system
+for use with the libtar driver program. The zlib package is available from:
+
+ http://www.gzip.org/zlib/
+
+If zlib is installed on your system, but you do not wish to use it,
+specify the --without-zlib option when you invoke configure.
+
+
+More Information
+----------------
+
+For documentation of the libtar API, see the enclosed manpages. For more
+information on the libtar package, see:
+
+ http://www-dev.cites.uiuc.edu/libtar/
+
+Source code for the latest version of libtar will be available there, as
+well as Encap binary distributions for many common platforms.
+
+
+Supported Platforms
+-------------------
+
+I develop and test libtar on the following platforms:
+
+ AIX 4.3.3 and 5.1
+ HP-UX 11.00
+ IRIX 6.5
+ RedHat Linux 7.2
+ Solaris 8 and 9
+
+It should also build on the following platforms, but I do not actively
+support them:
+
+ AIX 3.2.5
+ AIX 4.2.1
+ Cygwin
+ FreeBSD
+ HP-UX 10.20
+ Linux/libc5
+ OpenBSD
+ Solaris 2.5
+ Solaris 2.6
+ Solaris 7
+
+If you successfully build libtar on another platform, please email me a
+patch and/or configuration information.
+
+
+Compatibility Code
+------------------
+
+libtar depends on some library calls which are not available or not
+usable on some platforms. To accomodate these systems, I've included
+a version of these calls in the compat subdirectory.
+
+I've slightly modified these functions for integration into this source
+tree, but the functionality has not been modified from the original
+source. Please note that while this code should work for you, I didn't
+write it, so please don't send me bug reports on it.
+
+
+Author
+------
+
+Feedback and bug reports are welcome.
+
+Mark D. Roth <roth@uiuc.edu>
+Campus Information Technologies and Educational Services
+University of Illinois at Urbana-Champaign
+
diff --git a/libtar/TODO b/libtar/TODO
new file mode 100644
index 000000000..f2f859f54
--- /dev/null
+++ b/libtar/TODO
@@ -0,0 +1,21 @@
+Functionality:
+--------------
+
+* add list mode to allow nodes to be inserted in any arbitrary location
+* add "*_hash_iterate()" function
+* add flags argument to *_list_del() that allows the listptr to be set
+ to the previous or next element
+* add a generic pointer type to replace *_listptr_t and *_hashptr_t ???
+
+
+Code Cleanup:
+-------------
+
+* rename functions:
+ *_list_next => *_listptr_next()
+ *_list_prev => *_listptr_prev()
+ *_hash_next => *_hashptr_next()
+* start using "*_list_t" and "*_hash_t" instead of "*_list_t *" and
+ "*_hash_t *" ?
+* add prefixes to structure member field names
+
diff --git a/libtar/android_utils.c b/libtar/android_utils.c
new file mode 100644
index 000000000..4aa3425b7
--- /dev/null
+++ b/libtar/android_utils.c
@@ -0,0 +1,123 @@
+/*
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/xattr.h>
+#include <string.h>
+#include <linux/limits.h>
+#include <errno.h>
+
+#include "libtar.h"
+#include "android_utils.h"
+
+/* This code may come in handy later if we ever need to extend to storing more user.inode_* xattrs
+#define USER_INODE_SEPARATOR "\0"
+#define ANDROID_USER_INODE_XATTR_PREFIX "user.inode_"
+#define ANDROID_USER_INODE_XATTR_PREFIX_LEN strlen(ANDROID_USER_INODE_XATTR_PREFIX)
+
+char* scan_xattrs_for_user_inode (const char *realname, size_t *return_size)
+{
+ ssize_t size;
+ char xattr_list[PATH_MAX];
+ size = listxattr(realname, xattr_list, sizeof(xattr_list));
+ if (size < 0) {
+ return NULL;
+ }
+ char xattr[T_BLOCKSIZE];
+ char *xattr_ptr;
+ int first = 1;
+ *return_size = 0;
+ for (int i = 0; i < size; i++) {
+ if (xattr_list[i]) {
+ xattr_ptr = xattr_list + i;
+ if (strncmp(xattr_ptr, ANDROID_USER_INODE_XATTR_PREFIX, ANDROID_USER_INODE_XATTR_PREFIX_LEN) == 0) {
+ // found a user.inode xattr
+ if (first) {
+ first = 0;
+ strcpy(xattr, xattr_ptr);
+ *return_size = strlen(xattr_ptr);
+ } else {
+ char *ptr = xattr + *return_size;
+ snprintf(ptr, T_BLOCKSIZE - *return_size, "%s", xattr_ptr);
+ *return_size += strlen(xattr_ptr) + 1; // + 1 for null separator
+ if (*return_size >= T_BLOCKSIZE) {
+ *return_size = 0;
+ return NULL;
+ }
+ }
+ }
+ i += strlen(xattr_ptr);
+ }
+ }
+ if (first)
+ return NULL;
+ return strdup(xattr);
+}*/
+
+/*
+ * get_path_inode and write_path_inode were taken from frameworks/native/cmds/installd/utils.cpp
+ */
+
+static int get_path_inode(const char* path, ino_t *inode) {
+ struct stat buf;
+ memset(&buf, 0, sizeof(buf));
+ if (stat(path, &buf) != 0) {
+ printf("failed to stat %s\n", path);
+ return -1;
+ }
+ *inode = buf.st_ino;
+ return 0;
+}
+
+/**
+ * Write the inode of a specific child file into the given xattr on the
+ * parent directory. This allows you to find the child later, even if its
+ * name is encrypted.
+ */
+int write_path_inode(const char* parent, const char* name, const char* inode_xattr) {
+ ino_t inode = 0;
+ uint64_t inode_raw = 0;
+ char path[PATH_MAX];
+ snprintf(path, PATH_MAX, "%s/%s", parent, name);
+
+ if (mkdirhier(path) == -1) {
+ printf("failed to mkdirhier for %s\n", path);
+ return -1;
+ }
+
+ if (get_path_inode(path, &inode) != 0) {
+ return -1;
+ }
+
+ // Check to see if already set correctly
+ if (getxattr(parent, inode_xattr, &inode_raw, sizeof(inode_raw)) == sizeof(inode_raw)) {
+ if (inode_raw == inode) {
+ // Already set correctly; skip writing
+ return 0;
+ }
+ }
+
+ inode_raw = inode;
+ printf("setting %s on %s pointing to %s\n", inode_xattr, parent, path);
+ if (setxattr(parent, inode_xattr, &inode_raw, sizeof(inode_raw), 0) != 0 && errno != EOPNOTSUPP) {
+ printf("Failed to write xattr %s at %s (%s)\n", inode_xattr, parent, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
diff --git a/libtar/android_utils.h b/libtar/android_utils.h
new file mode 100644
index 000000000..72cb9285b
--- /dev/null
+++ b/libtar/android_utils.h
@@ -0,0 +1,22 @@
+/*
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef LIBTAR_ANDROID_UTILS_H
+#define LIBTAR_ANDROID_UTILS_H
+
+int write_path_inode(const char* parent, const char* name, const char* inode_xattr);
+
+#endif
diff --git a/libtar/append.c b/libtar/append.c
new file mode 100644
index 000000000..8f09de262
--- /dev/null
+++ b/libtar/append.c
@@ -0,0 +1,439 @@
+/*
+** Copyright 1998-2003 University of Illinois Board of Trustees
+** Copyright 1998-2003 Mark D. Roth
+** All rights reserved.
+**
+** append.c - libtar code to append files to a tar archive
+**
+** Mark D. Roth <roth@uiuc.edu>
+** Campus Information Technologies and Educational Services
+** University of Illinois at Urbana-Champaign
+*/
+
+#include <internal.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <stdbool.h>
+
+#include <sys/capability.h>
+#include <sys/xattr.h>
+#include <linux/xattr.h>
+
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <string.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include <selinux/selinux.h>
+
+#ifdef HAVE_EXT4_CRYPT
+# include "ext4crypt_tar.h"
+#endif
+#include "android_utils.h"
+
+struct tar_dev
+{
+ dev_t td_dev;
+ libtar_hash_t *td_h;
+};
+typedef struct tar_dev tar_dev_t;
+
+struct tar_ino
+{
+ ino_t ti_ino;
+ char ti_name[MAXPATHLEN];
+};
+typedef struct tar_ino tar_ino_t;
+
+
+/* free memory associated with a tar_dev_t */
+void
+tar_dev_free(tar_dev_t *tdp)
+{
+ libtar_hash_free(tdp->td_h, free);
+ free(tdp);
+}
+
+
+/* appends a file to the tar archive */
+int
+tar_append_file(TAR *t, const char *realname, const char *savename)
+{
+ struct stat s;
+ int i;
+ libtar_hashptr_t hp;
+ tar_dev_t *td = NULL;
+ tar_ino_t *ti = NULL;
+ char path[MAXPATHLEN];
+
+#ifdef DEBUG
+ printf("==> tar_append_file(TAR=0x%lx (\"%s\"), realname=\"%s\", "
+ "savename=\"%s\")\n", t, t->pathname, realname,
+ (savename ? savename : "[NULL]"));
+#endif
+
+ if (lstat(realname, &s) != 0)
+ {
+#ifdef DEBUG
+ perror("lstat()");
+#endif
+ return -1;
+ }
+
+ /* set header block */
+#ifdef DEBUG
+ puts("tar_append_file(): setting header block...");
+#endif
+ memset(&(t->th_buf), 0, sizeof(struct tar_header));
+ th_set_from_stat(t, &s);
+
+ /* set the header path */
+#ifdef DEBUG
+ puts("tar_append_file(): setting header path...");
+#endif
+ th_set_path(t, (savename ? savename : realname));
+
+ /* get selinux context */
+ if (t->options & TAR_STORE_SELINUX)
+ {
+ if (t->th_buf.selinux_context != NULL)
+ {
+ free(t->th_buf.selinux_context);
+ t->th_buf.selinux_context = NULL;
+ }
+
+ security_context_t selinux_context = NULL;
+ if (lgetfilecon(realname, &selinux_context) >= 0)
+ {
+ t->th_buf.selinux_context = strdup(selinux_context);
+ printf(" ==> set selinux context: %s\n", selinux_context);
+ freecon(selinux_context);
+ }
+ else
+ {
+#ifdef DEBUG
+ perror("Failed to get selinux context");
+#endif
+ }
+ }
+
+#ifdef HAVE_EXT4_CRYPT
+ if (TH_ISDIR(t) && t->options & TAR_STORE_EXT4_POL)
+ {
+ if (t->th_buf.eep != NULL)
+ {
+ free(t->th_buf.eep);
+ t->th_buf.eep = NULL;
+ }
+
+ t->th_buf.eep = (struct ext4_encryption_policy*)malloc(sizeof(struct ext4_encryption_policy));
+ if (!t->th_buf.eep) {
+ printf("malloc ext4_encryption_policy\n");
+ return -1;
+ }
+ if (e4crypt_policy_get_struct(realname, t->th_buf.eep))
+ {
+ char tar_policy[EXT4_KEY_DESCRIPTOR_SIZE];
+ memset(tar_policy, 0, sizeof(tar_policy));
+ char policy_hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX];
+ policy_to_hex(t->th_buf.eep->master_key_descriptor, policy_hex);
+ if (lookup_ref_key(t->th_buf.eep->master_key_descriptor, &tar_policy[0])) {
+ printf("found policy '%s' - '%s' - '%s'\n", realname, tar_policy, policy_hex);
+ memcpy(t->th_buf.eep->master_key_descriptor, tar_policy, EXT4_KEY_DESCRIPTOR_SIZE);
+ } else {
+ printf("failed to lookup tar policy for '%s' - '%s'\n", realname, policy_hex);
+ free(t->th_buf.eep);
+ t->th_buf.eep = NULL;
+ return -1;
+ }
+ }
+ else
+ {
+ // no policy found, but this is not an error as not all dirs will have a policy
+ free(t->th_buf.eep);
+ t->th_buf.eep = NULL;
+ }
+ }
+#endif
+
+ /* get posix file capabilities */
+ if (TH_ISREG(t) && t->options & TAR_STORE_POSIX_CAP)
+ {
+ if (t->th_buf.has_cap_data)
+ {
+ memset(&t->th_buf.cap_data, 0, sizeof(struct vfs_cap_data));
+ t->th_buf.has_cap_data = 0;
+ }
+
+ if (getxattr(realname, XATTR_NAME_CAPS, &t->th_buf.cap_data, sizeof(struct vfs_cap_data)) >= 0)
+ {
+ t->th_buf.has_cap_data = 1;
+#if 1 //def DEBUG
+ print_caps(&t->th_buf.cap_data);
+#endif
+ }
+ }
+
+ /* get android user.default xattr */
+ if (TH_ISDIR(t) && t->options & TAR_STORE_ANDROID_USER_XATTR)
+ {
+ if (getxattr(realname, "user.default", NULL, 0) >= 0)
+ {
+ t->th_buf.has_user_default = 1;
+#if 1 //def DEBUG
+ printf("storing xattr user.default\n");
+#endif
+ }
+ if (getxattr(realname, "user.inode_cache", NULL, 0) >= 0)
+ {
+ t->th_buf.has_user_cache = 1;
+#if 1 //def DEBUG
+ printf("storing xattr user.inode_cache\n");
+#endif
+ }
+ if (getxattr(realname, "user.inode_code_cache", NULL, 0) >= 0)
+ {
+ t->th_buf.has_user_code_cache = 1;
+#if 1 //def DEBUG
+ printf("storing xattr user.inode_code_cache\n");
+#endif
+ }
+ }
+
+ /* check if it's a hardlink */
+#ifdef DEBUG
+ puts("tar_append_file(): checking inode cache for hardlink...");
+#endif
+ libtar_hashptr_reset(&hp);
+ if (libtar_hash_getkey(t->h, &hp, &(s.st_dev),
+ (libtar_matchfunc_t)dev_match) != 0)
+ td = (tar_dev_t *)libtar_hashptr_data(&hp);
+ else
+ {
+#ifdef DEBUG
+ printf("+++ adding hash for device (0x%lx, 0x%lx)...\n",
+ major(s.st_dev), minor(s.st_dev));
+#endif
+ td = (tar_dev_t *)calloc(1, sizeof(tar_dev_t));
+ td->td_dev = s.st_dev;
+ td->td_h = libtar_hash_new(256, (libtar_hashfunc_t)ino_hash);
+ if (td->td_h == NULL)
+ return -1;
+ if (libtar_hash_add(t->h, td) == -1)
+ return -1;
+ }
+ libtar_hashptr_reset(&hp);
+ if (libtar_hash_getkey(td->td_h, &hp, &(s.st_ino),
+ (libtar_matchfunc_t)ino_match) != 0)
+ {
+ ti = (tar_ino_t *)libtar_hashptr_data(&hp);
+#ifdef DEBUG
+ printf(" tar_append_file(): encoding hard link \"%s\" "
+ "to \"%s\"...\n", realname, ti->ti_name);
+#endif
+ t->th_buf.typeflag = LNKTYPE;
+ th_set_link(t, ti->ti_name);
+ }
+ else
+ {
+#ifdef DEBUG
+ printf("+++ adding entry: device (0x%lx,0x%lx), inode %ld "
+ "(\"%s\")...\n", major(s.st_dev), minor(s.st_dev),
+ s.st_ino, realname);
+#endif
+ ti = (tar_ino_t *)calloc(1, sizeof(tar_ino_t));
+ if (ti == NULL)
+ return -1;
+ ti->ti_ino = s.st_ino;
+ snprintf(ti->ti_name, sizeof(ti->ti_name), "%s",
+ savename ? savename : realname);
+ libtar_hash_add(td->td_h, ti);
+ }
+
+ /* check if it's a symlink */
+ if (TH_ISSYM(t))
+ {
+ i = readlink(realname, path, sizeof(path));
+ if (i == -1)
+ return -1;
+ if (i >= MAXPATHLEN)
+ i = MAXPATHLEN - 1;
+ path[i] = '\0';
+#ifdef DEBUG
+ printf("tar_append_file(): encoding symlink \"%s\" -> "
+ "\"%s\"...\n", realname, path);
+#endif
+ th_set_link(t, path);
+ }
+
+ /* print file info */
+ if (t->options & TAR_VERBOSE)
+ printf("%s\n", th_get_pathname(t));
+
+#ifdef DEBUG
+ puts("tar_append_file(): writing header");
+#endif
+ /* write header */
+ if (th_write(t) != 0)
+ {
+#ifdef DEBUG
+ printf("t->fd = %d\n", t->fd);
+#endif
+ return -1;
+ }
+#ifdef DEBUG
+ puts("tar_append_file(): back from th_write()");
+#endif
+
+ /* if it's a regular file, write the contents as well */
+ if (TH_ISREG(t) && tar_append_regfile(t, realname) != 0)
+ return -1;
+
+ return 0;
+}
+
+
+/* write EOF indicator */
+int
+tar_append_eof(TAR *t)
+{
+ int i, j;
+ char block[T_BLOCKSIZE];
+
+ memset(&block, 0, T_BLOCKSIZE);
+ for (j = 0; j < 2; j++)
+ {
+ i = tar_block_write(t, &block);
+ if (i != T_BLOCKSIZE)
+ {
+ if (i != -1)
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+/* add file contents to a tarchive */
+int
+tar_append_regfile(TAR *t, const char *realname)
+{
+ char block[T_BLOCKSIZE];
+ int filefd;
+ int64_t i, size;
+ ssize_t j;
+ int rv = -1;
+
+#if defined(O_BINARY)
+ filefd = open(realname, O_RDONLY|O_BINARY);
+#else
+ filefd = open(realname, O_RDONLY);
+#endif
+ if (filefd == -1)
+ {
+#ifdef DEBUG
+ perror("open()");
+#endif
+ return -1;
+ }
+
+ size = th_get_size(t);
+ for (i = size; i > T_BLOCKSIZE; i -= T_BLOCKSIZE)
+ {
+ j = read(filefd, &block, T_BLOCKSIZE);
+ if (j != T_BLOCKSIZE)
+ {
+ if (j != -1)
+ errno = EINVAL;
+ goto fail;
+ }
+ if (tar_block_write(t, &block) == -1)
+ goto fail;
+ }
+
+ if (i > 0)
+ {
+ j = read(filefd, &block, i);
+ if (j == -1)
+ goto fail;
+ memset(&(block[i]), 0, T_BLOCKSIZE - i);
+ if (tar_block_write(t, &block) == -1)
+ goto fail;
+ }
+
+ /* success! */
+ rv = 0;
+fail:
+ close(filefd);
+
+ return rv;
+}
+
+
+/* add file contents to a tarchive */
+int
+tar_append_file_contents(TAR *t, const char *savename, mode_t mode,
+ uid_t uid, gid_t gid, void *buf, size_t len)
+{
+ struct stat st;
+
+ memset(&st, 0, sizeof(st));
+ st.st_mode = S_IFREG | mode;
+ st.st_uid = uid;
+ st.st_gid = gid;
+ st.st_mtime = time(NULL);
+ st.st_size = len;
+
+ th_set_from_stat(t, &st);
+ th_set_path(t, savename);
+
+ /* write header */
+ if (th_write(t) != 0)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "tar_append_file_contents(): could not write header, t->fd = %d\n", t->fd);
+#endif
+ return -1;
+ }
+
+ return tar_append_buffer(t, buf, len);
+}
+
+int
+tar_append_buffer(TAR *t, void *buf, size_t len)
+{
+ char block[T_BLOCKSIZE];
+ int i;
+ size_t size = len;
+
+ for (i = size; i > T_BLOCKSIZE; i -= T_BLOCKSIZE)
+ {
+ if (tar_block_write(t, buf) == -1)
+ return -1;
+ buf = (char *)buf + T_BLOCKSIZE;
+ }
+
+ if (i > 0)
+ {
+ memcpy(block, buf, i);
+ memset(&(block[i]), 0, T_BLOCKSIZE - i);
+ if (tar_block_write(t, &block) == -1)
+ return -1;
+ }
+
+ return 0;
+}
+
diff --git a/libtar/basename.c b/libtar/basename.c
new file mode 100644
index 000000000..3f4d31584
--- /dev/null
+++ b/libtar/basename.c
@@ -0,0 +1,74 @@
+/* $OpenBSD: basename.c,v 1.4 1999/05/30 17:10:30 espie Exp $ */
+
+/*
+ * Copyright (c) 1997 Todd C. Miller <Todd.Miller@courtesan.com>
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+//static char rcsid[] = "$OpenBSD: basename.c,v 1.4 1999/05/30 17:10:30 espie Exp $";
+#endif /* not lint */
+
+#include <errno.h>
+#include <string.h>
+#include <sys/param.h>
+
+char *
+openbsd_basename(path)
+ const char *path;
+{
+ static char bname[MAXPATHLEN];
+ register const char *endp, *startp;
+
+ /* Empty or NULL string gets treated as "." */
+ if (path == NULL || *path == '\0') {
+ (void)strcpy(bname, ".");
+ return(bname);
+ }
+
+ /* Strip trailing slashes */
+ endp = path + strlen(path) - 1;
+ while (endp > path && *endp == '/')
+ endp--;
+
+ /* All slashes becomes "/" */
+ if (endp == path && *endp == '/') {
+ (void)strcpy(bname, "/");
+ return(bname);
+ }
+
+ /* Find the start of the base */
+ startp = endp;
+ while (startp > path && *(startp - 1) != '/')
+ startp--;
+
+ if (endp - startp + 1 > (int)sizeof(bname)) {
+ errno = ENAMETOOLONG;
+ return(NULL);
+ }
+ (void)strncpy(bname, startp, endp - startp + 1);
+ bname[endp - startp + 1] = '\0';
+ return(bname);
+}
diff --git a/libtar/block.c b/libtar/block.c
new file mode 100644
index 000000000..834c164ad
--- /dev/null
+++ b/libtar/block.c
@@ -0,0 +1,735 @@
+/*
+** Copyright 1998-2003 University of Illinois Board of Trustees
+** Copyright 1998-2003 Mark D. Roth
+** All rights reserved.
+**
+** block.c - libtar code to handle tar archive header blocks
+**
+** Mark D. Roth <roth@uiuc.edu>
+** Campus Information Technologies and Educational Services
+** University of Illinois at Urbana-Champaign
+*/
+
+#include <internal.h>
+#include <errno.h>
+
+#ifdef STDC_HEADERS
+# include <string.h>
+# include <stdlib.h>
+#endif
+
+#ifdef HAVE_EXT4_CRYPT
+# include "ext4crypt_tar.h"
+#endif
+
+#define BIT_ISSET(bitmask, bit) ((bitmask) & (bit))
+
+// Used to identify selinux_context in extended ('x')
+// metadata. From RedHat implementation.
+#define SELINUX_TAG "RHT.security.selinux="
+#define SELINUX_TAG_LEN strlen(SELINUX_TAG)
+
+// Used to identify e4crypt_policy in extended ('x')
+#define E4CRYPT_TAG "TWRP.security.e4crypt="
+#define E4CRYPT_TAG_LEN strlen(E4CRYPT_TAG)
+
+// Used to identify Posix capabilities in extended ('x')
+#define CAPABILITIES_TAG "SCHILY.xattr.security.capability="
+#define CAPABILITIES_TAG_LEN strlen(CAPABILITIES_TAG)
+
+// Used to identify Android user.default xattr in extended ('x')
+#define ANDROID_USER_DEFAULT_TAG "ANDROID.user.default"
+#define ANDROID_USER_DEFAULT_TAG_LEN strlen(ANDROID_USER_DEFAULT_TAG)
+
+// Used to identify Android user.inode_cache xattr in extended ('x')
+#define ANDROID_USER_CACHE_TAG "ANDROID.user.inode_cache"
+#define ANDROID_USER_CACHE_TAG_LEN strlen(ANDROID_USER_CACHE_TAG)
+
+// Used to identify Android user.inode_code_cache xattr in extended ('x')
+#define ANDROID_USER_CODE_CACHE_TAG "ANDROID.user.inode_code_cache"
+#define ANDROID_USER_CODE_CACHE_TAG_LEN strlen(ANDROID_USER_CODE_CACHE_TAG)
+
+/* read a header block */
+/* FIXME: the return value of this function should match the return value
+ of tar_block_read(), which is a macro which references a prototype
+ that returns a ssize_t. So far, this is safe, since tar_block_read()
+ only ever reads 512 (T_BLOCKSIZE) bytes at a time, so any difference
+ in size of ssize_t and int is of negligible risk. BUT, if
+ T_BLOCKSIZE ever changes, or ever becomes a variable parameter
+ controllable by the user, all the code that calls it,
+ including this function and all code that calls it, should be
+ fixed for security reasons.
+ Thanks to Chris Palmer for the critique.
+*/
+int
+th_read_internal(TAR *t)
+{
+ int i;
+ int num_zero_blocks = 0;
+
+#ifdef DEBUG
+ printf("==> th_read_internal(TAR=\"%s\")\n", t->pathname);
+#endif
+
+ while ((i = tar_block_read(t, &(t->th_buf))) == T_BLOCKSIZE)
+ {
+ /* two all-zero blocks mark EOF */
+ if (t->th_buf.name[0] == '\0')
+ {
+ num_zero_blocks++;
+ if (!BIT_ISSET(t->options, TAR_IGNORE_EOT)
+ && num_zero_blocks >= 2)
+ return 0; /* EOF */
+ else
+ continue;
+ }
+
+ /* verify magic and version */
+ if (BIT_ISSET(t->options, TAR_CHECK_MAGIC)
+ && strncmp(t->th_buf.magic, TMAGIC, TMAGLEN - 1) != 0)
+ {
+#ifdef DEBUG
+ puts("!!! unknown magic value in tar header");
+#endif
+ return -2;
+ }
+
+ if (BIT_ISSET(t->options, TAR_CHECK_VERSION)
+ && strncmp(t->th_buf.version, TVERSION, TVERSLEN) != 0)
+ {
+#ifdef DEBUG
+ puts("!!! unknown version value in tar header");
+#endif
+ return -2;
+ }
+
+ /* check chksum */
+ if (!BIT_ISSET(t->options, TAR_IGNORE_CRC)
+ && !th_crc_ok(t))
+ {
+#ifdef DEBUG
+ puts("!!! tar header checksum error");
+#endif
+ return -2;
+ }
+
+ break;
+ }
+
+#ifdef DEBUG
+ printf("<== th_read_internal(): returning %d\n", i);
+#endif
+ return i;
+}
+
+
+/* wrapper function for th_read_internal() to handle GNU extensions */
+int
+th_read(TAR *t)
+{
+ int i;
+ size_t sz, j, blocks;
+ char *ptr;
+
+#ifdef DEBUG
+ printf("==> th_read(t=0x%lx)\n", t);
+#endif
+
+ if (t->th_buf.gnu_longname != NULL)
+ free(t->th_buf.gnu_longname);
+ if (t->th_buf.gnu_longlink != NULL)
+ free(t->th_buf.gnu_longlink);
+ if (t->th_buf.selinux_context != NULL)
+ free(t->th_buf.selinux_context);
+#ifdef HAVE_EXT4_CRYPT
+ if (t->th_buf.eep != NULL)
+ free(t->th_buf.eep);
+#endif
+ if (t->th_buf.has_cap_data)
+ {
+ memset(&t->th_buf.cap_data, 0, sizeof(struct vfs_cap_data));
+ t->th_buf.has_cap_data = 0;
+ }
+ t->th_buf.has_user_default = 0;
+ t->th_buf.has_user_cache = 0;
+ t->th_buf.has_user_code_cache = 0;
+
+ memset(&(t->th_buf), 0, sizeof(struct tar_header));
+
+ i = th_read_internal(t);
+ if (i == 0)
+ return 1;
+ else if (i != T_BLOCKSIZE)
+ {
+ if (i != -1)
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* check for GNU long link extention */
+ if (TH_ISLONGLINK(t))
+ {
+ sz = th_get_size(t);
+ blocks = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0);
+ if (blocks > ((size_t)-1 / T_BLOCKSIZE))
+ {
+ errno = E2BIG;
+ return -1;
+ }
+#ifdef DEBUG
+ printf(" th_read(): GNU long linkname detected "
+ "(%ld bytes, %d blocks)\n", sz, blocks);
+#endif
+ t->th_buf.gnu_longlink = (char *)malloc(blocks * T_BLOCKSIZE);
+ if (t->th_buf.gnu_longlink == NULL)
+ return -1;
+
+ for (j = 0, ptr = t->th_buf.gnu_longlink; j < blocks;
+ j++, ptr += T_BLOCKSIZE)
+ {
+#ifdef DEBUG
+ printf(" th_read(): reading long linkname "
+ "(%d blocks left, ptr == %ld)\n", blocks-j, ptr);
+#endif
+ i = tar_block_read(t, ptr);
+ if (i != T_BLOCKSIZE)
+ {
+ if (i != -1)
+ errno = EINVAL;
+ return -1;
+ }
+#ifdef DEBUG
+ printf(" th_read(): read block == \"%s\"\n", ptr);
+#endif
+ }
+#ifdef DEBUG
+ printf(" th_read(): t->th_buf.gnu_longlink == \"%s\"\n",
+ t->th_buf.gnu_longlink);
+#endif
+
+ i = th_read_internal(t);
+ if (i != T_BLOCKSIZE)
+ {
+ if (i != -1)
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ /* check for GNU long name extention */
+ if (TH_ISLONGNAME(t))
+ {
+ sz = th_get_size(t);
+ blocks = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0);
+ if (blocks > ((size_t)-1 / T_BLOCKSIZE))
+ {
+ errno = E2BIG;
+ return -1;
+ }
+#ifdef DEBUG
+ printf(" th_read(): GNU long filename detected "
+ "(%ld bytes, %d blocks)\n", sz, blocks);
+#endif
+ t->th_buf.gnu_longname = (char *)malloc(blocks * T_BLOCKSIZE);
+ if (t->th_buf.gnu_longname == NULL)
+ return -1;
+
+ for (j = 0, ptr = t->th_buf.gnu_longname; j < blocks;
+ j++, ptr += T_BLOCKSIZE)
+ {
+#ifdef DEBUG
+ printf(" th_read(): reading long filename "
+ "(%d blocks left, ptr == %ld)\n", blocks-j, ptr);
+#endif
+ i = tar_block_read(t, ptr);
+ if (i != T_BLOCKSIZE)
+ {
+ if (i != -1)
+ errno = EINVAL;
+ return -1;
+ }
+#ifdef DEBUG
+ printf(" th_read(): read block == \"%s\"\n", ptr);
+#endif
+ }
+#ifdef DEBUG
+ printf(" th_read(): t->th_buf.gnu_longname == \"%s\"\n",
+ t->th_buf.gnu_longname);
+#endif
+
+ i = th_read_internal(t);
+ if (i != T_BLOCKSIZE)
+ {
+ if (i != -1)
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ // Extended headers (selinux contexts, posix file capabilities, ext4 encryption policies)
+ while(TH_ISEXTHEADER(t) || TH_ISPOLHEADER(t))
+ {
+ sz = th_get_size(t);
+
+ if(sz >= T_BLOCKSIZE) // Not supported
+ {
+#ifdef DEBUG
+ printf(" th_read(): Extended header is too long!\n");
+#endif
+ }
+ else
+ {
+ char buf[T_BLOCKSIZE];
+ i = tar_block_read(t, buf);
+ if (i != T_BLOCKSIZE)
+ {
+ if (i != -1)
+ errno = EINVAL;
+ return -1;
+ }
+
+ // To be sure
+ buf[T_BLOCKSIZE-1] = 0;
+
+ int len = strlen(buf);
+ // posix capabilities
+ char *start = strstr(buf, CAPABILITIES_TAG);
+ if (start && start+CAPABILITIES_TAG_LEN < buf+len)
+ {
+ start += CAPABILITIES_TAG_LEN;
+ memcpy(&t->th_buf.cap_data, start, sizeof(struct vfs_cap_data));
+ t->th_buf.has_cap_data = 1;
+#ifdef DEBUG
+ printf(" th_read(): Posix capabilities detected\n");
+#endif
+ } // end posix capabilities
+ // selinux contexts
+ start = strstr(buf, SELINUX_TAG);
+ if (start && start+SELINUX_TAG_LEN < buf+len)
+ {
+ start += SELINUX_TAG_LEN;
+ char *end = strchr(start, '\n');
+ if(end)
+ {
+ t->th_buf.selinux_context = strndup(start, end-start);
+#ifdef DEBUG
+ printf(" th_read(): SELinux context xattr detected: %s\n", t->th_buf.selinux_context);
+#endif
+ }
+ } // end selinux contexts
+ // android user.default xattr
+ start = strstr(buf, ANDROID_USER_DEFAULT_TAG);
+ if (start)
+ {
+ t->th_buf.has_user_default = 1;
+#ifdef DEBUG
+ printf(" th_read(): android user.default xattr detected\n");
+#endif
+ } // end android user.default xattr
+ // android user.inode_cache xattr
+ start = strstr(buf, ANDROID_USER_CACHE_TAG);
+ if (start)
+ {
+ t->th_buf.has_user_cache = 1;
+#ifdef DEBUG
+ printf(" th_read(): android user.inode_cache xattr detected\n");
+#endif
+ } // end android user.inode_cache xattr
+ // android user.inode_code_cache xattr
+ start = strstr(buf, ANDROID_USER_CODE_CACHE_TAG);
+ if (start)
+ {
+ t->th_buf.has_user_code_cache = 1;
+#ifdef DEBUG
+ printf(" th_read(): android user.inode_code_cache xattr detected\n");
+#endif
+ } // end android user.inode_code_cache xattr
+#ifdef HAVE_EXT4_CRYPT
+ start = strstr(buf, E4CRYPT_TAG);
+ if (start && start+E4CRYPT_TAG_LEN < buf+len)
+ {
+ t->th_buf.eep = (struct ext4_encryption_policy*)malloc(sizeof(struct ext4_encryption_policy));
+ if (!t->th_buf.eep) {
+ printf("malloc ext4_encryption_policy\n");
+ return -1;
+ }
+ start += E4CRYPT_TAG_LEN;
+ if (*start == '2')
+ {
+ start++;
+ char *newline_check = start + sizeof(struct ext4_encryption_policy);
+ if (*newline_check != '\n')
+ printf("did not find newline char in expected location, continuing anyway...\n");
+ memcpy(t->th_buf.eep, start, sizeof(struct ext4_encryption_policy));
+#ifdef DEBUG
+ printf(" th_read(): E4Crypt policy v2 detected: %i %i %i %i %s\n",
+ (int)t->th_buf.eep->version,
+ (int)t->th_buf.eep->contents_encryption_mode,
+ (int)t->th_buf.eep->filenames_encryption_mode,
+ (int)t->th_buf.eep->flags,
+ t->th_buf.eep->master_key_descriptor);
+#endif
+ }
+ else
+ {
+ e4crypt_policy_fill_default_struct(t->th_buf.eep);
+ char *end = strchr(start, '\n');
+ if(!end)
+ end = strchr(start, '\0');
+ if(end)
+ {
+ strncpy(t->th_buf.eep->master_key_descriptor, start, end-start);
+#ifdef DEBUG
+ printf(" th_read(): E4Crypt policy v1 detected: %s\n", t->th_buf.eep->master_key_descriptor);
+#endif
+ }
+ }
+ }
+#endif // HAVE_EXT4_CRYPT
+ }
+
+ i = th_read_internal(t);
+ if (i != T_BLOCKSIZE)
+ {
+ if (i != -1)
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/* write an extended block */
+static int
+th_write_extended(TAR *t, char* buf, uint64_t sz)
+{
+ char type2;
+ uint64_t sz2;
+ int i;
+
+ /* save old size and type */
+ type2 = t->th_buf.typeflag;
+ sz2 = th_get_size(t);
+
+ /* write out initial header block with fake size and type */
+ t->th_buf.typeflag = TH_EXT_TYPE;
+
+ if(sz >= T_BLOCKSIZE) // impossible
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ th_set_size(t, sz);
+ th_finish(t);
+ i = tar_block_write(t, &(t->th_buf));
+ if (i != T_BLOCKSIZE)
+ {
+ if (i != -1)
+ errno = EINVAL;
+ return -1;
+ }
+
+ i = tar_block_write(t, buf);
+ if (i != T_BLOCKSIZE)
+ {
+ if (i != -1)
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* reset type and size to original values */
+ t->th_buf.typeflag = type2;
+ th_set_size(t, sz2);
+ memset(buf, 0, T_BLOCKSIZE);
+ return 0;
+}
+
+/* write a header block */
+int
+th_write(TAR *t)
+{
+ int i, j;
+ char type2;
+ uint64_t sz, sz2, total_sz = 0;
+ char *ptr;
+ char buf[T_BLOCKSIZE];
+
+#ifdef DEBUG
+ printf("==> th_write(TAR=\"%s\")\n", t->pathname);
+ th_print(t);
+#endif
+
+ if ((t->options & TAR_GNU) && t->th_buf.gnu_longlink != NULL)
+ {
+#ifdef DEBUG
+ printf("th_write(): using gnu_longlink (\"%s\")\n",
+ t->th_buf.gnu_longlink);
+#endif
+ /* save old size and type */
+ type2 = t->th_buf.typeflag;
+ sz2 = th_get_size(t);
+
+ /* write out initial header block with fake size and type */
+ t->th_buf.typeflag = GNU_LONGLINK_TYPE;
+ sz = strlen(t->th_buf.gnu_longlink);
+ th_set_size(t, sz);
+ th_finish(t);
+ i = tar_block_write(t, &(t->th_buf));
+ if (i != T_BLOCKSIZE)
+ {
+ if (i != -1)
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* write out extra blocks containing long name */
+ for (j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0),
+ ptr = t->th_buf.gnu_longlink; j > 1;
+ j--, ptr += T_BLOCKSIZE)
+ {
+ i = tar_block_write(t, ptr);
+ if (i != T_BLOCKSIZE)
+ {
+ if (i != -1)
+ errno = EINVAL;
+ return -1;
+ }
+ }
+ memset(buf, 0, T_BLOCKSIZE);
+ strncpy(buf, ptr, T_BLOCKSIZE);
+ i = tar_block_write(t, &buf);
+ if (i != T_BLOCKSIZE)
+ {
+ if (i != -1)
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* reset type and size to original values */
+ t->th_buf.typeflag = type2;
+ th_set_size(t, sz2);
+ }
+
+ if ((t->options & TAR_GNU) && t->th_buf.gnu_longname != NULL)
+ {
+#ifdef DEBUG
+ printf("th_write(): using gnu_longname (\"%s\")\n",
+ t->th_buf.gnu_longname);
+#endif
+ /* save old size and type */
+ type2 = t->th_buf.typeflag;
+ sz2 = th_get_size(t);
+
+ /* write out initial header block with fake size and type */
+ t->th_buf.typeflag = GNU_LONGNAME_TYPE;
+ sz = strlen(t->th_buf.gnu_longname);
+ th_set_size(t, sz);
+ th_finish(t);
+ i = tar_block_write(t, &(t->th_buf));
+ if (i != T_BLOCKSIZE)
+ {
+ if (i != -1)
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* write out extra blocks containing long name */
+ for (j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0),
+ ptr = t->th_buf.gnu_longname; j > 1;
+ j--, ptr += T_BLOCKSIZE)
+ {
+ i = tar_block_write(t, ptr);
+ if (i != T_BLOCKSIZE)
+ {
+ if (i != -1)
+ errno = EINVAL;
+ return -1;
+ }
+ }
+ memset(buf, 0, T_BLOCKSIZE);
+ strncpy(buf, ptr, T_BLOCKSIZE);
+ i = tar_block_write(t, &buf);
+ if (i != T_BLOCKSIZE)
+ {
+ if (i != -1)
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* reset type and size to original values */
+ t->th_buf.typeflag = type2;
+ th_set_size(t, sz2);
+ }
+
+ memset(buf, 0, T_BLOCKSIZE);
+ ptr = buf;
+
+ if((t->options & TAR_STORE_SELINUX) && t->th_buf.selinux_context != NULL)
+ {
+#ifdef DEBUG
+ printf("th_write(): using selinux_context (\"%s\")\n",
+ t->th_buf.selinux_context);
+#endif
+ /* setup size - EXT header has format "*size of this whole tag as ascii numbers* *space* *content* *newline* */
+ // size newline
+ sz = SELINUX_TAG_LEN + strlen(t->th_buf.selinux_context) + 3 + 1;
+
+ if(sz >= 100) // another ascci digit for size
+ ++sz;
+
+ total_sz += sz;
+ snprintf(ptr, T_BLOCKSIZE, "%d "SELINUX_TAG"%s\n", (int)sz, t->th_buf.selinux_context);
+ ptr += sz;
+ }
+
+#ifdef HAVE_EXT4_CRYPT
+ if((t->options & TAR_STORE_EXT4_POL) && t->th_buf.eep != NULL)
+ {
+#ifdef DEBUG
+ printf("th_write(): using e4crypt_policy %s\n",
+ t->th_buf.eep->master_key_descriptor);
+#endif
+ /* setup size - EXT header has format "*size of this whole tag as ascii numbers* *space* *version code* *content* *newline* */
+ // size newline
+ sz = E4CRYPT_TAG_LEN + sizeof(struct ext4_encryption_policy) + 1 + 3 + 1;
+
+ if(sz >= 100) // another ascci digit for size
+ ++sz;
+
+ if (total_sz + sz >= T_BLOCKSIZE)
+ {
+ if (th_write_extended(t, &buf[0], total_sz))
+ return -1;
+ ptr = buf;
+ total_sz = sz;
+ }
+ else
+ total_sz += sz;
+
+ snprintf(ptr, T_BLOCKSIZE, "%d "E4CRYPT_TAG"2", (int)sz);
+ memcpy(ptr + sz - sizeof(struct ext4_encryption_policy) - 1, t->th_buf.eep, sizeof(struct ext4_encryption_policy));
+ char *nlptr = ptr + sz - 1;
+ *nlptr = '\n';
+ ptr += sz;
+ }
+#endif
+
+ if((t->options & TAR_STORE_POSIX_CAP) && t->th_buf.has_cap_data)
+ {
+#ifdef DEBUG
+ printf("th_write(): has a posix capability\n");
+#endif
+ sz = CAPABILITIES_TAG_LEN + sizeof(struct vfs_cap_data) + 3 + 1;
+
+ if(sz >= 100) // another ascci digit for size
+ ++sz;
+
+ if (total_sz + sz >= T_BLOCKSIZE)
+ {
+ if (th_write_extended(t, &buf[0], total_sz))
+ return -1;
+ ptr = buf;
+ total_sz = sz;
+ }
+ else
+ total_sz += sz;
+
+ snprintf(ptr, T_BLOCKSIZE, "%d "CAPABILITIES_TAG, (int)sz);
+ memcpy(ptr + CAPABILITIES_TAG_LEN + 3, &t->th_buf.cap_data, sizeof(struct vfs_cap_data));
+ char *nlptr = ptr + sz - 1;
+ *nlptr = '\n';
+ ptr += sz;
+ }
+ if (t->options & TAR_STORE_ANDROID_USER_XATTR)
+ {
+ if (t->th_buf.has_user_default) {
+#ifdef DEBUG
+ printf("th_write(): has android user.default xattr\n");
+#endif
+ sz = ANDROID_USER_DEFAULT_TAG_LEN + 3 + 1;
+
+ if (total_sz + sz >= T_BLOCKSIZE)
+ {
+ if (th_write_extended(t, &buf[0], total_sz))
+ return -1;
+ ptr = buf;
+ total_sz = sz;
+ }
+ else
+ total_sz += sz;
+
+ snprintf(ptr, T_BLOCKSIZE, "%d "ANDROID_USER_DEFAULT_TAG, (int)sz);
+ char *nlptr = ptr + sz - 1;
+ *nlptr = '\n';
+ ptr += sz;
+ }
+ if (t->th_buf.has_user_cache) {
+#ifdef DEBUG
+ printf("th_write(): has android user.inode_cache xattr\n");
+#endif
+ sz = ANDROID_USER_CACHE_TAG_LEN + 3 + 1;
+
+ if (total_sz + sz >= T_BLOCKSIZE)
+ {
+ if (th_write_extended(t, &buf[0], total_sz))
+ return -1;
+ ptr = buf;
+ total_sz = sz;
+ }
+ else
+ total_sz += sz;
+
+ snprintf(ptr, T_BLOCKSIZE, "%d "ANDROID_USER_CACHE_TAG, (int)sz);
+ char *nlptr = ptr + sz - 1;
+ *nlptr = '\n';
+ ptr += sz;
+ }
+ if (t->th_buf.has_user_code_cache) {
+#ifdef DEBUG
+ printf("th_write(): has android user.inode_code_cache xattr\n");
+#endif
+ sz = ANDROID_USER_CODE_CACHE_TAG_LEN + 3 + 1;
+
+ if (total_sz + sz >= T_BLOCKSIZE)
+ {
+ if (th_write_extended(t, &buf[0], total_sz))
+ return -1;
+ ptr = buf;
+ total_sz = sz;
+ }
+ else
+ total_sz += sz;
+
+ snprintf(ptr, T_BLOCKSIZE, "%d "ANDROID_USER_CODE_CACHE_TAG, (int)sz);
+ char *nlptr = ptr + sz - 1;
+ *nlptr = '\n';
+ ptr += sz;
+ }
+ }
+ if (total_sz > 0 && th_write_extended(t, &buf[0], total_sz)) // write any outstanding tar extended header
+ return -1;
+
+ th_finish(t);
+
+#ifdef DEBUG
+ /* print tar header */
+ th_print(t);
+#endif
+
+ i = tar_block_write(t, &(t->th_buf));
+ if (i != T_BLOCKSIZE)
+ {
+ if (i != -1)
+ errno = EINVAL;
+ return -1;
+ }
+
+#ifdef DEBUG
+ puts("th_write(): returning 0");
+#endif
+ return 0;
+}
+
+
diff --git a/libtar/compat.h b/libtar/compat.h
new file mode 100644
index 000000000..16b3c3b7c
--- /dev/null
+++ b/libtar/compat.h
@@ -0,0 +1,257 @@
+/* prototypes for borrowed "compatibility" code */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+
+#include <stdarg.h>
+#include <stddef.h>
+
+#ifdef HAVE_LIBGEN_H
+# include <libgen.h>
+#endif
+
+
+#if defined(NEED_BASENAME) && !defined(HAVE_BASENAME)
+
+# ifdef basename
+# undef basename /* fix glibc brokenness */
+# endif
+
+char *openbsd_basename(const char *);
+# define basename openbsd_basename
+
+#endif /* NEED_BASENAME && ! HAVE_BASENAME */
+
+
+#if defined(NEED_DIRNAME) && !defined(HAVE_DIRNAME)
+
+char *openbsd_dirname(const char *);
+# define dirname openbsd_dirname
+
+#endif /* NEED_DIRNAME && ! HAVE_DIRNAME */
+
+
+#ifdef NEED_FNMATCH
+# ifndef HAVE_FNMATCH
+
+# define FNM_NOMATCH 1 /* Match failed. */
+
+# define FNM_NOESCAPE 0x01 /* Disable backslash escaping. */
+# define FNM_PATHNAME 0x02 /* Slash must be matched by slash. */
+# define FNM_PERIOD 0x04 /* Period must be matched by period. */
+
+# define FNM_LEADING_DIR 0x08 /* Ignore /<tail> after Imatch. */
+# define FNM_CASEFOLD 0x10 /* Case insensitive search. */
+# define FNM_IGNORECASE FNM_CASEFOLD
+# define FNM_FILE_NAME FNM_PATHNAME
+
+int openbsd_fnmatch(const char *, const char *, int);
+# define fnmatch openbsd_fnmatch
+
+# else /* HAVE_FNMATCH */
+
+# ifdef HAVE_FNMATCH_H
+# include <fnmatch.h>
+# endif
+
+# endif /* ! HAVE_FNMATCH */
+#endif /* NEED_FNMATCH */
+
+
+#ifdef NEED_GETHOSTBYNAME_R
+
+# include <netdb.h>
+
+# if GETHOSTBYNAME_R_NUM_ARGS != 6
+
+int compat_gethostbyname_r(const char *, struct hostent *,
+ char *, size_t, struct hostent **, int *);
+
+# define gethostbyname_r compat_gethostbyname_r
+
+# endif /* GETHOSTBYNAME_R_NUM_ARGS != 6 */
+
+#endif /* NEED_GETHOSTBYNAME_R */
+
+
+#if defined(NEED_GETHOSTNAME) && !defined(HAVE_GETHOSTNAME)
+
+int gethostname(char *, size_t);
+
+#endif /* NEED_GETHOSTNAME && ! HAVE_GETHOSTNAME */
+
+
+#ifdef NEED_GETSERVBYNAME_R
+
+# include <netdb.h>
+
+# if GETSERVBYNAME_R_NUM_ARGS != 6
+
+int compat_getservbyname_r(const char *, const char *, struct servent *,
+ char *, size_t, struct servent **);
+
+# define getservbyname_r compat_getservbyname_r
+
+# endif /* GETSERVBYNAME_R_NUM_ARGS != 6 */
+
+#endif /* NEED_GETSERVBYNAME_R */
+
+
+
+#ifdef NEED_GLOB
+# ifndef HAVE_GLOB
+
+typedef struct {
+ int gl_pathc; /* Count of total paths so far. */
+ int gl_matchc; /* Count of paths matching pattern. */
+ int gl_offs; /* Reserved at beginning of gl_pathv. */
+ int gl_flags; /* Copy of flags parameter to glob. */
+ char **gl_pathv; /* List of paths matching pattern. */
+ /* Copy of errfunc parameter to glob. */
+ int (*gl_errfunc)(const char *, int);
+
+ /*
+ * Alternate filesystem access methods for glob; replacement
+ * versions of closedir(3), readdir(3), opendir(3), stat(2)
+ * and lstat(2).
+ */
+ void (*gl_closedir)(void *);
+ struct dirent *(*gl_readdir)(void *);
+ void *(*gl_opendir)(const char *);
+ int (*gl_lstat)(const char *, struct stat *);
+ int (*gl_stat)(const char *, struct stat *);
+} glob_t;
+
+/* Flags */
+# define GLOB_APPEND 0x0001 /* Append to output from previous call. */
+# define GLOB_DOOFFS 0x0002 /* Use gl_offs. */
+# define GLOB_ERR 0x0004 /* Return on error. */
+# define GLOB_MARK 0x0008 /* Append / to matching directories. */
+# define GLOB_NOCHECK 0x0010 /* Return pattern itself if nothing matches. */
+# define GLOB_NOSORT 0x0020 /* Don't sort. */
+
+# define GLOB_ALTDIRFUNC 0x0040 /* Use alternately specified directory funcs. */
+# define GLOB_BRACE 0x0080 /* Expand braces ala csh. */
+# define GLOB_MAGCHAR 0x0100 /* Pattern had globbing characters. */
+# define GLOB_NOMAGIC 0x0200 /* GLOB_NOCHECK without magic chars (csh). */
+# define GLOB_QUOTE 0x0400 /* Quote special chars with \. */
+# define GLOB_TILDE 0x0800 /* Expand tilde names from the passwd file. */
+# define GLOB_NOESCAPE 0x1000 /* Disable backslash escaping. */
+
+/* Error values returned by glob(3) */
+# define GLOB_NOSPACE (-1) /* Malloc call failed. */
+# define GLOB_ABORTED (-2) /* Unignored error. */
+# define GLOB_NOMATCH (-3) /* No match and GLOB_NOCHECK not set. */
+# define GLOB_NOSYS (-4) /* Function not supported. */
+# define GLOB_ABEND GLOB_ABORTED
+
+int openbsd_glob(const char *, int, int (*)(const char *, int), glob_t *);
+void openbsd_globfree(glob_t *);
+# define glob openbsd_glob
+# define globfree openbsd_globfree
+
+# else /* HAVE_GLOB */
+
+# ifdef HAVE_GLOB_H
+# include <glob.h>
+# endif
+
+# endif /* ! HAVE_GLOB */
+#endif /* NEED_GLOB */
+
+
+#if defined(NEED_INET_ATON) && !defined(HAVE_INET_ATON)
+
+int inet_aton(const char *, struct in_addr *);
+
+#endif /* NEED_INET_ATON && ! HAVE_INET_ATON */
+
+
+#ifdef NEED_MAKEDEV
+
+# ifdef MAJOR_IN_MKDEV
+# include <sys/mkdev.h>
+# else
+# ifdef MAJOR_IN_SYSMACROS
+# include <sys/sysmacros.h>
+# endif
+# endif
+
+/*
+** On most systems makedev() has two args.
+** Some weird systems, like QNX6, have makedev() functions that expect
+** an extra first argument for "node", which can be 0 for a local
+** machine.
+*/
+
+# ifdef MAKEDEV_THREE_ARGS
+# define compat_makedev(maj, min) makedev(0, maj, min)
+# else
+# define compat_makedev makedev
+# endif
+
+#endif /* NEED_MAKEDEV */
+
+
+#if defined(NEED_SNPRINTF) && !defined(HAVE_SNPRINTF)
+
+int mutt_snprintf(char *, size_t, const char *, ...);
+int mutt_vsnprintf(char *, size_t, const char *, va_list);
+#define snprintf mutt_snprintf
+#define vsnprintf mutt_vsnprintf
+
+#endif /* NEED_SNPRINTF && ! HAVE_SNPRINTF */
+
+
+#if defined(NEED_STRLCAT) && !defined(HAVE_STRLCAT)
+
+size_t strlcat(char *, const char *, size_t);
+
+#endif /* NEED_STRLCAT && ! HAVE_STRLCAT */
+
+
+#if defined(NEED_STRLCPY) && !defined(HAVE_STRLCPY)
+
+size_t strlcpy(char *, const char *, size_t);
+
+#endif /* NEED_STRLCPY && ! HAVE_STRLCPY */
+
+
+#if defined(NEED_STRDUP) && !defined(HAVE_STRDUP)
+
+char *openbsd_strdup(const char *);
+# define strdup openbsd_strdup
+
+#endif /* NEED_STRDUP && ! HAVE_STRDUP */
+
+
+#if defined(NEED_STRMODE) && !defined(HAVE_STRMODE)
+
+void strmode(register mode_t, register char *);
+
+#endif /* NEED_STRMODE && ! HAVE_STRMODE */
+
+
+#if defined(NEED_STRRSTR) && !defined(HAVE_STRRSTR)
+
+char *strrstr(char *, char *);
+
+#endif /* NEED_STRRSTR && ! HAVE_STRRSTR */
+
+
+#ifdef NEED_STRSEP
+
+# ifdef HAVE_STRSEP
+# define _LINUX_SOURCE_COMPAT /* needed on AIX 4.3.3 */
+# else
+
+char *strsep(register char **, register const char *);
+
+# endif
+
+#endif /* NEED_STRSEP */
+
+
diff --git a/libtar/config.h b/libtar/config.h
new file mode 100644
index 000000000..c658020ea
--- /dev/null
+++ b/libtar/config.h
@@ -0,0 +1,188 @@
+/* config.h. Generated by configure. */
+/* config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define if your system has a working basename */
+/* #undef HAVE_BASENAME */
+
+/* Define to 1 if you have the <ctype.h> header file. */
+/* #undef HAVE_CTYPE_H */
+
+/* Define to 1 if the system has the type `dev_t'. */
+#define HAVE_DEV_T 1
+
+/* Define if your system has a working dirname */
+/* #undef HAVE_DIRNAME */
+
+/* Define to 1 if your system has a working POSIX `fnmatch' function. */
+#define HAVE_FNMATCH 1
+
+/* Define to 1 if you have the <fnmatch.h> header file. */
+#define HAVE_FNMATCH_H 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the `lchown' function. */
+#define HAVE_LCHOWN 1
+
+/* Define to 1 if you have the <libgen.h> header file. */
+#define HAVE_LIBGEN_H 1
+
+/* Define to 1 if you have the `z' library (-lz). */
+#define HAVE_LIBZ 1
+
+/* Define to 1 if the system has the type `major_t'. */
+/* #undef HAVE_MAJOR_T */
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if the system has the type `minor_t'. */
+/* #undef HAVE_MINOR_T */
+
+/* Define to 1 if the system has the type `nlink_t'. */
+#define HAVE_NLINK_T 1
+
+/* Define if your system has a working snprintf */
+#define HAVE_SNPRINTF 1
+
+/* Define to 1 if the system has the type `socklen_t'. */
+#define HAVE_SOCKLEN_T 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define if you have the strdup function */
+#define HAVE_STRDUP 1
+
+/* Define to 1 if you have the `strftime' function. */
+#define HAVE_STRFTIME 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define if you have the strlcpy function */
+/* #undef HAVE_STRLCPY */
+
+/* Define if you have the strmode function */
+/* #undef HAVE_STRMODE */
+
+/* Define if you have the strsep function */
+#define HAVE_STRSEP 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if the system has the type `uint64_t'. */
+#define HAVE_UINT64_T 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if `major', `minor', and `makedev' are declared in <mkdev.h>.
+ */
+/* #undef MAJOR_IN_MKDEV */
+
+/* Define to 1 if `major', `minor', and `makedev' are declared in
+ <sysmacros.h>. */
+/* #undef MAJOR_IN_SYSMACROS */
+
+/* Define as 1 if makedev expects three arguments */
+/* #undef MAKEDEV_THREE_ARGS */
+
+/* Define if you want to use the basename function */
+#define NEED_BASENAME 1
+
+/* Define if you want to use the dirname function */
+#define NEED_DIRNAME 1
+
+/* Define if you want to use the fnmatch function */
+#define NEED_FNMATCH 1
+
+/* Define if you want to use the makedev function */
+#define NEED_MAKEDEV 1
+
+/* Define if you want to use the snprintf function */
+#define NEED_SNPRINTF 1
+
+/* Define if you want to use the strdup function */
+#define NEED_STRDUP 1
+
+/* Define if you want to use the strlcpy function */
+#define NEED_STRLCPY 1
+
+/* Define if you want to use the strmode function */
+#define NEED_STRMODE 1
+
+/* Define if you want to use the strsep function */
+#define NEED_STRSEP 1
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "libtar"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "libtar 1.2.11"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "libtar"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "1.2.11"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define to 1 if on AIX 3.
+ System headers sometimes define this.
+ We just want to avoid a redefinition error message. */
+#ifndef _ALL_SOURCE
+/* # undef _ALL_SOURCE */
+#endif
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to `unsigned long' if not defined in system header files. */
+/* #undef dev_t */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef gid_t */
+
+/* Define to `unsigned int' if not defined in system header files. */
+#define major_t unsigned int
+
+/* Define to `unsigned int' if not defined in system header files. */
+#define minor_t unsigned int
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef mode_t */
+
+/* Define to `unsigned short' if not defined in system header files. */
+/* #undef nlink_t */
+
+/* Define to `long' if <sys/types.h> does not define. */
+/* #undef off_t */
+
+/* Define to `unsigned' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* Define to `unsigned long' if not defined in system header files. */
+/* #undef socklen_t */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef uid_t */
+
+/* Define to `long long' if not defined in system header files. */
+/* #undef uint64_t */
diff --git a/libtar/decode.c b/libtar/decode.c
new file mode 100644
index 000000000..1a3d0ee56
--- /dev/null
+++ b/libtar/decode.c
@@ -0,0 +1,135 @@
+/*
+** Copyright 1998-2003 University of Illinois Board of Trustees
+** Copyright 1998-2003 Mark D. Roth
+** All rights reserved.
+**
+** decode.c - libtar code to decode tar header blocks
+**
+** Mark D. Roth <roth@uiuc.edu>
+** Campus Information Technologies and Educational Services
+** University of Illinois at Urbana-Champaign
+*/
+
+#include <internal.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <pwd.h>
+#include <grp.h>
+
+#ifdef STDC_HEADERS
+# include <string.h>
+#endif
+
+
+/* determine full path name */
+char *
+th_get_pathname(TAR *t)
+{
+ if (t->th_buf.gnu_longname)
+ return t->th_buf.gnu_longname;
+
+ /* allocate the th_pathname buffer if not already */
+ if (t->th_pathname == NULL)
+ {
+ t->th_pathname = malloc(MAXPATHLEN * sizeof(char));
+ if (t->th_pathname == NULL)
+ /* out of memory */
+ return NULL;
+ }
+
+ if (t->th_buf.prefix[0] == '\0')
+ {
+ snprintf(t->th_pathname, MAXPATHLEN, "%.100s", t->th_buf.name);
+ }
+ else
+ {
+ snprintf(t->th_pathname, MAXPATHLEN, "%.155s/%.100s",
+ t->th_buf.prefix, t->th_buf.name);
+ }
+
+ /* will be deallocated in tar_close() */
+ return t->th_pathname;
+}
+
+
+uid_t
+th_get_uid(TAR *t)
+{
+ int uid;
+ struct passwd *pw;
+
+ if (!(t->options & TAR_USE_NUMERIC_ID)) {
+ pw = getpwnam(t->th_buf.uname);
+ if (pw != NULL)
+ return pw->pw_uid;
+ }
+
+ /* if the password entry doesn't exist */
+ sscanf(t->th_buf.uid, "%o", &uid);
+ return uid;
+}
+
+
+gid_t
+th_get_gid(TAR *t)
+{
+ int gid;
+ struct group *gr;
+
+ if (!(t->options & TAR_USE_NUMERIC_ID)) {
+ gr = getgrnam(t->th_buf.gname);
+ if (gr != NULL)
+ return gr->gr_gid;
+ }
+
+ /* if the group entry doesn't exist */
+ sscanf(t->th_buf.gid, "%o", &gid);
+ return gid;
+}
+
+
+mode_t
+th_get_mode(TAR *t)
+{
+ mode_t mode;
+
+ mode = (mode_t)oct_to_int(t->th_buf.mode, sizeof(t->th_buf.mode));
+ if (! (mode & S_IFMT))
+ {
+ switch (t->th_buf.typeflag)
+ {
+ case SYMTYPE:
+ mode |= S_IFLNK;
+ break;
+ case CHRTYPE:
+ mode |= S_IFCHR;
+ break;
+ case BLKTYPE:
+ mode |= S_IFBLK;
+ break;
+ case DIRTYPE:
+ mode |= S_IFDIR;
+ break;
+ case FIFOTYPE:
+ mode |= S_IFIFO;
+ break;
+ case AREGTYPE:
+ if (t->th_buf.name[strnlen(t->th_buf.name, T_NAMELEN) - 1] == '/')
+ {
+ mode |= S_IFDIR;
+ break;
+ }
+ /* FALLTHROUGH */
+ case LNKTYPE:
+ case REGTYPE:
+ default:
+ mode |= S_IFREG;
+ }
+ }
+
+ return mode;
+}
+
+
diff --git a/libtar/dirname.c b/libtar/dirname.c
new file mode 100644
index 000000000..31dbe13a8
--- /dev/null
+++ b/libtar/dirname.c
@@ -0,0 +1,77 @@
+/* $OpenBSD: dirname.c,v 1.4 1999/05/30 17:10:30 espie Exp $ */
+
+/*
+ * Copyright (c) 1997 Todd C. Miller <Todd.Miller@courtesan.com>
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+//static char rcsid[] = "$OpenBSD: dirname.c,v 1.4 1999/05/30 17:10:30 espie Exp $";
+#endif /* not lint */
+
+#include <errno.h>
+#include <string.h>
+#include <sys/param.h>
+
+char *
+openbsd_dirname(path)
+ const char *path;
+{
+ static char bname[MAXPATHLEN];
+ register const char *endp;
+
+ /* Empty or NULL string gets treated as "." */
+ if (path == NULL || *path == '\0') {
+ (void)strcpy(bname, ".");
+ return(bname);
+ }
+
+ /* Strip trailing slashes */
+ endp = path + strlen(path) - 1;
+ while (endp > path && *endp == '/')
+ endp--;
+
+ /* Find the start of the dir */
+ while (endp > path && *endp != '/')
+ endp--;
+
+ /* Either the dir is "/" or there are no slashes */
+ if (endp == path) {
+ (void)strcpy(bname, *endp == '/' ? "/" : ".");
+ return(bname);
+ } else {
+ do {
+ endp--;
+ } while (endp > path && *endp == '/');
+ }
+
+ if (endp - path + 1 > (int)sizeof(bname)) {
+ errno = ENAMETOOLONG;
+ return(NULL);
+ }
+ (void)strncpy(bname, path, endp - path + 1);
+ bname[endp - path + 1] = '\0';
+ return(bname);
+}
diff --git a/libtar/encode.c b/libtar/encode.c
new file mode 100644
index 000000000..1e679d81e
--- /dev/null
+++ b/libtar/encode.c
@@ -0,0 +1,239 @@
+/*
+** Copyright 1998-2003 University of Illinois Board of Trustees
+** Copyright 1998-2003 Mark D. Roth
+** All rights reserved.
+**
+** encode.c - libtar code to encode tar header blocks
+**
+** Mark D. Roth <roth@uiuc.edu>
+** Campus Information Technologies and Educational Services
+** University of Illinois at Urbana-Champaign
+*/
+
+#include <internal.h>
+
+#include <stdio.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/types.h>
+#include <sys/sysmacros.h>
+
+#ifdef STDC_HEADERS
+# include <string.h>
+# include <stdlib.h>
+#endif
+
+
+/* magic, version, and checksum */
+void
+th_finish(TAR *t)
+{
+ if (t->options & TAR_GNU)
+ {
+ /* we're aiming for this result, but must do it in
+ * two calls to avoid FORTIFY segfaults on some Linux
+ * systems:
+ * strncpy(t->th_buf.magic, "ustar ", 8);
+ */
+ strncpy(t->th_buf.magic, "ustar ", 6);
+ strncpy(t->th_buf.version, " ", 2);
+ }
+ else
+ {
+ strncpy(t->th_buf.version, TVERSION, TVERSLEN);
+ strncpy(t->th_buf.magic, TMAGIC, TMAGLEN);
+ }
+
+ int_to_oct(th_crc_calc(t), t->th_buf.chksum, 8);
+}
+
+
+/* map a file mode to a typeflag */
+void
+th_set_type(TAR *t, mode_t mode)
+{
+ if (S_ISLNK(mode))
+ t->th_buf.typeflag = SYMTYPE;
+ if (S_ISREG(mode))
+ t->th_buf.typeflag = REGTYPE;
+ if (S_ISDIR(mode))
+ t->th_buf.typeflag = DIRTYPE;
+ if (S_ISCHR(mode))
+ t->th_buf.typeflag = CHRTYPE;
+ if (S_ISBLK(mode))
+ t->th_buf.typeflag = BLKTYPE;
+ if (S_ISFIFO(mode) || S_ISSOCK(mode))
+ t->th_buf.typeflag = FIFOTYPE;
+}
+
+
+/* encode file path */
+void
+th_set_path(TAR *t, const char *pathname)
+{
+ char suffix[2] = "";
+ char *tmp;
+ size_t pathname_len = strlen(pathname);
+
+#ifdef DEBUG
+ printf("in th_set_path(th, pathname=\"%s\")\n", pathname);
+#endif
+
+ if (t->th_buf.gnu_longname != NULL)
+ free(t->th_buf.gnu_longname);
+ t->th_buf.gnu_longname = NULL;
+
+ /* old archive compatibility (not needed for gnu): add trailing / to directories */
+ if (pathname[pathname_len - 1] != '/' && TH_ISDIR(t))
+ strcpy(suffix, "/");
+
+ if (pathname_len >= T_NAMELEN && (t->options & TAR_GNU))
+ {
+ /* GNU-style long name (no file name length limit) */
+ t->th_buf.gnu_longname = strdup(pathname);
+ strncpy(t->th_buf.name, t->th_buf.gnu_longname, T_NAMELEN);
+ }
+ else if (pathname_len >= T_NAMELEN)
+ {
+ /* POSIX-style prefix field:
+ * The maximum length of a file name is limited to 256 characters,
+ * provided that the file name can be split at a directory separator
+ * in two parts. The first part being at most 155 bytes long and
+ * the second part being at most 100 bytes long. So, in most cases
+ * the maximum file name length will be shorter than 256 characters.
+ */
+ char tail_path[T_NAMELEN + 1];
+ tmp = strchr(&(pathname[pathname_len - T_NAMELEN]), '/');
+ if (tmp == NULL)
+ {
+ printf("!!! '/' not found in \"%s\"\n", pathname);
+ return;
+ }
+ snprintf(tail_path, T_NAMELEN + 1, "%s%s", &tmp[1], suffix);
+ strncpy(t->th_buf.name, tail_path, T_NAMELEN);
+
+ /*
+ * first part, max = 155 == sizeof(t->th_buf.prefix) , include NULL if it fits
+ * trailing '/' is added during decode: decode.c/th_get_pathname()
+ */
+ if (tmp - pathname >= 155) {
+ strncpy(t->th_buf.prefix, pathname, 155);
+ } else {
+ snprintf(t->th_buf.prefix, (tmp - pathname + 1), "%s", pathname);
+ }
+ }
+ else {
+ /* any short name for all formats, or classic tar format (99 chars max) */
+ snprintf(t->th_buf.name, T_NAMELEN, "%s%s", pathname, suffix);
+ }
+
+#ifdef DEBUG
+ puts("returning from th_set_path()...");
+#endif
+}
+
+
+/* encode link path */
+void
+th_set_link(TAR *t, const char *linkname)
+{
+#ifdef DEBUG
+ printf("==> th_set_link(th, linkname=\"%s\")\n", linkname);
+#endif
+
+ if (strlen(linkname) >= T_NAMELEN && (t->options & TAR_GNU))
+ {
+ /* --format=gnu: GNU-style long name (no file name length limit) */
+ t->th_buf.gnu_longlink = strdup(linkname);
+ strcpy(t->th_buf.linkname, "././@LongLink");
+ }
+ else if (strlen(linkname) >= T_NAMELEN)
+ {
+ /* --format=ustar: 100 chars max limit for symbolic links */
+ strncpy(t->th_buf.linkname, linkname, T_NAMELEN);
+ if (t->th_buf.gnu_longlink != NULL)
+ free(t->th_buf.gnu_longlink);
+ t->th_buf.gnu_longlink = NULL;
+ } else {
+ /* all short links or v7 tar format: The maximum length of a symbolic link name is limited to 99 characters */
+ snprintf(t->th_buf.linkname, T_NAMELEN, "%s", linkname);
+ if (t->th_buf.gnu_longlink != NULL)
+ free(t->th_buf.gnu_longlink);
+ t->th_buf.gnu_longlink = NULL;
+ }
+}
+
+
+/* encode device info */
+void
+th_set_device(TAR *t, dev_t device)
+{
+#ifdef DEBUG
+ printf("th_set_device(): major = %d, minor = %d\n",
+ major(device), minor(device));
+#endif
+ int_to_oct(major(device), t->th_buf.devmajor, 8);
+ int_to_oct(minor(device), t->th_buf.devminor, 8);
+}
+
+
+/* encode user info */
+void
+th_set_user(TAR *t, uid_t uid)
+{
+ struct passwd *pw;
+
+ if (!(t->options & TAR_USE_NUMERIC_ID)) {
+ pw = getpwuid(uid);
+ if (pw != NULL)
+ strlcpy(t->th_buf.uname, pw->pw_name, sizeof(t->th_buf.uname));
+ }
+
+ int_to_oct(uid, t->th_buf.uid, 8);
+}
+
+
+/* encode group info */
+void
+th_set_group(TAR *t, gid_t gid)
+{
+ struct group *gr;
+
+ if (!(t->options & TAR_USE_NUMERIC_ID)) {
+ gr = getgrgid(gid);
+ if (gr != NULL)
+ strlcpy(t->th_buf.gname, gr->gr_name, sizeof(t->th_buf.gname));
+ }
+
+ int_to_oct(gid, t->th_buf.gid, 8);
+}
+
+
+/* encode file mode */
+void
+th_set_mode(TAR *t, mode_t fmode)
+{
+ if (S_ISSOCK(fmode))
+ {
+ fmode &= ~S_IFSOCK;
+ fmode |= S_IFIFO;
+ }
+ int_to_oct(fmode, (t)->th_buf.mode, 8);
+}
+
+
+void
+th_set_from_stat(TAR *t, struct stat *s)
+{
+ th_set_type(t, s->st_mode);
+ if (S_ISCHR(s->st_mode) || S_ISBLK(s->st_mode))
+ th_set_device(t, s->st_rdev);
+ th_set_user(t, s->st_uid);
+ th_set_group(t, s->st_gid);
+ th_set_mode(t, s->st_mode);
+ th_set_mtime(t, s->st_mtime);
+ if (S_ISREG(s->st_mode))
+ th_set_size(t, s->st_size);
+ else
+ th_set_size(t, 0);
+}
diff --git a/libtar/extract.c b/libtar/extract.c
new file mode 100644
index 000000000..ea86c233b
--- /dev/null
+++ b/libtar/extract.c
@@ -0,0 +1,667 @@
+/*
+** Copyright 1998-2003 University of Illinois Board of Trustees
+** Copyright 1998-2003 Mark D. Roth
+** All rights reserved.
+**
+** extract.c - libtar code to extract a file from a tar archive
+**
+** Mark D. Roth <roth@uiuc.edu>
+** Campus Information Technologies and Educational Services
+** University of Illinois at Urbana-Champaign
+*/
+
+#include <internal.h>
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <utime.h>
+
+#include <sys/capability.h>
+#include <sys/xattr.h>
+#include <linux/xattr.h>
+
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include <selinux/selinux.h>
+
+#ifdef HAVE_EXT4_CRYPT
+# include "ext4crypt_tar.h"
+#endif
+#include "android_utils.h"
+
+const unsigned long long progress_size = (unsigned long long)(T_BLOCKSIZE);
+
+static int
+tar_set_file_perms(TAR *t, const char *realname)
+{
+ mode_t mode;
+ uid_t uid;
+ gid_t gid;
+ struct utimbuf ut;
+ const char *filename;
+ char *pn;
+
+ pn = th_get_pathname(t);
+ filename = (realname ? realname : pn);
+ mode = th_get_mode(t);
+ uid = th_get_uid(t);
+ gid = th_get_gid(t);
+ ut.modtime = ut.actime = th_get_mtime(t);
+
+#ifdef DEBUG
+ printf("tar_set_file_perms(): setting perms: %s (mode %04o, uid %d, gid %d)\n",
+ filename, mode, uid, gid);
+#endif
+
+ /* change owner/group */
+ if (geteuid() == 0)
+#ifdef HAVE_LCHOWN
+ if (lchown(filename, uid, gid) == -1)
+ {
+# ifdef DEBUG
+ fprintf(stderr, "lchown(\"%s\", %d, %d): %s\n",
+ filename, uid, gid, strerror(errno));
+# endif
+#else /* ! HAVE_LCHOWN */
+ if (!TH_ISSYM(t) && chown(filename, uid, gid) == -1)
+ {
+# ifdef DEBUG
+ fprintf(stderr, "chown(\"%s\", %d, %d): %s\n",
+ filename, uid, gid, strerror(errno));
+# endif
+#endif /* HAVE_LCHOWN */
+ return -1;
+ }
+
+ /* change access/modification time */
+ if (!TH_ISSYM(t) && utime(filename, &ut) == -1)
+ {
+#ifdef DEBUG
+ perror("utime()");
+#endif
+ return -1;
+ }
+
+ /* change permissions */
+ if (!TH_ISSYM(t) && chmod(filename, mode) == -1)
+ {
+#ifdef DEBUG
+ perror("chmod()");
+#endif
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/* switchboard */
+int
+tar_extract_file(TAR *t, const char *realname, const char *prefix, const int *progress_fd)
+{
+ int i;
+#ifdef LIBTAR_FILE_HASH
+ char *lnp;
+ char *pn;
+ int pathname_len;
+ int realname_len;
+#endif
+
+ if (t->options & TAR_NOOVERWRITE)
+ {
+ struct stat s;
+
+ if (lstat(realname, &s) == 0 || errno != ENOENT)
+ {
+ errno = EEXIST;
+ return -1;
+ }
+ }
+
+ if (TH_ISDIR(t))
+ {
+ i = tar_extract_dir(t, realname);
+ if (i == 1)
+ i = 0;
+ }
+ else if (TH_ISLNK(t))
+ i = tar_extract_hardlink(t, realname, prefix);
+ else if (TH_ISSYM(t))
+ i = tar_extract_symlink(t, realname);
+ else if (TH_ISCHR(t))
+ i = tar_extract_chardev(t, realname);
+ else if (TH_ISBLK(t))
+ i = tar_extract_blockdev(t, realname);
+ else if (TH_ISFIFO(t))
+ i = tar_extract_fifo(t, realname);
+ else /* if (TH_ISREG(t)) */
+ i = tar_extract_regfile(t, realname, progress_fd);
+
+ if (i != 0) {
+ fprintf(stderr, "tar_extract_file(): failed to extract %s !!!\n", realname);
+ return i;
+ }
+
+ i = tar_set_file_perms(t, realname);
+ if (i != 0) {
+ fprintf(stderr, "tar_extract_file(): failed to set permissions on %s !!!\n", realname);
+ return i;
+ }
+
+ if((t->options & TAR_STORE_SELINUX) && t->th_buf.selinux_context != NULL)
+ {
+#ifdef DEBUG
+ printf("tar_extract_file(): restoring SELinux context %s to file %s\n", t->th_buf.selinux_context, realname);
+#endif
+ if (lsetfilecon(realname, t->th_buf.selinux_context) < 0)
+ fprintf(stderr, "tar_extract_file(): failed to restore SELinux context %s to file %s !!!\n", t->th_buf.selinux_context, realname);
+ }
+
+ if((t->options & TAR_STORE_POSIX_CAP) && t->th_buf.has_cap_data)
+ {
+#if 1 //def DEBUG
+ printf("tar_extract_file(): restoring posix capabilities to file %s\n", realname);
+ print_caps(&t->th_buf.cap_data);
+#endif
+ if (setxattr(realname, XATTR_NAME_CAPS, &t->th_buf.cap_data, sizeof(struct vfs_cap_data), 0) < 0)
+ fprintf(stderr, "tar_extract_file(): failed to restore posix capabilities to file %s !!!\n", realname);
+ }
+
+#ifdef LIBTAR_FILE_HASH
+ pn = th_get_pathname(t);
+ pathname_len = strlen(pn) + 1;
+ realname_len = strlen(realname) + 1;
+ lnp = (char *)calloc(1, pathname_len + realname_len);
+ if (lnp == NULL)
+ return -1;
+ strcpy(&lnp[0], pn);
+ strcpy(&lnp[pathname_len], realname);
+#ifdef DEBUG
+ printf("tar_extract_file(): calling libtar_hash_add(): key=\"%s\", "
+ "value=\"%s\"\n", pn, realname);
+#endif
+ if (libtar_hash_add(t->h, lnp) != 0)
+ return -1;
+ free(lnp);
+#endif
+
+ return 0;
+}
+
+
+/* extract regular file */
+int
+tar_extract_regfile(TAR *t, const char *realname, const int *progress_fd)
+{
+ int64_t size, i;
+ ssize_t k;
+ int fdout;
+ char buf[T_BLOCKSIZE];
+ const char *filename;
+ char *pn;
+
+#ifdef DEBUG
+ printf(" ==> tar_extract_regfile(realname=\"%s\")\n", realname);
+#endif
+
+ if (!TH_ISREG(t))
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ pn = th_get_pathname(t);
+ filename = (realname ? realname : pn);
+ size = th_get_size(t);
+
+ if (mkdirhier(dirname(filename)) == -1)
+ return -1;
+
+ printf(" ==> extracting: %s (file size %" PRId64 " bytes)\n",
+ filename, size);
+
+ fdout = open(filename, O_WRONLY | O_CREAT | O_TRUNC
+#ifdef O_BINARY
+ | O_BINARY
+#endif
+ , 0666);
+ if (fdout == -1)
+ {
+#ifdef DEBUG
+ perror("open()");
+#endif
+ return -1;
+ }
+
+ /* extract the file */
+ for (i = size; i > 0; i -= T_BLOCKSIZE)
+ {
+ k = tar_block_read(t, buf);
+ if (k != T_BLOCKSIZE)
+ {
+ if (k != -1)
+ errno = EINVAL;
+ close(fdout);
+ return -1;
+ }
+
+ /* write block to output file */
+ if (write(fdout, buf,
+ ((i > T_BLOCKSIZE) ? T_BLOCKSIZE : i)) == -1)
+ {
+ close(fdout);
+ return -1;
+ }
+ else
+ {
+ if (*progress_fd != 0)
+ write(*progress_fd, &progress_size, sizeof(progress_size));
+ }
+ }
+
+ /* close output file */
+ if (close(fdout) == -1)
+ return -1;
+
+#ifdef DEBUG
+ printf("### done extracting %s\n", filename);
+#endif
+
+ return 0;
+}
+
+
+/* skip regfile */
+int
+tar_skip_regfile(TAR *t)
+{
+ int64_t size, i;
+ ssize_t k;
+ char buf[T_BLOCKSIZE];
+
+ if (!TH_ISREG(t))
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ size = th_get_size(t);
+ for (i = size; i > 0; i -= T_BLOCKSIZE)
+ {
+ k = tar_block_read(t, buf);
+ if (k != T_BLOCKSIZE)
+ {
+ if (k != -1)
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+/* hardlink */
+int
+tar_extract_hardlink(TAR * t, const char *realname, const char *prefix)
+{
+ const char *filename;
+ char *pn;
+ char *linktgt = NULL;
+ char *newtgt = NULL;
+ char *lnp;
+ libtar_hashptr_t hp;
+
+ if (!TH_ISLNK(t))
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ pn = th_get_pathname(t);
+ filename = (realname ? realname : pn);
+ if (mkdirhier(dirname(filename)) == -1)
+ return -1;
+ if (unlink(filename) == -1 && errno != ENOENT)
+ return -1;
+ libtar_hashptr_reset(&hp);
+ if (libtar_hash_getkey(t->h, &hp, th_get_linkname(t),
+ (libtar_matchfunc_t)libtar_str_match) != 0)
+ {
+ lnp = (char *)libtar_hashptr_data(&hp);
+ linktgt = &lnp[strlen(lnp) + 1];
+ }
+ else
+ linktgt = th_get_linkname(t);
+
+ newtgt = strdup(linktgt);
+ sprintf(linktgt, "%s/%s", prefix, newtgt);
+
+ printf(" ==> extracting: %s (link to %s)\n", filename, linktgt);
+
+ if (link(linktgt, filename) == -1)
+ {
+ fprintf(stderr, "tar_extract_hardlink(): failed restore of hardlink '%s' but returning as if nothing bad happened\n", filename);
+ return 0; // Used to be -1
+ }
+
+ return 0;
+}
+
+
+/* symlink */
+int
+tar_extract_symlink(TAR *t, const char *realname)
+{
+ const char *filename;
+ char *pn;
+
+ if (!TH_ISSYM(t))
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ pn = th_get_pathname(t);
+ filename = (realname ? realname : pn);
+ if (mkdirhier(dirname(filename)) == -1)
+ return -1;
+
+ if (unlink(filename) == -1 && errno != ENOENT)
+ return -1;
+
+ printf(" ==> extracting: %s (symlink to %s)\n",
+ filename, th_get_linkname(t));
+
+ if (symlink(th_get_linkname(t), filename) == -1)
+ {
+#ifdef DEBUG
+ perror("symlink()");
+#endif
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/* character device */
+int
+tar_extract_chardev(TAR *t, const char *realname)
+{
+ mode_t mode;
+ unsigned long devmaj, devmin;
+ const char *filename;
+ char *pn;
+
+ if (!TH_ISCHR(t))
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ pn = th_get_pathname(t);
+ filename = (realname ? realname : pn);
+ mode = th_get_mode(t);
+ devmaj = th_get_devmajor(t);
+ devmin = th_get_devminor(t);
+
+ if (mkdirhier(dirname(filename)) == -1)
+ return -1;
+
+ printf(" ==> extracting: %s (character device %ld,%ld)\n",
+ filename, devmaj, devmin);
+
+ if (mknod(filename, mode | S_IFCHR,
+ compat_makedev(devmaj, devmin)) == -1)
+ {
+ fprintf(stderr, "tar_extract_chardev(): failed restore of character device '%s' but returning as if nothing bad happened\n", filename);
+ return 0; // Used to be -1
+ }
+
+ return 0;
+}
+
+
+/* block device */
+int
+tar_extract_blockdev(TAR *t, const char *realname)
+{
+ mode_t mode;
+ unsigned long devmaj, devmin;
+ const char *filename;
+ char *pn;
+
+ if (!TH_ISBLK(t))
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ pn = th_get_pathname(t);
+ filename = (realname ? realname : pn);
+ mode = th_get_mode(t);
+ devmaj = th_get_devmajor(t);
+ devmin = th_get_devminor(t);
+
+ if (mkdirhier(dirname(filename)) == -1)
+ return -1;
+
+ printf(" ==> extracting: %s (block device %ld,%ld)\n",
+ filename, devmaj, devmin);
+
+ if (mknod(filename, mode | S_IFBLK,
+ compat_makedev(devmaj, devmin)) == -1)
+ {
+ fprintf(stderr, "tar_extract_blockdev(): failed restore of block device '%s' but returning as if nothing bad happened\n", filename);
+ return 0; // Used to be -1
+ }
+
+ return 0;
+}
+
+/* directory */
+int
+tar_extract_dir(TAR *t, const char *realname)
+{
+ mode_t mode;
+ const char *filename;
+ char *pn;
+
+ if (!TH_ISDIR(t))
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ pn = th_get_pathname(t);
+ filename = (realname ? realname : pn);
+ mode = th_get_mode(t);
+
+ if (mkdirhier(dirname(filename)) == -1)
+ return -1;
+
+ printf(" ==> extracting: %s (mode %04o, directory)\n", filename,
+ mode);
+
+ if (mkdir(filename, mode) == -1)
+ {
+ if (errno == EEXIST)
+ {
+ if (chmod(filename, mode) == -1)
+ {
+#ifdef DEBUG
+ perror("chmod()");
+#endif
+ return -1;
+ }
+ else
+ {
+#if 1 //def DEBUG
+ puts(" *** using existing directory");
+#endif
+ return 1;
+ }
+ }
+ else
+ {
+#ifdef DEBUG
+ perror("mkdir()");
+#endif
+ return -1;
+ }
+ }
+
+ if (t->options & TAR_STORE_ANDROID_USER_XATTR)
+ {
+ if (t->th_buf.has_user_default) {
+#if 1 //def DEBUG
+ printf("tar_extract_file(): restoring android user.default xattr to %s\n", realname);
+#endif
+ if (setxattr(realname, "user.default", NULL, 0, 0) < 0) {
+ fprintf(stderr, "tar_extract_file(): failed to restore android user.default to file %s !!!\n", realname);
+ return -1;
+ }
+ }
+ if (t->th_buf.has_user_cache) {
+#if 1 //def DEBUG
+ printf("tar_extract_file(): restoring android user.inode_cache xattr to %s\n", realname);
+#endif
+ if (write_path_inode(realname, "cache", "user.inode_cache"))
+ return -1;
+ }
+ if (t->th_buf.has_user_code_cache) {
+#if 1 //def DEBUG
+ printf("tar_extract_file(): restoring android user.inode_code_cache xattr to %s\n", realname);
+#endif
+ if (write_path_inode(realname, "code_cache", "user.inode_code_cache"))
+ return -1;
+ }
+ }
+
+#ifdef HAVE_EXT4_CRYPT
+ if(t->th_buf.eep != NULL)
+ {
+#ifdef DEBUG
+ printf("tar_extract_file(): restoring EXT4 crypt policy %s to dir %s\n", t->th_buf.eep->master_key_descriptor, realname);
+#endif
+ char binary_policy[EXT4_KEY_DESCRIPTOR_SIZE];
+ if (!lookup_ref_tar(t->th_buf.eep->master_key_descriptor, &binary_policy[0])) {
+ printf("error looking up proper e4crypt policy for '%s' - %s\n", realname, t->th_buf.eep->master_key_descriptor);
+ return -1;
+ }
+ char policy_hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX];
+ policy_to_hex(binary_policy, policy_hex);
+ printf("restoring policy %s > '%s' to '%s'\n", t->th_buf.eep->master_key_descriptor, policy_hex, realname);
+ memcpy(&t->th_buf.eep->master_key_descriptor, binary_policy, EXT4_KEY_DESCRIPTOR_SIZE);
+ if (!e4crypt_policy_set_struct(realname, t->th_buf.eep))
+ {
+ printf("tar_extract_file(): failed to restore EXT4 crypt policy to dir '%s' '%s'!!!\n", realname, policy_hex);
+ //return -1; // This may not be an error in some cases, so log and ignore
+ }
+ }
+#endif
+
+ return 0;
+}
+
+
+/* FIFO */
+int
+tar_extract_fifo(TAR *t, const char *realname)
+{
+ mode_t mode;
+ const char *filename;
+ char *pn;
+
+ if (!TH_ISFIFO(t))
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ pn = th_get_pathname(t);
+ filename = (realname ? realname : pn);
+ mode = th_get_mode(t);
+
+ if (mkdirhier(dirname(filename)) == -1)
+ return -1;
+
+
+ printf(" ==> extracting: %s (fifo)\n", filename);
+
+ if (mkfifo(filename, mode) == -1)
+ {
+#ifdef DEBUG
+ perror("mkfifo()");
+#endif
+ return -1;
+ }
+
+ return 0;
+}
+
+/* extract file contents from a tarchive */
+int
+tar_extract_file_contents(TAR *t, void *buf, size_t *lenp)
+{
+ char block[T_BLOCKSIZE];
+ int64_t size, i;
+ ssize_t k;
+
+#ifdef DEBUG
+ printf(" ==> tar_extract_file_contents\n");
+#endif
+
+ if (!TH_ISREG(t))
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ size = th_get_size(t);
+ if ((uint64_t)size > *lenp)
+ {
+ errno = ENOSPC;
+ return -1;
+ }
+
+ /* extract the file */
+ for (i = size; i >= T_BLOCKSIZE; i -= T_BLOCKSIZE)
+ {
+ k = tar_block_read(t, buf);
+ if (k != T_BLOCKSIZE)
+ {
+ if (k != -1)
+ errno = EINVAL;
+ return -1;
+ }
+ buf = (char *)buf + T_BLOCKSIZE;
+ }
+ if (i > 0) {
+ k = tar_block_read(t, block);
+ if (k != T_BLOCKSIZE)
+ {
+ if (k != -1)
+ errno = EINVAL;
+ return -1;
+ }
+ memcpy(buf, block, i);
+ }
+ *lenp = (size_t)size;
+
+#ifdef DEBUG
+ printf("### done extracting contents\n");
+#endif
+ return 0;
+}
diff --git a/libtar/fnmatch.c b/libtar/fnmatch.c
new file mode 100644
index 000000000..fe75f0eeb
--- /dev/null
+++ b/libtar/fnmatch.c
@@ -0,0 +1,237 @@
+/* $OpenBSD: fnmatch.c,v 1.6 1998/03/19 00:29:59 millert Exp $ */
+
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Guido van Rossum.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)fnmatch.c 8.2 (Berkeley) 4/16/94";
+#else
+static char rcsid[] = "$OpenBSD: fnmatch.c,v 1.6 1998/03/19 00:29:59 millert Exp $";
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
+ * Compares a filename or pathname to a pattern.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+
+#ifdef STDC_HEADERS
+# include <string.h>
+#endif
+
+#ifdef HAVE_CTYPE_H
+# include <ctype.h>
+#endif
+
+#include <compat.h>
+
+
+#define EOS '\0'
+
+#define RANGE_MATCH 1
+#define RANGE_NOMATCH 0
+#define RANGE_ERROR (-1)
+
+#ifdef NO_IBM_COMPILER_HORKAGE
+static int rangematch (const char *, char, int, char **);
+#else
+static int rangematch ();
+#endif
+
+int
+fnmatch(pattern, string, flags)
+ const char *pattern, *string;
+ int flags;
+{
+ const char *stringstart;
+ char *newp;
+ char c, test;
+
+ for (stringstart = string;;)
+ switch (c = *pattern++) {
+ case EOS:
+ if ((flags & FNM_LEADING_DIR) && *string == '/')
+ return (0);
+ return (*string == EOS ? 0 : FNM_NOMATCH);
+ case '?':
+ if (*string == EOS)
+ return (FNM_NOMATCH);
+ if (*string == '/' && (flags & FNM_PATHNAME))
+ return (FNM_NOMATCH);
+ if (*string == '.' && (flags & FNM_PERIOD) &&
+ (string == stringstart ||
+ ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+ return (FNM_NOMATCH);
+ ++string;
+ break;
+ case '*':
+ c = *pattern;
+ /* Collapse multiple stars. */
+ while (c == '*')
+ c = *++pattern;
+
+ if (*string == '.' && (flags & FNM_PERIOD) &&
+ (string == stringstart ||
+ ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+ return (FNM_NOMATCH);
+
+ /* Optimize for pattern with * at end or before /. */
+ if (c == EOS) {
+ if (flags & FNM_PATHNAME)
+ return ((flags & FNM_LEADING_DIR) ||
+ strchr(string, '/') == NULL ?
+ 0 : FNM_NOMATCH);
+ else
+ return (0);
+ } else if (c == '/' && (flags & FNM_PATHNAME)) {
+ if ((string = strchr(string, '/')) == NULL)
+ return (FNM_NOMATCH);
+ break;
+ }
+
+ /* General case, use recursion. */
+ while ((test = *string) != EOS) {
+ if (!fnmatch(pattern, string, flags & ~FNM_PERIOD))
+ return (0);
+ if (test == '/' && (flags & FNM_PATHNAME))
+ break;
+ ++string;
+ }
+ return (FNM_NOMATCH);
+ case '[':
+ if (*string == EOS)
+ return (FNM_NOMATCH);
+ if (*string == '/' && (flags & FNM_PATHNAME))
+ return (FNM_NOMATCH);
+ if (*string == '.' && (flags & FNM_PERIOD) &&
+ (string == stringstart ||
+ ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+ return (FNM_NOMATCH);
+
+ switch (rangematch(pattern, *string, flags, &newp)) {
+ case RANGE_ERROR:
+ /* not a good range, treat as normal text */
+ goto normal;
+ case RANGE_MATCH:
+ pattern = newp;
+ break;
+ case RANGE_NOMATCH:
+ return (FNM_NOMATCH);
+ }
+ ++string;
+ break;
+ case '\\':
+ if (!(flags & FNM_NOESCAPE)) {
+ if ((c = *pattern++) == EOS) {
+ c = '\\';
+ --pattern;
+ }
+ }
+ /* FALLTHROUGH */
+ default:
+ normal:
+ if (c != *string && !((flags & FNM_CASEFOLD) &&
+ (tolower((unsigned char)c) ==
+ tolower((unsigned char)*string))))
+ return (FNM_NOMATCH);
+ ++string;
+ break;
+ }
+ /* NOTREACHED */
+}
+
+static int
+rangematch(pattern, test, flags, newp)
+ const char *pattern;
+ char test;
+ int flags;
+ char **newp;
+{
+ int negate, ok;
+ char c, c2;
+
+ /*
+ * A bracket expression starting with an unquoted circumflex
+ * character produces unspecified results (IEEE 1003.2-1992,
+ * 3.13.2). This implementation treats it like '!', for
+ * consistency with the regular expression syntax.
+ * J.T. Conklin (conklin@ngai.kaleida.com)
+ */
+ if ((negate = (*pattern == '!' || *pattern == '^')))
+ ++pattern;
+
+ if (flags & FNM_CASEFOLD)
+ test = tolower((unsigned char)test);
+
+ /*
+ * A right bracket shall lose its special meaning and represent
+ * itself in a bracket expression if it occurs first in the list.
+ * -- POSIX.2 2.8.3.2
+ */
+ ok = 0;
+ c = *pattern++;
+ do {
+ if (c == '\\' && !(flags & FNM_NOESCAPE))
+ c = *pattern++;
+ if (c == EOS)
+ return (RANGE_ERROR);
+ if (c == '/' && (flags & FNM_PATHNAME))
+ return (RANGE_NOMATCH);
+ if ((flags & FNM_CASEFOLD))
+ c = tolower((unsigned char)c);
+ if (*pattern == '-'
+ && (c2 = *(pattern+1)) != EOS && c2 != ']') {
+ pattern += 2;
+ if (c2 == '\\' && !(flags & FNM_NOESCAPE))
+ c2 = *pattern++;
+ if (c2 == EOS)
+ return (RANGE_ERROR);
+ if (flags & FNM_CASEFOLD)
+ c2 = tolower((unsigned char)c2);
+ if (c <= test && test <= c2)
+ ok = 1;
+ } else if (c == test)
+ ok = 1;
+ } while ((c = *pattern++) != ']');
+
+ *newp = (char *)pattern;
+ return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH);
+}
diff --git a/libtar/gethostbyname_r.c b/libtar/gethostbyname_r.c
new file mode 100644
index 000000000..5264b8444
--- /dev/null
+++ b/libtar/gethostbyname_r.c
@@ -0,0 +1,41 @@
+/*
+** Copyright 2002 University of Illinois Board of Trustees
+** Copyright 2002 Mark D. Roth
+** All rights reserved.
+**
+** gethostbyname_r.c - gethostbyname_r() function for compatibility library
+**
+** Mark D. Roth <roth@uiuc.edu>
+** Campus Information Technologies and Educational Services
+** University of Illinois at Urbana-Champaign
+*/
+
+#include <config.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <netdb.h>
+
+
+int
+compat_gethostbyname_r(const char *name, struct hostent *hp,
+ char *buf, size_t buflen,
+ struct hostent **hpp, int *herr)
+{
+#if GETHOSTBYNAME_R_NUM_ARGS == 5
+ *hpp = gethostbyname_r(name, hp, buf, buflen, herr);
+
+ if (*hpp == NULL)
+ return -1;
+ return 0;
+#elif GETHOSTBYNAME_R_NUM_ARGS == 3
+ struct hostent_data hdata;
+
+ if (gethostbyname_r(name, hp, &hdata) == -1)
+ return -1;
+ *hpp = hp;
+ return 0;
+#endif /* GETHOSTBYNAME_R_NUM_ARGS == 5 */
+}
+
+
diff --git a/libtar/gethostname.c b/libtar/gethostname.c
new file mode 100644
index 000000000..1abaae124
--- /dev/null
+++ b/libtar/gethostname.c
@@ -0,0 +1,36 @@
+/* gethostname.c: minimal substitute for missing gethostname() function
+ * created 2000-Mar-02 jmk
+ * requires SVR4 uname() and -lc
+ *
+ * by Jim Knoble <jmknoble@pobox.com>
+ * Copyright ? 2000 Jim Knoble
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear in
+ * supporting documentation.
+ *
+ * This software is provided "as is", without warranty of any kind,
+ * express or implied, including but not limited to the warranties of
+ * merchantability, fitness for a particular purpose and
+ * noninfringement. In no event shall the author(s) be liable for any
+ * claim, damages or other liability, whether in an action of contract,
+ * tort or otherwise, arising from, out of or in connection with the
+ * software or the use or other dealings in the software.
+ */
+
+#include <string.h>
+#include <sys/utsname.h>
+
+int gethostname(char *name, size_t len)
+{
+ struct utsname u;
+ int status = uname(&u);
+ if (-1 != status) {
+ strncpy(name, u.nodename, len);
+ name[len - 1] = '\0';
+ }
+ return(status);
+}
+
diff --git a/libtar/getservbyname_r.c b/libtar/getservbyname_r.c
new file mode 100644
index 000000000..e386bc9f6
--- /dev/null
+++ b/libtar/getservbyname_r.c
@@ -0,0 +1,41 @@
+/*
+** Copyright 2002 University of Illinois Board of Trustees
+** Copyright 2002 Mark D. Roth
+** All rights reserved.
+**
+** getservbyname_r.c - getservbyname_r() function for compatibility library
+**
+** Mark D. Roth <roth@uiuc.edu>
+** Campus Information Technologies and Educational Services
+** University of Illinois at Urbana-Champaign
+*/
+
+#include <config.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <netdb.h>
+
+
+int
+compat_getservbyname_r(const char *name, const char *proto,
+ struct servent *sp, char *buf, size_t buflen,
+ struct servent **spp)
+{
+#if GETSERVBYNAME_R_NUM_ARGS == 5
+ *spp = getservbyname_r(name, proto, sp, buf, buflen);
+
+ if (*spp == NULL)
+ return -1;
+ return 0;
+#elif GETSERVBYNAME_R_NUM_ARGS == 4
+ struct servent_data sdata;
+
+ if (getservbyname_r(name, proto, sp, &sdata) == -1)
+ return -1;
+ *spp = sp;
+ return 0;
+#endif /* GETSERVBYNAME_R_NUM_ARGS == 5 */
+}
+
+
diff --git a/libtar/glob.c b/libtar/glob.c
new file mode 100644
index 000000000..9ee235a35
--- /dev/null
+++ b/libtar/glob.c
@@ -0,0 +1,873 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Guido van Rossum.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)glob.c 8.3 (Berkeley) 10/13/93";
+#else
+static char rcsid[] = "$OpenBSD: glob.c,v 1.8 1998/08/14 21:39:30 deraadt Exp $";
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * glob(3) -- a superset of the one defined in POSIX 1003.2.
+ *
+ * The [!...] convention to negate a range is supported (SysV, Posix, ksh).
+ *
+ * Optional extra services, controlled by flags not defined by POSIX:
+ *
+ * GLOB_QUOTE:
+ * Escaping convention: \ inhibits any special meaning the following
+ * character might have (except \ at end of string is retained).
+ * GLOB_MAGCHAR:
+ * Set in gl_flags if pattern contained a globbing character.
+ * GLOB_NOMAGIC:
+ * Same as GLOB_NOCHECK, but it will only append pattern if it did
+ * not contain any magic characters. [Used in csh style globbing]
+ * GLOB_ALTDIRFUNC:
+ * Use alternately specified directory access functions.
+ * GLOB_TILDE:
+ * expand ~user/foo to the /home/dir/of/user/foo
+ * GLOB_BRACE:
+ * expand {1,2}{a,b} to 1a 1b 2a 2b
+ * gl_matchc:
+ * Number of matches in the current invocation of glob.
+ */
+
+#include <config.h>
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stdio.h>
+
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <string.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include <compat.h>
+
+
+#define DOLLAR '$'
+#define DOT '.'
+#define EOS '\0'
+#define LBRACKET '['
+#define NOT '!'
+#define QUESTION '?'
+#define QUOTE '\\'
+#define RANGE '-'
+#define RBRACKET ']'
+#define SEP '/'
+#define STAR '*'
+#define TILDE '~'
+#define UNDERSCORE '_'
+#define LBRACE '{'
+#define RBRACE '}'
+#define SLASH '/'
+#define COMMA ','
+
+#ifndef DEBUG
+
+#define M_QUOTE 0x8000
+#define M_PROTECT 0x4000
+#define M_MASK 0xffff
+#define M_ASCII 0x00ff
+
+typedef u_short Char;
+
+#else
+
+#define M_QUOTE 0x80
+#define M_PROTECT 0x40
+#define M_MASK 0xff
+#define M_ASCII 0x7f
+
+typedef char Char;
+
+#endif
+
+
+#define CHAR(c) ((Char)((c)&M_ASCII))
+#define META(c) ((Char)((c)|M_QUOTE))
+#define M_ALL META('*')
+#define M_END META(']')
+#define M_NOT META('!')
+#define M_ONE META('?')
+#define M_RNG META('-')
+#define M_SET META('[')
+#define ismeta(c) (((c)&M_QUOTE) != 0)
+
+
+static int compare (const void *, const void *);
+static void g_Ctoc (const Char *, char *);
+static int g_lstat (Char *, struct stat *, glob_t *);
+static DIR *g_opendir (Char *, glob_t *);
+static Char *g_strchr (Char *, int);
+#ifdef notdef
+static Char *g_strcat (Char *, const Char *);
+#endif
+static int g_stat (Char *, struct stat *, glob_t *);
+static int glob0 (const Char *, glob_t *);
+static int glob1 (Char *, glob_t *);
+static int glob2 (Char *, Char *, Char *, glob_t *);
+static int glob3 (Char *, Char *, Char *, Char *, glob_t *);
+static int globextend (const Char *, glob_t *);
+static const Char * globtilde (const Char *, Char *, size_t, glob_t *);
+static int globexp1 (const Char *, glob_t *);
+static int globexp2 (const Char *, const Char *, glob_t *, int *);
+static int match (Char *, Char *, Char *);
+#ifdef DEBUG
+static void qprintf (const char *, Char *);
+#endif
+
+int
+openbsd_glob(pattern, flags, errfunc, pglob)
+ const char *pattern;
+ int flags, (*errfunc) __P((const char *, int));
+ glob_t *pglob;
+{
+ const u_char *patnext;
+ int c;
+ Char *bufnext, *bufend, patbuf[MAXPATHLEN+1];
+
+ patnext = (u_char *) pattern;
+ if (!(flags & GLOB_APPEND)) {
+ pglob->gl_pathc = 0;
+ pglob->gl_pathv = NULL;
+ if (!(flags & GLOB_DOOFFS))
+ pglob->gl_offs = 0;
+ }
+ pglob->gl_flags = flags & ~GLOB_MAGCHAR;
+ pglob->gl_errfunc = errfunc;
+ pglob->gl_matchc = 0;
+
+ bufnext = patbuf;
+ bufend = bufnext + MAXPATHLEN;
+ if (flags & GLOB_NOESCAPE)
+ while (bufnext < bufend && (c = *patnext++) != EOS)
+ *bufnext++ = c;
+ else {
+ /* Protect the quoted characters. */
+ while (bufnext < bufend && (c = *patnext++) != EOS)
+ if (c == QUOTE) {
+ if ((c = *patnext++) == EOS) {
+ c = QUOTE;
+ --patnext;
+ }
+ *bufnext++ = c | M_PROTECT;
+ }
+ else
+ *bufnext++ = c;
+ }
+ *bufnext = EOS;
+
+ if (flags & GLOB_BRACE)
+ return globexp1(patbuf, pglob);
+ else
+ return glob0(patbuf, pglob);
+}
+
+/*
+ * Expand recursively a glob {} pattern. When there is no more expansion
+ * invoke the standard globbing routine to glob the rest of the magic
+ * characters
+ */
+static int globexp1(pattern, pglob)
+ const Char *pattern;
+ glob_t *pglob;
+{
+ const Char* ptr = pattern;
+ int rv;
+
+ /* Protect a single {}, for find(1), like csh */
+ if (pattern[0] == LBRACE && pattern[1] == RBRACE && pattern[2] == EOS)
+ return glob0(pattern, pglob);
+
+ while ((ptr = (const Char *) g_strchr((Char *) ptr, LBRACE)) != NULL)
+ if (!globexp2(ptr, pattern, pglob, &rv))
+ return rv;
+
+ return glob0(pattern, pglob);
+}
+
+
+/*
+ * Recursive brace globbing helper. Tries to expand a single brace.
+ * If it succeeds then it invokes globexp1 with the new pattern.
+ * If it fails then it tries to glob the rest of the pattern and returns.
+ */
+static int globexp2(ptr, pattern, pglob, rv)
+ const Char *ptr, *pattern;
+ glob_t *pglob;
+ int *rv;
+{
+ int i;
+ Char *lm, *ls;
+ const Char *pe, *pm, *pl;
+ Char patbuf[MAXPATHLEN + 1];
+
+ /* copy part up to the brace */
+ for (lm = patbuf, pm = pattern; pm != ptr; *lm++ = *pm++)
+ continue;
+ ls = lm;
+
+ /* Find the balanced brace */
+ for (i = 0, pe = ++ptr; *pe; pe++)
+ if (*pe == LBRACKET) {
+ /* Ignore everything between [] */
+ for (pm = pe++; *pe != RBRACKET && *pe != EOS; pe++)
+ continue;
+ if (*pe == EOS) {
+ /*
+ * We could not find a matching RBRACKET.
+ * Ignore and just look for RBRACE
+ */
+ pe = pm;
+ }
+ }
+ else if (*pe == LBRACE)
+ i++;
+ else if (*pe == RBRACE) {
+ if (i == 0)
+ break;
+ i--;
+ }
+
+ /* Non matching braces; just glob the pattern */
+ if (i != 0 || *pe == EOS) {
+ *rv = glob0(patbuf, pglob);
+ return 0;
+ }
+
+ for (i = 0, pl = pm = ptr; pm <= pe; pm++)
+ switch (*pm) {
+ case LBRACKET:
+ /* Ignore everything between [] */
+ for (pl = pm++; *pm != RBRACKET && *pm != EOS; pm++)
+ continue;
+ if (*pm == EOS) {
+ /*
+ * We could not find a matching RBRACKET.
+ * Ignore and just look for RBRACE
+ */
+ pm = pl;
+ }
+ break;
+
+ case LBRACE:
+ i++;
+ break;
+
+ case RBRACE:
+ if (i) {
+ i--;
+ break;
+ }
+ /* FALLTHROUGH */
+ case COMMA:
+ if (i && *pm == COMMA)
+ break;
+ else {
+ /* Append the current string */
+ for (lm = ls; (pl < pm); *lm++ = *pl++)
+ continue;
+ /*
+ * Append the rest of the pattern after the
+ * closing brace
+ */
+ for (pl = pe + 1; (*lm++ = *pl++) != EOS;)
+ continue;
+
+ /* Expand the current pattern */
+#ifdef DEBUG
+ qprintf("globexp2:", patbuf);
+#endif
+ *rv = globexp1(patbuf, pglob);
+
+ /* move after the comma, to the next string */
+ pl = pm + 1;
+ }
+ break;
+
+ default:
+ break;
+ }
+ *rv = 0;
+ return 0;
+}
+
+
+
+/*
+ * expand tilde from the passwd file.
+ */
+static const Char *
+globtilde(pattern, patbuf, patbuf_len, pglob)
+ const Char *pattern;
+ Char *patbuf;
+ size_t patbuf_len;
+ glob_t *pglob;
+{
+ struct passwd *pwd;
+ char *h;
+ const Char *p;
+ Char *b, *eb;
+
+ if (*pattern != TILDE || !(pglob->gl_flags & GLOB_TILDE))
+ return pattern;
+
+ /* Copy up to the end of the string or / */
+ eb = &patbuf[patbuf_len - 1];
+ for (p = pattern + 1, h = (char *) patbuf;
+ h < (char *)eb && *p && *p != SLASH; *h++ = *p++)
+ continue;
+
+ *h = EOS;
+
+ if (((char *) patbuf)[0] == EOS) {
+ /*
+ * handle a plain ~ or ~/ by expanding $HOME
+ * first and then trying the password file
+ */
+#ifdef HAVE_ISSETUGID
+ if (issetugid() != 0 || (h = getenv("HOME")) == NULL) {
+#endif
+ if ((pwd = getpwuid(getuid())) == NULL)
+ return pattern;
+ else
+ h = pwd->pw_dir;
+#ifdef HAVE_ISSETUGID
+ }
+#endif
+ }
+ else {
+ /*
+ * Expand a ~user
+ */
+ if ((pwd = getpwnam((char*) patbuf)) == NULL)
+ return pattern;
+ else
+ h = pwd->pw_dir;
+ }
+
+ /* Copy the home directory */
+ for (b = patbuf; b < eb && *h; *b++ = *h++)
+ continue;
+
+ /* Append the rest of the pattern */
+ while (b < eb && (*b++ = *p++) != EOS)
+ continue;
+ *b = EOS;
+
+ return patbuf;
+}
+
+
+/*
+ * The main glob() routine: compiles the pattern (optionally processing
+ * quotes), calls glob1() to do the real pattern matching, and finally
+ * sorts the list (unless unsorted operation is requested). Returns 0
+ * if things went well, nonzero if errors occurred. It is not an error
+ * to find no matches.
+ */
+static int
+glob0(pattern, pglob)
+ const Char *pattern;
+ glob_t *pglob;
+{
+ const Char *qpatnext;
+ int c, err, oldpathc;
+ Char *bufnext, patbuf[MAXPATHLEN+1];
+
+ qpatnext = globtilde(pattern, patbuf, sizeof(patbuf) / sizeof(Char),
+ pglob);
+ oldpathc = pglob->gl_pathc;
+ bufnext = patbuf;
+
+ /* We don't need to check for buffer overflow any more. */
+ while ((c = *qpatnext++) != EOS) {
+ switch (c) {
+ case LBRACKET:
+ c = *qpatnext;
+ if (c == NOT)
+ ++qpatnext;
+ if (*qpatnext == EOS ||
+ g_strchr((Char *) qpatnext+1, RBRACKET) == NULL) {
+ *bufnext++ = LBRACKET;
+ if (c == NOT)
+ --qpatnext;
+ break;
+ }
+ *bufnext++ = M_SET;
+ if (c == NOT)
+ *bufnext++ = M_NOT;
+ c = *qpatnext++;
+ do {
+ *bufnext++ = CHAR(c);
+ if (*qpatnext == RANGE &&
+ (c = qpatnext[1]) != RBRACKET) {
+ *bufnext++ = M_RNG;
+ *bufnext++ = CHAR(c);
+ qpatnext += 2;
+ }
+ } while ((c = *qpatnext++) != RBRACKET);
+ pglob->gl_flags |= GLOB_MAGCHAR;
+ *bufnext++ = M_END;
+ break;
+ case QUESTION:
+ pglob->gl_flags |= GLOB_MAGCHAR;
+ *bufnext++ = M_ONE;
+ break;
+ case STAR:
+ pglob->gl_flags |= GLOB_MAGCHAR;
+ /* collapse adjacent stars to one,
+ * to avoid exponential behavior
+ */
+ if (bufnext == patbuf || bufnext[-1] != M_ALL)
+ *bufnext++ = M_ALL;
+ break;
+ default:
+ *bufnext++ = CHAR(c);
+ break;
+ }
+ }
+ *bufnext = EOS;
+#ifdef DEBUG
+ qprintf("glob0:", patbuf);
+#endif
+
+ if ((err = glob1(patbuf, pglob)) != 0)
+ return(err);
+
+ /*
+ * If there was no match we are going to append the pattern
+ * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified
+ * and the pattern did not contain any magic characters
+ * GLOB_NOMAGIC is there just for compatibility with csh.
+ */
+ if (pglob->gl_pathc == oldpathc) {
+ if ((pglob->gl_flags & GLOB_NOCHECK) ||
+ ((pglob->gl_flags & GLOB_NOMAGIC) &&
+ !(pglob->gl_flags & GLOB_MAGCHAR)))
+ return(globextend(pattern, pglob));
+ else
+ return(GLOB_NOMATCH);
+ }
+ if (!(pglob->gl_flags & GLOB_NOSORT))
+ qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc,
+ pglob->gl_pathc - oldpathc, sizeof(char *), compare);
+ return(0);
+}
+
+static int
+compare(p, q)
+ const void *p, *q;
+{
+ return(strcmp(*(char **)p, *(char **)q));
+}
+
+static int
+glob1(pattern, pglob)
+ Char *pattern;
+ glob_t *pglob;
+{
+ Char pathbuf[MAXPATHLEN+1];
+
+ /* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */
+ if (*pattern == EOS)
+ return(0);
+ return(glob2(pathbuf, pathbuf, pattern, pglob));
+}
+
+/*
+ * The functions glob2 and glob3 are mutually recursive; there is one level
+ * of recursion for each segment in the pattern that contains one or more
+ * meta characters.
+ */
+static int
+glob2(pathbuf, pathend, pattern, pglob)
+ Char *pathbuf, *pathend, *pattern;
+ glob_t *pglob;
+{
+ struct stat sb;
+ Char *p, *q;
+ int anymeta;
+
+ /*
+ * Loop over pattern segments until end of pattern or until
+ * segment with meta character found.
+ */
+ for (anymeta = 0;;) {
+ if (*pattern == EOS) { /* End of pattern? */
+ *pathend = EOS;
+ if (g_lstat(pathbuf, &sb, pglob))
+ return(0);
+
+ if (((pglob->gl_flags & GLOB_MARK) &&
+ pathend[-1] != SEP) && (S_ISDIR(sb.st_mode)
+ || (S_ISLNK(sb.st_mode) &&
+ (g_stat(pathbuf, &sb, pglob) == 0) &&
+ S_ISDIR(sb.st_mode)))) {
+ *pathend++ = SEP;
+ *pathend = EOS;
+ }
+ ++pglob->gl_matchc;
+ return(globextend(pathbuf, pglob));
+ }
+
+ /* Find end of next segment, copy tentatively to pathend. */
+ q = pathend;
+ p = pattern;
+ while (*p != EOS && *p != SEP) {
+ if (ismeta(*p))
+ anymeta = 1;
+ *q++ = *p++;
+ }
+
+ if (!anymeta) { /* No expansion, do next segment. */
+ pathend = q;
+ pattern = p;
+ while (*pattern == SEP)
+ *pathend++ = *pattern++;
+ } else /* Need expansion, recurse. */
+ return(glob3(pathbuf, pathend, pattern, p, pglob));
+ }
+ /* NOTREACHED */
+}
+
+static int
+glob3(pathbuf, pathend, pattern, restpattern, pglob)
+ Char *pathbuf, *pathend, *pattern, *restpattern;
+ glob_t *pglob;
+{
+ register struct dirent *dp;
+ DIR *dirp;
+ int err;
+ char buf[MAXPATHLEN];
+
+ /*
+ * The readdirfunc declaration can't be prototyped, because it is
+ * assigned, below, to two functions which are prototyped in glob.h
+ * and dirent.h as taking pointers to differently typed opaque
+ * structures.
+ */
+ struct dirent *(*readdirfunc)();
+
+ *pathend = EOS;
+ errno = 0;
+
+ if ((dirp = g_opendir(pathbuf, pglob)) == NULL) {
+ /* TODO: don't call for ENOENT or ENOTDIR? */
+ if (pglob->gl_errfunc) {
+ g_Ctoc(pathbuf, buf);
+ if (pglob->gl_errfunc(buf, errno) ||
+ pglob->gl_flags & GLOB_ERR)
+ return (GLOB_ABORTED);
+ }
+ return(0);
+ }
+
+ err = 0;
+
+ /* Search directory for matching names. */
+ if (pglob->gl_flags & GLOB_ALTDIRFUNC)
+ readdirfunc = pglob->gl_readdir;
+ else
+ readdirfunc = readdir;
+ while ((dp = (*readdirfunc)(dirp))) {
+ register u_char *sc;
+ register Char *dc;
+
+ /* Initial DOT must be matched literally. */
+ if (dp->d_name[0] == DOT && *pattern != DOT)
+ continue;
+ for (sc = (u_char *) dp->d_name, dc = pathend;
+ (*dc++ = *sc++) != EOS;)
+ continue;
+ if (!match(pathend, pattern, restpattern)) {
+ *pathend = EOS;
+ continue;
+ }
+ err = glob2(pathbuf, --dc, restpattern, pglob);
+ if (err)
+ break;
+ }
+
+ if (pglob->gl_flags & GLOB_ALTDIRFUNC)
+ (*pglob->gl_closedir)(dirp);
+ else
+ closedir(dirp);
+ return(err);
+}
+
+
+/*
+ * Extend the gl_pathv member of a glob_t structure to accomodate a new item,
+ * add the new item, and update gl_pathc.
+ *
+ * This assumes the BSD realloc, which only copies the block when its size
+ * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic
+ * behavior.
+ *
+ * Return 0 if new item added, error code if memory couldn't be allocated.
+ *
+ * Invariant of the glob_t structure:
+ * Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and
+ * gl_pathv points to (gl_offs + gl_pathc + 1) items.
+ */
+static int
+globextend(path, pglob)
+ const Char *path;
+ glob_t *pglob;
+{
+ register char **pathv;
+ register int i;
+ u_int newsize;
+ char *copy;
+ const Char *p;
+
+ newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs);
+ pathv = pglob->gl_pathv ?
+ realloc((char *)pglob->gl_pathv, newsize) :
+ malloc(newsize);
+ if (pathv == NULL) {
+ if (pglob->gl_pathv)
+ free(pglob->gl_pathv);
+ return(GLOB_NOSPACE);
+ }
+
+ if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) {
+ /* first time around -- clear initial gl_offs items */
+ pathv += pglob->gl_offs;
+ for (i = pglob->gl_offs; --i >= 0; )
+ *--pathv = NULL;
+ }
+ pglob->gl_pathv = pathv;
+
+ for (p = path; *p++;)
+ continue;
+ if ((copy = malloc(p - path)) != NULL) {
+ g_Ctoc(path, copy);
+ pathv[pglob->gl_offs + pglob->gl_pathc++] = copy;
+ }
+ pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
+ return(copy == NULL ? GLOB_NOSPACE : 0);
+}
+
+
+/*
+ * pattern matching function for filenames. Each occurrence of the *
+ * pattern causes a recursion level.
+ */
+static int
+match(name, pat, patend)
+ register Char *name, *pat, *patend;
+{
+ int ok, negate_range;
+ Char c, k;
+
+ while (pat < patend) {
+ c = *pat++;
+ switch (c & M_MASK) {
+ case M_ALL:
+ if (pat == patend)
+ return(1);
+ do
+ if (match(name, pat, patend))
+ return(1);
+ while (*name++ != EOS);
+ return(0);
+ case M_ONE:
+ if (*name++ == EOS)
+ return(0);
+ break;
+ case M_SET:
+ ok = 0;
+ if ((k = *name++) == EOS)
+ return(0);
+ if ((negate_range = ((*pat & M_MASK) == M_NOT)) != EOS)
+ ++pat;
+ while (((c = *pat++) & M_MASK) != M_END)
+ if ((*pat & M_MASK) == M_RNG) {
+ if (c <= k && k <= pat[1])
+ ok = 1;
+ pat += 2;
+ } else if (c == k)
+ ok = 1;
+ if (ok == negate_range)
+ return(0);
+ break;
+ default:
+ if (*name++ != c)
+ return(0);
+ break;
+ }
+ }
+ return(*name == EOS);
+}
+
+/* Free allocated data belonging to a glob_t structure. */
+void
+openbsd_globfree(pglob)
+ glob_t *pglob;
+{
+ register int i;
+ register char **pp;
+
+ if (pglob->gl_pathv != NULL) {
+ pp = pglob->gl_pathv + pglob->gl_offs;
+ for (i = pglob->gl_pathc; i--; ++pp)
+ if (*pp)
+ free(*pp);
+ free(pglob->gl_pathv);
+ }
+}
+
+static DIR *
+g_opendir(str, pglob)
+ register Char *str;
+ glob_t *pglob;
+{
+ char buf[MAXPATHLEN];
+
+ if (!*str)
+ strcpy(buf, ".");
+ else
+ g_Ctoc(str, buf);
+
+ if (pglob->gl_flags & GLOB_ALTDIRFUNC)
+ return((*pglob->gl_opendir)(buf));
+
+ return(opendir(buf));
+}
+
+static int
+g_lstat(fn, sb, pglob)
+ register Char *fn;
+ struct stat *sb;
+ glob_t *pglob;
+{
+ char buf[MAXPATHLEN];
+
+ g_Ctoc(fn, buf);
+ if (pglob->gl_flags & GLOB_ALTDIRFUNC)
+ return((*pglob->gl_lstat)(buf, sb));
+ return(lstat(buf, sb));
+}
+
+static int
+g_stat(fn, sb, pglob)
+ register Char *fn;
+ struct stat *sb;
+ glob_t *pglob;
+{
+ char buf[MAXPATHLEN];
+
+ g_Ctoc(fn, buf);
+ if (pglob->gl_flags & GLOB_ALTDIRFUNC)
+ return((*pglob->gl_stat)(buf, sb));
+ return(stat(buf, sb));
+}
+
+static Char *
+g_strchr(str, ch)
+ Char *str;
+ int ch;
+{
+ do {
+ if (*str == ch)
+ return (str);
+ } while (*str++);
+ return (NULL);
+}
+
+#ifdef notdef
+static Char *
+g_strcat(dst, src)
+ Char *dst;
+ const Char* src;
+{
+ Char *sdst = dst;
+
+ while (*dst++)
+ continue;
+ --dst;
+ while((*dst++ = *src++) != EOS)
+ continue;
+
+ return (sdst);
+}
+#endif
+
+static void
+g_Ctoc(str, buf)
+ register const Char *str;
+ char *buf;
+{
+ register char *dc;
+
+ for (dc = buf; (*dc++ = *str++) != EOS;)
+ continue;
+}
+
+#ifdef DEBUG
+static void
+qprintf(str, s)
+ const char *str;
+ register Char *s;
+{
+ register Char *p;
+
+ (void)printf("%s:\n", str);
+ for (p = s; *p; p++)
+ (void)printf("%c", CHAR(*p));
+ (void)printf("\n");
+ for (p = s; *p; p++)
+ (void)printf("%c", *p & M_PROTECT ? '"' : ' ');
+ (void)printf("\n");
+ for (p = s; *p; p++)
+ (void)printf("%c", ismeta(*p) ? '_' : ' ');
+ (void)printf("\n");
+}
+#endif
diff --git a/libtar/handle.c b/libtar/handle.c
new file mode 100644
index 000000000..a26c094cc
--- /dev/null
+++ b/libtar/handle.c
@@ -0,0 +1,132 @@
+/*
+** Copyright 1998-2003 University of Illinois Board of Trustees
+** Copyright 1998-2003 Mark D. Roth
+** All rights reserved.
+**
+** handle.c - libtar code for initializing a TAR handle
+**
+** Mark D. Roth <roth@uiuc.edu>
+** Campus Information Technologies and Educational Services
+** University of Illinois at Urbana-Champaign
+*/
+
+#include <internal.h>
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+#endif
+
+
+const char libtar_version[] = PACKAGE_VERSION;
+
+static tartype_t default_type = { open, close, read, write };
+
+
+static int
+tar_init(TAR **t, const char *pathname, tartype_t *type,
+ int oflags, int mode __unused, int options)
+{
+ if ((oflags & O_ACCMODE) == O_RDWR)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ *t = (TAR *)calloc(1, sizeof(TAR));
+ if (*t == NULL)
+ return -1;
+
+ (*t)->pathname = pathname;
+ (*t)->options = options;
+ (*t)->type = (type ? type : &default_type);
+ (*t)->oflags = oflags;
+
+ if ((oflags & O_ACCMODE) == O_RDONLY)
+ (*t)->h = libtar_hash_new(256,
+ (libtar_hashfunc_t)path_hashfunc);
+ else
+ (*t)->h = libtar_hash_new(16, (libtar_hashfunc_t)dev_hash);
+ if ((*t)->h == NULL)
+ {
+ free(*t);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/* open a new tarfile handle */
+int
+tar_open(TAR **t, const char *pathname, tartype_t *type,
+ int oflags, int mode, int options)
+{
+ if (tar_init(t, pathname, type, oflags, mode, options) == -1)
+ return -1;
+
+ if ((options & TAR_NOOVERWRITE) && (oflags & O_CREAT))
+ oflags |= O_EXCL;
+
+#ifdef O_BINARY
+ oflags |= O_BINARY;
+#endif
+
+ (*t)->fd = (*((*t)->type->openfunc))(pathname, oflags, mode);
+ if ((*t)->fd == -1)
+ {
+ libtar_hash_free((*t)->h, NULL);
+ free(*t);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int
+tar_fdopen(TAR **t, int fd, const char *pathname, tartype_t *type,
+ int oflags, int mode, int options)
+{
+ if (tar_init(t, pathname, type, oflags, mode, options) == -1)
+ return -1;
+
+ (*t)->fd = fd;
+ return 0;
+}
+
+
+int
+tar_fd(TAR *t)
+{
+ return t->fd;
+}
+
+
+/* close tarfile handle */
+int
+tar_close(TAR *t)
+{
+ int i;
+
+ i = (*(t->type->closefunc))(t->fd);
+
+ if (t->h != NULL)
+ libtar_hash_free(t->h, ((t->oflags & O_ACCMODE) == O_RDONLY
+ ? free
+ : (libtar_freefunc_t)tar_dev_free));
+ if (t->th_pathname != NULL)
+ free(t->th_pathname);
+ free(t);
+
+ return i;
+}
+
+
diff --git a/libtar/inet_aton.c b/libtar/inet_aton.c
new file mode 100644
index 000000000..a935d5a6b
--- /dev/null
+++ b/libtar/inet_aton.c
@@ -0,0 +1,27 @@
+/*
+** Copyright 2002 University of Illinois Board of Trustees
+** Copyright 2002 Mark D. Roth
+** All rights reserved.
+**
+** inet_aton.c - inet_aton() function for compatibility library
+**
+** Mark D. Roth <roth@uiuc.edu>
+** Campus Information Technologies and Educational Services
+** University of Illinois at Urbana-Champaign
+*/
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+
+int
+inet_aton(const char *cp, struct in_addr *inp)
+{
+ inp->s_addr = inet_addr(cp);
+ if (inp->s_addr == -1)
+ return 0;
+ return 1;
+}
+
+
diff --git a/libtar/internal.h b/libtar/internal.h
new file mode 100644
index 000000000..23243d296
--- /dev/null
+++ b/libtar/internal.h
@@ -0,0 +1,17 @@
+/*
+** Copyright 2002-2003 University of Illinois Board of Trustees
+** Copyright 2002-2003 Mark D. Roth
+** All rights reserved.
+**
+** internal.h - internal header file for libtar
+**
+** Mark D. Roth <roth@uiuc.edu>
+** Campus Information Technologies and Educational Services
+** University of Illinois at Urbana-Champaign
+*/
+
+#include <config.h>
+#include <compat.h>
+
+#include <libtar.h>
+
diff --git a/libtar/libtar.h b/libtar/libtar.h
new file mode 100644
index 000000000..aa637b13a
--- /dev/null
+++ b/libtar/libtar.h
@@ -0,0 +1,352 @@
+/*
+** Copyright 1998-2003 University of Illinois Board of Trustees
+** Copyright 1998-2003 Mark D. Roth
+** All rights reserved.
+**
+** libtar.h - header file for libtar library
+**
+** Mark D. Roth <roth@uiuc.edu>
+** Campus Information Technologies and Educational Services
+** University of Illinois at Urbana-Champaign
+*/
+
+#ifndef LIBTAR_H
+#define LIBTAR_H
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <linux/capability.h>
+#include "tar.h"
+
+#include "libtar_listhash.h"
+
+#ifdef HAVE_EXT4_CRYPT
+# include "ext4crypt_tar.h"
+#endif
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+/* useful constants */
+/* see FIXME note in block.c regarding T_BLOCKSIZE */
+#define T_BLOCKSIZE 512
+#define T_NAMELEN 100
+#define T_PREFIXLEN 155
+#define T_MAXPATHLEN (T_NAMELEN + T_PREFIXLEN)
+
+/* GNU extensions for typeflag */
+#define GNU_LONGNAME_TYPE 'L'
+#define GNU_LONGLINK_TYPE 'K'
+
+/* extended metadata for next file - used to store selinux_context */
+#define TH_EXT_TYPE 'x'
+#define TH_POL_TYPE_DO_NOT_USE 'p'
+
+/* our version of the tar header structure */
+struct tar_header
+{
+ char name[100];
+ char mode[8];
+ char uid[8];
+ char gid[8];
+ char size[12];
+ char mtime[12];
+ char chksum[8];
+ char typeflag;
+ char linkname[100];
+ char magic[6];
+ char version[2];
+ char uname[32];
+ char gname[32];
+ char devmajor[8];
+ char devminor[8];
+ char prefix[155];
+ char padding[12];
+ char *gnu_longname;
+ char *gnu_longlink;
+ char *selinux_context;
+#ifdef HAVE_EXT4_CRYPT
+ struct ext4_encryption_policy *eep;
+#endif
+ int has_cap_data;
+ struct vfs_cap_data cap_data;
+ int has_user_default;
+ int has_user_cache;
+ int has_user_code_cache;
+};
+
+
+/***** handle.c ************************************************************/
+
+typedef int (*openfunc_t)(const char *, int, ...);
+typedef int (*closefunc_t)(int);
+typedef ssize_t (*readfunc_t)(int, void *, size_t);
+typedef ssize_t (*writefunc_t)(int, const void *, size_t);
+
+typedef struct
+{
+ openfunc_t openfunc;
+ closefunc_t closefunc;
+ readfunc_t readfunc;
+ writefunc_t writefunc;
+}
+tartype_t;
+
+typedef struct
+{
+ tartype_t *type;
+ const char *pathname;
+ long fd;
+ int oflags;
+ int options;
+ struct tar_header th_buf;
+ libtar_hash_t *h;
+
+ /* introduced in libtar 1.2.21 */
+ char *th_pathname;
+}
+TAR;
+
+/* constant values for the TAR options field */
+#define TAR_GNU 1 /* use GNU extensions */
+#define TAR_VERBOSE 2 /* output file info to stdout */
+#define TAR_NOOVERWRITE 4 /* don't overwrite existing files */
+#define TAR_IGNORE_EOT 8 /* ignore double zero blocks as EOF */
+#define TAR_CHECK_MAGIC 16 /* check magic in file header */
+#define TAR_CHECK_VERSION 32 /* check version in file header */
+#define TAR_IGNORE_CRC 64 /* ignore CRC in file header */
+#define TAR_STORE_SELINUX 128 /* store selinux context */
+#define TAR_USE_NUMERIC_ID 256 /* favor numeric owner over names */
+#define TAR_STORE_EXT4_POL 512 /* store ext4 crypto policy */
+#define TAR_STORE_POSIX_CAP 1024 /* store posix file capabilities */
+#define TAR_STORE_ANDROID_USER_XATTR 2048 /* store android user.* xattr */
+
+/* this is obsolete - it's here for backwards-compatibility only */
+#define TAR_IGNORE_MAGIC 0
+
+extern const char libtar_version[];
+
+
+/* open a new tarfile handle */
+int tar_open(TAR **t, const char *pathname, tartype_t *type,
+ int oflags, int mode, int options);
+
+/* make a tarfile handle out of a previously-opened descriptor */
+int tar_fdopen(TAR **t, int fd, const char *pathname, tartype_t *type,
+ int oflags, int mode, int options);
+
+/* returns the descriptor associated with t */
+int tar_fd(TAR *t);
+
+/* close tarfile handle */
+int tar_close(TAR *t);
+
+
+/***** append.c ************************************************************/
+
+/* forward declaration to appease the compiler */
+struct tar_dev;
+
+/* cleanup function */
+void tar_dev_free(struct tar_dev *tdp);
+
+/* Appends a file to the tar archive.
+ * Arguments:
+ * t = TAR handle to append to
+ * realname = path of file to append
+ * savename = name to save the file under in the archive
+ */
+int tar_append_file(TAR *t, const char *realname, const char *savename);
+
+/* write EOF indicator */
+int tar_append_eof(TAR *t);
+
+/* add file contents to a tarchive */
+int tar_append_regfile(TAR *t, const char *realname);
+
+/* Appends in-memory file contents to a tarchive.
+ * Arguments:
+ * t = TAR handle to append to
+ * savename = name to save the file under in the archive
+ * mode = mode
+ * uid, gid = owner
+ * buf, len = in-memory buffer
+ */
+int tar_append_file_contents(TAR *t, const char *savename, mode_t mode,
+ uid_t uid, gid_t gid, void *buf, size_t len);
+
+/* add buffer to a tarchive */
+int tar_append_buffer(TAR *t, void *buf, size_t len);
+
+/***** block.c *************************************************************/
+
+/* macros for reading/writing tarchive blocks */
+#define tar_block_read(t, buf) \
+ (*((t)->type->readfunc))((t)->fd, (char *)(buf), T_BLOCKSIZE)
+#define tar_block_write(t, buf) \
+ (*((t)->type->writefunc))((t)->fd, (char *)(buf), T_BLOCKSIZE)
+
+/* read/write a header block */
+int th_read(TAR *t);
+int th_write(TAR *t);
+
+
+/***** decode.c ************************************************************/
+
+/* determine file type */
+#define TH_ISREG(t) ((t)->th_buf.typeflag == REGTYPE \
+ || (t)->th_buf.typeflag == AREGTYPE \
+ || (t)->th_buf.typeflag == CONTTYPE \
+ || (S_ISREG((mode_t)oct_to_int((t)->th_buf.mode, sizeof((t)->th_buf.mode))) \
+ && (t)->th_buf.typeflag != LNKTYPE))
+#define TH_ISLNK(t) ((t)->th_buf.typeflag == LNKTYPE)
+#define TH_ISSYM(t) ((t)->th_buf.typeflag == SYMTYPE \
+ || S_ISLNK((mode_t)oct_to_int((t)->th_buf.mode, sizeof((t)->th_buf.mode))))
+#define TH_ISCHR(t) ((t)->th_buf.typeflag == CHRTYPE \
+ || S_ISCHR((mode_t)oct_to_int((t)->th_buf.mode, sizeof((t)->th_buf.mode))))
+#define TH_ISBLK(t) ((t)->th_buf.typeflag == BLKTYPE \
+ || S_ISBLK((mode_t)oct_to_int((t)->th_buf.mode, sizeof((t)->th_buf.mode))))
+#define TH_ISDIR(t) ((t)->th_buf.typeflag == DIRTYPE \
+ || S_ISDIR((mode_t)oct_to_int((t)->th_buf.mode, sizeof((t)->th_buf.mode))) \
+ || ((t)->th_buf.typeflag == AREGTYPE \
+ && strnlen((t)->th_buf.name, T_NAMELEN) \
+ && ((t)->th_buf.name[strnlen((t)->th_buf.name, T_NAMELEN) - 1] == '/')))
+#define TH_ISFIFO(t) ((t)->th_buf.typeflag == FIFOTYPE \
+ || S_ISFIFO((mode_t)oct_to_int((t)->th_buf.mode, sizeof((t)->th_buf.mode))))
+#define TH_ISLONGNAME(t) ((t)->th_buf.typeflag == GNU_LONGNAME_TYPE)
+#define TH_ISLONGLINK(t) ((t)->th_buf.typeflag == GNU_LONGLINK_TYPE)
+#define TH_ISEXTHEADER(t) ((t)->th_buf.typeflag == TH_EXT_TYPE)
+#define TH_ISPOLHEADER(t) ((t)->th_buf.typeflag == TH_POL_TYPE_DO_NOT_USE)
+
+/* decode tar header info */
+#define th_get_crc(t) oct_to_int((t)->th_buf.chksum, sizeof((t)->th_buf.chksum))
+#define th_get_size(t) oct_to_int_ex((t)->th_buf.size, sizeof((t)->th_buf.size))
+#define th_get_mtime(t) oct_to_int_ex((t)->th_buf.mtime, sizeof((t)->th_buf.mtime))
+#define th_get_devmajor(t) oct_to_int((t)->th_buf.devmajor, sizeof((t)->th_buf.devmajor))
+#define th_get_devminor(t) oct_to_int((t)->th_buf.devminor, sizeof((t)->th_buf.devminor))
+#define th_get_linkname(t) ((t)->th_buf.gnu_longlink \
+ ? (t)->th_buf.gnu_longlink \
+ : (t)->th_buf.linkname)
+char *th_get_pathname(TAR *t);
+mode_t th_get_mode(TAR *t);
+uid_t th_get_uid(TAR *t);
+gid_t th_get_gid(TAR *t);
+
+
+/***** encode.c ************************************************************/
+
+/* encode file info in th_header */
+void th_set_type(TAR *t, mode_t mode);
+void th_set_path(TAR *t, const char *pathname);
+void th_set_link(TAR *t, const char *linkname);
+void th_set_device(TAR *t, dev_t device);
+void th_set_user(TAR *t, uid_t uid);
+void th_set_group(TAR *t, gid_t gid);
+void th_set_mode(TAR *t, mode_t fmode);
+#define th_set_mtime(t, fmtime) \
+ int_to_oct_ex((fmtime), (t)->th_buf.mtime, sizeof((t)->th_buf.mtime))
+#define th_set_size(t, fsize) \
+ int_to_oct_ex((fsize), (t)->th_buf.size, sizeof((t)->th_buf.size))
+
+/* encode everything at once (except the pathname and linkname) */
+void th_set_from_stat(TAR *t, struct stat *s);
+
+/* encode magic, version, and crc - must be done after everything else is set */
+void th_finish(TAR *t);
+
+
+/***** extract.c ***********************************************************/
+
+/* sequentially extract next file from t */
+int tar_extract_file(TAR *t, const char *realname, const char *prefix, const int *progress_fd);
+
+/* extract different file types */
+int tar_extract_dir(TAR *t, const char *realname);
+int tar_extract_hardlink(TAR *t, const char *realname, const char *prefix);
+int tar_extract_symlink(TAR *t, const char *realname);
+int tar_extract_chardev(TAR *t, const char *realname);
+int tar_extract_blockdev(TAR *t, const char *realname);
+int tar_extract_fifo(TAR *t, const char *realname);
+
+/* for regfiles, we need to extract the content blocks as well */
+int tar_extract_regfile(TAR *t, const char *realname, const int *progress_fd);
+int tar_skip_regfile(TAR *t);
+
+/* extract regfile to buffer */
+int tar_extract_file_contents(TAR *t, void *buf, size_t *lenp);
+
+/***** output.c ************************************************************/
+
+/* print the tar header */
+void th_print(TAR *t);
+
+/* print "ls -l"-like output for the file described by th */
+void th_print_long_ls(TAR *t);
+
+
+/***** util.c *************************************************************/
+
+/* hashing function for pathnames */
+int path_hashfunc(char *key, int numbuckets);
+
+/* matching function for dev_t's */
+int dev_match(dev_t *dev1, dev_t *dev2);
+
+/* matching function for ino_t's */
+int ino_match(ino_t *ino1, ino_t *ino2);
+
+/* hashing function for dev_t's */
+int dev_hash(dev_t *dev);
+
+/* hashing function for ino_t's */
+int ino_hash(ino_t *inode);
+
+/* create any necessary dirs */
+int mkdirhier(char *path);
+
+/* calculate header checksum */
+int th_crc_calc(TAR *t);
+
+/* calculate a signed header checksum */
+int th_signed_crc_calc(TAR *t);
+
+/* compare checksums in a forgiving way */
+#define th_crc_ok(t) (th_get_crc(t) == th_crc_calc(t) || th_get_crc(t) == th_signed_crc_calc(t))
+
+/* string-octal to integer conversion */
+int64_t oct_to_int(char *oct, size_t len);
+
+/* string-octal or binary to integer conversion */
+int64_t oct_to_int_ex(char *oct, size_t len);
+
+/* integer to NULL-terminated string-octal conversion */
+void int_to_oct(int64_t num, char *oct, size_t octlen);
+
+/* integer to string-octal conversion, or binary as necessary */
+void int_to_oct_ex(int64_t num, char *oct, size_t octlen);
+
+/* prints posix file capabilities */
+void print_caps(struct vfs_cap_data *cap_data);
+
+
+/***** wrapper.c **********************************************************/
+
+/* extract groups of files */
+int tar_extract_glob(TAR *t, char *globname, char *prefix);
+int tar_extract_all(TAR *t, char *prefix, const int *progress_fd);
+
+/* add a whole tree of files */
+int tar_append_tree(TAR *t, char *realdir, char *savedir);
+
+/* find an entry */
+int tar_find(TAR *t, char *searchstr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ! LIBTAR_H */
+
diff --git a/libtar/libtar_hash.c b/libtar/libtar_hash.c
new file mode 100644
index 000000000..796ebbee6
--- /dev/null
+++ b/libtar/libtar_hash.c
@@ -0,0 +1,344 @@
+/* listhash/libtar_hash.c. Generated from hash.c.in by configure. */
+
+/*
+** Copyright 1998-2002 University of Illinois Board of Trustees
+** Copyright 1998-2002 Mark D. Roth
+** All rights reserved.
+**
+** libtar_hash.c - hash table routines
+**
+** Mark D. Roth <roth@uiuc.edu>
+** Campus Information Technologies and Educational Services
+** University of Illinois at Urbana-Champaign
+*/
+
+#include <config.h>
+#include <compat.h>
+
+#include <libtar_listhash.h>
+
+#include <stdio.h>
+#include <errno.h>
+
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+#endif
+
+
+/*
+** libtar_hashptr_reset() - reset a hash pointer
+*/
+void
+libtar_hashptr_reset(libtar_hashptr_t *hp)
+{
+ libtar_listptr_reset(&(hp->node));
+ hp->bucket = -1;
+}
+
+
+/*
+** libtar_hashptr_data() - retrieve the data being pointed to
+*/
+void *
+libtar_hashptr_data(libtar_hashptr_t *hp)
+{
+ return libtar_listptr_data(&(hp->node));
+}
+
+
+/*
+** libtar_str_hashfunc() - default hash function, optimized for
+** 7-bit strings
+*/
+unsigned int
+libtar_str_hashfunc(char *key, unsigned int num_buckets)
+{
+#if 0
+ register unsigned result = 0;
+ register int i;
+
+ if (key == NULL)
+ return 0;
+
+ for (i = 0; *key != '\0' && i < 32; i++)
+ result = result * 33U + *key++;
+
+ return (result % num_buckets);
+#else
+ if (key == NULL)
+ return 0;
+
+ return (key[0] % num_buckets);
+#endif
+}
+
+
+/*
+** libtar_hash_nents() - return number of elements from hash
+*/
+unsigned int
+libtar_hash_nents(libtar_hash_t *h)
+{
+ return h->nents;
+}
+
+
+/*
+** libtar_hash_new() - create a new hash
+*/
+libtar_hash_t *
+libtar_hash_new(int num, libtar_hashfunc_t hashfunc)
+{
+ libtar_hash_t *hash;
+
+ hash = (libtar_hash_t *)calloc(1, sizeof(libtar_hash_t));
+ if (hash == NULL)
+ return NULL;
+ hash->numbuckets = num;
+ if (hashfunc != NULL)
+ hash->hashfunc = hashfunc;
+ else
+ hash->hashfunc = (libtar_hashfunc_t)libtar_str_hashfunc;
+
+ hash->table = (libtar_list_t **)calloc(num, sizeof(libtar_list_t *));
+ if (hash->table == NULL)
+ {
+ free(hash);
+ return NULL;
+ }
+
+ return hash;
+}
+
+
+/*
+** libtar_hash_next() - get next element in hash
+** returns:
+** 1 data found
+** 0 end of list
+*/
+int
+libtar_hash_next(libtar_hash_t *h,
+ libtar_hashptr_t *hp)
+{
+#ifdef DS_DEBUG
+ printf("==> libtar_hash_next(h=0x%lx, hp={%d,0x%lx})\n",
+ h, hp->bucket, hp->node);
+#endif
+
+ if (hp->bucket >= 0 && hp->node != NULL &&
+ libtar_list_next(h->table[hp->bucket], &(hp->node)) != 0)
+ {
+#ifdef DS_DEBUG
+ printf(" libtar_hash_next(): found additional "
+ "data in current bucket (%d), returing 1\n",
+ hp->bucket);
+#endif
+ return 1;
+ }
+
+#ifdef DS_DEBUG
+ printf(" libtar_hash_next(): done with bucket %d\n",
+ hp->bucket);
+#endif
+
+ for (hp->bucket++; hp->bucket < h->numbuckets; hp->bucket++)
+ {
+#ifdef DS_DEBUG
+ printf(" libtar_hash_next(): "
+ "checking bucket %d\n", hp->bucket);
+#endif
+ hp->node = NULL;
+ if (h->table[hp->bucket] != NULL &&
+ libtar_list_next(h->table[hp->bucket],
+ &(hp->node)) != 0)
+ {
+#ifdef DS_DEBUG
+ printf(" libtar_hash_next(): "
+ "found data in bucket %d, returing 1\n",
+ hp->bucket);
+#endif
+ return 1;
+ }
+ }
+
+ if (hp->bucket == h->numbuckets)
+ {
+#ifdef DS_DEBUG
+ printf(" libtar_hash_next(): hash pointer "
+ "wrapped to 0\n");
+#endif
+ hp->bucket = -1;
+ hp->node = NULL;
+ }
+
+#ifdef DS_DEBUG
+ printf("<== libtar_hash_next(): no more data, "
+ "returning 0\n");
+#endif
+ return 0;
+}
+
+
+/*
+** libtar_hash_del() - delete an entry from the hash
+** returns:
+** 0 success
+** -1 (and sets errno) failure
+*/
+int
+libtar_hash_del(libtar_hash_t *h,
+ libtar_hashptr_t *hp)
+{
+ if (hp->bucket < 0
+ || hp->bucket >= h->numbuckets
+ || h->table[hp->bucket] == NULL
+ || hp->node == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ libtar_list_del(h->table[hp->bucket], &(hp->node));
+ h->nents--;
+ return 0;
+}
+
+
+/*
+** libtar_hash_empty() - empty the hash
+*/
+void
+libtar_hash_empty(libtar_hash_t *h, libtar_freefunc_t freefunc)
+{
+ int i;
+
+ for (i = 0; i < h->numbuckets; i++)
+ if (h->table[i] != NULL)
+ libtar_list_empty(h->table[i], freefunc);
+
+ h->nents = 0;
+}
+
+
+/*
+** libtar_hash_free() - delete all of the nodes in the hash
+*/
+void
+libtar_hash_free(libtar_hash_t *h, libtar_freefunc_t freefunc)
+{
+ int i;
+
+ for (i = 0; i < h->numbuckets; i++)
+ if (h->table[i] != NULL)
+ libtar_list_free(h->table[i], freefunc);
+
+ free(h->table);
+ free(h);
+}
+
+
+/*
+** libtar_hash_search() - iterative search for an element in a hash
+** returns:
+** 1 match found
+** 0 no match
+*/
+int
+libtar_hash_search(libtar_hash_t *h,
+ libtar_hashptr_t *hp, void *data,
+ libtar_matchfunc_t matchfunc)
+{
+ while (libtar_hash_next(h, hp) != 0)
+ if ((*matchfunc)(data, libtar_listptr_data(&(hp->node))) != 0)
+ return 1;
+
+ return 0;
+}
+
+
+/*
+** libtar_hash_getkey() - hash-based search for an element in a hash
+** returns:
+** 1 match found
+** 0 no match
+*/
+int
+libtar_hash_getkey(libtar_hash_t *h,
+ libtar_hashptr_t *hp, void *key,
+ libtar_matchfunc_t matchfunc)
+{
+#ifdef DS_DEBUG
+ printf("==> libtar_hash_getkey(h=0x%lx, hp={%d,0x%lx}, "
+ "key=0x%lx, matchfunc=0x%lx)\n",
+ h, hp->bucket, hp->node, key, matchfunc);
+#endif
+
+ if (hp->bucket == -1)
+ {
+ hp->bucket = (*(h->hashfunc))(key, h->numbuckets);
+#ifdef DS_DEBUG
+ printf(" libtar_hash_getkey(): hp->bucket "
+ "set to %d\n", hp->bucket);
+#endif
+ }
+
+ if (h->table[hp->bucket] == NULL)
+ {
+#ifdef DS_DEBUG
+ printf(" libtar_hash_getkey(): no list "
+ "for bucket %d, returning 0\n", hp->bucket);
+#endif
+ hp->bucket = -1;
+ return 0;
+ }
+
+#ifdef DS_DEBUG
+ printf("<== libtar_hash_getkey(): "
+ "returning libtar_list_search()\n");
+#endif
+ return libtar_list_search(h->table[hp->bucket], &(hp->node),
+ key, matchfunc);
+}
+
+
+/*
+** libtar_hash_add() - add an element to the hash
+** returns:
+** 0 success
+** -1 (and sets errno) failure
+*/
+int
+libtar_hash_add(libtar_hash_t *h, void *data)
+{
+ int bucket, i;
+
+#ifdef DS_DEBUG
+ printf("==> libtar_hash_add(h=0x%lx, data=0x%lx)\n",
+ h, data);
+#endif
+
+ bucket = (*(h->hashfunc))(data, h->numbuckets);
+#ifdef DS_DEBUG
+ printf(" libtar_hash_add(): inserting in bucket %d\n",
+ bucket);
+#endif
+ if (h->table[bucket] == NULL)
+ {
+#ifdef DS_DEBUG
+ printf(" libtar_hash_add(): creating new list\n");
+#endif
+ h->table[bucket] = libtar_list_new(LIST_QUEUE, NULL);
+ }
+
+#ifdef DS_DEBUG
+ printf("<== libtar_hash_add(): "
+ "returning libtar_list_add()\n");
+#endif
+ i = libtar_list_add(h->table[bucket], data);
+ if (i == 0)
+ h->nents++;
+ return i;
+}
+
+
diff --git a/libtar/libtar_list.c b/libtar/libtar_list.c
new file mode 100644
index 000000000..2c5febb01
--- /dev/null
+++ b/libtar/libtar_list.c
@@ -0,0 +1,458 @@
+/* listhash/libtar_list.c. Generated from list.c.in by configure. */
+
+/*
+** Copyright 1998-2002 University of Illinois Board of Trustees
+** Copyright 1998-2002 Mark D. Roth
+** All rights reserved.
+**
+** libtar_list.c - linked list routines
+**
+** Mark D. Roth <roth@uiuc.edu>
+** Campus Information Technologies and Educational Services
+** University of Illinois at Urbana-Champaign
+*/
+
+#include <config.h>
+#include <compat.h>
+
+#include <libtar_listhash.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/param.h>
+
+#ifdef STDC_HEADERS
+# include <string.h>
+# include <stdlib.h>
+#endif
+
+
+/*
+** libtar_listptr_reset() - reset a list pointer
+*/
+void
+libtar_listptr_reset(libtar_listptr_t *lp)
+{
+ *lp = NULL;
+}
+
+
+/*
+** libtar_listptr_data() - retrieve the data pointed to by lp
+*/
+void *
+libtar_listptr_data(libtar_listptr_t *lp)
+{
+ return (*lp)->data;
+}
+
+
+/*
+** libtar_list_new() - create a new, empty list
+*/
+libtar_list_t *
+libtar_list_new(int flags, libtar_cmpfunc_t cmpfunc)
+{
+ libtar_list_t *newlist;
+
+#ifdef DS_DEBUG
+ printf("in libtar_list_new(%d, 0x%lx)\n", flags, cmpfunc);
+#endif
+
+ if (flags != LIST_USERFUNC
+ && flags != LIST_STACK
+ && flags != LIST_QUEUE)
+ {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ newlist = (libtar_list_t *)calloc(1, sizeof(libtar_list_t));
+ if (cmpfunc != NULL)
+ newlist->cmpfunc = cmpfunc;
+ else
+ newlist->cmpfunc = (libtar_cmpfunc_t)strcmp;
+ newlist->flags = flags;
+
+ return newlist;
+}
+
+
+/*
+** libtar_list_iterate() - call a function for every element
+** in a list
+*/
+int
+libtar_list_iterate(libtar_list_t *l,
+ libtar_iterate_func_t plugin,
+ void *state)
+{
+ libtar_listptr_t n;
+
+ if (l == NULL)
+ return -1;
+
+ for (n = l->first; n != NULL; n = n->next)
+ {
+ if ((*plugin)(n->data, state) == -1)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+** libtar_list_empty() - empty the list
+*/
+void
+libtar_list_empty(libtar_list_t *l, libtar_freefunc_t freefunc)
+{
+ libtar_listptr_t n;
+
+ for (n = l->first; n != NULL; n = l->first)
+ {
+ l->first = n->next;
+ if (freefunc != NULL)
+ (*freefunc)(n->data);
+ free(n);
+ }
+
+ l->nents = 0;
+}
+
+
+/*
+** libtar_list_free() - remove and free() the whole list
+*/
+void
+libtar_list_free(libtar_list_t *l, libtar_freefunc_t freefunc)
+{
+ libtar_list_empty(l, freefunc);
+ free(l);
+}
+
+
+/*
+** libtar_list_nents() - return number of elements in the list
+*/
+unsigned int
+libtar_list_nents(libtar_list_t *l)
+{
+ return l->nents;
+}
+
+
+/*
+** libtar_list_add() - adds an element to the list
+** returns:
+** 0 success
+** -1 (and sets errno) failure
+*/
+int
+libtar_list_add(libtar_list_t *l, void *data)
+{
+ libtar_listptr_t n, m;
+
+#ifdef DS_DEBUG
+ printf("==> libtar_list_add(\"%s\")\n", (char *)data);
+#endif
+
+ n = (libtar_listptr_t)malloc(sizeof(struct libtar_node));
+ if (n == NULL)
+ return -1;
+ n->data = data;
+ l->nents++;
+
+#ifdef DS_DEBUG
+ printf(" libtar_list_add(): allocated data\n");
+#endif
+
+ /* if the list is empty */
+ if (l->first == NULL)
+ {
+ l->last = l->first = n;
+ n->next = n->prev = NULL;
+#ifdef DS_DEBUG
+ printf("<== libtar_list_add(): list was empty; "
+ "added first element and returning 0\n");
+#endif
+ return 0;
+ }
+
+#ifdef DS_DEBUG
+ printf(" libtar_list_add(): list not empty\n");
+#endif
+
+ if (l->flags == LIST_STACK)
+ {
+ n->prev = NULL;
+ n->next = l->first;
+ if (l->first != NULL)
+ l->first->prev = n;
+ l->first = n;
+#ifdef DS_DEBUG
+ printf("<== libtar_list_add(): LIST_STACK set; "
+ "added in front\n");
+#endif
+ return 0;
+ }
+
+ if (l->flags == LIST_QUEUE)
+ {
+ n->prev = l->last;
+ n->next = NULL;
+ if (l->last != NULL)
+ l->last->next = n;
+ l->last = n;
+#ifdef DS_DEBUG
+ printf("<== libtar_list_add(): LIST_QUEUE set; "
+ "added at end\n");
+#endif
+ return 0;
+ }
+
+ for (m = l->first; m != NULL; m = m->next)
+ if ((*(l->cmpfunc))(data, m->data) < 0)
+ {
+ /*
+ ** if we find one that's bigger,
+ ** insert data before it
+ */
+#ifdef DS_DEBUG
+ printf(" libtar_list_add(): gotcha..."
+ "inserting data\n");
+#endif
+ if (m == l->first)
+ {
+ l->first = n;
+ n->prev = NULL;
+ m->prev = n;
+ n->next = m;
+#ifdef DS_DEBUG
+ printf("<== libtar_list_add(): "
+ "added first, returning 0\n");
+#endif
+ return 0;
+ }
+ m->prev->next = n;
+ n->prev = m->prev;
+ m->prev = n;
+ n->next = m;
+#ifdef DS_DEBUG
+ printf("<== libtar_list_add(): added middle,"
+ " returning 0\n");
+#endif
+ return 0;
+ }
+
+#ifdef DS_DEBUG
+ printf(" libtar_list_add(): new data larger than current "
+ "list elements\n");
+#endif
+
+ /* if we get here, data is bigger than everything in the list */
+ l->last->next = n;
+ n->prev = l->last;
+ l->last = n;
+ n->next = NULL;
+#ifdef DS_DEBUG
+ printf("<== libtar_list_add(): added end, returning 0\n");
+#endif
+ return 0;
+}
+
+
+/*
+** libtar_list_del() - remove the element pointed to by n
+** from the list l
+*/
+void
+libtar_list_del(libtar_list_t *l, libtar_listptr_t *n)
+{
+ libtar_listptr_t m;
+
+#ifdef DS_DEBUG
+ printf("==> libtar_list_del()\n");
+#endif
+
+ l->nents--;
+
+ m = (*n)->next;
+
+ if ((*n)->prev)
+ (*n)->prev->next = (*n)->next;
+ else
+ l->first = (*n)->next;
+ if ((*n)->next)
+ (*n)->next->prev = (*n)->prev;
+ else
+ l->last = (*n)->prev;
+
+ free(*n);
+ *n = m;
+}
+
+
+/*
+** libtar_list_next() - get the next element in the list
+** returns:
+** 1 success
+** 0 end of list
+*/
+int
+libtar_list_next(libtar_list_t *l,
+ libtar_listptr_t *n)
+{
+ if (*n == NULL)
+ *n = l->first;
+ else
+ *n = (*n)->next;
+
+ return (*n != NULL ? 1 : 0);
+}
+
+
+/*
+** libtar_list_prev() - get the previous element in the list
+** returns:
+** 1 success
+** 0 end of list
+*/
+int
+libtar_list_prev(libtar_list_t *l,
+ libtar_listptr_t *n)
+{
+ if (*n == NULL)
+ *n = l->last;
+ else
+ *n = (*n)->prev;
+
+ return (*n != NULL ? 1 : 0);
+}
+
+
+/*
+** libtar_str_match() - string matching function
+** returns:
+** 1 match
+** 0 no match
+*/
+int
+libtar_str_match(char *check, char *data)
+{
+ return !strcmp(check, data);
+}
+
+
+/*
+** libtar_list_add_str() - splits string str into delim-delimited
+** elements and adds them to list l
+** returns:
+** 0 success
+** -1 (and sets errno) failure
+*/
+int
+libtar_list_add_str(libtar_list_t *l,
+ char *str, char *delim)
+{
+ char tmp[10240];
+ char *tokp, *nextp = tmp;
+
+ strlcpy(tmp, str, sizeof(tmp));
+ while ((tokp = strsep(&nextp, delim)) != NULL)
+ {
+ if (*tokp == '\0')
+ continue;
+ if (libtar_list_add(l, strdup(tokp)))
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+** libtar_list_search() - find an entry in a list
+** returns:
+** 1 match found
+** 0 no match
+*/
+int
+libtar_list_search(libtar_list_t *l,
+ libtar_listptr_t *n, void *data,
+ libtar_matchfunc_t matchfunc)
+{
+#ifdef DS_DEBUG
+ printf("==> libtar_list_search(l=0x%lx, n=0x%lx, \"%s\")\n",
+ l, n, (char *)data);
+#endif
+
+ if (matchfunc == NULL)
+ matchfunc = (libtar_matchfunc_t)libtar_str_match;
+
+ if (*n == NULL)
+ *n = l->first;
+ else
+ *n = (*n)->next;
+
+ for (; *n != NULL; *n = (*n)->next)
+ {
+#ifdef DS_DEBUG
+ printf("checking against \"%s\"\n", (char *)(*n)->data);
+#endif
+ if ((*(matchfunc))(data, (*n)->data) != 0)
+ return 1;
+ }
+
+#ifdef DS_DEBUG
+ printf("no matches found\n");
+#endif
+ return 0;
+}
+
+
+/*
+** libtar_list_dup() - copy an existing list
+*/
+libtar_list_t *
+libtar_list_dup(libtar_list_t *l)
+{
+ libtar_list_t *newlist;
+ libtar_listptr_t n;
+
+ newlist = libtar_list_new(l->flags, l->cmpfunc);
+ for (n = l->first; n != NULL; n = n->next)
+ libtar_list_add(newlist, n->data);
+
+#ifdef DS_DEBUG
+ printf("returning from libtar_list_dup()\n");
+#endif
+ return newlist;
+}
+
+
+/*
+** libtar_list_merge() - merge two lists into a new list
+*/
+libtar_list_t *
+libtar_list_merge(libtar_cmpfunc_t cmpfunc, int flags,
+ libtar_list_t *list1,
+ libtar_list_t *list2)
+{
+ libtar_list_t *newlist;
+ libtar_listptr_t n;
+
+ newlist = libtar_list_new(flags, cmpfunc);
+
+ n = NULL;
+ while (libtar_list_next(list1, &n) != 0)
+ libtar_list_add(newlist, n->data);
+ n = NULL;
+ while (libtar_list_next(list2, &n) != 0)
+ libtar_list_add(newlist, n->data);
+
+ return newlist;
+}
+
+
diff --git a/libtar/libtar_listhash.h b/libtar/libtar_listhash.h
new file mode 100644
index 000000000..48c0d7409
--- /dev/null
+++ b/libtar/libtar_listhash.h
@@ -0,0 +1,203 @@
+/* listhash/libtar_listhash.h. Generated from listhash.h.in by configure. */
+
+/*
+** Copyright 1998-2002 University of Illinois Board of Trustees
+** Copyright 1998-2002 Mark D. Roth
+** All rights reserved.
+**
+** libtar_listhash.h - header file for listhash module
+**
+** Mark D. Roth <roth@uiuc.edu>
+** Campus Information Technologies and Educational Services
+** University of Illinois at Urbana-Champaign
+*/
+
+#ifndef libtar_LISTHASH_H
+#define libtar_LISTHASH_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/***** list.c **********************************************************/
+
+/*
+** Comparison function (used to determine order of elements in a list)
+** returns less than, equal to, or greater than 0
+** if data1 is less than, equal to, or greater than data2
+*/
+typedef int (*libtar_cmpfunc_t)(void *, void *);
+
+/*
+** Free function (for freeing allocated memory in each element)
+*/
+typedef void (*libtar_freefunc_t)(void *);
+
+/*
+** Plugin function for libtar_list_iterate()
+*/
+typedef int (*libtar_iterate_func_t)(void *, void *);
+
+/*
+** Matching function (used to find elements in a list)
+** first argument is the data to search for
+** second argument is the list element it's being compared to
+** returns 0 if no match is found, non-zero otherwise
+*/
+typedef int (*libtar_matchfunc_t)(void *, void *);
+
+
+struct libtar_node
+{
+ void *data;
+ struct libtar_node *next;
+ struct libtar_node *prev;
+};
+typedef struct libtar_node *libtar_listptr_t;
+
+struct libtar_list
+{
+ libtar_listptr_t first;
+ libtar_listptr_t last;
+ libtar_cmpfunc_t cmpfunc;
+ int flags;
+ unsigned int nents;
+};
+typedef struct libtar_list libtar_list_t;
+
+
+/* values for flags */
+#define LIST_USERFUNC 0 /* use cmpfunc() to order */
+#define LIST_STACK 1 /* new elements go in front */
+#define LIST_QUEUE 2 /* new elements go at the end */
+
+
+/* reset a list pointer */
+void libtar_listptr_reset(libtar_listptr_t *);
+
+/* retrieve the data being pointed to */
+void *libtar_listptr_data(libtar_listptr_t *);
+
+/* creates a new, empty list */
+libtar_list_t *libtar_list_new(int, libtar_cmpfunc_t);
+
+/* call a function for every element in a list */
+int libtar_list_iterate(libtar_list_t *,
+ libtar_iterate_func_t, void *);
+
+/* empty the list */
+void libtar_list_empty(libtar_list_t *,
+ libtar_freefunc_t);
+
+/* remove and free() the entire list */
+void libtar_list_free(libtar_list_t *,
+ libtar_freefunc_t);
+
+/* add elements */
+int libtar_list_add(libtar_list_t *, void *);
+
+/* removes an element from the list - returns -1 on error */
+void libtar_list_del(libtar_list_t *,
+ libtar_listptr_t *);
+
+/* returns 1 when valid data is returned, or 0 at end of list */
+int libtar_list_next(libtar_list_t *,
+ libtar_listptr_t *);
+
+/* returns 1 when valid data is returned, or 0 at end of list */
+int libtar_list_prev(libtar_list_t *,
+ libtar_listptr_t *);
+
+/* return 1 if the data matches a list entry, 0 otherwise */
+int libtar_list_search(libtar_list_t *,
+ libtar_listptr_t *, void *,
+ libtar_matchfunc_t);
+
+/* return number of elements from list */
+unsigned int libtar_list_nents(libtar_list_t *);
+
+/* adds elements from a string delimited by delim */
+int libtar_list_add_str(libtar_list_t *, char *, char *);
+
+/* string matching function */
+int libtar_str_match(char *, char *);
+
+
+/***** hash.c **********************************************************/
+
+/*
+** Hashing function (determines which bucket the given key hashes into)
+** first argument is the key to hash
+** second argument is the total number of buckets
+** returns the bucket number
+*/
+typedef unsigned int (*libtar_hashfunc_t)(void *, unsigned int);
+
+
+struct libtar_hashptr
+{
+ int bucket;
+ libtar_listptr_t node;
+};
+typedef struct libtar_hashptr libtar_hashptr_t;
+
+struct libtar_hash
+{
+ int numbuckets;
+ libtar_list_t **table;
+ libtar_hashfunc_t hashfunc;
+ unsigned int nents;
+};
+typedef struct libtar_hash libtar_hash_t;
+
+
+/* reset a hash pointer */
+void libtar_hashptr_reset(libtar_hashptr_t *);
+
+/* retrieve the data being pointed to */
+void *libtar_hashptr_data(libtar_hashptr_t *);
+
+/* default hash function, optimized for 7-bit strings */
+unsigned int libtar_str_hashfunc(char *, unsigned int);
+
+/* return number of elements from hash */
+unsigned int libtar_hash_nents(libtar_hash_t *);
+
+/* create a new hash */
+libtar_hash_t *libtar_hash_new(int, libtar_hashfunc_t);
+
+/* empty the hash */
+void libtar_hash_empty(libtar_hash_t *,
+ libtar_freefunc_t);
+
+/* delete all the libtar_nodes of the hash and clean up */
+void libtar_hash_free(libtar_hash_t *,
+ libtar_freefunc_t);
+
+/* returns 1 when valid data is returned, or 0 at end of list */
+int libtar_hash_next(libtar_hash_t *,
+ libtar_hashptr_t *);
+
+/* return 1 if the data matches a list entry, 0 otherwise */
+int libtar_hash_search(libtar_hash_t *,
+ libtar_hashptr_t *, void *,
+ libtar_matchfunc_t);
+
+/* return 1 if the key matches a list entry, 0 otherwise */
+int libtar_hash_getkey(libtar_hash_t *,
+ libtar_hashptr_t *, void *,
+ libtar_matchfunc_t);
+
+/* inserting data */
+int libtar_hash_add(libtar_hash_t *, void *);
+
+/* delete an entry */
+int libtar_hash_del(libtar_hash_t *,
+ libtar_hashptr_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ! libtar_LISTHASH_H */
+
diff --git a/libtar/output.c b/libtar/output.c
new file mode 100644
index 000000000..f5431b6a7
--- /dev/null
+++ b/libtar/output.c
@@ -0,0 +1,143 @@
+/*
+** Copyright 1998-2003 University of Illinois Board of Trustees
+** Copyright 1998-2003 Mark D. Roth
+** All rights reserved.
+**
+** output.c - libtar code to print out tar header blocks
+**
+** Mark D. Roth <roth@uiuc.edu>
+** Campus Information Technologies and Educational Services
+** University of Illinois at Urbana-Champaign
+*/
+
+#include <internal.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include <grp.h>
+#include <time.h>
+#include <limits.h>
+#include <sys/param.h>
+
+#ifdef STDC_HEADERS
+# include <string.h>
+#endif
+
+#ifdef HAVE_EXT4_CRYPT
+# include "ext4crypt_tar.h"
+#endif
+
+
+#ifndef _POSIX_LOGIN_NAME_MAX
+# define _POSIX_LOGIN_NAME_MAX 9
+#endif
+
+
+void
+th_print(TAR *t)
+{
+ puts("\nPrinting tar header:");
+ printf(" name = \"%.100s\"\n", t->th_buf.name);
+ printf(" mode = \"%.8s\"\n", t->th_buf.mode);
+ printf(" uid = \"%.8s\"\n", t->th_buf.uid);
+ printf(" gid = \"%.8s\"\n", t->th_buf.gid);
+ printf(" size = \"%.12s\"\n", t->th_buf.size);
+ printf(" mtime = \"%.12s\"\n", t->th_buf.mtime);
+ printf(" chksum = \"%.8s\"\n", t->th_buf.chksum);
+ printf(" typeflag = \'%c\'\n", t->th_buf.typeflag);
+ printf(" linkname = \"%.100s\"\n", t->th_buf.linkname);
+ printf(" magic = \"%.6s\"\n", t->th_buf.magic);
+ /*printf(" version = \"%.2s\"\n", t->th_buf.version); */
+ /*printf(" version[0] = \'%c\',version[1] = \'%c\'\n",
+ t->th_buf.version[0], t->th_buf.version[1]);*/
+ printf(" uname = \"%.32s\"\n", t->th_buf.uname);
+ printf(" gname = \"%.32s\"\n", t->th_buf.gname);
+ printf(" devmajor = \"%.8s\"\n", t->th_buf.devmajor);
+ printf(" devminor = \"%.8s\"\n", t->th_buf.devminor);
+ printf(" prefix = \"%.155s\"\n", t->th_buf.prefix);
+ printf(" padding = \"%.12s\"\n", t->th_buf.padding);
+ printf(" gnu_longname = \"%s\"\n",
+ (t->th_buf.gnu_longname ? t->th_buf.gnu_longname : "[NULL]"));
+ printf(" gnu_longlink = \"%s\"\n",
+ (t->th_buf.gnu_longlink ? t->th_buf.gnu_longlink : "[NULL]"));
+#ifdef HAVE_EXT4_CRYPT
+ printf(" eep = \"%s\"\n",
+ (t->th_buf.eep ? t->th_buf.eep->master_key_descriptor : "[NULL]"));
+#endif
+}
+
+
+void
+th_print_long_ls(TAR *t)
+{
+ char modestring[12];
+ struct passwd *pw;
+ struct group *gr;
+ uid_t uid;
+ gid_t gid;
+ char username[_POSIX_LOGIN_NAME_MAX];
+ char groupname[_POSIX_LOGIN_NAME_MAX];
+ time_t mtime;
+ struct tm *mtm;
+
+#ifdef HAVE_STRFTIME
+ char timebuf[18];
+#else
+ const char *months[] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+ };
+#endif
+
+ uid = th_get_uid(t);
+ pw = getpwuid(uid);
+ if ((t->options & TAR_USE_NUMERIC_ID) || pw == NULL)
+ snprintf(username, sizeof(username), "%d", uid);
+ else
+ strlcpy(username, pw->pw_name, sizeof(username));
+
+ gid = th_get_gid(t);
+ gr = getgrgid(gid);
+ if ((t->options & TAR_USE_NUMERIC_ID) || gr == NULL)
+ snprintf(groupname, sizeof(groupname), "%d", gid);
+ else
+ strlcpy(groupname, gr->gr_name, sizeof(groupname));
+
+ strmode(th_get_mode(t), modestring);
+ printf("%.10s %-8.8s %-8.8s ", modestring, username, groupname);
+
+ if (TH_ISCHR(t) || TH_ISBLK(t))
+ printf(" %3d, %3d ", (int)th_get_devmajor(t), (int)th_get_devminor(t));
+ else
+ printf("%9ld ", (long)th_get_size(t));
+
+ mtime = th_get_mtime(t);
+ mtm = localtime(&mtime);
+#ifdef HAVE_STRFTIME
+ strftime(timebuf, sizeof(timebuf), "%h %e %H:%M %Y", mtm);
+ printf("%s", timebuf);
+#else
+ printf("%.3s %2d %2d:%02d %4d",
+ months[mtm->tm_mon],
+ mtm->tm_mday, mtm->tm_hour, mtm->tm_min, mtm->tm_year + 1900);
+#endif
+
+ printf(" %s", th_get_pathname(t));
+
+ if (TH_ISSYM(t) || TH_ISLNK(t))
+ {
+ if (TH_ISSYM(t))
+ printf(" -> ");
+ else
+ printf(" link to ");
+ if ((t->options & TAR_GNU) && t->th_buf.gnu_longlink != NULL)
+ printf("%s", t->th_buf.gnu_longlink);
+ else
+ printf("%.100s", t->th_buf.linkname);
+ }
+
+ putchar('\n');
+}
+
+
diff --git a/libtar/snprintf.c b/libtar/snprintf.c
new file mode 100644
index 000000000..8c228d447
--- /dev/null
+++ b/libtar/snprintf.c
@@ -0,0 +1,761 @@
+/**************************************************************
+ * Original:
+ * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
+ * A bombproof version of doprnt (dopr) included.
+ * Sigh. This sort of thing is always nasty do deal with. Note that
+ * the version here does not include floating point...
+ *
+ * snprintf() is used instead of sprintf() as it does limit checks
+ * for string length. This covers a nasty loophole.
+ *
+ * The other functions are there to prevent NULL pointers from
+ * causing nast effects.
+ *
+ * More Recently:
+ * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
+ * This was ugly. It is still ugly. I opted out of floating point
+ * numbers, but the formatter understands just about everything
+ * from the normal C string format, at least as far as I can tell from
+ * the Solaris 2.5 printf(3S) man page.
+ *
+ * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
+ * Ok, added some minimal floating point support, which means this
+ * probably requires libm on most operating systems. Don't yet
+ * support the exponent (e,E) and sigfig (g,G). Also, fmtint()
+ * was pretty badly broken, it just wasn't being exercised in ways
+ * which showed it, so that's been fixed. Also, formated the code
+ * to mutt conventions, and removed dead code left over from the
+ * original. Also, there is now a builtin-test, just compile with:
+ * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
+ * and run snprintf for results.
+ *
+ * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
+ * The PGP code was using unsigned hexadecimal formats.
+ * Unfortunately, unsigned formats simply didn't work.
+ *
+ * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
+ * The original code assumed that both snprintf() and vsnprintf() were
+ * missing. Some systems only have snprintf() but not vsnprintf(), so
+ * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
+ *
+ * Chris Frey <cdfrey@foursquare.net> 2012/09/05
+ * Removed varargs.h and all dependency on it.
+ *
+ **************************************************************/
+
+#include <config.h>
+
+#if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF)
+
+#include <string.h>
+# include <ctype.h>
+#include <sys/types.h>
+
+#include <stdarg.h>
+#define VA_LOCAL_DECL va_list ap
+#define VA_START(f) va_start(ap, f)
+#define VA_SHIFT(v,t) ; /* no-op for ANSI */
+#define VA_END va_end(ap)
+
+/*int snprintf (char *str, size_t count, const char *fmt, ...);*/
+/*int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);*/
+
+static void dopr (char *buffer, size_t maxlen, const char *format,
+ va_list args);
+static void fmtstr (char *buffer, size_t *currlen, size_t maxlen,
+ char *value, int flags, int min, int max);
+static void fmtint (char *buffer, size_t *currlen, size_t maxlen,
+ long value, int base, int min, int max, int flags);
+static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
+ long double fvalue, int min, int max, int flags);
+static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c );
+
+/*
+ * dopr(): poor man's version of doprintf
+ */
+
+/* format read states */
+#define DP_S_DEFAULT 0
+#define DP_S_FLAGS 1
+#define DP_S_MIN 2
+#define DP_S_DOT 3
+#define DP_S_MAX 4
+#define DP_S_MOD 5
+#define DP_S_CONV 6
+#define DP_S_DONE 7
+
+/* format flags - Bits */
+#define DP_F_MINUS (1 << 0)
+#define DP_F_PLUS (1 << 1)
+#define DP_F_SPACE (1 << 2)
+#define DP_F_NUM (1 << 3)
+#define DP_F_ZERO (1 << 4)
+#define DP_F_UP (1 << 5)
+#define DP_F_UNSIGNED (1 << 6)
+
+/* Conversion Flags */
+#define DP_C_SHORT 1
+#define DP_C_LONG 2
+#define DP_C_LDOUBLE 3
+
+#define char_to_int(p) (p - '0')
+#define MAX(p,q) ((p >= q) ? p : q)
+
+static void dopr (char *buffer, size_t maxlen, const char *format, va_list args)
+{
+ char ch;
+ long value;
+ long double fvalue;
+ char *strvalue;
+ int min;
+ int max;
+ int state;
+ int flags;
+ int cflags;
+ size_t currlen;
+
+ state = DP_S_DEFAULT;
+ currlen = flags = cflags = min = 0;
+ max = -1;
+ ch = *format++;
+
+ while (state != DP_S_DONE)
+ {
+ if ((ch == '\0') || (currlen >= maxlen))
+ state = DP_S_DONE;
+
+ switch(state)
+ {
+ case DP_S_DEFAULT:
+ if (ch == '%')
+ state = DP_S_FLAGS;
+ else
+ dopr_outch (buffer, &currlen, maxlen, ch);
+ ch = *format++;
+ break;
+ case DP_S_FLAGS:
+ switch (ch)
+ {
+ case '-':
+ flags |= DP_F_MINUS;
+ ch = *format++;
+ break;
+ case '+':
+ flags |= DP_F_PLUS;
+ ch = *format++;
+ break;
+ case ' ':
+ flags |= DP_F_SPACE;
+ ch = *format++;
+ break;
+ case '#':
+ flags |= DP_F_NUM;
+ ch = *format++;
+ break;
+ case '0':
+ flags |= DP_F_ZERO;
+ ch = *format++;
+ break;
+ default:
+ state = DP_S_MIN;
+ break;
+ }
+ break;
+ case DP_S_MIN:
+ if (isdigit((unsigned char)ch))
+ {
+ min = 10*min + char_to_int (ch);
+ ch = *format++;
+ }
+ else if (ch == '*')
+ {
+ min = va_arg (args, int);
+ ch = *format++;
+ state = DP_S_DOT;
+ }
+ else
+ state = DP_S_DOT;
+ break;
+ case DP_S_DOT:
+ if (ch == '.')
+ {
+ state = DP_S_MAX;
+ ch = *format++;
+ }
+ else
+ state = DP_S_MOD;
+ break;
+ case DP_S_MAX:
+ if (isdigit((unsigned char)ch))
+ {
+ if (max < 0)
+ max = 0;
+ max = 10*max + char_to_int (ch);
+ ch = *format++;
+ }
+ else if (ch == '*')
+ {
+ max = va_arg (args, int);
+ ch = *format++;
+ state = DP_S_MOD;
+ }
+ else
+ state = DP_S_MOD;
+ break;
+ case DP_S_MOD:
+ /* Currently, we don't support Long Long, bummer */
+ switch (ch)
+ {
+ case 'h':
+ cflags = DP_C_SHORT;
+ ch = *format++;
+ break;
+ case 'l':
+ cflags = DP_C_LONG;
+ ch = *format++;
+ break;
+ case 'L':
+ cflags = DP_C_LDOUBLE;
+ ch = *format++;
+ break;
+ default:
+ break;
+ }
+ state = DP_S_CONV;
+ break;
+ case DP_S_CONV:
+ switch (ch)
+ {
+ case 'd':
+ case 'i':
+ if (cflags == DP_C_SHORT)
+ value = va_arg (args, int);
+ else if (cflags == DP_C_LONG)
+ value = va_arg (args, long int);
+ else
+ value = va_arg (args, int);
+ fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
+ break;
+ case 'o':
+ flags |= DP_F_UNSIGNED;
+ if (cflags == DP_C_SHORT)
+ value = va_arg (args, unsigned int);
+ else if (cflags == DP_C_LONG)
+ value = va_arg (args, unsigned long int);
+ else
+ value = va_arg (args, unsigned int);
+ fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
+ break;
+ case 'u':
+ flags |= DP_F_UNSIGNED;
+ if (cflags == DP_C_SHORT)
+ value = va_arg (args, unsigned int);
+ else if (cflags == DP_C_LONG)
+ value = va_arg (args, unsigned long int);
+ else
+ value = va_arg (args, unsigned int);
+ fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
+ break;
+ case 'X':
+ flags |= DP_F_UP;
+ case 'x':
+ flags |= DP_F_UNSIGNED;
+ if (cflags == DP_C_SHORT)
+ value = va_arg (args, unsigned int);
+ else if (cflags == DP_C_LONG)
+ value = va_arg (args, unsigned long int);
+ else
+ value = va_arg (args, unsigned int);
+ fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
+ break;
+ case 'f':
+ if (cflags == DP_C_LDOUBLE)
+ fvalue = va_arg (args, long double);
+ else
+ fvalue = va_arg (args, double);
+ /* um, floating point? */
+ fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
+ break;
+ case 'E':
+ flags |= DP_F_UP;
+ case 'e':
+ if (cflags == DP_C_LDOUBLE)
+ fvalue = va_arg (args, long double);
+ else
+ fvalue = va_arg (args, double);
+ break;
+ case 'G':
+ flags |= DP_F_UP;
+ case 'g':
+ if (cflags == DP_C_LDOUBLE)
+ fvalue = va_arg (args, long double);
+ else
+ fvalue = va_arg (args, double);
+ break;
+ case 'c':
+ dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
+ break;
+ case 's':
+ strvalue = va_arg (args, char *);
+ if (max < 0)
+ max = maxlen; /* ie, no max */
+ fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
+ break;
+ case 'p':
+ strvalue = va_arg (args, void *);
+ fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
+ break;
+ case 'n':
+ if (cflags == DP_C_SHORT)
+ {
+ short int *num;
+ num = va_arg (args, short int *);
+ *num = currlen;
+ }
+ else if (cflags == DP_C_LONG)
+ {
+ long int *num;
+ num = va_arg (args, long int *);
+ *num = currlen;
+ }
+ else
+ {
+ int *num;
+ num = va_arg (args, int *);
+ *num = currlen;
+ }
+ break;
+ case '%':
+ dopr_outch (buffer, &currlen, maxlen, ch);
+ break;
+ case 'w':
+ /* not supported yet, treat as next char */
+ ch = *format++;
+ break;
+ default:
+ /* Unknown, skip */
+ break;
+ }
+ ch = *format++;
+ state = DP_S_DEFAULT;
+ flags = cflags = min = 0;
+ max = -1;
+ break;
+ case DP_S_DONE:
+ break;
+ default:
+ /* hmm? */
+ break; /* some picky compilers need this */
+ }
+ }
+ if (currlen < maxlen - 1)
+ buffer[currlen] = '\0';
+ else
+ buffer[maxlen - 1] = '\0';
+}
+
+static void fmtstr (char *buffer, size_t *currlen, size_t maxlen,
+ char *value, int flags, int min, int max)
+{
+ int padlen, strln; /* amount to pad */
+ int cnt = 0;
+
+ if (value == 0)
+ {
+ value = "<NULL>";
+ }
+
+ for (strln = 0; value[strln]; ++strln); /* strlen */
+ padlen = min - strln;
+ if (padlen < 0)
+ padlen = 0;
+ if (flags & DP_F_MINUS)
+ padlen = -padlen; /* Left Justify */
+
+ while ((padlen > 0) && (cnt < max))
+ {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ --padlen;
+ ++cnt;
+ }
+ while (*value && (cnt < max))
+ {
+ dopr_outch (buffer, currlen, maxlen, *value++);
+ ++cnt;
+ }
+ while ((padlen < 0) && (cnt < max))
+ {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ ++padlen;
+ ++cnt;
+ }
+}
+
+/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
+
+static void fmtint (char *buffer, size_t *currlen, size_t maxlen,
+ long value, int base, int min, int max, int flags)
+{
+ int signvalue = 0;
+ unsigned long uvalue;
+ char convert[20];
+ int place = 0;
+ int spadlen = 0; /* amount to space pad */
+ int zpadlen = 0; /* amount to zero pad */
+ int caps = 0;
+
+ if (max < 0)
+ max = 0;
+
+ uvalue = value;
+
+ if(!(flags & DP_F_UNSIGNED))
+ {
+ if( value < 0 ) {
+ signvalue = '-';
+ uvalue = -value;
+ }
+ else
+ if (flags & DP_F_PLUS) /* Do a sign (+/i) */
+ signvalue = '+';
+ else
+ if (flags & DP_F_SPACE)
+ signvalue = ' ';
+ }
+
+ if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
+
+ do {
+ convert[place++] =
+ (caps? "0123456789ABCDEF":"0123456789abcdef")
+ [uvalue % (unsigned)base ];
+ uvalue = (uvalue / (unsigned)base );
+ } while(uvalue && (place < 20));
+ if (place == 20) place--;
+ convert[place] = 0;
+
+ zpadlen = max - place;
+ spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
+ if (zpadlen < 0) zpadlen = 0;
+ if (spadlen < 0) spadlen = 0;
+ if (flags & DP_F_ZERO)
+ {
+ zpadlen = MAX(zpadlen, spadlen);
+ spadlen = 0;
+ }
+ if (flags & DP_F_MINUS)
+ spadlen = -spadlen; /* Left Justifty */
+
+#ifdef DEBUG_SNPRINTF
+ dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
+ zpadlen, spadlen, min, max, place));
+#endif
+
+ /* Spaces */
+ while (spadlen > 0)
+ {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ --spadlen;
+ }
+
+ /* Sign */
+ if (signvalue)
+ dopr_outch (buffer, currlen, maxlen, signvalue);
+
+ /* Zeros */
+ if (zpadlen > 0)
+ {
+ while (zpadlen > 0)
+ {
+ dopr_outch (buffer, currlen, maxlen, '0');
+ --zpadlen;
+ }
+ }
+
+ /* Digits */
+ while (place > 0)
+ dopr_outch (buffer, currlen, maxlen, convert[--place]);
+
+ /* Left Justified spaces */
+ while (spadlen < 0) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ ++spadlen;
+ }
+}
+
+static long double abs_val (long double value)
+{
+ long double result = value;
+
+ if (value < 0)
+ result = -value;
+
+ return result;
+}
+
+static long double pow10_ (int exp)
+{
+ long double result = 1;
+
+ while (exp)
+ {
+ result *= 10;
+ exp--;
+ }
+
+ return result;
+}
+
+static long round_ (long double value)
+{
+ long intpart;
+
+ intpart = value;
+ value = value - intpart;
+ if (value >= 0.5)
+ intpart++;
+
+ return intpart;
+}
+
+static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
+ long double fvalue, int min, int max, int flags)
+{
+ int signvalue = 0;
+ long double ufvalue;
+ char iconvert[20];
+ char fconvert[20];
+ int iplace = 0;
+ int fplace = 0;
+ int padlen = 0; /* amount to pad */
+ int zpadlen = 0;
+ int caps = 0;
+ long intpart;
+ long fracpart;
+
+ /*
+ * AIX manpage says the default is 0, but Solaris says the default
+ * is 6, and sprintf on AIX defaults to 6
+ */
+ if (max < 0)
+ max = 6;
+
+ ufvalue = abs_val (fvalue);
+
+ if (fvalue < 0)
+ signvalue = '-';
+ else
+ if (flags & DP_F_PLUS) /* Do a sign (+/i) */
+ signvalue = '+';
+ else
+ if (flags & DP_F_SPACE)
+ signvalue = ' ';
+
+#if 0
+ if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
+#endif
+
+ intpart = ufvalue;
+
+ /*
+ * Sorry, we only support 9 digits past the decimal because of our
+ * conversion method
+ */
+ if (max > 9)
+ max = 9;
+
+ /* We "cheat" by converting the fractional part to integer by
+ * multiplying by a factor of 10
+ */
+ fracpart = round_ ((pow10_ (max)) * (ufvalue - intpart));
+
+ if (fracpart >= pow10_ (max))
+ {
+ intpart++;
+ fracpart -= pow10_ (max);
+ }
+
+#ifdef DEBUG_SNPRINTF
+ dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart));
+#endif
+
+ /* Convert integer part */
+ do {
+ iconvert[iplace++] =
+ (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10];
+ intpart = (intpart / 10);
+ } while(intpart && (iplace < 20));
+ if (iplace == 20) iplace--;
+ iconvert[iplace] = 0;
+
+ /* Convert fractional part */
+ do {
+ fconvert[fplace++] =
+ (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10];
+ fracpart = (fracpart / 10);
+ } while(fracpart && (fplace < 20));
+ if (fplace == 20) fplace--;
+ fconvert[fplace] = 0;
+
+ /* -1 for decimal point, another -1 if we are printing a sign */
+ padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
+ zpadlen = max - fplace;
+ if (zpadlen < 0)
+ zpadlen = 0;
+ if (padlen < 0)
+ padlen = 0;
+ if (flags & DP_F_MINUS)
+ padlen = -padlen; /* Left Justifty */
+
+ if ((flags & DP_F_ZERO) && (padlen > 0))
+ {
+ if (signvalue)
+ {
+ dopr_outch (buffer, currlen, maxlen, signvalue);
+ --padlen;
+ signvalue = 0;
+ }
+ while (padlen > 0)
+ {
+ dopr_outch (buffer, currlen, maxlen, '0');
+ --padlen;
+ }
+ }
+ while (padlen > 0)
+ {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ --padlen;
+ }
+ if (signvalue)
+ dopr_outch (buffer, currlen, maxlen, signvalue);
+
+ while (iplace > 0)
+ dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
+
+ /*
+ * Decimal point. This should probably use locale to find the correct
+ * char to print out.
+ */
+ dopr_outch (buffer, currlen, maxlen, '.');
+
+ while (fplace > 0)
+ dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
+
+ while (zpadlen > 0)
+ {
+ dopr_outch (buffer, currlen, maxlen, '0');
+ --zpadlen;
+ }
+
+ while (padlen < 0)
+ {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ ++padlen;
+ }
+}
+
+static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c)
+{
+ if (*currlen < maxlen)
+ buffer[(*currlen)++] = c;
+}
+#endif /* !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) */
+
+#ifndef HAVE_VSNPRINTF
+int mutt_vsnprintf (char *str, size_t count, const char *fmt, va_list args)
+{
+ str[0] = 0;
+ dopr(str, count, fmt, args);
+ return(strlen(str));
+}
+#endif /* !HAVE_VSNPRINTF */
+
+#ifndef HAVE_SNPRINTF
+int mutt_snprintf (char *str,size_t count,const char *fmt,...)
+{
+ VA_LOCAL_DECL;
+
+ VA_START (fmt);
+ VA_SHIFT (str, char *);
+ VA_SHIFT (count, size_t );
+ VA_SHIFT (fmt, char *);
+ (void) mutt_vsnprintf(str, count, fmt, ap);
+ VA_END;
+ return(strlen(str));
+}
+
+#ifdef TEST_SNPRINTF
+#ifndef LONG_STRING
+#define LONG_STRING 1024
+#endif
+int main (void)
+{
+ char buf1[LONG_STRING];
+ char buf2[LONG_STRING];
+ char *fp_fmt[] = {
+ "%-1.5f",
+ "%1.5f",
+ "%123.9f",
+ "%10.5f",
+ "% 10.5f",
+ "%+22.9f",
+ "%+4.9f",
+ "%01.3f",
+ "%4f",
+ "%3.1f",
+ "%3.2f",
+ NULL
+ };
+ double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996,
+ 0.9996, 1.996, 4.136, 0};
+ char *int_fmt[] = {
+ "%-1.5d",
+ "%1.5d",
+ "%123.9d",
+ "%5.5d",
+ "%10.5d",
+ "% 10.5d",
+ "%+22.33d",
+ "%01.3d",
+ "%4d",
+ NULL
+ };
+ long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
+ int x, y;
+ int fail = 0;
+ int num = 0;
+
+ printf ("Testing snprintf format codes against system sprintf...\n");
+
+ for (x = 0; fp_fmt[x] != NULL ; x++)
+ for (y = 0; fp_nums[y] != 0 ; y++)
+ {
+ mutt_snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]);
+ sprintf (buf2, fp_fmt[x], fp_nums[y]);
+ if (strcmp (buf1, buf2))
+ {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n",
+ fp_fmt[x], buf1, buf2);
+ fail++;
+ }
+ num++;
+ }
+
+ for (x = 0; int_fmt[x] != NULL ; x++)
+ for (y = 0; int_nums[y] != 0 ; y++)
+ {
+ mutt_snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]);
+ sprintf (buf2, int_fmt[x], int_nums[y]);
+ if (strcmp (buf1, buf2))
+ {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n",
+ int_fmt[x], buf1, buf2);
+ fail++;
+ }
+ num++;
+ }
+ printf ("%d tests failed out of %d.\n", fail, num);
+}
+#endif /* SNPRINTF_TEST */
+
+#endif /* !HAVE_SNPRINTF */
diff --git a/libtar/strdup.c b/libtar/strdup.c
new file mode 100644
index 000000000..75e9af505
--- /dev/null
+++ b/libtar/strdup.c
@@ -0,0 +1,62 @@
+/* $OpenBSD: strdup.c,v 1.3 1997/08/20 04:18:52 millert Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)strdup.c 8.1 (Berkeley) 6/4/93";
+#else
+static char *rcsid = "$OpenBSD: strdup.c,v 1.3 1997/08/20 04:18:52 millert Exp $";
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+char *
+openbsd_strdup(str)
+ const char *str;
+{
+ size_t siz;
+ char *copy;
+
+ siz = strlen(str) + 1;
+ if ((copy = malloc(siz)) == NULL)
+ return(NULL);
+ (void)memcpy(copy, str, siz);
+ return(copy);
+}
diff --git a/libtar/strlcat.c b/libtar/strlcat.c
new file mode 100644
index 000000000..39125393c
--- /dev/null
+++ b/libtar/strlcat.c
@@ -0,0 +1,72 @@
+/* $OpenBSD: strlcat.c,v 1.5 2001/01/13 16:17:24 millert Exp $ */
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: strlcat.c,v 1.5 2001/01/13 16:17:24 millert Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Appends src to string dst of size siz (unlike strncat, siz is the
+ * full size of dst, not space left). At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz <= strlen(dst)).
+ * Returns strlen(initial dst) + strlen(src); if retval >= siz,
+ * truncation occurred.
+ */
+size_t strlcat(dst, src, siz)
+ char *dst;
+ const char *src;
+ size_t siz;
+{
+ register char *d = dst;
+ register const char *s = src;
+ register size_t n = siz;
+ size_t dlen;
+
+ /* Find the end of dst and adjust bytes left but don't go past end */
+ while (n-- != 0 && *d != '\0')
+ d++;
+ dlen = d - dst;
+ n = siz - dlen;
+
+ if (n == 0)
+ return(dlen + strlen(s));
+ while (*s != '\0') {
+ if (n != 1) {
+ *d++ = *s;
+ n--;
+ }
+ s++;
+ }
+ *d = '\0';
+
+ return(dlen + (s - src)); /* count does not include NUL */
+}
diff --git a/libtar/strlcpy.c b/libtar/strlcpy.c
new file mode 100644
index 000000000..300a28bc3
--- /dev/null
+++ b/libtar/strlcpy.c
@@ -0,0 +1,68 @@
+/* $OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $ */
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Copy src to string dst of size siz. At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t strlcpy(dst, src, siz)
+ char *dst;
+ const char *src;
+ size_t siz;
+{
+ register char *d = dst;
+ register const char *s = src;
+ register size_t n = siz;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0 && --n != 0) {
+ do {
+ if ((*d++ = *s++) == 0)
+ break;
+ } while (--n != 0);
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++)
+ ;
+ }
+
+ return(s - src - 1); /* count does not include NUL */
+}
diff --git a/libtar/strmode.c b/libtar/strmode.c
new file mode 100644
index 000000000..5e7f15e85
--- /dev/null
+++ b/libtar/strmode.c
@@ -0,0 +1,152 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: strmode.c,v 1.3 1997/06/13 13:57:20 deraadt Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+
+void
+strmode(mode, p)
+ register mode_t mode;
+ register char *p;
+{
+ /* print type */
+ switch (mode & S_IFMT) {
+ case S_IFDIR: /* directory */
+ *p++ = 'd';
+ break;
+ case S_IFCHR: /* character special */
+ *p++ = 'c';
+ break;
+ case S_IFBLK: /* block special */
+ *p++ = 'b';
+ break;
+ case S_IFREG: /* regular */
+ *p++ = '-';
+ break;
+ case S_IFLNK: /* symbolic link */
+ *p++ = 'l';
+ break;
+ case S_IFSOCK: /* socket */
+ *p++ = 's';
+ break;
+#ifdef S_IFIFO
+ case S_IFIFO: /* fifo */
+ *p++ = 'p';
+ break;
+#endif
+#ifdef S_IFWHT
+ case S_IFWHT: /* whiteout */
+ *p++ = 'w';
+ break;
+#endif
+ default: /* unknown */
+ *p++ = '?';
+ break;
+ }
+ /* usr */
+ if (mode & S_IRUSR)
+ *p++ = 'r';
+ else
+ *p++ = '-';
+ if (mode & S_IWUSR)
+ *p++ = 'w';
+ else
+ *p++ = '-';
+ switch (mode & (S_IXUSR | S_ISUID)) {
+ case 0:
+ *p++ = '-';
+ break;
+ case S_IXUSR:
+ *p++ = 'x';
+ break;
+ case S_ISUID:
+ *p++ = 'S';
+ break;
+ case S_IXUSR | S_ISUID:
+ *p++ = 's';
+ break;
+ }
+ /* group */
+ if (mode & S_IRGRP)
+ *p++ = 'r';
+ else
+ *p++ = '-';
+ if (mode & S_IWGRP)
+ *p++ = 'w';
+ else
+ *p++ = '-';
+ switch (mode & (S_IXGRP | S_ISGID)) {
+ case 0:
+ *p++ = '-';
+ break;
+ case S_IXGRP:
+ *p++ = 'x';
+ break;
+ case S_ISGID:
+ *p++ = 'S';
+ break;
+ case S_IXGRP | S_ISGID:
+ *p++ = 's';
+ break;
+ }
+ /* other */
+ if (mode & S_IROTH)
+ *p++ = 'r';
+ else
+ *p++ = '-';
+ if (mode & S_IWOTH)
+ *p++ = 'w';
+ else
+ *p++ = '-';
+ switch (mode & (S_IXOTH | S_ISVTX)) {
+ case 0:
+ *p++ = '-';
+ break;
+ case S_IXOTH:
+ *p++ = 'x';
+ break;
+ case S_ISVTX:
+ *p++ = 'T';
+ break;
+ case S_IXOTH | S_ISVTX:
+ *p++ = 't';
+ break;
+ }
+ *p++ = ' '; /* will be a '+' if ACL's implemented */
+ *p = '\0';
+}
diff --git a/libtar/strrstr.c b/libtar/strrstr.c
new file mode 100644
index 000000000..097ef9182
--- /dev/null
+++ b/libtar/strrstr.c
@@ -0,0 +1,40 @@
+/*
+** Copyright 1998-2002 University of Illinois Board of Trustees
+** Copyright 1998-2002 Mark D. Roth
+** All rights reserved.
+**
+** strrstr.c - strrstr() function for compatibility library
+**
+** Mark D. Roth <roth@uiuc.edu>
+** Campus Information Technologies and Educational Services
+** University of Illinois at Urbana-Champaign
+*/
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <string.h>
+
+
+/*
+** find the last occurrance of find in string
+*/
+char *
+strrstr(char *string, char *find)
+{
+ size_t stringlen, findlen;
+ char *cp;
+
+ findlen = strlen(find);
+ stringlen = strlen(string);
+ if (findlen > stringlen)
+ return NULL;
+
+ for (cp = string + stringlen - findlen; cp >= string; cp--)
+ if (strncmp(cp, find, findlen) == 0)
+ return cp;
+
+ return NULL;
+}
+
+
diff --git a/libtar/strsep.c b/libtar/strsep.c
new file mode 100644
index 000000000..3132962c9
--- /dev/null
+++ b/libtar/strsep.c
@@ -0,0 +1,87 @@
+/* $OpenBSD: strsep.c,v 1.3 1997/08/20 04:28:14 millert Exp $ */
+
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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 <string.h>
+#include <stdio.h>
+
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)strsep.c 8.1 (Berkeley) 6/4/93";
+#else
+static char *rcsid = "$OpenBSD: strsep.c,v 1.3 1997/08/20 04:28:14 millert Exp $";
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+#ifndef HAVE_STRSEP
+/*
+ * Get next token from string *stringp, where tokens are possibly-empty
+ * strings separated by characters from delim.
+ *
+ * Writes NULs into the string at *stringp to end tokens.
+ * delim need not remain constant from call to call.
+ * On return, *stringp points past the last NUL written (if there might
+ * be further tokens), or is NULL (if there are definitely no more tokens).
+ *
+ * If *stringp is NULL, strsep returns NULL.
+ */
+char *
+strsep(stringp, delim)
+ register char **stringp;
+ register const char *delim;
+{
+ register char *s;
+ register const char *spanp;
+ register int c, sc;
+ char *tok;
+
+ if ((s = *stringp) == NULL)
+ return (NULL);
+ for (tok = s;;) {
+ c = *s++;
+ spanp = delim;
+ do {
+ if ((sc = *spanp++) == c) {
+ if (c == 0)
+ s = NULL;
+ else
+ s[-1] = 0;
+ *stringp = s;
+ return (tok);
+ }
+ } while (sc != 0);
+ }
+ /* NOTREACHED */
+}
+#endif /* ! HAVE_STRSEP */
diff --git a/libtar/tar.h b/libtar/tar.h
new file mode 100644
index 000000000..ddfef755d
--- /dev/null
+++ b/libtar/tar.h
@@ -0,0 +1,108 @@
+/* Extended tar format from POSIX.1.
+ Copyright (C) 1992, 1996 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Written by David J. MacKenzie.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#ifndef _TAR_H
+#define _TAR_H 1
+
+/* A tar archive consists of 512-byte blocks.
+ Each file in the archive has a header block followed by 0+ data blocks.
+ Two blocks of NUL bytes indicate the end of the archive. */
+
+/* The fields of header blocks:
+ All strings are stored as ISO 646 (approximately ASCII) strings.
+
+ Fields are numeric unless otherwise noted below; numbers are ISO 646
+ representations of octal numbers, with leading zeros as needed.
+
+ linkname is only valid when typeflag==LNKTYPE. It doesn't use prefix;
+ files that are links to pathnames >100 chars long can not be stored
+ in a tar archive.
+
+ If typeflag=={LNKTYPE,SYMTYPE,DIRTYPE} then size must be 0.
+
+ devmajor and devminor are only valid for typeflag=={BLKTYPE,CHRTYPE}.
+
+ chksum contains the sum of all 512 bytes in the header block,
+ treating each byte as an 8-bit unsigned value and treating the
+ 8 bytes of chksum as blank characters.
+
+ uname and gname are used in preference to uid and gid, if those
+ names exist locally.
+
+ Field Name Byte Offset Length in Bytes Field Type
+ name 0 100 NUL-terminated if NUL fits
+ mode 100 8
+ uid 108 8
+ gid 116 8
+ size 124 12
+ mtime 136 12
+ chksum 148 8
+ typeflag 156 1 see below
+ linkname 157 100 NUL-terminated if NUL fits
+ magic 257 6 must be TMAGIC (NUL term.)
+ version 263 2 must be TVERSION
+ uname 265 32 NUL-terminated
+ gname 297 32 NUL-terminated
+ devmajor 329 8
+ devminor 337 8
+ prefix 345 155 NUL-terminated if NUL fits
+
+ If the first character of prefix is '\0', the file name is name;
+ otherwise, it is prefix/name. Files whose pathnames don't fit in that
+ length can not be stored in a tar archive. */
+
+/* The bits in mode: */
+#define TSUID 04000
+#define TSGID 02000
+#define TSVTX 01000
+#define TUREAD 00400
+#define TUWRITE 00200
+#define TUEXEC 00100
+#define TGREAD 00040
+#define TGWRITE 00020
+#define TGEXEC 00010
+#define TOREAD 00004
+#define TOWRITE 00002
+#define TOEXEC 00001
+
+/* The values for typeflag:
+ Values 'A'-'Z' are reserved for custom implementations.
+ All other values are reserved for future POSIX.1 revisions. */
+
+#define REGTYPE '0' /* Regular file (preferred code). */
+#define AREGTYPE '\0' /* Regular file (alternate code). */
+#define LNKTYPE '1' /* Hard link. */
+#define SYMTYPE '2' /* Symbolic link (hard if not supported). */
+#define CHRTYPE '3' /* Character special. */
+#define BLKTYPE '4' /* Block special. */
+#define DIRTYPE '5' /* Directory. */
+#define FIFOTYPE '6' /* Named pipe. */
+#define CONTTYPE '7' /* Contiguous file */
+ /* (regular file if not supported). */
+
+/* Contents of magic field and its length. */
+#define TMAGIC "ustar"
+#define TMAGLEN 6
+
+/* Contents of the version field and its length. */
+#define TVERSION "00"
+#define TVERSLEN 2
+
+#endif /* tar.h */
diff --git a/libtar/util.c b/libtar/util.c
new file mode 100644
index 000000000..7fb3f51f0
--- /dev/null
+++ b/libtar/util.c
@@ -0,0 +1,221 @@
+/*
+** Copyright 1998-2003 University of Illinois Board of Trustees
+** Copyright 1998-2003 Mark D. Roth
+** All rights reserved.
+**
+** util.c - miscellaneous utility code for libtar
+**
+** Mark D. Roth <roth@uiuc.edu>
+** Campus Information Technologies and Educational Services
+** University of Illinois at Urbana-Champaign
+*/
+
+#include <internal.h>
+
+#include <stdio.h>
+#include <sys/param.h>
+#include <errno.h>
+#include <linux/capability.h>
+
+#ifdef STDC_HEADERS
+# include <string.h>
+#endif
+
+
+/* hashing function for pathnames */
+int
+path_hashfunc(char *key, int numbuckets)
+{
+ char buf[MAXPATHLEN];
+ char *p;
+
+ strcpy(buf, key);
+ p = basename(buf);
+
+ return (((unsigned int)p[0]) % numbuckets);
+}
+
+
+/* matching function for dev_t's */
+int
+dev_match(dev_t *dev1, dev_t *dev2)
+{
+ return !memcmp(dev1, dev2, sizeof(dev_t));
+}
+
+
+/* matching function for ino_t's */
+int
+ino_match(ino_t *ino1, ino_t *ino2)
+{
+ return !memcmp(ino1, ino2, sizeof(ino_t));
+}
+
+
+/* hashing function for dev_t's */
+int
+dev_hash(dev_t *dev)
+{
+ return *dev % 16;
+}
+
+
+/* hashing function for ino_t's */
+int
+ino_hash(ino_t *inode)
+{
+ return *inode % 256;
+}
+
+
+/*
+** mkdirhier() - create all directories in a given path
+** returns:
+** 0 success
+** 1 all directories already exist
+** -1 (and sets errno) error
+*/
+int
+mkdirhier(char *path)
+{
+ char src[MAXPATHLEN], dst[MAXPATHLEN] = "";
+ char *dirp, *nextp = src;
+ int retval = 1;
+
+ if (strlcpy(src, path, sizeof(src)) > sizeof(src))
+ {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+
+ if (path[0] == '/')
+ strcpy(dst, "/");
+
+ while ((dirp = strsep(&nextp, "/")) != NULL)
+ {
+ if (*dirp == '\0')
+ continue;
+
+ if (dst[0] != '\0')
+ strcat(dst, "/");
+ strcat(dst, dirp);
+
+ if (mkdir(dst, 0777) == -1)
+ {
+ if (errno != EEXIST)
+ return -1;
+ }
+ else
+ retval = 0;
+ }
+
+ return retval;
+}
+
+
+/* calculate header checksum */
+int
+th_crc_calc(TAR *t)
+{
+ int i, sum = 0;
+
+ for (i = 0; i < T_BLOCKSIZE; i++)
+ sum += ((unsigned char *)(&(t->th_buf)))[i];
+ for (i = 0; i < 8; i++)
+ sum += (' ' - (unsigned char)t->th_buf.chksum[i]);
+
+ return sum;
+}
+
+/* calculate a signed header checksum */
+int
+th_signed_crc_calc(TAR *t)
+{
+ int i, sum = 0;
+
+ for (i = 0; i < T_BLOCKSIZE; i++)
+ sum += ((signed char *)(&(t->th_buf)))[i];
+ for (i = 0; i < 8; i++)
+ sum += (' ' - (signed char)t->th_buf.chksum[i]);
+
+ return sum;
+}
+
+/* string-octal to integer conversion */
+int64_t
+oct_to_int(char *oct, size_t octlen)
+{
+ long long int val;
+ char tmp[octlen + 1];
+
+ memcpy(tmp, oct, octlen);
+ tmp[octlen] = '\0';
+ return sscanf(oct, "%llo", &val) == 1 ? (int64_t)val : 0;
+}
+
+
+/* string-octal or binary to integer conversion */
+int64_t oct_to_int_ex(char *oct, size_t octlen)
+{
+ if (*(unsigned char *)oct & 0x80) {
+ int64_t val = 0;
+ char tmp[octlen];
+ unsigned char *p;
+ unsigned int i;
+
+ memcpy(tmp, oct, octlen);
+ *tmp &= 0x7f;
+ p = (unsigned char *)tmp + octlen - sizeof(val);
+ for (i = 0; i < sizeof(val); ++i) {
+ val <<= 8;
+ val |= *(p++);
+ }
+ return val;
+ }
+ return oct_to_int(oct, octlen);
+}
+
+
+/* integer to NULL-terminated string-octal conversion */
+void int_to_oct(int64_t num, char *oct, size_t octlen)
+{
+ char tmp[sizeof(num)*3 + 1];
+ int olen;
+
+ olen = sprintf(tmp, "%0*llo", (int)octlen, (long long)num);
+ memcpy(oct, tmp + olen - octlen + 1, octlen);
+}
+
+
+/* integer to string-octal conversion, or binary as necessary */
+void
+int_to_oct_ex(int64_t num, char *oct, size_t octlen)
+{
+ if (num < 0 || num >= ((int64_t)1 << ((octlen - 1) * 3))) {
+ unsigned char *p;
+ unsigned int i;
+
+ memset(oct, 0, octlen);
+ p = (unsigned char *)oct + octlen;
+ for (i = 0; i < sizeof(num); ++i) {
+ *(--p) = num & 0xff;
+ num >>= 8;
+ }
+ if (num < 0) {
+ for (; i < octlen; ++i) {
+ *(--p) = 0xff;
+ }
+ }
+ *(unsigned char *)oct |= 0x80;
+ return;
+ }
+ int_to_oct(num, oct, octlen);
+}
+
+void print_caps(struct vfs_cap_data *cap_data) {
+ printf(" magic_etc=%u \n", cap_data->magic_etc);
+ printf(" data[0].permitted=%u \n", cap_data->data[0].permitted);
+ printf(" data[0].inheritable=%u \n", cap_data->data[0].inheritable);
+ printf(" data[1].permitted=%u \n", cap_data->data[1].permitted);
+ printf(" data[1].inheritable=%u \n", cap_data->data[1].inheritable);
+}
diff --git a/libtar/wrapper.c b/libtar/wrapper.c
new file mode 100644
index 000000000..d3d285f44
--- /dev/null
+++ b/libtar/wrapper.c
@@ -0,0 +1,181 @@
+/*
+** Copyright 1998-2003 University of Illinois Board of Trustees
+** Copyright 1998-2003 Mark D. Roth
+** All rights reserved.
+**
+** wrapper.c - libtar high-level wrapper code
+**
+** Mark D. Roth <roth@uiuc.edu>
+** Campus Information Technologies and Educational Services
+** University of Illinois at Urbana-Champaign
+*/
+
+#include <internal.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <dirent.h>
+#include <errno.h>
+
+#ifdef STDC_HEADERS
+# include <string.h>
+#endif
+
+
+int
+tar_extract_glob(TAR *t, char *globname, char *prefix)
+{
+ char *filename;
+ char buf[MAXPATHLEN];
+ int i, fd = 0;
+
+ while ((i = th_read(t)) == 0)
+ {
+ filename = th_get_pathname(t);
+ if (fnmatch(globname, filename, FNM_PATHNAME | FNM_PERIOD))
+ {
+ if (TH_ISREG(t) && tar_skip_regfile(t))
+ return -1;
+ continue;
+ }
+ if (t->options & TAR_VERBOSE)
+ th_print_long_ls(t);
+ if (prefix != NULL)
+ snprintf(buf, sizeof(buf), "%s/%s", prefix, filename);
+ else
+ strlcpy(buf, filename, sizeof(buf));
+ if (tar_extract_file(t, buf, prefix, &fd) != 0)
+ return -1;
+ }
+
+ return (i == 1 ? 0 : -1);
+}
+
+
+int
+tar_extract_all(TAR *t, char *prefix, const int *progress_fd)
+{
+ char *filename;
+ char buf[MAXPATHLEN];
+ int i;
+
+#ifdef DEBUG
+ printf("==> tar_extract_all(TAR *t, \"%s\")\n",
+ (prefix ? prefix : "(null)"));
+#endif
+
+ while ((i = th_read(t)) == 0)
+ {
+#ifdef DEBUG
+ puts(" tar_extract_all(): calling th_get_pathname()");
+#endif
+ filename = th_get_pathname(t);
+ if (t->options & TAR_VERBOSE)
+ th_print_long_ls(t);
+ if (prefix != NULL)
+ snprintf(buf, sizeof(buf), "%s/%s", prefix, filename);
+ else
+ strlcpy(buf, filename, sizeof(buf));
+#ifdef DEBUG
+ printf(" tar_extract_all(): calling tar_extract_file(t, "
+ "\"%s\")\n", buf);
+#endif
+ if (tar_extract_file(t, buf, prefix, progress_fd) != 0)
+ return -1;
+ }
+
+ return (i == 1 ? 0 : -1);
+}
+
+
+int
+tar_append_tree(TAR *t, char *realdir, char *savedir)
+{
+ char realpath[MAXPATHLEN];
+ char savepath[MAXPATHLEN];
+ struct dirent *dent;
+ DIR *dp;
+ struct stat s;
+
+#ifdef DEBUG
+ printf("==> tar_append_tree(0x%lx, \"%s\", \"%s\")\n",
+ t, realdir, (savedir ? savedir : "[NULL]"));
+#endif
+
+ if (tar_append_file(t, realdir, savedir) != 0)
+ return -1;
+
+#ifdef DEBUG
+ puts(" tar_append_tree(): done with tar_append_file()...");
+#endif
+
+ dp = opendir(realdir);
+ if (dp == NULL)
+ {
+ if (errno == ENOTDIR)
+ return 0;
+ return -1;
+ }
+ while ((dent = readdir(dp)) != NULL)
+ {
+ if (strcmp(dent->d_name, ".") == 0 ||
+ strcmp(dent->d_name, "..") == 0)
+ continue;
+
+ snprintf(realpath, MAXPATHLEN, "%s/%s", realdir,
+ dent->d_name);
+ if (savedir)
+ snprintf(savepath, MAXPATHLEN, "%s/%s", savedir,
+ dent->d_name);
+
+ if (lstat(realpath, &s) != 0)
+ return -1;
+
+ if (S_ISDIR(s.st_mode))
+ {
+ if (tar_append_tree(t, realpath,
+ (savedir ? savepath : NULL)) != 0)
+ return -1;
+ continue;
+ }
+
+ if (tar_append_file(t, realpath,
+ (savedir ? savepath : NULL)) != 0)
+ return -1;
+ }
+
+ closedir(dp);
+
+ return 0;
+}
+
+
+int
+tar_find(TAR *t, char *searchstr)
+{
+ if (!searchstr)
+ return 0;
+
+ char *filename;
+ int i, entryfound = 0;
+#ifdef DEBUG
+ printf("==> tar_find(0x%lx, %s)\n", (long unsigned int)t, searchstr);
+#endif
+ while ((i = th_read(t)) == 0) {
+ filename = th_get_pathname(t);
+ if (fnmatch(searchstr, filename, FNM_FILE_NAME | FNM_PERIOD) == 0) {
+ entryfound++;
+#ifdef DEBUG
+ printf("Found matching entry: %s\n", filename);
+#endif
+ break;
+ }
+ }
+#ifdef DEBUG
+ if (!entryfound)
+ printf("No matching entry found.\n");
+#endif
+
+ return entryfound;
+}