177 lines
6.3 KiB
C++
177 lines
6.3 KiB
C++
/*
|
|
* Copyright (C) 2022 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <ftw.h>
|
|
#include <unistd.h>
|
|
|
|
#include <android-base/file.h>
|
|
#include <android-base/logging.h>
|
|
#include <android-base/properties.h>
|
|
#include <fs_mgr.h>
|
|
#include <gtest/gtest.h>
|
|
#include <liblp/liblp.h>
|
|
#include <vintf/VintfObject.h>
|
|
#include <vintf/parse_string.h>
|
|
|
|
using namespace std::literals;
|
|
|
|
namespace {
|
|
|
|
void VerifyDlkmPartition(const std::string &name) {
|
|
const auto TAG = __FUNCTION__ + "("s + name + ")";
|
|
|
|
const auto dlkm_symlink = "/" + name + "/lib/modules";
|
|
const auto dlkm_partition = name + "_dlkm";
|
|
const auto dlkm_directory = "/" + dlkm_partition + "/lib/modules";
|
|
|
|
// Check existence of /{name}/lib/modules.
|
|
if (access(dlkm_symlink.c_str(), F_OK)) {
|
|
if (errno == ENOENT) {
|
|
GTEST_LOG_(INFO) << TAG << ": '" << dlkm_symlink
|
|
<< "' doesn't exist, skip checking it.";
|
|
SUCCEED();
|
|
} else {
|
|
ADD_FAILURE() << "access(" << dlkm_symlink << "): " << strerror(errno);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// If it exists then make sure it is a directory.
|
|
struct stat st;
|
|
ASSERT_EQ(0, stat(dlkm_symlink.c_str(), &st))
|
|
<< "stat(" << dlkm_symlink << "): " << strerror(errno);
|
|
if (!S_ISDIR(st.st_mode)) {
|
|
ADD_FAILURE() << "'" << dlkm_symlink << "' is not a directory.";
|
|
return;
|
|
}
|
|
|
|
// If it is a directory then check if it is empty or not.
|
|
auto not_empty_callback = [](const char *, const struct stat *, int) {
|
|
return 1;
|
|
};
|
|
int ret = ftw(dlkm_symlink.c_str(), not_empty_callback, 128);
|
|
ASSERT_NE(-1, ret) << "ftw(" << dlkm_symlink << "): " << strerror(errno);
|
|
|
|
if (ret == 0) {
|
|
// ftw() returns without visiting any file, so the directory must be empty.
|
|
GTEST_LOG_(INFO) << TAG << ": '" << dlkm_symlink
|
|
<< "' is empty directory, skip checking it.";
|
|
SUCCEED();
|
|
return;
|
|
}
|
|
// Otherwise ftw() must had returned 1, which means the callback is called at
|
|
// least once, so /{name}/lib/modules must not be empty.
|
|
ASSERT_EQ(1, ret);
|
|
|
|
// We want to ensure /{name}/lib/modules symlinks to /{name}_dlkm/lib/modules.
|
|
ASSERT_EQ(0, lstat(dlkm_symlink.c_str(), &st))
|
|
<< "lstat(" << dlkm_symlink << "): " << strerror(errno);
|
|
if (!S_ISLNK(st.st_mode)) {
|
|
ADD_FAILURE() << "'" << dlkm_symlink << "' is not a symlink.";
|
|
return;
|
|
}
|
|
|
|
std::string link_target;
|
|
ASSERT_TRUE(android::base::Readlink(dlkm_symlink, &link_target))
|
|
<< "readlink(" << dlkm_symlink << "): " << strerror(errno);
|
|
if (link_target != dlkm_directory) {
|
|
ADD_FAILURE() << "'" << dlkm_symlink << "' must be a symlink pointing at '"
|
|
<< dlkm_directory << "'.";
|
|
} else {
|
|
GTEST_LOG_(INFO) << TAG << ": '" << dlkm_symlink << "' -> '"
|
|
<< dlkm_directory << "'.";
|
|
}
|
|
|
|
// Ensure {name}_dlkm is a logical partition.
|
|
const auto super_device = fs_mgr_get_super_partition_name();
|
|
const auto slot_suffix = fs_mgr_get_slot_suffix();
|
|
const auto slot_number =
|
|
android::fs_mgr::SlotNumberForSlotSuffix(slot_suffix);
|
|
auto lp_metadata = android::fs_mgr::ReadMetadata(super_device, slot_number);
|
|
ASSERT_NE(nullptr, lp_metadata)
|
|
<< "ReadMetadata(" << super_device << "): " << strerror(errno);
|
|
auto lp_partition = android::fs_mgr::FindPartition(
|
|
*lp_metadata, dlkm_partition + slot_suffix);
|
|
EXPECT_NE(nullptr, lp_partition)
|
|
<< "Cannot find logical partition of '" << dlkm_partition << "'";
|
|
}
|
|
|
|
} // namespace
|
|
|
|
class DlkmPartitionTest : public testing::Test {
|
|
protected:
|
|
void SetUp() override {
|
|
// Fetch device runtime information.
|
|
runtime_info = android::vintf::VintfObject::GetRuntimeInfo();
|
|
ASSERT_NE(nullptr, runtime_info);
|
|
|
|
const auto product_first_api_level =
|
|
android::base::GetIntProperty("ro.product.first_api_level", 0);
|
|
ASSERT_NE(0, product_first_api_level)
|
|
<< "ro.product.first_api_level is undefined.";
|
|
|
|
const auto board_api_level = android::base::GetIntProperty(
|
|
"ro.board.api_level", __ANDROID_API_FUTURE__);
|
|
const auto board_first_api_level = android::base::GetIntProperty(
|
|
"ro.board.first_api_level", __ANDROID_API_FUTURE__);
|
|
vendor_api_level = android::base::GetIntProperty(
|
|
"ro.vendor.api_level",
|
|
std::min(product_first_api_level,
|
|
std::max(board_api_level, board_first_api_level)));
|
|
ASSERT_NE(0, vendor_api_level) << "ro.vendor.api_level is undefined.";
|
|
}
|
|
|
|
std::shared_ptr<const android::vintf::RuntimeInfo> runtime_info;
|
|
int vendor_api_level;
|
|
};
|
|
|
|
TEST_F(DlkmPartitionTest, VendorDlkmPartition) {
|
|
if (vendor_api_level < __ANDROID_API_S__) {
|
|
GTEST_SKIP()
|
|
<< "Exempt from vendor_dlkm partition test. ro.vendor.api_level: "
|
|
<< vendor_api_level;
|
|
}
|
|
if (runtime_info->kernelVersion().dropMinor() !=
|
|
android::vintf::Version{5, 4} &&
|
|
runtime_info->kernelVersion().dropMinor() <
|
|
android::vintf::Version{5, 10}) {
|
|
GTEST_SKIP() << "Exempt from vendor_dlkm partition test. kernel: "
|
|
<< runtime_info->kernelVersion();
|
|
}
|
|
ASSERT_NO_FATAL_FAILURE(VerifyDlkmPartition("vendor"));
|
|
ASSERT_NO_FATAL_FAILURE(VerifyDlkmPartition("odm"));
|
|
}
|
|
|
|
TEST_F(DlkmPartitionTest, SystemDlkmPartition) {
|
|
if (vendor_api_level < __ANDROID_API_T__) {
|
|
GTEST_SKIP()
|
|
<< "Exempt from system_dlkm partition test. ro.vendor.api_level ("
|
|
<< vendor_api_level << ") < " << __ANDROID_API_T__;
|
|
}
|
|
if (runtime_info->kernelVersion().dropMinor() <
|
|
android::vintf::Version{5, 10}) {
|
|
GTEST_SKIP() << "Exempt from system_dlkm partition test. kernel: "
|
|
<< runtime_info->kernelVersion();
|
|
}
|
|
ASSERT_NO_FATAL_FAILURE(VerifyDlkmPartition("system"));
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
android::base::InitLogging(argv, android::base::StderrLogger);
|
|
return RUN_ALL_TESTS();
|
|
}
|