summaryrefslogtreecommitdiffstats
path: root/src/ipsw.c
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2023-09-14 02:34:06 +0200
committerGravatar Nikias Bassen2023-09-14 02:34:06 +0200
commitdbe7313260ddef6b2eb60b3772d1595bfd91f24c (patch)
tree2405ac44d7e4386a7b245d9230f17300d83195a3 /src/ipsw.c
parent17969ef91403f1a914d43584caf9fa0da89d649e (diff)
downloadidevicerestore-dbe7313260ddef6b2eb60b3772d1595bfd91f24c.tar.gz
idevicerestore-dbe7313260ddef6b2eb60b3772d1595bfd91f24c.tar.bz2
Refactor ipsw code to transparently stream images directly from ZIP or extracted ipsw
This allows flashing directly from IPSW archive without having to extract it first, and ultimately removes the "Extracting filesystem from IPSW" part. Restoring from extracted IPSW is also supported, just pass the path to the directory that has all the files from a given IPSW.
Diffstat (limited to 'src/ipsw.c')
-rw-r--r--src/ipsw.c269
1 files changed, 154 insertions, 115 deletions
diff --git a/src/ipsw.c b/src/ipsw.c
index fafe583..58d817c 100644
--- a/src/ipsw.c
+++ b/src/ipsw.c
@@ -55,16 +55,8 @@
#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;
@@ -310,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;
@@ -324,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 {
@@ -335,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)
@@ -348,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);
@@ -386,30 +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 = NULL;
if (!ipsw || !infile || !outfile) {
error("ERROR: Invalid argument\n");
return -1;
}
- archive = ipsw_open(ipsw);
- if (archive == NULL) {
- error("ERROR: Invalid archive\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;
@@ -417,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;
}
@@ -428,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;
@@ -472,7 +467,7 @@ 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) {
@@ -553,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;
}
@@ -631,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;
}
@@ -639,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;
}
@@ -647,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) {
@@ -656,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;
@@ -664,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;
}
@@ -674,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 {
@@ -684,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) {
@@ -692,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);
@@ -703,45 +681,40 @@ 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_send(const char* ipsw, const char* infile, int blocksize, ipsw_send_cb send_callback, void* ctx)
+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;
- 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;
}
@@ -750,7 +723,6 @@ int ipsw_extract_send(const char* ipsw, const char* infile, int blocksize, ipsw_
if (buffer == NULL) {
error("ERROR: Out of memory\n");
zip_fclose(zfile);
- ipsw_close(archive);
return -1;
}
@@ -771,10 +743,9 @@ int ipsw_extract_send(const char* ipsw, const char* infile, int blocksize, ipsw_
}
done += zr;
}
- zip_fclose(zfile);
free(buffer);
} 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) {
@@ -783,7 +754,6 @@ int ipsw_extract_send(const char* ipsw, const char* infile, int blocksize, ipsw_
#endif
error("ERROR: %s: stat failed for %s: %s\n", __func__, filepath, strerror(errno));
free(filepath);
- ipsw_close(archive);
return -1;
}
total_size = fst.st_size;
@@ -791,7 +761,6 @@ int ipsw_extract_send(const char* ipsw, const char* infile, int blocksize, ipsw_
if (buffer == NULL) {
error("ERROR: Out of memory\n");
free(filepath);
- ipsw_close(archive);
return -1;
}
@@ -802,7 +771,6 @@ int ipsw_extract_send(const char* ipsw, const char* infile, int blocksize, ipsw_
error("ERROR: %s: readlink failed for %s: %s\n", __func__, filepath, strerror(errno));
free(filepath);
free(buffer);
- ipsw_close(archive);
return -1;
}
send_callback(ctx, buffer, (size_t)rl);
@@ -813,7 +781,6 @@ int ipsw_extract_send(const char* ipsw, const char* infile, int blocksize, ipsw_
error("ERROR: %s: fopen failed for %s: %s\n", __func__, filepath, strerror(errno));
free(filepath);
free(buffer);
- ipsw_close(archive);
return -2;
}
@@ -838,7 +805,6 @@ int ipsw_extract_send(const char* ipsw, const char* infile, int blocksize, ipsw_
free(filepath);
free(buffer);
}
- ipsw_close(archive);
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);
@@ -851,7 +817,7 @@ int ipsw_extract_send(const char* ipsw, const char* infile, int blocksize, ipsw_
return 0;
}
-int ipsw_extract_build_manifest(const char* ipsw, plist_t* buildmanifest, int *tss_enabled)
+int ipsw_extract_build_manifest(ipsw_archive_t ipsw, plist_t* buildmanifest, int *tss_enabled)
{
unsigned int size = 0;
unsigned char* data = NULL;
@@ -881,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;
@@ -895,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);
@@ -936,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);
@@ -950,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;
}
@@ -972,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;
@@ -980,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;
@@ -1010,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];
@@ -1352,3 +1303,91 @@ 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 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;
+ }
+
+ } else {
+ 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;
+ }
+ }
+ 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);
+}
+
+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;
+ }
+}