1 /*
2 * Copyright (C) 2020 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 "apexd_utils.h"
18
19 #include <android-base/file.h>
20 #include <android-base/result-gmock.h>
21 #include <android-base/result.h>
22 #include <android-base/stringprintf.h>
23 #include <android-base/strings.h>
24 #include <errno.h>
25 #include <gtest/gtest.h>
26
27 #include <filesystem>
28 #include <fstream>
29 #include <new>
30 #include <string>
31
32 #include "apexd.h"
33 #include "apexd_test_utils.h"
34
35 namespace android {
36 namespace apex {
37 namespace {
38
39 namespace fs = std::filesystem;
40
41 using android::base::Basename;
42 using android::base::Join;
43 using android::base::StringPrintf;
44 using android::base::testing::Ok;
45 using ::testing::Not;
46 using ::testing::UnorderedElementsAre;
47 using ::testing::UnorderedElementsAreArray;
48
49 // TODO(b/170327382): add unit tests for apexd_utils.h
50
TEST(ApexdUtilTest,DeleteDirContent)51 TEST(ApexdUtilTest, DeleteDirContent) {
52 TemporaryDir root_dir;
53 TemporaryFile child_file_1(root_dir.path);
54 TemporaryFile child_file_2(root_dir.path);
55 std::string child_dir = StringPrintf("%s/child-dir", root_dir.path);
56 CreateDirIfNeeded(child_dir, 0755);
57 TemporaryFile nested_file(child_dir);
58
59 auto content = ReadDir(root_dir.path, [](auto _) { return true; });
60 ASSERT_RESULT_OK(content);
61 ASSERT_EQ(content->size(), 3u);
62
63 auto del_result = DeleteDirContent(root_dir.path);
64 ASSERT_RESULT_OK(del_result);
65 content = ReadDir(root_dir.path, [](auto _) { return true; });
66 ASSERT_RESULT_OK(content);
67 ASSERT_EQ(content->size(), 0u);
68 }
69
TEST(ApexdUtilTest,FindFirstExistingDirectoryBothExist)70 TEST(ApexdUtilTest, FindFirstExistingDirectoryBothExist) {
71 TemporaryDir first_dir;
72 TemporaryDir second_dir;
73 auto result = FindFirstExistingDirectory(first_dir.path, second_dir.path);
74 ASSERT_RESULT_OK(result);
75 ASSERT_EQ(*result, first_dir.path);
76 }
77
TEST(ApexdUtilTest,FindFirstExistingDirectoryOnlyFirstExist)78 TEST(ApexdUtilTest, FindFirstExistingDirectoryOnlyFirstExist) {
79 TemporaryDir first_dir;
80 auto second_dir = "/data/local/tmp/does/not/exist";
81 auto result = FindFirstExistingDirectory(first_dir.path, second_dir);
82 ASSERT_RESULT_OK(result);
83 ASSERT_EQ(*result, first_dir.path);
84 }
85
TEST(ApexdUtilTest,FindFirstExistingDirectoryOnlySecondExist)86 TEST(ApexdUtilTest, FindFirstExistingDirectoryOnlySecondExist) {
87 auto first_dir = "/data/local/tmp/does/not/exist";
88 TemporaryDir second_dir;
89 auto result = FindFirstExistingDirectory(first_dir, second_dir.path);
90 ASSERT_RESULT_OK(result);
91 ASSERT_EQ(*result, second_dir.path);
92 }
93
TEST(ApexdUtilTest,FindFirstExistingDirectoryNoneExist)94 TEST(ApexdUtilTest, FindFirstExistingDirectoryNoneExist) {
95 auto first_dir = "/data/local/tmp/does/not/exist";
96 auto second_dir = "/data/local/tmp/also/does/not/exist";
97 auto result = FindFirstExistingDirectory(first_dir, second_dir);
98 ASSERT_THAT(result, Not(Ok()));
99 }
100
TEST(ApexdUtilTest,FindFirstExistingDirectoryFirstFileSecondDir)101 TEST(ApexdUtilTest, FindFirstExistingDirectoryFirstFileSecondDir) {
102 TemporaryFile first_file;
103 TemporaryDir second_dir;
104 auto result = FindFirstExistingDirectory(first_file.path, second_dir.path);
105 ASSERT_RESULT_OK(result);
106 ASSERT_EQ(*result, second_dir.path);
107 }
108
TEST(ApexdUtilTest,FindFirstExistingDirectoryFirstDirSecondFile)109 TEST(ApexdUtilTest, FindFirstExistingDirectoryFirstDirSecondFile) {
110 TemporaryDir first_dir;
111 TemporaryFile second_file;
112 auto result = FindFirstExistingDirectory(first_dir.path, second_file.path);
113 ASSERT_RESULT_OK(result);
114 ASSERT_EQ(*result, first_dir.path);
115 }
116
TEST(ApexdUtilTest,FindFirstExistingDirectoryBothFiles)117 TEST(ApexdUtilTest, FindFirstExistingDirectoryBothFiles) {
118 TemporaryFile first_file;
119 TemporaryFile second_file;
120 auto result = FindFirstExistingDirectory(first_file.path, second_file.path);
121 ASSERT_THAT(result, Not(Ok()));
122 }
123
TEST(ApexdUtilTest,FindFirstExistingDirectoryFirstFileSecondDoesNotExist)124 TEST(ApexdUtilTest, FindFirstExistingDirectoryFirstFileSecondDoesNotExist) {
125 TemporaryFile first_file;
126 auto second_dir = "/data/local/tmp/does/not/exist";
127 auto result = FindFirstExistingDirectory(first_file.path, second_dir);
128 ASSERT_THAT(result, Not(Ok()));
129 }
130
TEST(ApexdUtilTest,FindFirstExistingDirectoryFirsDoesNotExistSecondFile)131 TEST(ApexdUtilTest, FindFirstExistingDirectoryFirsDoesNotExistSecondFile) {
132 auto first_dir = "/data/local/tmp/does/not/exist";
133 TemporaryFile second_file;
134 auto result = FindFirstExistingDirectory(first_dir, second_file.path);
135 ASSERT_THAT(result, Not(Ok()));
136 }
137
TEST(ApexdUtilTest,MoveDir)138 TEST(ApexdUtilTest, MoveDir) {
139 TemporaryDir from;
140 TemporaryDir to;
141
142 TemporaryFile from_1(from.path);
143 auto from_subdir = StringPrintf("%s/subdir", from.path);
144 if (mkdir(from_subdir.c_str(), 07000) != 0) {
145 FAIL() << "Failed to mkdir " << from_subdir << " : " << strerror(errno);
146 }
147 TemporaryFile from_2(from_subdir);
148
149 auto result = MoveDir(from.path, to.path);
150 ASSERT_RESULT_OK(result);
151 ASSERT_TRUE(fs::is_empty(from.path));
152
153 std::vector<std::string> content;
154 for (const auto& it : fs::recursive_directory_iterator(to.path)) {
155 content.push_back(it.path());
156 }
157
158 static const std::vector<std::string> expected = {
159 StringPrintf("%s/%s", to.path, Basename(from_1.path).c_str()),
160 StringPrintf("%s/subdir", to.path),
161 StringPrintf("%s/subdir/%s", to.path, Basename(from_2.path).c_str()),
162 };
163 ASSERT_THAT(content, UnorderedElementsAreArray(expected));
164 }
165
TEST(ApexdUtilTest,MoveDirFromIsNotDirectory)166 TEST(ApexdUtilTest, MoveDirFromIsNotDirectory) {
167 TemporaryFile from;
168 TemporaryDir to;
169 ASSERT_THAT(MoveDir(from.path, to.path), Not(Ok()));
170 }
171
TEST(ApexdUtilTest,MoveDirToIsNotDirectory)172 TEST(ApexdUtilTest, MoveDirToIsNotDirectory) {
173 TemporaryDir from;
174 TemporaryFile to;
175 TemporaryFile from_1(from.path);
176 ASSERT_THAT(MoveDir(from.path, to.path), Not(Ok()));
177 }
178
TEST(ApexdUtilTest,MoveDirFromDoesNotExist)179 TEST(ApexdUtilTest, MoveDirFromDoesNotExist) {
180 TemporaryDir to;
181 ASSERT_THAT(MoveDir("/data/local/tmp/does/not/exist", to.path), Not(Ok()));
182 }
183
TEST(ApexdUtilTest,MoveDirToDoesNotExist)184 TEST(ApexdUtilTest, MoveDirToDoesNotExist) {
185 namespace fs = std::filesystem;
186
187 TemporaryDir from;
188 TemporaryFile from_1(from.path);
189 auto from_subdir = StringPrintf("%s/subdir", from.path);
190 if (mkdir(from_subdir.c_str(), 07000) != 0) {
191 FAIL() << "Failed to mkdir " << from_subdir << " : " << strerror(errno);
192 }
193 TemporaryFile from_2(from_subdir);
194
195 ASSERT_THAT(MoveDir(from.path, "/data/local/tmp/does/not/exist"), Not(Ok()));
196
197 // Check that |from| directory is not empty.
198 std::vector<std::string> content;
199 for (const auto& it : fs::recursive_directory_iterator(from.path)) {
200 content.push_back(it.path());
201 }
202
203 ASSERT_THAT(content,
204 UnorderedElementsAre(from_1.path, from_subdir, from_2.path));
205 }
206
TEST(ApexdUtilTest,FindFilesBySuffix)207 TEST(ApexdUtilTest, FindFilesBySuffix) {
208 TemporaryDir td;
209
210 // create files with different suffix
211 const std::string first_filename = StringPrintf("%s/first.a", td.path);
212 const std::string second_filename = StringPrintf("%s/second.b", td.path);
213 const std::string third_filename = StringPrintf("%s/third.c", td.path);
214 const std::string fourth_filename = StringPrintf("%s/fourth.c", td.path);
215
216 std::ofstream first_file(first_filename);
217 std::ofstream second_file(second_filename);
218 std::ofstream third_file(third_filename);
219 std::ofstream fourth_file(fourth_filename);
220
221 auto result = FindFilesBySuffix(td.path, {".b", ".c"});
222 ASSERT_RESULT_OK(result);
223 ASSERT_THAT(*result, UnorderedElementsAre(second_filename, third_filename,
224 fourth_filename));
225 }
226
TEST(ApexdTestUtilsTest,MountNamespaceRestorer)227 TEST(ApexdTestUtilsTest, MountNamespaceRestorer) {
228 auto original_namespace = GetCurrentMountNamespace();
229 ASSERT_RESULT_OK(original_namespace);
230 {
231 MountNamespaceRestorer restorer;
232 // Switch to new mount namespace.
233 ASSERT_NE(-1, unshare(CLONE_NEWNS));
234 auto current_namespace = GetCurrentMountNamespace();
235 ASSERT_RESULT_OK(current_namespace);
236 ASSERT_NE(original_namespace, current_namespace);
237 }
238 // Check that we switched back to the original namespace upon exiting the
239 // scope.
240 auto current_namespace = GetCurrentMountNamespace();
241 ASSERT_RESULT_OK(current_namespace);
242 ASSERT_EQ(*original_namespace, *current_namespace);
243 }
244
TEST(ApexdTestUtilsTest,SetUpApexTestEnvironment)245 TEST(ApexdTestUtilsTest, SetUpApexTestEnvironment) {
246 auto original_apex_mounts = GetApexMounts();
247 ASSERT_GT(original_apex_mounts.size(), 0u);
248 auto original_dir_content = ReadDir("/apex", [](auto _) { return true; });
249 ASSERT_RESULT_OK(original_dir_content);
250 {
251 MountNamespaceRestorer restorer;
252 ASSERT_RESULT_OK(SetUpApexTestEnvironment());
253 // Check /apex is apex_mnt_dir.
254 char* context;
255 ASSERT_GT(getfilecon("/apex", &context), 0);
256 EXPECT_EQ(std::string(context), "u:object_r:apex_mnt_dir:s0");
257 freecon(context);
258 // Check no apexes are mounted in our test environment.
259 auto new_apex_mounts = GetApexMounts();
260 ASSERT_EQ(new_apex_mounts.size(), 0u);
261 // Check that /apex is empty.
262 auto dir_content = ReadDir("/apex", [](auto _) { return true; });
263 ASSERT_RESULT_OK(dir_content);
264 ASSERT_EQ(dir_content->size(), 0u)
265 << "Found following entries: " << Join(*dir_content, ',');
266 // Check that we can still access /data.
267 std::string test_dir = android::base::GetExecutableDirectory();
268 ASSERT_TRUE(android::base::StartsWith(test_dir, "/data"));
269 TemporaryFile tf(test_dir);
270 // Check that we can write.
271 ASSERT_TRUE(android::base::WriteStringToFile("secret", tf.path));
272 // And check that we can still read it
273 std::string content;
274 ASSERT_TRUE(android::base::ReadFileToString(tf.path, &content));
275 ASSERT_EQ(content, "secret");
276 }
277 auto apex_mounts = GetApexMounts();
278 ASSERT_THAT(apex_mounts, UnorderedElementsAreArray(original_apex_mounts));
279 auto apex_dir_content = ReadDir("/apex", [](auto _) { return true; });
280 ASSERT_RESULT_OK(apex_dir_content);
281 ASSERT_EQ(apex_dir_content->size(), original_dir_content->size());
282 }
283
284 } // namespace
285 } // namespace apex
286 } // namespace android
287