diff options
Diffstat (limited to 'libtar/extract.c')
-rw-r--r-- | libtar/extract.c | 570 |
1 files changed, 570 insertions, 0 deletions
diff --git a/libtar/extract.c b/libtar/extract.c new file mode 100644 index 000000000..69e08bd54 --- /dev/null +++ b/libtar/extract.c @@ -0,0 +1,570 @@ +/* +** 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 <stdio.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 + +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +#define DEBUG + +static int +tar_set_file_perms(TAR *t, char *realname) +{ + mode_t mode; + uid_t uid; + gid_t gid; + struct utimbuf ut; + char *filename; + + filename = (realname ? realname : th_get_pathname(t)); + 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); +#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, char *realname, char *prefix, const int *progress_fd) +{ + int i; + char *lnp; + int pathname_len; + int realname_len; + + if (t->options & TAR_NOOVERWRITE) + { + struct stat s; + + if (lstat(realname, &s) == 0 || errno != ENOENT) + { + errno = EEXIST; + return -1; + } + } + + 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"); + i = tar_extract_hardlink(t, realname, prefix); + } + else if (TH_ISSYM(t)) { + printf("sym\n"); + i = tar_extract_symlink(t, realname); + } + else if (TH_ISCHR(t)) { + printf("chr\n"); + i = tar_extract_chardev(t, realname); + } + else if (TH_ISBLK(t)) { + printf("blk\n"); + i = tar_extract_blockdev(t, realname); + } + else if (TH_ISFIFO(t)) { + printf("fifo\n"); + i = tar_extract_fifo(t, realname); + } + else /* if (TH_ISREG(t)) */ { + printf("reg\n"); + i = tar_extract_regfile(t, realname, progress_fd); + } + + if (i != 0) { + printf("FAILED RESTORE OF FILE i: %s\n", realname); + return i; + } + + i = tar_set_file_perms(t, realname); + if (i != 0) { + printf("FAILED SETTING PERMS: %d\n", i); + return i; + } + +#ifdef HAVE_SELINUX + 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); +#endif + if (lsetfilecon(realname, t->th_buf.selinux_context) < 0) { + fprintf(stderr, "Failed to restore SELinux context %s!\n", strerror(errno)); + } + } +#endif + +/* + pathname_len = strlen(th_get_pathname(t)) + 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[pathname_len], realname); +#ifdef DEBUG + printf("tar_extract_file(): calling libtar_hash_add(): key=\"%s\", " + "value=\"%s\"\n", th_get_pathname(t), realname); +#endif + if (libtar_hash_add(t->h, lnp) != 0) + return -1; + free(lnp); +*/ + return 0; +} + + +/* extract regular file */ +int +tar_extract_regfile(TAR *t, char *realname, const int *progress_fd) +{ + //mode_t mode; + size_t size, i; + //uid_t uid; + //gid_t gid; + int fdout; + int k; + char buf[T_BLOCKSIZE]; + char *filename; + + fflush(NULL); +#ifdef DEBUG + printf("==> tar_extract_regfile(t=0x%lx, realname=\"%s\")\n", t, + realname); +#endif + + if (!TH_ISREG(t)) + { + errno = EINVAL; + return -1; + } + + filename = (realname ? realname : th_get_pathname(t)); + //mode = th_get_mode(t); + 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 + 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; + } + +#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)) + { + k = tar_block_read(t, buf); + if (k != T_BLOCKSIZE) + { + if (k != -1) + errno = EINVAL; + return -1; + } + + /* write block to output file */ + if (write(fdout, buf, + ((i > T_BLOCKSIZE) ? T_BLOCKSIZE : i)) == -1) + return -1; + } + + /* close output file */ + if (close(fdout) == -1) + return -1; + +#ifdef DEBUG + printf("### done extracting %s\n", filename); +#endif + + if (*progress_fd != 0) { + unsigned long long file_size = (unsigned long long)(size); + write(*progress_fd, &file_size, sizeof(file_size)); + } + + return 0; +} + + +/* skip regfile */ +int +tar_skip_regfile(TAR *t) +{ + int k; + size_t size, i; + char buf[T_BLOCKSIZE]; + + if (!TH_ISREG(t)) + { + errno = EINVAL; + return -1; + } + + size = th_get_size(t); + for (i = size; i > 0; i -= tar_min(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, char *realname, char *prefix) +{ + char *filename; + char *linktgt = NULL; + char *lnp; + libtar_hashptr_t hp; + + if (!TH_ISLNK(t)) + { + errno = EINVAL; + return -1; + } + + filename = (realname ? realname : th_get_pathname(t)); + if (mkdirhier(dirname(filename)) == -1) + 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); + char *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); + return 0; // Used to be -1 + } + + return 0; +} + + +/* symlink */ +int +tar_extract_symlink(TAR *t, char *realname) +{ + char *filename; + + 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"); + return -1; + } + + if (unlink(filename) == -1 && errno != ENOENT) { + printf("unlink\n"); + 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 + perror("symlink()"); +#endif + return -1; + } + + return 0; +} + + +/* character device */ +int +tar_extract_chardev(TAR *t, char *realname) +{ + mode_t mode; + unsigned long devmaj, devmin; + char *filename; + + if (!TH_ISCHR(t)) + { + errno = EINVAL; + return -1; + } + + filename = (realname ? realname : th_get_pathname(t)); + mode = th_get_mode(t); + devmaj = th_get_devmajor(t); + devmin = th_get_devminor(t); + + 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; + } + + return 0; +} + + +/* block device */ +int +tar_extract_blockdev(TAR *t, char *realname) +{ + mode_t mode; + unsigned long devmaj, devmin; + char *filename; + + if (!TH_ISBLK(t)) + { + errno = EINVAL; + return -1; + } + + filename = (realname ? realname : th_get_pathname(t)); + mode = th_get_mode(t); + devmaj = th_get_devmajor(t); + devmin = th_get_devminor(t); + + 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; + } + + return 0; +} + + +/* directory */ +int +tar_extract_dir(TAR *t, char *realname) +{ + mode_t mode; + char *filename; + if (!TH_ISDIR(t)) + { + errno = EINVAL; + return -1; + } + + filename = (realname ? realname : th_get_pathname(t)); + mode = th_get_mode(t); + + if (mkdirhier(dirname(filename)) == -1) { + printf("tar_extract_dir mkdirhier failed\n"); + return -1; + } + +#ifdef DEBUG + printf(" ==> extracting: %s (mode %04o, directory)\n", filename, + mode); +#endif + if (mkdir(filename, mode) == -1) + { + if (errno == EEXIST) + { +#ifdef DEBUG + printf(" *** using existing directory"); +#endif + } + else + { +#ifdef DEBUG + perror("mkdir()"); +#endif + return -1; + } + } + + return 0; +} + + +/* FIFO */ +int +tar_extract_fifo(TAR *t, char *realname) +{ + mode_t mode; + char *filename; + + if (!TH_ISFIFO(t)) + { + errno = EINVAL; + return -1; + } + + filename = (realname ? realname : th_get_pathname(t)); + 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 + perror("mkfifo()"); +#endif + return -1; + } + + return 0; +} + + |