Fix ftw/nftw to only report unreadable directories once.
Also remove all the copy & paste.
Bug: http://b/28197840
Change-Id: Ia43e9ffd838dabb511a6e54403d6f62066383e4d
diff --git a/tests/ftw_test.cpp b/tests/ftw_test.cpp
index b7e5bd5..ea494ba 100644
--- a/tests/ftw_test.cpp
+++ b/tests/ftw_test.cpp
@@ -16,6 +16,7 @@
#include <ftw.h>
+#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
@@ -24,6 +25,7 @@
#include "TemporaryFile.h"
+#include <android-base/stringprintf.h>
#include <gtest/gtest.h>
static void MakeTree(const char* root) {
@@ -39,7 +41,7 @@
snprintf(path, sizeof(path), "%s/dangler", root);
ASSERT_EQ(0, symlink("/does-not-exist", path));
snprintf(path, sizeof(path), "%s/symlink", root);
- ASSERT_EQ(0, symlink("sub2", path));
+ ASSERT_EQ(0, symlink("dir/sub", path));
int fd;
snprintf(path, sizeof(path), "%s/regular", root);
@@ -51,8 +53,21 @@
ASSERT_TRUE(fpath != NULL);
ASSERT_TRUE(sb != NULL);
+ // Was it a case where the struct stat we're given is meaningless?
+ if (tflag == FTW_NS || tflag == FTW_SLN) {
+ // If so, double-check that we really can't stat.
+ struct stat sb;
+ EXPECT_EQ(-1, stat(fpath, &sb));
+ return;
+ }
+
+ // Otherwise check that the struct stat matches the type flag.
if (S_ISDIR(sb->st_mode)) {
- EXPECT_TRUE(tflag == FTW_D || tflag == FTW_DNR || tflag == FTW_DP) << fpath;
+ if (access(fpath, R_OK) == 0) {
+ EXPECT_TRUE(tflag == FTW_D || tflag == FTW_DP) << fpath << ' ' << tflag;
+ } else {
+ EXPECT_EQ(FTW_DNR, tflag) << fpath;
+ }
} else if (S_ISLNK(sb->st_mode)) {
EXPECT_EQ(FTW_SL, tflag) << fpath;
} else {
@@ -60,7 +75,7 @@
}
}
-void sanity_check_nftw(const char* fpath, const struct stat* sb, int tflag, struct FTW* ftwbuf) {
+void sanity_check_nftw(const char* fpath, const struct stat* sb, int tflag, FTW* ftwbuf) {
sanity_check_ftw(fpath, sb, tflag);
ASSERT_EQ('/', fpath[ftwbuf->base - 1]) << fpath;
}
@@ -75,12 +90,12 @@
return 0;
}
-int check_nftw(const char* fpath, const struct stat* sb, int tflag, struct FTW* ftwbuf) {
+int check_nftw(const char* fpath, const struct stat* sb, int tflag, FTW* ftwbuf) {
sanity_check_nftw(fpath, sb, tflag, ftwbuf);
return 0;
}
-int check_nftw64(const char* fpath, const struct stat64* sb, int tflag, struct FTW* ftwbuf) {
+int check_nftw64(const char* fpath, const struct stat64* sb, int tflag, FTW* ftwbuf) {
sanity_check_nftw(fpath, reinterpret_cast<const struct stat*>(sb), tflag, ftwbuf);
return 0;
}
@@ -108,3 +123,33 @@
MakeTree(root.dirname);
ASSERT_EQ(0, nftw64(root.dirname, check_nftw64, 128, 0));
}
+
+template <typename StatT>
+static int bug_28197840_ftw(const char* path, const StatT*, int flag) {
+ EXPECT_EQ(strstr(path, "unreadable") != nullptr ? FTW_DNR : FTW_D, flag) << path;
+ return 0;
+}
+
+template <typename StatT>
+static int bug_28197840_nftw(const char* path, const StatT* sb, int flag, FTW*) {
+ return bug_28197840_ftw(path, sb, flag);
+}
+
+TEST(ftw, bug_28197840) {
+ // Drop root for this test, because root can still read directories even if
+ // permissions would imply otherwise.
+ if (getuid() == 0) {
+ passwd* pwd = getpwnam("shell");
+ ASSERT_EQ(0, setuid(pwd->pw_uid));
+ }
+
+ TemporaryDir root;
+
+ std::string path = android::base::StringPrintf("%s/unreadable-directory", root.dirname);
+ ASSERT_EQ(0, mkdir(path.c_str(), 0000)) << path;
+
+ ASSERT_EQ(0, ftw(root.dirname, bug_28197840_ftw<struct stat>, 128));
+ ASSERT_EQ(0, ftw64(root.dirname, bug_28197840_ftw<struct stat64>, 128));
+ ASSERT_EQ(0, nftw(root.dirname, bug_28197840_nftw<struct stat>, 128, FTW_PHYS));
+ ASSERT_EQ(0, nftw64(root.dirname, bug_28197840_nftw<struct stat64>, 128, FTW_PHYS));
+}