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