diff options
author | 2024-04-28 15:35:30 +0800 | |
---|---|---|
committer | 2024-05-18 21:52:45 +0200 | |
commit | 8bed93c820909afe0b9290110f5f73fbe1125a23 (patch) | |
tree | 0afeca602cc701bb6b718bb2b574e0b74985b245 | |
parent | d4efb4ed1bd16cf11fbd0e8c1c41522474aced8d (diff) | |
download | libimobiledevice-8bed93c820909afe0b9290110f5f73fbe1125a23.tar.gz libimobiledevice-8bed93c820909afe0b9290110f5f73fbe1125a23.tar.bz2 |
tools/afcclient: Allow put directory to device
Signed-off-by: tomriddly <tomriddly@qq.com>
-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) |