diff --git a/adb/commandline.cpp b/adb/commandline.cpp index 2c60485ce..248bb0fd1 100644 --- a/adb/commandline.cpp +++ b/adb/commandline.cpp @@ -33,6 +33,7 @@ #include #include +#include #include #include @@ -101,11 +102,11 @@ static void help() { " will disconnect from all connected TCP/IP devices.\n" "\n" "device commands:\n" - " adb push \n" - " - copy file/dir to device\n" - " adb pull [-a] []\n" - " - copy file/dir from device\n" - " ('-a' means copy timestamp and mode)\n" + " adb push ... \n" + " - copy files/dirs to device\n" + " adb pull [-a] ... \n" + " - copy files/dirs from device\n" + " (-a preserves file timestamp and mode)\n" " adb sync [ ] - copy host->device only if changed\n" " (-l means list but don't copy)\n" " adb shell [-Ttx] - run remote shell interactively\n" @@ -1065,31 +1066,35 @@ static std::string find_product_out_path(const std::string& hint) { return path; } -static void parse_push_pull_args(const char **arg, int narg, - char const **path1, char const **path2, - int *copy_attrs) { - *copy_attrs = 0; +static void parse_push_pull_args(const char** arg, int narg, + std::vector* srcs, + const char** dst, bool* copy_attrs) { + *copy_attrs = false; + srcs->clear(); + bool ignore_flags = false; while (narg > 0) { - if (!strcmp(*arg, "-p")) { - // Silently ignore for backwards compatibility. - } else if (!strcmp(*arg, "-a")) { - *copy_attrs = 1; + if (ignore_flags || *arg[0] != '-') { + srcs->push_back(*arg); } else { - break; + if (!strcmp(*arg, "-p")) { + // Silently ignore for backwards compatibility. + } else if (!strcmp(*arg, "-a")) { + *copy_attrs = true; + } else if (!strcmp(*arg, "--")) { + ignore_flags = true; + } else { + fprintf(stderr, "adb: unrecognized option '%s'\n", *arg); + exit(1); + } } ++arg; --narg; } - if (narg > 0) { - *path1 = *arg; - ++arg; - --narg; - } - - if (narg > 0) { - *path2 = *arg; + if (srcs->size() > 1) { + *dst = srcs->back(); + srcs->pop_back(); } } @@ -1561,20 +1566,22 @@ int adb_commandline(int argc, const char **argv) { return do_sync_ls(argv[1]) ? 0 : 1; } else if (!strcmp(argv[0], "push")) { - int copy_attrs = 0; - const char* lpath = NULL, *rpath = NULL; + bool copy_attrs = false; + std::vector srcs; + const char* dst = nullptr; - parse_push_pull_args(&argv[1], argc - 1, &lpath, &rpath, ©_attrs); - if (!lpath || !rpath || copy_attrs != 0) return usage(); - return do_sync_push(lpath, rpath) ? 0 : 1; + parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs); + if (srcs.empty() || !dst) return usage(); + return do_sync_push(srcs, dst) ? 0 : 1; } else if (!strcmp(argv[0], "pull")) { - int copy_attrs = 0; - const char* rpath = NULL, *lpath = "."; + bool copy_attrs = false; + std::vector srcs; + const char* dst = "."; - parse_push_pull_args(&argv[1], argc - 1, &rpath, &lpath, ©_attrs); - if (!rpath) return usage(); - return do_sync_pull(rpath, lpath, copy_attrs) ? 0 : 1; + parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs); + if (srcs.empty()) return usage(); + return do_sync_pull(srcs, dst, copy_attrs) ? 0 : 1; } else if (!strcmp(argv[0], "install")) { if (argc < 2) return usage(); @@ -1764,8 +1771,9 @@ static int install_app(TransportType transport, const char* serial, int argc, co } int result = -1; - const char* apk_file = argv[last_apk]; - std::string apk_dest = android::base::StringPrintf(where, adb_basename(apk_file).c_str()); + std::vector apk_file = {argv[last_apk]}; + std::string apk_dest = android::base::StringPrintf( + where, adb_basename(argv[last_apk]).c_str()); if (!do_sync_push(apk_file, apk_dest.c_str())) goto cleanup_apk; argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */ result = pm_command(transport, serial, argc, argv); diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp index 3e3bb5740..7a605808f 100644 --- a/adb/file_sync_client.cpp +++ b/adb/file_sync_client.cpp @@ -625,32 +625,55 @@ static bool copy_local_dir_remote(SyncConnection& sc, const char* lpath, const c return true; } -bool do_sync_push(const char* lpath, const char* rpath) { +bool do_sync_push(const std::vector& srcs, const char* dst) { SyncConnection sc; if (!sc.IsValid()) return false; - struct stat st; - if (stat(lpath, &st)) { - sc.Error("cannot stat '%s': %s", lpath, strerror(errno)); - return false; - } - - if (S_ISDIR(st.st_mode)) { - return copy_local_dir_remote(sc, lpath, rpath, false, false); - } - + bool success = true; unsigned mode; - if (!sync_stat(sc, rpath, nullptr, &mode, nullptr)) return false; - std::string path_holder; - if (mode != 0 && S_ISDIR(mode)) { - // If we're copying a local file to a remote directory, - // we really want to copy to remote_dir + "/" + local_filename. - path_holder = android::base::StringPrintf("%s/%s", rpath, adb_basename(lpath).c_str()); - rpath = path_holder.c_str(); + if (!sync_stat(sc, dst, nullptr, &mode, nullptr)) return false; + bool dst_isdir = mode != 0 && S_ISDIR(mode); + + if (!dst_isdir) { + if (srcs.size() > 1) { + sc.Error("target '%s' is not a directory", dst); + return false; + } else { + size_t dst_len = strlen(dst); + if (dst[dst_len - 1] == '/') { + sc.Error("failed to access '%s': Not a directory", dst); + return false; + } + } } - bool result = sync_send(sc, lpath, rpath, st.st_mtime, st.st_mode); + + for (const char* src_path : srcs) { + const char* dst_path = dst; + struct stat st; + if (stat(src_path, &st)) { + sc.Error("cannot stat '%s': %s", src_path, strerror(errno)); + success = false; + continue; + } + + if (S_ISDIR(st.st_mode)) { + success &= copy_local_dir_remote(sc, src_path, dst, false, false); + continue; + } + + std::string path_holder; + if (mode != 0 && S_ISDIR(mode)) { + // If we're copying a local file to a remote directory, + // we really want to copy to remote_dir + "/" + local_filename. + path_holder = android::base::StringPrintf( + "%s/%s", dst_path, adb_basename(src_path).c_str()); + dst_path = path_holder.c_str(); + } + success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode); + } + sc.Print("\n"); - return result; + return success; } struct sync_ls_build_list_cb_args { @@ -733,7 +756,7 @@ static int set_time_and_mode(const char *lpath, time_t time, unsigned int mode) } static bool copy_remote_dir_local(SyncConnection& sc, const char* rpath, const char* lpath, - int copy_attrs) { + bool copy_attrs) { // Make sure that both directory paths end in a slash. std::string rpath_clean(rpath); std::string lpath_clean(lpath); @@ -774,43 +797,80 @@ static bool copy_remote_dir_local(SyncConnection& sc, const char* rpath, const c return true; } -bool do_sync_pull(const char* rpath, const char* lpath, int copy_attrs) { +bool do_sync_pull(const std::vector& srcs, const char* dst, + bool copy_attrs) { SyncConnection sc; if (!sc.IsValid()) return false; + bool success = true; unsigned mode, time; - if (!sync_stat(sc, rpath, &time, &mode, nullptr)) return false; - if (mode == 0) { - sc.Error("remote object '%s' does not exist", rpath); - return false; + struct stat st; + if (stat(dst, &st)) { + // If we're only pulling one file, the destination path might point to + // a path that doesn't exist yet. + if (srcs.size() != 1 || errno != ENOENT) { + sc.Error("cannot stat '%s': %s", dst, strerror(errno)); + return false; + } } - if (S_ISREG(mode) || S_ISLNK(mode) || S_ISCHR(mode) || S_ISBLK(mode)) { - std::string path_holder; - struct stat st; - if (stat(lpath, &st) == 0) { - if (S_ISDIR(st.st_mode)) { - // If we're copying a remote file to a local directory, - // we really want to copy to local_dir + "/" + basename(remote). - path_holder = android::base::StringPrintf("%s/%s", lpath, adb_basename(rpath).c_str()); - lpath = path_holder.c_str(); - } - } - if (!sync_recv(sc, rpath, lpath)) { + bool dst_isdir = S_ISDIR(st.st_mode); + if (!dst_isdir) { + if (srcs.size() > 1) { + sc.Error("target '%s' is not a directory", dst); return false; } else { - if (copy_attrs && set_time_and_mode(lpath, time, mode)) { + size_t dst_len = strlen(dst); + if (dst[dst_len - 1] == '/') { + sc.Error("failed to access '%s': Not a directory", dst); return false; } } - sc.Print("\n"); - return true; - } else if (S_ISDIR(mode)) { - return copy_remote_dir_local(sc, rpath, lpath, copy_attrs); } - sc.Error("remote object '%s' not a file or directory", rpath); - return false; + for (const char* src_path : srcs) { + const char* dst_path = dst; + if (!sync_stat(sc, src_path, &time, &mode, nullptr)) return false; + if (mode == 0) { + sc.Error("remote object '%s' does not exist", src_path); + success = false; + continue; + } + + if (S_ISREG(mode) || S_ISLNK(mode) || S_ISCHR(mode) || S_ISBLK(mode)) { + std::string path_holder; + struct stat st; + if (stat(dst_path, &st) == 0) { + if (S_ISDIR(st.st_mode)) { + // If we're copying a remote file to a local directory, + // we really want to copy to local_dir + "/" + + // basename(remote). + path_holder = android::base::StringPrintf( + "%s/%s", dst_path, adb_basename(src_path).c_str()); + dst_path = path_holder.c_str(); + } + } + if (!sync_recv(sc, src_path, dst_path)) { + success = false; + continue; + } else { + if (copy_attrs && set_time_and_mode(dst_path, time, mode)) { + success = false; + continue; + } + } + } else if (S_ISDIR(mode)) { + success &= copy_remote_dir_local(sc, src_path, dst_path, copy_attrs); + continue; + } else { + sc.Error("remote object '%s' not a file or directory", src_path); + success = false; + continue; + } + } + + sc.Print("\n"); + return success; } bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only) { diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h index 7c4d55446..38382c1ed 100644 --- a/adb/file_sync_service.h +++ b/adb/file_sync_service.h @@ -18,6 +18,7 @@ #define _FILE_SYNC_SERVICE_H_ #include +#include #define MKID(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24)) @@ -64,9 +65,11 @@ union syncmsg { void file_sync_service(int fd, void* cookie); bool do_sync_ls(const char* path); -bool do_sync_push(const char* lpath, const char* rpath); +bool do_sync_push(const std::vector& srcs, const char* dst); +bool do_sync_pull(const std::vector& srcs, const char* dst, + bool copy_attrs); + bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only); -bool do_sync_pull(const char* rpath, const char* lpath, int copy_attrs); #define SYNC_DATA_MAX (64*1024)