From 6f57f7c60167b11e0e7769310aa8878d2980a3da Mon Sep 17 00:00:00 2001 From: James Christopher Adduono Date: Tue, 1 Mar 2016 16:01:53 -0500 Subject: Merge code from upstream libtar + bug fixes All updates and fixes applied from upstream libtar as of March 1, 2016. Debug flag is disabled, however non-debug output now provides 1 line of useful output per object extracted. I've also merged some fixes from CyanogenMod's fork of libtar: From: Tom Marshall Date: Thu, 11 Feb 2016 16:24:40 -0800 Subject: libtar: Cleanup, secure, and extend numeric fields Commit: e18b457ea1cbf6be1adc3b75450ed1c737cd82ea From: Tom Marshall Date: Thu, 11 Feb 2016 12:49:30 -0800 Subject: libtar: Make file sizes 64-bit clean Commit: e628c2025549a24018bc568351465130a05daafb From: Tom Marshall Date: Thu, 17 Apr 2014 09:39:25 -0700 Subject: libtar: Add methods for in-memory files Commit: 8ec5627a8ff0a91724c6d5b344f0e887da922527 From: Tom Marshall Date: Wed, 2 Jul 2014 09:34:40 -0700 Subject: libtar: Fix hardlink extract Commit: 166d83a51e0c51abcea37694dbd7df92d03c1f56 From: philz-cwm6 Date: Sat, 26 Apr 2014 01:11:35 +0200 Subject: libtar: Various bug fixes and enhancements Commit: a271d763e94235ccee9ecaabdb52bf4b9b2f8c06 (Some of this was not merged in, as better solutions were available from upstream libtar) From: Tom Marshall Date: Wed, 9 Apr 2014 09:35:54 -0700 Subject: libtar: Add const qualifiers to reduce compile warnings Commit: 0600afa19fe827d06d3fcf24a7aabd52dbf487b4 Change-Id: I6d008cb6fdf950f835bbed63aeb8727cc5c86083 --- libtar/extract.c | 311 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 171 insertions(+), 140 deletions(-) (limited to 'libtar/extract.c') 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 #include +#include #include #include #include #include #include -#include -#define DEBUG #ifdef STDC_HEADERS # include #endif @@ -29,26 +28,30 @@ # include #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; +} -- cgit v1.2.3