1 /* 2 * Copyright (C) 2017 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 <glob.h> 18 19 #include <dirent.h> 20 #include <gtest/gtest.h> 21 22 #include <string> 23 #include <vector> 24 25 #include <android-base/file.h> 26 27 #if defined(__BIONIC__) 28 #define ASSERT_MATCH_COUNT(n_,g_) ASSERT_EQ(n_, g_.gl_matchc) 29 #else 30 #define ASSERT_MATCH_COUNT(n_,g_) 31 #endif 32 33 // 34 // Helper for use with GLOB_ALTDIRFUNC to iterate over the elements of `fake_dir`. 35 // 36 37 static std::vector<std::string> fake_dir; 38 static size_t fake_dir_offset; fake_closedir(void *)39 static void fake_closedir(void*) { 40 } fake_readdir(void *)41 static dirent* fake_readdir(void*) { 42 static dirent d; 43 if (fake_dir_offset >= fake_dir.size()) return nullptr; 44 strcpy(d.d_name, fake_dir[fake_dir_offset++].c_str()); 45 return &d; 46 } fake_opendir(const char * path)47 static void* fake_opendir(const char* path) { 48 fake_dir_offset = 0; 49 if (strcmp(path, "/opendir-fail/") == 0) { 50 errno = EINVAL; 51 return nullptr; 52 } 53 return &fake_dir; 54 } fake_lstat(const char *,struct stat *)55 static int fake_lstat(const char*, struct stat*) { 56 return 0; 57 } fake_stat(const char *,struct stat *)58 static int fake_stat(const char*, struct stat*) { 59 return 0; 60 } InstallFake(glob_t * g)61 static void InstallFake(glob_t* g) { 62 g->gl_closedir = fake_closedir; 63 g->gl_readdir = fake_readdir; 64 g->gl_opendir = fake_opendir; 65 g->gl_lstat = fake_lstat; 66 g->gl_stat = fake_stat; 67 } 68 TEST(glob,glob_result_GLOB_NOMATCH)69 TEST(glob, glob_result_GLOB_NOMATCH) { 70 glob_t g = {}; 71 ASSERT_EQ(GLOB_NOMATCH, glob("/will/match/nothing", 0, nullptr, &g)); 72 ASSERT_EQ(0U, g.gl_pathc); 73 ASSERT_MATCH_COUNT(0U, g); 74 } 75 TEST(glob,glob_GLOB_APPEND)76 TEST(glob, glob_GLOB_APPEND) { 77 glob_t g = {}; 78 ASSERT_EQ(0, glob("/proc/version", 0, nullptr, &g)); 79 ASSERT_EQ(1U, g.gl_pathc); 80 ASSERT_MATCH_COUNT(1U, g); 81 ASSERT_STREQ("/proc/version", g.gl_pathv[0]); 82 ASSERT_EQ(nullptr, g.gl_pathv[1]); 83 ASSERT_EQ(0, glob("/proc/version", GLOB_APPEND, nullptr, &g)); 84 ASSERT_EQ(2U, g.gl_pathc); 85 ASSERT_MATCH_COUNT(1U, g); 86 ASSERT_STREQ("/proc/version", g.gl_pathv[0]); 87 ASSERT_STREQ("/proc/version", g.gl_pathv[1]); 88 ASSERT_EQ(nullptr, g.gl_pathv[2]); 89 globfree(&g); 90 } 91 TEST(glob,glob_GLOB_DOOFFS)92 TEST(glob, glob_GLOB_DOOFFS) { 93 glob_t g = {}; 94 g.gl_offs = 2; 95 ASSERT_EQ(0, glob("/proc/version", GLOB_DOOFFS, nullptr, &g)); 96 ASSERT_EQ(1U, g.gl_pathc); 97 ASSERT_MATCH_COUNT(1U, g); 98 ASSERT_EQ(nullptr, g.gl_pathv[0]); 99 ASSERT_EQ(nullptr, g.gl_pathv[1]); 100 ASSERT_STREQ("/proc/version", g.gl_pathv[2]); 101 ASSERT_EQ(nullptr, g.gl_pathv[3]); 102 globfree(&g); 103 } 104 105 static std::string g_failure_path; 106 static int g_failure_errno; 107 static int test_error_callback_result; test_error_callback(const char * failure_path,int failure_errno)108 static int test_error_callback(const char* failure_path, int failure_errno) { 109 g_failure_path = failure_path; 110 g_failure_errno = failure_errno; 111 return test_error_callback_result; 112 } 113 TEST(glob,glob_gl_errfunc)114 TEST(glob, glob_gl_errfunc) { 115 glob_t g = {}; 116 InstallFake(&g); 117 118 test_error_callback_result = 0; 119 g_failure_errno = 0; 120 ASSERT_EQ(GLOB_NOMATCH, glob("/opendir-fail/x*", GLOB_ALTDIRFUNC, test_error_callback, &g)); 121 ASSERT_EQ("/opendir-fail/", g_failure_path); 122 ASSERT_EQ(EINVAL, g_failure_errno); 123 124 test_error_callback_result = 1; 125 g_failure_errno = 0; 126 ASSERT_EQ(GLOB_ABORTED, glob("/opendir-fail/x*", GLOB_ALTDIRFUNC, test_error_callback, &g)); 127 ASSERT_EQ("/opendir-fail/", g_failure_path); 128 ASSERT_EQ(EINVAL, g_failure_errno); 129 } 130 TEST(glob,glob_GLOB_ERR)131 TEST(glob, glob_GLOB_ERR) { 132 glob_t g = {}; 133 InstallFake(&g); 134 135 ASSERT_EQ(GLOB_NOMATCH, glob("/opendir-fail/x*", GLOB_ALTDIRFUNC, nullptr, &g)); 136 137 ASSERT_EQ(GLOB_ABORTED, glob("/opendir-fail/x*", GLOB_ALTDIRFUNC | GLOB_ERR, nullptr, &g)); 138 } 139 TEST(glob,glob_GLOB_MARK)140 TEST(glob, glob_GLOB_MARK) { 141 TemporaryDir td; 142 // The pattern we're about to pass doesn't have a trailing '/'... 143 ASSERT_NE('/', std::string(td.path).back()); 144 145 glob_t g = {}; 146 // Using GLOB_MARK gets you a trailing '/' on a directory... 147 ASSERT_EQ(0, glob(td.path, GLOB_MARK, nullptr, &g)); 148 ASSERT_EQ(1U, g.gl_pathc); 149 ASSERT_MATCH_COUNT(1U, g); 150 ASSERT_EQ(std::string(td.path) + "/", g.gl_pathv[0]); 151 ASSERT_EQ(nullptr, g.gl_pathv[1]); 152 153 TemporaryFile tf; 154 // But not on a file... 155 ASSERT_EQ(0, glob(tf.path, GLOB_MARK, nullptr, &g)); 156 ASSERT_EQ(1U, g.gl_pathc); 157 ASSERT_MATCH_COUNT(1U, g); 158 ASSERT_STREQ(tf.path, g.gl_pathv[0]); 159 ASSERT_EQ(nullptr, g.gl_pathv[1]); 160 161 globfree(&g); 162 } 163 TEST(glob,glob_GLOB_NOCHECK)164 TEST(glob, glob_GLOB_NOCHECK) { 165 glob_t g = {}; 166 ASSERT_EQ(0, glob("/will/match/nothing", GLOB_NOCHECK, nullptr, &g)); 167 ASSERT_EQ(1U, g.gl_pathc); 168 ASSERT_MATCH_COUNT(0U, g); 169 ASSERT_STREQ("/will/match/nothing", g.gl_pathv[0]); 170 ASSERT_EQ(nullptr, g.gl_pathv[1]); 171 globfree(&g); 172 } 173 TEST(glob,glob_GLOB_NOSORT)174 TEST(glob, glob_GLOB_NOSORT) { 175 fake_dir = { "c", "a", "d", "b" }; 176 177 glob_t g = {}; 178 InstallFake(&g); 179 180 ASSERT_EQ(0, glob("*", GLOB_ALTDIRFUNC, nullptr, &g)); 181 ASSERT_EQ(4U, g.gl_pathc); 182 ASSERT_MATCH_COUNT(4U, g); 183 ASSERT_STREQ("a", g.gl_pathv[0]); 184 ASSERT_STREQ("b", g.gl_pathv[1]); 185 ASSERT_STREQ("c", g.gl_pathv[2]); 186 ASSERT_STREQ("d", g.gl_pathv[3]); 187 ASSERT_EQ(nullptr, g.gl_pathv[4]); 188 189 ASSERT_EQ(0, glob("*", GLOB_ALTDIRFUNC | GLOB_NOSORT, nullptr, &g)); 190 ASSERT_EQ(4U, g.gl_pathc); 191 ASSERT_MATCH_COUNT(4U, g); 192 ASSERT_STREQ("c", g.gl_pathv[0]); 193 ASSERT_STREQ("a", g.gl_pathv[1]); 194 ASSERT_STREQ("d", g.gl_pathv[2]); 195 ASSERT_STREQ("b", g.gl_pathv[3]); 196 ASSERT_EQ(nullptr, g.gl_pathv[4]); 197 } 198 TEST(glob,glob_GLOB_MAGCHAR)199 TEST(glob, glob_GLOB_MAGCHAR) { 200 glob_t g = {}; 201 ASSERT_EQ(GLOB_NOMATCH, glob("/does-not-exist", 0, nullptr, &g)); 202 ASSERT_TRUE((g.gl_flags & GLOB_MAGCHAR) == 0); 203 ASSERT_EQ(GLOB_NOMATCH, glob("/does-not-exist*", 0, nullptr, &g)); 204 ASSERT_TRUE((g.gl_flags & GLOB_MAGCHAR) != 0); 205 206 // We can lie, but glob(3) will turn that into truth... 207 ASSERT_EQ(GLOB_NOMATCH, glob("/does-not-exist", GLOB_MAGCHAR, nullptr, &g)); 208 ASSERT_TRUE((g.gl_flags & GLOB_MAGCHAR) == 0); 209 } 210 CheckGlob(const char * pattern,const std::vector<std::string> & expected_matches)211 static void CheckGlob(const char* pattern, const std::vector<std::string>& expected_matches) { 212 glob_t g = {}; 213 InstallFake(&g); 214 215 int expected_result = expected_matches.empty() ? GLOB_NOMATCH : 0; 216 ASSERT_EQ(expected_result, glob(pattern, GLOB_ALTDIRFUNC, nullptr, &g)) << pattern; 217 ASSERT_EQ(expected_matches.size(), g.gl_pathc); 218 ASSERT_MATCH_COUNT(expected_matches.size(), g); 219 for (size_t i = 0; i < expected_matches.size(); ++i) { 220 ASSERT_EQ(expected_matches[i], g.gl_pathv[i]); 221 } 222 if (!expected_matches.empty()) { 223 ASSERT_EQ(nullptr, g.gl_pathv[expected_matches.size()]); 224 } 225 globfree(&g); 226 } 227 TEST(glob,glob_globbing)228 TEST(glob, glob_globbing) { 229 fake_dir = { "f1", "f2", "f30", "f40" }; 230 231 CheckGlob("f?", { "f1", "f2" }); 232 CheckGlob("f??", { "f30", "f40" }); 233 CheckGlob("f*", { "f1", "f2", "f30", "f40" }); 234 } 235 TEST(glob,glob_globbing_rsc)236 TEST(glob, glob_globbing_rsc) { 237 // https://research.swtch.com/glob 238 fake_dir = { "axbxcxdxe" }; 239 CheckGlob("a*b*c*d*e*", { "axbxcxdxe" }); 240 fake_dir = { "axbxcxdxexxx" }; 241 CheckGlob("a*b*c*d*e*", { "axbxcxdxexxx" }); 242 fake_dir = { "abxbbxdbxebxczzx" }; 243 CheckGlob("a*b?c*x", { "abxbbxdbxebxczzx" }); 244 fake_dir = { "abxbbxdbxebxczzy" }; 245 CheckGlob("a*b?c*x", {}); 246 247 fake_dir = { std::string(100, 'a') }; 248 CheckGlob("a*a*a*a*b", {}); 249 } 250