diff options
Diffstat (limited to 'tools/ideviceimagemounter.c')
-rw-r--r-- | tools/ideviceimagemounter.c | 873 |
1 files changed, 562 insertions, 311 deletions
diff --git a/tools/ideviceimagemounter.c b/tools/ideviceimagemounter.c index 3d299f8..511583e 100644 --- a/tools/ideviceimagemounter.c +++ b/tools/ideviceimagemounter.c | |||
@@ -1,26 +1,30 @@ | |||
1 | /** | 1 | /* |
2 | * ideviceimagemounter -- Mount developer/debug disk images on the iPhone/iPod | 2 | * ideviceimagemounter.c |
3 | * Mount developer/debug disk images on the device | ||
3 | * | 4 | * |
4 | * Copyright (C) 2010 Nikias Bassen <nikias@gmx.li> | 5 | * Copyright (C) 2010 Nikias Bassen <nikias@gmx.li> |
5 | * | 6 | * |
6 | * Licensed under the GNU General Public License Version 2 | 7 | * This library is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Lesser General Public | ||
9 | * License as published by the Free Software Foundation; either | ||
10 | * version 2.1 of the License, or (at your option) any later version. | ||
7 | * | 11 | * |
8 | * This program is free software; you can redistribute it and/or modify | 12 | * This library is distributed in the hope that it will be useful, |
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 | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | * GNU General Public License for more profile. | 15 | * Lesser General Public License for more details. |
17 | * | 16 | * |
18 | * You should have received a copy of the GNU General Public License | 17 | * You should have received a copy of the GNU Lesser General Public |
19 | * along with this program; if not, write to the Free Software | 18 | * License along with this library; if not, write to the Free Software |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 | 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
21 | * USA | ||
22 | */ | 20 | */ |
23 | 21 | ||
22 | #ifdef HAVE_CONFIG_H | ||
23 | #include <config.h> | ||
24 | #endif | ||
25 | |||
26 | #define TOOL_NAME "ideviceimagemounter" | ||
27 | |||
24 | #include <stdlib.h> | 28 | #include <stdlib.h> |
25 | #define _GNU_SOURCE 1 | 29 | #define _GNU_SOURCE 1 |
26 | #define __USE_GNU 1 | 30 | #define __USE_GNU 1 |
@@ -28,297 +32,311 @@ | |||
28 | #include <string.h> | 32 | #include <string.h> |
29 | #include <getopt.h> | 33 | #include <getopt.h> |
30 | #include <errno.h> | 34 | #include <errno.h> |
31 | #include <glib.h> | ||
32 | #include <libgen.h> | 35 | #include <libgen.h> |
36 | #include <time.h> | ||
37 | #include <sys/time.h> | ||
38 | #include <inttypes.h> | ||
39 | #ifndef WIN32 | ||
40 | #include <signal.h> | ||
41 | #endif | ||
33 | 42 | ||
34 | #include <libimobiledevice/libimobiledevice.h> | 43 | #include <libimobiledevice/libimobiledevice.h> |
35 | #include <libimobiledevice/lockdown.h> | 44 | #include <libimobiledevice/lockdown.h> |
36 | #include <libimobiledevice/afc.h> | 45 | #include <libimobiledevice/afc.h> |
37 | #include <libimobiledevice/notification_proxy.h> | 46 | #include <libimobiledevice/notification_proxy.h> |
38 | #include <libimobiledevice/mobile_image_mounter.h> | 47 | #include <libimobiledevice/mobile_image_mounter.h> |
39 | 48 | #include <libimobiledevice-glue/sha.h> | |
40 | static int indent_level = 0; | 49 | #include <libimobiledevice-glue/utils.h> |
50 | #include <asprintf.h> | ||
51 | #include <plist/plist.h> | ||
52 | #include <libtatsu/tss.h> | ||
41 | 53 | ||
42 | static int list_mode = 0; | 54 | static int list_mode = 0; |
55 | static int use_network = 0; | ||
43 | static int xml_mode = 0; | 56 | static int xml_mode = 0; |
44 | static char *uuid = NULL; | 57 | static const char *udid = NULL; |
45 | static char *imagetype = NULL; | 58 | static const char *imagetype = NULL; |
46 | 59 | ||
47 | static const char PKG_PATH[] = "PublicStaging"; | 60 | static const char PKG_PATH[] = "PublicStaging"; |
48 | static const char PATH_PREFIX[] = "/private/var/mobile/Media"; | 61 | static const char PATH_PREFIX[] = "/private/var/mobile/Media"; |
49 | 62 | ||
50 | static void print_usage(int argc, char **argv) | 63 | typedef enum { |
64 | DISK_IMAGE_UPLOAD_TYPE_AFC, | ||
65 | DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE | ||
66 | } disk_image_upload_type_t; | ||
67 | |||
68 | enum cmd_mode { | ||
69 | CMD_NONE = 0, | ||
70 | CMD_MOUNT, | ||
71 | CMD_UNMOUNT, | ||
72 | CMD_LIST, | ||
73 | CMD_DEVMODESTATUS | ||
74 | }; | ||
75 | |||
76 | int cmd = CMD_NONE; | ||
77 | |||
78 | static void print_usage(int argc, char **argv, int is_error) | ||
51 | { | 79 | { |
52 | char *name = NULL; | 80 | char *name = strrchr(argv[0], '/'); |
53 | 81 | fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] COMMAND [COMMAND OPTIONS...]\n", (name ? name + 1: argv[0])); | |
54 | name = strrchr(argv[0], '/'); | 82 | fprintf(is_error ? stderr : stdout, |
55 | printf("Usage: %s [OPTIONS] IMAGE_FILE IMAGE_SIGNATURE_FILE\n\n", (name ? name + 1: argv[0])); | 83 | "\n" |
56 | printf("Mounts the specified disk image on the device.\n\n"); | 84 | "Mount, list, or unmount a disk image on the device.\n" |
57 | printf(" -u, --uuid UUID\ttarget specific device by its 40-digit device UUID\n"); | 85 | "\n" |
58 | printf(" -l, --list\t\tList mount information\n"); | 86 | "COMMANDS:\n" |
59 | printf(" -t, --imagetype\tImage type to use, default is 'Developer'\n"); | 87 | " mount PATH Mount the developer disk image at PATH.\n" |
60 | printf(" -x, --xml\t\tUse XML output\n"); | 88 | " For iOS 17+, PATH is a directory containing a .dmg image,\n" |
61 | printf(" -d, --debug\t\tenable communication debugging\n"); | 89 | " a BuildManifest.plist, and a Firmware sub-directory;\n" |
62 | printf(" -h, --help\t\tprints usage information\n"); | 90 | " for older versions PATH is a .dmg filename with a" |
63 | printf("\n"); | 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" | ||
96 | "\n" | ||
97 | "OPTIONS:\n" | ||
98 | " -u, --udid UDID target specific device by UDID\n" | ||
99 | " -n, --network connect to network device\n" | ||
100 | " -t, --imagetype TYPE Image type to use, default is 'Developer'\n" | ||
101 | " -x, --xml Use XML output\n" | ||
102 | " -d, --debug enable communication debugging\n" | ||
103 | " -h, --help prints usage information\n" | ||
104 | " -v, --version prints version information\n" | ||
105 | "\n" | ||
106 | "Homepage: <" PACKAGE_URL ">\n" | ||
107 | "Bug Reports: <" PACKAGE_BUGREPORT ">\n" | ||
108 | ); | ||
64 | } | 109 | } |
65 | 110 | ||
66 | static void parse_opts(int argc, char **argv) | 111 | static void parse_opts(int argc, char **argv) |
67 | { | 112 | { |
113 | int debug_level = 0; | ||
68 | static struct option longopts[] = { | 114 | static struct option longopts[] = { |
69 | {"help", 0, NULL, 'h'}, | 115 | { "help", no_argument, NULL, 'h' }, |
70 | {"uuid", 0, NULL, 'u'}, | 116 | { "udid", required_argument, NULL, 'u' }, |
71 | {"list", 0, NULL, 'l'}, | 117 | { "network", no_argument, NULL, 'n' }, |
72 | {"imagetype", 0, NULL, 't'}, | 118 | { "imagetype", required_argument, NULL, 't' }, |
73 | {"xml", 0, NULL, 'x'}, | 119 | { "xml", no_argument, NULL, 'x' }, |
74 | {"debug", 0, NULL, 'd'}, | 120 | { "debug", no_argument, NULL, 'd' }, |
75 | {NULL, 0, NULL, 0} | 121 | { "version", no_argument, NULL, 'v' }, |
122 | { NULL, 0, NULL, 0 } | ||
76 | }; | 123 | }; |
77 | int c; | 124 | int c; |
78 | 125 | ||
79 | while (1) { | 126 | while (1) { |
80 | c = getopt_long(argc, argv, "hu:lt:xd", longopts, | 127 | c = getopt_long(argc, argv, "hu:t:xdnv", longopts, NULL); |
81 | (int *) 0); | ||
82 | if (c == -1) { | 128 | if (c == -1) { |
83 | break; | 129 | break; |
84 | } | 130 | } |
85 | 131 | ||
86 | switch (c) { | 132 | switch (c) { |
87 | case 'h': | 133 | case 'h': |
88 | print_usage(argc, argv); | 134 | print_usage(argc, argv, 0); |
89 | exit(0); | 135 | exit(0); |
90 | case 'u': | 136 | case 'u': |
91 | if (strlen(optarg) != 40) { | 137 | if (!*optarg) { |
92 | printf("%s: invalid UUID specified (length != 40)\n", | 138 | fprintf(stderr, "ERROR: UDID must not be empty!\n"); |
93 | argv[0]); | 139 | print_usage(argc, argv, 1); |
94 | print_usage(argc, argv); | ||
95 | exit(2); | 140 | exit(2); |
96 | } | 141 | } |
97 | uuid = strdup(optarg); | 142 | udid = optarg; |
98 | break; | 143 | break; |
99 | case 'l': | 144 | case 'n': |
100 | list_mode = 1; | 145 | use_network = 1; |
101 | break; | 146 | break; |
102 | case 't': | 147 | case 't': |
103 | imagetype = strdup(optarg); | 148 | imagetype = optarg; |
104 | break; | 149 | break; |
105 | case 'x': | 150 | case 'x': |
106 | xml_mode = 1; | 151 | xml_mode = 1; |
107 | break; | 152 | break; |
108 | case 'd': | 153 | case 'd': |
109 | idevice_set_debug_level(1); | 154 | debug_level++; |
110 | break; | 155 | break; |
156 | case 'v': | ||
157 | printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION); | ||
158 | exit(0); | ||
111 | default: | 159 | default: |
112 | print_usage(argc, argv); | 160 | print_usage(argc, argv, 1); |
113 | exit(2); | 161 | exit(2); |
114 | } | 162 | } |
115 | } | 163 | } |
164 | idevice_set_debug_level(debug_level); | ||
165 | tss_set_debug_level(debug_level); | ||
116 | } | 166 | } |
117 | 167 | ||
118 | static void plist_node_to_string(plist_t node); | 168 | static ssize_t mim_upload_cb(void* buf, size_t size, void* userdata) |
119 | |||
120 | static void plist_array_to_string(plist_t node) | ||
121 | { | 169 | { |
122 | /* iterate over items */ | 170 | return fread(buf, 1, size, (FILE*)userdata); |
123 | int i, count; | ||
124 | plist_t subnode = NULL; | ||
125 | |||
126 | count = plist_array_get_size(node); | ||
127 | |||
128 | for (i = 0; i < count; i++) { | ||
129 | subnode = plist_array_get_item(node, i); | ||
130 | printf("%*s", indent_level, ""); | ||
131 | printf("%d: ", i); | ||
132 | plist_node_to_string(subnode); | ||
133 | } | ||
134 | } | ||
135 | |||
136 | static void plist_dict_to_string(plist_t node) | ||
137 | { | ||
138 | /* iterate over key/value pairs */ | ||
139 | plist_dict_iter it = NULL; | ||
140 | |||
141 | char* key = NULL; | ||
142 | plist_t subnode = NULL; | ||
143 | plist_dict_new_iter(node, &it); | ||
144 | plist_dict_next_item(node, it, &key, &subnode); | ||
145 | while (subnode) | ||
146 | { | ||
147 | printf("%*s", indent_level, ""); | ||
148 | printf("%s", key); | ||
149 | if (plist_get_node_type(subnode) == PLIST_ARRAY) | ||
150 | printf("[%d]: ", plist_array_get_size(subnode)); | ||
151 | else | ||
152 | printf(": "); | ||
153 | free(key); | ||
154 | key = NULL; | ||
155 | plist_node_to_string(subnode); | ||
156 | plist_dict_next_item(node, it, &key, &subnode); | ||
157 | } | ||
158 | free(it); | ||
159 | } | ||
160 | |||
161 | static void plist_node_to_string(plist_t node) | ||
162 | { | ||
163 | char *s = NULL; | ||
164 | char *data = NULL; | ||
165 | double d; | ||
166 | uint8_t b; | ||
167 | uint64_t u = 0; | ||
168 | GTimeVal tv = { 0, 0 }; | ||
169 | |||
170 | plist_type t; | ||
171 | |||
172 | if (!node) | ||
173 | return; | ||
174 | |||
175 | t = plist_get_node_type(node); | ||
176 | |||
177 | switch (t) { | ||
178 | case PLIST_BOOLEAN: | ||
179 | plist_get_bool_val(node, &b); | ||
180 | printf("%s\n", (b ? "true" : "false")); | ||
181 | break; | ||
182 | |||
183 | case PLIST_UINT: | ||
184 | plist_get_uint_val(node, &u); | ||
185 | printf("%llu\n", (long long)u); | ||
186 | break; | ||
187 | |||
188 | case PLIST_REAL: | ||
189 | plist_get_real_val(node, &d); | ||
190 | printf("%f\n", d); | ||
191 | break; | ||
192 | |||
193 | case PLIST_STRING: | ||
194 | plist_get_string_val(node, &s); | ||
195 | printf("%s\n", s); | ||
196 | free(s); | ||
197 | break; | ||
198 | |||
199 | case PLIST_KEY: | ||
200 | plist_get_key_val(node, &s); | ||
201 | printf("%s: ", s); | ||
202 | free(s); | ||
203 | break; | ||
204 | |||
205 | case PLIST_DATA: | ||
206 | plist_get_data_val(node, &data, &u); | ||
207 | uint64_t i; | ||
208 | for (i = 0; i < u; i++) { | ||
209 | printf("%02x", (unsigned char)data[i]); | ||
210 | } | ||
211 | free(data); | ||
212 | printf("\n"); | ||
213 | g_free(s); | ||
214 | break; | ||
215 | |||
216 | case PLIST_DATE: | ||
217 | plist_get_date_val(node, (int32_t*)&tv.tv_sec, (int32_t*)&tv.tv_usec); | ||
218 | s = g_time_val_to_iso8601(&tv); | ||
219 | printf("%s\n", s); | ||
220 | free(s); | ||
221 | break; | ||
222 | |||
223 | case PLIST_ARRAY: | ||
224 | printf("\n"); | ||
225 | indent_level++; | ||
226 | plist_array_to_string(node); | ||
227 | indent_level--; | ||
228 | break; | ||
229 | |||
230 | case PLIST_DICT: | ||
231 | printf("\n"); | ||
232 | indent_level++; | ||
233 | plist_dict_to_string(node); | ||
234 | indent_level--; | ||
235 | break; | ||
236 | |||
237 | default: | ||
238 | break; | ||
239 | } | ||
240 | } | ||
241 | |||
242 | static void print_xml(plist_t node) | ||
243 | { | ||
244 | char *xml = NULL; | ||
245 | uint32_t len = 0; | ||
246 | plist_to_xml(node, &xml, &len); | ||
247 | if (xml) | ||
248 | puts(xml); | ||
249 | } | 171 | } |
250 | 172 | ||
251 | int main(int argc, char **argv) | 173 | int main(int argc, char **argv) |
252 | { | 174 | { |
253 | idevice_t device = NULL; | 175 | idevice_t device = NULL; |
254 | lockdownd_client_t lckd = NULL; | 176 | lockdownd_client_t lckd = NULL; |
177 | lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR; | ||
255 | mobile_image_mounter_client_t mim = NULL; | 178 | mobile_image_mounter_client_t mim = NULL; |
256 | afc_client_t afc = NULL; | 179 | afc_client_t afc = NULL; |
257 | uint16_t port = 0; | 180 | lockdownd_service_descriptor_t service = NULL; |
258 | int res = -1; | 181 | int res = -1; |
259 | char *image_path = NULL; | 182 | char *image_path = NULL; |
183 | size_t image_size = 0; | ||
260 | char *image_sig_path = NULL; | 184 | char *image_sig_path = NULL; |
261 | 185 | ||
186 | #ifndef WIN32 | ||
187 | signal(SIGPIPE, SIG_IGN); | ||
188 | #endif | ||
262 | parse_opts(argc, argv); | 189 | parse_opts(argc, argv); |
263 | 190 | ||
264 | argc -= optind; | 191 | argc -= optind; |
265 | argv += optind; | 192 | argv += optind; |
266 | 193 | ||
267 | if (!list_mode) { | 194 | if (argc == 0) { |
268 | if (argc < 1) { | 195 | fprintf(stderr, "ERROR: Missing command.\n\n"); |
269 | printf("ERROR: No IMAGE_FILE has been given!\n"); | 196 | print_usage(argc+optind, argv-optind, 1); |
270 | return -1; | 197 | return 2; |
271 | } | 198 | } |
272 | image_path = strdup(argv[0]); | 199 | |
273 | if (argc >= 2) { | 200 | char* cmdstr = argv[0]; |
274 | 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; | ||
275 | } else { | 219 | } else { |
276 | if (asprintf(&image_sig_path, "%s.signature", image_path) < 0) { | 220 | cmd = CMD_MOUNT; |
277 | printf("Out of memory?!\n"); | ||
278 | return -1; | ||
279 | } | ||
280 | } | 221 | } |
281 | } | 222 | } |
282 | 223 | ||
283 | if (IDEVICE_E_SUCCESS != idevice_new(&device, uuid)) { | 224 | argc -= optind2; |
284 | printf("No device found, is it plugged in?\n"); | 225 | argv += optind2; |
285 | return -1; | 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 | |||
256 | if (IDEVICE_E_SUCCESS != idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX)) { | ||
257 | if (udid) { | ||
258 | printf("No device found with udid %s.\n", udid); | ||
259 | } else { | ||
260 | printf("No device found.\n"); | ||
261 | } | ||
262 | return 1; | ||
286 | } | 263 | } |
287 | 264 | ||
288 | if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(device, &lckd, "ideviceimagemounter")) { | 265 | if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lckd, TOOL_NAME))) { |
289 | printf("ERROR: could not connect to lockdown. Exiting.\n"); | 266 | printf("ERROR: Could not connect to lockdown, error code %d.\n", ldret); |
290 | goto leave; | 267 | goto leave; |
291 | } | 268 | } |
292 | 269 | ||
293 | lockdownd_start_service(lckd, "com.apple.mobile.mobile_image_mounter", &port); | 270 | plist_t pver = NULL; |
271 | char *product_version = NULL; | ||
272 | lockdownd_get_value(lckd, NULL, "ProductVersion", &pver); | ||
273 | if (pver && plist_get_node_type(pver) == PLIST_STRING) { | ||
274 | plist_get_string_val(pver, &product_version); | ||
275 | } | ||
276 | disk_image_upload_type_t disk_image_upload_type = DISK_IMAGE_UPLOAD_TYPE_AFC; | ||
277 | int product_version_major = 0; | ||
278 | int product_version_minor = 0; | ||
279 | if (product_version) { | ||
280 | if (sscanf(product_version, "%d.%d.%*d", &product_version_major, &product_version_minor) == 2) { | ||
281 | if (product_version_major >= 7) | ||
282 | disk_image_upload_type = DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE; | ||
283 | } | ||
284 | } | ||
294 | 285 | ||
295 | if (port == 0) { | 286 | if (product_version_major >= 16) { |
287 | uint8_t dev_mode_status = 0; | ||
288 | plist_t val = NULL; | ||
289 | ldret = lockdownd_get_value(lckd, "com.apple.security.mac.amfi", "DeveloperModeStatus", &val); | ||
290 | if (ldret == LOCKDOWN_E_SUCCESS) { | ||
291 | plist_get_bool_val(val, &dev_mode_status); | ||
292 | plist_free(val); | ||
293 | } | ||
294 | if (!dev_mode_status) { | ||
295 | printf("ERROR: You have to enable Developer Mode on the given device in order to allowing mounting a developer disk image.\n"); | ||
296 | goto leave; | ||
297 | } | ||
298 | } | ||
299 | |||
300 | lockdownd_start_service(lckd, "com.apple.mobile.mobile_image_mounter", &service); | ||
301 | |||
302 | if (!service || service->port == 0) { | ||
296 | printf("ERROR: Could not start mobile_image_mounter service!\n"); | 303 | printf("ERROR: Could not start mobile_image_mounter service!\n"); |
297 | goto leave; | 304 | goto leave; |
298 | } | 305 | } |
299 | 306 | ||
300 | if (mobile_image_mounter_new(device, port, &mim) != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | 307 | if (mobile_image_mounter_new(device, service, &mim) != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { |
301 | printf("ERROR: Could not connect to mobile_image_mounter!\n"); | 308 | printf("ERROR: Could not connect to mobile_image_mounter!\n"); |
302 | goto leave; | 309 | goto leave; |
303 | } | 310 | } |
311 | |||
312 | if (service) { | ||
313 | lockdownd_service_descriptor_free(service); | ||
314 | service = NULL; | ||
315 | } | ||
304 | 316 | ||
305 | if (!list_mode) { | 317 | if (cmd == CMD_MOUNT) { |
306 | struct stat fst; | 318 | struct stat fst; |
307 | port = 0; | 319 | if (disk_image_upload_type == DISK_IMAGE_UPLOAD_TYPE_AFC) { |
308 | if ((lockdownd_start_service(lckd, "com.apple.afc", &port) != | 320 | if ((lockdownd_start_service(lckd, "com.apple.afc", &service) != |
309 | LOCKDOWN_E_SUCCESS) || !port) { | 321 | LOCKDOWN_E_SUCCESS) || !service || !service->port) { |
310 | fprintf(stderr, "Could not start com.apple.afc!\n"); | 322 | fprintf(stderr, "Could not start com.apple.afc!\n"); |
311 | goto leave; | 323 | goto leave; |
312 | } | 324 | } |
313 | if (afc_client_new(device, port, &afc) != AFC_E_SUCCESS) { | 325 | if (afc_client_new(device, service, &afc) != AFC_E_SUCCESS) { |
314 | fprintf(stderr, "Could not connect to AFC!\n"); | 326 | fprintf(stderr, "Could not connect to AFC!\n"); |
315 | goto leave; | 327 | goto leave; |
328 | } | ||
329 | if (service) { | ||
330 | lockdownd_service_descriptor_free(service); | ||
331 | service = NULL; | ||
332 | } | ||
316 | } | 333 | } |
317 | if (stat(image_path, &fst) != 0) { | 334 | if (stat(image_path, &fst) != 0) { |
318 | fprintf(stderr, "ERROR: stat: %s: %s\n", image_path, strerror(errno)); | 335 | fprintf(stderr, "ERROR: stat: %s: %s\n", image_path, strerror(errno)); |
319 | goto leave; | 336 | goto leave; |
320 | } | 337 | } |
321 | if (stat(image_sig_path, &fst) != 0) { | 338 | image_size = fst.st_size; |
339 | if (product_version_major < 17 && stat(image_sig_path, &fst) != 0) { | ||
322 | 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)); |
323 | goto leave; | 341 | goto leave; |
324 | } | 342 | } |
@@ -327,49 +345,240 @@ int main(int argc, char **argv) | |||
327 | lockdownd_client_free(lckd); | 345 | lockdownd_client_free(lckd); |
328 | lckd = NULL; | 346 | lckd = NULL; |
329 | 347 | ||
330 | mobile_image_mounter_error_t err; | 348 | mobile_image_mounter_error_t err = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR; |
331 | plist_t result = NULL; | 349 | plist_t result = NULL; |
332 | 350 | ||
333 | if (list_mode) { | 351 | if (cmd == CMD_LIST) { |
334 | /* list mounts mode */ | 352 | /* list mounts mode */ |
335 | if (!imagetype) { | 353 | if (!imagetype) { |
336 | imagetype = strdup("Developer"); | 354 | if (product_version_major < 17) { |
355 | imagetype = "Developer"; | ||
356 | } else { | ||
357 | imagetype = "Personalized"; | ||
358 | } | ||
337 | } | 359 | } |
338 | err = mobile_image_mounter_lookup_image(mim, imagetype, &result); | 360 | err = mobile_image_mounter_lookup_image(mim, imagetype, &result); |
339 | free(imagetype); | ||
340 | if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | 361 | if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) { |
341 | res = 0; | 362 | res = 0; |
342 | if (xml_mode) { | 363 | plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0); |
343 | print_xml(result); | ||
344 | } else { | ||
345 | plist_dict_to_string(result); | ||
346 | } | ||
347 | } else { | 364 | } else { |
348 | printf("Error: lookup_image returned %d\n", err); | 365 | printf("Error: lookup_image returned %d\n", err); |
349 | } | 366 | } |
350 | } else { | 367 | } else if (cmd == CMD_MOUNT) { |
351 | char sig[8192]; | 368 | unsigned char *sig = NULL; |
352 | size_t sig_length = 0; | 369 | size_t sig_length = 0; |
353 | FILE *f = fopen(image_sig_path, "r"); | 370 | FILE *f; |
354 | if (!f) { | 371 | struct stat fst; |
355 | fprintf(stderr, "Error opening signature file '%s': %s\n", image_sig_path, strerror(errno)); | 372 | plist_t mount_options = NULL; |
356 | goto leave; | ||
357 | } | ||
358 | sig_length = fread(sig, 1, sizeof(sig), f); | ||
359 | fclose(f); | ||
360 | if (sig_length == 0) { | ||
361 | fprintf(stderr, "Could not read signature from file '%s'\n", image_sig_path); | ||
362 | goto leave; | ||
363 | } | ||
364 | 373 | ||
365 | f = fopen(image_path, "r"); | 374 | if (product_version_major < 17) { |
366 | if (!f) { | 375 | f = fopen(image_sig_path, "rb"); |
367 | fprintf(stderr, "Error opening image file '%s': %s\n", image_path, strerror(errno)); | 376 | if (!f) { |
368 | goto leave; | 377 | fprintf(stderr, "Error opening signature file '%s': %s\n", image_sig_path, strerror(errno)); |
378 | goto leave; | ||
379 | } | ||
380 | if (fstat(fileno(f), &fst) != 0) { | ||
381 | fprintf(stderr, "Error: fstat: %s\n", strerror(errno)); | ||
382 | goto leave; | ||
383 | } | ||
384 | sig = malloc(fst.st_size); | ||
385 | sig_length = fread(sig, 1, fst.st_size, f); | ||
386 | fclose(f); | ||
387 | if (sig_length == 0) { | ||
388 | fprintf(stderr, "Could not read signature from file '%s'\n", image_sig_path); | ||
389 | goto leave; | ||
390 | } | ||
391 | |||
392 | f = fopen(image_path, "rb"); | ||
393 | if (!f) { | ||
394 | fprintf(stderr, "Error opening image file '%s': %s\n", image_path, strerror(errno)); | ||
395 | goto leave; | ||
396 | } | ||
397 | } else { | ||
398 | if (stat(image_path, &fst) != 0) { | ||
399 | fprintf(stderr, "Error: stat: '%s': %s\n", image_path, strerror(errno)); | ||
400 | goto leave; | ||
401 | } | ||
402 | if (!S_ISDIR(fst.st_mode)) { | ||
403 | fprintf(stderr, "Error: Personalized Disk Image mount expects a directory as image path.\n"); | ||
404 | goto leave; | ||
405 | } | ||
406 | char* build_manifest_path = string_build_path(image_path, "BuildManifest.plist", NULL); | ||
407 | plist_t build_manifest = NULL; | ||
408 | if (plist_read_from_file(build_manifest_path, &build_manifest, NULL) != 0) { | ||
409 | free(build_manifest_path); | ||
410 | build_manifest_path = string_build_path(image_path, "Restore", "BuildManifest.plist", NULL); | ||
411 | if (plist_read_from_file(build_manifest_path, &build_manifest, NULL) == 0) { | ||
412 | char* image_path_new = string_build_path(image_path, "Restore", NULL); | ||
413 | free(image_path); | ||
414 | image_path = image_path_new; | ||
415 | } | ||
416 | } | ||
417 | if (!build_manifest) { | ||
418 | fprintf(stderr, "Error: Could not locate BuildManifest.plist inside given disk image path!\n"); | ||
419 | goto leave; | ||
420 | } | ||
421 | |||
422 | plist_t identifiers = NULL; | ||
423 | mobile_image_mounter_error_t merr = mobile_image_mounter_query_personalization_identifiers(mim, NULL, &identifiers); | ||
424 | if (merr != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
425 | fprintf(stderr, "Failed to query personalization identifiers: %d\n", merr); | ||
426 | goto error_out; | ||
427 | } | ||
428 | |||
429 | unsigned int board_id = plist_dict_get_uint(identifiers, "BoardId"); | ||
430 | unsigned int chip_id = plist_dict_get_uint(identifiers, "ChipID"); | ||
431 | |||
432 | plist_t build_identities = plist_dict_get_item(build_manifest, "BuildIdentities"); | ||
433 | plist_array_iter iter; | ||
434 | plist_array_new_iter(build_identities, &iter); | ||
435 | plist_t item = NULL; | ||
436 | plist_t build_identity = NULL; | ||
437 | do { | ||
438 | plist_array_next_item(build_identities, iter, &item); | ||
439 | if (!item) { | ||
440 | break; | ||
441 | } | ||
442 | unsigned int bi_board_id = (unsigned int)plist_dict_get_uint(item, "ApBoardID"); | ||
443 | unsigned int bi_chip_id = (unsigned int)plist_dict_get_uint(item, "ApChipID"); | ||
444 | if (bi_chip_id == chip_id && bi_board_id == board_id) { | ||
445 | build_identity = item; | ||
446 | break; | ||
447 | } | ||
448 | } while (item); | ||
449 | plist_mem_free(iter); | ||
450 | if (!build_identity) { | ||
451 | fprintf(stderr, "Error: The given disk image is not compatible with the current device.\n"); | ||
452 | goto leave; | ||
453 | } | ||
454 | plist_t p_tc_path = plist_access_path(build_identity, 4, "Manifest", "LoadableTrustCache", "Info", "Path"); | ||
455 | if (!p_tc_path) { | ||
456 | fprintf(stderr, "Error: Could not determine path for trust cache!\n"); | ||
457 | goto leave; | ||
458 | } | ||
459 | plist_t p_dmg_path = plist_access_path(build_identity, 4, "Manifest", "PersonalizedDMG", "Info", "Path"); | ||
460 | if (!p_dmg_path) { | ||
461 | fprintf(stderr, "Error: Could not determine path for disk image!\n"); | ||
462 | goto leave; | ||
463 | } | ||
464 | char *tc_path = string_build_path(image_path, plist_get_string_ptr(p_tc_path, NULL), NULL); | ||
465 | unsigned char* trust_cache = NULL; | ||
466 | uint64_t trust_cache_size = 0; | ||
467 | if (!buffer_read_from_filename(tc_path, (char**)&trust_cache, &trust_cache_size)) { | ||
468 | fprintf(stderr, "Error: Trust cache does not exist at '%s'!\n", tc_path); | ||
469 | goto leave; | ||
470 | } | ||
471 | mount_options = plist_new_dict(); | ||
472 | plist_dict_set_item(mount_options, "ImageTrustCache", plist_new_data((char*)trust_cache, trust_cache_size)); | ||
473 | free(trust_cache); | ||
474 | char *dmg_path = string_build_path(image_path, plist_get_string_ptr(p_dmg_path, NULL), NULL); | ||
475 | free(image_path); | ||
476 | image_path = dmg_path; | ||
477 | f = fopen(image_path, "rb"); | ||
478 | if (!f) { | ||
479 | fprintf(stderr, "Error opening image file '%s': %s\n", image_path, strerror(errno)); | ||
480 | goto leave; | ||
481 | } | ||
482 | |||
483 | unsigned char buf[8192]; | ||
484 | unsigned char sha384_digest[48]; | ||
485 | sha384_context ctx; | ||
486 | sha384_init(&ctx); | ||
487 | fstat(fileno(f), &fst); | ||
488 | image_size = fst.st_size; | ||
489 | while (!feof(f)) { | ||
490 | ssize_t fr = fread(buf, 1, sizeof(buf), f); | ||
491 | if (fr <= 0) { | ||
492 | break; | ||
493 | } | ||
494 | sha384_update(&ctx, buf, fr); | ||
495 | } | ||
496 | rewind(f); | ||
497 | sha384_final(&ctx, sha384_digest); | ||
498 | unsigned char* manifest = NULL; | ||
499 | unsigned int manifest_size = 0; | ||
500 | /* check if the device already has a personalization manifest for this image */ | ||
501 | if (mobile_image_mounter_query_personalization_manifest(mim, "DeveloperDiskImage", sha384_digest, sizeof(sha384_digest), &manifest, &manifest_size) == MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
502 | printf("Using existing personalization manifest from device.\n"); | ||
503 | } else { | ||
504 | /* we need to re-connect in this case */ | ||
505 | mobile_image_mounter_free(mim); | ||
506 | mim = NULL; | ||
507 | if (mobile_image_mounter_start_service(device, &mim, TOOL_NAME) != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
508 | goto error_out; | ||
509 | } | ||
510 | printf("No personalization manifest, requesting from TSS...\n"); | ||
511 | unsigned char* nonce = NULL; | ||
512 | unsigned int nonce_size = 0; | ||
513 | |||
514 | /* create new TSS request and fill parameters */ | ||
515 | plist_t request = tss_request_new(NULL); | ||
516 | plist_t params = plist_new_dict(); | ||
517 | tss_parameters_add_from_manifest(params, build_identity, 1); | ||
518 | |||
519 | /* copy all `Ap,*` items from identifiers */ | ||
520 | plist_dict_iter di = NULL; | ||
521 | plist_dict_new_iter(identifiers, &di); | ||
522 | plist_t node = NULL; | ||
523 | do { | ||
524 | char* key = NULL; | ||
525 | plist_dict_next_item(identifiers, di, &key, &node); | ||
526 | if (node) { | ||
527 | if (!strncmp(key, "Ap,", 3)) { | ||
528 | plist_dict_set_item(request, key, plist_copy(node)); | ||
529 | } | ||
530 | } | ||
531 | free(key); | ||
532 | } while (node); | ||
533 | plist_mem_free(di); | ||
534 | |||
535 | plist_dict_copy_uint(params, identifiers, "ApECID", "UniqueChipID"); | ||
536 | plist_dict_set_item(params, "ApProductionMode", plist_new_bool(1)); | ||
537 | plist_dict_set_item(params, "ApSecurityMode", plist_new_bool(1)); | ||
538 | plist_dict_set_item(params, "ApSupportsImg4", plist_new_bool(1)); | ||
539 | |||
540 | /* query nonce from image mounter service */ | ||
541 | merr = mobile_image_mounter_query_nonce(mim, "DeveloperDiskImage", &nonce, &nonce_size); | ||
542 | if (merr == MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
543 | plist_dict_set_item(params, "ApNonce", plist_new_data((char*)nonce, nonce_size)); | ||
544 | } else { | ||
545 | fprintf(stderr, "ERROR: Failed to query nonce for developer disk image: %d\n", merr); | ||
546 | goto error_out; | ||
547 | } | ||
548 | mobile_image_mounter_free(mim); | ||
549 | mim = NULL; | ||
550 | |||
551 | 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)); | ||
552 | plist_dict_set_item(params, "UID_MODE", plist_new_bool(0)); | ||
553 | tss_request_add_ap_tags(request, params, NULL); | ||
554 | tss_request_add_common_tags(request, params, NULL); | ||
555 | tss_request_add_ap_img4_tags(request, params); | ||
556 | plist_free(params); | ||
557 | |||
558 | /* request IM4M from TSS */ | ||
559 | plist_t response = tss_request_send(request, NULL); | ||
560 | plist_free(request); | ||
561 | |||
562 | plist_t p_manifest = plist_dict_get_item(response, "ApImg4Ticket"); | ||
563 | if (!PLIST_IS_DATA(p_manifest)) { | ||
564 | fprintf(stderr, "Failed to get Img4Ticket\n"); | ||
565 | goto error_out; | ||
566 | } | ||
567 | |||
568 | uint64_t m4m_len = 0; | ||
569 | plist_get_data_val(p_manifest, (char**)&manifest, &m4m_len); | ||
570 | manifest_size = m4m_len; | ||
571 | plist_free(response); | ||
572 | printf("Done.\n"); | ||
573 | } | ||
574 | sig = manifest; | ||
575 | sig_length = manifest_size; | ||
576 | |||
577 | imagetype = "Personalized"; | ||
369 | } | 578 | } |
370 | 579 | ||
371 | char *targetname = NULL; | 580 | char *targetname = NULL; |
372 | if (asprintf(&targetname, "%s/%s", PKG_PATH, basename(image_path)) < 0) { | 581 | if (asprintf(&targetname, "%s/%s", PKG_PATH, "staging.dimage") < 0) { |
373 | fprintf(stderr, "Out of memory!?\n"); | 582 | fprintf(stderr, "Out of memory!?\n"); |
374 | goto leave; | 583 | goto leave; |
375 | } | 584 | } |
@@ -379,68 +588,91 @@ int main(int argc, char **argv) | |||
379 | goto leave; | 588 | goto leave; |
380 | } | 589 | } |
381 | 590 | ||
382 | printf("Copying '%s' --> '%s'\n", image_path, targetname); | 591 | if (!imagetype) { |
383 | 592 | imagetype = "Developer"; | |
384 | char **strs = NULL; | ||
385 | if (afc_get_file_info(afc, PKG_PATH, &strs) != AFC_E_SUCCESS) { | ||
386 | if (afc_make_directory(afc, PKG_PATH) != AFC_E_SUCCESS) { | ||
387 | fprintf(stderr, "WARNING: Could not create directory '%s' on device!\n", PKG_PATH); | ||
388 | } | ||
389 | } | ||
390 | if (strs) { | ||
391 | int i = 0; | ||
392 | while (strs[i]) { | ||
393 | free(strs[i]); | ||
394 | i++; | ||
395 | } | ||
396 | free(strs); | ||
397 | } | 593 | } |
398 | 594 | ||
399 | uint64_t af = 0; | 595 | if (!mim) { |
400 | if ((afc_file_open(afc, targetname, AFC_FOPEN_WRONLY, &af) != | 596 | if (mobile_image_mounter_start_service(device, &mim, TOOL_NAME) != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { |
401 | AFC_E_SUCCESS) || !af) { | 597 | goto error_out; |
402 | fclose(f); | 598 | } |
403 | fprintf(stderr, "afc_file_open on '%s' failed!\n", targetname); | ||
404 | goto leave; | ||
405 | } | 599 | } |
406 | 600 | ||
407 | char buf[8192]; | 601 | switch(disk_image_upload_type) { |
408 | size_t amount = 0; | 602 | case DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE: |
409 | do { | 603 | printf("Uploading %s\n", image_path); |
410 | amount = fread(buf, 1, sizeof(buf), f); | 604 | err = mobile_image_mounter_upload_image(mim, imagetype, image_size, sig, sig_length, mim_upload_cb, f); |
411 | if (amount > 0) { | 605 | break; |
412 | uint32_t written, total = 0; | 606 | case DISK_IMAGE_UPLOAD_TYPE_AFC: |
413 | while (total < amount) { | 607 | default: |
414 | written = 0; | 608 | printf("Uploading %s --> afc:///%s\n", image_path, targetname); |
415 | if (afc_file_write(afc, af, buf, amount, &written) != | 609 | char **strs = NULL; |
416 | AFC_E_SUCCESS) { | 610 | if (afc_get_file_info(afc, PKG_PATH, &strs) != AFC_E_SUCCESS) { |
417 | fprintf(stderr, "AFC Write error!\n"); | 611 | if (afc_make_directory(afc, PKG_PATH) != AFC_E_SUCCESS) { |
418 | break; | 612 | fprintf(stderr, "WARNING: Could not create directory '%s' on device!\n", PKG_PATH); |
613 | } | ||
614 | } | ||
615 | if (strs) { | ||
616 | int i = 0; | ||
617 | while (strs[i]) { | ||
618 | free(strs[i]); | ||
619 | i++; | ||
419 | } | 620 | } |
420 | total += written; | 621 | free(strs); |
421 | } | 622 | } |
422 | if (total != amount) { | 623 | |
423 | fprintf(stderr, "Error: wrote only %d of %d\n", total, | 624 | uint64_t af = 0; |
424 | (unsigned int)amount); | 625 | if ((afc_file_open(afc, targetname, AFC_FOPEN_WRONLY, &af) != |
425 | afc_file_close(afc, af); | 626 | AFC_E_SUCCESS) || !af) { |
426 | fclose(f); | 627 | fclose(f); |
628 | fprintf(stderr, "afc_file_open on '%s' failed!\n", targetname); | ||
427 | goto leave; | 629 | goto leave; |
428 | } | 630 | } |
429 | } | 631 | |
632 | char buf[8192]; | ||
633 | size_t amount = 0; | ||
634 | do { | ||
635 | amount = fread(buf, 1, sizeof(buf), f); | ||
636 | if (amount > 0) { | ||
637 | uint32_t written, total = 0; | ||
638 | while (total < amount) { | ||
639 | written = 0; | ||
640 | if (afc_file_write(afc, af, buf + total, amount - total, &written) != | ||
641 | AFC_E_SUCCESS) { | ||
642 | fprintf(stderr, "AFC Write error!\n"); | ||
643 | break; | ||
644 | } | ||
645 | total += written; | ||
646 | } | ||
647 | if (total != amount) { | ||
648 | fprintf(stderr, "Error: wrote only %d of %d\n", total, | ||
649 | (unsigned int)amount); | ||
650 | afc_file_close(afc, af); | ||
651 | fclose(f); | ||
652 | goto leave; | ||
653 | } | ||
654 | } | ||
655 | } | ||
656 | while (amount > 0); | ||
657 | |||
658 | afc_file_close(afc, af); | ||
659 | break; | ||
430 | } | 660 | } |
431 | while (amount > 0); | ||
432 | 661 | ||
433 | afc_file_close(afc, af); | ||
434 | fclose(f); | 662 | fclose(f); |
435 | 663 | ||
664 | if (err != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
665 | if (err == MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED) { | ||
666 | printf("ERROR: Device is locked, can't mount. Unlock device and try again.\n"); | ||
667 | } else { | ||
668 | printf("ERROR: Unknown error occurred, can't mount.\n"); | ||
669 | } | ||
670 | goto error_out; | ||
671 | } | ||
436 | printf("done.\n"); | 672 | printf("done.\n"); |
437 | 673 | ||
438 | printf("Mounting...\n"); | 674 | printf("Mounting...\n"); |
439 | if (!imagetype) { | 675 | err = mobile_image_mounter_mount_image_with_options(mim, mountname, sig, sig_length, imagetype, mount_options, &result); |
440 | imagetype = strdup("Developer"); | ||
441 | } | ||
442 | err = mobile_image_mounter_mount_image(mim, mountname, sig, sig_length, imagetype, &result); | ||
443 | free(imagetype); | ||
444 | if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | 676 | if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) { |
445 | if (result) { | 677 | if (result) { |
446 | plist_t node = plist_dict_get_item(result, "Status"); | 678 | plist_t node = plist_dict_get_item(result, "Status"); |
@@ -453,20 +685,12 @@ int main(int argc, char **argv) | |||
453 | res = 0; | 685 | res = 0; |
454 | } else { | 686 | } else { |
455 | printf("unexpected status value:\n"); | 687 | printf("unexpected status value:\n"); |
456 | if (xml_mode) { | 688 | plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0); |
457 | print_xml(result); | ||
458 | } else { | ||
459 | plist_dict_to_string(result); | ||
460 | } | ||
461 | } | 689 | } |
462 | free(status); | 690 | free(status); |
463 | } else { | 691 | } else { |
464 | printf("unexpected result:\n"); | 692 | printf("unexpected result:\n"); |
465 | if (xml_mode) { | 693 | plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0); |
466 | print_xml(result); | ||
467 | } else { | ||
468 | plist_dict_to_string(result); | ||
469 | } | ||
470 | } | 694 | } |
471 | } | 695 | } |
472 | node = plist_dict_get_item(result, "Error"); | 696 | node = plist_dict_get_item(result, "Error"); |
@@ -478,31 +702,58 @@ int main(int argc, char **argv) | |||
478 | free(error); | 702 | free(error); |
479 | } else { | 703 | } else { |
480 | printf("unexpected result:\n"); | 704 | printf("unexpected result:\n"); |
481 | if (xml_mode) { | 705 | plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0); |
482 | print_xml(result); | ||
483 | } else { | ||
484 | plist_dict_to_string(result); | ||
485 | } | ||
486 | } | 706 | } |
487 | 707 | node = plist_dict_get_item(result, "DetailedError"); | |
488 | } else { | 708 | if (node) { |
489 | if (xml_mode) { | 709 | printf("DetailedError: %s\n", plist_get_string_ptr(node, NULL)); |
490 | print_xml(result); | ||
491 | } else { | ||
492 | plist_dict_to_string(result); | ||
493 | } | 710 | } |
711 | } else { | ||
712 | plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0); | ||
494 | } | 713 | } |
495 | } | 714 | } |
496 | } else { | 715 | } else { |
497 | printf("Error: mount_image returned %d\n", err); | 716 | printf("Error: mount_image returned %d\n", err); |
498 | 717 | ||
499 | } | 718 | } |
719 | } else if (cmd == CMD_UNMOUNT) { | ||
720 | err = mobile_image_mounter_unmount_image(mim, argv[0]); | ||
721 | switch (err) { | ||
722 | case MOBILE_IMAGE_MOUNTER_E_SUCCESS: | ||
723 | printf("Success\n"); | ||
724 | res = 0; | ||
725 | break; | ||
726 | case MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED: | ||
727 | printf("Error: '%s' is not mounted\n", argv[0]); | ||
728 | res = 1; | ||
729 | break; | ||
730 | case MOBILE_IMAGE_MOUNTER_E_NOT_SUPPORTED: | ||
731 | printf("Error: 'unmount' is not supported on this device\n"); | ||
732 | res = 1; | ||
733 | break; | ||
734 | case MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED: | ||
735 | printf("Error: device is locked\n"); | ||
736 | res = 1; | ||
737 | break; | ||
738 | default: | ||
739 | printf("Error: unmount returned %d\n", err); | ||
740 | break; | ||
741 | } | ||
742 | } else if (cmd == CMD_DEVMODESTATUS) { | ||
743 | err = mobile_image_mounter_query_developer_mode_status(mim, &result); | ||
744 | if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
745 | res = 0; | ||
746 | plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0); | ||
747 | } else { | ||
748 | printf("Error: query_developer_mode_status returned %d\n", err); | ||
749 | } | ||
500 | } | 750 | } |
501 | 751 | ||
502 | if (result) { | 752 | if (result) { |
503 | plist_free(result); | 753 | plist_free(result); |
504 | } | 754 | } |
505 | 755 | ||
756 | error_out: | ||
506 | /* perform hangup command */ | 757 | /* perform hangup command */ |
507 | mobile_image_mounter_hangup(mim); | 758 | mobile_image_mounter_hangup(mim); |
508 | /* free client */ | 759 | /* free client */ |
@@ -518,7 +769,7 @@ leave: | |||
518 | idevice_free(device); | 769 | idevice_free(device); |
519 | 770 | ||
520 | if (image_path) | 771 | if (image_path) |
521 | free(image_path); | 772 | free(image_path); |
522 | if (image_sig_path) | 773 | if (image_sig_path) |
523 | free(image_sig_path); | 774 | free(image_sig_path); |
524 | 775 | ||