1 /*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <ftw.h>
18
19 #include <fcntl.h>
20 #include <pwd.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26
27 #include <android-base/file.h>
28 #include <android-base/stringprintf.h>
29 #include <gtest/gtest.h>
30
31 #include "utils.h"
32
MakeTree(const char * root)33 static void MakeTree(const char* root) {
34 char path[PATH_MAX];
35
36 snprintf(path, sizeof(path), "%s/dir", root);
37 ASSERT_EQ(0, mkdir(path, 0755)) << path;
38 snprintf(path, sizeof(path), "%s/dir/sub", root);
39 ASSERT_EQ(0, mkdir(path, 0555)) << path;
40 snprintf(path, sizeof(path), "%s/unreadable-dir", root);
41 ASSERT_EQ(0, mkdir(path, 0000)) << path;
42
43 snprintf(path, sizeof(path), "%s/dangler", root);
44 ASSERT_EQ(0, symlink("/does-not-exist", path));
45 snprintf(path, sizeof(path), "%s/symlink", root);
46 ASSERT_EQ(0, symlink("dir/sub", path));
47
48 int fd;
49 snprintf(path, sizeof(path), "%s/regular", root);
50 ASSERT_NE(-1, fd = open(path, O_CREAT|O_TRUNC, 0666));
51 ASSERT_EQ(0, close(fd));
52 }
53
smoke_test_ftw(const char * fpath,const struct stat * sb,int tflag)54 void smoke_test_ftw(const char* fpath, const struct stat* sb, int tflag) {
55 ASSERT_TRUE(fpath != nullptr);
56 ASSERT_TRUE(sb != nullptr);
57
58 // Was it a case where the struct stat we're given is meaningless?
59 if (tflag == FTW_NS || tflag == FTW_SLN) {
60 // If so, double-check that we really can't stat.
61 struct stat sb;
62 EXPECT_EQ(-1, stat(fpath, &sb));
63 return;
64 }
65
66 // Otherwise check that the struct stat matches the type flag.
67 if (S_ISDIR(sb->st_mode)) {
68 if (access(fpath, R_OK) == 0) {
69 EXPECT_TRUE(tflag == FTW_D || tflag == FTW_DP) << fpath << ' ' << tflag;
70 } else {
71 EXPECT_EQ(FTW_DNR, tflag) << fpath;
72 }
73 } else if (S_ISLNK(sb->st_mode)) {
74 EXPECT_EQ(FTW_SL, tflag) << fpath;
75 } else {
76 EXPECT_EQ(FTW_F, tflag) << fpath;
77 }
78 }
79
smoke_test_nftw(const char * fpath,const struct stat * sb,int tflag,FTW * ftwbuf)80 void smoke_test_nftw(const char* fpath, const struct stat* sb, int tflag, FTW* ftwbuf) {
81 smoke_test_ftw(fpath, sb, tflag);
82 ASSERT_EQ('/', fpath[ftwbuf->base - 1]) << fpath;
83 }
84
check_ftw(const char * fpath,const struct stat * sb,int tflag)85 int check_ftw(const char* fpath, const struct stat* sb, int tflag) {
86 smoke_test_ftw(fpath, sb, tflag);
87 return 0;
88 }
89
check_ftw64(const char * fpath,const struct stat64 * sb,int tflag)90 int check_ftw64(const char* fpath, const struct stat64* sb, int tflag) {
91 smoke_test_ftw(fpath, reinterpret_cast<const struct stat*>(sb), tflag);
92 return 0;
93 }
94
check_nftw(const char * fpath,const struct stat * sb,int tflag,FTW * ftwbuf)95 int check_nftw(const char* fpath, const struct stat* sb, int tflag, FTW* ftwbuf) {
96 smoke_test_nftw(fpath, sb, tflag, ftwbuf);
97 return 0;
98 }
99
check_nftw64(const char * fpath,const struct stat64 * sb,int tflag,FTW * ftwbuf)100 int check_nftw64(const char* fpath, const struct stat64* sb, int tflag, FTW* ftwbuf) {
101 smoke_test_nftw(fpath, reinterpret_cast<const struct stat*>(sb), tflag, ftwbuf);
102 return 0;
103 }
104
TEST(ftw,ftw)105 TEST(ftw, ftw) {
106 TemporaryDir root;
107 MakeTree(root.path);
108 ASSERT_EQ(0, ftw(root.path, check_ftw, 128));
109 }
110
TEST(ftw,ftw64_smoke)111 TEST(ftw, ftw64_smoke) {
112 TemporaryDir root;
113 MakeTree(root.path);
114 ASSERT_EQ(0, ftw64(root.path, check_ftw64, 128));
115 }
116
TEST(ftw,nftw)117 TEST(ftw, nftw) {
118 TemporaryDir root;
119 MakeTree(root.path);
120 ASSERT_EQ(0, nftw(root.path, check_nftw, 128, 0));
121 }
122
TEST(ftw,nftw64_smoke)123 TEST(ftw, nftw64_smoke) {
124 TemporaryDir root;
125 MakeTree(root.path);
126 ASSERT_EQ(0, nftw64(root.path, check_nftw64, 128, 0));
127 }
128
129 template <typename StatT>
bug_28197840_ftw(const char * path,const StatT *,int flag)130 static int bug_28197840_ftw(const char* path, const StatT*, int flag) {
131 EXPECT_EQ(strstr(path, "unreadable") != nullptr ? FTW_DNR : FTW_D, flag) << path;
132 return 0;
133 }
134
135 template <typename StatT>
bug_28197840_nftw(const char * path,const StatT * sb,int flag,FTW *)136 static int bug_28197840_nftw(const char* path, const StatT* sb, int flag, FTW*) {
137 return bug_28197840_ftw(path, sb, flag);
138 }
139
TEST(ftw,bug_28197840)140 TEST(ftw, bug_28197840) {
141 // Drop root for this test, because root can still read directories even if
142 // permissions would imply otherwise.
143 if (getuid() == 0) {
144 passwd* pwd = getpwnam("shell");
145 ASSERT_EQ(0, setuid(pwd->pw_uid));
146 }
147
148 TemporaryDir root;
149
150 std::string path = android::base::StringPrintf("%s/unreadable-directory", root.path);
151 ASSERT_EQ(0, mkdir(path.c_str(), 0000)) << path;
152
153 ASSERT_EQ(0, ftw(root.path, bug_28197840_ftw<struct stat>, 128));
154 ASSERT_EQ(0, ftw64(root.path, bug_28197840_ftw<struct stat64>, 128));
155 ASSERT_EQ(0, nftw(root.path, bug_28197840_nftw<struct stat>, 128, FTW_PHYS));
156 ASSERT_EQ(0, nftw64(root.path, bug_28197840_nftw<struct stat64>, 128, FTW_PHYS));
157 }
158
159 template <typename StatT>
null_ftw_callback(const char *,const StatT *,int)160 static int null_ftw_callback(const char*, const StatT*, int) {
161 return 0;
162 }
163
164 template <typename StatT>
null_nftw_callback(const char *,const StatT *,int,FTW *)165 static int null_nftw_callback(const char*, const StatT*, int, FTW*) {
166 return 0;
167 }
168
TEST(ftw,ftw_non_existent_ENOENT)169 TEST(ftw, ftw_non_existent_ENOENT) {
170 errno = 0;
171 ASSERT_EQ(-1, ftw("/does/not/exist", null_ftw_callback<struct stat>, 128));
172 ASSERT_ERRNO(ENOENT);
173 errno = 0;
174 ASSERT_EQ(-1, ftw64("/does/not/exist", null_ftw_callback<struct stat64>, 128));
175 ASSERT_ERRNO(ENOENT);
176 }
177
TEST(ftw,nftw_non_existent_ENOENT)178 TEST(ftw, nftw_non_existent_ENOENT) {
179 errno = 0;
180 ASSERT_EQ(-1, nftw("/does/not/exist", null_nftw_callback<struct stat>, 128, FTW_PHYS));
181 ASSERT_ERRNO(ENOENT);
182 errno = 0;
183 ASSERT_EQ(-1, nftw64("/does/not/exist", null_nftw_callback<struct stat64>, 128, FTW_PHYS));
184 ASSERT_ERRNO(ENOENT);
185 }
186
TEST(ftw,ftw_empty_ENOENT)187 TEST(ftw, ftw_empty_ENOENT) {
188 errno = 0;
189 ASSERT_EQ(-1, ftw("", null_ftw_callback<struct stat>, 128));
190 ASSERT_ERRNO(ENOENT);
191 errno = 0;
192 ASSERT_EQ(-1, ftw64("", null_ftw_callback<struct stat64>, 128));
193 ASSERT_ERRNO(ENOENT);
194 }
195
TEST(ftw,nftw_empty_ENOENT)196 TEST(ftw, nftw_empty_ENOENT) {
197 errno = 0;
198 ASSERT_EQ(-1, nftw("", null_nftw_callback<struct stat>, 128, FTW_PHYS));
199 ASSERT_ERRNO(ENOENT);
200 errno = 0;
201 ASSERT_EQ(-1, nftw64("", null_nftw_callback<struct stat64>, 128, FTW_PHYS));
202 ASSERT_ERRNO(ENOENT);
203 }
204