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