diff --git a/adb/Android.mk b/adb/Android.mk index 695cbe08c4c29c41720e7973ee560ba3152fd42d..8f56d744e8181f660ee399eae84eb963b3c12675 100644 --- a/adb/Android.mk +++ b/adb/Android.mk @@ -62,6 +62,7 @@ LIBADB_TEST_SRCS := \ fdevent_test.cpp \ socket_test.cpp \ sysdeps_test.cpp \ + sysdeps/stat_test.cpp \ transport_test.cpp \ LIBADB_CFLAGS := \ @@ -89,6 +90,7 @@ LIBADB_linux_SRC_FILES := \ LIBADB_windows_SRC_FILES := \ sysdeps_win32.cpp \ + sysdeps/win32/stat.cpp \ usb_windows.cpp \ LIBADB_TEST_windows_SRCS := \ diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp index d5c33d36fd8e6388137b6c58351cbc94a4da9954..a78f8d356a060df25fc2c6e201929cd669cc2a80 100644 --- a/adb/adb_utils_test.cpp +++ b/adb/adb_utils_test.cpp @@ -123,14 +123,20 @@ TEST(adb_utils, adb_dirname) { void test_mkdirs(const std::string basepath) { // Test creating a directory hierarchy. - EXPECT_TRUE(mkdirs(basepath)); + ASSERT_TRUE(mkdirs(basepath)); // Test finding an existing directory hierarchy. - EXPECT_TRUE(mkdirs(basepath)); + ASSERT_TRUE(mkdirs(basepath)); + // Test mkdirs on an existing hierarchy with a trailing slash. + ASSERT_TRUE(mkdirs(basepath + '/')); +#if defined(_WIN32) + ASSERT_TRUE(mkdirs(basepath + '\\')); +#endif + const std::string filepath = basepath + "/file"; // Verify that the hierarchy was created by trying to create a file in it. - EXPECT_NE(-1, adb_creat(filepath.c_str(), 0600)); + ASSERT_NE(-1, adb_creat(filepath.c_str(), 0600)); // If a file exists where we want a directory, the operation should fail. - EXPECT_FALSE(mkdirs(filepath)); + ASSERT_FALSE(mkdirs(filepath)); } TEST(adb_utils, mkdirs) { diff --git a/adb/sysdeps.h b/adb/sysdeps.h index 75dcc86313b02afa785296d8d15b8ad45475bdc2..bd19f88cde6aa6205b9c998d791ddc9867c4735d 100644 --- a/adb/sysdeps.h +++ b/adb/sysdeps.h @@ -34,6 +34,8 @@ #include <android-base/unique_fd.h> #include <android-base/utf8.h> +#include "sysdeps/stat.h" + /* * TEMP_FAILURE_RETRY is defined by some, but not all, versions of * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's @@ -199,8 +201,6 @@ static __inline__ void close_on_exec(int fd) /* nothing really */ } -#define lstat stat /* no symlinks on Win32 */ - #define S_ISLNK(m) 0 /* no symlinks on Win32 */ extern int adb_unlink(const char* path); @@ -307,27 +307,6 @@ static __inline__ int adb_is_absolute_host_path(const char* path) { return isalpha(path[0]) && path[1] == ':' && path[2] == '\\'; } -// We later define a macro mapping 'stat' to 'adb_stat'. This causes: -// struct stat s; -// stat(filename, &s); -// To turn into the following: -// struct adb_stat s; -// adb_stat(filename, &s); -// To get this to work, we need to make 'struct adb_stat' the same as -// 'struct stat'. Note that this definition of 'struct adb_stat' uses the -// *current* macro definition of stat, so it may actually be inheriting from -// struct _stat32i64 (or some other remapping). -struct adb_stat : public stat {}; - -static_assert(sizeof(struct adb_stat) == sizeof(struct stat), - "structures should be the same"); - -extern int adb_stat(const char* f, struct adb_stat* s); - -// stat is already a macro, undefine it so we can redefine it. -#undef stat -#define stat adb_stat - // UTF-8 versions of POSIX APIs. extern DIR* adb_opendir(const char* dirname); extern struct dirent* adb_readdir(DIR* dir); diff --git a/adb/sysdeps/stat.h b/adb/sysdeps/stat.h new file mode 100644 index 0000000000000000000000000000000000000000..595359529c7d38598e2d5af7b27cc39b488beca4 --- /dev/null +++ b/adb/sysdeps/stat.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#if defined(_WIN32) +// stat is broken on Win32: stat on a path with a trailing slash or backslash will always fail with +// ENOENT. +int adb_stat(const char* path, struct adb_stat* buf); + +// We later define a macro mapping 'stat' to 'adb_stat'. This causes: +// struct stat s; +// stat(filename, &s); +// To turn into the following: +// struct adb_stat s; +// adb_stat(filename, &s); +// To get this to work, we need to make 'struct adb_stat' the same as +// 'struct stat'. Note that this definition of 'struct adb_stat' uses the +// *current* macro definition of stat, so it may actually be inheriting from +// struct _stat32i64 (or some other remapping). +struct adb_stat : public stat {}; + +#undef stat +#define stat adb_stat + +// Windows doesn't have lstat. +#define lstat adb_stat + +#endif diff --git a/adb/sysdeps/stat_test.cpp b/adb/sysdeps/stat_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2c2e0eeeb34c08a5ec2688ad240d995d46855fa2 --- /dev/null +++ b/adb/sysdeps/stat_test.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <string> + +#include <android-base/test_utils.h> +#include <gtest/gtest.h> + +#include "adb_utils.h" +#include "sysdeps.h" + +TEST(sysdeps, stat) { + TemporaryDir td; + TemporaryFile tf; + + struct stat st; + ASSERT_EQ(0, stat(td.path, &st)); + ASSERT_FALSE(S_ISREG(st.st_mode)); + ASSERT_TRUE(S_ISDIR(st.st_mode)); + + ASSERT_EQ(0, stat((std::string(td.path) + '/').c_str(), &st)); + ASSERT_TRUE(S_ISDIR(st.st_mode)); + +#if defined(_WIN32) + ASSERT_EQ(0, stat((std::string(td.path) + '\\').c_str(), &st)); + ASSERT_TRUE(S_ISDIR(st.st_mode)); +#endif + + std::string nonexistent_path = std::string(td.path) + "/nonexistent"; + ASSERT_EQ(-1, stat(nonexistent_path.c_str(), &st)); + ASSERT_EQ(ENOENT, errno); + + ASSERT_EQ(-1, stat((nonexistent_path + "/").c_str(), &st)); + ASSERT_EQ(ENOENT, errno); + +#if defined(_WIN32) + ASSERT_EQ(-1, stat((nonexistent_path + "\\").c_str(), &st)); + ASSERT_EQ(ENOENT, errno); +#endif + + ASSERT_EQ(0, stat(tf.path, &st)); + ASSERT_TRUE(S_ISREG(st.st_mode)); + ASSERT_FALSE(S_ISDIR(st.st_mode)); + + ASSERT_EQ(-1, stat((std::string(tf.path) + '/').c_str(), &st)); + ASSERT_EQ(ENOTDIR, errno); + +#if defined(_WIN32) + ASSERT_EQ(-1, stat((std::string(tf.path) + '\\').c_str(), &st)); + ASSERT_EQ(ENOTDIR, errno); +#endif +} diff --git a/adb/sysdeps/win32/stat.cpp b/adb/sysdeps/win32/stat.cpp new file mode 100644 index 0000000000000000000000000000000000000000..844c1ce9ae7893e36560fa9c26b5ba36d7dd7c22 --- /dev/null +++ b/adb/sysdeps/win32/stat.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sysdeps/stat.h" + +#include <errno.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <string> + +#include <android-base/utf8.h> + +// Version of stat() that takes a UTF-8 path. +int adb_stat(const char* path, struct adb_stat* s) { +// This definition of wstat seems to be missing from <sys/stat.h>. +#if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64) +#ifdef _USE_32BIT_TIME_T +#define wstat _wstat32i64 +#else +#define wstat _wstat64 +#endif +#else +// <sys/stat.h> has a function prototype for wstat() that should be available. +#endif + + std::wstring path_wide; + if (!android::base::UTF8ToWide(path, &path_wide)) { + errno = ENOENT; + return -1; + } + + // If the path has a trailing slash, stat will fail with ENOENT regardless of whether the path + // is a directory or not. + bool expected_directory = false; + while (*path_wide.rbegin() == u'/' || *path_wide.rbegin() == u'\\') { + path_wide.pop_back(); + expected_directory = true; + } + + struct adb_stat st; + int result = wstat(path_wide.c_str(), &st); + if (result == 0 && expected_directory) { + if (!S_ISDIR(st.st_mode)) { + errno = ENOTDIR; + return -1; + } + } + + memcpy(s, &st, sizeof(st)); + return result; +} diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp index bc09fdcfbc0a5efe547ebde8cc8da135cc0c933d..e6416809c4ba3ff22181dc147c28d60ef6367287 100644 --- a/adb/sysdeps_win32.cpp +++ b/adb/sysdeps_win32.cpp @@ -2298,30 +2298,6 @@ int unix_open(const char* path, int options, ...) { } } -// Version of stat() that takes a UTF-8 path. -int adb_stat(const char* path, struct adb_stat* s) { -#pragma push_macro("wstat") -// This definition of wstat seems to be missing from <sys/stat.h>. -#if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64) -#ifdef _USE_32BIT_TIME_T -#define wstat _wstat32i64 -#else -#define wstat _wstat64 -#endif -#else -// <sys/stat.h> has a function prototype for wstat() that should be available. -#endif - - std::wstring path_wide; - if (!android::base::UTF8ToWide(path, &path_wide)) { - return -1; - } - - return wstat(path_wide.c_str(), s); - -#pragma pop_macro("wstat") -} - // Version of opendir() that takes a UTF-8 path. DIR* adb_opendir(const char* path) { std::wstring path_wide;