1 /*
2 * Copyright (C) 2022 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 #include <unistd.h>
19
20 #include <android-base/file.h>
21 #include <android-base/logging.h>
22 #include <android-base/properties.h>
23 #include <fs_mgr.h>
24 #include <gtest/gtest.h>
25 #include <liblp/liblp.h>
26 #include <vintf/VintfObject.h>
27 #include <vintf/parse_string.h>
28
29 using namespace std::literals;
30
31 namespace {
32
VerifyDlkmPartition(const std::string & name)33 void VerifyDlkmPartition(const std::string &name) {
34 const auto TAG = __FUNCTION__ + "("s + name + ")";
35
36 const auto dlkm_symlink = "/" + name + "/lib/modules";
37 const auto dlkm_partition = name + "_dlkm";
38 const auto dlkm_directory = "/" + dlkm_partition + "/lib/modules";
39
40 // Check existence of /{name}/lib/modules.
41 if (access(dlkm_symlink.c_str(), F_OK)) {
42 if (errno == ENOENT) {
43 GTEST_LOG_(INFO) << TAG << ": '" << dlkm_symlink
44 << "' doesn't exist, skip checking it.";
45 SUCCEED();
46 } else {
47 ADD_FAILURE() << "access(" << dlkm_symlink << "): " << strerror(errno);
48 }
49 return;
50 }
51
52 // If it exists then make sure it is a directory.
53 struct stat st;
54 ASSERT_EQ(0, stat(dlkm_symlink.c_str(), &st))
55 << "stat(" << dlkm_symlink << "): " << strerror(errno);
56 if (!S_ISDIR(st.st_mode)) {
57 ADD_FAILURE() << "'" << dlkm_symlink << "' is not a directory.";
58 return;
59 }
60
61 // If it is a directory then check if it is empty or not.
62 auto not_empty_callback = [](const char *, const struct stat *, int) {
63 return 1;
64 };
65 int ret = ftw(dlkm_symlink.c_str(), not_empty_callback, 128);
66 ASSERT_NE(-1, ret) << "ftw(" << dlkm_symlink << "): " << strerror(errno);
67
68 if (ret == 0) {
69 // ftw() returns without visiting any file, so the directory must be empty.
70 GTEST_LOG_(INFO) << TAG << ": '" << dlkm_symlink
71 << "' is empty directory, skip checking it.";
72 SUCCEED();
73 return;
74 }
75 // Otherwise ftw() must had returned 1, which means the callback is called at
76 // least once, so /{name}/lib/modules must not be empty.
77 ASSERT_EQ(1, ret);
78
79 // We want to ensure /{name}/lib/modules symlinks to /{name}_dlkm/lib/modules.
80 ASSERT_EQ(0, lstat(dlkm_symlink.c_str(), &st))
81 << "lstat(" << dlkm_symlink << "): " << strerror(errno);
82 if (!S_ISLNK(st.st_mode)) {
83 ADD_FAILURE() << "'" << dlkm_symlink << "' is not a symlink.";
84 return;
85 }
86
87 std::string link_target;
88 ASSERT_TRUE(android::base::Readlink(dlkm_symlink, &link_target))
89 << "readlink(" << dlkm_symlink << "): " << strerror(errno);
90 if (link_target != dlkm_directory) {
91 ADD_FAILURE() << "'" << dlkm_symlink << "' must be a symlink pointing at '"
92 << dlkm_directory << "'.";
93 } else {
94 GTEST_LOG_(INFO) << TAG << ": '" << dlkm_symlink << "' -> '"
95 << dlkm_directory << "'.";
96 }
97
98 // Ensure {name}_dlkm is a logical partition.
99 const auto super_device = fs_mgr_get_super_partition_name();
100 const auto slot_suffix = fs_mgr_get_slot_suffix();
101 const auto slot_number =
102 android::fs_mgr::SlotNumberForSlotSuffix(slot_suffix);
103 auto lp_metadata = android::fs_mgr::ReadMetadata(super_device, slot_number);
104 ASSERT_NE(nullptr, lp_metadata)
105 << "ReadMetadata(" << super_device << "): " << strerror(errno);
106 auto lp_partition = android::fs_mgr::FindPartition(
107 *lp_metadata, dlkm_partition + slot_suffix);
108 EXPECT_NE(nullptr, lp_partition)
109 << "Cannot find logical partition of '" << dlkm_partition << "'";
110 }
111
112 } // namespace
113
114 class DlkmPartitionTest : public testing::Test {
115 protected:
SetUp()116 void SetUp() override {
117 // Fetch device runtime information.
118 runtime_info = android::vintf::VintfObject::GetRuntimeInfo();
119 ASSERT_NE(nullptr, runtime_info);
120
121 product_first_api_level =
122 android::base::GetIntProperty("ro.product.first_api_level", 0);
123 ASSERT_NE(0, product_first_api_level)
124 << "ro.product.first_api_level is undefined.";
125
126 const auto board_api_level = android::base::GetIntProperty(
127 "ro.board.api_level", __ANDROID_API_FUTURE__);
128 const auto board_first_api_level = android::base::GetIntProperty(
129 "ro.board.first_api_level", __ANDROID_API_FUTURE__);
130 vendor_api_level = android::base::GetIntProperty(
131 "ro.vendor.api_level",
132 std::min(product_first_api_level,
133 std::max(board_api_level, board_first_api_level)));
134 ASSERT_NE(0, vendor_api_level) << "ro.vendor.api_level is undefined.";
135 }
136
137 std::shared_ptr<const android::vintf::RuntimeInfo> runtime_info;
138 int vendor_api_level;
139 int product_first_api_level;
140 };
141
TEST_F(DlkmPartitionTest,VendorDlkmPartition)142 TEST_F(DlkmPartitionTest, VendorDlkmPartition) {
143 if (vendor_api_level < __ANDROID_API_S__) {
144 GTEST_SKIP()
145 << "Exempt from vendor_dlkm partition test. ro.vendor.api_level ("
146 << vendor_api_level << ") < " << __ANDROID_API_S__;
147 }
148 // Only enforce this test on products launched with Android T and later.
149 if (product_first_api_level < __ANDROID_API_T__) {
150 GTEST_SKIP() << "Exempt from vendor_dlkm partition test. "
151 "ro.product.first_api_level ("
152 << product_first_api_level << ") < " << __ANDROID_API_T__;
153 }
154 if (runtime_info->kernelVersion().dropMinor() !=
155 android::vintf::Version{5, 4} &&
156 runtime_info->kernelVersion().dropMinor() <
157 android::vintf::Version{5, 10}) {
158 GTEST_SKIP() << "Exempt from vendor_dlkm partition test. kernel: "
159 << runtime_info->kernelVersion();
160 }
161 ASSERT_NO_FATAL_FAILURE(VerifyDlkmPartition("vendor"));
162 ASSERT_NO_FATAL_FAILURE(VerifyDlkmPartition("odm"));
163 }
164
TEST_F(DlkmPartitionTest,SystemDlkmPartition)165 TEST_F(DlkmPartitionTest, SystemDlkmPartition) {
166 if (vendor_api_level < __ANDROID_API_T__) {
167 GTEST_SKIP()
168 << "Exempt from system_dlkm partition test. ro.vendor.api_level ("
169 << vendor_api_level << ") < " << __ANDROID_API_T__;
170 }
171 if (runtime_info->kernelVersion().dropMinor() <
172 android::vintf::Version{5, 10}) {
173 GTEST_SKIP() << "Exempt from system_dlkm partition test. kernel: "
174 << runtime_info->kernelVersion();
175 }
176 ASSERT_NO_FATAL_FAILURE(VerifyDlkmPartition("system"));
177 }
178
main(int argc,char * argv[])179 int main(int argc, char *argv[]) {
180 ::testing::InitGoogleTest(&argc, argv);
181 android::base::InitLogging(argv, android::base::StderrLogger);
182 return RUN_ALL_TESTS();
183 }
184