adb: don't explode directories when pushing/pulling.

Previously, `adb pull /data/local/tmp` would spew all of the contents of
/data/local/tmp into the current directory. This patch makes push/pull
keep directories intact.

Bug: http://b/25394682
Change-Id: I2304ae9e61745a2b9536f58a6012640bf8ff422a
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index 90bf456..736411c 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -657,8 +657,9 @@
     unsigned dst_mode;
     if (!sync_stat(sc, dst, nullptr, &dst_mode, nullptr)) return false;
     bool dst_exists = (dst_mode != 0);
+    bool dst_isdir = S_ISDIR(dst_mode);
 
-    if (!S_ISDIR(dst_mode)) {
+    if (!dst_isdir) {
         if (srcs.size() > 1) {
             sc.Error("target '%s' is not a directory", dst);
             return false;
@@ -684,12 +685,30 @@
         }
 
         if (S_ISDIR(st.st_mode)) {
-            success &= copy_local_dir_remote(sc, src_path, dst, false, false);
+            std::string dst_dir = dst;
+
+            // If the destination path existed originally, the source directory
+            // should be copied as a child of the destination.
+            if (dst_exists) {
+                if (!dst_isdir) {
+                    sc.Error("target '%s' is not a directory", dst);
+                    return false;
+                }
+                // dst is a POSIX path, so we don't want to use the sysdeps
+                // helpers here.
+                if (dst_dir.back() != '/') {
+                    dst_dir.push_back('/');
+                }
+                dst_dir.append(adb_basename(src_path));
+            }
+
+            success &= copy_local_dir_remote(sc, src_path, dst_dir.c_str(),
+                                             false, false);
             continue;
         }
 
         std::string path_holder;
-        if (S_ISDIR(dst_mode)) {
+        if (dst_isdir) {
             // 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(
@@ -859,7 +878,8 @@
         }
     }
 
-    if (dst_exists && !S_ISDIR(st.st_mode)) {
+    bool dst_isdir = dst_exists && S_ISDIR(st.st_mode);
+    if (!dst_isdir) {
         if (srcs.size() > 1) {
             sc.Error("target '%s' is not a directory", dst);
             return false;
@@ -890,16 +910,12 @@
         if (S_ISREG(src_mode) || S_ISLNK(src_mode)) {
             // TODO(b/25601283): symlinks shouldn't be handled as files.
             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 (dst_isdir) {
+                // 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;
@@ -912,7 +928,23 @@
                 }
             }
         } else if (S_ISDIR(src_mode)) {
-            success &= copy_remote_dir_local(sc, src_path, dst_path, copy_attrs);
+            std::string dst_dir = dst;
+
+            // If the destination path existed originally, the source directory
+            // should be copied as a child of the destination.
+            if (dst_exists) {
+                if (!dst_isdir) {
+                    sc.Error("target '%s' is not a directory", dst);
+                    return false;
+                }
+                if (!adb_is_separator(dst_dir.back())) {
+                    dst_dir.push_back(OS_PATH_SEPARATOR);
+                }
+                dst_dir.append(adb_basename(src_path));
+            }
+
+            success &= copy_remote_dir_local(sc, src_path, dst_dir.c_str(),
+                                             copy_attrs);
             continue;
         } else {
             sc.Error("remote object '%s' not a file or directory", src_path);