1/* vi: set sw=4 ts=4: */ 2 3#include "libbb.h" 4#include "unarchive.h" 5 6#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS 7static char *longname; 8static char *linkname; 9#else 10enum { 11 longname = 0, 12 linkname = 0, 13}; 14#endif 15 16/* NB: _DESTROYS_ str[len] character! */ 17static unsigned long long getOctal(char *str, int len) 18{ 19 unsigned long long v; 20 /* Actually, tar header allows leading spaces also. 21 * Oh well, we will be liberal and skip this... 22 * The only downside probably is that we allow "-123" too :) 23 if (*str < '0' || *str > '7') 24 bb_error_msg_and_die("corrupted octal value in tar header"); 25 */ 26 str[len] = '\0'; 27 v = strtoull(str, &str, 8); 28 if (*str && (!ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY || *str != ' ')) 29 bb_error_msg_and_die("corrupted octal value in tar header"); 30 return v; 31} 32#define GET_OCTAL(a) getOctal((a), sizeof(a)) 33 34void BUG_tar_header_size(void); 35char get_header_tar(archive_handle_t *archive_handle) 36{ 37 static smallint end; 38 39 file_header_t *file_header = archive_handle->file_header; 40 struct { 41 /* ustar header, Posix 1003.1 */ 42 char name[100]; /* 0-99 */ 43 char mode[8]; /* 100-107 */ 44 char uid[8]; /* 108-115 */ 45 char gid[8]; /* 116-123 */ 46 char size[12]; /* 124-135 */ 47 char mtime[12]; /* 136-147 */ 48 char chksum[8]; /* 148-155 */ 49 char typeflag; /* 156-156 */ 50 char linkname[100]; /* 157-256 */ 51 char magic[6]; /* 257-262 */ 52 char version[2]; /* 263-264 */ 53 char uname[32]; /* 265-296 */ 54 char gname[32]; /* 297-328 */ 55 char devmajor[8]; /* 329-336 */ 56 char devminor[8]; /* 337-344 */ 57 char prefix[155]; /* 345-499 */ 58 char padding[12]; /* 500-512 */ 59 } tar; 60 char *cp; 61 int i, sum_u, sum; 62#if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY 63 int sum_s; 64#endif 65 int parse_names; 66 67 if (sizeof(tar) != 512) 68 BUG_tar_header_size(); 69 70#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS 71 again: 72#endif 73 /* Align header */ 74 data_align(archive_handle, 512); 75 76 again_after_align: 77 78 xread(archive_handle->src_fd, &tar, 512); 79 archive_handle->offset += 512; 80 81 /* If there is no filename its an empty header */ 82 if (tar.name[0] == 0) { 83 if (end) { 84 /* This is the second consecutive empty header! End of archive! 85 * Read until the end to empty the pipe from gz or bz2 86 */ 87 while (full_read(archive_handle->src_fd, &tar, 512) == 512) 88 /* repeat */; 89 return EXIT_FAILURE; 90 } 91 end = 1; 92 return EXIT_SUCCESS; 93 } 94 end = 0; 95 96 /* Check header has valid magic, "ustar" is for the proper tar 97 * 0's are for the old tar format 98 */ 99 if (strncmp(tar.magic, "ustar", 5) != 0) { 100#if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY 101 if (memcmp(tar.magic, "\0\0\0\0", 5) != 0) 102#endif 103 bb_error_msg_and_die("invalid tar magic"); 104 } 105 106 /* Do checksum on headers. 107 * POSIX says that checksum is done on unsigned bytes, but 108 * Sun and HP-UX gets it wrong... more details in 109 * GNU tar source. */ 110#if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY 111 sum_s = ' ' * sizeof(tar.chksum); 112#endif 113 sum_u = ' ' * sizeof(tar.chksum); 114 for (i = 0; i < 148; i++) { 115 sum_u += ((unsigned char*)&tar)[i]; 116#if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY 117 sum_s += ((signed char*)&tar)[i]; 118#endif 119 } 120 for (i = 156; i < 512; i++) { 121 sum_u += ((unsigned char*)&tar)[i]; 122#if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY 123 sum_s += ((signed char*)&tar)[i]; 124#endif 125 } 126#if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY 127 sum = strtoul(tar.chksum, &cp, 8); 128 if ((*cp && *cp != ' ') 129 || (sum_u != sum USE_FEATURE_TAR_OLDSUN_COMPATIBILITY(&& sum_s != sum)) 130 ) { 131 bb_error_msg_and_die("invalid tar header checksum"); 132 } 133#else 134 /* This field does not need special treatment (getOctal) */ 135 sum = xstrtoul(tar.chksum, 8); 136 if (sum_u != sum USE_FEATURE_TAR_OLDSUN_COMPATIBILITY(&& sum_s != sum)) { 137 bb_error_msg_and_die("invalid tar header checksum"); 138 } 139#endif 140 141 /* 0 is reserved for high perf file, treat as normal file */ 142 if (!tar.typeflag) tar.typeflag = '0'; 143 parse_names = (tar.typeflag >= '0' && tar.typeflag <= '7'); 144 145 /* getOctal trashes subsequent field, therefore we call it 146 * on fields in reverse order */ 147 if (tar.devmajor[0]) { 148 unsigned minor = GET_OCTAL(tar.devminor); 149 unsigned major = GET_OCTAL(tar.devmajor); 150 file_header->device = makedev(major, minor); 151 } 152 file_header->link_target = NULL; 153 if (!linkname && parse_names && tar.linkname[0]) { 154 /* we trash magic[0] here, it's ok */ 155 tar.linkname[sizeof(tar.linkname)] = '\0'; 156 file_header->link_target = xstrdup(tar.linkname); 157 /* Will link_target be free()ed? */ 158 } 159 file_header->mtime = GET_OCTAL(tar.mtime); 160 file_header->size = GET_OCTAL(tar.size); 161 file_header->gid = GET_OCTAL(tar.gid); 162 file_header->uid = GET_OCTAL(tar.uid); 163 /* Set bits 0-11 of the files mode */ 164 file_header->mode = 07777 & GET_OCTAL(tar.mode); 165 166 file_header->name = NULL; 167 if (!longname && parse_names) { 168 /* we trash mode[0] here, it's ok */ 169 tar.name[sizeof(tar.name)] = '\0'; 170 if (tar.prefix[0]) { 171 /* and padding[0] */ 172 tar.prefix[sizeof(tar.prefix)] = '\0'; 173 file_header->name = concat_path_file(tar.prefix, tar.name); 174 } else 175 file_header->name = xstrdup(tar.name); 176 } 177 178 /* Set bits 12-15 of the files mode */ 179 /* (typeflag was not trashed because chksum does not use getOctal) */ 180 switch (tar.typeflag) { 181 /* busybox identifies hard links as being regular files with 0 size and a link name */ 182 case '1': 183 file_header->mode |= S_IFREG; 184 break; 185 case '7': 186 /* case 0: */ 187 case '0': 188#if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY 189 if (last_char_is(file_header->name, '/')) { 190 file_header->mode |= S_IFDIR; 191 } else 192#endif 193 file_header->mode |= S_IFREG; 194 break; 195 case '2': 196 file_header->mode |= S_IFLNK; 197 break; 198 case '3': 199 file_header->mode |= S_IFCHR; 200 break; 201 case '4': 202 file_header->mode |= S_IFBLK; 203 break; 204 case '5': 205 file_header->mode |= S_IFDIR; 206 break; 207 case '6': 208 file_header->mode |= S_IFIFO; 209 break; 210#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS 211 case 'L': 212 /* free: paranoia: tar with several consecutive longnames */ 213 free(longname); 214 /* For paranoia reasons we allocate extra NUL char */ 215 longname = xzalloc(file_header->size + 1); 216 /* We read ASCIZ string, including NUL */ 217 xread(archive_handle->src_fd, longname, file_header->size); 218 archive_handle->offset += file_header->size; 219 /* return get_header_tar(archive_handle); */ 220 /* gcc 4.1.1 didn't optimize it into jump */ 221 /* so we will do it ourself, this also saves stack */ 222 goto again; 223 case 'K': 224 free(linkname); 225 linkname = xzalloc(file_header->size + 1); 226 xread(archive_handle->src_fd, linkname, file_header->size); 227 archive_handle->offset += file_header->size; 228 /* return get_header_tar(archive_handle); */ 229 goto again; 230 case 'D': /* GNU dump dir */ 231 case 'M': /* Continuation of multi volume archive */ 232 case 'N': /* Old GNU for names > 100 characters */ 233 case 'S': /* Sparse file */ 234 case 'V': /* Volume header */ 235#endif 236 case 'g': /* pax global header */ 237 case 'x': { /* pax extended header */ 238 off_t sz; 239 bb_error_msg("warning: skipping header '%c'", tar.typeflag); 240 sz = (file_header->size + 511) & ~(off_t)511; 241 archive_handle->offset += sz; 242 sz >>= 9; /* sz /= 512 but w/o contortions for signed div */ 243 while (sz--) 244 xread(archive_handle->src_fd, &tar, 512); 245 /* return get_header_tar(archive_handle); */ 246 goto again_after_align; 247 } 248 default: 249 bb_error_msg_and_die("unknown typeflag: 0x%x", tar.typeflag); 250 } 251 252#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS 253 if (longname) { 254 file_header->name = longname; 255 longname = NULL; 256 } 257 if (linkname) { 258 file_header->link_target = linkname; 259 linkname = NULL; 260 } 261#endif 262 if (!strncmp(file_header->name, "/../"+1, 3) 263 || strstr(file_header->name, "/../") 264 ) { 265 bb_error_msg_and_die("name with '..' encountered: '%s'", 266 file_header->name); 267 } 268 269 /* Strip trailing '/' in directories */ 270 /* Must be done after mode is set as '/' is used to check if it's a directory */ 271 cp = last_char_is(file_header->name, '/'); 272 273 if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) { 274 archive_handle->action_header(archive_handle->file_header); 275 /* Note that we kill the '/' only after action_header() */ 276 /* (like GNU tar 1.15.1: verbose mode outputs "dir/dir/") */ 277 if (cp) *cp = '\0'; 278 archive_handle->flags |= ARCHIVE_EXTRACT_QUIET; 279 archive_handle->action_data(archive_handle); 280 llist_add_to(&(archive_handle->passed), file_header->name); 281 } else { 282 data_skip(archive_handle); 283 free(file_header->name); 284 } 285 archive_handle->offset += file_header->size; 286 287 free(file_header->link_target); 288 /* Do not free(file_header->name)! */ 289 290 return EXIT_SUCCESS; 291} 292