1 /* 2 * Copyright (C) 2000 by Glenn McGrath 3 * Copyright (C) 2001 by Laurence Anderson 4 * 5 * Based on previous work by busybox developers and others. 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU Library General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 20 */ 21 22 #include <stdio.h> 23 #include <errno.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <unistd.h> 27 #include <utime.h> 28 #include <libgen.h> 29 30 #include "libbb.h" 31 #include "gzip.h" 32 33 #define CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY 1 34 #define CONFIG_FEATURE_TAR_GNU_EXTENSIONS 35 36 #ifdef CONFIG_FEATURE_TAR_GNU_EXTENSIONS 37 static char *longname = NULL; 38 static char *linkname = NULL; 39 #endif 40 41 off_t archive_offset; 42 43 static ssize_t seek_forward(struct gzip_handle *zh, ssize_t len) 44 { 45 ssize_t slen = gzip_seek(zh, len); 46 47 if (slen == len) 48 archive_offset += len; 49 50 return slen; 51 } 52 53 /* Extract the data postioned at src_stream to either filesystem, stdout or 54 * buffer depending on the value of 'function' which is defined in libbb.h 55 * 56 * prefix doesnt have to be just a directory, it may prefix the filename as well. 57 * 58 * e.g. '/var/lib/dpkg/info/dpkg.' will extract all files to the base bath 59 * '/var/lib/dpkg/info/' and all files/dirs created in that dir will have 60 * 'dpkg.' as their prefix 61 * 62 * For this reason if prefix does point to a dir then it must end with a 63 * trailing '/' or else the last dir will be assumed to be the file prefix 64 */ 65 static char *extract_archive(struct gzip_handle *src_stream, FILE * out_stream, 66 const file_header_t * file_entry, 67 const int function, const char *prefix, int *err) 68 { 69 FILE *dst_stream = NULL; 70 char *full_name = NULL; 71 char *full_link_name = NULL; 72 char *buffer = NULL; 73 struct utimbuf t; 74 75 *err = 0; 76 77 /* prefix doesnt have to be a proper path it may prepend 78 * the filename as well */ 79 if (prefix != NULL) { 80 /* strip leading '/' in filename to extract as prefix may not be dir */ 81 /* Cant use concat_path_file here as prefix might not be a directory */ 82 char *path = file_entry->name; 83 if (strncmp("./", path, 2) == 0) { 84 path += 2; 85 if (strlen(path) == 0) 86 /* Do nothing, current dir already exists. */ 87 return NULL; 88 } 89 full_name = xmalloc(strlen(prefix) + strlen(path) + 1); 90 strcpy(full_name, prefix); 91 strcat(full_name, path); 92 if (file_entry->link_name) { 93 full_link_name = 94 xmalloc(strlen(prefix) + 95 strlen(file_entry->link_name) + 1); 96 strcpy(full_link_name, prefix); 97 strcat(full_link_name, file_entry->link_name); 98 } 99 } else { 100 full_name = xstrdup(file_entry->name); 101 if (file_entry->link_name) 102 full_link_name = xstrdup(file_entry->link_name); 103 } 104 105 if (function & extract_to_stream) { 106 if (S_ISREG(file_entry->mode)) { 107 *err = 108 gzip_copy(src_stream, out_stream, file_entry->size); 109 archive_offset += file_entry->size; 110 } 111 } else if (function & extract_one_to_buffer) { 112 if (S_ISREG(file_entry->mode)) { 113 buffer = (char *)xmalloc(file_entry->size + 1); 114 gzip_read(src_stream, buffer, file_entry->size); 115 buffer[file_entry->size] = '\0'; 116 archive_offset += file_entry->size; 117 goto cleanup; 118 } 119 } else if (function & extract_all_to_fs) { 120 struct stat oldfile; 121 int stat_res; 122 stat_res = lstat(full_name, &oldfile); 123 if (stat_res == 0) { /* The file already exists */ 124 if ((function & extract_unconditional) 125 || (oldfile.st_mtime < file_entry->mtime)) { 126 if (!S_ISDIR(oldfile.st_mode)) { 127 unlink(full_name); /* Directories might not be empty etc */ 128 } 129 } else { 130 if ((function & extract_quiet) != extract_quiet) { 131 *err = -1; 132 error_msg 133 ("%s not created: newer or same age file exists", 134 file_entry->name); 135 } 136 seek_forward(src_stream, file_entry->size); 137 goto cleanup; 138 } 139 } 140 if (function & extract_create_leading_dirs) { /* Create leading directories with default umask */ 141 char *buf, *parent; 142 buf = xstrdup(full_name); 143 parent = dirname(buf); 144 if (make_directory(parent, -1, FILEUTILS_RECUR) != 0) { 145 if ((function & extract_quiet) != extract_quiet) { 146 *err = -1; 147 error_msg 148 ("couldn't create leading directories"); 149 } 150 } 151 free(buf); 152 } 153 switch (file_entry->mode & S_IFMT) { 154 case S_IFREG: 155 if (file_entry->link_name) { /* Found a cpio hard link */ 156 if (link(full_link_name, full_name) != 0) { 157 if ((function & extract_quiet) != 158 extract_quiet) { 159 *err = -1; 160 perror_msg 161 ("Cannot link from %s to '%s'", 162 file_entry->name, 163 file_entry->link_name); 164 } 165 } 166 } else { 167 if ((dst_stream = 168 wfopen(full_name, "w")) == NULL) { 169 *err = -1; 170 seek_forward(src_stream, 171 file_entry->size); 172 goto cleanup; 173 } 174 archive_offset += file_entry->size; 175 *err = 176 gzip_copy(src_stream, dst_stream, 177 file_entry->size); 178 fclose(dst_stream); 179 } 180 break; 181 case S_IFDIR: 182 if (stat_res != 0) { 183 if (mkdir(full_name, file_entry->mode) < 0) { 184 if ((function & extract_quiet) != 185 extract_quiet) { 186 *err = -1; 187 perror_msg("Cannot make dir %s", 188 full_name); 189 } 190 } 191 } 192 break; 193 case S_IFLNK: 194 if (symlink(file_entry->link_name, full_name) < 0) { 195 if ((function & extract_quiet) != extract_quiet) { 196 *err = -1; 197 perror_msg 198 ("Cannot create symlink from %s to '%s'", 199 file_entry->name, 200 file_entry->link_name); 201 } 202 goto cleanup; 203 } 204 break; 205 case S_IFSOCK: 206 case S_IFBLK: 207 case S_IFCHR: 208 case S_IFIFO: 209 if (mknod 210 (full_name, file_entry->mode, 211 file_entry->device) == -1) { 212 if ((function & extract_quiet) != extract_quiet) { 213 *err = -1; 214 perror_msg("Cannot create node %s", 215 file_entry->name); 216 } 217 goto cleanup; 218 } 219 break; 220 default: 221 *err = -1; 222 perror_msg("Don't know how to handle %s", full_name); 223 224 } 225 226 /* Changing a symlink's properties normally changes the properties of the 227 * file pointed to, so dont try and change the date or mode, lchown does 228 * does the right thing, but isnt available in older versions of libc */ 229 if (S_ISLNK(file_entry->mode)) { 230 #if (__GLIBC__ > 2) && (__GLIBC_MINOR__ > 1) 231 lchown(full_name, file_entry->uid, file_entry->gid); 232 #endif 233 } else { 234 if (function & extract_preserve_date) { 235 t.actime = file_entry->mtime; 236 t.modtime = file_entry->mtime; 237 utime(full_name, &t); 238 } 239 chown(full_name, file_entry->uid, file_entry->gid); 240 chmod(full_name, file_entry->mode); 241 } 242 } else { 243 /* If we arent extracting data we have to skip it, 244 * if data size is 0 then then just do it anyway 245 * (saves testing for it) */ 246 seek_forward(src_stream, file_entry->size); 247 } 248 249 /* extract_list and extract_verbose_list can be used in conjunction 250 * with one of the above four extraction functions, so do this seperately */ 251 if (function & extract_verbose_list) { 252 fprintf(out_stream, "%s %d/%d %8d %s ", 253 mode_string(file_entry->mode), file_entry->uid, 254 file_entry->gid, (int)file_entry->size, 255 time_string(file_entry->mtime)); 256 } 257 if ((function & extract_list) || (function & extract_verbose_list)) { 258 /* fputs doesnt add a trailing \n, so use fprintf */ 259 fprintf(out_stream, "%s\n", file_entry->name); 260 } 261 262 cleanup: 263 free(full_name); 264 if (full_link_name) 265 free(full_link_name); 266 267 return buffer; 268 } 269 270 static char *unarchive(struct gzip_handle *src_stream, FILE * out_stream, 271 file_header_t * (*get_headers) (struct gzip_handle *), 272 void (*free_headers) (file_header_t *), 273 const int extract_function, 274 const char *prefix, const char **extract_names, int *err) 275 { 276 file_header_t *file_entry; 277 int extract_flag; 278 int i; 279 char *buffer = NULL; 280 281 *err = 0; 282 283 archive_offset = 0; 284 while ((file_entry = get_headers(src_stream)) != NULL) { 285 extract_flag = TRUE; 286 287 if (extract_names != NULL) { 288 int found_flag = FALSE; 289 char *p = file_entry->name; 290 291 if (p[0] == '.' && p[1] == '/') 292 p += 2; 293 294 for (i = 0; extract_names[i] != 0; i++) { 295 if (strcmp(extract_names[i], p) == 0) { 296 found_flag = TRUE; 297 break; 298 } 299 } 300 if (extract_function & extract_exclude_list) { 301 if (found_flag == TRUE) { 302 extract_flag = FALSE; 303 } 304 } else { 305 /* If its not found in the include list dont extract it */ 306 if (found_flag == FALSE) { 307 extract_flag = FALSE; 308 } 309 } 310 } 311 312 if (extract_flag == TRUE) { 313 buffer = extract_archive(src_stream, out_stream, 314 file_entry, extract_function, 315 prefix, err); 316 *err = 0; /* XXX: ignore extraction errors */ 317 if (*err) { 318 free_headers(file_entry); 319 break; 320 } 321 } else { 322 /* seek past the data entry */ 323 seek_forward(src_stream, file_entry->size); 324 } 325 free_headers(file_entry); 326 } 327 328 return buffer; 329 } 330 331 static file_header_t *get_header_tar(struct gzip_handle *tar_stream) 332 { 333 union { 334 unsigned char raw[512]; 335 struct { 336 char name[100]; /* 0-99 */ 337 char mode[8]; /* 100-107 */ 338 char uid[8]; /* 108-115 */ 339 char gid[8]; /* 116-123 */ 340 char size[12]; /* 124-135 */ 341 char mtime[12]; /* 136-147 */ 342 char chksum[8]; /* 148-155 */ 343 char typeflag; /* 156-156 */ 344 char linkname[100]; /* 157-256 */ 345 char magic[6]; /* 257-262 */ 346 char version[2]; /* 263-264 */ 347 char uname[32]; /* 265-296 */ 348 char gname[32]; /* 297-328 */ 349 char devmajor[8]; /* 329-336 */ 350 char devminor[8]; /* 337-344 */ 351 char prefix[155]; /* 345-499 */ 352 char padding[12]; /* 500-512 */ 353 } formated; 354 } tar; 355 file_header_t *tar_entry = NULL; 356 long i; 357 long sum = 0; 358 359 if (archive_offset % 512 != 0) { 360 seek_forward(tar_stream, 512 - (archive_offset % 512)); 361 } 362 363 if (gzip_read(tar_stream, tar.raw, 512) != 512) { 364 /* Unfortunately its common for tar files to have all sorts of 365 * trailing garbage, fail silently */ 366 // error_msg("Couldnt read header"); 367 return (NULL); 368 } 369 archive_offset += 512; 370 371 /* Check header has valid magic, unfortunately some tar files 372 * have empty (0'ed) tar entries at the end, which will 373 * cause this to fail, so fail silently for now 374 */ 375 if (strncmp(tar.formated.magic, "ustar", 5) != 0) { 376 #ifdef CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY 377 if (strncmp(tar.formated.magic, "\0\0\0\0\0", 5) != 0) 378 #endif 379 return (NULL); 380 } 381 382 /* Do checksum on headers */ 383 for (i = 0; i < 148; i++) { 384 sum += tar.raw[i]; 385 } 386 sum += ' ' * 8; 387 for (i = 156; i < 512; i++) { 388 sum += tar.raw[i]; 389 } 390 if (sum != strtol(tar.formated.chksum, NULL, 8)) { 391 if (strtol(tar.formated.chksum, NULL, 8) != 0) 392 error_msg("Invalid tar header checksum"); 393 return (NULL); 394 } 395 396 /* convert to type'ed variables */ 397 tar_entry = xcalloc(1, sizeof(file_header_t)); 398 399 // tar_entry->name = xstrdup(tar.formated.name); 400 401 /* 402 parse_mode(tar.formated.mode, &tar_entry->mode); 403 */ 404 tar_entry->mode = 07777 & strtol(tar.formated.mode, NULL, 8); 405 406 tar_entry->uid = strtol(tar.formated.uid, NULL, 8); 407 tar_entry->gid = strtol(tar.formated.gid, NULL, 8); 408 tar_entry->size = strtol(tar.formated.size, NULL, 8); 409 tar_entry->mtime = strtol(tar.formated.mtime, NULL, 8); 410 411 tar_entry->device = (strtol(tar.formated.devmajor, NULL, 8) << 8) + 412 strtol(tar.formated.devminor, NULL, 8); 413 414 /* Fix mode, used by the old format */ 415 switch (tar.formated.typeflag) { 416 /* hard links are detected as regular files with 0 size and a link name */ 417 case '1': 418 tar_entry->mode |= S_IFREG; 419 break; 420 case 0: 421 case '': 422 423 #ifdef CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY 424 if (last_char_is(tar_entry->name, '/')) { 425 tar_entry->mode |= S_IFDIR; 426 } else 427 #endif 428 tar_entry->mode |= S_IFREG; 429 break; 430 case '2': 431 tar_entry->mode |= S_IFLNK; 432 break; 433 case '3': 434 tar_entry->mode |= S_IFCHR; 435 break; 436 case '4': 437 tar_entry->mode |= S_IFBLK; 438 break; 439 case '5': 440 tar_entry->mode |= S_IFDIR; 441 break; 442 case '6': 443 tar_entry->mode |= S_IFIFO; 444 break; 445 #ifdef CONFIG_FEATURE_TAR_GNU_EXTENSIONS 446 case 'L':{ 447 longname = xmalloc(tar_entry->size + 1); 448 if (gzip_read(tar_stream, longname, tar_entry->size) != 449 tar_entry->size) 450 return NULL; 451 longname[tar_entry->size] = '\0'; 452 archive_offset += tar_entry->size; 453 454 return (get_header_tar(tar_stream)); 455 } 456 case 'K':{ 457 linkname = xmalloc(tar_entry->size + 1); 458 if (gzip_read(tar_stream, linkname, tar_entry->size) != 459 tar_entry->size) 460 return NULL; 461 linkname[tar_entry->size] = '\0'; 462 archive_offset += tar_entry->size; 463 464 return (get_header_tar(tar_stream)); 465 } 466 case 'D': 467 case 'M': 468 case 'N': 469 case 'S': 470 case 'V': 471 perror_msg("Ignoring GNU extension type %c", 472 tar.formated.typeflag); 473 #endif 474 default: 475 perror_msg("Unknown typeflag: 0x%x", tar.formated.typeflag); 476 break; 477 478 } 479 480 #ifdef CONFIG_FEATURE_TAR_GNU_EXTENSIONS 481 if (longname) { 482 tar_entry->name = longname; 483 longname = NULL; 484 } else 485 #endif 486 { 487 tar_entry->name = xstrndup(tar.formated.name, 100); 488 489 if (tar.formated.prefix[0]) { 490 char *temp = tar_entry->name; 491 char *prefixTemp = xstrndup(tar.formated.prefix, 155); 492 tar_entry->name = concat_path_file(prefixTemp, temp); 493 free(temp); 494 free(prefixTemp); 495 } 496 } 497 498 if (linkname) { 499 tar_entry->link_name = linkname; 500 linkname = NULL; 501 } else { 502 tar_entry->link_name = *tar.formated.linkname != '\0' ? 503 xstrndup(tar.formated.linkname, 100) : NULL; 504 } 505 506 return (tar_entry); 507 } 508 509 static void free_header_tar(file_header_t * tar_entry) 510 { 511 if (tar_entry == NULL) 512 return; 513 514 free(tar_entry->name); 515 if (tar_entry->link_name) 516 free(tar_entry->link_name); 517 518 free(tar_entry); 519 } 520 521 char *deb_extract(const char *package_filename, FILE * out_stream, 522 const int extract_function, const char *prefix, 523 const char *filename, int *err) 524 { 525 FILE *deb_stream = NULL; 526 const char **file_list = NULL; 527 char *output_buffer = NULL; 528 char *ared_file = NULL; 529 struct gzip_handle tar_outer = { }, tar_inner = { }; 530 file_header_t *tar_header; 531 532 *err = 0; 533 534 if (filename != NULL) { 535 file_list = xmalloc(sizeof(char *) * 2); 536 file_list[0] = filename; 537 file_list[1] = NULL; 538 } 539 540 if (extract_function & extract_control_tar_gz) { 541 ared_file = "control.tar.gz"; 542 } else if (extract_function & extract_data_tar_gz) { 543 ared_file = "data.tar.gz"; 544 } else { 545 opkg_msg(ERROR, "Internal error: extract_function=%x\n", 546 extract_function); 547 *err = -1; 548 goto cleanup; 549 } 550 551 /* open the debian package to be worked on */ 552 deb_stream = wfopen(package_filename, "r"); 553 if (deb_stream == NULL) { 554 *err = -1; 555 goto cleanup; 556 } 557 /* set the buffer size */ 558 setvbuf(deb_stream, NULL, _IOFBF, 0x8000); 559 560 tar_outer.file = deb_stream; 561 gzip_exec(&tar_outer, NULL); 562 563 /* walk through outer tar file to find ared_file */ 564 while ((tar_header = get_header_tar(&tar_outer)) != NULL) { 565 int name_offset = 0; 566 if (strncmp(tar_header->name, "./", 2) == 0) 567 name_offset = 2; 568 569 if (strcmp(ared_file, tar_header->name + name_offset) == 0) { 570 tar_inner.gzip = &tar_outer; 571 gzip_exec(&tar_inner, NULL); 572 573 archive_offset = 0; 574 575 output_buffer = unarchive(&tar_inner, 576 out_stream, 577 get_header_tar, 578 free_header_tar, 579 extract_function, 580 prefix, file_list, err); 581 582 free_header_tar(tar_header); 583 gzip_close(&tar_inner); 584 break; 585 } 586 587 seek_forward(&tar_outer, tar_header->size); 588 free_header_tar(tar_header); 589 } 590 591 cleanup: 592 gzip_close(&tar_outer); 593 594 if (file_list) 595 free(file_list); 596 597 return output_buffer; 598 } 599
This page was automatically generated by LXR 0.3.1. • OpenWrt