Skip to content

Commit

Permalink
Extract OS component when using older ipsw archives
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nikias committed Nov 2, 2023
1 parent 4072cd9 commit c871c59
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 11 deletions.
18 changes: 10 additions & 8 deletions src/asr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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;
}
Expand All @@ -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);
Expand Down
17 changes: 17 additions & 0 deletions src/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
4 changes: 4 additions & 0 deletions src/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -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[];
Expand Down Expand Up @@ -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
Expand Down
75 changes: 75 additions & 0 deletions src/idevicerestore.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
#define SHA384 sha384
#endif

#include <libimobiledevice-glue/utils.h>

#include "dfu.h"
#include "tss.h"
#include "img3.h"
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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);
}
Expand Down
20 changes: 18 additions & 2 deletions src/ipsw.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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;
}
Expand All @@ -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) {
Expand Down
3 changes: 3 additions & 0 deletions src/ipsw.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
17 changes: 16 additions & 1 deletion src/restore.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -903,20 +904,31 @@ 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);
}

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;
}
Expand All @@ -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;
Expand All @@ -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");

Expand Down

0 comments on commit c871c59

Please sign in to comment.