diff options
Diffstat (limited to 'src/ipsw.c')
| -rw-r--r-- | src/ipsw.c | 786 |
1 files changed, 567 insertions, 219 deletions
@@ -34,37 +34,21 @@ #include <sys/types.h> #include <dirent.h> #include <zip.h> -#ifdef HAVE_OPENSSL -#include <openssl/sha.h> -#else -#include "sha1.h" -#define SHA_CTX SHA1_CTX -#define SHA1_Init SHA1Init -#define SHA1_Update SHA1Update -#define SHA1_Final SHA1Final -#endif +#include <libimobiledevice-glue/sha.h> #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); @@ -85,56 +69,59 @@ int ipsw_print_info(const char* path) struct stat fst; if (stat(path, &fst) != 0) { - error("ERROR: '%s': %s\n", path, strerror(errno)); + logger(LL_ERROR, "'%s': %s\n", path, strerror(errno)); return -1; } char thepath[PATH_MAX]; if (S_ISDIR(fst.st_mode)) { - sprintf(thepath, "%s/BuildManifest.plist", path); + snprintf(thepath, sizeof(thepath), "%s/BuildManifest.plist", path); if (stat(thepath, &fst) != 0) { - error("ERROR: '%s': %s\n", thepath, strerror(errno)); + logger(LL_ERROR, "'%s': %s\n", thepath, strerror(errno)); return -1; } } else { - sprintf(thepath, "%s", path); + snprintf(thepath, sizeof(thepath), "%s", path); } FILE* f = fopen(thepath, "r"); if (!f) { - error("ERROR: Can't open '%s': %s\n", thepath, strerror(errno)); + logger(LL_ERROR, "Can't open '%s': %s\n", thepath, strerror(errno)); return -1; } uint32_t magic; if (fread(&magic, 1, 4, f) != 4) { fclose(f); - fprintf(stderr, "Failed to read from '%s'\n", path); + logger(LL_ERROR, "Failed to read from '%s'\n", path); return -1; } fclose(f); - char* plist_buf = NULL; + void* plist_buf = NULL; uint32_t plist_len = 0; if (memcmp(&magic, "PK\x03\x04", 4) == 0) { - unsigned int rlen = 0; - if (ipsw_extract_to_memory(thepath, "BuildManifest.plist", (unsigned char**)&plist_buf, &rlen) < 0) { - error("ERROR: Failed to extract BuildManifest.plist from IPSW!\n"); + ipsw_archive_t ipsw = ipsw_open(thepath); + size_t rlen = 0; + if (ipsw_extract_to_memory(ipsw, "BuildManifest.plist", &plist_buf, &rlen) < 0) { + ipsw_close(ipsw); + logger(LL_ERROR, "Failed to extract BuildManifest.plist from IPSW!\n"); return -1; } + ipsw_close(ipsw); plist_len = (uint32_t)rlen; } else { size_t rlen = 0; - if (read_file(thepath, (void**)&plist_buf, &rlen) < 0) { - error("ERROR: Failed to read BuildManifest.plist!\n"); + if (read_file(thepath, &plist_buf, &rlen) < 0) { + logger(LL_ERROR, "Failed to read BuildManifest.plist!\n"); return -1; } plist_len = (uint32_t)rlen; } 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 +139,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 +154,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 +163,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 +188,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 +227,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 +280,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,32 +297,41 @@ 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)calloc(1, sizeof(struct ipsw_archive)); if (archive == NULL) { - error("ERROR: Out of memory\n"); + logger(LL_ERROR, "Out of memory\n"); return NULL; } struct stat fst; if (stat(ipsw, &fst) != 0) { - error("ERROR: ipsw_open %s: %s\n", ipsw, strerror(errno)); + logger(LL_ERROR, "ipsw_open %s: %s\n", ipsw, strerror(errno)); return NULL; } - archive->path = strdup(ipsw); if (S_ISDIR(fst.st_mode)) { - archive->zip = NULL; + archive->zip = 0; } else { - archive->zip = zip_open(ipsw, 0, &err); - if (archive->zip == NULL) { - error("ERROR: zip_open: %s: %d\n", ipsw, err); + struct zip *zip = zip_open(ipsw, 0, &err); + if (zip == NULL) { + logger(LL_ERROR, "zip_open: %s: %d\n", ipsw, err); free(archive); return NULL; } + archive->zip = 1; + } + archive->path = strdup(ipsw); + return (ipsw_archive_t)archive; +} + +void ipsw_close(ipsw_archive_t ipsw) +{ + if (ipsw != NULL) { + free(ipsw->path); + free(ipsw); } - return archive; } int ipsw_is_directory(const char* ipsw) @@ -340,37 +344,45 @@ 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) { - error("ERROR: Invalid archive\n"); + if (ipsw == NULL) { + logger(LL_ERROR, "Invalid archive\n"); return -1; } - if (archive->zip) { - int zindex = zip_name_locate(archive->zip, infile, 0); + if (ipsw->zip) { + int err = 0; + struct zip *zip = zip_open(ipsw->path, 0, &err); + if (zip == NULL) { + logger(LL_ERROR, "zip_open: %s: %d\n", ipsw->path, err); + return -1; + } + int zindex = zip_name_locate(zip, infile, 0); if (zindex < 0) { - error("ERROR: zip_name_locate: %s\n", infile); - ipsw_close(archive); + logger(LL_ERROR, "zip_name_locate: %s\n", infile); + zip_unchange_all(zip); + zip_close(zip); return -1; } struct zip_stat zstat; zip_stat_init(&zstat); - if (zip_stat_index(archive->zip, zindex, 0, &zstat) != 0) { - error("ERROR: zip_stat_index: %s\n", infile); - ipsw_close(archive); + if (zip_stat_index(zip, zindex, 0, &zstat) != 0) { + logger(LL_ERROR, "zip_stat_index: %s\n", infile); + zip_unchange_all(zip); + zip_close(zip); return -1; } + zip_unchange_all(zip); + zip_close(zip); *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,57 +390,75 @@ 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) { + logger(LL_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 err = 0; + struct zip *zip = zip_open(ipsw->path, 0, &err); + if (zip == NULL) { + logger(LL_ERROR, "zip_open: %s: %d\n", ipsw->path, err); + return -1; + } + + int zindex = zip_name_locate(zip, infile, 0); if (zindex < 0) { - error("ERROR: zip_name_locate: %s\n", infile); + zip_unchange_all(zip); + zip_close(zip); + logger(LL_ERROR, "zip_name_locate: %s\n", infile); return -1; } struct zip_stat zstat; zip_stat_init(&zstat); - if (zip_stat_index(archive->zip, zindex, 0, &zstat) != 0) { - error("ERROR: zip_stat_index: %s\n", infile); + if (zip_stat_index(zip, zindex, 0, &zstat) != 0) { + zip_unchange_all(zip); + zip_close(zip); + logger(LL_ERROR, "zip_stat_index: %s\n", infile); return -1; } char* buffer = (char*) malloc(BUFSIZE); if (buffer == NULL) { - error("ERROR: Unable to allocate memory\n"); + zip_unchange_all(zip); + zip_close(zip); + logger(LL_ERROR, "Unable to allocate memory\n"); return -1; } - struct zip_file* zfile = zip_fopen_index(archive->zip, zindex, 0); + struct zip_file* zfile = zip_fopen_index(zip, zindex, 0); if (zfile == NULL) { - error("ERROR: zip_fopen_index: %s\n", infile); + zip_unchange_all(zip); + zip_close(zip); + logger(LL_ERROR, "zip_fopen_index: %s\n", infile); return -1; } FILE* fd = fopen(outfile, "wb"); if (fd == NULL) { - error("ERROR: Unable to open output file: %s\n", outfile); zip_fclose(zfile); + zip_unchange_all(zip); + zip_close(zip); + logger(LL_ERROR, "Unable to open output file: %s\n", outfile); return -1; } + if (print_progress) { + register_progress('IPSW', "Extracting"); + } uint64_t i, bytes = 0; int count, size = BUFSIZE; - double progress; for(i = zstat.size; i > 0; i -= count) { if (cancel_flag) { break; @@ -437,31 +467,43 @@ int ipsw_extract_to_file_with_progress(const char* ipsw, const char* infile, con size = i; count = zip_fread(zfile, buffer, size); if (count < 0) { - error("ERROR: zip_fread: %s\n", infile); + int zep = 0; + int sep = 0; + zip_file_error_get(zfile, &zep, &sep); + logger(LL_ERROR, "zip_fread: %s %d %d\n", infile, zep, sep); ret = -1; break; } if (fwrite(buffer, 1, count, fd) != count) { - error("ERROR: frite: %s\n", outfile); + logger(LL_ERROR, "Writing to '%s' failed: %s\n", outfile, strerror(errno)); ret = -1; break; } bytes += size; if (print_progress) { - progress = ((double)bytes / (double)zstat.size) * 100.0; - print_progress_bar(progress); + double progress = ((double)bytes / (double)zstat.size); + set_progress('IPSW', progress); } } free(buffer); + if (print_progress) { + finalize_progress('IPSW'); + } fclose(fd); zip_fclose(zfile); + zip_unchange_all(zip); + zip_close(zip); } 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)); + logger(LL_ERROR, "realpath failed on %s: %s\n", filepath, strerror(errno)); ret = -1; goto leave; } else { @@ -475,21 +517,21 @@ int ipsw_extract_to_file_with_progress(const char* ipsw, const char* infile, con } FILE *fi = fopen(actual_filepath, "rb"); if (!fi) { - error("ERROR: fopen: %s: %s\n", actual_filepath, strerror(errno)); + logger(LL_ERROR, "fopen: %s: %s\n", actual_filepath, strerror(errno)); ret = -1; goto leave; } struct stat fst; if (fstat(fileno(fi), &fst) != 0) { fclose(fi); - error("ERROR: fstat: %s: %s\n", actual_filepath, strerror(errno)); + logger(LL_ERROR, "fstat: %s: %s\n", actual_filepath, strerror(errno)); ret = -1; goto leave; } FILE *fo = fopen(actual_outfile, "wb"); if (!fo) { fclose(fi); - error("ERROR: fopen: %s: %s\n", actual_outfile, strerror(errno)); + logger(LL_ERROR, "fopen: %s: %s\n", actual_outfile, strerror(errno)); ret = -1; goto leave; } @@ -497,34 +539,39 @@ int ipsw_extract_to_file_with_progress(const char* ipsw, const char* infile, con if (buffer == NULL) { fclose(fi); fclose(fo); - error("ERROR: Unable to allocate memory\n"); + logger(LL_ERROR, "Unable to allocate memory\n"); ret = -1; goto leave;; } + if (print_progress) { + register_progress('IPSW', "Extracting"); + } uint64_t bytes = 0; - double progress; while (!feof(fi)) { if (cancel_flag) { break; } ssize_t r = fread(buffer, 1, BUFSIZE, fi); if (r < 0) { - error("ERROR: fread failed: %s\n", strerror(errno)); + logger(LL_ERROR, "fread failed: %s\n", strerror(errno)); ret = -1; break; } if (fwrite(buffer, 1, r, fo) != r) { - error("ERROR: fwrite failed\n"); + logger(LL_ERROR, "Writing to '%s' failed: %s\n", actual_outfile, strerror(errno)); ret = -1; break; } bytes += r; if (print_progress) { - progress = ((double)bytes / (double)fst.st_size) * 100.0; - print_progress_bar(progress); + double progress = ((double)bytes / (double)fst.st_size); + set_progress('IPSW', progress); } } + if (print_progress) { + finalize_progress('IPSW'); + } free(buffer); fclose(fi); @@ -534,146 +581,172 @@ 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 err = 0; + struct zip *zip = zip_open(ipsw->path, 0, &err); + if (zip == NULL) { + logger(LL_ERROR, "zip_open: %s: %d\n", ipsw->path, err); + return 0; + } + int zindex = zip_name_locate(zip, infile, 0); + zip_unchange_all(zip); + zip_close(zip); 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, void** pbuffer, size_t* psize) { size_t size = 0; unsigned char* buffer = NULL; - ipsw_archive* archive = ipsw_open(ipsw); - if (archive == NULL) { - error("ERROR: Invalid archive\n"); + if (ipsw == NULL) { + logger(LL_ERROR, "Invalid archive\n"); return -1; } - if (archive->zip) { - int zindex = zip_name_locate(archive->zip, infile, 0); + if (ipsw->zip) { + int err = 0; + struct zip *zip = zip_open(ipsw->path, 0, &err); + if (zip == NULL) { + logger(LL_ERROR, "zip_open: %s: %d\n", ipsw->path, err); + return -1; + } + + int zindex = zip_name_locate(zip, infile, 0); if (zindex < 0) { - debug("NOTE: zip_name_locate: '%s' not found in archive.\n", infile); - ipsw_close(archive); + zip_unchange_all(zip); + zip_close(zip); + logger(LL_DEBUG, "zip_name_locate: '%s' not found in archive.\n", infile); return -1; } struct zip_stat zstat; zip_stat_init(&zstat); - if (zip_stat_index(archive->zip, zindex, 0, &zstat) != 0) { - error("ERROR: zip_stat_index: %s\n", infile); - ipsw_close(archive); + if (zip_stat_index(zip, zindex, 0, &zstat) != 0) { + zip_unchange_all(zip); + zip_close(zip); + logger(LL_ERROR, "zip_stat_index: %s\n", infile); return -1; } - struct zip_file* zfile = zip_fopen_index(archive->zip, zindex, 0); + struct zip_file* zfile = zip_fopen_index(zip, zindex, 0); if (zfile == NULL) { - error("ERROR: zip_fopen_index: %s\n", infile); - ipsw_close(archive); + zip_unchange_all(zip); + zip_close(zip); + logger(LL_ERROR, "zip_fopen_index: %s\n", infile); return -1; } size = zstat.size; + if ((uint64_t)size != (uint64_t)zstat.size) { + logger(LL_ERROR, "Not enough memory to allocate a buffer of size %" PRIu64 "\n", (uint64_t)zstat.size); + zip_fclose(zfile); + zip_unchange_all(zip); + zip_close(zip); + return -1; + } buffer = (unsigned char*) malloc(size+1); if (buffer == NULL) { - error("ERROR: Out of memory\n"); + logger(LL_ERROR, "Out of memory\n"); zip_fclose(zfile); - ipsw_close(archive); + zip_unchange_all(zip); + zip_close(zip); return -1; } - if (zip_fread(zfile, buffer, size) != size) { - error("ERROR: zip_fread: %s\n", infile); - zip_fclose(zfile); + zip_int64_t zr = zip_fread(zfile, buffer, size); + zip_fclose(zfile); + zip_unchange_all(zip); + zip_close(zip); + if (zr < 0) { + int zep = 0; + int sep = 0; + zip_file_error_get(zfile, &zep, &sep); + logger(LL_ERROR, "zip_fread: %s %d %d\n", infile, zep, sep); + free(buffer); + return -1; + } else if (zr != size) { + logger(LL_ERROR, "zip_fread: %s got only %zu of %zu\n", infile, (size_t)zr, size); free(buffer); - ipsw_close(archive); return -1; } buffer[size] = '\0'; - - 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) { #else if (lstat(filepath, &fst) != 0) { #endif - error("ERROR: %s: stat failed for %s: %s\n", __func__, filepath, strerror(errno)); + logger(LL_ERROR, "%s: stat failed for %s: %s\n", __func__, filepath, strerror(errno)); free(filepath); - ipsw_close(archive); return -1; } size = fst.st_size; + if ((uint64_t)size != (uint64_t)fst.st_size) { + logger(LL_ERROR, "Not enough memory to allocate a buffer of size %" PRIu64 "\n", (uint64_t)fst.st_size); + free(filepath); + return -1; + } buffer = (unsigned char*)malloc(size+1); if (buffer == NULL) { - error("ERROR: Out of memory\n"); + logger(LL_ERROR, "Out of memory\n"); free(filepath); - ipsw_close(archive); return -1; } #ifndef WIN32 if (S_ISLNK(fst.st_mode)) { if (readlink(filepath, (char*)buffer, size) < 0) { - error("ERROR: %s: readlink failed for %s: %s\n", __func__, filepath, strerror(errno)); + logger(LL_ERROR, "%s: readlink failed for %s: %s\n", __func__, filepath, strerror(errno)); free(filepath); free(buffer); - ipsw_close(archive); return -1; } } else { #endif FILE *f = fopen(filepath, "rb"); if (!f) { - error("ERROR: %s: fopen failed for %s: %s\n", __func__, filepath, strerror(errno)); + logger(LL_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) { fclose(f); - error("ERROR: %s: fread failed for %s: %s\n", __func__, filepath, strerror(errno)); + logger(LL_ERROR, "%s: fread failed for %s: %s\n", __func__, filepath, strerror(errno)); free(filepath); free(buffer); - ipsw_close(archive); return -1; } fclose(f); @@ -684,24 +757,171 @@ 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) { + logger(LL_ERROR, "Invalid archive\n"); + return -1; + } + + if (ipsw->zip) { + int err = 0; + struct zip *zip = zip_open(ipsw->path, 0, &err); + if (zip == NULL) { + logger(LL_ERROR, "zip_open: %s: %d\n", ipsw->path, err); + return -1; + } + + int zindex = zip_name_locate(zip, infile, 0); + if (zindex < 0) { + zip_unchange_all(zip); + zip_close(zip); + logger(LL_DEBUG, "zip_name_locate: '%s' not found in archive.\n", infile); + return -1; + } + + struct zip_stat zstat; + zip_stat_init(&zstat); + if (zip_stat_index(zip, zindex, 0, &zstat) != 0) { + zip_unchange_all(zip); + zip_close(zip); + logger(LL_ERROR, "zip_stat_index: %s\n", infile); + return -1; + } + + struct zip_file* zfile = zip_fopen_index(zip, zindex, 0); + if (zfile == NULL) { + zip_unchange_all(zip); + zip_close(zip); + logger(LL_ERROR, "zip_fopen_index: %s\n", infile); + return -1; + } + + total_size = zstat.size; + buffer = (unsigned char*) malloc(blocksize); + if (buffer == NULL) { + zip_fclose(zfile); + zip_unchange_all(zip); + zip_close(zip); + logger(LL_ERROR, "Out of memory\n"); + 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) { + logger(LL_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) { + logger(LL_ERROR, "%s: send failed\n", __func__); + break; + } + done += zr; + } + free(buffer); + zip_fclose(zfile); + zip_unchange_all(zip); + zip_close(zip); + } 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 + logger(LL_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) { + logger(LL_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) { + logger(LL_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) { + logger(LL_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) { + logger(LL_ERROR, "%s: fread failed for %s: %s\n", __func__, filepath, strerror(errno)); + break; + } + if (send_callback(ctx, buffer, fr, done, total_size) < 0) { + logger(LL_ERROR, "%s: send failed\n", __func__); + break; + } + done += fr; + } + fclose(f); +#ifndef WIN32 + } +#endif + free(filepath); + free(buffer); + } + + if (done < total_size) { + logger(LL_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; + size_t size = 0; + void* data = NULL; *tss_enabled = 0; /* older devices don't require personalized firmwares and use a BuildManifesto.plist */ if (ipsw_file_exists(ipsw, "BuildManifesto.plist")) { if (ipsw_extract_to_memory(ipsw, "BuildManifesto.plist", &data, &size) == 0) { - plist_from_xml((char*)data, size, buildmanifest); + plist_from_memory((char*)data, size, buildmanifest, NULL); free(data); return 0; } @@ -713,7 +933,7 @@ int ipsw_extract_build_manifest(const char* ipsw, plist_t* buildmanifest, int *t /* whereas newer devices do not require personalized firmwares and use a BuildManifest.plist */ if (ipsw_extract_to_memory(ipsw, "BuildManifest.plist", &data, &size) == 0) { *tss_enabled = 1; - plist_from_xml((char*)data, size, buildmanifest); + plist_from_memory((char*)data, size, buildmanifest, NULL); free(data); return 0; } @@ -721,13 +941,13 @@ 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; + size_t size = 0; + void* data = NULL; if (ipsw_extract_to_memory(ipsw, "Restore.plist", &data, &size) == 0) { - plist_from_xml((char*)data, size, restore_plist); + plist_from_memory((char*)data, size, restore_plist, NULL); free(data); return 0; } @@ -735,15 +955,15 @@ 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); if (!dirp) { - error("ERROR: failed to open directory %s\n", base); + logger(LL_ERROR, "failed to open directory %s\n", base); free(base); return -1; } @@ -770,16 +990,16 @@ static int ipsw_list_contents_recurse(ipsw_archive *archive, const char *path, i ret = lstat(fpath, &st); #endif if (ret != 0) { - error("ERROR: %s: stat failed for %s: %s\n", __func__, fpath, strerror(errno)); + logger(LL_ERROR, "%s: stat failed for %s: %s\n", __func__, fpath, strerror(errno)); free(fpath); free(subpath); 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 +1010,26 @@ 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) { + logger(LL_ERROR, "Invalid IPSW archive\n"); return -1; } - if (archive->zip) { - int64_t entries = zip_get_num_entries(archive->zip, 0); + if (ipsw->zip) { + int err = 0; + struct zip *zip = zip_open(ipsw->path, 0, &err); + if (zip == NULL) { + logger(LL_ERROR, "zip_open: %s: %d\n", ipsw->path, err); + return -1; + } + + int64_t entries = zip_get_num_entries(zip, 0); if (entries < 0) { - error("ERROR: zip_get_num_entries failed\n"); - ipsw_close(archive); + logger(LL_ERROR, "zip_get_num_entries failed\n"); return -1; } @@ -812,21 +1037,21 @@ 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) { - error("ERROR: zip_stat_index failed for %s\n", stat.name); + if (zip_stat_index(zip, index, 0, &stat) < 0) { + logger(LL_ERROR, "zip_stat_index failed for %s\n", stat.name); ret = -1; continue; } uint8_t opsys; uint32_t attributes; - if (zip_file_get_external_attributes(archive->zip, index, 0, &opsys, &attributes) < 0) { - error("ERROR: zip_file_get_external_attributes failed for %s\n", stat.name); + if (zip_file_get_external_attributes(zip, index, 0, &opsys, &attributes) < 0) { + logger(LL_ERROR, "zip_file_get_external_attributes failed for %s\n", stat.name); ret = -1; continue; } if (opsys != ZIP_OPSYS_UNIX) { - error("ERROR: File %s does not have UNIX attributes\n", stat.name); + logger(LL_ERROR, "File %s does not have UNIX attributes\n", stat.name); ret = -1; continue; } @@ -850,30 +1075,17 @@ 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]; char *jdata = NULL; - uint32_t jsize = 0; + size_t jsize = 0; plist_t dict = NULL; plist_t node = NULL; plist_t fws = NULL; @@ -888,33 +1100,33 @@ int ipsw_get_signed_firmwares(const char* product, plist_t* firmwares) *firmwares = NULL; snprintf(url, sizeof(url), "https://api.ipsw.me/v4/device/%s", product); - if (download_to_buffer(url, &jdata, &jsize) < 0) { - error("ERROR: Download from %s failed.\n", url); + if (download_to_buffer(url, (void**)&jdata, &jsize) < 0) { + logger(LL_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"); + logger(LL_ERROR, "Failed to parse json data.\n"); plist_free(dict); return -1; } node = plist_dict_get_item(dict, "identifier"); if (!node || plist_get_node_type(node) != PLIST_STRING) { - error("ERROR: Unexpected json data returned - missing 'identifier'\n"); + logger(LL_ERROR, "Unexpected json data returned - missing 'identifier'\n"); plist_free(dict); return -1; } product_type = plist_get_string_ptr(node, NULL); if (!product_type || strcmp(product_type, product) != 0) { - error("ERROR: Unexpected json data returned - failed to read identifier\n"); + logger(LL_ERROR, "Unexpected json data returned - failed to read identifier\n"); plist_free(dict); return -1; } fws = plist_dict_get_item(dict, "firmwares"); if (!fws || plist_get_node_type(fws) != PLIST_ARRAY) { - error("ERROR: Unexpected json data returned - missing 'firmwares'\n"); + logger(LL_ERROR, "Unexpected json data returned - missing 'firmwares'\n"); plist_free(dict); return -1; } @@ -946,14 +1158,14 @@ int ipsw_get_latest_fw(plist_t version_data, const char* product, char** fwurl, plist_t n1 = plist_dict_get_item(version_data, "MobileDeviceSoftwareVersionsByVersion"); if (!n1) { - error("%s: ERROR: Can't find MobileDeviceSoftwareVersionsByVersion dict in version data\n", __func__); + logger(LL_ERROR, "%s: ERROR: Can't find MobileDeviceSoftwareVersionsByVersion dict in version data\n", __func__); return -1; } plist_dict_iter iter = NULL; plist_dict_new_iter(n1, &iter); if (!iter) { - error("%s: ERROR: Can't get dict iter\n", __func__); + logger(LL_ERROR, "%s: ERROR: Can't get dict iter\n", __func__); return -1; } char* key = NULL; @@ -974,21 +1186,21 @@ int ipsw_get_latest_fw(plist_t version_data, const char* product, char** fwurl, free(iter); if (major == 0) { - error("%s: ERROR: Can't find major version?!\n", __func__); + logger(LL_ERROR, "%s: ERROR: Can't find major version?!\n", __func__); return -1; } char majstr[32]; // should be enough for a uint64_t value - sprintf(majstr, "%"PRIu64, (uint64_t)major); + snprintf(majstr, sizeof(majstr), "%"PRIu64, (uint64_t)major); n1 = plist_access_path(version_data, 7, "MobileDeviceSoftwareVersionsByVersion", majstr, "MobileDeviceSoftwareVersions", product, "Unknown", "Universal", "Restore"); if (!n1) { - error("%s: ERROR: Can't get Unknown/Universal/Restore node?!\n", __func__); + logger(LL_ERROR, "%s: ERROR: Can't get Unknown/Universal/Restore node?!\n", __func__); return -1; } plist_t n2 = plist_dict_get_item(n1, "BuildVersion"); if (!n2 || (plist_get_node_type(n2) != PLIST_STRING)) { - error("%s: ERROR: Can't get build version node?!\n", __func__); + logger(LL_ERROR, "%s: ERROR: Can't get build version node?!\n", __func__); return -1; } @@ -997,7 +1209,7 @@ int ipsw_get_latest_fw(plist_t version_data, const char* product, char** fwurl, n1 = plist_access_path(version_data, 5, "MobileDeviceSoftwareVersionsByVersion", majstr, "MobileDeviceSoftwareVersions", product, strval); if (!n1) { - error("%s: ERROR: Can't get MobileDeviceSoftwareVersions/%s node?!\n", __func__, strval); + logger(LL_ERROR, "%s: ERROR: Can't get MobileDeviceSoftwareVersions/%s node?!\n", __func__, strval); free(strval); return -1; } @@ -1013,7 +1225,7 @@ int ipsw_get_latest_fw(plist_t version_data, const char* product, char** fwurl, free(strval); strval = NULL; if (!n1 || (plist_dict_get_size(n1) == 0)) { - error("%s: ERROR: Can't get MobileDeviceSoftwareVersions/%s dict\n", __func__, product); + logger(LL_ERROR, "%s: ERROR: Can't get MobileDeviceSoftwareVersions/%s dict\n", __func__, product); return -1; } } @@ -1031,7 +1243,7 @@ int ipsw_get_latest_fw(plist_t version_data, const char* product, char** fwurl, n2 = plist_access_path(n1, 2, "Restore", "FirmwareURL"); if (!n2 || (plist_get_node_type(n2) != PLIST_STRING)) { - error("%s: ERROR: Can't get FirmwareURL node\n", __func__); + logger(LL_ERROR, "%s: ERROR: Can't get FirmwareURL node\n", __func__); return -1; } @@ -1064,15 +1276,25 @@ static int sha1_verify_fp(FILE* f, unsigned char* expected_sha1) { unsigned char tsha1[20]; char buf[8192]; + size_t total = 0; + struct stat fst; + int lastprog = 0; if (!f) return 0; - SHA_CTX sha1ctx; - SHA1_Init(&sha1ctx); + sha1_context sha1ctx; + sha1_init(&sha1ctx); rewind(f); + fstat(fileno(f), &fst); while (!feof(f)) { size_t sz = fread(buf, 1, 8192, f); - SHA1_Update(&sha1ctx, (const void*)buf, sz); + sha1_update(&sha1ctx, buf, sz); + total += sz; + double p = (double)total / (double)fst.st_size; + if ((int)(p*100) > lastprog) { + set_progress('SHA1', p); + lastprog = (int)(p*100); + } } - SHA1_Final(tsha1, &sha1ctx); + sha1_final(&sha1ctx, tsha1); return (memcmp(expected_sha1, tsha1, 20) == 0) ? 1 : 0; } @@ -1080,25 +1302,25 @@ int ipsw_download_fw(const char *fwurl, unsigned char* isha1, const char* todir, { char* fwfn = strrchr(fwurl, '/'); if (!fwfn) { - error("ERROR: can't get local filename for firmware ipsw\n"); + logger(LL_ERROR, "can't get local filename for firmware ipsw\n"); return -2; } fwfn++; char fwlfn[PATH_MAX - 5]; if (todir) { - sprintf(fwlfn, "%s/%s", todir, fwfn); + snprintf(fwlfn, sizeof(fwlfn), "%s/%s", todir, fwfn); } else { - sprintf(fwlfn, "%s", fwfn); + snprintf(fwlfn, sizeof(fwlfn), "%s", fwfn); } char fwlock[PATH_MAX]; - sprintf(fwlock, "%s.lock", fwlfn); + snprintf(fwlock, sizeof(fwlock), "%s.lock", fwlfn); lock_info_t lockinfo; if (lock_file(fwlock, &lockinfo) != 0) { - error("WARNING: Could not lock file '%s'\n", fwlock); + logger(LL_WARNING, "Could not lock file '%s'\n", fwlock); } int need_dl = 0; @@ -1106,13 +1328,15 @@ int ipsw_download_fw(const char *fwurl, unsigned char* isha1, const char* todir, FILE* f = fopen(fwlfn, "rb"); if (f) { if (memcmp(zsha1, isha1, 20) != 0) { - info("Verifying '%s'...\n", fwlfn); + logger(LL_INFO, "Verifying '%s'...\n", fwlfn); + register_progress('SHA1', "Verifying"); if (sha1_verify_fp(f, isha1)) { - info("Checksum matches.\n"); + logger(LL_INFO, "Checksum matches.\n"); } else { - info("Checksum does not match.\n"); + logger(LL_INFO, "Checksum does not match.\n"); need_dl = 1; } + finalize_progress('SHA1'); } fclose(f); } else { @@ -1122,29 +1346,35 @@ int ipsw_download_fw(const char *fwurl, unsigned char* isha1, const char* todir, int res = 0; if (need_dl) { if (strncmp(fwurl, "protected:", 10) == 0) { - error("ERROR: Can't download '%s' because it needs a purchase.\n", fwfn); + logger(LL_ERROR, "Can't download '%s' because it needs a purchase.\n", fwfn); res = -3; } else { remove(fwlfn); - info("Downloading firmware (%s)\n", fwurl); + logger(LL_INFO, "Downloading firmware (%s)\n", fwurl); download_to_file(fwurl, fwlfn, 1); + if (global_quit_flag > 0) { + logger(LL_NOTICE, "Download aborted by user\n"); + return -1; + } if (memcmp(isha1, zsha1, 20) != 0) { - info("\nVerifying '%s'...\n", fwlfn); + logger(LL_INFO, "Verifying '%s'...\n", fwlfn); FILE* f = fopen(fwlfn, "rb"); if (f) { + register_progress('SHA1', "Verifying"); if (sha1_verify_fp(f, isha1)) { - info("Checksum matches.\n"); + logger(LL_INFO, "Checksum matches.\n"); } else { - error("ERROR: File download failed (checksum mismatch).\n"); + logger(LL_ERROR, "File download failed (checksum mismatch).\n"); res = -4; } + finalize_progress('SHA1'); fclose(f); // make sure to remove invalid files if (res < 0) remove(fwlfn); } else { - error("ERROR: Can't open '%s' for checksum verification\n", fwlfn); + logger(LL_ERROR, "Can't open '%s' for checksum verification\n", fwlfn); res = -5; } } @@ -1155,7 +1385,7 @@ int ipsw_download_fw(const char *fwurl, unsigned char* isha1, const char* todir, } if (unlock_file(&lockinfo) != 0) { - error("WARNING: Could not unlock file '%s'\n", fwlock); + logger(LL_WARNING, "Could not unlock file '%s'\n", fwlock); } return res; @@ -1169,17 +1399,17 @@ int ipsw_download_latest_fw(plist_t version_data, const char* product, const cha *ipswfile = NULL; if ((ipsw_get_latest_fw(version_data, product, &fwurl, isha1) < 0) || !fwurl) { - error("ERROR: can't get URL for latest firmware\n"); + logger(LL_ERROR, "can't get URL for latest firmware\n"); return -1; } char* fwfn = strrchr(fwurl, '/'); if (!fwfn) { - error("ERROR: can't get local filename for firmware ipsw\n"); + logger(LL_ERROR, "can't get local filename for firmware ipsw\n"); return -2; } fwfn++; - info("Latest firmware is %s\n", fwfn); + logger(LL_INFO, "Latest firmware is %s\n", fwfn); int res = ipsw_download_fw(fwurl, isha1, todir, ipswfile); @@ -1192,3 +1422,121 @@ 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) { + int err = 0; + struct zip *zip = zip_open(ipsw->path, 0, &err); + if (zip == NULL) { + logger(LL_ERROR, "zip_open: %s: %d\n", ipsw->path, err); + return NULL; + } + + zip_stat_t zst; + zip_int64_t zindex = zip_name_locate(zip, path, 0); + if (zindex < 0) { + logger(LL_ERROR, "zip_name_locate: %s not found\n", path); + zip_unchange_all(zip); + zip_close(zip); + free(handle); + return NULL; + } + handle->zfile = zip_fopen_index(zip, zindex, 0); + if (handle->zfile == NULL) { + logger(LL_ERROR, "zip_fopen_index: %s could not be opened\n", path); + zip_unchange_all(zip); + zip_close(zip); + free(handle); + return NULL; + } + zip_stat_init(&zst); + zip_stat(zip, path, 0, &zst); + handle->size = zst.size; + handle->seekable = (zst.comp_method == ZIP_CM_STORE); + handle->zip = zip; + } else { + struct stat st; + char *filepath = build_path(ipsw->path, path); + handle->file = fopen(filepath, "rb"); + free(filepath); + if (!handle->file) { + logger(LL_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); + zip_unchange_all(handle->zip); + zip_close(handle->zip); + } 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 { + logger(LL_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 { + logger(LL_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 { + logger(LL_ERROR, "%s: Invalid file handle\n", __func__); + return -1; + } +} |
