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