1 /*
2 * ifuse.c
3 * A Fuse filesystem which exposes the iPhone's filesystem.
4 *
5 * Copyright (c) 2008 Matt Colyer All Rights Reserved.
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
22 #define FUSE_USE_VERSION 26
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif /* HAVE_CONFIG_H */
27
28 #include <fuse.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <unistd.h>
34 #include <stdint.h>
35 #include <stdlib.h>
36
37 #define AFC_SERVICE_NAME "com.apple.afc"
38 #define AFC2_SERVICE_NAME "com.apple.afc2"
39 #define HOUSE_ARREST_SERVICE_NAME "com.apple.mobile.house_arrest"
40
41 #include <libimobiledevice/libimobiledevice.h>
42 #include <libimobiledevice/lockdown.h>
43 #include <libimobiledevice/afc.h>
44 #include <libimobiledevice/house_arrest.h>
45 #include <libimobiledevice/installation_proxy.h>
46
47 /* FreeBSD and others don't have ENODATA, so let's fake it */
48 #ifndef ENODATA
49 #define ENODATA EIO
50 #endif
51
52 house_arrest_client_t house_arrest = NULL;
53
54 /* assume this is the default block size */
55 int g_blocksize = 4096;
56
57 idevice_t device = NULL;
58 lockdownd_client_t control = NULL;
59
60 int debug = 0;
61
62 static struct {
63 char *mount_point;
64 char *device_udid;
65 char *appid;
66 int use_container;
67 int should_list_apps;
68 char *service_name;
69 lockdownd_service_descriptor_t service;
70 int use_network;
71 } opts;
72
73 enum {
74 KEY_HELP = 1,
75 KEY_VERSION,
76 KEY_ROOT,
77 KEY_UDID,
78 KEY_UDID_LONG,
79 KEY_NETWORK,
80 KEY_NETWORK_LONG,
81 KEY_VENDOR_DOCUMENTS_LONG,
82 KEY_VENDOR_CONTAINER_LONG,
83 KEY_LIST_APPS_LONG,
84 KEY_DEBUG,
85 KEY_DEBUG_LONG
86 };
87
88 static struct fuse_opt ifuse_opts[] = {
89 FUSE_OPT_KEY("-V", KEY_VERSION),
90 FUSE_OPT_KEY("--version", KEY_VERSION),
91 FUSE_OPT_KEY("-h", KEY_HELP),
92 FUSE_OPT_KEY("--help", KEY_HELP),
93 FUSE_OPT_KEY("-u %s", KEY_UDID),
94 FUSE_OPT_KEY("--udid %s", KEY_UDID_LONG),
95 FUSE_OPT_KEY("-n", KEY_NETWORK),
96 FUSE_OPT_KEY("--network", KEY_NETWORK_LONG),
97 FUSE_OPT_KEY("--root", KEY_ROOT),
98 FUSE_OPT_KEY("-d", KEY_DEBUG),
99 FUSE_OPT_KEY("--debug", KEY_DEBUG_LONG),
100 FUSE_OPT_KEY("--documents %s", KEY_VENDOR_DOCUMENTS_LONG),
101 FUSE_OPT_KEY("--container %s", KEY_VENDOR_CONTAINER_LONG),
102 FUSE_OPT_KEY("--list-apps", KEY_LIST_APPS_LONG),
103 FUSE_OPT_END
104 };
105
free_dictionary(char ** dictionary)106 static void free_dictionary(char **dictionary)
107 {
108 int i = 0;
109
110 if (!dictionary)
111 return;
112
113 for (i = 0; dictionary[i]; i++) {
114 free(dictionary[i]);
115 }
116 free(dictionary);
117 }
118
119 struct afc_error_mapping {
120 afc_error_t from;
121 int to;
122 } static afc_error_to_errno_map[] = {
123 {AFC_E_SUCCESS , 0},
124 {AFC_E_OP_HEADER_INVALID , EIO},
125 {AFC_E_NO_RESOURCES , EMFILE},
126 {AFC_E_READ_ERROR , ENOTDIR},
127 {AFC_E_WRITE_ERROR , EIO},
128 {AFC_E_UNKNOWN_PACKET_TYPE , EIO},
129 {AFC_E_INVALID_ARG , EINVAL},
130 {AFC_E_OBJECT_NOT_FOUND , ENOENT},
131 {AFC_E_OBJECT_IS_DIR , EISDIR},
132 {AFC_E_DIR_NOT_EMPTY , ENOTEMPTY},
133 {AFC_E_PERM_DENIED , EPERM},
134 {AFC_E_SERVICE_NOT_CONNECTED , ENXIO},
135 {AFC_E_OP_TIMEOUT , ETIMEDOUT},
136 {AFC_E_TOO_MUCH_DATA , EFBIG},
137 {AFC_E_END_OF_DATA , ENODATA},
138 {AFC_E_OP_NOT_SUPPORTED , ENOSYS},
139 {AFC_E_OBJECT_EXISTS , EEXIST},
140 {AFC_E_OBJECT_BUSY , EBUSY},
141 {AFC_E_NO_SPACE_LEFT , ENOSPC},
142 {AFC_E_OP_WOULD_BLOCK , EWOULDBLOCK},
143 {AFC_E_IO_ERROR , EIO},
144 {AFC_E_OP_INTERRUPTED , EINTR},
145 {AFC_E_OP_IN_PROGRESS , EALREADY},
146 {AFC_E_INTERNAL_ERROR , EIO},
147 {-1}
148 };
149
150 /**
151 * Tries to convert the AFC error value into a meaningful errno value.
152 *
153 * @param client AFC client to retrieve status value from.
154 *
155 * @return errno value.
156 */
get_afc_error_as_errno(afc_error_t error)157 static int get_afc_error_as_errno(afc_error_t error)
158 {
159 int i = 0;
160 int res = -1;
161
162 while (afc_error_to_errno_map[i++].from != -1) {
163 if (afc_error_to_errno_map[i].from == error) {
164 res = afc_error_to_errno_map[i++].to;
165 break;
166 }
167 }
168
169 if (res == -1) {
170 fprintf(stderr, "Unknown AFC status %d.\n", error);
171 res = EIO;
172 }
173
174 return res;
175 }
176
get_afc_file_mode(afc_file_mode_t * afc_mode,int flags)177 static int get_afc_file_mode(afc_file_mode_t *afc_mode, int flags)
178 {
179 switch (flags & O_ACCMODE) {
180 case O_RDONLY:
181 *afc_mode = AFC_FOPEN_RDONLY;
182 break;
183 case O_WRONLY:
184 if ((flags & O_TRUNC) == O_TRUNC) {
185 *afc_mode = AFC_FOPEN_WRONLY;
186 } else if ((flags & O_APPEND) == O_APPEND) {
187 *afc_mode = AFC_FOPEN_APPEND;
188 } else {
189 *afc_mode = AFC_FOPEN_RW;
190 }
191 break;
192 case O_RDWR:
193 if ((flags & O_TRUNC) == O_TRUNC) {
194 *afc_mode = AFC_FOPEN_WR;
195 } else if ((flags & O_APPEND) == O_APPEND) {
196 *afc_mode = AFC_FOPEN_RDAPPEND;
197 } else {
198 *afc_mode = AFC_FOPEN_RW;
199 }
200 break;
201 default:
202 *afc_mode = 0;
203 return -1;
204 }
205 return 0;
206 }
207
ifuse_getattr(const char * path,struct stat * stbuf)208 static int ifuse_getattr(const char *path, struct stat *stbuf)
209 {
210 int i;
211 int res = 0;
212 char **info = NULL;
213
214 afc_client_t afc = fuse_get_context()->private_data;
215 afc_error_t ret = afc_get_file_info(afc, path, &info);
216
217 memset(stbuf, 0, sizeof(struct stat));
218 if (ret != AFC_E_SUCCESS) {
219 int e = get_afc_error_as_errno(ret);
220 res = -e;
221 } else if (!info) {
222 res = -1;
223 } else {
224 // get file attributes from info list
225 for (i = 0; info[i]; i += 2) {
226 if (!strcmp(info[i], "st_size")) {
227 stbuf->st_size = atoll(info[i+1]);
228 } else if (!strcmp(info[i], "st_blocks")) {
229 stbuf->st_blocks = atoi(info[i+1]);
230 } else if (!strcmp(info[i], "st_ifmt")) {
231 if (!strcmp(info[i+1], "S_IFREG")) {
232 stbuf->st_mode = S_IFREG;
233 } else if (!strcmp(info[i+1], "S_IFDIR")) {
234 stbuf->st_mode = S_IFDIR;
235 } else if (!strcmp(info[i+1], "S_IFLNK")) {
236 stbuf->st_mode = S_IFLNK;
237 } else if (!strcmp(info[i+1], "S_IFBLK")) {
238 stbuf->st_mode = S_IFBLK;
239 } else if (!strcmp(info[i+1], "S_IFCHR")) {
240 stbuf->st_mode = S_IFCHR;
241 } else if (!strcmp(info[i+1], "S_IFIFO")) {
242 stbuf->st_mode = S_IFIFO;
243 } else if (!strcmp(info[i+1], "S_IFSOCK")) {
244 stbuf->st_mode = S_IFSOCK;
245 }
246 } else if (!strcmp(info[i], "st_nlink")) {
247 stbuf->st_nlink = atoi(info[i+1]);
248 } else if (!strcmp(info[i], "st_mtime")) {
249 stbuf->st_mtime = (time_t)(atoll(info[i+1]) / 1000000000);
250 }
251 #ifdef _DARWIN_FEATURE_64_BIT_INODE
252 else if (!strcmp(info[i], "st_birthtime")) { /* available on iOS 7+ */
253 stbuf->st_birthtime = (time_t)(atoll(info[i+1]) / 1000000000);
254 }
255 #endif
256 }
257 free_dictionary(info);
258
259 // set permission bits according to the file type
260 if (S_ISDIR(stbuf->st_mode)) {
261 stbuf->st_mode |= 0755;
262 } else if (S_ISLNK(stbuf->st_mode)) {
263 stbuf->st_mode |= 0777;
264 } else {
265 stbuf->st_mode |= 0644;
266 }
267
268 // and set some additional info
269 stbuf->st_uid = getuid();
270 stbuf->st_gid = getgid();
271
272 stbuf->st_blksize = g_blocksize;
273 }
274
275 return res;
276 }
277
ifuse_readdir(const char * path,void * buf,fuse_fill_dir_t filler,off_t offset,struct fuse_file_info * fi)278 static int ifuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi)
279 {
280 int i;
281 char **dirs = NULL;
282 afc_client_t afc = fuse_get_context()->private_data;
283
284 afc_read_directory(afc, path, &dirs);
285
286 if (!dirs)
287 return -ENOENT;
288
289 for (i = 0; dirs[i]; i++) {
290 filler(buf, dirs[i], NULL, 0);
291 }
292
293 free_dictionary(dirs);
294
295 return 0;
296 }
297
ifuse_open(const char * path,struct fuse_file_info * fi)298 static int ifuse_open(const char *path, struct fuse_file_info *fi)
299 {
300 afc_client_t afc = fuse_get_context()->private_data;
301 afc_error_t err;
302 afc_file_mode_t mode = 0;
303
304 err = get_afc_file_mode(&mode, fi->flags);
305 if (err != AFC_E_SUCCESS || (mode == 0)) {
306 return -EPERM;
307 }
308
309 err = afc_file_open(afc, path, mode, &fi->fh);
310 if (err != AFC_E_SUCCESS) {
311 int res = get_afc_error_as_errno(err);
312 return -res;
313 }
314
315 return 0;
316 }
317
ifuse_create(const char * path,mode_t mode,struct fuse_file_info * fi)318 static int ifuse_create(const char *path, mode_t mode, struct fuse_file_info *fi)
319 {
320 return ifuse_open(path, fi);
321 }
322
ifuse_read(const char * path,char * buf,size_t size,off_t offset,struct fuse_file_info * fi)323 static int ifuse_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi)
324 {
325 uint32_t bytes = 0;
326 afc_client_t afc = fuse_get_context()->private_data;
327
328 if (size == 0)
329 return 0;
330
331 afc_error_t err = afc_file_seek(afc, fi->fh, offset, SEEK_SET);
332 if (err != AFC_E_SUCCESS) {
333 int res = get_afc_error_as_errno(err);
334 return -res;
335 }
336
337 err = afc_file_read(afc, fi->fh, buf, size, &bytes);
338 if (err != AFC_E_SUCCESS) {
339 int res = get_afc_error_as_errno(err);
340 return -res;
341 }
342
343 return bytes;
344 }
345
ifuse_write(const char * path,const char * buf,size_t size,off_t offset,struct fuse_file_info * fi)346 static int ifuse_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi)
347 {
348 uint32_t bytes = 0;
349 afc_client_t afc = fuse_get_context()->private_data;
350
351 if (size == 0)
352 return 0;
353
354 afc_error_t err = afc_file_seek(afc, fi->fh, offset, SEEK_SET);
355 if (err != AFC_E_SUCCESS) {
356 int res = get_afc_error_as_errno(err);
357 return -res;
358 }
359
360 err = afc_file_write(afc, fi->fh, buf, size, &bytes);
361 if (err != AFC_E_SUCCESS) {
362 int res = get_afc_error_as_errno(err);
363 return -res;
364 }
365
366 return bytes;
367 }
368
ifuse_utimens(const char * path,const struct timespec tv[2])369 static int ifuse_utimens(const char *path, const struct timespec tv[2])
370 {
371 afc_client_t afc = fuse_get_context()->private_data;
372 uint64_t mtime = (uint64_t)tv[1].tv_sec * (uint64_t)1000000000 + (uint64_t)tv[1].tv_nsec;
373
374 afc_error_t err = afc_set_file_time(afc, path, mtime);
375 if (err == AFC_E_UNKNOWN_PACKET_TYPE) {
376 /* ignore error for pre-3.1 devices as they do not support setting file modification times */
377 return 0;
378 }
379 if (err != AFC_E_SUCCESS) {
380 int res = get_afc_error_as_errno(err);
381 return -res;
382 }
383
384 return 0;
385 }
386
ifuse_fsync(const char * path,int datasync,struct fuse_file_info * fi)387 static int ifuse_fsync(const char *path, int datasync, struct fuse_file_info *fi)
388 {
389 return 0;
390 }
391
ifuse_release(const char * path,struct fuse_file_info * fi)392 static int ifuse_release(const char *path, struct fuse_file_info *fi)
393 {
394 afc_client_t afc = fuse_get_context()->private_data;
395
396 afc_file_close(afc, fi->fh);
397
398 return 0;
399 }
400
ifuse_init(struct fuse_conn_info * conn)401 void *ifuse_init(struct fuse_conn_info *conn)
402 {
403 afc_client_t afc = NULL;
404
405 conn->async_read = 0;
406
407 if (house_arrest) {
408 afc_client_new_from_house_arrest_client(house_arrest, &afc);
409 } else {
410 afc_client_new(device, opts.service, &afc);
411 }
412
413 lockdownd_client_free(control);
414 control = NULL;
415
416 if (afc) {
417 // get file system block size
418 int i;
419 char **info_raw = NULL;
420 if ((AFC_E_SUCCESS == afc_get_device_info(afc, &info_raw)) && info_raw) {
421 for (i = 0; info_raw[i]; i+=2) {
422 if (!strcmp(info_raw[i], "FSBlockSize")) {
423 g_blocksize = atoi(info_raw[i + 1]);
424 break;
425 }
426 }
427 free_dictionary(info_raw);
428 }
429 }
430
431 return afc;
432 }
433
ifuse_cleanup(void * data)434 void ifuse_cleanup(void *data)
435 {
436 afc_client_t afc = (afc_client_t) data;
437
438 afc_client_free(afc);
439 if (control) {
440 lockdownd_client_free(control);
441 }
442 idevice_free(device);
443 }
444
ifuse_flush(const char * path,struct fuse_file_info * fi)445 int ifuse_flush(const char *path, struct fuse_file_info *fi)
446 {
447 return 0;
448 }
449
ifuse_statfs(const char * path,struct statvfs * stats)450 int ifuse_statfs(const char *path, struct statvfs *stats)
451 {
452 afc_client_t afc = fuse_get_context()->private_data;
453 char **info_raw = NULL;
454 uint64_t totalspace = 0, freespace = 0;
455 int i = 0, blocksize = 0;
456
457 afc_error_t err = afc_get_device_info(afc, &info_raw);
458 if (err != AFC_E_SUCCESS) {
459 int res = get_afc_error_as_errno(err);
460 return -res;
461 }
462 if (!info_raw)
463 return -ENOENT;
464
465 for (i = 0; info_raw[i]; i++) {
466 if (!strcmp(info_raw[i], "FSTotalBytes")) {
467 totalspace = strtoull(info_raw[i + 1], (char **) NULL, 10);
468 } else if (!strcmp(info_raw[i], "FSFreeBytes")) {
469 freespace = strtoull(info_raw[i + 1], (char **) NULL, 10);
470 } else if (!strcmp(info_raw[i], "FSBlockSize")) {
471 blocksize = atoi(info_raw[i + 1]);
472 }
473 }
474 free_dictionary(info_raw);
475
476 stats->f_bsize = stats->f_frsize = blocksize;
477 stats->f_blocks = totalspace / blocksize;
478 stats->f_bfree = stats->f_bavail = freespace / blocksize;
479 stats->f_namemax = 255;
480 stats->f_files = stats->f_ffree = 1000000000;
481
482 return 0;
483 }
484
ifuse_truncate(const char * path,off_t size)485 int ifuse_truncate(const char *path, off_t size)
486 {
487 afc_client_t afc = fuse_get_context()->private_data;
488 afc_error_t err = afc_truncate(afc, path, size);
489 if (err != AFC_E_SUCCESS) {
490 int res = get_afc_error_as_errno(err);
491 return -res;
492 }
493 return 0;
494 }
495
ifuse_ftruncate(const char * path,off_t size,struct fuse_file_info * fi)496 int ifuse_ftruncate(const char *path, off_t size, struct fuse_file_info *fi)
497 {
498 afc_client_t afc = fuse_get_context()->private_data;
499
500 afc_error_t err = afc_file_truncate(afc, fi->fh, size);
501 if (err != AFC_E_SUCCESS) {
502 int res = get_afc_error_as_errno(err);
503 return -res;
504 }
505 return 0;
506 }
507
ifuse_readlink(const char * path,char * linktarget,size_t buflen)508 int ifuse_readlink(const char *path, char *linktarget, size_t buflen)
509 {
510 int i, ret;
511 char **info = NULL;
512 if (!path || !linktarget || (buflen == 0)) {
513 return -EINVAL;
514 }
515 linktarget[0] = '\0'; // in case the link target cannot be determined
516 afc_client_t afc = fuse_get_context()->private_data;
517 afc_error_t err = afc_get_file_info(afc, path, &info);
518 if ((err == AFC_E_SUCCESS) && info) {
519 ret = -1;
520 for (i = 0; info[i]; i+=2) {
521 if (!strcmp(info[i], "LinkTarget")) {
522 strncpy(linktarget, info[i+1], buflen-1);
523 linktarget[buflen-1] = '\0';
524 ret = 0;
525 }
526 }
527 free_dictionary(info);
528 } else {
529 ret = get_afc_error_as_errno(err);
530 return -ret;
531 }
532
533 return ret;
534 }
535
ifuse_symlink(const char * target,const char * linkname)536 int ifuse_symlink(const char *target, const char *linkname)
537 {
538 afc_client_t afc = fuse_get_context()->private_data;
539
540 afc_error_t err = afc_make_link(afc, AFC_SYMLINK, target, linkname);
541 if (err == AFC_E_SUCCESS)
542 return 0;
543
544 return -get_afc_error_as_errno(err);
545 }
546
ifuse_link(const char * target,const char * linkname)547 int ifuse_link(const char *target, const char *linkname)
548 {
549 afc_client_t afc = fuse_get_context()->private_data;
550
551 afc_error_t err = afc_make_link(afc, AFC_HARDLINK, target, linkname);
552 if (err == AFC_E_SUCCESS)
553 return 0;
554
555 return -get_afc_error_as_errno(err);
556 }
557
ifuse_unlink(const char * path)558 int ifuse_unlink(const char *path)
559 {
560 afc_client_t afc = fuse_get_context()->private_data;
561
562 afc_error_t err = afc_remove_path(afc, path);
563 if (err == AFC_E_SUCCESS)
564 return 0;
565
566 return -get_afc_error_as_errno(err);
567 }
568
ifuse_rename(const char * from,const char * to)569 int ifuse_rename(const char *from, const char *to)
570 {
571 afc_client_t afc = fuse_get_context()->private_data;
572
573 afc_error_t err = afc_rename_path(afc, from, to);
574 if (err == AFC_E_SUCCESS)
575 return 0;
576
577 return -get_afc_error_as_errno(err);
578 }
579
ifuse_mkdir(const char * dir,mode_t ignored)580 int ifuse_mkdir(const char *dir, mode_t ignored)
581 {
582 afc_client_t afc = fuse_get_context()->private_data;
583
584 afc_error_t err = afc_make_directory(afc, dir);
585 if (err == AFC_E_SUCCESS)
586 return 0;
587
588 return -get_afc_error_as_errno(err);
589 }
590
591 static struct fuse_operations ifuse_oper = {
592 .getattr = ifuse_getattr,
593 .statfs = ifuse_statfs,
594 .readdir = ifuse_readdir,
595 .mkdir = ifuse_mkdir,
596 .rmdir = ifuse_unlink,
597 .create = ifuse_create,
598 .open = ifuse_open,
599 .read = ifuse_read,
600 .write = ifuse_write,
601 .truncate = ifuse_truncate,
602 .ftruncate = ifuse_ftruncate,
603 .readlink = ifuse_readlink,
604 .symlink = ifuse_symlink,
605 .link = ifuse_link,
606 .unlink = ifuse_unlink,
607 .rename = ifuse_rename,
608 .utimens = ifuse_utimens,
609 .fsync = ifuse_fsync,
610 .release = ifuse_release,
611 .init = ifuse_init,
612 .destroy = ifuse_cleanup
613 };
614
print_usage()615 static void print_usage()
616 {
617 fprintf(stderr, "Usage: " PACKAGE_NAME " MOUNTPOINT [OPTIONS]\n");
618 fprintf(stderr, "\n");
619 fprintf(stderr, "Mount directories of an iOS device locally using fuse.\n");
620 fprintf(stderr, "\n");
621 fprintf(stderr, "OPTIONS:\n");
622 fprintf(stderr, " -o opt,[opt...]\tmount options\n");
623 fprintf(stderr, " -u, --udid UDID\tmount specific device by UDID\n");
624 fprintf(stderr, " -n, --network\t\tconnect to network device\n");
625 fprintf(stderr, " -h, --help\t\tprint usage information\n");
626 fprintf(stderr, " -V, --version\t\tprint version\n");
627 fprintf(stderr, " -d, --debug\t\tenable libimobiledevice communication debugging\n");
628 fprintf(stderr, " --documents APPID\tmount 'Documents' folder of app identified by APPID\n");
629 fprintf(stderr, " --container APPID\tmount sandbox root of an app identified by APPID\n");
630 fprintf(stderr, " --list-apps\t\tlist installed apps that have file sharing enabled\n");
631 fprintf(stderr, " --root\t\tmount root file system (jailbroken device required)\n");
632 fprintf(stderr, "\n");
633 fprintf(stderr, "Example:\n");
634 fprintf(stderr, "\n");
635 fprintf(stderr, " $ ifuse /media/iPhone --root\n\n");
636 fprintf(stderr, " This mounts the root filesystem of the first attached device on\n");
637 fprintf(stderr, " this computer in the directory /media/iPhone.\n");
638 fprintf(stderr, "\n");
639 fprintf(stderr, "Homepage: <" PACKAGE_URL ">\n");
640 fprintf(stderr, "Bug Reports: <" PACKAGE_BUGREPORT ">\n");
641 }
642
ifuse_opt_proc(void * data,const char * arg,int key,struct fuse_args * outargs)643 static int ifuse_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs)
644 {
645 static int option_num = 0;
646 int res = 1;
647
648 switch (key) {
649 case KEY_UDID_LONG:
650 opts.device_udid = strdup(arg+6);
651 res = 0;
652 break;
653 case KEY_UDID:
654 opts.device_udid = strdup(arg+2);
655 res = 0;
656 break;
657 case KEY_NETWORK:
658 case KEY_NETWORK_LONG:
659 opts.use_network = 1;
660 res = 0;
661 break;
662 case KEY_VENDOR_CONTAINER_LONG:
663 opts.use_container = 1;
664 opts.appid = strdup(arg+11);
665 opts.service_name = HOUSE_ARREST_SERVICE_NAME;
666 res = 0;
667 break;
668 case KEY_VENDOR_DOCUMENTS_LONG:
669 opts.appid = strdup(arg+11);
670 opts.service_name = HOUSE_ARREST_SERVICE_NAME;
671 res = 0;
672 break;
673 case KEY_DEBUG:
674 case KEY_DEBUG_LONG:
675 idevice_set_debug_level(1);
676 res = 0;
677 break;
678 case KEY_ROOT:
679 opts.service_name = AFC2_SERVICE_NAME;
680 res = 0;
681 break;
682 case KEY_HELP:
683 print_usage();
684 exit(EXIT_SUCCESS);
685 case KEY_VERSION:
686 fprintf(stderr, "%s %s\n", PACKAGE_NAME, PACKAGE_VERSION);
687 exit(EXIT_SUCCESS);
688 case KEY_LIST_APPS_LONG:
689 opts.should_list_apps = 1;
690 break;
691 case FUSE_OPT_KEY_OPT:
692 /* ignore other options and pass them to fuse_main later */
693 break;
694 case FUSE_OPT_KEY_NONOPT:
695 if(option_num == 0) {
696 opts.mount_point = strdup(arg);
697 }
698 if(option_num == 1) {
699 /* compatibility to older style which passed a device file option first */
700 free(opts.mount_point);
701 opts.mount_point = strdup(arg);
702 }
703 option_num++;
704 break;
705 }
706 return res;
707 }
708
list_available_apps(idevice_t dev)709 static void list_available_apps(idevice_t dev)
710 {
711 instproxy_client_t ip = NULL;
712 if (instproxy_client_start_service(dev, &ip, "ifuse") != INSTPROXY_E_SUCCESS) {
713 fprintf(stderr, "ERROR: Couldn't connect to installation proxy on device\n");
714 goto leave_cleanup;
715 }
716
717 plist_t client_opts = instproxy_client_options_new();
718 instproxy_client_options_add(client_opts, "ApplicationType", "Any", NULL);
719 instproxy_client_options_set_return_attributes(client_opts,
720 "CFBundleIdentifier",
721 "CFBundleDisplayName",
722 "CFBundleVersion",
723 "UIFileSharingEnabled",
724 NULL
725 );
726
727 plist_t apps = NULL;
728 instproxy_browse(ip, client_opts, &apps);
729
730 if (!apps || (plist_get_node_type(apps) != PLIST_ARRAY)) {
731 fprintf(stderr, "ERROR: instproxy_browse returned an invalid plist?!\n");
732 goto leave_cleanup;
733 }
734
735 /* output colum titles */
736 printf("\"%s\",\"%s\",\"%s\"\n", "CFBundleIdentifier", "CFBundleVersion", "CFBundleDisplayName");
737
738 /* output rows with app information */
739 uint32_t i = 0;
740 for (i = 0; i < plist_array_get_size(apps); i++) {
741 plist_t node = plist_array_get_item(apps, i);
742 if (node && plist_get_node_type(node) == PLIST_DICT) {
743 uint8_t sharing_enabled = 0;
744 plist_t val = plist_dict_get_item(node, "UIFileSharingEnabled");
745 if (val && plist_get_node_type(val) == PLIST_BOOLEAN) {
746 plist_get_bool_val(val, &sharing_enabled);
747 }
748 if (sharing_enabled) {
749 char *bid = NULL;
750 char *ver = NULL;
751 char *name = NULL;
752 val = plist_dict_get_item(node, "CFBundleIdentifier");
753 if (val) {
754 plist_get_string_val(val, &bid);
755 }
756 val = plist_dict_get_item(node, "CFBundleVersion");
757 if (val) {
758 plist_get_string_val(val, &ver);
759 }
760 val = plist_dict_get_item(node, "CFBundleDisplayName");
761 if (val) {
762 plist_get_string_val(val, &name);
763 }
764 printf("\"%s\",\"%s\",\"%s\"\n", bid, ver, name);
765 free(bid);
766 free(ver);
767 free(name);
768 }
769 }
770 }
771 plist_free(apps);
772
773 leave_cleanup:
774 instproxy_client_free(ip);
775 idevice_free(dev);
776 }
777
main(int argc,char * argv[])778 int main(int argc, char *argv[])
779 {
780 int res = EXIT_FAILURE;
781 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
782 struct stat mst;
783 idevice_error_t err = IDEVICE_E_UNKNOWN_ERROR;
784 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
785 enum idevice_options lookup_opts = IDEVICE_LOOKUP_USBMUX | IDEVICE_LOOKUP_NETWORK;
786
787 memset(&opts, 0, sizeof(opts));
788 opts.service_name = AFC_SERVICE_NAME;
789
790 if (fuse_opt_parse(&args, NULL, ifuse_opts, ifuse_opt_proc) == -1) {
791 return EXIT_FAILURE;
792 }
793
794 if (opts.device_udid && !*opts.device_udid) {
795 fprintf(stderr, "ERROR: UDID must not be empty\n");
796 return EXIT_FAILURE;
797 }
798
799 if (!opts.should_list_apps) {
800 if (!opts.mount_point) {
801 fprintf(stderr, "ERROR: No mount point specified\n");
802 return EXIT_FAILURE;
803 }
804
805 if (stat(opts.mount_point, &mst) < 0) {
806 if (errno == ENOENT) {
807 fprintf(stderr, "ERROR: the mount point specified does not exist\n");
808 return EXIT_FAILURE;
809 }
810
811 fprintf(stderr, "There was an error accessing the mount point: %s\n", strerror(errno));
812 return EXIT_FAILURE;
813 }
814 }
815
816 err = idevice_new_with_options(&device, opts.device_udid, (opts.use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
817 if (err != IDEVICE_E_SUCCESS) {
818 if (opts.device_udid) {
819 printf("ERROR: Device %s not found!\n", opts.device_udid);
820 } else {
821 printf("ERROR: No device found!\n");
822 }
823 fprintf(stderr, "Is the device properly connected?\n");
824 fprintf(stderr, "If it is make sure that your user has permissions to access the raw USB device.\n");
825 fprintf(stderr, "If you're still having issues try unplugging the device and reconnecting it.\n");
826 return EXIT_FAILURE;
827 }
828
829 if (!device) {
830 return EXIT_FAILURE;
831 }
832
833 if (opts.should_list_apps) {
834 list_available_apps(device);
835 return EXIT_SUCCESS;
836 }
837
838 ret = lockdownd_client_new_with_handshake(device, &control, "ifuse");
839 if (ret != LOCKDOWN_E_SUCCESS) {
840 idevice_free(device);
841 if (ret == LOCKDOWN_E_PASSWORD_PROTECTED) {
842 fprintf(stderr, "Please disable the password protection on your device and try again.\n");
843 fprintf(stderr, "The device does not allow pairing as long as a password has been set.\n");
844 fprintf(stderr, "You can enable it again after the connection succeeded.\n");
845 #ifdef LOCKDOWN_E_PAIRING_DIALOG_PENDING
846 } else if (ret == LOCKDOWN_E_PAIRING_DIALOG_PENDING) {
847 fprintf(stderr, "Please dismiss the trust dialog on your device and try again.\n");
848 fprintf(stderr, "The device does not allow pairing as long as the dialog has not been accepted.\n");
849 #endif
850 } else {
851 fprintf(stderr, "Failed to connect to lockdownd service on the device.\n");
852 fprintf(stderr, "Try again. If it still fails try rebooting your device.\n");
853 }
854 return EXIT_FAILURE;
855 }
856
857 if ((lockdownd_start_service(control, opts.service_name, &opts.service) != LOCKDOWN_E_SUCCESS) || !opts.service) {
858 lockdownd_client_free(control);
859 idevice_free(device);
860 fprintf(stderr, "Failed to start AFC service '%s' on the device.\n", opts.service_name);
861 if (!strcmp(opts.service_name, AFC2_SERVICE_NAME)) {
862 fprintf(stderr, "This service enables access to the root filesystem of your device.\n");
863 fprintf(stderr, "Your device needs to be jailbroken and have the AFC2 service installed.\n");
864 }
865 return EXIT_FAILURE;
866 }
867
868 if (!strcmp(opts.service_name, HOUSE_ARREST_SERVICE_NAME)) {
869 house_arrest_client_new(device, opts.service, &house_arrest);
870 if (!house_arrest) {
871 fprintf(stderr, "Could not start document sharing service!\n");
872 return EXIT_FAILURE;
873 }
874
875 /* FIXME: iOS 3.x house_arrest does not know about VendDocuments yet, thus use VendContainer and chroot manually with fuse subdir module */
876 if (house_arrest_send_command(house_arrest, opts.use_container ? "VendContainer": "VendDocuments", opts.appid) != HOUSE_ARREST_E_SUCCESS) {
877 fprintf(stderr, "Could not send house_arrest command!\n");
878 goto leave_err;
879 }
880
881 plist_t dict = NULL;
882 if (house_arrest_get_result(house_arrest, &dict) != HOUSE_ARREST_E_SUCCESS) {
883 fprintf(stderr, "Could not get result from document sharing service!\n");
884 goto leave_err;
885 }
886 plist_t node = plist_dict_get_item(dict, "Error");
887 if (node) {
888 char *str = NULL;
889 plist_get_string_val(node, &str);
890 fprintf(stderr, "ERROR: %s\n", str);
891 if (str && !strcmp(str, "InstallationLookupFailed")) {
892 fprintf(stderr, "The App '%s' is either not present on the device, or the 'UIFileSharingEnabled' key is not set in its Info.plist. Starting with iOS 8.3 this key is mandatory to allow access to an app's Documents folder.\n", opts.appid);
893 }
894 free(str);
895 goto leave_err;
896 }
897 plist_free(dict);
898
899 if (opts.use_container == 0) {
900 fuse_opt_add_arg(&args, "-omodules=subdir");
901 fuse_opt_add_arg(&args, "-osubdir=Documents");
902 }
903 }
904 res = fuse_main(args.argc, args.argv, &ifuse_oper, NULL);
905
906 leave_err:
907 if (house_arrest) {
908 house_arrest_client_free(house_arrest);
909 }
910 return res;
911 }
912