From 1e611a33d524a7c7a31181aca10d1f4a4c9fc1b2 Mon Sep 17 00:00:00 2001 From: Josh Gao Date: Fri, 26 Feb 2016 13:26:55 -0800 Subject: [PATCH] adb: symlinks to dirs count as dirs for pull destination. This matches scp's behavior when pulling a directory that collides with a symlink to a directory. Bug: http://b/27362811 Change-Id: I0936d1ad48f13e24cd382e8e8400cc752bac3b66 --- adb/adb_utils.cpp | 4 ++- adb/test_device.py | 66 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp index 26e376ce6..3333fc61b 100644 --- a/adb/adb_utils.cpp +++ b/adb/adb_utils.cpp @@ -153,7 +153,9 @@ bool mkdirs(const std::string& path) { // - Recursive, so it uses stack space relative to number of directory // components. - if (directory_exists(path)) { + // If path points to a symlink to a directory, that's fine. + struct stat sb; + if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) { return true; } diff --git a/adb/test_device.py b/adb/test_device.py index 18174a2ae..2acc444ca 100644 --- a/adb/test_device.py +++ b/adb/test_device.py @@ -845,6 +845,72 @@ class FileOperationsTest(DeviceTest): if host_dir is not None: shutil.rmtree(host_dir) + def test_pull_dir_symlink(self): + """Pull a directory into a symlink to a directory. + + Bug: http://b/27362811 + """ + if os.name != "posix": + raise unittest.SkipTest('requires POSIX') + + try: + host_dir = tempfile.mkdtemp() + real_dir = os.path.join(host_dir, "dir") + symlink = os.path.join(host_dir, "symlink") + os.mkdir(real_dir) + os.symlink(real_dir, symlink) + + self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR]) + self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR]) + + # Populate device directory with random files. + temp_files = make_random_device_files( + self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32) + + self.device.pull(remote=self.DEVICE_TEMP_DIR, local=symlink) + + for temp_file in temp_files: + host_path = os.path.join( + real_dir, posixpath.basename(self.DEVICE_TEMP_DIR), + temp_file.base_name) + self._verify_local(temp_file.checksum, host_path) + + self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR]) + finally: + if host_dir is not None: + shutil.rmtree(host_dir) + + def test_pull_dir_symlink_collision(self): + """Pull a directory into a colliding symlink to directory.""" + if os.name != "posix": + raise unittest.SkipTest('requires POSIX') + + try: + host_dir = tempfile.mkdtemp() + real_dir = os.path.join(host_dir, "real") + tmp_dirname = os.path.basename(self.DEVICE_TEMP_DIR) + symlink = os.path.join(host_dir, tmp_dirname) + os.mkdir(real_dir) + os.symlink(real_dir, symlink) + + self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR]) + self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR]) + + # Populate device directory with random files. + temp_files = make_random_device_files( + self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32) + + self.device.pull(remote=self.DEVICE_TEMP_DIR, local=host_dir) + + for temp_file in temp_files: + host_path = os.path.join(real_dir, temp_file.base_name) + self._verify_local(temp_file.checksum, host_path) + + self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR]) + finally: + if host_dir is not None: + shutil.rmtree(host_dir) + def test_pull_symlink_dir(self): """Pull a symlink to a directory of symlinks to files.""" try: