summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar tomriddly2024-04-28 12:14:39 +0800
committerGravatar Nikias Bassen2024-05-18 21:43:39 +0200
commitd4efb4ed1bd16cf11fbd0e8c1c41522474aced8d (patch)
tree6c0cda7d480a63b3e8f8d94a9b5305b71cd13561
parent469d21c6d506d107a5462c8b10e516f5790c35d3 (diff)
downloadlibimobiledevice-d4efb4ed1bd16cf11fbd0e8c1c41522474aced8d.tar.gz
libimobiledevice-d4efb4ed1bd16cf11fbd0e8c1c41522474aced8d.tar.bz2
tools/afcclient: Allow get folder from device to local.
Signed-off-by: tomriddly <tomriddly@qq.com>
-rw-r--r--tools/afcclient.c250
1 files changed, 164 insertions, 86 deletions
diff --git a/tools/afcclient.c b/tools/afcclient.c
index 9bcd77b..826ad5b 100644
--- a/tools/afcclient.c
+++ b/tools/afcclient.c
@@ -89,6 +89,16 @@ static idevice_subscription_context_t context = NULL;
89static char* curdir = NULL; 89static char* curdir = NULL;
90static size_t curdir_len = 0; 90static size_t curdir_len = 0;
91 91
92static int is_directory(const char* path)
93{
94 struct stat tst;
95#ifdef WIN32
96 return (stat(path, &tst) == 0) && S_ISDIR(tst.st_mode);
97#else
98 return (lstat(path, &tst) == 0) && S_ISDIR(tst.st_mode);
99#endif
100}
101
92static void print_usage(int argc, char **argv, int is_error) 102static void print_usage(int argc, char **argv, int is_error)
93{ 103{
94 char *name = strrchr(argv[0], '/'); 104 char *name = strrchr(argv[0], '/');
@@ -173,7 +183,7 @@ static void handle_help(afc_client_t afc, int argc, char** argv)
173 printf("rm PATH - remove item at PATH\n"); 183 printf("rm PATH - remove item at PATH\n");
174 printf("get PATH [LOCALPATH] - transfer file at PATH from device to LOCALPATH\n"); 184 printf("get PATH [LOCALPATH] - transfer file at PATH from device to LOCALPATH\n");
175 printf("put LOCALPATH [PATH] - transfer local file at LOCALPATH to device at PATH\n"); 185 printf("put LOCALPATH [PATH] - transfer local file at LOCALPATH to device at PATH\n");
176 printf("\n"); 186 printf("\n");
177} 187}
178 188
179static const char* path_get_basename(const char* path) 189static const char* path_get_basename(const char* path)
@@ -200,7 +210,7 @@ static int timeval_subtract(struct timeval *result, struct timeval *x, struct ti
200 result->tv_sec = x->tv_sec - y->tv_sec; 210 result->tv_sec = x->tv_sec - y->tv_sec;
201 result->tv_usec = x->tv_usec - y->tv_usec; 211 result->tv_usec = x->tv_usec - y->tv_usec;
202 /* Return 1 if result is negative. */ 212 /* Return 1 if result is negative. */
203 return x->tv_sec < y->tv_sec; 213 return x->tv_sec < y->tv_sec;
204} 214}
205 215
206struct str_item { 216struct str_item {
@@ -674,110 +684,178 @@ static void handle_remove(afc_client_t afc, int argc, char** argv)
674 } 684 }
675} 685}
676 686
677static void handle_get(afc_client_t afc, int argc, char** argv) 687static uint8_t get_single_file(afc_client_t afc, const char *srcpath, const char *dstpath, uint64_t file_size)
678{ 688{
679 if (argc < 1 || argc > 2) { 689 uint64_t fh = 0;
680 printf("Error: Invalid number of arguments\n"); 690 afc_error_t err = afc_file_open(afc, srcpath, AFC_FOPEN_RDONLY, &fh);
681 return; 691 if (err != AFC_E_SUCCESS) {
692 printf("Error: Failed to open file '%s': %s (%d)\n", srcpath, afc_strerror(err), err);
693 return 0;
682 } 694 }
683 char *srcpath = NULL; 695 FILE *f = fopen(dstpath, "wb");
684 char* dstpath = NULL; 696 if (!f) {
685 if (argc == 1) { 697 printf("Error: Failed to open local file '%s': %s\n", dstpath, strerror(errno));
686 srcpath = get_absolute_path(argv[0]); 698 return 0;
687 dstpath = strdup(path_get_basename(argv[0])); 699 }
688 } else { 700 struct timeval t1;
689 srcpath = get_absolute_path(argv[0]); 701 struct timeval t2;
690 dstpath = strdup(argv[1]); 702 struct timeval tdiff;
703 size_t bufsize = 0x100000;
704 char *buf = malloc(bufsize);
705 size_t total = 0;
706 int progress = 0;
707 int lastprog = 0;
708 if (file_size > 0x400000) {
709 progress = 1;
710 gettimeofday(&t1, NULL);
711 }
712 uint8_t succeed = 1;
713 while (err == AFC_E_SUCCESS) {
714 uint32_t bytes_read = 0;
715 size_t chunk = 0;
716 err = afc_file_read(afc, fh, buf, bufsize, &bytes_read);
717 if (bytes_read == 0) {
718 break;
719 }
720 while (chunk < bytes_read) {
721 size_t wr = fwrite(buf + chunk, 1, bytes_read - chunk, f);
722 if (wr == 0) {
723 if (progress) {
724 printf("\n");
725 }
726 printf("Error: Failed to write to local file\n");
727 succeed = 0;
728 break;
729 }
730 chunk += wr;
731 }
732 total += chunk;
733 if (progress) {
734 int prog = (int) ((double) total / (double) file_size * 100.0f);
735 if (prog > lastprog) {
736 gettimeofday(&t2, NULL);
737 timeval_subtract(&tdiff, &t2, &t1);
738 double time_in_sec = (double) tdiff.tv_sec + (double) tdiff.tv_usec / 1000000;
739 printf("\r%d%% (%0.1f MB/s) ", prog, (double) total / 1048576.0f / time_in_sec);
740 fflush(stdout);
741 lastprog = prog;
742 }
743 }
691 } 744 }
745 if (progress) {
746 printf("\n");
747 }
748 if (err != AFC_E_SUCCESS) {
749 printf("Error: Failed to read from file '%s': %s (%d)\n", srcpath, afc_strerror(err), err);
750 succeed = 0;
751 }
752 free(buf);
753 fclose(f);
754 afc_file_close(afc, fh);
755 return succeed;
756}
692 757
758static uint8_t get_file(afc_client_t afc, const char *srcpath, const char *dstpath)
759{
693 char **info = NULL; 760 char **info = NULL;
694 uint64_t file_size = 0; 761 uint64_t file_size = 0;
695 afc_get_file_info(afc, srcpath, &info); 762 afc_get_file_info(afc, srcpath, &info);
763 uint8_t is_dir = 0;
696 if (info) { 764 if (info) {
697 char **p = info; 765 char **p = info;
698 while (p && *p) { 766 while (p && *p) {
699 if (!strcmp(*p, "st_size")) { 767 if (!strcmp(*p, "st_size")) {
700 p++; 768 p++;
701 file_size = (uint64_t)strtoull(*p, NULL, 10); 769 file_size = (uint64_t) strtoull(*p, NULL, 10);
770 }
771 if (!strcmp(*p, "st_ifmt")) {
772 p++;
773 is_dir = !strcmp(*p, "S_IFDIR");
774 }
775 p++;
776 }
777 afc_dictionary_free(info);
778 }
779 uint8_t succeed = 1;
780 if (is_dir) {
781 char **entries = NULL;
782 afc_error_t err = afc_read_directory(afc, srcpath, &entries);
783 if (err != AFC_E_SUCCESS) {
784 printf("Error: Failed to list '%s': %s (%d)\n", srcpath, afc_strerror(err), err);
785 return 0;
786 }
787 char **p = entries;
788 size_t srcpath_len = strlen(srcpath);
789 int srcpath_is_root = strcmp(srcpath, "/") == 0;
790 if (!is_directory(dstpath) && mkdir(dstpath, 0777) != 0) {
791 printf("Error: Failed to create folder '%s': %s\n", dstpath, strerror(errno));
792 afc_dictionary_free(entries);
793 return 0;
794 }
795 while (p && *p) {
796 if (strcmp(".", *p) == 0 || strcmp("..", *p) == 0) {
797 p++;
798 continue;
799 }
800 size_t len = srcpath_len + 1 + strlen(*p) + 1;
801 char *testpath = (char *) malloc(len);
802 if (srcpath_is_root) {
803 snprintf(testpath, len, "/%s", *p);
804 } else {
805 snprintf(testpath, len, "%s/%s", srcpath, *p);
806 }
807 size_t dst_len = strlen(dstpath) + 1 + strlen(*p) + 1;
808 char *newdst = (char *) malloc(dst_len);
809 snprintf(newdst, dst_len, "%s/%s", dstpath, *p);
810 if (!get_file(afc, testpath, newdst)) {
811 succeed = 0;
702 break; 812 break;
703 } 813 }
814 free(testpath);
815 free(newdst);
704 p++; 816 p++;
705 } 817 }
818 afc_dictionary_free(entries);
819 } else {
820 succeed = get_single_file(afc, srcpath, dstpath, file_size);
706 } 821 }
707 uint64_t fh = 0; 822 return succeed;
708 afc_error_t err = afc_file_open(afc, srcpath, AFC_FOPEN_RDONLY, &fh); 823}
709 if (err != AFC_E_SUCCESS) { 824
710 free(srcpath); 825static void handle_get(afc_client_t afc, int argc, char **argv)
711 free(dstpath); 826{
712 printf("Error: Failed to open file '%s': %s (%d)\n", argv[0], afc_strerror(err), err); 827 if (argc < 1 || argc > 2) {
828 printf("Error: Invalid number of arguments\n");
713 return; 829 return;
714 } 830 }
715 FILE *f = fopen(dstpath, "wb"); 831 char *srcpath = NULL;
716 if (!f && errno == EISDIR) { 832 char *dstpath = NULL;
717 const char* basen = path_get_basename(argv[0]); 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) {
838 srcpath = get_absolute_path(argv[0]);
839 dstpath = strdup(path_get_basename(argv[0]));
840 } else {
841 srcpath = get_absolute_path(argv[0]);
842 dstpath = strdup(argv[1]);
843 }
844
845 if (is_directory(dstpath)) {
846 const char *basen = path_get_basename(argv[0]);
718 size_t len = strlen(dstpath) + 1 + strlen(basen) + 1; 847 size_t len = strlen(dstpath) + 1 + strlen(basen) + 1;
719 char* newdst = (char*)malloc(len); 848 char *newdst = (char *) malloc(len);
720 snprintf(newdst, len, "%s/%s", dstpath, basen); 849 snprintf(newdst, len, "%s/%s", dstpath, basen);
721 f = fopen(newdst, "wb"); 850 get_file(afc, srcpath, newdst);
851 free(srcpath);
722 free(newdst); 852 free(newdst);
723 } 853 free(dstpath);
724 if (f) {
725 struct timeval t1;
726 struct timeval t2;
727 struct timeval tdiff;
728 size_t bufsize = 0x100000;
729 char* buf = malloc(bufsize);
730 size_t total = 0;
731 int progress = 0;
732 int lastprog = 0;
733 if (file_size > 0x400000) {
734 progress = 1;
735 gettimeofday(&t1, NULL);
736 }
737 while (err == AFC_E_SUCCESS) {
738 uint32_t bytes_read = 0;
739 size_t chunk = 0;
740 err = afc_file_read(afc, fh, buf, bufsize, &bytes_read);
741 if (bytes_read == 0) {
742 break;
743 }
744 while (chunk < bytes_read) {
745 size_t wr = fwrite(buf+chunk, 1, bytes_read-chunk, f);
746 if (wr == 0) {
747 if (progress) {
748 printf("\n");
749 }
750 printf("Error: Failed to write to local file\n");
751 break;
752 }
753 chunk += wr;
754 }
755 total += chunk;
756 if (progress) {
757 int prog = (int)((double)total / (double)file_size * 100.0f);
758 if (prog > lastprog) {
759 gettimeofday(&t2, NULL);
760 timeval_subtract(&tdiff, &t2, &t1);
761 double time_in_sec = (double)tdiff.tv_sec + (double)tdiff.tv_usec/1000000;
762 printf("\r%d%% (%0.1f MB/s) ", prog, (double)total/1048576.0f / time_in_sec);
763 fflush(stdout);
764 lastprog = prog;
765 }
766 }
767 }
768 if (progress) {
769 printf("\n");
770 }
771 if (err != AFC_E_SUCCESS) {
772 printf("Error: Failed to read from file '%s': %s (%d)\n", argv[0], afc_strerror(err), err);
773 }
774 free(buf);
775 fclose(f);
776 } else { 854 } else {
777 printf("Error: Failed to open local file '%s': %s\n", dstpath, strerror(errno)); 855 get_file(afc, srcpath, dstpath);
856 free(srcpath);
857 free(dstpath);
778 } 858 }
779 afc_file_close(afc, fh);
780 free(srcpath);
781} 859}
782 860
783static void handle_put(afc_client_t afc, int argc, char** argv) 861static void handle_put(afc_client_t afc, int argc, char** argv)
@@ -975,8 +1053,8 @@ static void parse_cmdline(int* p_argc, char*** p_argv, const char* cmdline)
975 } else if (*pos == '"' || *pos == '\'') { 1053 } else if (*pos == '"' || *pos == '\'') {
976 if (!qpos) { 1054 if (!qpos) {
977 qpos = pos; 1055 qpos = pos;
978 } else { 1056 } else {
979 qpos = NULL; 1057 qpos = NULL;
980 } 1058 }
981 pos++; 1059 pos++;
982 } else if (*pos == '\0' || (!qpos && isspace(*pos))) { 1060 } else if (*pos == '\0' || (!qpos && isspace(*pos))) {
@@ -1043,7 +1121,7 @@ static int process_args(afc_client_t afc, int argc, char** argv)
1043 handle_file_info(afc, argc-1, argv+1); 1121 handle_file_info(afc, argc-1, argv+1);
1044 } 1122 }
1045 else if (!strcmp(argv[0], "ls") || !strcmp(argv[0], "list")) { 1123 else if (!strcmp(argv[0], "ls") || !strcmp(argv[0], "list")) {
1046 handle_list(afc, argc-1, argv+1); 1124 handle_list(afc, argc-1, argv+1);
1047 } 1125 }
1048 else if (!strcmp(argv[0], "mv") || !strcmp(argv[0], "rename")) { 1126 else if (!strcmp(argv[0], "mv") || !strcmp(argv[0], "rename")) {
1049 handle_rename(afc, argc-1, argv+1); 1127 handle_rename(afc, argc-1, argv+1);
@@ -1113,7 +1191,7 @@ static void start_cmdline(afc_client_t afc)
1113 break; 1191 break;
1114 } 1192 }
1115 } 1193 }
1116 } 1194 }
1117} 1195}
1118 1196
1119static void device_event_cb(const idevice_event_t* event, void* userdata) 1197static void device_event_cb(const idevice_event_t* event, void* userdata)