int ar_main(int argc, char **argv) { static const char msg_unsupported_err[] ALIGN1 = "archive %s is not supported"; archive_handle_t *archive_handle; unsigned opt; char magic[8]; archive_handle = init_handle(); /* Prepend '-' to the first argument if required */ opt_complementary = "--:p:t:x:-1:p--tx:t--px:x--pt"; opt = getopt32(argv, "ptxovcr"); if (opt & AR_CTX_PRINT) { archive_handle->action_data = data_extract_to_stdout; } if (opt & AR_CTX_LIST) { archive_handle->action_header = header_list; } if (opt & AR_CTX_EXTRACT) { archive_handle->action_data = data_extract_all; } if (opt & AR_OPT_PRESERVE_DATE) { archive_handle->flags |= ARCHIVE_PRESERVE_DATE; } if (opt & AR_OPT_VERBOSE) { archive_handle->action_header = header_verbose_list_ar; } if (opt & AR_OPT_CREATE) { bb_error_msg_and_die(msg_unsupported_err, "creation"); } if (opt & AR_OPT_INSERT) { bb_error_msg_and_die(msg_unsupported_err, "insertion"); } archive_handle->src_fd = xopen(argv[optind++], O_RDONLY); while (optind < argc) { archive_handle->filter = filter_accept_list; llist_add_to(&(archive_handle->accept), argv[optind++]); } xread(archive_handle->src_fd, magic, 7); if (strncmp(magic, "!<arch>", 7) != 0) { bb_error_msg_and_die("invalid ar magic"); } archive_handle->offset += 7; while (get_header_ar(archive_handle) == EXIT_SUCCESS) continue; return EXIT_SUCCESS; }
extern void unpack_ar_archive(archive_handle_t *ar_archive) { char magic[7]; archive_xread_all(ar_archive, magic, 7); if (strncmp(magic, "!<arch>", 7) != 0) { bb_error_msg_and_die("Invalid ar magic"); } ar_archive->offset += 7; while (get_header_ar(ar_archive) == EXIT_SUCCESS); }
void FAST_FUNC unpack_ar_archive(archive_handle_t *ar_archive) { char magic[7]; xread(ar_archive->src_fd, magic, AR_MAGIC_LEN); if (strncmp(magic, AR_MAGIC, AR_MAGIC_LEN) != 0) { bb_error_msg_and_die("invalid ar magic"); } ar_archive->offset += AR_MAGIC_LEN; while (get_header_ar(ar_archive) == EXIT_SUCCESS) continue; }
extern char get_header_ar(archive_handle_t *archive_handle) { file_header_t *typed = archive_handle->file_header; union { char raw[60]; struct { char name[16]; char date[12]; char uid[6]; char gid[6]; char mode[8]; char size[10]; char magic[2]; } formated; } ar; #ifdef CONFIG_FEATURE_AR_LONG_FILENAMES static char *ar_long_names; static unsigned int ar_long_name_size; #endif /* dont use bb_xread as we want to handle the error ourself */ if (read(archive_handle->src_fd, ar.raw, 60) != 60) { /* End Of File */ return(EXIT_FAILURE); } /* Some ar entries have a trailing '\n' after the previous data entry */ if (ar.raw[0] == '\n') { /* fix up the header, we started reading 1 byte too early */ memmove(ar.raw, &ar.raw[1], 59); ar.raw[59] = bb_xread_char(archive_handle->src_fd); archive_handle->offset++; } archive_handle->offset += 60; /* align the headers based on the header magic */ if ((ar.formated.magic[0] != '`') || (ar.formated.magic[1] != '\n')) { bb_error_msg_and_die("Invalid ar header"); } typed->mode = strtol(ar.formated.mode, NULL, 8); typed->mtime = atoi(ar.formated.date); typed->uid = atoi(ar.formated.uid); typed->gid = atoi(ar.formated.gid); typed->size = atoi(ar.formated.size); /* long filenames have '/' as the first character */ if (ar.formated.name[0] == '/') { #ifdef CONFIG_FEATURE_AR_LONG_FILENAMES if (ar.formated.name[1] == '/') { /* If the second char is a '/' then this entries data section * stores long filename for multiple entries, they are stored * in static variable long_names for use in future entries */ ar_long_name_size = typed->size; ar_long_names = xmalloc(ar_long_name_size); bb_xread_all(archive_handle->src_fd, ar_long_names, ar_long_name_size); archive_handle->offset += ar_long_name_size; /* This ar entries data section only contained filenames for other records * they are stored in the static ar_long_names for future reference */ return (get_header_ar(archive_handle)); /* Return next header */ } else if (ar.formated.name[1] == ' ') { /* This is the index of symbols in the file for compilers */ data_skip(archive_handle); archive_handle->offset += typed->size; return (get_header_ar(archive_handle)); /* Return next header */ } else { /* The number after the '/' indicates the offset in the ar data section (saved in variable long_name) that conatains the real filename */ const unsigned int long_offset = atoi(&ar.formated.name[1]); if (long_offset >= ar_long_name_size) { bb_error_msg_and_die("Cant resolve long filename"); } typed->name = bb_xstrdup(ar_long_names + long_offset); } #else bb_error_msg_and_die("long filenames not supported"); #endif } else { /* short filenames */ typed->name = bb_xstrndup(ar.formated.name, 16); } typed->name[strcspn(typed->name, " /")] = '\0'; if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) { archive_handle->action_header(typed); if (archive_handle->sub_archive) { while (archive_handle->action_data_subarchive(archive_handle->sub_archive) == EXIT_SUCCESS); } else { archive_handle->action_data(archive_handle); } } else { data_skip(archive_handle); } archive_handle->offset += typed->size; /* Set the file pointer to the correct spot, we may have been reading a compressed file */ lseek(archive_handle->src_fd, archive_handle->offset, SEEK_SET); return(EXIT_SUCCESS); }
char * deb_extract(const char *package_filename, FILE *out_stream, const int extract_function, const char *prefix, const char *filename, int *err) { FILE *deb_stream = NULL; file_header_t *ar_header = NULL; const char **file_list = NULL; char *output_buffer = NULL; char *ared_file = NULL; char ar_magic[8]; int gz_err; *err = 0; if (filename != NULL) { file_list = xmalloc(sizeof(char *) * 2); file_list[0] = filename; file_list[1] = NULL; } if (extract_function & extract_control_tar_gz) { ared_file = "control.tar.gz"; } else if (extract_function & extract_data_tar_gz) { ared_file = "data.tar.gz"; } else { error_msg( "Internal error: extract_function=%x\n", extract_function); *err = -1; goto cleanup; } /* open the debian package to be worked on */ deb_stream = wfopen(package_filename, "r"); if (deb_stream == NULL) { *err = -1; goto cleanup; } /* set the buffer size */ setvbuf(deb_stream, NULL, _IOFBF, 0x8000); /* check ar magic */ fread(ar_magic, 1, 8, deb_stream); if (strncmp(ar_magic,"!<arch>",7) == 0) { archive_offset = 8; while ((ar_header = get_header_ar(deb_stream)) != NULL) { if (strcmp(ared_file, ar_header->name) == 0) { int gunzip_pid = 0; FILE *uncompressed_stream; /* open a stream of decompressed data */ uncompressed_stream = gz_open(deb_stream, &gunzip_pid); if (uncompressed_stream == NULL) { *err = -1; goto cleanup; } archive_offset = 0; output_buffer = unarchive(uncompressed_stream, out_stream, get_header_tar, free_header_tar, extract_function, prefix, file_list, err); fclose(uncompressed_stream); gz_err = gz_close(gunzip_pid); if (gz_err) *err = -1; free_header_ar(ar_header); break; } if (fseek(deb_stream, ar_header->size, SEEK_CUR) == -1) { perror_msg("Couldn't fseek into %s", package_filename); *err = -1; free_header_ar(ar_header); goto cleanup; } free_header_ar(ar_header); } goto cleanup; } else if (strncmp(ar_magic, "\037\213", 2) == 0) { /* it's a gz file, let's assume it's an opkg */ int unzipped_opkg_pid; FILE *unzipped_opkg_stream; file_header_t *tar_header; archive_offset = 0; if (fseek(deb_stream, 0, SEEK_SET) == -1) { perror_msg( "Couldn't fseek into %s", package_filename); *err = -1; goto cleanup; } unzipped_opkg_stream = gz_open(deb_stream, &unzipped_opkg_pid); if (unzipped_opkg_stream == NULL) { *err = -1; goto cleanup; } /* walk through outer tar file to find ared_file */ while ((tar_header = get_header_tar(unzipped_opkg_stream)) != NULL) { int name_offset = 0; if (strncmp(tar_header->name, "./", 2) == 0) name_offset = 2; if (strcmp(ared_file, tar_header->name+name_offset) == 0) { int gunzip_pid = 0; FILE *uncompressed_stream; /* open a stream of decompressed data */ uncompressed_stream = gz_open(unzipped_opkg_stream, &gunzip_pid); if (uncompressed_stream == NULL) { *err = -1; goto cleanup; } archive_offset = 0; output_buffer = unarchive(uncompressed_stream, out_stream, get_header_tar, free_header_tar, extract_function, prefix, file_list, err); free_header_tar(tar_header); fclose(uncompressed_stream); gz_err = gz_close(gunzip_pid); if (gz_err) *err = -1; break; } seek_sub_file(unzipped_opkg_stream, tar_header->size); free_header_tar(tar_header); } fclose(unzipped_opkg_stream); gz_err = gz_close(unzipped_opkg_pid); if (gz_err) *err = -1; goto cleanup; } else { *err = -1; error_msg("%s: invalid magic", package_filename); } cleanup: if (deb_stream) fclose(deb_stream); if (file_list) free(file_list); return output_buffer; }
static file_header_t * get_header_ar(FILE *src_stream) { file_header_t *typed; union { char raw[60]; struct { char name[16]; char date[12]; char uid[6]; char gid[6]; char mode[8]; char size[10]; char magic[2]; } formated; } ar; static char *ar_long_names; if (fread(ar.raw, 1, 60, src_stream) != 60) { return(NULL); } archive_offset += 60; /* align the headers based on the header magic */ if ((ar.formated.magic[0] != '`') || (ar.formated.magic[1] != '\n')) { /* some version of ar, have an extra '\n' after each data entry, * this puts the next header out by 1 */ if (ar.formated.magic[1] != '`') { error_msg("Invalid magic"); return(NULL); } /* read the next char out of what would be the data section, * if its a '\n' then it is a valid header offset by 1*/ archive_offset++; if (fgetc(src_stream) != '\n') { error_msg("Invalid magic"); return(NULL); } /* fix up the header, we started reading 1 byte too early */ /* raw_header[60] wont be '\n' as it should, but it doesnt matter */ memmove(ar.raw, &ar.raw[1], 59); } typed = (file_header_t *) xcalloc(1, sizeof(file_header_t)); typed->size = (size_t) atoi(ar.formated.size); /* long filenames have '/' as the first character */ if (ar.formated.name[0] == '/') { if (ar.formated.name[1] == '/') { /* If the second char is a '/' then this entries data section * stores long filename for multiple entries, they are stored * in static variable long_names for use in future entries */ ar_long_names = (char *) xrealloc(ar_long_names, typed->size); fread(ar_long_names, 1, typed->size, src_stream); archive_offset += typed->size; /* This ar entries data section only contained filenames for other records * they are stored in the static ar_long_names for future reference */ return (get_header_ar(src_stream)); /* Return next header */ } else if (ar.formated.name[1] == ' ') { /* This is the index of symbols in the file for compilers */ seek_sub_file(src_stream, typed->size); return (get_header_ar(src_stream)); /* Return next header */ } else { /* The number after the '/' indicates the offset in the ar data section (saved in variable long_name) that conatains the real filename */ if (!ar_long_names) { error_msg("Cannot resolve long file name"); return (NULL); } typed->name = xstrdup(ar_long_names + atoi(&ar.formated.name[1])); } } else { /* short filenames */ typed->name = xcalloc(1, 16); strncpy(typed->name, ar.formated.name, 16); } typed->name[strcspn(typed->name, " /")]='\0'; /* convert the rest of the now valid char header to its typed struct */ parse_mode(ar.formated.mode, &typed->mode); typed->mtime = atoi(ar.formated.date); typed->uid = atoi(ar.formated.uid); typed->gid = atoi(ar.formated.gid); return(typed); }