ideviceimagemounter.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. /*
  2. * ideviceimagemounter.c
  3. * Mount developer/debug disk images on the device
  4. *
  5. * Copyright (C) 2010 Nikias Bassen <nikias@gmx.li>
  6. *
  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.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this library; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. */
  21. #include <stdlib.h>
  22. #define _GNU_SOURCE 1
  23. #define __USE_GNU 1
  24. #include <stdio.h>
  25. #include <string.h>
  26. #include <getopt.h>
  27. #include <errno.h>
  28. #include <libgen.h>
  29. #include <time.h>
  30. #include <sys/time.h>
  31. #include <inttypes.h>
  32. #include <libimobiledevice/libimobiledevice.h>
  33. #include <libimobiledevice/lockdown.h>
  34. #include <libimobiledevice/afc.h>
  35. #include <libimobiledevice/notification_proxy.h>
  36. #include <libimobiledevice/mobile_image_mounter.h>
  37. #include <asprintf.h>
  38. #include "common/utils.h"
  39. static int list_mode = 0;
  40. static int xml_mode = 0;
  41. static char *udid = NULL;
  42. static char *imagetype = NULL;
  43. static const char PKG_PATH[] = "PublicStaging";
  44. static const char PATH_PREFIX[] = "/private/var/mobile/Media";
  45. typedef enum {
  46. DISK_IMAGE_UPLOAD_TYPE_AFC,
  47. DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE
  48. } disk_image_upload_type_t;
  49. static void print_usage(int argc, char **argv)
  50. {
  51. char *name = NULL;
  52. name = strrchr(argv[0], '/');
  53. printf("Usage: %s [OPTIONS] IMAGE_FILE IMAGE_SIGNATURE_FILE\n\n", (name ? name + 1: argv[0]));
  54. printf("Mounts the specified disk image on the device.\n\n");
  55. printf(" -u, --udid UDID\ttarget specific device by its 40-digit device UDID\n");
  56. printf(" -l, --list\t\tList mount information\n");
  57. printf(" -t, --imagetype\tImage type to use, default is 'Developer'\n");
  58. printf(" -x, --xml\t\tUse XML output\n");
  59. printf(" -d, --debug\t\tenable communication debugging\n");
  60. printf(" -h, --help\t\tprints usage information\n");
  61. printf("\n");
  62. printf("Homepage: <http://libimobiledevice.org>\n");
  63. }
  64. static void parse_opts(int argc, char **argv)
  65. {
  66. static struct option longopts[] = {
  67. {"help", 0, NULL, 'h'},
  68. {"udid", 0, NULL, 'u'},
  69. {"list", 0, NULL, 'l'},
  70. {"imagetype", 0, NULL, 't'},
  71. {"xml", 0, NULL, 'x'},
  72. {"debug", 0, NULL, 'd'},
  73. {NULL, 0, NULL, 0}
  74. };
  75. int c;
  76. while (1) {
  77. c = getopt_long(argc, argv, "hu:lt:xd", longopts,
  78. (int *) 0);
  79. if (c == -1) {
  80. break;
  81. }
  82. switch (c) {
  83. case 'h':
  84. print_usage(argc, argv);
  85. exit(0);
  86. case 'u':
  87. if (strlen(optarg) != 40) {
  88. printf("%s: invalid UDID specified (length != 40)\n",
  89. argv[0]);
  90. print_usage(argc, argv);
  91. exit(2);
  92. }
  93. udid = strdup(optarg);
  94. break;
  95. case 'l':
  96. list_mode = 1;
  97. break;
  98. case 't':
  99. imagetype = strdup(optarg);
  100. break;
  101. case 'x':
  102. xml_mode = 1;
  103. break;
  104. case 'd':
  105. idevice_set_debug_level(1);
  106. break;
  107. default:
  108. print_usage(argc, argv);
  109. exit(2);
  110. }
  111. }
  112. }
  113. static void print_xml(plist_t node)
  114. {
  115. char *xml = NULL;
  116. uint32_t len = 0;
  117. plist_to_xml(node, &xml, &len);
  118. if (xml)
  119. puts(xml);
  120. }
  121. static ssize_t mim_upload_cb(void* buf, size_t size, void* userdata)
  122. {
  123. return fread(buf, 1, size, (FILE*)userdata);
  124. }
  125. int main(int argc, char **argv)
  126. {
  127. idevice_t device = NULL;
  128. lockdownd_client_t lckd = NULL;
  129. lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
  130. mobile_image_mounter_client_t mim = NULL;
  131. afc_client_t afc = NULL;
  132. lockdownd_service_descriptor_t service = NULL;
  133. int res = -1;
  134. char *image_path = NULL;
  135. size_t image_size = 0;
  136. char *image_sig_path = NULL;
  137. parse_opts(argc, argv);
  138. argc -= optind;
  139. argv += optind;
  140. if (!list_mode) {
  141. if (argc < 1) {
  142. printf("ERROR: No IMAGE_FILE has been given!\n");
  143. return -1;
  144. }
  145. image_path = strdup(argv[0]);
  146. if (argc >= 2) {
  147. image_sig_path = strdup(argv[1]);
  148. } else {
  149. if (asprintf(&image_sig_path, "%s.signature", image_path) < 0) {
  150. printf("Out of memory?!\n");
  151. return -1;
  152. }
  153. }
  154. }
  155. if (IDEVICE_E_SUCCESS != idevice_new(&device, udid)) {
  156. printf("No device found, is it plugged in?\n");
  157. return -1;
  158. }
  159. if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lckd, "ideviceimagemounter"))) {
  160. printf("ERROR: Could not connect to lockdown, error code %d.\n", ldret);
  161. goto leave;
  162. }
  163. plist_t pver = NULL;
  164. char *product_version = NULL;
  165. lockdownd_get_value(lckd, NULL, "ProductVersion", &pver);
  166. if (pver && plist_get_node_type(pver) == PLIST_STRING) {
  167. plist_get_string_val(pver, &product_version);
  168. }
  169. disk_image_upload_type_t disk_image_upload_type = DISK_IMAGE_UPLOAD_TYPE_AFC;
  170. int product_version_major = 0;
  171. int product_version_minor = 0;
  172. if (product_version) {
  173. if (sscanf(product_version, "%d.%d.%*d", &product_version_major, &product_version_minor) == 2) {
  174. if (product_version_major >= 7)
  175. disk_image_upload_type = DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE;
  176. }
  177. }
  178. lockdownd_start_service(lckd, "com.apple.mobile.mobile_image_mounter", &service);
  179. if (!service || service->port == 0) {
  180. printf("ERROR: Could not start mobile_image_mounter service!\n");
  181. goto leave;
  182. }
  183. if (mobile_image_mounter_new(device, service, &mim) != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
  184. printf("ERROR: Could not connect to mobile_image_mounter!\n");
  185. goto leave;
  186. }
  187. if (service) {
  188. lockdownd_service_descriptor_free(service);
  189. service = NULL;
  190. }
  191. if (!list_mode) {
  192. struct stat fst;
  193. if (disk_image_upload_type == DISK_IMAGE_UPLOAD_TYPE_AFC) {
  194. if ((lockdownd_start_service(lckd, "com.apple.afc", &service) !=
  195. LOCKDOWN_E_SUCCESS) || !service || !service->port) {
  196. fprintf(stderr, "Could not start com.apple.afc!\n");
  197. goto leave;
  198. }
  199. if (afc_client_new(device, service, &afc) != AFC_E_SUCCESS) {
  200. fprintf(stderr, "Could not connect to AFC!\n");
  201. goto leave;
  202. }
  203. if (service) {
  204. lockdownd_service_descriptor_free(service);
  205. service = NULL;
  206. }
  207. }
  208. if (stat(image_path, &fst) != 0) {
  209. fprintf(stderr, "ERROR: stat: %s: %s\n", image_path, strerror(errno));
  210. goto leave;
  211. }
  212. image_size = fst.st_size;
  213. if (stat(image_sig_path, &fst) != 0) {
  214. fprintf(stderr, "ERROR: stat: %s: %s\n", image_sig_path, strerror(errno));
  215. goto leave;
  216. }
  217. }
  218. lockdownd_client_free(lckd);
  219. lckd = NULL;
  220. mobile_image_mounter_error_t err;
  221. plist_t result = NULL;
  222. if (list_mode) {
  223. /* list mounts mode */
  224. if (!imagetype) {
  225. imagetype = strdup("Developer");
  226. }
  227. err = mobile_image_mounter_lookup_image(mim, imagetype, &result);
  228. free(imagetype);
  229. if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
  230. res = 0;
  231. if (xml_mode) {
  232. print_xml(result);
  233. } else {
  234. plist_print_to_stream(result, stdout);
  235. }
  236. } else {
  237. printf("Error: lookup_image returned %d\n", err);
  238. }
  239. } else {
  240. char sig[8192];
  241. size_t sig_length = 0;
  242. FILE *f = fopen(image_sig_path, "rb");
  243. if (!f) {
  244. fprintf(stderr, "Error opening signature file '%s': %s\n", image_sig_path, strerror(errno));
  245. goto leave;
  246. }
  247. sig_length = fread(sig, 1, sizeof(sig), f);
  248. fclose(f);
  249. if (sig_length == 0) {
  250. fprintf(stderr, "Could not read signature from file '%s'\n", image_sig_path);
  251. goto leave;
  252. }
  253. f = fopen(image_path, "rb");
  254. if (!f) {
  255. fprintf(stderr, "Error opening image file '%s': %s\n", image_path, strerror(errno));
  256. goto leave;
  257. }
  258. char *targetname = NULL;
  259. if (asprintf(&targetname, "%s/%s", PKG_PATH, "staging.dimage") < 0) {
  260. fprintf(stderr, "Out of memory!?\n");
  261. goto leave;
  262. }
  263. char *mountname = NULL;
  264. if (asprintf(&mountname, "%s/%s", PATH_PREFIX, targetname) < 0) {
  265. fprintf(stderr, "Out of memory!?\n");
  266. goto leave;
  267. }
  268. if (!imagetype) {
  269. imagetype = strdup("Developer");
  270. }
  271. switch(disk_image_upload_type) {
  272. case DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE:
  273. printf("Uploading %s\n", image_path);
  274. err = mobile_image_mounter_upload_image(mim, imagetype, image_size, sig, sig_length, mim_upload_cb, f);
  275. break;
  276. case DISK_IMAGE_UPLOAD_TYPE_AFC:
  277. default:
  278. printf("Uploading %s --> afc:///%s\n", image_path, targetname);
  279. char **strs = NULL;
  280. if (afc_get_file_info(afc, PKG_PATH, &strs) != AFC_E_SUCCESS) {
  281. if (afc_make_directory(afc, PKG_PATH) != AFC_E_SUCCESS) {
  282. fprintf(stderr, "WARNING: Could not create directory '%s' on device!\n", PKG_PATH);
  283. }
  284. }
  285. if (strs) {
  286. int i = 0;
  287. while (strs[i]) {
  288. free(strs[i]);
  289. i++;
  290. }
  291. free(strs);
  292. }
  293. uint64_t af = 0;
  294. if ((afc_file_open(afc, targetname, AFC_FOPEN_WRONLY, &af) !=
  295. AFC_E_SUCCESS) || !af) {
  296. fclose(f);
  297. fprintf(stderr, "afc_file_open on '%s' failed!\n", targetname);
  298. goto leave;
  299. }
  300. char buf[8192];
  301. size_t amount = 0;
  302. do {
  303. amount = fread(buf, 1, sizeof(buf), f);
  304. if (amount > 0) {
  305. uint32_t written, total = 0;
  306. while (total < amount) {
  307. written = 0;
  308. if (afc_file_write(afc, af, buf, amount, &written) !=
  309. AFC_E_SUCCESS) {
  310. fprintf(stderr, "AFC Write error!\n");
  311. break;
  312. }
  313. total += written;
  314. }
  315. if (total != amount) {
  316. fprintf(stderr, "Error: wrote only %d of %d\n", total,
  317. (unsigned int)amount);
  318. afc_file_close(afc, af);
  319. fclose(f);
  320. goto leave;
  321. }
  322. }
  323. }
  324. while (amount > 0);
  325. afc_file_close(afc, af);
  326. break;
  327. }
  328. fclose(f);
  329. printf("done.\n");
  330. printf("Mounting...\n");
  331. err = mobile_image_mounter_mount_image(mim, mountname, sig, sig_length, imagetype, &result);
  332. free(imagetype);
  333. if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
  334. if (result) {
  335. plist_t node = plist_dict_get_item(result, "Status");
  336. if (node) {
  337. char *status = NULL;
  338. plist_get_string_val(node, &status);
  339. if (status) {
  340. if (!strcmp(status, "Complete")) {
  341. printf("Done.\n");
  342. res = 0;
  343. } else {
  344. printf("unexpected status value:\n");
  345. if (xml_mode) {
  346. print_xml(result);
  347. } else {
  348. plist_print_to_stream(result, stdout);
  349. }
  350. }
  351. free(status);
  352. } else {
  353. printf("unexpected result:\n");
  354. if (xml_mode) {
  355. print_xml(result);
  356. } else {
  357. plist_print_to_stream(result, stdout);
  358. }
  359. }
  360. }
  361. node = plist_dict_get_item(result, "Error");
  362. if (node) {
  363. char *error = NULL;
  364. plist_get_string_val(node, &error);
  365. if (error) {
  366. printf("Error: %s\n", error);
  367. free(error);
  368. } else {
  369. printf("unexpected result:\n");
  370. if (xml_mode) {
  371. print_xml(result);
  372. } else {
  373. plist_print_to_stream(result, stdout);
  374. }
  375. }
  376. } else {
  377. if (xml_mode) {
  378. print_xml(result);
  379. } else {
  380. plist_print_to_stream(result, stdout);
  381. }
  382. }
  383. }
  384. } else {
  385. printf("Error: mount_image returned %d\n", err);
  386. }
  387. }
  388. if (result) {
  389. plist_free(result);
  390. }
  391. /* perform hangup command */
  392. mobile_image_mounter_hangup(mim);
  393. /* free client */
  394. mobile_image_mounter_free(mim);
  395. leave:
  396. if (afc) {
  397. afc_client_free(afc);
  398. }
  399. if (lckd) {
  400. lockdownd_client_free(lckd);
  401. }
  402. idevice_free(device);
  403. if (image_path)
  404. free(image_path);
  405. if (image_sig_path)
  406. free(image_sig_path);
  407. return res;
  408. }