diff options
-rw-r--r-- | .github/workflows/build.yml | 2 | ||||
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | common/Makefile.am | 2 | ||||
-rw-r--r-- | docs/Makefile.am | 3 | ||||
-rw-r--r-- | docs/afcclient.1 | 8 | ||||
-rw-r--r-- | tools/afcclient.c | 638 |
6 files changed, 485 insertions, 169 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 584f2e9..76cd02a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -74,7 +74,7 @@ jobs: else brew install libtool autoconf automake pkgconfig fi - pip3 install cython + pip3 install --break-system-packages cython shell: bash - name: fetch libplist uses: dawidd6/action-download-artifact@v3 @@ -37,6 +37,7 @@ docs/html libimobiledevice-1.0.pc tools/.libs/* tools/idevice* +tools/afcclient !tools/idevice*.[ch] cython/.libs/* cython/*.c diff --git a/common/Makefile.am b/common/Makefile.am index bd09bad..ba7ed9c 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -7,12 +7,14 @@ AM_CFLAGS = \ $(ssl_lib_CFLAGS) \ $(LFS_CFLAGS) \ $(libusbmuxd_CFLAGS) \ + $(limd_glue_CFLAGS) \ $(libplist_CFLAGS) AM_LDFLAGS = \ $(ssl_lib_LIBS) \ ${libpthread_LIBS} \ $(libusbmuxd_LIBS) \ + $(limd_glue_LIBS) \ $(libplist_LIBS) noinst_LTLIBRARIES = libinternalcommon.la diff --git a/docs/Makefile.am b/docs/Makefile.am index 4a4c56f..8156d4f 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -18,7 +18,8 @@ man_MANS = \ idevicedebug.1 \ idevicedevmodectl.1 \ idevicenotificationproxy.1 \ - idevicesetlocation.1 + idevicesetlocation.1 \ + afcclient.1 EXTRA_DIST = $(man_MANS) diff --git a/docs/afcclient.1 b/docs/afcclient.1 index ca7cb86..a4eeacb 100644 --- a/docs/afcclient.1 +++ b/docs/afcclient.1 @@ -32,16 +32,14 @@ create directory at PATH .B ln [-s] FILE [LINK] Create a (symbolic) link to file named LINKNAME. \f[B]NOTE: This feature has been disabled in newer versions of iOS\f[]. .TP -.B rm PATH +.B rm [-rf] PATH remove item at PATH .TP -.B get PATH [LOCALPATH] +.B get [-rf] PATH [LOCALPATH] transfer file at PATH from device to LOCALPATH, or current directory if omitted. If LOCALPATH is a directory, the file will be stored inside the directory. -\f[B]WARNING\f[]: Existing files will be overwritten! .TP -.B put LOCALPATH [PATH] +.B put [-rf] LOCALPATH [PATH] transfer local file at LOCALPATH to device at PATH, or current directory if omitted. If PATH is a directory, the file will be stored inside the directory. -\f[B]WARNING\f[]: Existing files will be overwritten! .TP .SH OPTIONS diff --git a/tools/afcclient.c b/tools/afcclient.c index 9bcd77b..71a1c32 100644 --- a/tools/afcclient.c +++ b/tools/afcclient.c @@ -36,6 +36,7 @@ #include <signal.h> #include <ctype.h> #include <unistd.h> +#include <dirent.h> #ifdef WIN32 #include <windows.h> @@ -70,6 +71,7 @@ #include <plist/plist.h> #include <libimobiledevice-glue/termcolors.h> +#include <libimobiledevice-glue/utils.h> #undef st_mtime #undef st_birthtime @@ -89,6 +91,26 @@ static idevice_subscription_context_t context = NULL; static char* curdir = NULL; static size_t curdir_len = 0; +static int file_exists(const char* path) +{ + struct stat tst; +#ifdef WIN32 + return (stat(path, &tst) == 0); +#else + return (lstat(path, &tst) == 0); +#endif +} + +static int is_directory(const char* path) +{ + struct stat tst; +#ifdef WIN32 + return (stat(path, &tst) == 0) && S_ISDIR(tst.st_mode); +#else + return (lstat(path, &tst) == 0) && S_ISDIR(tst.st_mode); +#endif +} + static void print_usage(int argc, char **argv, int is_error) { char *name = strrchr(argv[0], '/'); @@ -171,9 +193,9 @@ static void handle_help(afc_client_t afc, int argc, char** argv) printf("ln [-s] FILE [LINK] - create a (symbolic) link to file named LINKNAME\n"); printf(" NOTE: This feature has been disabled in newer versions of iOS.\n"); printf("rm PATH - remove item at PATH\n"); - printf("get PATH [LOCALPATH] - transfer file at PATH from device to LOCALPATH\n"); - printf("put LOCALPATH [PATH] - transfer local file at LOCALPATH to device at PATH\n"); - printf("\n"); + printf("get [-rf] PATH [LOCALPATH] - transfer file at PATH from device to LOCALPATH\n"); + printf("put [-rf] LOCALPATH [PATH] - transfer local file at LOCALPATH to device at PATH\n"); + printf("\n"); } static const char* path_get_basename(const char* path) @@ -200,7 +222,7 @@ static int timeval_subtract(struct timeval *result, struct timeval *x, struct ti result->tv_sec = x->tv_sec - y->tv_sec; result->tv_usec = x->tv_usec - y->tv_usec; /* Return 1 if result is negative. */ - return x->tv_sec < y->tv_sec; + return x->tv_sec < y->tv_sec; } struct str_item { @@ -674,204 +696,496 @@ static void handle_remove(afc_client_t afc, int argc, char** argv) } } -static void handle_get(afc_client_t afc, int argc, char** argv) +static uint8_t get_single_file(afc_client_t afc, const char *srcpath, const char *dstpath, uint64_t file_size, uint8_t force_overwrite) { - if (argc < 1 || argc > 2) { - printf("Error: Invalid number of arguments\n"); - return; + uint64_t fh = 0; + afc_error_t err = afc_file_open(afc, srcpath, AFC_FOPEN_RDONLY, &fh); + if (err != AFC_E_SUCCESS) { + printf("Error: Failed to open file '%s': %s (%d)\n", srcpath, afc_strerror(err), err); + return 0; } - char *srcpath = NULL; - char* dstpath = NULL; - if (argc == 1) { - srcpath = get_absolute_path(argv[0]); - dstpath = strdup(path_get_basename(argv[0])); - } else { - srcpath = get_absolute_path(argv[0]); - dstpath = strdup(argv[1]); + if (file_exists(dstpath) && !force_overwrite) { + printf("Error: Failed to overwrite existing file without '-f' option: %s\n", dstpath); + return 0; } + FILE *f = fopen(dstpath, "wb"); + if (!f) { + printf("Error: Failed to open local file '%s': %s\n", dstpath, strerror(errno)); + return 0; + } + struct timeval t1; + struct timeval t2; + struct timeval tdiff; + size_t bufsize = 0x100000; + char *buf = malloc(bufsize); + size_t total = 0; + int progress = 0; + int lastprog = 0; + if (file_size > 0x400000) { + progress = 1; + gettimeofday(&t1, NULL); + } + uint8_t succeed = 1; + while (err == AFC_E_SUCCESS) { + uint32_t bytes_read = 0; + size_t chunk = 0; + err = afc_file_read(afc, fh, buf, bufsize, &bytes_read); + if (bytes_read == 0) { + break; + } + while (chunk < bytes_read) { + size_t wr = fwrite(buf + chunk, 1, bytes_read - chunk, f); + if (wr == 0) { + if (progress) { + printf("\n"); + } + printf("Error: Failed to write to local file\n"); + succeed = 0; + break; + } + chunk += wr; + } + total += chunk; + if (progress) { + int prog = (int) ((double) total / (double) file_size * 100.0f); + if (prog > lastprog) { + gettimeofday(&t2, NULL); + timeval_subtract(&tdiff, &t2, &t1); + double time_in_sec = (double) tdiff.tv_sec + (double) tdiff.tv_usec / 1000000; + printf("\r%d%% (%0.1f MB/s) ", prog, (double) total / 1048576.0f / time_in_sec); + fflush(stdout); + lastprog = prog; + } + } + } + if (progress) { + printf("\n"); + } + if (err != AFC_E_SUCCESS) { + printf("Error: Failed to read from file '%s': %s (%d)\n", srcpath, afc_strerror(err), err); + succeed = 0; + } + free(buf); + fclose(f); + afc_file_close(afc, fh); + return succeed; +} +static int __mkdir(const char* path) +{ +#ifdef WIN32 + return mkdir(path); +#else + return mkdir(path, 0755); +#endif +} + +static uint8_t get_file(afc_client_t afc, const char *srcpath, const char *dstpath, uint8_t force_overwrite, uint8_t recursive_get) +{ char **info = NULL; uint64_t file_size = 0; - afc_get_file_info(afc, srcpath, &info); + afc_error_t err = afc_get_file_info(afc, srcpath, &info); + if (err == AFC_E_OBJECT_NOT_FOUND) { + printf("Error: Failed to read from file '%s': %s (%d)\n", srcpath, afc_strerror(err), err); + return 0; + } + uint8_t is_dir = 0; if (info) { char **p = info; while (p && *p) { if (!strcmp(*p, "st_size")) { p++; - file_size = (uint64_t)strtoull(*p, NULL, 10); + file_size = (uint64_t) strtoull(*p, NULL, 10); + } + if (!strcmp(*p, "st_ifmt")) { + p++; + is_dir = !strcmp(*p, "S_IFDIR"); + } + p++; + } + afc_dictionary_free(info); + } + uint8_t succeed = 1; + if (is_dir) { + if (!recursive_get) { + printf("Error: Failed to get a directory without '-r' option: %s\n", srcpath); + return 0; + } + char **entries = NULL; + err = afc_read_directory(afc, srcpath, &entries); + if (err != AFC_E_SUCCESS) { + printf("Error: Failed to list '%s': %s (%d)\n", srcpath, afc_strerror(err), err); + return 0; + } + char **p = entries; + size_t srcpath_len = strlen(srcpath); + uint8_t srcpath_is_root = strcmp(srcpath, "/") == 0; + // if directory exists, check force_overwrite flag + if (is_directory(dstpath)) { + if (!force_overwrite) { + printf("Error: Failed to write into existing directory without '-f': %s\n", dstpath); + return 0; + } + } else if (__mkdir(dstpath) != 0) { + printf("Error: Failed to create directory '%s': %s\n", dstpath, strerror(errno)); + afc_dictionary_free(entries); + return 0; + } + while (p && *p) { + if (strcmp(".", *p) == 0 || strcmp("..", *p) == 0) { + p++; + continue; + } + size_t len = srcpath_is_root ? strlen(*p) + 1 : srcpath_len + 1 + strlen(*p) + 1; + char *testpath = (char *) malloc(len); + if (srcpath_is_root) { + snprintf(testpath, len, "/%s", *p); + } else { + snprintf(testpath, len, "%s/%s", srcpath, *p); + } + uint8_t dst_is_root = strcmp(srcpath, "/") == 0; + size_t dst_len = dst_is_root ? strlen(*p) + 1 : strlen(dstpath) + 1 + strlen(*p) + 1; + char *newdst = (char *) malloc(dst_len); + if (dst_is_root) { + snprintf(newdst, dst_len, "/%s", *p); + } else { + snprintf(newdst, dst_len, "%s/%s", dstpath, *p); + } + if (!get_file(afc, testpath, newdst, force_overwrite, recursive_get)) { + succeed = 0; break; } + free(testpath); + free(newdst); p++; } + afc_dictionary_free(entries); + } else { + succeed = get_single_file(afc, srcpath, dstpath, file_size, force_overwrite); } - uint64_t fh = 0; - afc_error_t err = afc_file_open(afc, srcpath, AFC_FOPEN_RDONLY, &fh); - if (err != AFC_E_SUCCESS) { - free(srcpath); - free(dstpath); - printf("Error: Failed to open file '%s': %s (%d)\n", argv[0], afc_strerror(err), err); + return succeed; +} + +static void handle_get(afc_client_t afc, int argc, char **argv) +{ + if (argc < 1) { + printf("Error: Invalid number of arguments\n"); return; } - FILE *f = fopen(dstpath, "wb"); - if (!f && errno == EISDIR) { - const char* basen = path_get_basename(argv[0]); - size_t len = strlen(dstpath) + 1 + strlen(basen) + 1; - char* newdst = (char*)malloc(len); - snprintf(newdst, len, "%s/%s", dstpath, basen); - f = fopen(newdst, "wb"); + uint8_t force_overwrite = 0, recursive_get = 0; + char *srcpath = NULL; + char *dstpath = NULL; + int i = 0; + for ( ; i < argc; i++) { + if (!strcmp(argv[i], "--")) { + i++; + break; + } else if (!strcmp(argv[i], "-r")) { + recursive_get = 1; + } else if (!strcmp(argv[i], "-f")) { + force_overwrite = 1; + } else if (!strcmp(argv[i], "-rf") || !strcmp(argv[i], "-fr")) { + recursive_get = 1; + force_overwrite = 1; + } else { + break; + } + } + if (argc - i == 1) { + char *tmp = strdup(argv[i]); + size_t src_len = strlen(tmp); + if (src_len > 1 && tmp[src_len - 1] == '/') { + tmp[src_len - 1] = '\0'; + } + srcpath = get_absolute_path(tmp); + dstpath = strdup(path_get_basename(tmp)); + free(tmp); + } else if (argc - i == 2) { + char *tmp = strdup(argv[i]); + size_t src_len = strlen(tmp); + if (src_len > 1 && tmp[src_len - 1] == '/') { + tmp[src_len - 1] = '\0'; + } + srcpath = get_absolute_path(tmp); + dstpath = strdup(argv[i + 1]); + size_t dst_len = strlen(dstpath); + if (dst_len > 1 && dstpath[dst_len - 1] == '/') { + dstpath[dst_len - 1] = '\0'; + } + free(tmp); + } + + // target is a directory, put file under this target + if (is_directory(dstpath)) { + const char *basen = path_get_basename(argv[0]); + uint8_t dst_is_root = strcmp(dstpath, "/") == 0; + size_t len = dst_is_root ? (strlen(basen) + 1) : (strlen(dstpath) + 1 + strlen(basen) + 1); + char *newdst = (char *) malloc(len); + if (dst_is_root) { + snprintf(newdst, len, "/%s", basen); + } else { + snprintf(newdst, len, "%s/%s", dstpath, basen); + } + get_file(afc, srcpath, newdst, force_overwrite, recursive_get); + free(srcpath); free(newdst); + free(dstpath); + } else { + // target is not a dir or does not exist, just try to create or rewrite it + get_file(afc, srcpath, dstpath, force_overwrite, recursive_get); + free(srcpath); + free(dstpath); } - if (f) { - struct timeval t1; - struct timeval t2; - struct timeval tdiff; - size_t bufsize = 0x100000; - char* buf = malloc(bufsize); - size_t total = 0; - int progress = 0; - int lastprog = 0; - if (file_size > 0x400000) { - progress = 1; - gettimeofday(&t1, NULL); - } - while (err == AFC_E_SUCCESS) { - uint32_t bytes_read = 0; - size_t chunk = 0; - err = afc_file_read(afc, fh, buf, bufsize, &bytes_read); - if (bytes_read == 0) { - break; - } - while (chunk < bytes_read) { - size_t wr = fwrite(buf+chunk, 1, bytes_read-chunk, f); - if (wr == 0) { - if (progress) { - printf("\n"); - } - printf("Error: Failed to write to local file\n"); - break; +} + +static uint8_t put_single_file(afc_client_t afc, const char *srcpath, const char *dstpath, uint8_t force_overwrite) +{ + char **info = NULL; + afc_error_t ret = afc_get_file_info(afc, dstpath, &info); + // file exists, only overwrite with '-f' option was set + if (ret == AFC_E_SUCCESS && info) { + afc_dictionary_free(info); + if (!force_overwrite) { + printf("Error: Failed to write into existing file without '-f' option: %s\n", dstpath); + return 0; + } + } + FILE *f = fopen(srcpath, "rb"); + if (!f) { + printf("Error: Failed to open local file '%s': %s\n", srcpath, strerror(errno)); + return 0; + } + struct timeval t1; + struct timeval t2; + struct timeval tdiff; + struct stat fst; + int progress = 0; + size_t bufsize = 0x100000; + char *buf = malloc(bufsize); + + fstat(fileno(f), &fst); + if (fst.st_size >= 0x400000) { + progress = 1; + gettimeofday(&t1, NULL); + } + size_t total = 0; + int lastprog = 0; + uint64_t fh = 0; + afc_error_t err = afc_file_open(afc, dstpath, AFC_FOPEN_RW, &fh); + uint8_t succeed = 1; + while (err == AFC_E_SUCCESS) { + uint32_t bytes_read = fread(buf, 1, bufsize, f); + if (bytes_read == 0) { + if (!feof(f)) { + if (progress) { + printf("\n"); } - chunk += wr; + printf("Error: Failed to read from local file\n"); + succeed = 0; } - total += chunk; - if (progress) { - int prog = (int)((double)total / (double)file_size * 100.0f); - if (prog > lastprog) { - gettimeofday(&t2, NULL); - timeval_subtract(&tdiff, &t2, &t1); - double time_in_sec = (double)tdiff.tv_sec + (double)tdiff.tv_usec/1000000; - printf("\r%d%% (%0.1f MB/s) ", prog, (double)total/1048576.0f / time_in_sec); - fflush(stdout); - lastprog = prog; + break; + } + uint32_t chunk = 0; + while (chunk < bytes_read) { + uint32_t bytes_written = 0; + err = afc_file_write(afc, fh, buf + chunk, bytes_read - chunk, &bytes_written); + if (err != AFC_E_SUCCESS) { + if (progress) { + printf("\n"); } + printf("Error: Failed to write to device file\n"); + succeed = 0; + break; } + chunk += bytes_written; } + total += chunk; if (progress) { - printf("\n"); + int prog = (int) ((double) total / (double) fst.st_size * 100.0f); + if (prog > lastprog) { + gettimeofday(&t2, NULL); + timeval_subtract(&tdiff, &t2, &t1); + double time_in_sec = (double) tdiff.tv_sec + (double) tdiff.tv_usec / 1000000; + printf("\r%d%% (%0.1f MB/s) ", prog, (double) total / 1048576.0f / time_in_sec); + fflush(stdout); + lastprog = prog; + } } - if (err != AFC_E_SUCCESS) { - printf("Error: Failed to read from file '%s': %s (%d)\n", argv[0], afc_strerror(err), err); + } + free(buf); + afc_file_close(afc, fh); + fclose(f); + return succeed; +} + +static uint8_t put_file(afc_client_t afc, const char *srcpath, const char *dstpath, uint8_t force_overwrite, uint8_t recursive_put) +{ + if (is_directory(srcpath)) { + if (!recursive_put) { + printf("Error: Failed to put directory without '-r' option: %s\n", srcpath); + return 0; + } + char **info = NULL; + afc_error_t err = afc_get_file_info(afc, dstpath, &info); + //create if target directory does not exist + afc_dictionary_free(info); + if (err == AFC_E_OBJECT_NOT_FOUND) { + err = afc_make_directory(afc, dstpath); + if (err != AFC_E_SUCCESS) { + printf("Error: Failed to create directory '%s': %s (%d)\n", dstpath, afc_strerror(err), err); + return 0; + } + } else if (!force_overwrite) { + printf("Error: Failed to put existing directory without '-f' option: %s\n", dstpath); + return 0; + } + afc_get_file_info(afc, dstpath, &info); + uint8_t is_dir = 0; + if (info) { + char **p = info; + while (p && *p) { + if (!strcmp(*p, "st_ifmt")) { + p++; + is_dir = !strcmp(*p, "S_IFDIR"); + break; + } + p++; + } + afc_dictionary_free(info); + } + if (!is_dir) { + printf("Error: Failed to create or access directory: '%s'\n", dstpath); + return 0; + } + + // walk dir recursively to put files + DIR *cur_dir = opendir(srcpath); + if (cur_dir) { + struct dirent *ep; + while ((ep = readdir(cur_dir))) { + if ((strcmp(ep->d_name, ".") == 0) || (strcmp(ep->d_name, "..") == 0)) { + continue; + } + char *fpath = string_build_path(srcpath, ep->d_name, NULL); + if (fpath) { + uint8_t dst_is_root = strcmp(dstpath, "/") == 0; + size_t len = dst_is_root ? strlen(ep->d_name) + 1 : strlen(dstpath) + 1 + strlen(ep->d_name) + 1; + char *newdst = (char *) malloc(len); + if (dst_is_root) { + snprintf(newdst, len, "/%s", ep->d_name); + } else { + snprintf(newdst, len, "%s/%s", dstpath, ep->d_name); + } + if (!put_file(afc, fpath, newdst, force_overwrite, recursive_put)) { + free(newdst); + free(fpath); + return 0; + } + free(newdst); + free(fpath); + } + } + closedir(cur_dir); + } else { + printf("Error: Failed to visit directory: '%s': %s\n", srcpath, strerror(errno)); + return 0; } - free(buf); - fclose(f); } else { - printf("Error: Failed to open local file '%s': %s\n", dstpath, strerror(errno)); + return put_single_file(afc, srcpath, dstpath, force_overwrite); } - afc_file_close(afc, fh); - free(srcpath); + return 1; } -static void handle_put(afc_client_t afc, int argc, char** argv) +static void handle_put(afc_client_t afc, int argc, char **argv) { - if (argc < 1 || argc > 2) { + if (argc < 1) { printf("Error: Invalid number of arguments\n"); return; } - + int i = 0; + uint8_t force_overwrite = 0, recursive_put = 0; + for ( ; i < argc; i++) { + if (!strcmp(argv[i], "--")) { + i++; + break; + } else if (!strcmp(argv[i], "-r")) { + recursive_put = 1; + } else if (!strcmp(argv[i], "-f")) { + force_overwrite = 1; + } else if (!strcmp(argv[i], "-rf") || !strcmp(argv[i], "-fr")) { + recursive_put = 1; + force_overwrite = 1; + } else { + break; + } + } + if (i >= argc) { + printf("Error: Invalid number of arguments\n"); + return; + } + char *srcpath = strdup(argv[i]); + size_t src_len = strlen(srcpath); + if (src_len > 1 && srcpath[src_len - 1] == '/') { + srcpath[src_len - 1] = '\0'; + } char *dstpath = NULL; - if (argc == 1) { - dstpath = get_absolute_path(path_get_basename(argv[0])); + if (argc - i == 1) { + dstpath = get_absolute_path(path_get_basename(srcpath)); + } else if (argc - i == 2) { + char *tmp = strdup(argv[i + 1]); + size_t dst_len = strlen(tmp); + if (dst_len > 1 && tmp[dst_len - 1] == '/') { + tmp[dst_len - 1] = '\0'; + } + dstpath = get_absolute_path(tmp); + free(tmp); } else { - dstpath = get_absolute_path(argv[1]); + printf("Error: Invalid number of arguments\n"); + return; } - - uint64_t fh = 0; - FILE *f = fopen(argv[0], "rb"); - if (f) { - afc_error_t err = afc_file_open(afc, dstpath, AFC_FOPEN_RW, &fh); - if (err == AFC_E_OBJECT_IS_DIR) { - const char* basen = path_get_basename(argv[0]); - size_t len = strlen(dstpath) + 1 + strlen(basen) + 1; - char* newdst = (char*)malloc(len); - snprintf(newdst, len, "%s/%s", dstpath, basen); - free(dstpath); - dstpath = get_absolute_path(newdst); - free(newdst); - err = afc_file_open(afc, dstpath, AFC_FOPEN_RW, &fh); - } - if (err != AFC_E_SUCCESS) { - printf("Error: Failed to open file '%s' on device: %s (%d)\n", argv[1], afc_strerror(err), err); - } else { - struct timeval t1; - struct timeval t2; - struct timeval tdiff; - struct stat fst; - int progress = 0; - size_t bufsize = 0x100000; - char* buf = malloc(bufsize); - - fstat(fileno(f), &fst); - if (fst.st_size >= 0x400000) { - progress = 1; - gettimeofday(&t1, NULL); - } - size_t total = 0; - int lastprog = 0; - while (err == AFC_E_SUCCESS) { - uint32_t bytes_read = fread(buf, 1, bufsize, f); - if (bytes_read == 0) { - if (!feof(f)) { - if (progress) { - printf("\n"); - } - printf("Error: Failed to read from local file\n"); - } + char **info = NULL; + afc_error_t err = afc_get_file_info(afc, dstpath, &info); + // target does not exist, put directly + if (err == AFC_E_OBJECT_NOT_FOUND) { + put_file(afc, srcpath, dstpath, force_overwrite, recursive_put); + free(srcpath); + free(dstpath); + } else { + uint8_t is_dir = 0; + if (info) { + char **p = info; + while (p && *p) { + if (!strcmp(*p, "st_ifmt")) { + p++; + is_dir = !strcmp(*p, "S_IFDIR"); break; } - uint32_t chunk = 0; - while (chunk < bytes_read) { - uint32_t bytes_written = 0; - err = afc_file_write(afc, fh, buf+chunk, bytes_read-chunk, &bytes_written); - if (err != AFC_E_SUCCESS) { - if (progress) { - printf("\n"); - } - printf("Error: Failed to write to device file\n"); - break; - } - chunk += bytes_written; - } - total += chunk; - if (progress) { - int prog = (int)((double)total / (double)fst.st_size * 100.0f); - if (prog > lastprog) { - gettimeofday(&t2, NULL); - timeval_subtract(&tdiff, &t2, &t1); - double time_in_sec = (double)tdiff.tv_sec + (double)tdiff.tv_usec/1000000; - printf("\r%d%% (%0.1f MB/s) ", prog, (double)total/1048576.0f / time_in_sec); - fflush(stdout); - lastprog = prog; - } - } + p++; } - printf("\n"); - free(buf); - afc_file_close(afc, fh); + afc_dictionary_free(info); } - fclose(f); - } else { - printf("Error: Failed to open local file '%s': %s\n", argv[0], strerror(errno)); + // target is a directory, try to put under this directory + if (is_dir) { + const char *basen = path_get_basename(srcpath); + uint8_t dst_is_root = strcmp(dstpath, "/") == 0; + size_t len = dst_is_root ? strlen(basen) + 1 : strlen(dstpath) + 1 + strlen(basen) + 1; + char *newdst = (char *) malloc(len); + if (dst_is_root) { + snprintf(newdst, len, "/%s", basen); + } else { + snprintf(newdst, len, "%s/%s", dstpath, basen); + } + free(dstpath); + dstpath = get_absolute_path(newdst); + free(newdst); + put_file(afc, srcpath, dstpath, force_overwrite, recursive_put); + } else { + //target is common file, rewrite it + put_file(afc, srcpath, dstpath, force_overwrite, recursive_put); + } + free(srcpath); + free(dstpath); } - free(dstpath); } static void handle_pwd(afc_client_t afc, int argc, char** argv) @@ -975,8 +1289,8 @@ static void parse_cmdline(int* p_argc, char*** p_argv, const char* cmdline) } else if (*pos == '"' || *pos == '\'') { if (!qpos) { qpos = pos; - } else { - qpos = NULL; + } else { + qpos = NULL; } pos++; } else if (*pos == '\0' || (!qpos && isspace(*pos))) { @@ -1043,7 +1357,7 @@ static int process_args(afc_client_t afc, int argc, char** argv) handle_file_info(afc, argc-1, argv+1); } else if (!strcmp(argv[0], "ls") || !strcmp(argv[0], "list")) { - handle_list(afc, argc-1, argv+1); + handle_list(afc, argc-1, argv+1); } else if (!strcmp(argv[0], "mv") || !strcmp(argv[0], "rename")) { handle_rename(afc, argc-1, argv+1); @@ -1113,7 +1427,7 @@ static void start_cmdline(afc_client_t afc) break; } } - } + } } static void device_event_cb(const idevice_event_t* event, void* userdata) |