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 ENABLE_FEATURE_TAR_SELINUX 16 char *sctx = archive_handle->tar__next_file_sctx; 17 if (!sctx) 18 sctx = archive_handle->tar__global_sctx; 19 if (sctx) { /* setfscreatecon is 4 syscalls, avoid if possible */ 20 setfscreatecon(sctx); 21 free(archive_handle->tar__next_file_sctx); 22 archive_handle->tar__next_file_sctx = NULL; 23 } 24#endif 25 26 if (archive_handle->ah_flags & ARCHIVE_CREATE_LEADING_DIRS) { 27 char *slash = strrchr(file_header->name, '/'); 28 if (slash) { 29 *slash = '\0'; 30 bb_make_directory(file_header->name, -1, FILEUTILS_RECUR); 31 *slash = '/'; 32 } 33 } 34 35 if (archive_handle->ah_flags & ARCHIVE_UNLINK_OLD) { 36 /* Remove the entry if it exists */ 37 if (!S_ISDIR(file_header->mode)) { 38 /* Is it hardlink? 39 * We encode hard links as regular files of size 0 with a symlink */ 40 if (S_ISREG(file_header->mode) 41 && file_header->link_target 42 && file_header->size == 0 43 ) { 44 /* Ugly special case: 45 * tar cf t.tar hardlink1 hardlink2 hardlink1 46 * results in this tarball structure: 47 * hardlink1 48 * hardlink2 -> hardlink1 49 * hardlink1 -> hardlink1 <== !!! 50 */ 51 if (strcmp(file_header->link_target, file_header->name) == 0) 52 goto ret; 53 } 54 /* Proceed with deleting */ 55 if (unlink(file_header->name) == -1 56 && errno != ENOENT 57 ) { 58 bb_perror_msg_and_die("can't remove old file %s", 59 file_header->name); 60 } 61 } 62 } 63 else if (archive_handle->ah_flags & ARCHIVE_EXTRACT_NEWER) { 64 /* Remove the existing entry if its older than the extracted entry */ 65 struct stat existing_sb; 66 if (lstat(file_header->name, &existing_sb) == -1) { 67 if (errno != ENOENT) { 68 bb_perror_msg_and_die("can't stat old file"); 69 } 70 } 71 else if (existing_sb.st_mtime >= file_header->mtime) { 72 if (!(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)) { 73 bb_error_msg("%s not created: newer or " 74 "same age file exists", file_header->name); 75 } 76 data_skip(archive_handle); 77 goto ret; 78 } 79 else if ((unlink(file_header->name) == -1) && (errno != EISDIR)) { 80 bb_perror_msg_and_die("can't remove old file %s", 81 file_header->name); 82 } 83 } 84 85 /* Handle hard links separately 86 * We encode hard links as regular files of size 0 with a symlink */ 87 if (S_ISREG(file_header->mode) 88 && file_header->link_target 89 && file_header->size == 0 90 ) { 91 /* hard link */ 92 res = link(file_header->link_target, file_header->name); 93 if ((res == -1) && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)) { 94 bb_perror_msg("can't create %slink " 95 "from %s to %s", "hard", 96 file_header->name, 97 file_header->link_target); 98 } 99 /* Hardlinks have no separate mode/ownership, skip chown/chmod */ 100 goto ret; 101 } 102 103 /* Create the filesystem entry */ 104 switch (file_header->mode & S_IFMT) { 105 case S_IFREG: { 106 /* Regular file */ 107 int flags = O_WRONLY | O_CREAT | O_EXCL; 108 if (archive_handle->ah_flags & ARCHIVE_O_TRUNC) 109 flags = O_WRONLY | O_CREAT | O_TRUNC; 110 dst_fd = xopen3(file_header->name, 111 flags, 112 file_header->mode 113 ); 114 bb_copyfd_exact_size(archive_handle->src_fd, dst_fd, file_header->size); 115 close(dst_fd); 116 break; 117 } 118 case S_IFDIR: 119 res = mkdir(file_header->name, file_header->mode); 120 if ((res == -1) 121 && (errno != EISDIR) /* btw, Linux doesn't return this */ 122 && (errno != EEXIST) 123 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) 124 ) { 125 bb_perror_msg("can't make dir %s", file_header->name); 126 } 127 break; 128 case S_IFLNK: 129 /* Symlink */ 130//TODO: what if file_header->link_target == NULL (say, corrupted tarball?) 131 res = symlink(file_header->link_target, file_header->name); 132 if ((res == -1) 133 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) 134 ) { 135 bb_perror_msg("can't create %slink " 136 "from %s to %s", "sym", 137 file_header->name, 138 file_header->link_target); 139 } 140 break; 141 case S_IFSOCK: 142 case S_IFBLK: 143 case S_IFCHR: 144 case S_IFIFO: 145 res = mknod(file_header->name, file_header->mode, file_header->device); 146 if ((res == -1) 147 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) 148 ) { 149 bb_perror_msg("can't create node %s", file_header->name); 150 } 151 break; 152 default: 153 bb_error_msg_and_die("unrecognized file type"); 154 } 155 156 if (!S_ISLNK(file_header->mode)) { 157 if (!(archive_handle->ah_flags & ARCHIVE_DONT_RESTORE_OWNER)) { 158 uid_t uid = file_header->uid; 159 gid_t gid = file_header->gid; 160#if ENABLE_FEATURE_TAR_UNAME_GNAME 161 if (!(archive_handle->ah_flags & ARCHIVE_NUMERIC_OWNER)) { 162 if (file_header->tar__uname) { 163//TODO: cache last name/id pair? 164 struct passwd *pwd = getpwnam(file_header->tar__uname); 165 if (pwd) uid = pwd->pw_uid; 166 } 167 if (file_header->tar__gname) { 168 struct group *grp = getgrnam(file_header->tar__gname); 169 if (grp) gid = grp->gr_gid; 170 } 171 } 172#endif 173 /* GNU tar 1.15.1 uses chown, not lchown */ 174 chown(file_header->name, uid, gid); 175 } 176 /* uclibc has no lchmod, glibc is even stranger - 177 * it has lchmod which seems to do nothing! 178 * so we use chmod... */ 179 if (!(archive_handle->ah_flags & ARCHIVE_DONT_RESTORE_PERM)) { 180 chmod(file_header->name, file_header->mode); 181 } 182 if (archive_handle->ah_flags & ARCHIVE_RESTORE_DATE) { 183 struct timeval t[2]; 184 185 t[1].tv_sec = t[0].tv_sec = file_header->mtime; 186 t[1].tv_usec = t[0].tv_usec = 0; 187 utimes(file_header->name, t); 188 } 189 } 190 191 ret: ; 192#if ENABLE_FEATURE_TAR_SELINUX 193 if (sctx) { 194 /* reset the context after creating an entry */ 195 setfscreatecon(NULL); 196 } 197#endif 198} 199