| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453 |
- /*
- * ideviceimagemounter.c
- * Mount developer/debug disk images on the device
- *
- * Copyright (C) 2010 Nikias Bassen <nikias@gmx.li>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
- #include <stdlib.h>
- #define _GNU_SOURCE 1
- #define __USE_GNU 1
- #include <stdio.h>
- #include <string.h>
- #include <getopt.h>
- #include <errno.h>
- #include <libgen.h>
- #include <time.h>
- #include <sys/time.h>
- #include <inttypes.h>
- #include <libimobiledevice/libimobiledevice.h>
- #include <libimobiledevice/lockdown.h>
- #include <libimobiledevice/afc.h>
- #include <libimobiledevice/notification_proxy.h>
- #include <libimobiledevice/mobile_image_mounter.h>
- #include <asprintf.h>
- #include "common/utils.h"
- static int list_mode = 0;
- static int xml_mode = 0;
- static char *udid = NULL;
- static char *imagetype = NULL;
- static const char PKG_PATH[] = "PublicStaging";
- static const char PATH_PREFIX[] = "/private/var/mobile/Media";
- typedef enum {
- DISK_IMAGE_UPLOAD_TYPE_AFC,
- DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE
- } disk_image_upload_type_t;
- static void print_usage(int argc, char **argv)
- {
- char *name = NULL;
- name = strrchr(argv[0], '/');
- printf("Usage: %s [OPTIONS] IMAGE_FILE IMAGE_SIGNATURE_FILE\n\n", (name ? name + 1: argv[0]));
- printf("Mounts the specified disk image on the device.\n\n");
- printf(" -u, --udid UDID\ttarget specific device by its 40-digit device UDID\n");
- printf(" -l, --list\t\tList mount information\n");
- printf(" -t, --imagetype\tImage type to use, default is 'Developer'\n");
- printf(" -x, --xml\t\tUse XML output\n");
- printf(" -d, --debug\t\tenable communication debugging\n");
- printf(" -h, --help\t\tprints usage information\n");
- printf("\n");
- printf("Homepage: <http://libimobiledevice.org>\n");
- }
- static void parse_opts(int argc, char **argv)
- {
- static struct option longopts[] = {
- {"help", 0, NULL, 'h'},
- {"udid", 0, NULL, 'u'},
- {"list", 0, NULL, 'l'},
- {"imagetype", 0, NULL, 't'},
- {"xml", 0, NULL, 'x'},
- {"debug", 0, NULL, 'd'},
- {NULL, 0, NULL, 0}
- };
- int c;
- while (1) {
- c = getopt_long(argc, argv, "hu:lt:xd", longopts,
- (int *) 0);
- if (c == -1) {
- break;
- }
- switch (c) {
- case 'h':
- print_usage(argc, argv);
- exit(0);
- case 'u':
- if (strlen(optarg) != 40) {
- printf("%s: invalid UDID specified (length != 40)\n",
- argv[0]);
- print_usage(argc, argv);
- exit(2);
- }
- udid = strdup(optarg);
- break;
- case 'l':
- list_mode = 1;
- break;
- case 't':
- imagetype = strdup(optarg);
- break;
- case 'x':
- xml_mode = 1;
- break;
- case 'd':
- idevice_set_debug_level(1);
- break;
- default:
- print_usage(argc, argv);
- exit(2);
- }
- }
- }
- static void print_xml(plist_t node)
- {
- char *xml = NULL;
- uint32_t len = 0;
- plist_to_xml(node, &xml, &len);
- if (xml)
- puts(xml);
- }
- static ssize_t mim_upload_cb(void* buf, size_t size, void* userdata)
- {
- return fread(buf, 1, size, (FILE*)userdata);
- }
- int main(int argc, char **argv)
- {
- idevice_t device = NULL;
- lockdownd_client_t lckd = NULL;
- lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
- mobile_image_mounter_client_t mim = NULL;
- afc_client_t afc = NULL;
- lockdownd_service_descriptor_t service = NULL;
- int res = -1;
- char *image_path = NULL;
- size_t image_size = 0;
- char *image_sig_path = NULL;
- parse_opts(argc, argv);
- argc -= optind;
- argv += optind;
- if (!list_mode) {
- if (argc < 1) {
- printf("ERROR: No IMAGE_FILE has been given!\n");
- return -1;
- }
- image_path = strdup(argv[0]);
- if (argc >= 2) {
- image_sig_path = strdup(argv[1]);
- } else {
- if (asprintf(&image_sig_path, "%s.signature", image_path) < 0) {
- printf("Out of memory?!\n");
- return -1;
- }
- }
- }
- if (IDEVICE_E_SUCCESS != idevice_new(&device, udid)) {
- printf("No device found, is it plugged in?\n");
- return -1;
- }
- if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lckd, "ideviceimagemounter"))) {
- printf("ERROR: Could not connect to lockdown, error code %d.\n", ldret);
- goto leave;
- }
- plist_t pver = NULL;
- char *product_version = NULL;
- lockdownd_get_value(lckd, NULL, "ProductVersion", &pver);
- if (pver && plist_get_node_type(pver) == PLIST_STRING) {
- plist_get_string_val(pver, &product_version);
- }
- disk_image_upload_type_t disk_image_upload_type = DISK_IMAGE_UPLOAD_TYPE_AFC;
- int product_version_major = 0;
- int product_version_minor = 0;
- if (product_version) {
- if (sscanf(product_version, "%d.%d.%*d", &product_version_major, &product_version_minor) == 2) {
- if (product_version_major >= 7)
- disk_image_upload_type = DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE;
- }
- }
- lockdownd_start_service(lckd, "com.apple.mobile.mobile_image_mounter", &service);
- if (!service || service->port == 0) {
- printf("ERROR: Could not start mobile_image_mounter service!\n");
- goto leave;
- }
- if (mobile_image_mounter_new(device, service, &mim) != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
- printf("ERROR: Could not connect to mobile_image_mounter!\n");
- goto leave;
- }
- if (service) {
- lockdownd_service_descriptor_free(service);
- service = NULL;
- }
- if (!list_mode) {
- struct stat fst;
- if (disk_image_upload_type == DISK_IMAGE_UPLOAD_TYPE_AFC) {
- if ((lockdownd_start_service(lckd, "com.apple.afc", &service) !=
- LOCKDOWN_E_SUCCESS) || !service || !service->port) {
- fprintf(stderr, "Could not start com.apple.afc!\n");
- goto leave;
- }
- if (afc_client_new(device, service, &afc) != AFC_E_SUCCESS) {
- fprintf(stderr, "Could not connect to AFC!\n");
- goto leave;
- }
- if (service) {
- lockdownd_service_descriptor_free(service);
- service = NULL;
- }
- }
- if (stat(image_path, &fst) != 0) {
- fprintf(stderr, "ERROR: stat: %s: %s\n", image_path, strerror(errno));
- goto leave;
- }
- image_size = fst.st_size;
- if (stat(image_sig_path, &fst) != 0) {
- fprintf(stderr, "ERROR: stat: %s: %s\n", image_sig_path, strerror(errno));
- goto leave;
- }
- }
- lockdownd_client_free(lckd);
- lckd = NULL;
- mobile_image_mounter_error_t err;
- plist_t result = NULL;
- if (list_mode) {
- /* list mounts mode */
- if (!imagetype) {
- imagetype = strdup("Developer");
- }
- err = mobile_image_mounter_lookup_image(mim, imagetype, &result);
- free(imagetype);
- if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
- res = 0;
- if (xml_mode) {
- print_xml(result);
- } else {
- plist_print_to_stream(result, stdout);
- }
- } else {
- printf("Error: lookup_image returned %d\n", err);
- }
- } else {
- char sig[8192];
- size_t sig_length = 0;
- FILE *f = fopen(image_sig_path, "rb");
- if (!f) {
- fprintf(stderr, "Error opening signature file '%s': %s\n", image_sig_path, strerror(errno));
- goto leave;
- }
- sig_length = fread(sig, 1, sizeof(sig), f);
- fclose(f);
- if (sig_length == 0) {
- fprintf(stderr, "Could not read signature from file '%s'\n", image_sig_path);
- goto leave;
- }
- f = fopen(image_path, "rb");
- if (!f) {
- fprintf(stderr, "Error opening image file '%s': %s\n", image_path, strerror(errno));
- goto leave;
- }
- char *targetname = NULL;
- if (asprintf(&targetname, "%s/%s", PKG_PATH, "staging.dimage") < 0) {
- fprintf(stderr, "Out of memory!?\n");
- goto leave;
- }
- char *mountname = NULL;
- if (asprintf(&mountname, "%s/%s", PATH_PREFIX, targetname) < 0) {
- fprintf(stderr, "Out of memory!?\n");
- goto leave;
- }
- if (!imagetype) {
- imagetype = strdup("Developer");
- }
- switch(disk_image_upload_type) {
- case DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE:
- printf("Uploading %s\n", image_path);
- err = mobile_image_mounter_upload_image(mim, imagetype, image_size, sig, sig_length, mim_upload_cb, f);
- break;
- case DISK_IMAGE_UPLOAD_TYPE_AFC:
- default:
- printf("Uploading %s --> afc:///%s\n", image_path, targetname);
- char **strs = NULL;
- if (afc_get_file_info(afc, PKG_PATH, &strs) != AFC_E_SUCCESS) {
- if (afc_make_directory(afc, PKG_PATH) != AFC_E_SUCCESS) {
- fprintf(stderr, "WARNING: Could not create directory '%s' on device!\n", PKG_PATH);
- }
- }
- if (strs) {
- int i = 0;
- while (strs[i]) {
- free(strs[i]);
- i++;
- }
- free(strs);
- }
- uint64_t af = 0;
- if ((afc_file_open(afc, targetname, AFC_FOPEN_WRONLY, &af) !=
- AFC_E_SUCCESS) || !af) {
- fclose(f);
- fprintf(stderr, "afc_file_open on '%s' failed!\n", targetname);
- goto leave;
- }
- char buf[8192];
- size_t amount = 0;
- do {
- amount = fread(buf, 1, sizeof(buf), f);
- if (amount > 0) {
- uint32_t written, total = 0;
- while (total < amount) {
- written = 0;
- if (afc_file_write(afc, af, buf, amount, &written) !=
- AFC_E_SUCCESS) {
- fprintf(stderr, "AFC Write error!\n");
- break;
- }
- total += written;
- }
- if (total != amount) {
- fprintf(stderr, "Error: wrote only %d of %d\n", total,
- (unsigned int)amount);
- afc_file_close(afc, af);
- fclose(f);
- goto leave;
- }
- }
- }
- while (amount > 0);
- afc_file_close(afc, af);
- break;
- }
- fclose(f);
- printf("done.\n");
- printf("Mounting...\n");
- err = mobile_image_mounter_mount_image(mim, mountname, sig, sig_length, imagetype, &result);
- free(imagetype);
- if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
- if (result) {
- plist_t node = plist_dict_get_item(result, "Status");
- if (node) {
- char *status = NULL;
- plist_get_string_val(node, &status);
- if (status) {
- if (!strcmp(status, "Complete")) {
- printf("Done.\n");
- res = 0;
- } else {
- printf("unexpected status value:\n");
- if (xml_mode) {
- print_xml(result);
- } else {
- plist_print_to_stream(result, stdout);
- }
- }
- free(status);
- } else {
- printf("unexpected result:\n");
- if (xml_mode) {
- print_xml(result);
- } else {
- plist_print_to_stream(result, stdout);
- }
- }
- }
- node = plist_dict_get_item(result, "Error");
- if (node) {
- char *error = NULL;
- plist_get_string_val(node, &error);
- if (error) {
- printf("Error: %s\n", error);
- free(error);
- } else {
- printf("unexpected result:\n");
- if (xml_mode) {
- print_xml(result);
- } else {
- plist_print_to_stream(result, stdout);
- }
- }
- } else {
- if (xml_mode) {
- print_xml(result);
- } else {
- plist_print_to_stream(result, stdout);
- }
- }
- }
- } else {
- printf("Error: mount_image returned %d\n", err);
- }
- }
- if (result) {
- plist_free(result);
- }
- /* perform hangup command */
- mobile_image_mounter_hangup(mim);
- /* free client */
- mobile_image_mounter_free(mim);
- leave:
- if (afc) {
- afc_client_free(afc);
- }
- if (lckd) {
- lockdownd_client_free(lckd);
- }
- idevice_free(device);
- if (image_path)
- free(image_path);
- if (image_sig_path)
- free(image_sig_path);
- return res;
- }
|