busybox/archival/libarchive/data_extract_all.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
   4 */
   5#include "libbb.h"
   6#include "bb_archive.h"
   7
   8void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
   9{
  10        file_header_t *file_header = archive_handle->file_header;
  11        int dst_fd;
  12        int res;
  13        char *hard_link;
  14#if ENABLE_FEATURE_TAR_LONG_OPTIONS
  15        char *dst_name;
  16#else
  17# define dst_name (file_header->name)
  18#endif
  19
  20#if ENABLE_FEATURE_TAR_SELINUX
  21        char *sctx = archive_handle->tar__sctx[PAX_NEXT_FILE];
  22        if (!sctx)
  23                sctx = archive_handle->tar__sctx[PAX_GLOBAL];
  24        if (sctx) { /* setfscreatecon is 4 syscalls, avoid if possible */
  25                setfscreatecon(sctx);
  26                free(archive_handle->tar__sctx[PAX_NEXT_FILE]);
  27                archive_handle->tar__sctx[PAX_NEXT_FILE] = NULL;
  28        }
  29#endif
  30
  31        /* Hard links are encoded as regular files of size 0
  32         * with a nonempty link field */
  33        hard_link = NULL;
  34        if (S_ISREG(file_header->mode) && file_header->size == 0)
  35                hard_link = file_header->link_target;
  36
  37#if ENABLE_FEATURE_TAR_LONG_OPTIONS
  38        dst_name = file_header->name;
  39        if (archive_handle->tar__strip_components) {
  40                unsigned n = archive_handle->tar__strip_components;
  41                do {
  42                        dst_name = strchr(dst_name, '/');
  43                        if (!dst_name || dst_name[1] == '\0') {
  44                                data_skip(archive_handle);
  45                                goto ret;
  46                        }
  47                        dst_name++;
  48                        /*
  49                         * Link target is shortened only for hardlinks:
  50                         * softlinks restored unchanged.
  51                         */
  52                        if (hard_link) {
  53// GNU tar 1.26 does not check that we reached end of link name:
  54// if "dir/hardlink" is hardlinked to "file",
  55// tar xvf a.tar --strip-components=1 says:
  56//  tar: hardlink: Cannot hard link to '': No such file or directory
  57// and continues processing. We silently skip such entries.
  58                                hard_link = strchr(hard_link, '/');
  59                                if (!hard_link || hard_link[1] == '\0') {
  60                                        data_skip(archive_handle);
  61                                        goto ret;
  62                                }
  63                                hard_link++;
  64                        }
  65                } while (--n != 0);
  66        }
  67#endif
  68
  69        if (archive_handle->ah_flags & ARCHIVE_CREATE_LEADING_DIRS) {
  70                char *slash = strrchr(dst_name, '/');
  71                if (slash) {
  72                        *slash = '\0';
  73                        bb_make_directory(dst_name, -1, FILEUTILS_RECUR);
  74                        *slash = '/';
  75                }
  76        }
  77
  78        if (archive_handle->ah_flags & ARCHIVE_UNLINK_OLD) {
  79                /* Remove the entry if it exists */
  80                if (!S_ISDIR(file_header->mode)) {
  81                        if (hard_link) {
  82                                /* Ugly special case:
  83                                 * tar cf t.tar hardlink1 hardlink2 hardlink1
  84                                 * results in this tarball structure:
  85                                 * hardlink1
  86                                 * hardlink2 -> hardlink1
  87                                 * hardlink1 -> hardlink1 <== !!!
  88                                 */
  89                                if (strcmp(hard_link, dst_name) == 0)
  90                                        goto ret;
  91                        }
  92                        /* Proceed with deleting */
  93                        if (unlink(dst_name) == -1
  94                         && errno != ENOENT
  95                        ) {
  96                                bb_perror_msg_and_die("can't remove old file %s",
  97                                                dst_name);
  98                        }
  99                }
 100        }
 101        else if (archive_handle->ah_flags & ARCHIVE_EXTRACT_NEWER) {
 102                /* Remove the existing entry if its older than the extracted entry */
 103                struct stat existing_sb;
 104                if (lstat(dst_name, &existing_sb) == -1) {
 105                        if (errno != ENOENT) {
 106                                bb_perror_msg_and_die("can't stat old file");
 107                        }
 108                }
 109                else if (existing_sb.st_mtime >= file_header->mtime) {
 110                        if (!S_ISDIR(file_header->mode)) {
 111                                bb_error_msg("%s not created: newer or "
 112                                        "same age file exists", dst_name);
 113                        }
 114                        data_skip(archive_handle);
 115                        goto ret;
 116                }
 117                else if ((unlink(dst_name) == -1) && (errno != EISDIR)) {
 118                        bb_perror_msg_and_die("can't remove old file %s",
 119                                        dst_name);
 120                }
 121        }
 122
 123        /* Handle hard links separately */
 124        if (hard_link) {
 125                create_or_remember_link(&archive_handle->link_placeholders,
 126                                hard_link,
 127                                dst_name,
 128                                1);
 129                /* Hardlinks have no separate mode/ownership, skip chown/chmod */
 130                goto ret;
 131        }
 132
 133        /* Create the filesystem entry */
 134        switch (file_header->mode & S_IFMT) {
 135        case S_IFREG: {
 136                /* Regular file */
 137                char *dst_nameN;
 138                int flags = O_WRONLY | O_CREAT | O_EXCL;
 139                if (archive_handle->ah_flags & ARCHIVE_O_TRUNC)
 140                        flags = O_WRONLY | O_CREAT | O_TRUNC;
 141                dst_nameN = dst_name;
 142#ifdef ARCHIVE_REPLACE_VIA_RENAME
 143                if (archive_handle->ah_flags & ARCHIVE_REPLACE_VIA_RENAME)
 144                        /* rpm-style temp file name */
 145                        dst_nameN = xasprintf("%s;%x", dst_name, (int)getpid());
 146#endif
 147                dst_fd = xopen3(dst_nameN,
 148                        flags,
 149                        file_header->mode
 150                        );
 151                bb_copyfd_exact_size(archive_handle->src_fd, dst_fd, file_header->size);
 152                close(dst_fd);
 153#ifdef ARCHIVE_REPLACE_VIA_RENAME
 154                if (archive_handle->ah_flags & ARCHIVE_REPLACE_VIA_RENAME) {
 155                        xrename(dst_nameN, dst_name);
 156                        free(dst_nameN);
 157                }
 158#endif
 159                break;
 160        }
 161        case S_IFDIR:
 162                res = mkdir(dst_name, file_header->mode);
 163                if ((res != 0)
 164                 && (errno != EISDIR) /* btw, Linux doesn't return this */
 165                 && (errno != EEXIST)
 166                ) {
 167                        bb_perror_msg("can't make dir %s", dst_name);
 168                }
 169                break;
 170        case S_IFLNK:
 171                /* Symlink */
 172//TODO: what if file_header->link_target == NULL (say, corrupted tarball?)
 173
 174                /* To avoid a directory traversal attack via symlinks,
 175                 * do not restore symlinks with ".." components
 176                 * or symlinks starting with "/", unless a magic
 177                 * envvar is set.
 178                 *
 179                 * For example, consider a .tar created via:
 180                 *  $ tar cvf bug.tar anything.txt
 181                 *  $ ln -s /tmp symlink
 182                 *  $ tar --append -f bug.tar symlink
 183                 *  $ rm symlink
 184                 *  $ mkdir symlink
 185                 *  $ tar --append -f bug.tar symlink/evil.py
 186                 *
 187                 * This will result in an archive that contains:
 188                 *  $ tar --list -f bug.tar
 189                 *  anything.txt
 190                 *  symlink [-> /tmp]
 191                 *  symlink/evil.py
 192                 *
 193                 * Untarring bug.tar would otherwise place evil.py in '/tmp'.
 194                 */
 195                create_or_remember_link(&archive_handle->link_placeholders,
 196                                file_header->link_target,
 197                                dst_name,
 198                                0);
 199                break;
 200        case S_IFSOCK:
 201        case S_IFBLK:
 202        case S_IFCHR:
 203        case S_IFIFO:
 204                res = mknod(dst_name, file_header->mode, file_header->device);
 205                if (res != 0) {
 206                        bb_perror_msg("can't create node %s", dst_name);
 207                }
 208                break;
 209        default:
 210                bb_error_msg_and_die("unrecognized file type");
 211        }
 212
 213        if (!S_ISLNK(file_header->mode)) {
 214                if (!(archive_handle->ah_flags & ARCHIVE_DONT_RESTORE_OWNER)) {
 215                        uid_t uid = file_header->uid;
 216                        gid_t gid = file_header->gid;
 217#if ENABLE_FEATURE_TAR_UNAME_GNAME
 218                        if (!(archive_handle->ah_flags & ARCHIVE_NUMERIC_OWNER)) {
 219                                if (file_header->tar__uname) {
 220//TODO: cache last name/id pair?
 221                                        struct passwd *pwd = getpwnam(file_header->tar__uname);
 222                                        if (pwd) uid = pwd->pw_uid;
 223                                }
 224                                if (file_header->tar__gname) {
 225                                        struct group *grp = getgrnam(file_header->tar__gname);
 226                                        if (grp) gid = grp->gr_gid;
 227                                }
 228                        }
 229#endif
 230                        /* GNU tar 1.15.1 uses chown, not lchown */
 231                        chown(dst_name, uid, gid);
 232                }
 233                /* uclibc has no lchmod, glibc is even stranger -
 234                 * it has lchmod which seems to do nothing!
 235                 * so we use chmod... */
 236                if (!(archive_handle->ah_flags & ARCHIVE_DONT_RESTORE_PERM)) {
 237                        chmod(dst_name, file_header->mode);
 238                }
 239                if (archive_handle->ah_flags & ARCHIVE_RESTORE_DATE) {
 240                        struct timeval t[2];
 241
 242                        t[1].tv_sec = t[0].tv_sec = file_header->mtime;
 243                        t[1].tv_usec = t[0].tv_usec = 0;
 244                        utimes(dst_name, t);
 245                }
 246        }
 247
 248 ret: ;
 249#if ENABLE_FEATURE_TAR_SELINUX
 250        if (sctx) {
 251                /* reset the context after creating an entry */
 252                setfscreatecon(NULL);
 253        }
 254#endif
 255}
 256