diff options
-rw-r--r-- | libtar/ChangeLog | 342 | ||||
-rw-r--r-- | libtar/append.c | 116 | ||||
-rw-r--r-- | libtar/basename.c | 2 | ||||
-rw-r--r-- | libtar/block.c | 89 | ||||
-rw-r--r-- | libtar/compat.h | 4 | ||||
-rw-r--r-- | libtar/decode.c | 47 | ||||
-rw-r--r-- | libtar/dirname.c | 2 | ||||
-rw-r--r-- | libtar/encode.c | 83 | ||||
-rw-r--r-- | libtar/extract.c | 311 | ||||
-rw-r--r-- | libtar/handle.c | 9 | ||||
-rw-r--r-- | libtar/libtar.h | 94 | ||||
-rw-r--r-- | libtar/libtar_listhash.h | 7 | ||||
-rw-r--r-- | libtar/output.c | 7 | ||||
-rw-r--r-- | libtar/util.c | 71 | ||||
-rw-r--r-- | libtar/wrapper.c | 110 |
15 files changed, 893 insertions, 401 deletions
diff --git a/libtar/ChangeLog b/libtar/ChangeLog index cde7675e2..03bef6874 100644 --- a/libtar/ChangeLog +++ b/libtar/ChangeLog @@ -1,15 +1,335 @@ -2002-12-09 added list_empty() and hash_empty() functions + 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 -2002-09-12 fixed list_iterate function to return -1 if it gets - an invalid argument + Both git downloads and tarball downloads are possible at this site. - include <config.h> and <compat.h> from source files, not - from header file, since header file is sometimes - installed as part of a user-visible API - (those APIs should eventually be redesigned without the - listhash code being publicly visible, but for now we - need to accomodate this) -2002-07-07 modified list iterate function to return int - (returns -1 if plugin function returns -1) +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/append.c b/libtar/append.c index 1831990be..4be679ccd 100644 --- a/libtar/append.c +++ b/libtar/append.c @@ -13,8 +13,11 @@ #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> @@ -28,7 +31,7 @@ #endif #ifdef HAVE_SELINUX -#include "selinux/selinux.h" +# include "selinux/selinux.h" #endif struct tar_dev @@ -57,7 +60,7 @@ tar_dev_free(tar_dev_t *tdp) /* appends a file to the tar archive */ int -tar_append_file(TAR *t, char *realname, char *savename) +tar_append_file(TAR *t, const char *realname, const char *savename) { struct stat s; int i; @@ -82,38 +85,46 @@ tar_append_file(TAR *t, char *realname, char *savename) /* set header block */ #ifdef DEBUG - puts(" tar_append_file(): setting header block..."); + 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..."); + puts("tar_append_file(): setting header path..."); #endif th_set_path(t, (savename ? savename : realname)); #ifdef HAVE_SELINUX /* get selinux context */ - if(t->options & TAR_STORE_SELINUX) { - if(t->th_buf.selinux_context != NULL) { + 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) { + if (lgetfilecon(realname, &selinux_context) >= 0) + { t->th_buf.selinux_context = strdup(selinux_context); - printf("setting selinux context: %s\n", selinux_context); + printf(" ==> set selinux context: %s\n", selinux_context); freecon(selinux_context); } else + { +#ifdef DEBUG perror("Failed to get selinux context"); +#endif + } } #endif + /* check if it's a hardlink */ #ifdef DEBUG - puts(" tar_append_file(): checking inode cache for hardlink..."); + puts("tar_append_file(): checking inode cache for hardlink..."); #endif libtar_hashptr_reset(&hp); if (libtar_hash_getkey(t->h, &hp, &(s.st_dev), @@ -171,7 +182,7 @@ tar_append_file(TAR *t, char *realname, char *savename) i = MAXPATHLEN - 1; path[i] = '\0'; #ifdef DEBUG - printf(" tar_append_file(): encoding symlink \"%s\" -> " + printf("tar_append_file(): encoding symlink \"%s\" -> " "\"%s\"...\n", realname, path); #endif th_set_link(t, path); @@ -179,10 +190,10 @@ tar_append_file(TAR *t, char *realname, char *savename) /* print file info */ if (t->options & TAR_VERBOSE) - th_print_long_ls(t); + printf("%s\n", th_get_pathname(t)); #ifdef DEBUG - puts(" tar_append_file(): writing header"); + puts("tar_append_file(): writing header"); #endif /* write header */ if (th_write(t) != 0) @@ -193,7 +204,7 @@ tar_append_file(TAR *t, char *realname, char *savename) return -1; } #ifdef DEBUG - puts(" tar_append_file(): back from th_write()"); + puts("tar_append_file(): back from th_write()"); #endif /* if it's a regular file, write the contents as well */ @@ -229,14 +240,19 @@ tar_append_eof(TAR *t) /* add file contents to a tarchive */ int -tar_append_regfile(TAR *t, char *realname) +tar_append_regfile(TAR *t, const char *realname) { char block[T_BLOCKSIZE]; int filefd; - int j; - size_t size, i; + 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 @@ -253,25 +269,83 @@ tar_append_regfile(TAR *t, char *realname) { if (j != -1) errno = EINVAL; - return -1; + goto fail; } if (tar_block_write(t, &block) == -1) - return -1; + goto fail; } if (i > 0) { j = read(filefd, &block, i); if (j == -1) - return -1; + goto fail; memset(&(block[i]), 0, T_BLOCKSIZE - i); if (tar_block_write(t, &block) == -1) - return -1; + goto fail; } + /* success! */ + rv = 0; +fail: close(filefd); - return 0; + 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 filefd; + int i, j; + 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 index 2ac1e139e..32108d613 100644 --- a/libtar/basename.c +++ b/libtar/basename.c @@ -64,7 +64,7 @@ openbsd_basename(path) while (startp > path && *(startp - 1) != '/') startp--; - if (endp - startp + 1 > sizeof(bname)) { + if (endp - startp + 1 > (int)sizeof(bname)) { errno = ENAMETOOLONG; return(NULL); } diff --git a/libtar/block.c b/libtar/block.c index 6ed9e6000..5d3c9d826 100644 --- a/libtar/block.c +++ b/libtar/block.c @@ -11,7 +11,6 @@ */ #include <internal.h> -#include <stdio.h> #include <errno.h> #ifdef STDC_HEADERS @@ -27,6 +26,17 @@ #define SELINUX_TAG_LEN 21 /* 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) { @@ -93,8 +103,8 @@ th_read_internal(TAR *t) int th_read(TAR *t) { - int i, j; - size_t sz; + int i; + size_t sz, j, blocks; char *ptr; #ifdef DEBUG @@ -126,21 +136,26 @@ th_read(TAR *t) if (TH_ISLONGLINK(t)) { sz = th_get_size(t); - j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0); + 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, j); + "(%ld bytes, %d blocks)\n", sz, blocks); #endif - t->th_buf.gnu_longlink = (char *)malloc(j * T_BLOCKSIZE); + t->th_buf.gnu_longlink = (char *)malloc(blocks * T_BLOCKSIZE); if (t->th_buf.gnu_longlink == NULL) return -1; - for (ptr = t->th_buf.gnu_longlink; j > 0; - j--, ptr += T_BLOCKSIZE) + 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", j, ptr); + "(%d blocks left, ptr == %ld)\n", blocks-j, ptr); #endif i = tar_block_read(t, ptr); if (i != T_BLOCKSIZE) @@ -171,21 +186,26 @@ th_read(TAR *t) if (TH_ISLONGNAME(t)) { sz = th_get_size(t); - j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0); + 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, j); + "(%ld bytes, %d blocks)\n", sz, blocks); #endif - t->th_buf.gnu_longname = (char *)malloc(j * T_BLOCKSIZE); + t->th_buf.gnu_longname = (char *)malloc(blocks * T_BLOCKSIZE); if (t->th_buf.gnu_longname == NULL) return -1; - for (ptr = t->th_buf.gnu_longname; j > 0; - j--, ptr += T_BLOCKSIZE) + 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", j, ptr); + "(%d blocks left, ptr == %ld)\n", blocks-j, ptr); #endif i = tar_block_read(t, ptr); if (i != T_BLOCKSIZE) @@ -263,41 +283,6 @@ th_read(TAR *t) } #endif -#if 0 - /* - ** work-around for old archive files with broken typeflag fields - ** NOTE: I fixed this in the TH_IS*() macros instead - */ - - /* - ** (directories are signified with a trailing '/') - */ - if (t->th_buf.typeflag == AREGTYPE - && t->th_buf.name[strlen(t->th_buf.name) - 1] == '/') - t->th_buf.typeflag = DIRTYPE; - - /* - ** fallback to using mode bits - */ - if (t->th_buf.typeflag == AREGTYPE) - { - mode = (mode_t)oct_to_int(t->th_buf.mode); - - if (S_ISREG(mode)) - t->th_buf.typeflag = REGTYPE; - else if (S_ISDIR(mode)) - t->th_buf.typeflag = DIRTYPE; - else if (S_ISFIFO(mode)) - t->th_buf.typeflag = FIFOTYPE; - else if (S_ISCHR(mode)) - t->th_buf.typeflag = CHRTYPE; - else if (S_ISBLK(mode)) - t->th_buf.typeflag = BLKTYPE; - else if (S_ISLNK(mode)) - t->th_buf.typeflag = SYMTYPE; - } -#endif - return 0; } @@ -308,7 +293,7 @@ th_write(TAR *t) { int i, j; char type2; - size_t sz, sz2; + uint64_t sz, sz2; char *ptr; char buf[T_BLOCKSIZE]; @@ -457,7 +442,7 @@ th_write(TAR *t) } memset(buf, 0, T_BLOCKSIZE); - snprintf(buf, T_BLOCKSIZE, "%d "SELINUX_TAG"%s\n", sz, t->th_buf.selinux_context); + snprintf(buf, T_BLOCKSIZE, "%d "SELINUX_TAG"%s\n", (int)sz, t->th_buf.selinux_context); i = tar_block_write(t, &buf); if (i != T_BLOCKSIZE) { diff --git a/libtar/compat.h b/libtar/compat.h index d0862943b..70ac2f435 100644 --- a/libtar/compat.h +++ b/libtar/compat.h @@ -12,10 +12,6 @@ # include <libgen.h> #endif -#ifdef HAVE_SELINUX -#include "selinux/selinux.h" -#endif - #if defined(NEED_BASENAME) && !defined(HAVE_BASENAME) diff --git a/libtar/decode.c b/libtar/decode.c index 383306d64..1a3d0ee56 100644 --- a/libtar/decode.c +++ b/libtar/decode.c @@ -13,6 +13,7 @@ #include <internal.h> #include <stdio.h> +#include <stdlib.h> #include <sys/param.h> #include <pwd.h> #include <grp.h> @@ -26,22 +27,30 @@ char * th_get_pathname(TAR *t) { - char filename[MAXPATHLEN]; - - if (t->th_buf.gnu_longname) { - printf("returning gnu longname\n"); + 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') + if (t->th_buf.prefix[0] == '\0') { - snprintf(filename, sizeof(filename), "%.155s/%.100s", + 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); - return strdup(filename); } - snprintf(filename, sizeof(filename), "%.100s", t->th_buf.name); - return strdup(filename); + /* will be deallocated in tar_close() */ + return t->th_pathname; } @@ -51,9 +60,11 @@ th_get_uid(TAR *t) int uid; struct passwd *pw; - pw = getpwnam(t->th_buf.uname); - if (pw != NULL) - return pw->pw_uid; + 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); @@ -67,9 +78,11 @@ th_get_gid(TAR *t) int gid; struct group *gr; - gr = getgrnam(t->th_buf.gname); - if (gr != NULL) - return gr->gr_gid; + 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); @@ -82,7 +95,7 @@ th_get_mode(TAR *t) { mode_t mode; - mode = (mode_t)oct_to_int(t->th_buf.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) @@ -103,7 +116,7 @@ th_get_mode(TAR *t) mode |= S_IFIFO; break; case AREGTYPE: - if (t->th_buf.name[strlen(t->th_buf.name) - 1] == '/') + if (t->th_buf.name[strnlen(t->th_buf.name, T_NAMELEN) - 1] == '/') { mode |= S_IFDIR; break; diff --git a/libtar/dirname.c b/libtar/dirname.c index 986db4aa1..4e060677e 100644 --- a/libtar/dirname.c +++ b/libtar/dirname.c @@ -67,7 +67,7 @@ openbsd_dirname(path) } while (endp > path && *endp == '/'); } - if (endp - path + 1 > sizeof(bname)) { + if (endp - path + 1 > (int)sizeof(bname)) { errno = ENAMETOOLONG; return(NULL); } diff --git a/libtar/encode.c b/libtar/encode.c index 662eff584..c9371527b 100644 --- a/libtar/encode.c +++ b/libtar/encode.c @@ -68,10 +68,11 @@ th_set_type(TAR *t, mode_t mode) /* encode file path */ void -th_set_path(TAR *t, char *pathname) +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); @@ -81,32 +82,49 @@ th_set_path(TAR *t, char *pathname) free(t->th_buf.gnu_longname); t->th_buf.gnu_longname = NULL; - if (pathname[strlen(pathname) - 1] != '/' && TH_ISDIR(t)) + /* old archive compatibility (not needed for gnu): add trailing / to directories */ + if (pathname[pathname_len - 1] != '/' && TH_ISDIR(t)) strcpy(suffix, "/"); - if (strlen(pathname) > T_NAMELEN-1 && (t->options & TAR_GNU)) + if (pathname_len >= T_NAMELEN && (t->options & TAR_GNU)) { - /* GNU-style long name */ + /* 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 (strlen(pathname) > T_NAMELEN) + else if (pathname_len >= T_NAMELEN) { - /* POSIX-style prefix field */ - tmp = strchr(&(pathname[strlen(pathname) - T_NAMELEN - 1]), '/'); + /* 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(t->th_buf.name, 100, "%s%s", &(tmp[1]), suffix); - snprintf(t->th_buf.prefix, - ((tmp - pathname + 1) < - 155 ? (tmp - pathname + 1) : 155), "%s", pathname); + 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); } - else - /* classic tar format */ - snprintf(t->th_buf.name, 100, "%s%s", pathname, suffix); #ifdef DEBUG puts("returning from th_set_path()..."); @@ -116,23 +134,28 @@ th_set_path(TAR *t, char *pathname) /* encode link path */ void -th_set_link(TAR *t, char *linkname) +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-1 && (t->options & TAR_GNU)) + if (strlen(linkname) >= T_NAMELEN && (t->options & TAR_GNU)) { - /* GNU longlink format */ + /* --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 + else if (strlen(linkname) >= T_NAMELEN) { - /* classic tar format */ - strlcpy(t->th_buf.linkname, linkname, - sizeof(t->th_buf.linkname)); + /* --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; @@ -159,9 +182,11 @@ th_set_user(TAR *t, uid_t uid) { struct passwd *pw; - pw = getpwuid(uid); - if (pw != NULL) - strlcpy(t->th_buf.uname, pw->pw_name, sizeof(t->th_buf.uname)); + 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); } @@ -173,9 +198,11 @@ th_set_group(TAR *t, gid_t gid) { struct group *gr; - gr = getgrgid(gid); - if (gr != NULL) - strlcpy(t->th_buf.gname, gr->gr_name, sizeof(t->th_buf.gname)); + 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); } @@ -209,5 +236,3 @@ th_set_from_stat(TAR *t, struct stat *s) else th_set_size(t, 0); } - - diff --git a/libtar/extract.c b/libtar/extract.c index 69e08bd54..257e140d5 100644 --- a/libtar/extract.c +++ b/libtar/extract.c @@ -13,14 +13,13 @@ #include <internal.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 <string.h> -#define DEBUG #ifdef STDC_HEADERS # include <stdlib.h> #endif @@ -29,26 +28,30 @@ # include <unistd.h> #endif -#define DEBUG +#ifdef HAVE_SELINUX +# include "selinux/selinux.h" +#endif static int -tar_set_file_perms(TAR *t, char *realname) +tar_set_file_perms(TAR *t, const char *realname) { mode_t mode; uid_t uid; gid_t gid; struct utimbuf ut; - char *filename; + const char *filename; + char *pn; - filename = (realname ? realname : th_get_pathname(t)); + 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(" ==> setting perms: %s (mode %04o, uid %d, gid %d)\n", - filename, mode, uid, gid); + printf("tar_set_file_perms(): setting perms: %s (mode %04o, uid %d, gid %d)\n", + filename, mode, uid, gid); #endif /* change owner/group */ @@ -95,12 +98,15 @@ tar_set_file_perms(TAR *t, char *realname) /* switchboard */ int -tar_extract_file(TAR *t, char *realname, char *prefix, const int *progress_fd) +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) { @@ -115,44 +121,31 @@ tar_extract_file(TAR *t, char *realname, char *prefix, const int *progress_fd) if (TH_ISDIR(t)) { - printf("dir\n"); i = tar_extract_dir(t, realname); if (i == 1) i = 0; } - else if (TH_ISLNK(t)) { - printf("link\n"); + else if (TH_ISLNK(t)) i = tar_extract_hardlink(t, realname, prefix); - } - else if (TH_ISSYM(t)) { - printf("sym\n"); + else if (TH_ISSYM(t)) i = tar_extract_symlink(t, realname); - } - else if (TH_ISCHR(t)) { - printf("chr\n"); + else if (TH_ISCHR(t)) i = tar_extract_chardev(t, realname); - } - else if (TH_ISBLK(t)) { - printf("blk\n"); + else if (TH_ISBLK(t)) i = tar_extract_blockdev(t, realname); - } - else if (TH_ISFIFO(t)) { - printf("fifo\n"); + else if (TH_ISFIFO(t)) i = tar_extract_fifo(t, realname); - } - else /* if (TH_ISREG(t)) */ { - printf("reg\n"); + else /* if (TH_ISREG(t)) */ i = tar_extract_regfile(t, realname, progress_fd); - } if (i != 0) { - printf("FAILED RESTORE OF FILE i: %s\n", realname); + fprintf(stderr, "tar_extract_file(): failed to extract %s !!!\n", realname); return i; } i = tar_set_file_perms(t, realname); if (i != 0) { - printf("FAILED SETTING PERMS: %d\n", i); + fprintf(stderr, "tar_extract_file(): failed to set permissions on %s !!!\n", realname); return i; } @@ -160,51 +153,48 @@ tar_extract_file(TAR *t, char *realname, char *prefix, const int *progress_fd) if((t->options & TAR_STORE_SELINUX) && t->th_buf.selinux_context != NULL) { #ifdef DEBUG - printf(" Restoring SELinux context %s to file %s\n", t->th_buf.selinux_context, realname); + 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, "Failed to restore SELinux context %s!\n", strerror(errno)); - } + 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); } #endif -/* - pathname_len = strlen(th_get_pathname(t)) + 1; +#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], th_get_pathname(t)); + strcpy(&lnp[0], pn); strcpy(&lnp[pathname_len], realname); #ifdef DEBUG printf("tar_extract_file(): calling libtar_hash_add(): key=\"%s\", " - "value=\"%s\"\n", th_get_pathname(t), realname); + "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, char *realname, const int *progress_fd) +tar_extract_regfile(TAR *t, const char *realname, const int *progress_fd) { - //mode_t mode; - size_t size, i; - //uid_t uid; - //gid_t gid; + int64_t size, i; + ssize_t k; int fdout; - int k; char buf[T_BLOCKSIZE]; - char *filename; + const char *filename; + char *pn; - fflush(NULL); #ifdef DEBUG - printf("==> tar_extract_regfile(t=0x%lx, realname=\"%s\")\n", t, - realname); + printf(" ==> tar_extract_regfile(realname=\"%s\")\n", realname); #endif if (!TH_ISREG(t)) @@ -213,21 +203,16 @@ tar_extract_regfile(TAR *t, char *realname, const int *progress_fd) return -1; } - filename = (realname ? realname : th_get_pathname(t)); - //mode = th_get_mode(t); + pn = th_get_pathname(t); + filename = (realname ? realname : pn); size = th_get_size(t); - //uid = th_get_uid(t); - //gid = th_get_gid(t); if (mkdirhier(dirname(filename)) == -1) return -1; -#ifdef DEBUG - //printf(" ==> extracting: %s (mode %04o, uid %d, gid %d, %d bytes)\n", - // filename, mode, uid, gid, size); - printf(" ==> extracting: %s (file size %d bytes)\n", - filename, size); -#endif + printf(" ==> extracting: %s (file size %lld bytes)\n", + filename, size); + fdout = open(filename, O_WRONLY | O_CREAT | O_TRUNC #ifdef O_BINARY | O_BINARY @@ -241,41 +226,25 @@ tar_extract_regfile(TAR *t, char *realname, const int *progress_fd) return -1; } -#if 0 - /* change the owner. (will only work if run as root) */ - if (fchown(fdout, uid, gid) == -1 && errno != EPERM) - { -#ifdef DEBUG - perror("fchown()"); -#endif - return -1; - } - - /* make sure the mode isn't inheritted from a file we're overwriting */ - if (fchmod(fdout, mode & 07777) == -1) - { -#ifdef DEBUG - perror("fchmod()"); -#endif - return -1; - } -#endif - /* extract the file */ - for (i = size; i > 0; i -= tar_min(i, T_BLOCKSIZE)) + 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; + } } /* close output file */ @@ -286,7 +255,8 @@ tar_extract_regfile(TAR *t, char *realname, const int *progress_fd) printf("### done extracting %s\n", filename); #endif - if (*progress_fd != 0) { + if (*progress_fd != 0) + { unsigned long long file_size = (unsigned long long)(size); write(*progress_fd, &file_size, sizeof(file_size)); } @@ -299,8 +269,8 @@ tar_extract_regfile(TAR *t, char *realname, const int *progress_fd) int tar_skip_regfile(TAR *t) { - int k; - size_t size, i; + int64_t size, i; + ssize_t k; char buf[T_BLOCKSIZE]; if (!TH_ISREG(t)) @@ -310,7 +280,7 @@ tar_skip_regfile(TAR *t) } size = th_get_size(t); - for (i = size; i > 0; i -= tar_min(i, T_BLOCKSIZE)) + for (i = size; i > 0; i -= T_BLOCKSIZE) { k = tar_block_read(t, buf); if (k != T_BLOCKSIZE) @@ -327,10 +297,12 @@ tar_skip_regfile(TAR *t) /* hardlink */ int -tar_extract_hardlink(TAR * t, char *realname, char *prefix) +tar_extract_hardlink(TAR * t, const char *realname, const char *prefix) { - char *filename; + const char *filename; + char *pn; char *linktgt = NULL; + char *newtgt = NULL; char *lnp; libtar_hashptr_t hp; @@ -340,9 +312,12 @@ tar_extract_hardlink(TAR * t, char *realname, char *prefix) return -1; } - filename = (realname ? realname : th_get_pathname(t)); + 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) @@ -352,17 +327,15 @@ tar_extract_hardlink(TAR * t, char *realname, char *prefix) } else linktgt = th_get_linkname(t); - char *newtgt = strdup(linktgt); + + newtgt = strdup(linktgt); sprintf(linktgt, "%s/%s", prefix, newtgt); -#ifdef DEBUG + printf(" ==> extracting: %s (link to %s)\n", filename, linktgt); -#endif + if (link(linktgt, filename) == -1) { -#ifdef DEBUG - perror("link()"); -#endif - printf("Failed restore of hardlink '%s' but returning as if nothing bad happened anyway\n", filename); + 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 } @@ -372,33 +345,28 @@ tar_extract_hardlink(TAR * t, char *realname, char *prefix) /* symlink */ int -tar_extract_symlink(TAR *t, char *realname) +tar_extract_symlink(TAR *t, const char *realname) { - char *filename; + const char *filename; + char *pn; if (!TH_ISSYM(t)) { - printf("not a sym\n"); errno = EINVAL; return -1; } - filename = (realname ? realname : th_get_pathname(t)); - printf("file: %s\n", filename); - if (mkdirhier(dirname(filename)) == -1) { - printf("mkdirhier\n"); + pn = th_get_pathname(t); + filename = (realname ? realname : pn); + if (mkdirhier(dirname(filename)) == -1) return -1; - } - if (unlink(filename) == -1 && errno != ENOENT) { - printf("unlink\n"); + if (unlink(filename) == -1 && errno != ENOENT) return -1; - } -#ifdef DEBUG printf(" ==> extracting: %s (symlink to %s)\n", filename, th_get_linkname(t)); -#endif + if (symlink(th_get_linkname(t), filename) == -1) { #ifdef DEBUG @@ -413,11 +381,12 @@ tar_extract_symlink(TAR *t, char *realname) /* character device */ int -tar_extract_chardev(TAR *t, char *realname) +tar_extract_chardev(TAR *t, const char *realname) { mode_t mode; unsigned long devmaj, devmin; - char *filename; + const char *filename; + char *pn; if (!TH_ISCHR(t)) { @@ -425,7 +394,8 @@ tar_extract_chardev(TAR *t, char *realname) return -1; } - filename = (realname ? realname : th_get_pathname(t)); + pn = th_get_pathname(t); + filename = (realname ? realname : pn); mode = th_get_mode(t); devmaj = th_get_devmajor(t); devmin = th_get_devminor(t); @@ -433,17 +403,14 @@ tar_extract_chardev(TAR *t, char *realname) if (mkdirhier(dirname(filename)) == -1) return -1; -#ifdef DEBUG printf(" ==> extracting: %s (character device %ld,%ld)\n", filename, devmaj, devmin); -#endif + if (mknod(filename, mode | S_IFCHR, compat_makedev(devmaj, devmin)) == -1) { -#ifdef DEBUG - printf("mknod() failed, returning good anyway"); -#endif - return 0; + 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; @@ -452,11 +419,12 @@ tar_extract_chardev(TAR *t, char *realname) /* block device */ int -tar_extract_blockdev(TAR *t, char *realname) +tar_extract_blockdev(TAR *t, const char *realname) { mode_t mode; unsigned long devmaj, devmin; - char *filename; + const char *filename; + char *pn; if (!TH_ISBLK(t)) { @@ -464,7 +432,8 @@ tar_extract_blockdev(TAR *t, char *realname) return -1; } - filename = (realname ? realname : th_get_pathname(t)); + pn = th_get_pathname(t); + filename = (realname ? realname : pn); mode = th_get_mode(t); devmaj = th_get_devmajor(t); devmin = th_get_devminor(t); @@ -472,17 +441,14 @@ tar_extract_blockdev(TAR *t, char *realname) if (mkdirhier(dirname(filename)) == -1) return -1; -#ifdef DEBUG printf(" ==> extracting: %s (block device %ld,%ld)\n", filename, devmaj, devmin); -#endif + if (mknod(filename, mode | S_IFBLK, compat_makedev(devmaj, devmin)) == -1) { -#ifdef DEBUG - printf("mknod() failed but returning anyway"); -#endif - return 0; + 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; @@ -491,35 +457,45 @@ tar_extract_blockdev(TAR *t, char *realname) /* directory */ int -tar_extract_dir(TAR *t, char *realname) +tar_extract_dir(TAR *t, const char *realname) { mode_t mode; - char *filename; + const char *filename; + char *pn; + if (!TH_ISDIR(t)) { errno = EINVAL; return -1; } - - filename = (realname ? realname : th_get_pathname(t)); + pn = th_get_pathname(t); + filename = (realname ? realname : pn); mode = th_get_mode(t); - if (mkdirhier(dirname(filename)) == -1) { - printf("tar_extract_dir mkdirhier failed\n"); + if (mkdirhier(dirname(filename)) == -1) return -1; - } -#ifdef DEBUG printf(" ==> extracting: %s (mode %04o, directory)\n", filename, mode); -#endif + if (mkdir(filename, mode) == -1) { if (errno == EEXIST) { + if (chmod(filename, mode) == -1) + { +#ifdef DEBUG + perror("chmod()"); +#endif + return -1; + } + else + { #ifdef DEBUG - printf(" *** using existing directory"); + puts(" *** using existing directory"); #endif + return 1; + } } else { @@ -536,10 +512,11 @@ tar_extract_dir(TAR *t, char *realname) /* FIFO */ int -tar_extract_fifo(TAR *t, char *realname) +tar_extract_fifo(TAR *t, const char *realname) { mode_t mode; - char *filename; + const char *filename; + char *pn; if (!TH_ISFIFO(t)) { @@ -547,15 +524,16 @@ tar_extract_fifo(TAR *t, char *realname) return -1; } - filename = (realname ? realname : th_get_pathname(t)); + pn = th_get_pathname(t); + filename = (realname ? realname : pn); mode = th_get_mode(t); if (mkdirhier(dirname(filename)) == -1) return -1; -#ifdef DEBUG + printf(" ==> extracting: %s (fifo)\n", filename); -#endif + if (mkfifo(filename, mode) == -1) { #ifdef DEBUG @@ -567,4 +545,57 @@ tar_extract_fifo(TAR *t, char *realname) 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/handle.c b/libtar/handle.c index ae974b9f0..28a7dc247 100644 --- a/libtar/handle.c +++ b/libtar/handle.c @@ -31,7 +31,7 @@ static tartype_t default_type = { open, close, read, write }; static int -tar_init(TAR **t, char *pathname, tartype_t *type, +tar_init(TAR **t, const char *pathname, tartype_t *type, int oflags, int mode, int options) { if ((oflags & O_ACCMODE) == O_RDWR) @@ -66,7 +66,7 @@ tar_init(TAR **t, char *pathname, tartype_t *type, /* open a new tarfile handle */ int -tar_open(TAR **t, char *pathname, tartype_t *type, +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) @@ -82,6 +82,7 @@ tar_open(TAR **t, char *pathname, tartype_t *type, (*t)->fd = (*((*t)->type->openfunc))(pathname, oflags, mode); if ((*t)->fd == -1) { + libtar_hash_free((*t)->h, NULL); free(*t); return -1; } @@ -91,7 +92,7 @@ tar_open(TAR **t, char *pathname, tartype_t *type, int -tar_fdopen(TAR **t, int fd, char *pathname, tartype_t *type, +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) @@ -121,6 +122,8 @@ tar_close(TAR *t) 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/libtar.h b/libtar/libtar.h index d2c4d003c..4a513754f 100644 --- a/libtar/libtar.h +++ b/libtar/libtar.h @@ -26,6 +26,7 @@ extern "C" /* useful constants */ +/* see FIXME note in block.c regarding T_BLOCKSIZE */ #define T_BLOCKSIZE 512 #define T_NAMELEN 100 #define T_PREFIXLEN 155 @@ -85,12 +86,15 @@ tartype_t; typedef struct { tartype_t *type; - char *pathname; + 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; @@ -103,6 +107,7 @@ TAR; #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 */ /* this is obsolete - it's here for backwards-compatibility only */ #define TAR_IGNORE_MAGIC 0 @@ -111,11 +116,11 @@ extern const char libtar_version[]; /* open a new tarfile handle */ -int tar_open(TAR **t, char *pathname, tartype_t *type, +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, char *pathname, tartype_t *type, +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 */ @@ -139,14 +144,27 @@ void tar_dev_free(struct tar_dev *tdp); * realname = path of file to append * savename = name to save the file under in the archive */ -int tar_append_file(TAR *t, char *realname, char *savename); +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, char *realname); +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 *************************************************************/ @@ -167,31 +185,32 @@ int th_write(TAR *t); #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)) \ + || (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))) + || 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))) + || 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))) + || 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)) \ + || S_ISDIR((mode_t)oct_to_int((t)->th_buf.mode, sizeof((t)->th_buf.mode))) \ || ((t)->th_buf.typeflag == AREGTYPE \ - && ((t)->th_buf.name[strlen((t)->th_buf.name) - 1] == '/'))) + && 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))) + || 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) /* decode tar header info */ -#define th_get_crc(t) oct_to_int((t)->th_buf.chksum) -#define th_get_size(t) oct_to_int((t)->th_buf.size) -#define th_get_mtime(t) oct_to_int((t)->th_buf.mtime) -#define th_get_devmajor(t) oct_to_int((t)->th_buf.devmajor) -#define th_get_devminor(t) oct_to_int((t)->th_buf.devminor) +#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) @@ -205,16 +224,16 @@ gid_t th_get_gid(TAR *t); /* encode file info in th_header */ void th_set_type(TAR *t, mode_t mode); -void th_set_path(TAR *t, char *pathname); -void th_set_link(TAR *t, char *linkname); +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_nonull((fmtime), (t)->th_buf.mtime, 12) + int_to_oct_ex((fmtime), (t)->th_buf.mtime, sizeof((t)->th_buf.mtime)) #define th_set_size(t, fsize) \ - int_to_oct_nonull((fsize), (t)->th_buf.size, 12) + 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); @@ -226,20 +245,22 @@ void th_finish(TAR *t); /***** extract.c ***********************************************************/ /* sequentially extract next file from t */ -int tar_extract_file(TAR *t, char *realname, char *prefix, const int *progress_fd); +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, char *realname); -int tar_extract_hardlink(TAR *t, char *realname, char *prefix); -int tar_extract_symlink(TAR *t, char *realname); -int tar_extract_chardev(TAR *t, char *realname); -int tar_extract_blockdev(TAR *t, char *realname); -int tar_extract_fifo(TAR *t, char *realname); +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, char *realname, const int *progress_fd); +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 ************************************************************/ @@ -280,16 +301,17 @@ int th_signed_crc_calc(TAR *t); #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 */ -int oct_to_int(char *oct); +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 */ -#define int_to_oct(num, oct, octlen) \ - snprintf((oct), (octlen), "%*lo ", (octlen) - 2, (unsigned long)(num)) +void int_to_oct(int64_t num, char *oct, size_t octlen); -/* integer to string-octal conversion, no NULL */ -void int_to_oct_nonull(int 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); -#define tar_min(x, y) (x < y ? x : y) /***** wrapper.c **********************************************************/ @@ -298,7 +320,7 @@ 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, char *exclude); +int tar_append_tree(TAR *t, char *realdir, char *savedir); /* find an entry */ int tar_find(TAR *t, char *searchstr); diff --git a/libtar/libtar_listhash.h b/libtar/libtar_listhash.h index fa33cfd25..48c0d7409 100644 --- a/libtar/libtar_listhash.h +++ b/libtar/libtar_listhash.h @@ -15,6 +15,9 @@ #ifndef libtar_LISTHASH_H #define libtar_LISTHASH_H +#ifdef __cplusplus +extern "C" { +#endif /***** list.c **********************************************************/ @@ -192,5 +195,9 @@ int libtar_hash_add(libtar_hash_t *, void *); 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 index a2db9293c..d2bf8bb0c 100644 --- a/libtar/output.c +++ b/libtar/output.c @@ -13,6 +13,7 @@ #include <internal.h> #include <stdio.h> +#include <stdlib.h> #include <pwd.h> #include <grp.h> #include <time.h> @@ -83,14 +84,14 @@ th_print_long_ls(TAR *t) uid = th_get_uid(t); pw = getpwuid(uid); - if (pw == NULL) + 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 (gr == NULL) + if ((t->options & TAR_USE_NUMERIC_ID) || gr == NULL) snprintf(groupname, sizeof(groupname), "%d", gid); else strlcpy(groupname, gr->gr_name, sizeof(groupname)); @@ -99,7 +100,7 @@ th_print_long_ls(TAR *t) printf("%.10s %-8.8s %-8.8s ", modestring, username, groupname); if (TH_ISCHR(t) || TH_ISBLK(t)) - printf(" %3d, %3d ", th_get_devmajor(t), th_get_devminor(t)); + printf(" %3d, %3d ", (int)th_get_devmajor(t), (int)th_get_devminor(t)); else printf("%9ld ", (long)th_get_size(t)); diff --git a/libtar/util.c b/libtar/util.c index 31e831507..f472f38b5 100644 --- a/libtar/util.c +++ b/libtar/util.c @@ -126,7 +126,6 @@ th_crc_calc(TAR *t) return sum; } - /* calculate a signed header checksum */ int th_signed_crc_calc(TAR *t) @@ -141,25 +140,73 @@ th_signed_crc_calc(TAR *t) return sum; } - /* string-octal to integer conversion */ -int -oct_to_int(char *oct) +int64_t +oct_to_int(char *oct, size_t octlen) { - int i; + 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; +} - sscanf(oct, "%o", &i); - return i; +/* 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 string-octal conversion, no NULL */ -void -int_to_oct_nonull(int num, char *oct, size_t octlen) +/* integer to NULL-terminated string-octal conversion */ +void int_to_oct(int64_t num, char *oct, size_t octlen) { - snprintf(oct, octlen, "%*lo", octlen - 1, (unsigned long)num); - oct[octlen - 1] = ' '; + 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); +} diff --git a/libtar/wrapper.c b/libtar/wrapper.c index 82f045f7d..d3d285f44 100644 --- a/libtar/wrapper.c +++ b/libtar/wrapper.c @@ -10,18 +10,19 @@ ** University of Illinois at Urbana-Champaign */ -#define DEBUG #include <internal.h> #include <stdio.h> +#include <stdlib.h> #include <sys/param.h> #include <dirent.h> #include <errno.h> -#include <stdlib.h> + #ifdef STDC_HEADERS # include <string.h> #endif + int tar_extract_glob(TAR *t, char *globname, char *prefix) { @@ -44,7 +45,7 @@ tar_extract_glob(TAR *t, char *globname, char *prefix) snprintf(buf, sizeof(buf), "%s/%s", prefix, filename); else strlcpy(buf, filename, sizeof(buf)); - if (tar_extract_file(t, filename, prefix, &fd) != 0) + if (tar_extract_file(t, buf, prefix, &fd) != 0) return -1; } @@ -58,11 +59,12 @@ tar_extract_all(TAR *t, char *prefix, const int *progress_fd) char *filename; char buf[MAXPATHLEN]; int i; - printf("prefix: %s\n", prefix); + #ifdef DEBUG printf("==> tar_extract_all(TAR *t, \"%s\")\n", (prefix ? prefix : "(null)")); #endif + while ((i = th_read(t)) == 0) { #ifdef DEBUG @@ -79,105 +81,71 @@ tar_extract_all(TAR *t, char *prefix, const int *progress_fd) printf(" tar_extract_all(): calling tar_extract_file(t, " "\"%s\")\n", buf); #endif - printf("item name: '%s'\n", filename); 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 *exclude) +tar_append_tree(TAR *t, char *realdir, char *savedir) { -#ifdef DEBUG - printf("==> tar_append_tree(0x%lx, \"%s\", \"%s\")\n", - (long unsigned int)t, realdir, (savedir ? savedir : "[NULL]")); -#endif - - char temp[1024]; - int skip = 0, i, n_spaces = 0; - char ** excluded = NULL; - char * p = NULL; - if (exclude) { - strcpy(temp, exclude); - p = strtok(exclude, " "); - if (p == NULL) { - excluded = realloc(excluded, sizeof(char*) * (++n_spaces)); - excluded[0] = temp; - } else { - while (p) { - excluded = realloc(excluded, sizeof(char*) * (++n_spaces)); - excluded[n_spaces-1] = p; - p = strtok(NULL, " "); - } - } - excluded = realloc(excluded, sizeof(char*) * (n_spaces+1)); - excluded[n_spaces] = 0; - for (i = 0; i < (n_spaces+1); i++) { - if (realdir == excluded[i]) { - printf(" excluding '%s'\n", excluded[i]); - skip = 1; - break; - } - } - } - if (skip == 0) { - if (tar_append_file(t, realdir, savedir) != 0) - return -1; - } - 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 (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) + while ((dent = readdir(dp)) != NULL) + { + if (strcmp(dent->d_name, ".") == 0 || + strcmp(dent->d_name, "..") == 0) continue; - if (exclude) { - int omit = 0; - for (i = 0; i < (n_spaces+1); i++) { - if (excluded[i] != NULL) { - if (strcmp(dent->d_name, excluded[i]) == 0 || strcmp(excluded[i], realdir) == 0) { - printf(" excluding '%s'\n", excluded[i]); - omit = 1; - break; - } - } - } - if (omit) - continue; - } - - snprintf(realpath, MAXPATHLEN, "%s/%s", realdir, dent->d_name); + snprintf(realpath, MAXPATHLEN, "%s/%s", realdir, + dent->d_name); if (savedir) - snprintf(savepath, MAXPATHLEN, "%s/%s", savedir, dent->d_name); + 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), (exclude ? exclude : NULL)) != 0) - return -1; - continue; - } else { - if (tar_append_file(t, realpath, (savedir ? savepath : NULL)) != 0) + 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); - free(excluded); return 0; } |