diff --git a/libcutils/tests/fs_config.cpp b/libcutils/tests/fs_config.cpp index 3917a0b2e..a62cd51a2 100644 --- a/libcutils/tests/fs_config.cpp +++ b/libcutils/tests/fs_config.cpp @@ -14,63 +14,188 @@ * limitations under the License. */ +#include + #include #include +#include +#include #include #include +#include -extern const struct fs_path_config* __for_testing_only__android_dirs; -extern const struct fs_path_config* __for_testing_only__android_files; +extern const fs_path_config* __for_testing_only__android_dirs; +extern const fs_path_config* __for_testing_only__android_files; -static void check_one(const struct fs_path_config* paths, const std::string& prefix, - const std::string& alternate) { - for (size_t idx = 0; paths[idx].prefix; ++idx) { - std::string path(paths[idx].prefix); - if (android::base::StartsWith(path, prefix.c_str())) { - path = alternate + path.substr(prefix.length()); - size_t second; - for (second = 0; paths[second].prefix; ++second) { - if (path == paths[second].prefix) break; +// Maximum entries in system/core/libcutils/fs_config.cpp:android_* before we +// hit a nullptr termination, before we declare the list is just too big or +// could be missing the nullptr. +static constexpr size_t max_idx = 4096; + +static bool check_unique(std::vector& paths, const std::string& config_name, + const std::string& prefix) { + bool retval = false; + + std::string alternate = "system/" + prefix; + + for (size_t idx = 0; idx < paths.size(); ++idx) { + size_t second; + std::string path(paths[idx]); + // check if there are multiple identical paths + for (second = idx + 1; second < paths.size(); ++second) { + if (path == paths[second]) { + GTEST_LOG_(ERROR) << "duplicate paths in " << config_name << ": " << paths[idx]; + retval = true; + break; } - if (!paths[second].prefix) { - // guaranteed to fail expectations, trigger test failure with - // a message that reports the violation as an inequality. - EXPECT_STREQ((prefix + path.substr(alternate.length())).c_str(), path.c_str()); + } + + // check if path is / + if (android::base::StartsWith(path, prefix.c_str())) { + // rebuild path to be system//... to check for alias + path = alternate + path.substr(prefix.size()); + for (second = 0; second < paths.size(); ++second) { + if (path == paths[second]) { + GTEST_LOG_(ERROR) << "duplicate alias paths in " << config_name << ": " + << paths[idx] << " and " << paths[second] + << " (remove latter)"; + retval = true; + break; + } + } + continue; + } + + // check if path is system// + if (android::base::StartsWith(path, alternate.c_str())) { + // rebuild path to be /... to check for alias + path = prefix + path.substr(alternate.size()); + for (second = 0; second < paths.size(); ++second) { + if (path == paths[second]) break; + } + if (second >= paths.size()) { + GTEST_LOG_(ERROR) << "replace path in " << config_name << ": " << paths[idx] + << " with " << path; + retval = true; } } } + return retval; } -static void check_two(const struct fs_path_config* paths, const std::string& prefix) { +static bool check_unique(const fs_path_config* paths, const char* type_name, + const std::string& prefix) { + std::string config("system/core/libcutils/fs_config.cpp:android_"); + config += type_name; + config += "[]"; + + bool retval = false; + std::vector paths_tmp; + for (size_t idx = 0; paths[idx].prefix; ++idx) { + if (idx > max_idx) { + GTEST_LOG_(WARNING) << config << ": has no end (missing null prefix)"; + retval = true; + break; + } + paths_tmp.push_back(paths[idx].prefix); + } + + return check_unique(paths_tmp, config, prefix) || retval; +} + +#define endof(pointer, field) (offsetof(typeof(*(pointer)), field) + sizeof((pointer)->field)) + +static bool check_unique(const std::string& config, const std::string& prefix) { + int retval = false; + + std::string data; + if (!android::base::ReadFileToString(config, &data)) return retval; + + const fs_path_config_from_file* pc = + reinterpret_cast(data.c_str()); + size_t len = data.size(); + + std::vector paths_tmp; + size_t entry_number = 0; + while (len > 0) { + uint16_t host_len = (len >= endof(pc, len)) ? pc->len : INT16_MAX; + if (host_len > len) { + GTEST_LOG_(WARNING) << config << ": truncated at entry " << entry_number << " (" + << host_len << " > " << len << ")"; + const std::string unknown("?"); + GTEST_LOG_(WARNING) + << config << ": entry[" << entry_number << "]={ " + << "len=" << ((len >= endof(pc, len)) + ? android::base::StringPrintf("%" PRIu16, pc->len) + : unknown) + << ", mode=" << ((len >= endof(pc, mode)) + ? android::base::StringPrintf("0%" PRIo16, pc->mode) + : unknown) + << ", uid=" << ((len >= endof(pc, uid)) + ? android::base::StringPrintf("%" PRIu16, pc->uid) + : unknown) + << ", gid=" << ((len >= endof(pc, gid)) + ? android::base::StringPrintf("%" PRIu16, pc->gid) + : unknown) + << ", capabilities=" + << ((len >= endof(pc, capabilities)) + ? android::base::StringPrintf("0x%" PRIx64, pc->capabilities) + : unknown) + << ", prefix=" + << ((len >= offsetof(fs_path_config_from_file, prefix)) + ? android::base::StringPrintf( + "\"%.*s...", (int)(len - offsetof(fs_path_config_from_file, prefix)), + pc->prefix) + : unknown) + << " }"; + retval = true; + break; + } + paths_tmp.push_back(pc->prefix); + + pc = reinterpret_cast(reinterpret_cast(pc) + + host_len); + len -= host_len; + ++entry_number; + } + + return check_unique(paths_tmp, config, prefix) || retval; +} + +void check_two(const fs_path_config* paths, const char* type_name, const char* prefix) { ASSERT_FALSE(paths == nullptr); - std::string alternate = "system/" + prefix; - check_one(paths, prefix, alternate); - check_one(paths, alternate, prefix); + ASSERT_FALSE(type_name == nullptr); + ASSERT_FALSE(prefix == nullptr); + bool check_internal = check_unique(paths, type_name, prefix); + EXPECT_FALSE(check_internal); + bool check_overrides = + check_unique(std::string("/") + prefix + "etc/fs_config_" + type_name, prefix); + EXPECT_FALSE(check_overrides); } TEST(fs_config, vendor_dirs_alias) { - check_two(__for_testing_only__android_dirs, "vendor/"); + check_two(__for_testing_only__android_dirs, "dirs", "vendor/"); } TEST(fs_config, vendor_files_alias) { - check_two(__for_testing_only__android_files, "vendor/"); + check_two(__for_testing_only__android_files, "files", "vendor/"); } TEST(fs_config, oem_dirs_alias) { - check_two(__for_testing_only__android_dirs, "oem/"); + check_two(__for_testing_only__android_dirs, "dirs", "oem/"); } TEST(fs_config, oem_files_alias) { - check_two(__for_testing_only__android_files, "oem/"); + check_two(__for_testing_only__android_files, "files", "oem/"); } TEST(fs_config, odm_dirs_alias) { - check_two(__for_testing_only__android_dirs, "odm/"); + check_two(__for_testing_only__android_dirs, "dirs", "odm/"); } TEST(fs_config, odm_files_alias) { - check_two(__for_testing_only__android_files, "odm/"); + check_two(__for_testing_only__android_files, "files", "odm/"); }