summaryrefslogtreecommitdiffstats
path: root/tools/ideviceimagemounter.c
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2010-02-17 16:25:51 +0100
committerGravatar Matt Colyer2010-02-19 09:34:33 -0800
commitf364f1984e3d3ea2baa18ec7e939f912ddc06dbf (patch)
tree65dc7c6b90c135db0fe78ae3baac5dd74970433e /tools/ideviceimagemounter.c
parentaaa1f20562068872cbba7e1f53d051a40a8ac4a4 (diff)
downloadlibimobiledevice-f364f1984e3d3ea2baa18ec7e939f912ddc06dbf.tar.gz
libimobiledevice-f364f1984e3d3ea2baa18ec7e939f912ddc06dbf.tar.bz2
New mobile_image_mounter interface plus ideviceimagemounter tool
Diffstat (limited to 'tools/ideviceimagemounter.c')
-rw-r--r--tools/ideviceimagemounter.c525
1 files changed, 525 insertions, 0 deletions
diff --git a/tools/ideviceimagemounter.c b/tools/ideviceimagemounter.c
new file mode 100644
index 0000000..03fe112
--- /dev/null
+++ b/tools/ideviceimagemounter.c
@@ -0,0 +1,525 @@
1/**
2 * ideviceimagemounter -- Mount developer/debug disk images on the iPhone/iPod
3 *
4 * Copyright (C) 2010 Nikias Bassen <nikias@gmx.li>
5 *
6 * Licensed under the GNU General Public License Version 2
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more profile.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
21 * USA
22 */
23
24#include <stdlib.h>
25#define _GNU_SOURCE 1
26#define __USE_GNU 1
27#include <stdio.h>
28#include <string.h>
29#include <getopt.h>
30#include <errno.h>
31#include <glib.h>
32
33#include <libimobiledevice/libimobiledevice.h>
34#include <libimobiledevice/lockdown.h>
35#include <libimobiledevice/afc.h>
36#include <libimobiledevice/notification_proxy.h>
37#include <libimobiledevice/mobile_image_mounter.h>
38
39static int indent_level = 0;
40
41static int list_mode = 0;
42static int xml_mode = 0;
43static char *uuid = NULL;
44static char *imagetype = NULL;
45
46static const char PKG_PATH[] = "PublicStaging";
47static const char PATH_PREFIX[] = "/private/var/mobile/Media";
48
49static void print_usage(int argc, char **argv)
50{
51 char *name = NULL;
52
53 name = strrchr(argv[0], '/');
54 printf("Usage: %s [OPTIONS] IMAGE_FILE IMAGE_SIGNATURE_FILE\n\n", (name ? name + 1: argv[0]));
55 printf("Mounts the specified disk image on the device.\n\n");
56 printf(" -u, --uuid UUID\ttarget specific device by its 40-digit device UUID\n");
57 printf(" -l, --list\t\tList mount information\n");
58 printf(" -t, --imagetype\tImage type to use, default is 'Developer'\n");
59 printf(" -x, --xml\t\tUse XML output\n");
60 printf(" -d, --debug\t\tenable communication debugging\n");
61 printf(" -h, --help\t\tprints usage information\n");
62 printf("\n");
63}
64
65static void parse_opts(int argc, char **argv)
66{
67 static struct option longopts[] = {
68 {"help", 0, NULL, 'h'},
69 {"uuid", 0, NULL, 'u'},
70 {"list", 0, NULL, 'l'},
71 {"imagetype", 0, NULL, 't'},
72 {"xml", 0, NULL, 'x'},
73 {"debug", 0, NULL, 'd'},
74 {NULL, 0, NULL, 0}
75 };
76 int c;
77
78 while (1) {
79 c = getopt_long(argc, argv, "hu:lt:xd", longopts,
80 (int *) 0);
81 if (c == -1) {
82 break;
83 }
84
85 switch (c) {
86 case 'h':
87 print_usage(argc, argv);
88 exit(0);
89 case 'u':
90 if (strlen(optarg) != 40) {
91 printf("%s: invalid UUID specified (length != 40)\n",
92 argv[0]);
93 print_usage(argc, argv);
94 exit(2);
95 }
96 uuid = strdup(optarg);
97 break;
98 case 'l':
99 list_mode = 1;
100 break;
101 case 't':
102 imagetype = strdup(optarg);
103 break;
104 case 'x':
105 xml_mode = 1;
106 break;
107 case 'd':
108 idevice_set_debug_level(1);
109 break;
110 default:
111 print_usage(argc, argv);
112 exit(2);
113 }
114 }
115}
116
117static void plist_node_to_string(plist_t node);
118
119static void plist_array_to_string(plist_t node)
120{
121 /* iterate over items */
122 int i, count;
123 plist_t subnode = NULL;
124
125 count = plist_array_get_size(node);
126
127 for (i = 0; i < count; i++) {
128 subnode = plist_array_get_item(node, i);
129 printf("%*s", indent_level, "");
130 printf("%d: ", i);
131 plist_node_to_string(subnode);
132 }
133}
134
135static void plist_dict_to_string(plist_t node)
136{
137 /* iterate over key/value pairs */
138 plist_dict_iter it = NULL;
139
140 char* key = NULL;
141 plist_t subnode = NULL;
142 plist_dict_new_iter(node, &it);
143 plist_dict_next_item(node, it, &key, &subnode);
144 while (subnode)
145 {
146 printf("%*s", indent_level, "");
147 printf("%s", key);
148 if (plist_get_node_type(subnode) == PLIST_ARRAY)
149 printf("[%d]: ", plist_array_get_size(subnode));
150 else
151 printf(": ");
152 free(key);
153 key = NULL;
154 plist_node_to_string(subnode);
155 plist_dict_next_item(node, it, &key, &subnode);
156 }
157 free(it);
158}
159
160static void plist_node_to_string(plist_t node)
161{
162 char *s = NULL;
163 char *data = NULL;
164 double d;
165 uint8_t b;
166 uint64_t u = 0;
167 GTimeVal tv = { 0, 0 };
168
169 plist_type t;
170
171 if (!node)
172 return;
173
174 t = plist_get_node_type(node);
175
176 switch (t) {
177 case PLIST_BOOLEAN:
178 plist_get_bool_val(node, &b);
179 printf("%s\n", (b ? "true" : "false"));
180 break;
181
182 case PLIST_UINT:
183 plist_get_uint_val(node, &u);
184 printf("%llu\n", (long long)u);
185 break;
186
187 case PLIST_REAL:
188 plist_get_real_val(node, &d);
189 printf("%f\n", d);
190 break;
191
192 case PLIST_STRING:
193 plist_get_string_val(node, &s);
194 printf("%s\n", s);
195 free(s);
196 break;
197
198 case PLIST_KEY:
199 plist_get_key_val(node, &s);
200 printf("%s: ", s);
201 free(s);
202 break;
203
204 case PLIST_DATA:
205 plist_get_data_val(node, &data, &u);
206 uint64_t i;
207 for (i = 0; i < u; i++) {
208 printf("%02x", (unsigned char)data[i]);
209 }
210 free(data);
211 printf("\n");
212 g_free(s);
213 break;
214
215 case PLIST_DATE:
216 plist_get_date_val(node, (int32_t*)&tv.tv_sec, (int32_t*)&tv.tv_usec);
217 s = g_time_val_to_iso8601(&tv);
218 printf("%s\n", s);
219 free(s);
220 break;
221
222 case PLIST_ARRAY:
223 printf("\n");
224 indent_level++;
225 plist_array_to_string(node);
226 indent_level--;
227 break;
228
229 case PLIST_DICT:
230 printf("\n");
231 indent_level++;
232 plist_dict_to_string(node);
233 indent_level--;
234 break;
235
236 default:
237 break;
238 }
239}
240
241static void print_xml(plist_t node)
242{
243 char *xml = NULL;
244 uint32_t len = 0;
245 plist_to_xml(node, &xml, &len);
246 if (xml)
247 puts(xml);
248}
249
250int main(int argc, char **argv)
251{
252 idevice_t device = NULL;
253 lockdownd_client_t lckd = NULL;
254 mobile_image_mounter_client_t mim = NULL;
255 afc_client_t afc = NULL;
256 uint16_t port = 0;
257 int res = -1;
258 char *image_path = NULL;
259 char *image_sig_path = NULL;
260
261 parse_opts(argc, argv);
262
263 argc -= optind;
264 argv += optind;
265
266 if (!list_mode) {
267 if (argc < 1) {
268 printf("ERROR: No IMAGE_FILE has been given!\n");
269 return -1;
270 }
271 image_path = strdup(argv[0]);
272 if (argc >= 2) {
273 image_sig_path = strdup(argv[1]);
274 } else {
275 if (asprintf(&image_sig_path, "%s.signature", image_path) < 0) {
276 printf("Out of memory?!\n");
277 return -1;
278 }
279 }
280 }
281
282 if (IDEVICE_E_SUCCESS != idevice_new(&device, uuid)) {
283 printf("No device found, is it plugged in?\n");
284 return -1;
285 }
286
287 if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(device, &lckd, "ideviceimagemounter")) {
288 printf("ERROR: could not connect to lockdown. Exiting.\n");
289 goto leave;
290 }
291
292 lockdownd_start_service(lckd, "com.apple.mobile.mobile_image_mounter", &port);
293
294 if (port == 0) {
295 printf("ERROR: Could not start mobile_image_mounter service!\n");
296 goto leave;
297 }
298
299 if (mobile_image_mounter_new(device, port, &mim) != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
300 printf("ERROR: Could not connect to mobile_image_mounter!\n");
301 goto leave;
302 }
303
304 if (!list_mode) {
305 struct stat fst;
306 port = 0;
307 if ((lockdownd_start_service(lckd, "com.apple.afc", &port) !=
308 LOCKDOWN_E_SUCCESS) || !port) {
309 fprintf(stderr, "Could not start com.apple.afc!\n");
310 goto leave;
311 }
312 if (afc_client_new(device, port, &afc) != AFC_E_SUCCESS) {
313 fprintf(stderr, "Could not connect to AFC!\n");
314 goto leave;
315 }
316 if (stat(image_path, &fst) != 0) {
317 fprintf(stderr, "ERROR: stat: %s: %s\n", image_path, strerror(errno));
318 goto leave;
319 }
320 if (stat(image_sig_path, &fst) != 0) {
321 fprintf(stderr, "ERROR: stat: %s: %s\n", image_sig_path, strerror(errno));
322 goto leave;
323 }
324 }
325
326 lockdownd_client_free(lckd);
327 lckd = NULL;
328
329 mobile_image_mounter_error_t err;
330 plist_t result = NULL;
331
332 if (list_mode) {
333 /* list mounts mode */
334 if (!imagetype) {
335 imagetype = strdup("Developer");
336 }
337 err = mobile_image_mounter_lookup_image(mim, imagetype, &result);
338 free(imagetype);
339 if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
340 res = 0;
341 if (xml_mode) {
342 print_xml(result);
343 } else {
344 plist_dict_to_string(result);
345 }
346 } else {
347 printf("Error: lookup_image returned %d\n", err);
348 }
349 } else {
350 char sig[8192];
351 size_t sig_length = 0;
352 FILE *f = fopen(image_sig_path, "r");
353 if (!f) {
354 fprintf(stderr, "Error opening signature file '%s': %s\n", image_sig_path, strerror(errno));
355 goto leave;
356 }
357 sig_length = fread(sig, 1, sizeof(sig), f);
358 fclose(f);
359 if (sig_length == 0) {
360 fprintf(stderr, "Could not read signature from file '%s'\n", image_sig_path);
361 goto leave;
362 }
363
364 f = fopen(image_path, "r");
365 if (!f) {
366 fprintf(stderr, "Error opening image file '%s': %s\n", image_path, strerror(errno));
367 goto leave;
368 }
369
370 char *targetname = NULL;
371 if (asprintf(&targetname, "%s/%s", PKG_PATH, basename(image_path)) < 0) {
372 fprintf(stderr, "Out of memory!?\n");
373 goto leave;
374 }
375 char *mountname = NULL;
376 if (asprintf(&mountname, "%s/%s", PATH_PREFIX, targetname) < 0) {
377 fprintf(stderr, "Out of memory!?\n");
378 goto leave;
379 }
380
381 printf("Copying '%s' --> '%s'\n", image_path, targetname);
382
383 char **strs = NULL;
384 if (afc_get_file_info(afc, PKG_PATH, &strs) != AFC_E_SUCCESS) {
385 if (afc_make_directory(afc, PKG_PATH) != AFC_E_SUCCESS) {
386 fprintf(stderr, "WARNING: Could not create directory '%s' on device!\n", PKG_PATH);
387 }
388 }
389 if (strs) {
390 int i = 0;
391 while (strs[i]) {
392 free(strs[i]);
393 i++;
394 }
395 free(strs);
396 }
397
398 uint64_t af = 0;
399 if ((afc_file_open(afc, targetname, AFC_FOPEN_WRONLY, &af) !=
400 AFC_E_SUCCESS) || !af) {
401 fclose(f);
402 fprintf(stderr, "afc_file_open on '%s' failed!\n", targetname);
403 goto leave;
404 }
405
406 char buf[8192];
407 size_t amount = 0;
408 do {
409 amount = fread(buf, 1, sizeof(buf), f);
410 if (amount > 0) {
411 uint32_t written, total = 0;
412 while (total < amount) {
413 written = 0;
414 if (afc_file_write(afc, af, buf, amount, &written) !=
415 AFC_E_SUCCESS) {
416 fprintf(stderr, "AFC Write error!\n");
417 break;
418 }
419 total += written;
420 }
421 if (total != amount) {
422 fprintf(stderr, "Error: wrote only %d of %d\n", total,
423 amount);
424 afc_file_close(afc, af);
425 fclose(f);
426 goto leave;
427 }
428 }
429 }
430 while (amount > 0);
431
432 afc_file_close(afc, af);
433 fclose(f);
434
435 printf("done.\n");
436
437 printf("Mounting...\n");
438 if (!imagetype) {
439 imagetype = strdup("Developer");
440 }
441 err = mobile_image_mounter_mount_image(mim, mountname, sig, sig_length, imagetype, &result);
442 free(imagetype);
443 if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
444 if (result) {
445 plist_t node = plist_dict_get_item(result, "Status");
446 if (node) {
447 char *status = NULL;
448 plist_get_string_val(node, &status);
449 if (status) {
450 if (!strcmp(status, "Complete")) {
451 printf("Done.\n");
452 res = 0;
453 } else {
454 printf("unexpected status value:\n");
455 if (xml_mode) {
456 print_xml(result);
457 } else {
458 plist_dict_to_string(result);
459 }
460 }
461 free(status);
462 } else {
463 printf("unexpected result:\n");
464 if (xml_mode) {
465 print_xml(result);
466 } else {
467 plist_dict_to_string(result);
468 }
469 }
470 }
471 node = plist_dict_get_item(result, "Error");
472 if (node) {
473 char *error = NULL;
474 plist_get_string_val(node, &error);
475 if (error) {
476 printf("Error: %s\n", error);
477 free(error);
478 } else {
479 printf("unexpected result:\n");
480 if (xml_mode) {
481 print_xml(result);
482 } else {
483 plist_dict_to_string(result);
484 }
485 }
486
487 } else {
488 if (xml_mode) {
489 print_xml(result);
490 } else {
491 plist_dict_to_string(result);
492 }
493 }
494 }
495 } else {
496 printf("Error: mount_image returned %d\n", err);
497
498 }
499 }
500
501 if (result) {
502 plist_free(result);
503 }
504
505 /* perform hangup command */
506 mobile_image_mounter_hangup(mim);
507 /* free client */
508 mobile_image_mounter_free(mim);
509
510leave:
511 if (afc) {
512 afc_client_free(afc);
513 }
514 if (lckd) {
515 lockdownd_client_free(lckd);
516 }
517 idevice_free(device);
518
519 if (image_path)
520 free(image_path);
521 if (image_sig_path)
522 free(image_sig_path);
523
524 return res;
525}