diff options
author | 2023-11-02 12:54:47 +0100 | |
---|---|---|
committer | 2023-11-02 12:54:47 +0100 | |
commit | c871c591e36d2a4083e3dda4c70144a0321ce70f (patch) | |
tree | ce6a990c037c78a84a5d2f99038a7e98e1425bf6 | |
parent | 4072cd965eab44993700b980d8848b46ec3be72e (diff) | |
download | idevicerestore-c871c591e36d2a4083e3dda4c70144a0321ce70f.tar.gz idevicerestore-c871c591e36d2a4083e3dda4c70144a0321ce70f.tar.bz2 |
Extract OS component when using older ipsw archives
Older ipsw archives have the root filesystem stored in compressed
format rather than just "stored". The "Verifying Filesystem" step
would then fail as compressed files are not seekable in ZIP files.
This commit introduces a detection for this and has the filesystem
extracted should it be required.
If not using a cache path, the temp file used for extraction will
be deleted after the procedure is completed.
-rw-r--r-- | src/asr.c | 18 | ||||
-rw-r--r-- | src/common.c | 17 | ||||
-rw-r--r-- | src/common.h | 4 | ||||
-rw-r--r-- | src/idevicerestore.c | 75 | ||||
-rw-r--r-- | src/ipsw.c | 20 | ||||
-rw-r--r-- | src/ipsw.h | 3 | ||||
-rw-r--r-- | src/restore.c | 17 |
7 files changed, 143 insertions, 11 deletions
@@ -216,9 +216,7 @@ int asr_perform_validation(asr_client_t asr, ipsw_file_handle_t file) plist_t payload_info = NULL; int attempts = 0; - ipsw_file_seek(file, 0, SEEK_END); - length = ipsw_file_tell(file); - ipsw_file_seek(file, 0, SEEK_SET); + length = ipsw_file_size(file); payload_info = plist_new_dict(); plist_dict_set_item(payload_info, "Port", plist_new_uint(1)); @@ -313,9 +311,14 @@ int asr_handle_oob_data_request(asr_client_t asr, plist_t packet, ipsw_file_hand return -1; } - ipsw_file_seek(file, oob_offset, SEEK_SET); - if (ipsw_file_read(file, oob_data, oob_length) != oob_length) { - error("ERROR: Unable to read OOB data from filesystem offset: %s\n", strerror(errno)); + if (ipsw_file_seek(file, oob_offset, SEEK_SET) < 0) { + error("ERROR: Unable to seek to OOB offset 0x%" PRIx64 "\n", oob_offset); + free(oob_data); + return -1; + } + int64_t ir = ipsw_file_read(file, oob_data, oob_length); + if (ir != oob_length) { + error("ERROR: Unable to read OOB data from filesystem offset 0x%" PRIx64 ", oob_length %" PRIu64 ", read returned %" PRIi64"\n", oob_offset, oob_length, ir); free(oob_data); return -1; } @@ -335,8 +338,7 @@ int asr_send_payload(asr_client_t asr, ipsw_file_handle_t file) uint64_t i, length, bytes = 0; double progress = 0; - ipsw_file_seek(file, 0, SEEK_END); - length = ipsw_file_tell(file); + length = ipsw_file_size(file); ipsw_file_seek(file, 0, SEEK_SET); data = (char*)malloc(ASR_PAYLOAD_CHUNK_SIZE + 20); diff --git a/src/common.c b/src/common.c index 068f2dd..0ad775c 100644 --- a/src/common.c +++ b/src/common.c @@ -695,3 +695,20 @@ int _plist_dict_copy_item(plist_t target_dict, plist_t source_dict, const char * plist_dict_set_item(target_dict, key, plist_copy(node)); return 0; } + +char* path_get_basename(char* path) +{ +#ifdef WIN32 + char *p = path + strlen(path); + while (p > path) { + if ((*p == '/') || (*p == '\\')) { + return p+1; + } + p--; + } + return p; +#else + char *p = strrchr(path, '/'); + return p ? p + 1 : path; +#endif +} diff --git a/src/common.h b/src/common.h index 6708943..c2a96b0 100644 --- a/src/common.h +++ b/src/common.h @@ -128,6 +128,8 @@ struct idevicerestore_client_t { int ignore_device_add_events; plist_t macos_variant; char* restore_variant; + char* filesystem; + int delete_fs; }; extern struct idevicerestore_mode_t idevicerestore_modes[]; @@ -193,6 +195,8 @@ int _plist_dict_copy_data(plist_t target_dict, plist_t source_dict, const char * int _plist_dict_copy_string(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key); int _plist_dict_copy_item(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key); +char* path_get_basename(char* path); + #ifdef __cplusplus } #endif diff --git a/src/idevicerestore.c b/src/idevicerestore.c index 56207a0..dc7750b 100644 --- a/src/idevicerestore.c +++ b/src/idevicerestore.c @@ -45,6 +45,8 @@ #define SHA384 sha384 #endif +#include <libimobiledevice-glue/utils.h> + #include "dfu.h" #include "tss.h" #include "img3.h" @@ -950,6 +952,73 @@ int idevicerestore_start(struct idevicerestore_client_t* client) } info("All required components found in IPSW\n"); + /* Get OS (filesystem) name from build identity */ + char* os_path = NULL; + if (build_identity_get_component_path(build_identity, "OS", &os_path) < 0) { + error("ERROR: Unable to get path for filesystem component\n"); + return -1; + } + + /* check if IPSW has OS component 'stored' in ZIP archive, otherwise we need to extract it */ + int needs_os_extraction = 0; + if (client->ipsw->zip) { + ipsw_file_handle_t zfile = ipsw_file_open(client->ipsw, os_path); + if (zfile) { + if (!zfile->seekable) { + needs_os_extraction = 1; + } + ipsw_file_close(zfile); + } + } + + if (needs_os_extraction && !(client->flags & FLAG_SHSHONLY)) { + char* tmpf = NULL; + struct stat st; + if (client->cache_dir) { + memset(&st, '\0', sizeof(struct stat)); + if (stat(client->cache_dir, &st) < 0) { + mkdir_with_parents(client->cache_dir, 0755); + } + char* ipsw_basename = path_get_basename(client->ipsw->path); + ipsw_basename = strdup(ipsw_basename); + char* p = strrchr(ipsw_basename, '.'); + if (p && isalpha(*(p+1))) { + *p = '\0'; + } + tmpf = string_build_path(client->cache_dir, ipsw_basename, NULL); + mkdir_with_parents(tmpf, 0755); + free(tmpf); + tmpf = string_build_path(client->cache_dir, ipsw_basename, os_path, NULL); + free(ipsw_basename); + } else { + tmpf = get_temp_filename(NULL); + client->delete_fs = 1; + } + + /* check if we already have it extracted */ + uint64_t fssize = 0; + ipsw_get_file_size(client->ipsw, os_path, &fssize); + memset(&st, '\0', sizeof(struct stat)); + if (stat(tmpf, &st) == 0) { + if ((fssize > 0) && ((uint64_t)st.st_size == fssize)) { + info("Using cached filesystem from '%s'\n", tmpf); + client->filesystem = tmpf; + } + } + + if (!client->filesystem) { + info("Extracting filesystem from IPSW: %s\n", os_path); + if (ipsw_extract_to_file_with_progress(client->ipsw, os_path, tmpf, 1) < 0) { + error("ERROR: Unable to extract filesystem from IPSW\n"); + info("Removing %s\n", tmpf); + unlink(tmpf); + free(tmpf); + return -1; + } + client->filesystem = tmpf; + } + } + idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.2); /* retrieve shsh blobs if required */ @@ -1338,6 +1407,12 @@ void idevicerestore_client_free(struct idevicerestore_client_t* client) if (client->ipsw) { ipsw_close(client->ipsw); } + if (client->filesystem) { + if (client->delete_fs) { + unlink(client->filesystem); + } + free(client->filesystem); + } if (client->version) { free(client->version); } @@ -1308,7 +1308,8 @@ 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 zindex = zip_name_locate(ipsw->zip, path, 0); + 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); @@ -1320,8 +1321,12 @@ ipsw_file_handle_t ipsw_file_open(ipsw_archive_t ipsw, const char* 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); @@ -1330,6 +1335,9 @@ ipsw_file_handle_t ipsw_file_open(ipsw_archive_t ipsw, const char* path) free(handle); return NULL; } + fstat(fileno(handle->file), &st); + handle->size = st.st_size; + handle->seekable = 1; } return handle; } @@ -1344,6 +1352,14 @@ void ipsw_file_close(ipsw_file_handle_t handle) 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) { @@ -49,12 +49,15 @@ typedef int (*ipsw_send_cb)(void *ctx, void *data, size_t size); struct ipsw_file_handle { FILE* file; struct zip_file* zfile; + uint64_t size; + int seekable; }; typedef struct ipsw_file_handle* ipsw_file_handle_t; ipsw_file_handle_t ipsw_file_open(ipsw_archive_t, const char* path); void ipsw_file_close(ipsw_file_handle_t handle); +uint64_t ipsw_file_size(ipsw_file_handle_t handle); int64_t ipsw_file_read(ipsw_file_handle_t handle, void* buffer, size_t size); int ipsw_file_seek(ipsw_file_handle_t handle, int64_t offset, int whence); int64_t ipsw_file_tell(ipsw_file_handle_t handle); diff --git a/src/restore.c b/src/restore.c index 8063f08..9e0268a 100644 --- a/src/restore.c +++ b/src/restore.c @@ -29,6 +29,7 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <libgen.h> #include <libimobiledevice/restore.h> #ifdef HAVE_REVERSE_PROXY #include <libimobiledevice/reverse_proxy.h> @@ -903,13 +904,23 @@ int restore_send_filesystem(struct idevicerestore_client_t* client, idevice_t de info("About to send filesystem...\n"); + ipsw_archive_t ipsw_dummy = NULL; ipsw_file_handle_t file = NULL; char* fsname = NULL; if (build_identity_get_component_path(build_identity, "OS", &fsname) < 0) { error("ERROR: Unable to get path for filesystem component\n"); return -1; } - file = ipsw_file_open(client->ipsw, fsname); + if (client->filesystem) { + char* path = strdup(client->filesystem); + char* fsname_base = path_get_basename(path); + char* parent_dir = dirname(path); + ipsw_dummy = ipsw_open(parent_dir); + free(path); + file = ipsw_file_open(ipsw_dummy, fsname_base); + } else { + file = ipsw_file_open(client->ipsw, fsname); + } if (!file) { error("ERROR: Unable to open '%s' in ipsw\n", fsname); free(fsname); @@ -917,6 +928,7 @@ int restore_send_filesystem(struct idevicerestore_client_t* client, idevice_t de if (asr_open_with_timeout(device, &asr) < 0) { ipsw_file_close(file); + ipsw_close(ipsw_dummy); error("ERROR: Unable to connect to ASR\n"); return -1; } @@ -929,6 +941,7 @@ int restore_send_filesystem(struct idevicerestore_client_t* client, idevice_t de info("Validating the filesystem\n"); if (asr_perform_validation(asr, file) < 0) { ipsw_file_close(file); + ipsw_close(ipsw_dummy); error("ERROR: ASR was unable to validate the filesystem\n"); asr_free(asr); return -1; @@ -940,11 +953,13 @@ int restore_send_filesystem(struct idevicerestore_client_t* client, idevice_t de info("Sending filesystem now...\n"); if (asr_send_payload(asr, file) < 0) { ipsw_file_close(file); + ipsw_close(ipsw_dummy); error("ERROR: Unable to send payload to ASR\n"); asr_free(asr); return -1; } ipsw_file_close(file); + ipsw_close(ipsw_dummy); info("Done sending filesystem\n"); |