diff options
Diffstat (limited to 'src/ipsw.c')
-rw-r--r-- | src/ipsw.c | 427 |
1 files changed, 321 insertions, 106 deletions
@@ -45,26 +45,18 @@ #endif #include <libimobiledevice-glue/termcolors.h> +#include <plist/plist.h> #include "ipsw.h" #include "locking.h" #include "download.h" #include "common.h" #include "idevicerestore.h" -#include "json_plist.h" #define BUFSIZE 0x100000 -typedef struct { - struct zip* zip; - char *path; -} ipsw_archive; - static int cancel_flag = 0; -ipsw_archive* ipsw_open(const char* ipsw); -void ipsw_close(ipsw_archive* archive); - static char* build_path(const char* path, const char* file) { size_t plen = strlen(path); @@ -118,11 +110,14 @@ int ipsw_print_info(const char* path) uint32_t plist_len = 0; if (memcmp(&magic, "PK\x03\x04", 4) == 0) { + ipsw_archive_t ipsw = ipsw_open(thepath); unsigned int rlen = 0; - if (ipsw_extract_to_memory(thepath, "BuildManifest.plist", (unsigned char**)&plist_buf, &rlen) < 0) { + if (ipsw_extract_to_memory(ipsw, "BuildManifest.plist", (unsigned char**)&plist_buf, &rlen) < 0) { + ipsw_close(ipsw); error("ERROR: Failed to extract BuildManifest.plist from IPSW!\n"); return -1; } + ipsw_close(ipsw); plist_len = (uint32_t)rlen; } else { size_t rlen = 0; @@ -134,7 +129,7 @@ int ipsw_print_info(const char* path) } plist_t manifest = NULL; - plist_from_memory(plist_buf, plist_len, &manifest); + plist_from_memory(plist_buf, plist_len, &manifest, NULL); free(plist_buf); plist_t val; @@ -152,10 +147,10 @@ int ipsw_print_info(const char* path) plist_get_string_val(val, &build_ver); } - cprintf(COLOR_WHITE "Product Version: " COLOR_BRIGHT_YELLOW "%s" COLOR_RESET COLOR_WHITE " Build: " COLOR_BRIGHT_YELLOW "%s" COLOR_RESET "\n", prod_ver, build_ver); + cprintf(FG_WHITE "Product Version: " FG_BRIGHT_YELLOW "%s" COLOR_RESET FG_WHITE " Build: " FG_BRIGHT_YELLOW "%s" COLOR_RESET "\n", prod_ver, build_ver); free(prod_ver); free(build_ver); - cprintf(COLOR_WHITE "Supported Product Types:" COLOR_RESET); + cprintf(FG_WHITE "Supported Product Types:" COLOR_RESET); val = plist_dict_get_item(manifest, "SupportedProductTypes"); if (val) { plist_array_iter iter = NULL; @@ -167,7 +162,7 @@ int ipsw_print_info(const char* path) if (item) { char* item_str = NULL; plist_get_string_val(item, &item_str); - cprintf(" " COLOR_BRIGHT_CYAN "%s" COLOR_RESET, item_str); + cprintf(" " FG_BRIGHT_CYAN "%s" COLOR_RESET, item_str); free(item_str); } } while (item); @@ -176,7 +171,7 @@ int ipsw_print_info(const char* path) } cprintf("\n"); - cprintf(COLOR_WHITE "Build Identities:" COLOR_RESET "\n"); + cprintf(FG_WHITE "Build Identities:" COLOR_RESET "\n"); plist_t build_ids_grouped = plist_new_dict(); @@ -201,7 +196,15 @@ int ipsw_print_info(const char* path) if (!group) { group = plist_new_dict(); node = plist_access_path(build_identity, 2, "Info", "RestoreBehavior"); - plist_dict_set_item(group, "RestoreBehavior", plist_copy(node)); + if (node) { + plist_dict_set_item(group, "RestoreBehavior", plist_copy(node)); + } else { + if (strstr(variant_str, "Upgrade")) { + plist_dict_set_item(group, "RestoreBehavior", plist_new_string("Update")); + } else if (strstr(variant_str, "Erase")) { + plist_dict_set_item(group, "RestoreBehavior", plist_new_string("Erase")); + } + } entries = plist_new_array(); plist_dict_set_item(group, "Entries", entries); plist_dict_set_item(build_ids_grouped, variant_str, group); @@ -232,7 +235,7 @@ int ipsw_print_info(const char* path) group_no++; node = plist_dict_get_item(group, "RestoreBehavior"); plist_get_string_val(node, &rbehavior); - cprintf(" " COLOR_WHITE "[%d] Variant: " COLOR_BRIGHT_CYAN "%s" COLOR_WHITE " Behavior: " COLOR_BRIGHT_CYAN "%s" COLOR_RESET "\n", group_no, key, rbehavior); + cprintf(" " FG_WHITE "[%d] Variant: " FG_BRIGHT_CYAN "%s" FG_WHITE " Behavior: " FG_BRIGHT_CYAN "%s" COLOR_RESET "\n", group_no, key, rbehavior); free(key); free(rbehavior); @@ -285,9 +288,9 @@ int ipsw_print_info(const char* path) irecv_device_t irecvdev = NULL; if (irecv_devices_get_device_by_hardware_model(hwmodel, &irecvdev) == 0) { - cprintf(" ChipID: " COLOR_GREEN "%04x" COLOR_RESET " BoardID: " COLOR_GREEN "%02x" COLOR_RESET " Model: " COLOR_YELLOW "%-8s" COLOR_RESET " " COLOR_MAGENTA "%s" COLOR_RESET "\n", (int)chip_id, (int)board_id, hwmodel, irecvdev->display_name); + cprintf(" ChipID: " FG_GREEN "%04x" COLOR_RESET " BoardID: " FG_GREEN "%02x" COLOR_RESET " Model: " FG_YELLOW "%-8s" COLOR_RESET " " FG_MAGENTA "%s" COLOR_RESET "\n", (int)chip_id, (int)board_id, hwmodel, irecvdev->display_name); } else { - cprintf(" ChipID: " COLOR_GREEN "%04x" COLOR_RESET " BoardID: " COLOR_GREEN "%02x" COLOR_RESET " Model: " COLOR_YELLOW "%s" COLOR_RESET "\n", (int)chip_id, (int)board_id, hwmodel); + cprintf(" ChipID: " FG_GREEN "%04x" COLOR_RESET " BoardID: " FG_GREEN "%02x" COLOR_RESET " Model: " FG_YELLOW "%s" COLOR_RESET "\n", (int)chip_id, (int)board_id, hwmodel); } free(hwmodel); } while (build_id); @@ -302,10 +305,10 @@ int ipsw_print_info(const char* path) return 0; } -ipsw_archive* ipsw_open(const char* ipsw) +ipsw_archive_t ipsw_open(const char* ipsw) { int err = 0; - ipsw_archive* archive = (ipsw_archive*) malloc(sizeof(ipsw_archive)); + ipsw_archive_t archive = (ipsw_archive_t)malloc(sizeof(struct ipsw_archive)); if (archive == NULL) { error("ERROR: Out of memory\n"); return NULL; @@ -316,7 +319,6 @@ ipsw_archive* ipsw_open(const char* ipsw) error("ERROR: ipsw_open %s: %s\n", ipsw, strerror(errno)); return NULL; } - archive->path = strdup(ipsw); if (S_ISDIR(fst.st_mode)) { archive->zip = NULL; } else { @@ -327,7 +329,20 @@ ipsw_archive* ipsw_open(const char* ipsw) return NULL; } } - return archive; + archive->path = strdup(ipsw); + return (ipsw_archive_t)archive; +} + +void ipsw_close(ipsw_archive_t ipsw) +{ + if (ipsw != NULL) { + free(ipsw->path); + if (ipsw->zip) { + zip_unchange_all(ipsw->zip); + zip_close(ipsw->zip); + } + free(ipsw); + } } int ipsw_is_directory(const char* ipsw) @@ -340,37 +355,33 @@ int ipsw_is_directory(const char* ipsw) return S_ISDIR(fst.st_mode); } -int ipsw_get_file_size(const char* ipsw, const char* infile, uint64_t* size) +int ipsw_get_file_size(ipsw_archive_t ipsw, const char* infile, uint64_t* size) { - ipsw_archive* archive = ipsw_open(ipsw); - if (archive == NULL) { + if (ipsw == NULL) { error("ERROR: Invalid archive\n"); return -1; } - if (archive->zip) { - int zindex = zip_name_locate(archive->zip, infile, 0); + if (ipsw->zip) { + int zindex = zip_name_locate(ipsw->zip, infile, 0); if (zindex < 0) { error("ERROR: zip_name_locate: %s\n", infile); - ipsw_close(archive); return -1; } struct zip_stat zstat; zip_stat_init(&zstat); - if (zip_stat_index(archive->zip, zindex, 0, &zstat) != 0) { + if (zip_stat_index(ipsw->zip, zindex, 0, &zstat) != 0) { error("ERROR: zip_stat_index: %s\n", infile); - ipsw_close(archive); return -1; } *size = zstat.size; } else { - char *filepath = build_path(archive->path, infile); + char *filepath = build_path(ipsw->path, infile); struct stat fst; if (stat(filepath, &fst) != 0) { free(filepath); - ipsw_close(archive); return -1; } free(filepath); @@ -378,23 +389,22 @@ int ipsw_get_file_size(const char* ipsw, const char* infile, uint64_t* size) *size = fst.st_size; } - ipsw_close(archive); return 0; } -int ipsw_extract_to_file_with_progress(const char* ipsw, const char* infile, const char* outfile, int print_progress) +int ipsw_extract_to_file_with_progress(ipsw_archive_t ipsw, const char* infile, const char* outfile, int print_progress) { int ret = 0; - ipsw_archive* archive = ipsw_open(ipsw); - if (archive == NULL) { - error("ERROR: Invalid archive\n"); + + if (!ipsw || !infile || !outfile) { + error("ERROR: Invalid argument\n"); return -1; } cancel_flag = 0; - if (archive->zip) { - int zindex = zip_name_locate(archive->zip, infile, 0); + if (ipsw->zip) { + int zindex = zip_name_locate(ipsw->zip, infile, 0); if (zindex < 0) { error("ERROR: zip_name_locate: %s\n", infile); return -1; @@ -402,7 +412,7 @@ int ipsw_extract_to_file_with_progress(const char* ipsw, const char* infile, con struct zip_stat zstat; zip_stat_init(&zstat); - if (zip_stat_index(archive->zip, zindex, 0, &zstat) != 0) { + if (zip_stat_index(ipsw->zip, zindex, 0, &zstat) != 0) { error("ERROR: zip_stat_index: %s\n", infile); return -1; } @@ -413,7 +423,7 @@ int ipsw_extract_to_file_with_progress(const char* ipsw, const char* infile, con return -1; } - struct zip_file* zfile = zip_fopen_index(archive->zip, zindex, 0); + struct zip_file* zfile = zip_fopen_index(ipsw->zip, zindex, 0); if (zfile == NULL) { error("ERROR: zip_fopen_index: %s\n", infile); return -1; @@ -442,7 +452,7 @@ int ipsw_extract_to_file_with_progress(const char* ipsw, const char* infile, con break; } if (fwrite(buffer, 1, count, fd) != count) { - error("ERROR: frite: %s\n", outfile); + error("ERROR: Writing to '%s' failed: %s\n", outfile, strerror(errno)); ret = -1; break; } @@ -457,9 +467,13 @@ int ipsw_extract_to_file_with_progress(const char* ipsw, const char* infile, con fclose(fd); zip_fclose(zfile); } else { - char *filepath = build_path(archive->path, infile); + char *filepath = build_path(ipsw->path, infile); char actual_filepath[PATH_MAX+1]; char actual_outfile[PATH_MAX+1]; + if (!filepath) { + ret = -1; + goto leave; + } if (!realpath(filepath, actual_filepath)) { error("ERROR: realpath failed on %s: %s\n", filepath, strerror(errno)); ret = -1; @@ -515,7 +529,7 @@ int ipsw_extract_to_file_with_progress(const char* ipsw, const char* infile, con break; } if (fwrite(buffer, 1, r, fo) != r) { - error("ERROR: fwrite failed\n"); + error("ERROR: Writing to '%s' failed: %s\n", actual_outfile, strerror(errno)); ret = -1; break; } @@ -534,76 +548,66 @@ int ipsw_extract_to_file_with_progress(const char* ipsw, const char* infile, con leave: free(filepath); } - ipsw_close(archive); if (cancel_flag) { ret = -2; } return ret; } -int ipsw_extract_to_file(const char* ipsw, const char* infile, const char* outfile) +int ipsw_extract_to_file(ipsw_archive_t ipsw, const char* infile, const char* outfile) { return ipsw_extract_to_file_with_progress(ipsw, infile, outfile, 0); } -int ipsw_file_exists(const char* ipsw, const char* infile) +int ipsw_file_exists(ipsw_archive_t ipsw, const char* infile) { - ipsw_archive* archive = ipsw_open(ipsw); - if (archive == NULL) { + if (!ipsw) { return 0; } - if (archive->zip) { - int zindex = zip_name_locate(archive->zip, infile, 0); + if (ipsw->zip) { + int zindex = zip_name_locate(ipsw->zip, infile, 0); if (zindex < 0) { - ipsw_close(archive); return 0; } } else { - char *filepath = build_path(archive->path, infile); + char *filepath = build_path(ipsw->path, infile); if (access(filepath, R_OK) != 0) { free(filepath); - ipsw_close(archive); return 0; } free(filepath); } - ipsw_close(archive); - return 1; } -int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** pbuffer, unsigned int* psize) +int ipsw_extract_to_memory(ipsw_archive_t ipsw, const char* infile, unsigned char** pbuffer, unsigned int* psize) { size_t size = 0; unsigned char* buffer = NULL; - ipsw_archive* archive = ipsw_open(ipsw); - if (archive == NULL) { + if (ipsw == NULL) { error("ERROR: Invalid archive\n"); return -1; } - if (archive->zip) { - int zindex = zip_name_locate(archive->zip, infile, 0); + if (ipsw->zip) { + int zindex = zip_name_locate(ipsw->zip, infile, 0); if (zindex < 0) { debug("NOTE: zip_name_locate: '%s' not found in archive.\n", infile); - ipsw_close(archive); return -1; } struct zip_stat zstat; zip_stat_init(&zstat); - if (zip_stat_index(archive->zip, zindex, 0, &zstat) != 0) { + if (zip_stat_index(ipsw->zip, zindex, 0, &zstat) != 0) { error("ERROR: zip_stat_index: %s\n", infile); - ipsw_close(archive); return -1; } - struct zip_file* zfile = zip_fopen_index(archive->zip, zindex, 0); + struct zip_file* zfile = zip_fopen_index(ipsw->zip, zindex, 0); if (zfile == NULL) { error("ERROR: zip_fopen_index: %s\n", infile); - ipsw_close(archive); return -1; } @@ -612,7 +616,6 @@ int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** if (buffer == NULL) { error("ERROR: Out of memory\n"); zip_fclose(zfile); - ipsw_close(archive); return -1; } @@ -620,7 +623,6 @@ int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** error("ERROR: zip_fread: %s\n", infile); zip_fclose(zfile); free(buffer); - ipsw_close(archive); return -1; } @@ -628,7 +630,7 @@ int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** zip_fclose(zfile); } else { - char *filepath = build_path(archive->path, infile); + char *filepath = build_path(ipsw->path, infile); struct stat fst; #ifdef WIN32 if (stat(filepath, &fst) != 0) { @@ -637,7 +639,6 @@ int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** #endif error("ERROR: %s: stat failed for %s: %s\n", __func__, filepath, strerror(errno)); free(filepath); - ipsw_close(archive); return -1; } size = fst.st_size; @@ -645,7 +646,6 @@ int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** if (buffer == NULL) { error("ERROR: Out of memory\n"); free(filepath); - ipsw_close(archive); return -1; } @@ -655,7 +655,6 @@ int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** error("ERROR: %s: readlink failed for %s: %s\n", __func__, filepath, strerror(errno)); free(filepath); free(buffer); - ipsw_close(archive); return -1; } } else { @@ -665,7 +664,6 @@ int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** error("ERROR: %s: fopen failed for %s: %s\n", __func__, filepath, strerror(errno)); free(filepath); free(buffer); - ipsw_close(archive); return -2; } if (fread(buffer, 1, size, f) != size) { @@ -673,7 +671,6 @@ int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** error("ERROR: %s: fread failed for %s: %s\n", __func__, filepath, strerror(errno)); free(filepath); free(buffer); - ipsw_close(archive); return -1; } fclose(f); @@ -684,14 +681,143 @@ int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** free(filepath); } - ipsw_close(archive); *pbuffer = buffer; *psize = size; return 0; } -int ipsw_extract_build_manifest(const char* ipsw, plist_t* buildmanifest, int *tss_enabled) +int ipsw_extract_send(ipsw_archive_t ipsw, const char* infile, int blocksize, ipsw_send_cb send_callback, void* ctx) +{ + unsigned char* buffer = NULL; + size_t done = 0; + size_t total_size = 0; + + if (ipsw == NULL) { + error("ERROR: Invalid archive\n"); + return -1; + } + + if (ipsw->zip) { + int zindex = zip_name_locate(ipsw->zip, infile, 0); + if (zindex < 0) { + debug("NOTE: zip_name_locate: '%s' not found in archive.\n", infile); + return -1; + } + + struct zip_stat zstat; + zip_stat_init(&zstat); + if (zip_stat_index(ipsw->zip, zindex, 0, &zstat) != 0) { + error("ERROR: zip_stat_index: %s\n", infile); + return -1; + } + + struct zip_file* zfile = zip_fopen_index(ipsw->zip, zindex, 0); + if (zfile == NULL) { + error("ERROR: zip_fopen_index: %s\n", infile); + return -1; + } + + total_size = zstat.size; + buffer = (unsigned char*) malloc(blocksize); + if (buffer == NULL) { + error("ERROR: Out of memory\n"); + zip_fclose(zfile); + return -1; + } + + while (done < total_size) { + size_t size = total_size-done; + if (size > blocksize) size = blocksize; + zip_int64_t zr = zip_fread(zfile, buffer, size); + if (zr < 0) { + error("ERROR: %s: zip_fread: %s\n", __func__, infile); + break; + } else if (zr == 0) { + // EOF + break; + } + if (send_callback(ctx, buffer, zr, done, total_size) < 0) { + error("ERROR: %s: send failed\n", __func__); + break; + } + done += zr; + } + free(buffer); + } else { + char *filepath = build_path(ipsw->path, infile); + struct stat fst; +#ifdef WIN32 + if (stat(filepath, &fst) != 0) { +#else + if (lstat(filepath, &fst) != 0) { +#endif + error("ERROR: %s: stat failed for %s: %s\n", __func__, filepath, strerror(errno)); + free(filepath); + return -1; + } + total_size = fst.st_size; + buffer = (unsigned char*)malloc(blocksize); + if (buffer == NULL) { + error("ERROR: Out of memory\n"); + free(filepath); + return -1; + } + +#ifndef WIN32 + if (S_ISLNK(fst.st_mode)) { + ssize_t rl = readlink(filepath, (char*)buffer, (total_size > blocksize) ? blocksize : total_size); + if (rl < 0) { + error("ERROR: %s: readlink failed for %s: %s\n", __func__, filepath, strerror(errno)); + free(filepath); + free(buffer); + return -1; + } + send_callback(ctx, buffer, (size_t)rl, 0, 0); + } else { +#endif + FILE *f = fopen(filepath, "rb"); + if (!f) { + error("ERROR: %s: fopen failed for %s: %s\n", __func__, filepath, strerror(errno)); + free(filepath); + free(buffer); + return -2; + } + + while (done < total_size) { + size_t size = total_size-done; + if (size > blocksize) size = blocksize; + size_t fr = fread(buffer, 1, size, f); + if (fr != size) { + error("ERROR: %s: fread failed for %s: %s\n", __func__, filepath, strerror(errno)); + break; + } + if (send_callback(ctx, buffer, fr, done, total_size) < 0) { + error("ERROR: %s: send failed\n", __func__); + break; + } + done += fr; + } + fclose(f); +#ifndef WIN32 + } +#endif + free(filepath); + free(buffer); + } + + if (done < total_size) { + error("ERROR: %s: Sending file data for %s failed (sent %" PRIu64 "/%" PRIu64 ")\n", __func__, infile, (uint64_t)done, (uint64_t)total_size); + return -1; + } + + // send a NULL buffer to mark end of transfer + send_callback(ctx, NULL, 0, done, total_size); + + return 0; +} + +int ipsw_extract_build_manifest(ipsw_archive_t ipsw, plist_t* buildmanifest, int *tss_enabled) { unsigned int size = 0; unsigned char* data = NULL; @@ -721,7 +847,7 @@ int ipsw_extract_build_manifest(const char* ipsw, plist_t* buildmanifest, int *t return -1; } -int ipsw_extract_restore_plist(const char* ipsw, plist_t* restore_plist) +int ipsw_extract_restore_plist(ipsw_archive_t ipsw, plist_t* restore_plist) { unsigned int size = 0; unsigned char* data = NULL; @@ -735,10 +861,10 @@ int ipsw_extract_restore_plist(const char* ipsw, plist_t* restore_plist) return -1; } -static int ipsw_list_contents_recurse(ipsw_archive *archive, const char *path, ipsw_list_cb cb, void *ctx) +static int ipsw_list_contents_recurse(ipsw_archive_t ipsw, const char *path, ipsw_list_cb cb, void *ctx) { int ret = 0; - char *base = build_path(archive->path, path); + char *base = build_path(ipsw->path, path); DIR *dirp = opendir(base); @@ -776,10 +902,10 @@ static int ipsw_list_contents_recurse(ipsw_archive *archive, const char *path, i break; } - ret = cb(ctx, archive->path, subpath, &st); + ret = cb(ctx, ipsw, subpath, &st); if (ret >= 0 && S_ISDIR(st.st_mode)) - ipsw_list_contents_recurse(archive, subpath, cb, ctx); + ipsw_list_contents_recurse(ipsw, subpath, cb, ctx); free(fpath); free(subpath); @@ -790,21 +916,19 @@ static int ipsw_list_contents_recurse(ipsw_archive *archive, const char *path, i return ret; } -int ipsw_list_contents(const char* ipsw, ipsw_list_cb cb, void *ctx) +int ipsw_list_contents(ipsw_archive_t ipsw, ipsw_list_cb cb, void *ctx) { int ret = 0; - ipsw_archive* archive = ipsw_open(ipsw); - if (archive == NULL) { - error("ERROR: Invalid archive\n"); + if (ipsw == NULL) { + error("ERROR: Invalid IPSW archive\n"); return -1; } - if (archive->zip) { - int64_t entries = zip_get_num_entries(archive->zip, 0); + if (ipsw->zip) { + int64_t entries = zip_get_num_entries(ipsw->zip, 0); if (entries < 0) { error("ERROR: zip_get_num_entries failed\n"); - ipsw_close(archive); return -1; } @@ -812,7 +936,7 @@ int ipsw_list_contents(const char* ipsw, ipsw_list_cb cb, void *ctx) zip_stat_t stat; zip_stat_init(&stat); - if (zip_stat_index(archive->zip, index, 0, &stat) < 0) { + if (zip_stat_index(ipsw->zip, index, 0, &stat) < 0) { error("ERROR: zip_stat_index failed for %s\n", stat.name); ret = -1; continue; @@ -820,7 +944,7 @@ int ipsw_list_contents(const char* ipsw, ipsw_list_cb cb, void *ctx) uint8_t opsys; uint32_t attributes; - if (zip_file_get_external_attributes(archive->zip, index, 0, &opsys, &attributes) < 0) { + if (zip_file_get_external_attributes(ipsw->zip, index, 0, &opsys, &attributes) < 0) { error("ERROR: zip_file_get_external_attributes failed for %s\n", stat.name); ret = -1; continue; @@ -850,25 +974,12 @@ int ipsw_list_contents(const char* ipsw, ipsw_list_cb cb, void *ctx) break; } } else { - ret = ipsw_list_contents_recurse(archive, "", cb, ctx); + ret = ipsw_list_contents_recurse(ipsw, "", cb, ctx); } - ipsw_close(archive); return ret; } -void ipsw_close(ipsw_archive* archive) -{ - if (archive != NULL) { - free(archive->path); - if (archive->zip) { - zip_unchange_all(archive->zip); - zip_close(archive->zip); - } - free(archive); - } -} - int ipsw_get_signed_firmwares(const char* product, plist_t* firmwares) { char url[256]; @@ -892,7 +1003,7 @@ int ipsw_get_signed_firmwares(const char* product, plist_t* firmwares) error("ERROR: Download from %s failed.\n", url); return -1; } - dict = json_to_plist(jdata); + plist_from_json(jdata, jsize, &dict); free(jdata); if (!dict || plist_get_node_type(dict) != PLIST_DICT) { error("ERROR: Failed to parse json data.\n"); @@ -1192,3 +1303,107 @@ void ipsw_cancel(void) { cancel_flag++; } + +ipsw_file_handle_t ipsw_file_open(ipsw_archive_t ipsw, const char* path) +{ + ipsw_file_handle_t handle = (ipsw_file_handle_t)calloc(1, sizeof(struct ipsw_file_handle)); + if (ipsw->zip) { + zip_stat_t zst; + zip_int64_t zindex = zip_name_locate(ipsw->zip, path, 0); + if (zindex < 0) { + error("ERROR: zip_name_locate: %s not found\n", path); + free(handle); + return NULL; + } + handle->zfile = zip_fopen_index(ipsw->zip, zindex, 0); + if (handle->zfile == NULL) { + error("ERROR: zip_fopen_index: %s could not be opened\n", path); + free(handle); + return NULL; + } + zip_stat_init(&zst); + zip_stat(ipsw->zip, path, 0, &zst); + handle->size = zst.size; + handle->seekable = (zst.comp_method == ZIP_CM_STORE); + } else { + struct stat st; + char *filepath = build_path(ipsw->path, path); + handle->file = fopen(filepath, "rb"); + free(filepath); + if (!handle->file) { + error("ERROR: fopen: %s could not be opened\n", path); + free(handle); + return NULL; + } + fstat(fileno(handle->file), &st); + handle->size = st.st_size; + handle->seekable = 1; + } + return handle; +} + +void ipsw_file_close(ipsw_file_handle_t handle) +{ + if (handle && handle->zfile) { + zip_fclose(handle->zfile); + } else if (handle && handle->file) { + fclose(handle->file); + } + free(handle); +} + +uint64_t ipsw_file_size(ipsw_file_handle_t handle) +{ + if (handle) { + return handle->size; + } + return 0; +} + +int64_t ipsw_file_read(ipsw_file_handle_t handle, void* buffer, size_t size) +{ + if (handle && handle->zfile) { + zip_int64_t zr = zip_fread(handle->zfile, buffer, size); + return (int64_t)zr; + } else if (handle && handle->file) { + return fread(buffer, 1, size, handle->file); + } else { + error("ERROR: %s: Invalid file handle\n", __func__); + return -1; + } +} + +int ipsw_file_seek(ipsw_file_handle_t handle, int64_t offset, int whence) +{ + if (handle && handle->zfile) { + return zip_fseek(handle->zfile, offset, whence); + } else if (handle && handle->file) { +#ifdef WIN32 + if (whence == SEEK_SET) { + rewind(handle->file); + } + return (_lseeki64(fileno(handle->file), offset, whence) < 0) ? -1 : 0; +#else + return fseeko(handle->file, offset, whence); +#endif + } else { + error("ERROR: %s: Invalid file handle\n", __func__); + return -1; + } +} + +int64_t ipsw_file_tell(ipsw_file_handle_t handle) +{ + if (handle && handle->zfile) { + return zip_ftell(handle->zfile); + } else if (handle && handle->file) { +#ifdef WIN32 + return _lseeki64(fileno(handle->file), 0, SEEK_CUR); +#else + return ftello(handle->file); +#endif + } else { + error("ERROR: %s: Invalid file handle\n", __func__); + return -1; + } +} |