diff options
| -rw-r--r-- | tools/afcclient.c | 295 | 
1 files changed, 211 insertions, 84 deletions
| diff --git a/tools/afcclient.c b/tools/afcclient.c index 826ad5b..adfb1ba 100644 --- a/tools/afcclient.c +++ b/tools/afcclient.c | |||
| @@ -36,6 +36,7 @@ | |||
| 36 | #include <signal.h> | 36 | #include <signal.h> | 
| 37 | #include <ctype.h> | 37 | #include <ctype.h> | 
| 38 | #include <unistd.h> | 38 | #include <unistd.h> | 
| 39 | #include <dirent.h> | ||
| 39 | 40 | ||
| 40 | #ifdef WIN32 | 41 | #ifdef WIN32 | 
| 41 | #include <windows.h> | 42 | #include <windows.h> | 
| @@ -70,6 +71,7 @@ | |||
| 70 | #include <plist/plist.h> | 71 | #include <plist/plist.h> | 
| 71 | 72 | ||
| 72 | #include <libimobiledevice-glue/termcolors.h> | 73 | #include <libimobiledevice-glue/termcolors.h> | 
| 74 | #include <libimobiledevice-glue/utils.h> | ||
| 73 | 75 | ||
| 74 | #undef st_mtime | 76 | #undef st_mtime | 
| 75 | #undef st_birthtime | 77 | #undef st_birthtime | 
| @@ -759,7 +761,11 @@ static uint8_t get_file(afc_client_t afc, const char *srcpath, const char *dstpa | |||
| 759 | { | 761 | { | 
| 760 | char **info = NULL; | 762 | char **info = NULL; | 
| 761 | uint64_t file_size = 0; | 763 | uint64_t file_size = 0; | 
| 762 | afc_get_file_info(afc, srcpath, &info); | 764 | afc_error_t err = afc_get_file_info(afc, srcpath, &info); | 
| 765 | if (err == AFC_E_OBJECT_NOT_FOUND) { | ||
| 766 | printf("Error: Failed to read from file '%s': %s (%d)\n", srcpath, afc_strerror(err), err); | ||
| 767 | return 0; | ||
| 768 | } | ||
| 763 | uint8_t is_dir = 0; | 769 | uint8_t is_dir = 0; | 
| 764 | if (info) { | 770 | if (info) { | 
| 765 | char **p = info; | 771 | char **p = info; | 
| @@ -779,7 +785,7 @@ static uint8_t get_file(afc_client_t afc, const char *srcpath, const char *dstpa | |||
| 779 | uint8_t succeed = 1; | 785 | uint8_t succeed = 1; | 
| 780 | if (is_dir) { | 786 | if (is_dir) { | 
| 781 | char **entries = NULL; | 787 | char **entries = NULL; | 
| 782 | afc_error_t err = afc_read_directory(afc, srcpath, &entries); | 788 | err = afc_read_directory(afc, srcpath, &entries); | 
| 783 | if (err != AFC_E_SUCCESS) { | 789 | if (err != AFC_E_SUCCESS) { | 
| 784 | printf("Error: Failed to list '%s': %s (%d)\n", srcpath, afc_strerror(err), err); | 790 | printf("Error: Failed to list '%s': %s (%d)\n", srcpath, afc_strerror(err), err); | 
| 785 | return 0; | 791 | return 0; | 
| @@ -830,18 +836,31 @@ static void handle_get(afc_client_t afc, int argc, char **argv) | |||
| 830 | } | 836 | } | 
| 831 | char *srcpath = NULL; | 837 | char *srcpath = NULL; | 
| 832 | char *dstpath = NULL; | 838 | char *dstpath = NULL; | 
| 833 | size_t src_len = strlen(argv[0]); | ||
| 834 | if (strcmp(argv[0], "/") != 0 && argv[0][src_len - 1] == '/') { | ||
| 835 | argv[0][src_len - 1] = '\0'; | ||
| 836 | } | ||
| 837 | if (argc == 1) { | 839 | if (argc == 1) { | 
| 838 | srcpath = get_absolute_path(argv[0]); | 840 | char *tmp = strdup(argv[0]); | 
| 839 | dstpath = strdup(path_get_basename(argv[0])); | 841 | size_t src_len = strlen(tmp); | 
| 842 | if (src_len > 1 && tmp[src_len - 1] == '/') { | ||
| 843 | tmp[src_len - 1] = '\0'; | ||
| 844 | } | ||
| 845 | srcpath = get_absolute_path(tmp); | ||
| 846 | dstpath = strdup(path_get_basename(tmp)); | ||
| 847 | free(tmp); | ||
| 840 | } else { | 848 | } else { | 
| 841 | srcpath = get_absolute_path(argv[0]); | 849 | char *tmp = strdup(argv[0]); | 
| 850 | size_t src_len = strlen(tmp); | ||
| 851 | if (src_len > 1 && tmp[src_len - 1] == '/') { | ||
| 852 | tmp[src_len - 1] = '\0'; | ||
| 853 | } | ||
| 854 | srcpath = get_absolute_path(tmp); | ||
| 842 | dstpath = strdup(argv[1]); | 855 | dstpath = strdup(argv[1]); | 
| 856 | size_t dst_len = strlen(dstpath); | ||
| 857 | if (dst_len > 1 && dstpath[dst_len - 1] == '/') { | ||
| 858 | dstpath[dst_len - 1] = '\0'; | ||
| 859 | } | ||
| 860 | free(tmp); | ||
| 843 | } | 861 | } | 
| 844 | 862 | ||
| 863 | // target is a directory, put file under this target | ||
| 845 | if (is_directory(dstpath)) { | 864 | if (is_directory(dstpath)) { | 
| 846 | const char *basen = path_get_basename(argv[0]); | 865 | const char *basen = path_get_basename(argv[0]); | 
| 847 | size_t len = strlen(dstpath) + 1 + strlen(basen) + 1; | 866 | size_t len = strlen(dstpath) + 1 + strlen(basen) + 1; | 
| @@ -852,104 +871,212 @@ static void handle_get(afc_client_t afc, int argc, char **argv) | |||
| 852 | free(newdst); | 871 | free(newdst); | 
| 853 | free(dstpath); | 872 | free(dstpath); | 
| 854 | } else { | 873 | } else { | 
| 874 | // target is not a dir or does not exist, just try to create or rewrite it | ||
| 855 | get_file(afc, srcpath, dstpath); | 875 | get_file(afc, srcpath, dstpath); | 
| 856 | free(srcpath); | 876 | free(srcpath); | 
| 857 | free(dstpath); | 877 | free(dstpath); | 
| 858 | } | 878 | } | 
| 859 | } | 879 | } | 
| 860 | 880 | ||
| 861 | static void handle_put(afc_client_t afc, int argc, char** argv) | 881 | static uint8_t put_single_file(afc_client_t afc, const char *srcpath, const char *dstpath) | 
| 882 | { | ||
| 883 | FILE *f = fopen(srcpath, "rb"); | ||
| 884 | if (!f) { | ||
| 885 | printf("Error: Failed to open local file '%s': %s\n", srcpath, strerror(errno)); | ||
| 886 | return 0; | ||
| 887 | } | ||
| 888 | struct timeval t1; | ||
| 889 | struct timeval t2; | ||
| 890 | struct timeval tdiff; | ||
| 891 | struct stat fst; | ||
| 892 | int progress = 0; | ||
| 893 | size_t bufsize = 0x100000; | ||
| 894 | char *buf = malloc(bufsize); | ||
| 895 | |||
| 896 | fstat(fileno(f), &fst); | ||
| 897 | if (fst.st_size >= 0x400000) { | ||
| 898 | progress = 1; | ||
| 899 | gettimeofday(&t1, NULL); | ||
| 900 | } | ||
| 901 | size_t total = 0; | ||
| 902 | int lastprog = 0; | ||
| 903 | uint64_t fh = 0; | ||
| 904 | afc_error_t err = afc_file_open(afc, dstpath, AFC_FOPEN_RW, &fh); | ||
| 905 | uint8_t succeed = 1; | ||
| 906 | while (err == AFC_E_SUCCESS) { | ||
| 907 | uint32_t bytes_read = fread(buf, 1, bufsize, f); | ||
| 908 | if (bytes_read == 0) { | ||
| 909 | if (!feof(f)) { | ||
| 910 | if (progress) { | ||
| 911 | printf("\n"); | ||
| 912 | } | ||
| 913 | printf("Error: Failed to read from local file\n"); | ||
| 914 | succeed = 0; | ||
| 915 | } | ||
| 916 | break; | ||
| 917 | } | ||
| 918 | uint32_t chunk = 0; | ||
| 919 | while (chunk < bytes_read) { | ||
| 920 | uint32_t bytes_written = 0; | ||
| 921 | err = afc_file_write(afc, fh, buf + chunk, bytes_read - chunk, &bytes_written); | ||
| 922 | if (err != AFC_E_SUCCESS) { | ||
| 923 | if (progress) { | ||
| 924 | printf("\n"); | ||
| 925 | } | ||
| 926 | printf("Error: Failed to write to device file\n"); | ||
| 927 | succeed = 0; | ||
| 928 | break; | ||
| 929 | } | ||
| 930 | chunk += bytes_written; | ||
| 931 | } | ||
| 932 | total += chunk; | ||
| 933 | if (progress) { | ||
| 934 | int prog = (int) ((double) total / (double) fst.st_size * 100.0f); | ||
| 935 | if (prog > lastprog) { | ||
| 936 | gettimeofday(&t2, NULL); | ||
| 937 | timeval_subtract(&tdiff, &t2, &t1); | ||
| 938 | double time_in_sec = (double) tdiff.tv_sec + (double) tdiff.tv_usec / 1000000; | ||
| 939 | printf("\r%d%% (%0.1f MB/s) ", prog, (double) total / 1048576.0f / time_in_sec); | ||
| 940 | fflush(stdout); | ||
| 941 | lastprog = prog; | ||
| 942 | } | ||
| 943 | } | ||
| 944 | } | ||
| 945 | printf("\n"); | ||
| 946 | free(buf); | ||
| 947 | afc_file_close(afc, fh); | ||
| 948 | fclose(f); | ||
| 949 | return succeed; | ||
| 950 | } | ||
| 951 | |||
| 952 | static uint8_t put_file(afc_client_t afc, const char *srcpath, const char *dstpath) | ||
| 953 | { | ||
| 954 | if (is_directory(srcpath)) { | ||
| 955 | char **info = NULL; | ||
| 956 | afc_error_t err = afc_get_file_info(afc, dstpath, &info); | ||
| 957 | //create if target directory does not exist | ||
| 958 | if (err == AFC_E_OBJECT_NOT_FOUND) { | ||
| 959 | err = afc_make_directory(afc, dstpath); | ||
| 960 | if (err != AFC_E_SUCCESS) { | ||
| 961 | printf("Error: Failed to create directory '%s': %s (%d)\n", dstpath, afc_strerror(err), err); | ||
| 962 | return 0; | ||
| 963 | } | ||
| 964 | } | ||
| 965 | afc_dictionary_free(info); | ||
| 966 | afc_get_file_info(afc, dstpath, &info); | ||
| 967 | uint8_t is_dir = 0; | ||
| 968 | if (info) { | ||
| 969 | char **p = info; | ||
| 970 | while (p && *p) { | ||
| 971 | if (!strcmp(*p, "st_ifmt")) { | ||
| 972 | p++; | ||
| 973 | is_dir = !strcmp(*p, "S_IFDIR"); | ||
| 974 | break; | ||
| 975 | } | ||
| 976 | p++; | ||
| 977 | } | ||
| 978 | afc_dictionary_free(info); | ||
| 979 | } | ||
| 980 | if (!is_dir) { | ||
| 981 | printf("Error: Failed to create or access directory: '%s'", dstpath); | ||
| 982 | return 0; | ||
| 983 | } | ||
| 984 | |||
| 985 | // walk dir recursively to put files | ||
| 986 | DIR *cur_dir = opendir(srcpath); | ||
| 987 | if (cur_dir) { | ||
| 988 | struct dirent *ep; | ||
| 989 | while ((ep = readdir(cur_dir))) { | ||
| 990 | if ((strcmp(ep->d_name, ".") == 0) || (strcmp(ep->d_name, "..") == 0)) { | ||
| 991 | continue; | ||
| 992 | } | ||
| 993 | char *fpath = string_build_path(srcpath, ep->d_name, NULL); | ||
| 994 | if (fpath) { | ||
| 995 | size_t len = strlen(srcpath) + 1 + strlen(ep->d_name) + 1; | ||
| 996 | char *newdst = (char *) malloc(len); | ||
| 997 | snprintf(newdst, len, "%s/%s", dstpath, ep->d_name); | ||
| 998 | if (!put_file(afc, fpath, newdst)) { | ||
| 999 | free(newdst); | ||
| 1000 | free(fpath); | ||
| 1001 | return 0; | ||
| 1002 | } | ||
| 1003 | free(newdst); | ||
| 1004 | free(fpath); | ||
| 1005 | } | ||
| 1006 | } | ||
| 1007 | closedir(cur_dir); | ||
| 1008 | } else { | ||
| 1009 | printf("Error: Failed to visit directory: '%s': %s", srcpath, strerror(errno)); | ||
| 1010 | return 0; | ||
| 1011 | } | ||
| 1012 | } else { | ||
| 1013 | return put_single_file(afc, srcpath, dstpath); | ||
| 1014 | } | ||
| 1015 | return 1; | ||
| 1016 | } | ||
| 1017 | |||
| 1018 | static void handle_put(afc_client_t afc, int argc, char **argv) | ||
| 862 | { | 1019 | { | 
| 863 | if (argc < 1 || argc > 2) { | 1020 | if (argc < 1 || argc > 2) { | 
| 864 | printf("Error: Invalid number of arguments\n"); | 1021 | printf("Error: Invalid number of arguments\n"); | 
| 865 | return; | 1022 | return; | 
| 866 | } | 1023 | } | 
| 867 | 1024 | ||
| 1025 | char *srcpath = strdup(argv[0]); | ||
| 1026 | size_t src_len = strlen(srcpath); | ||
| 1027 | if (src_len > 1 && srcpath[src_len - 1] == '/') { | ||
| 1028 | srcpath[src_len - 1] = '\0'; | ||
| 1029 | } | ||
| 868 | char *dstpath = NULL; | 1030 | char *dstpath = NULL; | 
| 869 | if (argc == 1) { | 1031 | if (argc == 1) { | 
| 870 | dstpath = get_absolute_path(path_get_basename(argv[0])); | 1032 | dstpath = get_absolute_path(path_get_basename(srcpath)); | 
| 871 | } else { | 1033 | } else { | 
| 872 | dstpath = get_absolute_path(argv[1]); | 1034 | char *tmp = strdup(argv[1]); | 
| 1035 | size_t dst_len = strlen(tmp); | ||
| 1036 | if (dst_len > 1 && tmp[dst_len - 1] == '/') { | ||
| 1037 | tmp[dst_len - 1] = '\0'; | ||
| 1038 | } | ||
| 1039 | dstpath = get_absolute_path(tmp); | ||
| 1040 | free(tmp); | ||
| 873 | } | 1041 | } | 
| 874 | 1042 | char **info = NULL; | |
| 875 | uint64_t fh = 0; | 1043 | afc_error_t err = afc_get_file_info(afc, dstpath, &info); | 
| 876 | FILE *f = fopen(argv[0], "rb"); | 1044 | // target does not exist, put directly | 
| 877 | if (f) { | 1045 | if (err == AFC_E_OBJECT_NOT_FOUND) { | 
| 878 | afc_error_t err = afc_file_open(afc, dstpath, AFC_FOPEN_RW, &fh); | 1046 | put_file(afc, srcpath, dstpath); | 
| 879 | if (err == AFC_E_OBJECT_IS_DIR) { | 1047 | free(srcpath); | 
| 880 | const char* basen = path_get_basename(argv[0]); | 1048 | free(dstpath); | 
| 1049 | } else { | ||
| 1050 | uint8_t is_dir = 0; | ||
| 1051 | if (info) { | ||
| 1052 | char **p = info; | ||
| 1053 | while (p && *p) { | ||
| 1054 | if (!strcmp(*p, "st_ifmt")) { | ||
| 1055 | p++; | ||
| 1056 | is_dir = !strcmp(*p, "S_IFDIR"); | ||
| 1057 | break; | ||
| 1058 | } | ||
| 1059 | p++; | ||
| 1060 | } | ||
| 1061 | afc_dictionary_free(info); | ||
| 1062 | } | ||
| 1063 | // target is a dir, put under this target | ||
| 1064 | if (is_dir) { | ||
| 1065 | const char *basen = path_get_basename(srcpath); | ||
| 881 | size_t len = strlen(dstpath) + 1 + strlen(basen) + 1; | 1066 | size_t len = strlen(dstpath) + 1 + strlen(basen) + 1; | 
| 882 | char* newdst = (char*)malloc(len); | 1067 | char *newdst = (char *) malloc(len); | 
| 883 | snprintf(newdst, len, "%s/%s", dstpath, basen); | 1068 | snprintf(newdst, len, "%s/%s", dstpath, basen); | 
| 884 | free(dstpath); | 1069 | free(dstpath); | 
| 885 | dstpath = get_absolute_path(newdst); | 1070 | dstpath = get_absolute_path(newdst); | 
| 886 | free(newdst); | 1071 | free(newdst); | 
| 887 | err = afc_file_open(afc, dstpath, AFC_FOPEN_RW, &fh); | 1072 | put_file(afc, srcpath, dstpath); | 
| 888 | } | ||
| 889 | if (err != AFC_E_SUCCESS) { | ||
| 890 | printf("Error: Failed to open file '%s' on device: %s (%d)\n", argv[1], afc_strerror(err), err); | ||
| 891 | } else { | 1073 | } else { | 
| 892 | struct timeval t1; | 1074 | //target is common file, rewrite it | 
| 893 | struct timeval t2; | 1075 | put_file(afc, srcpath, dstpath); | 
| 894 | struct timeval tdiff; | ||
| 895 | struct stat fst; | ||
| 896 | int progress = 0; | ||
| 897 | size_t bufsize = 0x100000; | ||
| 898 | char* buf = malloc(bufsize); | ||
| 899 | |||
| 900 | fstat(fileno(f), &fst); | ||
| 901 | if (fst.st_size >= 0x400000) { | ||
| 902 | progress = 1; | ||
| 903 | gettimeofday(&t1, NULL); | ||
| 904 | } | ||
| 905 | size_t total = 0; | ||
| 906 | int lastprog = 0; | ||
| 907 | while (err == AFC_E_SUCCESS) { | ||
| 908 | uint32_t bytes_read = fread(buf, 1, bufsize, f); | ||
| 909 | if (bytes_read == 0) { | ||
| 910 | if (!feof(f)) { | ||
| 911 | if (progress) { | ||
| 912 | printf("\n"); | ||
| 913 | } | ||
| 914 | printf("Error: Failed to read from local file\n"); | ||
| 915 | } | ||
| 916 | break; | ||
| 917 | } | ||
| 918 | uint32_t chunk = 0; | ||
| 919 | while (chunk < bytes_read) { | ||
| 920 | uint32_t bytes_written = 0; | ||
| 921 | err = afc_file_write(afc, fh, buf+chunk, bytes_read-chunk, &bytes_written); | ||
| 922 | if (err != AFC_E_SUCCESS) { | ||
| 923 | if (progress) { | ||
| 924 | printf("\n"); | ||
| 925 | } | ||
| 926 | printf("Error: Failed to write to device file\n"); | ||
| 927 | break; | ||
| 928 | } | ||
| 929 | chunk += bytes_written; | ||
| 930 | } | ||
| 931 | total += chunk; | ||
| 932 | if (progress) { | ||
| 933 | int prog = (int)((double)total / (double)fst.st_size * 100.0f); | ||
| 934 | if (prog > lastprog) { | ||
| 935 | gettimeofday(&t2, NULL); | ||
| 936 | timeval_subtract(&tdiff, &t2, &t1); | ||
| 937 | double time_in_sec = (double)tdiff.tv_sec + (double)tdiff.tv_usec/1000000; | ||
| 938 | printf("\r%d%% (%0.1f MB/s) ", prog, (double)total/1048576.0f / time_in_sec); | ||
| 939 | fflush(stdout); | ||
| 940 | lastprog = prog; | ||
| 941 | } | ||
| 942 | } | ||
| 943 | } | ||
| 944 | printf("\n"); | ||
| 945 | free(buf); | ||
| 946 | afc_file_close(afc, fh); | ||
| 947 | } | 1076 | } | 
| 948 | fclose(f); | 1077 | free(srcpath); | 
| 949 | } else { | 1078 | free(dstpath); | 
| 950 | printf("Error: Failed to open local file '%s': %s\n", argv[0], strerror(errno)); | ||
| 951 | } | 1079 | } | 
| 952 | free(dstpath); | ||
| 953 | } | 1080 | } | 
| 954 | 1081 | ||
| 955 | static void handle_pwd(afc_client_t afc, int argc, char** argv) | 1082 | static void handle_pwd(afc_client_t afc, int argc, char** argv) | 
