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 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 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 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 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 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 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 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 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 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 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 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 164 TEST(ApexdUtilTest, MoveDirFromIsNotDirectory) { 165 TemporaryFile from; 166 TemporaryDir to; 167 ASSERT_FALSE(IsOk(MoveDir(from.path, to.path))); 168 } 169 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 177 TEST(ApexdUtilTest, MoveDirFromDoesNotExist) { 178 TemporaryDir to; 179 ASSERT_FALSE(IsOk(MoveDir("/data/local/tmp/does/not/exist", to.path))); 180 } 181 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 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 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 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