summaryrefslogtreecommitdiffstats
path: root/tools/ideviceimagemounter.c
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2024-06-27 11:20:59 +0200
committerGravatar Nikias Bassen2024-06-27 11:20:59 +0200
commit68df374762b95ab40ca5242da66e3474360669b5 (patch)
treea75acdd2a57df58346f02e75577c7dad00b52b83 /tools/ideviceimagemounter.c
parented0d66d0341562731bb19928dfe48155509fa7a7 (diff)
downloadlibimobiledevice-68df374762b95ab40ca5242da66e3474360669b5.tar.gz
libimobiledevice-68df374762b95ab40ca5242da66e3474360669b5.tar.bz2
Add support for iOS 17+ Personalized Developer Disk image mounting
Diffstat (limited to 'tools/ideviceimagemounter.c')
-rw-r--r--tools/ideviceimagemounter.c396
1 files changed, 348 insertions, 48 deletions
diff --git a/tools/ideviceimagemounter.c b/tools/ideviceimagemounter.c
index f551b6c..52b0666 100644
--- a/tools/ideviceimagemounter.c
+++ b/tools/ideviceimagemounter.c
@@ -45,8 +45,11 @@
45#include <libimobiledevice/afc.h> 45#include <libimobiledevice/afc.h>
46#include <libimobiledevice/notification_proxy.h> 46#include <libimobiledevice/notification_proxy.h>
47#include <libimobiledevice/mobile_image_mounter.h> 47#include <libimobiledevice/mobile_image_mounter.h>
48#include <libimobiledevice-glue/sha.h>
49#include <libimobiledevice-glue/utils.h>
48#include <asprintf.h> 50#include <asprintf.h>
49#include <plist/plist.h> 51#include <plist/plist.h>
52#include <libtatsu/tss.h>
50 53
51static int list_mode = 0; 54static int list_mode = 0;
52static int use_network = 0; 55static int use_network = 0;
@@ -62,18 +65,38 @@ typedef enum {
62 DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE 65 DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE
63} disk_image_upload_type_t; 66} disk_image_upload_type_t;
64 67
68enum cmd_mode {
69 CMD_NONE = 0,
70 CMD_MOUNT,
71 CMD_UNMOUNT,
72 CMD_LIST,
73 CMD_DEVMODESTATUS
74};
75
76int cmd = CMD_NONE;
77
65static void print_usage(int argc, char **argv, int is_error) 78static void print_usage(int argc, char **argv, int is_error)
66{ 79{
67 char *name = strrchr(argv[0], '/'); 80 char *name = strrchr(argv[0], '/');
68 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] IMAGE_FILE IMAGE_SIGNATURE_FILE\n", (name ? name + 1: argv[0])); 81 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] COMMAND [COMMAND OPTIONS...]\n", (name ? name + 1: argv[0]));
69 fprintf(is_error ? stderr : stdout, 82 fprintf(is_error ? stderr : stdout,
70 "\n" 83 "\n"
71 "Mounts the specified disk image on the device.\n" 84 "Mount, list, or unmount a disk image on the device.\n"
85 "\n"
86 "COMMANDS:\n"
87 " mount PATH Mount the developer disk image at PATH.\n"
88 " For iOS 17+, PATH is a directory containing a .dmg image,\n"
89 " a BuildManifest.plist, and a Firmware sub-directory;\n"
90 " for older versions PATH is a .dmg filename with a"
91 " .dmg.signature in the same directory, or with another\n"
92 " parameter pointing to a file elsewhere.\n"
93 " list List mounted disk images.\n"
94 " unmount PATH Unmount the image mounted at PATH.\n"
95 " devmodestatus Query the developer mode status (iOS 16+)\n"
72 "\n" 96 "\n"
73 "OPTIONS:\n" 97 "OPTIONS:\n"
74 " -u, --udid UDID target specific device by UDID\n" 98 " -u, --udid UDID target specific device by UDID\n"
75 " -n, --network connect to network device\n" 99 " -n, --network connect to network device\n"
76 " -l, --list List mount information\n"
77 " -t, --imagetype TYPE Image type to use, default is 'Developer'\n" 100 " -t, --imagetype TYPE Image type to use, default is 'Developer'\n"
78 " -x, --xml Use XML output\n" 101 " -x, --xml Use XML output\n"
79 " -d, --debug enable communication debugging\n" 102 " -d, --debug enable communication debugging\n"
@@ -87,11 +110,11 @@ static void print_usage(int argc, char **argv, int is_error)
87 110
88static void parse_opts(int argc, char **argv) 111static void parse_opts(int argc, char **argv)
89{ 112{
113 int debug_level = 0;
90 static struct option longopts[] = { 114 static struct option longopts[] = {
91 { "help", no_argument, NULL, 'h' }, 115 { "help", no_argument, NULL, 'h' },
92 { "udid", required_argument, NULL, 'u' }, 116 { "udid", required_argument, NULL, 'u' },
93 { "network", no_argument, NULL, 'n' }, 117 { "network", no_argument, NULL, 'n' },
94 { "list", no_argument, NULL, 'l' },
95 { "imagetype", required_argument, NULL, 't' }, 118 { "imagetype", required_argument, NULL, 't' },
96 { "xml", no_argument, NULL, 'x' }, 119 { "xml", no_argument, NULL, 'x' },
97 { "debug", no_argument, NULL, 'd' }, 120 { "debug", no_argument, NULL, 'd' },
@@ -101,7 +124,7 @@ static void parse_opts(int argc, char **argv)
101 int c; 124 int c;
102 125
103 while (1) { 126 while (1) {
104 c = getopt_long(argc, argv, "hu:lt:xdnv", longopts, NULL); 127 c = getopt_long(argc, argv, "hu:t:xdnv", longopts, NULL);
105 if (c == -1) { 128 if (c == -1) {
106 break; 129 break;
107 } 130 }
@@ -121,9 +144,6 @@ static void parse_opts(int argc, char **argv)
121 case 'n': 144 case 'n':
122 use_network = 1; 145 use_network = 1;
123 break; 146 break;
124 case 'l':
125 list_mode = 1;
126 break;
127 case 't': 147 case 't':
128 imagetype = optarg; 148 imagetype = optarg;
129 break; 149 break;
@@ -131,7 +151,7 @@ static void parse_opts(int argc, char **argv)
131 xml_mode = 1; 151 xml_mode = 1;
132 break; 152 break;
133 case 'd': 153 case 'd':
134 idevice_set_debug_level(1); 154 debug_level++;
135 break; 155 break;
136 case 'v': 156 case 'v':
137 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION); 157 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
@@ -141,6 +161,8 @@ static void parse_opts(int argc, char **argv)
141 exit(2); 161 exit(2);
142 } 162 }
143 } 163 }
164 idevice_set_debug_level(debug_level);
165 tss_set_debug_level(debug_level);
144} 166}
145 167
146static ssize_t mim_upload_cb(void* buf, size_t size, void* userdata) 168static ssize_t mim_upload_cb(void* buf, size_t size, void* userdata)
@@ -169,29 +191,75 @@ int main(int argc, char **argv)
169 argc -= optind; 191 argc -= optind;
170 argv += optind; 192 argv += optind;
171 193
172 if (!list_mode) { 194 if (argc == 0) {
173 if (argc < 1) { 195 fprintf(stderr, "ERROR: Missing command.\n\n");
174 printf("ERROR: No IMAGE_FILE has been given!\n"); 196 print_usage(argc+optind, argv-optind, 1);
175 return -1; 197 return 2;
176 } 198 }
177 image_path = strdup(argv[0]); 199
178 if (argc >= 2) { 200 char* cmdstr = argv[0];
179 image_sig_path = strdup(argv[1]); 201
202 int optind2 = 0;
203 if (!strcmp(cmdstr, "mount")) {
204 cmd = CMD_MOUNT;
205 optind2++;
206 } else if (!strcmp(cmdstr, "list")) {
207 cmd = CMD_LIST;
208 optind2++;
209 } else if (!strcmp(cmdstr, "umount") || !strcmp(cmdstr, "unmount")) {
210 cmd = CMD_UNMOUNT;
211 optind2++;
212 } else if (!strcmp(cmdstr, "devmodestatus")) {
213 cmd = CMD_DEVMODESTATUS;
214 optind2++;
215 } else {
216 // assume mount command, unless -l / --list was specified
217 if (list_mode) {
218 cmd = CMD_LIST;
180 } else { 219 } else {
181 if (asprintf(&image_sig_path, "%s.signature", image_path) < 0) { 220 cmd = CMD_MOUNT;
182 printf("Out of memory?!\n");
183 return -1;
184 }
185 } 221 }
186 } 222 }
187 223
224 argc -= optind2;
225 argv += optind2;
226 optind += optind2;
227
228 switch (cmd) {
229 case CMD_MOUNT:
230 if (argc < 1) {
231 fprintf(stderr, "ERROR: Missing IMAGE_FILE for mount command\n");
232 print_usage(argc+optind, argv-optind, 1);
233 return 2;
234 }
235 image_path = strdup(argv[0]);
236 if (argc >= 2) {
237 image_sig_path = strdup(argv[1]);
238 } else {
239 if (asprintf(&image_sig_path, "%s.signature", image_path) < 0) {
240 printf("Out of memory?!\n");
241 return 1;
242 }
243 }
244 break;
245 case CMD_UNMOUNT:
246 if (argc != 1) {
247 fprintf(stderr, "ERROR: Missing mount path (argc = %d)\n", argc);
248 print_usage(argc+optind, argv-optind, 1);
249 return 2;
250 }
251 break;
252 default:
253 break;
254 }
255
188 if (IDEVICE_E_SUCCESS != idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX)) { 256 if (IDEVICE_E_SUCCESS != idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX)) {
189 if (udid) { 257 if (udid) {
190 printf("No device found with udid %s.\n", udid); 258 printf("No device found with udid %s.\n", udid);
191 } else { 259 } else {
192 printf("No device found.\n"); 260 printf("No device found.\n");
193 } 261 }
194 return -1; 262 return 1;
195 } 263 }
196 264
197 if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lckd, TOOL_NAME))) { 265 if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lckd, TOOL_NAME))) {
@@ -215,7 +283,7 @@ int main(int argc, char **argv)
215 } 283 }
216 } 284 }
217 285
218 if (product_version_major == 16) { 286 if (product_version_major >= 16) {
219 uint8_t dev_mode_status = 0; 287 uint8_t dev_mode_status = 0;
220 plist_t val = NULL; 288 plist_t val = NULL;
221 ldret = lockdownd_get_value(lckd, "com.apple.security.mac.amfi", "DeveloperModeStatus", &val); 289 ldret = lockdownd_get_value(lckd, "com.apple.security.mac.amfi", "DeveloperModeStatus", &val);
@@ -246,7 +314,7 @@ int main(int argc, char **argv)
246 service = NULL; 314 service = NULL;
247 } 315 }
248 316
249 if (!list_mode) { 317 if (cmd == CMD_MOUNT) {
250 struct stat fst; 318 struct stat fst;
251 if (disk_image_upload_type == DISK_IMAGE_UPLOAD_TYPE_AFC) { 319 if (disk_image_upload_type == DISK_IMAGE_UPLOAD_TYPE_AFC) {
252 if ((lockdownd_start_service(lckd, "com.apple.afc", &service) != 320 if ((lockdownd_start_service(lckd, "com.apple.afc", &service) !=
@@ -268,7 +336,7 @@ int main(int argc, char **argv)
268 goto leave; 336 goto leave;
269 } 337 }
270 image_size = fst.st_size; 338 image_size = fst.st_size;
271 if (stat(image_sig_path, &fst) != 0) { 339 if (product_version_major < 17 && stat(image_sig_path, &fst) != 0) {
272 fprintf(stderr, "ERROR: stat: %s: %s\n", image_sig_path, strerror(errno)); 340 fprintf(stderr, "ERROR: stat: %s: %s\n", image_sig_path, strerror(errno));
273 goto leave; 341 goto leave;
274 } 342 }
@@ -280,10 +348,14 @@ int main(int argc, char **argv)
280 mobile_image_mounter_error_t err = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR; 348 mobile_image_mounter_error_t err = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR;
281 plist_t result = NULL; 349 plist_t result = NULL;
282 350
283 if (list_mode) { 351 if (cmd == CMD_LIST) {
284 /* list mounts mode */ 352 /* list mounts mode */
285 if (!imagetype) { 353 if (!imagetype) {
286 imagetype = "Developer"; 354 if (product_version_major < 17) {
355 imagetype = "Developer";
356 } else {
357 imagetype = "Personalized";
358 }
287 } 359 }
288 err = mobile_image_mounter_lookup_image(mim, imagetype, &result); 360 err = mobile_image_mounter_lookup_image(mim, imagetype, &result);
289 if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) { 361 if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
@@ -292,25 +364,214 @@ int main(int argc, char **argv)
292 } else { 364 } else {
293 printf("Error: lookup_image returned %d\n", err); 365 printf("Error: lookup_image returned %d\n", err);
294 } 366 }
295 } else { 367 } else if (cmd == CMD_MOUNT) {
296 char sig[8192]; 368 unsigned char *sig = NULL;
297 size_t sig_length = 0; 369 size_t sig_length = 0;
298 FILE *f = fopen(image_sig_path, "rb"); 370 FILE *f;
299 if (!f) { 371 struct stat fst;
300 fprintf(stderr, "Error opening signature file '%s': %s\n", image_sig_path, strerror(errno)); 372 plist_t mount_options = NULL;
301 goto leave;
302 }
303 sig_length = fread(sig, 1, sizeof(sig), f);
304 fclose(f);
305 if (sig_length == 0) {
306 fprintf(stderr, "Could not read signature from file '%s'\n", image_sig_path);
307 goto leave;
308 }
309 373
310 f = fopen(image_path, "rb"); 374 if (product_version_major < 17) {
311 if (!f) { 375 f = fopen(image_sig_path, "rb");
312 fprintf(stderr, "Error opening image file '%s': %s\n", image_path, strerror(errno)); 376 if (!f) {
313 goto leave; 377 fprintf(stderr, "Error opening signature file '%s': %s\n", image_sig_path, strerror(errno));
378 goto leave;
379 }
380 fstat(fileno(f), &fst);
381 sig = malloc(sig_length);
382 sig_length = fread(sig, 1, fst.st_size, f);
383 fclose(f);
384 if (sig_length == 0) {
385 fprintf(stderr, "Could not read signature from file '%s'\n", image_sig_path);
386 goto leave;
387 }
388
389 f = fopen(image_path, "rb");
390 if (!f) {
391 fprintf(stderr, "Error opening image file '%s': %s\n", image_path, strerror(errno));
392 goto leave;
393 }
394 } else {
395 if (stat(image_path, &fst) != 0) {
396 fprintf(stderr, "Error: stat: '%s': %s\n", image_path, strerror(errno));
397 goto leave;
398 }
399 if (!S_ISDIR(fst.st_mode)) {
400 fprintf(stderr, "Error: Personalized Disk Image mount expects a directory as image path.\n");
401 goto leave;
402 }
403 char* build_manifest_path = string_build_path(image_path, "BuildManifest.plist", NULL);
404 plist_t build_manifest = NULL;
405 if (plist_read_from_file(build_manifest_path, &build_manifest, NULL) != 0) {
406 free(build_manifest_path);
407 build_manifest_path = string_build_path(image_path, "Restore", "BuildManifest.plist", NULL);
408 if (plist_read_from_file(build_manifest_path, &build_manifest, NULL) == 0) {
409 char* image_path_new = string_build_path(image_path, "Restore", NULL);
410 free(image_path);
411 image_path = image_path_new;
412 }
413 }
414 if (!build_manifest) {
415 fprintf(stderr, "Error: Could not locate BuildManifest.plist inside given disk image path!\n");
416 goto leave;
417 }
418
419 plist_t identifiers = NULL;
420 mobile_image_mounter_error_t merr = mobile_image_mounter_query_personalization_identifiers(mim, NULL, &identifiers);
421 if (merr != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
422 fprintf(stderr, "Failed to query personalization identifiers: %d\n", merr);
423 goto error_out;
424 }
425
426 unsigned int board_id = plist_dict_get_uint(identifiers, "BoardId");
427 unsigned int chip_id = plist_dict_get_uint(identifiers, "ChipID");
428
429 plist_t build_identities = plist_dict_get_item(build_manifest, "BuildIdentities");
430 plist_array_iter iter;
431 plist_array_new_iter(build_identities, &iter);
432 plist_t item = NULL;
433 plist_t build_identity = NULL;
434 do {
435 plist_array_next_item(build_identities, iter, &item);
436 if (!item) {
437 break;
438 }
439 unsigned int bi_board_id = (unsigned int)plist_dict_get_uint(item, "ApBoardID");
440 unsigned int bi_chip_id = (unsigned int)plist_dict_get_uint(item, "ApChipID");
441 if (bi_chip_id == chip_id && bi_board_id == board_id) {
442 build_identity = item;
443 break;
444 }
445 } while (item);
446 plist_mem_free(iter);
447 if (!build_identity) {
448 fprintf(stderr, "Error: The given disk image is not compatible with the current device.\n");
449 goto leave;
450 }
451 plist_t p_tc_path = plist_access_path(build_identity, 4, "Manifest", "LoadableTrustCache", "Info", "Path");
452 if (!p_tc_path) {
453 fprintf(stderr, "Error: Could not determine path for trust cache!\n");
454 goto leave;
455 }
456 plist_t p_dmg_path = plist_access_path(build_identity, 4, "Manifest", "PersonalizedDMG", "Info", "Path");
457 if (!p_dmg_path) {
458 fprintf(stderr, "Error: Could not determine path for disk image!\n");
459 goto leave;
460 }
461 char *tc_path = string_build_path(image_path, plist_get_string_ptr(p_tc_path, NULL), NULL);
462 unsigned char* trust_cache = NULL;
463 uint64_t trust_cache_size = 0;
464 if (!buffer_read_from_filename(tc_path, (char**)&trust_cache, &trust_cache_size)) {
465 fprintf(stderr, "Error: Trust cache does not exist at '%s'!\n", tc_path);
466 goto leave;
467 }
468 mount_options = plist_new_dict();
469 plist_dict_set_item(mount_options, "ImageTrustCache", plist_new_data((char*)trust_cache, trust_cache_size));
470 free(trust_cache);
471 char *dmg_path = string_build_path(image_path, plist_get_string_ptr(p_dmg_path, NULL), NULL);
472 free(image_path);
473 image_path = dmg_path;
474 f = fopen(image_path, "rb");
475 if (!f) {
476 fprintf(stderr, "Error opening image file '%s': %s\n", image_path, strerror(errno));
477 goto leave;
478 }
479
480 unsigned char buf[8192];
481 unsigned char sha384_digest[48];
482 sha384_context ctx;
483 sha384_init(&ctx);
484 fstat(fileno(f), &fst);
485 image_size = fst.st_size;
486 while (!feof(f)) {
487 ssize_t fr = fread(buf, 1, sizeof(buf), f);
488 if (fr <= 0) {
489 break;
490 }
491 sha384_update(&ctx, buf, fr);
492 }
493 rewind(f);
494 sha384_final(&ctx, sha384_digest);
495 unsigned char* manifest = NULL;
496 unsigned int manifest_size = 0;
497 /* check if the device already has a personalization manifest for this image */
498 if (mobile_image_mounter_query_personalization_manifest(mim, "DeveloperDiskImage", sha384_digest, sizeof(sha384_digest), &manifest, &manifest_size) == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
499 printf("Using existing personalization manifest from device.\n");
500 } else {
501 /* we need to re-connect in this case */
502 mobile_image_mounter_free(mim);
503 mim = NULL;
504 if (mobile_image_mounter_start_service(device, &mim, TOOL_NAME) != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
505 goto error_out;
506 }
507 printf("No personalization manifest, requesting from TSS...\n");
508 unsigned char* nonce = NULL;
509 unsigned int nonce_size = 0;
510
511 /* create new TSS request and fill parameters */
512 plist_t request = tss_request_new(NULL);
513 plist_t params = plist_new_dict();
514 tss_parameters_add_from_manifest(params, build_identity, 1);
515
516 /* copy all `Ap,*` items from identifiers */
517 plist_dict_iter di = NULL;
518 plist_dict_new_iter(identifiers, &di);
519 plist_t node = NULL;
520 do {
521 char* key = NULL;
522 plist_dict_next_item(identifiers, di, &key, &node);
523 if (node) {
524 if (!strncmp(key, "Ap,", 3)) {
525 plist_dict_set_item(request, key, plist_copy(node));
526 }
527 }
528 free(key);
529 } while (node);
530 plist_mem_free(di);
531
532 plist_dict_copy_uint(params, identifiers, "ApECID", "UniqueChipID");
533 plist_dict_set_item(params, "ApProductionMode", plist_new_bool(1));
534 plist_dict_set_item(params, "ApSecurityMode", plist_new_bool(1));
535 plist_dict_set_item(params, "ApSupportsImg4", plist_new_bool(1));
536
537 /* query nonce from image mounter service */
538 merr = mobile_image_mounter_query_nonce(mim, "DeveloperDiskImage", &nonce, &nonce_size);
539 if (merr == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
540 plist_dict_set_item(params, "ApNonce", plist_new_data((char*)nonce, nonce_size));
541 } else {
542 fprintf(stderr, "ERROR: Failed to query nonce for developer disk image: %d\n", merr);
543 goto error_out;
544 }
545 mobile_image_mounter_free(mim);
546 mim = NULL;
547
548 plist_dict_set_item(params, "ApSepNonce", plist_new_data("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 20));
549 plist_dict_set_item(params, "UID_MODE", plist_new_bool(0));
550 tss_request_add_ap_tags(request, params, NULL);
551 tss_request_add_common_tags(request, params, NULL);
552 tss_request_add_ap_img4_tags(request, params);
553 plist_free(params);
554
555 /* request IM4M from TSS */
556 plist_t response = tss_request_send(request, NULL);
557 plist_free(request);
558
559 plist_t p_manifest = plist_dict_get_item(response, "ApImg4Ticket");
560 if (!PLIST_IS_DATA(p_manifest)) {
561 fprintf(stderr, "Failed to get Img4Ticket\n");
562 goto error_out;
563 }
564
565 uint64_t m4m_len = 0;
566 plist_get_data_val(p_manifest, (char**)&manifest, &m4m_len);
567 manifest_size = m4m_len;
568 plist_free(response);
569 printf("Done.\n");
570 }
571 sig = manifest;
572 sig_length = manifest_size;
573
574 imagetype = "Personalized";
314 } 575 }
315 576
316 char *targetname = NULL; 577 char *targetname = NULL;
@@ -324,11 +585,16 @@ int main(int argc, char **argv)
324 goto leave; 585 goto leave;
325 } 586 }
326 587
327
328 if (!imagetype) { 588 if (!imagetype) {
329 imagetype = "Developer"; 589 imagetype = "Developer";
330 } 590 }
331 591
592 if (!mim) {
593 if (mobile_image_mounter_start_service(device, &mim, TOOL_NAME) != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
594 goto error_out;
595 }
596 }
597
332 switch(disk_image_upload_type) { 598 switch(disk_image_upload_type) {
333 case DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE: 599 case DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE:
334 printf("Uploading %s\n", image_path); 600 printf("Uploading %s\n", image_path);
@@ -403,7 +669,7 @@ int main(int argc, char **argv)
403 printf("done.\n"); 669 printf("done.\n");
404 670
405 printf("Mounting...\n"); 671 printf("Mounting...\n");
406 err = mobile_image_mounter_mount_image(mim, mountname, sig, sig_length, imagetype, &result); 672 err = mobile_image_mounter_mount_image_with_options(mim, mountname, sig, sig_length, imagetype, mount_options, &result);
407 if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) { 673 if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
408 if (result) { 674 if (result) {
409 plist_t node = plist_dict_get_item(result, "Status"); 675 plist_t node = plist_dict_get_item(result, "Status");
@@ -435,7 +701,10 @@ int main(int argc, char **argv)
435 printf("unexpected result:\n"); 701 printf("unexpected result:\n");
436 plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0); 702 plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0);
437 } 703 }
438 704 node = plist_dict_get_item(result, "DetailedError");
705 if (node) {
706 printf("DetailedError: %s\n", plist_get_string_ptr(node, NULL));
707 }
439 } else { 708 } else {
440 plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0); 709 plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0);
441 } 710 }
@@ -444,6 +713,37 @@ int main(int argc, char **argv)
444 printf("Error: mount_image returned %d\n", err); 713 printf("Error: mount_image returned %d\n", err);
445 714
446 } 715 }
716 } else if (cmd == CMD_UNMOUNT) {
717 err = mobile_image_mounter_unmount_image(mim, argv[0]);
718 switch (err) {
719 case MOBILE_IMAGE_MOUNTER_E_SUCCESS:
720 printf("Success\n");
721 res = 0;
722 break;
723 case MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED:
724 printf("Error: '%s' is not mounted\n", argv[0]);
725 res = 1;
726 break;
727 case MOBILE_IMAGE_MOUNTER_E_NOT_SUPPORTED:
728 printf("Error: 'unmount' is not supported on this device\n");
729 res = 1;
730 break;
731 case MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED:
732 printf("Error: device is locked\n");
733 res = 1;
734 break;
735 default:
736 printf("Error: unmount returned %d\n", err);
737 break;
738 }
739 } else if (cmd == CMD_DEVMODESTATUS) {
740 err = mobile_image_mounter_query_developer_mode_status(mim, &result);
741 if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
742 res = 0;
743 plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0);
744 } else {
745 printf("Error: query_developer_mode_status returned %d\n", err);
746 }
447 } 747 }
448 748
449 if (result) { 749 if (result) {
@@ -466,7 +766,7 @@ leave:
466 idevice_free(device); 766 idevice_free(device);
467 767
468 if (image_path) 768 if (image_path)
469 free(image_path); 769 free(image_path);
470 if (image_sig_path) 770 if (image_sig_path)
471 free(image_sig_path); 771 free(image_sig_path);
472 772