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