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