busybox/archival/libunarchive/data_extract_all.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
   4 */
   5
   6#include "libbb.h"
   7#include "unarchive.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
  15        if (archive_handle->ah_flags & ARCHIVE_CREATE_LEADING_DIRS) {
  16                char *name = xstrdup(file_header->name);
  17                bb_make_directory(dirname(name), -1, FILEUTILS_RECUR);
  18                free(name);
  19        }
  20
  21        /* Check if the file already exists */
  22        if (archive_handle->ah_flags & ARCHIVE_EXTRACT_UNCONDITIONAL) {
  23                /* Remove the entry if it exists */
  24                if ((!S_ISDIR(file_header->mode))
  25                 && (unlink(file_header->name) == -1)
  26                 && (errno != ENOENT)
  27                ) {
  28                        bb_perror_msg_and_die("cannot remove old file %s",
  29                                        file_header->name);
  30                }
  31        }
  32        else if (archive_handle->ah_flags & ARCHIVE_EXTRACT_NEWER) {
  33                /* Remove the existing entry if its older than the extracted entry */
  34                struct stat statbuf;
  35                if (lstat(file_header->name, &statbuf) == -1) {
  36                        if (errno != ENOENT) {
  37                                bb_perror_msg_and_die("cannot stat old file");
  38                        }
  39                }
  40                else if (statbuf.st_mtime <= file_header->mtime) {
  41                        if (!(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)) {
  42                                bb_error_msg("%s not created: newer or "
  43                                        "same age file exists", file_header->name);
  44                        }
  45                        data_skip(archive_handle);
  46                        return;
  47                }
  48                else if ((unlink(file_header->name) == -1) && (errno != EISDIR)) {
  49                        bb_perror_msg_and_die("cannot remove old file %s",
  50                                        file_header->name);
  51                }
  52        }
  53
  54        /* Handle hard links separately
  55         * We identified hard links as regular files of size 0 with a symlink */
  56        if (S_ISREG(file_header->mode) && (file_header->link_target)
  57         && (file_header->size == 0)
  58        ) {
  59                /* hard link */
  60                res = link(file_header->link_target, file_header->name);
  61                if ((res == -1) && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)) {
  62                        bb_perror_msg("cannot create %slink "
  63                                        "from %s to %s", "hard",
  64                                        file_header->name,
  65                                        file_header->link_target);
  66                }
  67        } else {
  68                /* Create the filesystem entry */
  69                switch (file_header->mode & S_IFMT) {
  70                case S_IFREG: {
  71                        /* Regular file */
  72                        dst_fd = xopen3(file_header->name, O_WRONLY | O_CREAT | O_EXCL,
  73                                                        file_header->mode);
  74                        bb_copyfd_exact_size(archive_handle->src_fd, dst_fd, file_header->size);
  75                        close(dst_fd);
  76                        break;
  77                }
  78                case S_IFDIR:
  79                        res = mkdir(file_header->name, file_header->mode);
  80                        if ((res == -1)
  81                         && (errno != EISDIR) /* btw, Linux doesn't return this */
  82                         && (errno != EEXIST)
  83                         && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)
  84                        ) {
  85                                bb_perror_msg("cannot make dir %s", file_header->name);
  86                        }
  87                        break;
  88                case S_IFLNK:
  89                        /* Symlink */
  90                        res = symlink(file_header->link_target, file_header->name);
  91                        if ((res == -1)
  92                         && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)
  93                        ) {
  94                                bb_perror_msg("cannot create %slink "
  95                                        "from %s to %s", "sym",
  96                                        file_header->name,
  97                                        file_header->link_target);
  98                        }
  99                        break;
 100                case S_IFSOCK:
 101                case S_IFBLK:
 102                case S_IFCHR:
 103                case S_IFIFO:
 104                        res = mknod(file_header->name, file_header->mode, file_header->device);
 105                        if ((res == -1)
 106                         && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)
 107                        ) {
 108                                bb_perror_msg("cannot create node %s", file_header->name);
 109                        }
 110                        break;
 111                default:
 112                        bb_error_msg_and_die("unrecognized file type");
 113                }
 114        }
 115
 116        if (!(archive_handle->ah_flags & ARCHIVE_NOPRESERVE_OWN)) {
 117#if ENABLE_FEATURE_TAR_UNAME_GNAME
 118                if (!(archive_handle->ah_flags & ARCHIVE_NUMERIC_OWNER)) {
 119                        uid_t uid = file_header->uid;
 120                        gid_t gid = file_header->gid;
 121
 122                        if (file_header->uname) {
 123                                struct passwd *pwd = getpwnam(file_header->uname);
 124                                if (pwd) uid = pwd->pw_uid;
 125                        }
 126                        if (file_header->gname) {
 127                                struct group *grp = getgrnam(file_header->gname);
 128                                if (grp) gid = grp->gr_gid;
 129                        }
 130                        lchown(file_header->name, uid, gid);
 131                } else
 132#endif
 133                        lchown(file_header->name, file_header->uid, file_header->gid);
 134        }
 135        if (!S_ISLNK(file_header->mode)) {
 136                /* uclibc has no lchmod, glibc is even stranger -
 137                 * it has lchmod which seems to do nothing!
 138                 * so we use chmod... */
 139                if (!(archive_handle->ah_flags & ARCHIVE_NOPRESERVE_PERM)) {
 140                        chmod(file_header->name, file_header->mode);
 141                }
 142                /* same for utime */
 143                if (archive_handle->ah_flags & ARCHIVE_PRESERVE_DATE) {
 144                        struct utimbuf t;
 145                        t.actime = t.modtime = file_header->mtime;
 146                        utime(file_header->name, &t);
 147                }
 148        }
 149}
 150