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