You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
285 lines
9.5 KiB
285 lines
9.5 KiB
4 months ago
|
/*
|
||
|
* Copyright (C) 2020 The Android Open Source Project
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
#include <filesystem>
|
||
|
#include <fstream>
|
||
|
#include <new>
|
||
|
#include <string>
|
||
|
|
||
|
#include <errno.h>
|
||
|
|
||
|
#include <android-base/file.h>
|
||
|
#include <android-base/result.h>
|
||
|
#include <android-base/stringprintf.h>
|
||
|
#include <android-base/strings.h>
|
||
|
#include <gtest/gtest.h>
|
||
|
|
||
|
#include "apexd.h"
|
||
|
#include "apexd_test_utils.h"
|
||
|
#include "apexd_utils.h"
|
||
|
|
||
|
namespace android {
|
||
|
namespace apex {
|
||
|
namespace {
|
||
|
|
||
|
namespace fs = std::filesystem;
|
||
|
|
||
|
using android::apex::testing::IsOk;
|
||
|
using android::base::Basename;
|
||
|
using android::base::Join;
|
||
|
using android::base::StringPrintf;
|
||
|
using ::testing::UnorderedElementsAre;
|
||
|
using ::testing::UnorderedElementsAreArray;
|
||
|
|
||
|
// TODO(b/170327382): add unit tests for apexd_utils.h
|
||
|
|
||
|
TEST(ApexdUtilTest, DeleteDirContent) {
|
||
|
TemporaryDir root_dir;
|
||
|
TemporaryFile child_file_1(root_dir.path);
|
||
|
TemporaryFile child_file_2(root_dir.path);
|
||
|
std::string child_dir = StringPrintf("%s/child-dir", root_dir.path);
|
||
|
CreateDirIfNeeded(child_dir, 0755);
|
||
|
TemporaryFile nested_file(child_dir);
|
||
|
|
||
|
auto content = ReadDir(root_dir.path, [](auto _) { return true; });
|
||
|
IsOk(content);
|
||
|
ASSERT_EQ(content->size(), 3u);
|
||
|
|
||
|
auto del_result = DeleteDirContent(root_dir.path);
|
||
|
IsOk(del_result);
|
||
|
content = ReadDir(root_dir.path, [](auto _) { return true; });
|
||
|
IsOk(content);
|
||
|
ASSERT_EQ(content->size(), 0u);
|
||
|
}
|
||
|
|
||
|
TEST(ApexdUtilTest, FindFirstExistingDirectoryBothExist) {
|
||
|
TemporaryDir first_dir;
|
||
|
TemporaryDir second_dir;
|
||
|
auto result = FindFirstExistingDirectory(first_dir.path, second_dir.path);
|
||
|
ASSERT_TRUE(IsOk(result));
|
||
|
ASSERT_EQ(*result, first_dir.path);
|
||
|
}
|
||
|
|
||
|
TEST(ApexdUtilTest, FindFirstExistingDirectoryOnlyFirstExist) {
|
||
|
TemporaryDir first_dir;
|
||
|
auto second_dir = "/data/local/tmp/does/not/exist";
|
||
|
auto result = FindFirstExistingDirectory(first_dir.path, second_dir);
|
||
|
ASSERT_TRUE(IsOk(result));
|
||
|
ASSERT_EQ(*result, first_dir.path);
|
||
|
}
|
||
|
|
||
|
TEST(ApexdUtilTest, FindFirstExistingDirectoryOnlySecondExist) {
|
||
|
auto first_dir = "/data/local/tmp/does/not/exist";
|
||
|
TemporaryDir second_dir;
|
||
|
auto result = FindFirstExistingDirectory(first_dir, second_dir.path);
|
||
|
ASSERT_TRUE(IsOk(result));
|
||
|
ASSERT_EQ(*result, second_dir.path);
|
||
|
}
|
||
|
|
||
|
TEST(ApexdUtilTest, FindFirstExistingDirectoryNoneExist) {
|
||
|
auto first_dir = "/data/local/tmp/does/not/exist";
|
||
|
auto second_dir = "/data/local/tmp/also/does/not/exist";
|
||
|
auto result = FindFirstExistingDirectory(first_dir, second_dir);
|
||
|
ASSERT_FALSE(IsOk(result));
|
||
|
}
|
||
|
|
||
|
TEST(ApexdUtilTest, FindFirstExistingDirectoryFirstFileSecondDir) {
|
||
|
TemporaryFile first_file;
|
||
|
TemporaryDir second_dir;
|
||
|
auto result = FindFirstExistingDirectory(first_file.path, second_dir.path);
|
||
|
ASSERT_TRUE(IsOk(result));
|
||
|
ASSERT_EQ(*result, second_dir.path);
|
||
|
}
|
||
|
|
||
|
TEST(ApexdUtilTest, FindFirstExistingDirectoryFirstDirSecondFile) {
|
||
|
TemporaryDir first_dir;
|
||
|
TemporaryFile second_file;
|
||
|
auto result = FindFirstExistingDirectory(first_dir.path, second_file.path);
|
||
|
ASSERT_TRUE(IsOk(result));
|
||
|
ASSERT_EQ(*result, first_dir.path);
|
||
|
}
|
||
|
|
||
|
TEST(ApexdUtilTest, FindFirstExistingDirectoryBothFiles) {
|
||
|
TemporaryFile first_file;
|
||
|
TemporaryFile second_file;
|
||
|
auto result = FindFirstExistingDirectory(first_file.path, second_file.path);
|
||
|
ASSERT_FALSE(IsOk(result));
|
||
|
}
|
||
|
|
||
|
TEST(ApexdUtilTest, FindFirstExistingDirectoryFirstFileSecondDoesNotExist) {
|
||
|
TemporaryFile first_file;
|
||
|
auto second_dir = "/data/local/tmp/does/not/exist";
|
||
|
auto result = FindFirstExistingDirectory(first_file.path, second_dir);
|
||
|
ASSERT_FALSE(IsOk(result));
|
||
|
}
|
||
|
|
||
|
TEST(ApexdUtilTest, FindFirstExistingDirectoryFirsDoesNotExistSecondFile) {
|
||
|
auto first_dir = "/data/local/tmp/does/not/exist";
|
||
|
TemporaryFile second_file;
|
||
|
auto result = FindFirstExistingDirectory(first_dir, second_file.path);
|
||
|
ASSERT_FALSE(IsOk(result));
|
||
|
}
|
||
|
|
||
|
TEST(ApexdUtilTest, MoveDir) {
|
||
|
TemporaryDir from;
|
||
|
TemporaryDir to;
|
||
|
|
||
|
TemporaryFile from_1(from.path);
|
||
|
auto from_subdir = StringPrintf("%s/subdir", from.path);
|
||
|
if (mkdir(from_subdir.c_str(), 07000) != 0) {
|
||
|
FAIL() << "Failed to mkdir " << from_subdir << " : " << strerror(errno);
|
||
|
}
|
||
|
TemporaryFile from_2(from_subdir);
|
||
|
|
||
|
auto result = MoveDir(from.path, to.path);
|
||
|
ASSERT_TRUE(IsOk(result));
|
||
|
ASSERT_TRUE(fs::is_empty(from.path));
|
||
|
|
||
|
std::vector<std::string> content;
|
||
|
for (const auto& it : fs::recursive_directory_iterator(to.path)) {
|
||
|
content.push_back(it.path());
|
||
|
}
|
||
|
|
||
|
static const std::vector<std::string> expected = {
|
||
|
StringPrintf("%s/%s", to.path, Basename(from_1.path).c_str()),
|
||
|
StringPrintf("%s/subdir", to.path),
|
||
|
StringPrintf("%s/subdir/%s", to.path, Basename(from_2.path).c_str()),
|
||
|
};
|
||
|
ASSERT_THAT(content, UnorderedElementsAreArray(expected));
|
||
|
}
|
||
|
|
||
|
TEST(ApexdUtilTest, MoveDirFromIsNotDirectory) {
|
||
|
TemporaryFile from;
|
||
|
TemporaryDir to;
|
||
|
ASSERT_FALSE(IsOk(MoveDir(from.path, to.path)));
|
||
|
}
|
||
|
|
||
|
TEST(ApexdUtilTest, MoveDirToIsNotDirectory) {
|
||
|
TemporaryDir from;
|
||
|
TemporaryFile to;
|
||
|
TemporaryFile from_1(from.path);
|
||
|
ASSERT_FALSE(IsOk(MoveDir(from.path, to.path)));
|
||
|
}
|
||
|
|
||
|
TEST(ApexdUtilTest, MoveDirFromDoesNotExist) {
|
||
|
TemporaryDir to;
|
||
|
ASSERT_FALSE(IsOk(MoveDir("/data/local/tmp/does/not/exist", to.path)));
|
||
|
}
|
||
|
|
||
|
TEST(ApexdUtilTest, MoveDirToDoesNotExist) {
|
||
|
namespace fs = std::filesystem;
|
||
|
|
||
|
TemporaryDir from;
|
||
|
TemporaryFile from_1(from.path);
|
||
|
auto from_subdir = StringPrintf("%s/subdir", from.path);
|
||
|
if (mkdir(from_subdir.c_str(), 07000) != 0) {
|
||
|
FAIL() << "Failed to mkdir " << from_subdir << " : " << strerror(errno);
|
||
|
}
|
||
|
TemporaryFile from_2(from_subdir);
|
||
|
|
||
|
ASSERT_FALSE(IsOk(MoveDir(from.path, "/data/local/tmp/does/not/exist")));
|
||
|
|
||
|
// Check that |from| directory is not empty.
|
||
|
std::vector<std::string> content;
|
||
|
for (const auto& it : fs::recursive_directory_iterator(from.path)) {
|
||
|
content.push_back(it.path());
|
||
|
}
|
||
|
|
||
|
ASSERT_THAT(content,
|
||
|
UnorderedElementsAre(from_1.path, from_subdir, from_2.path));
|
||
|
}
|
||
|
|
||
|
TEST(ApexdUtilTest, FindFilesBySuffix) {
|
||
|
TemporaryDir td;
|
||
|
|
||
|
// create files with different suffix
|
||
|
const std::string first_filename = StringPrintf("%s/first.a", td.path);
|
||
|
const std::string second_filename = StringPrintf("%s/second.b", td.path);
|
||
|
const std::string third_filename = StringPrintf("%s/third.c", td.path);
|
||
|
const std::string fourth_filename = StringPrintf("%s/fourth.c", td.path);
|
||
|
|
||
|
std::ofstream first_file(first_filename);
|
||
|
std::ofstream second_file(second_filename);
|
||
|
std::ofstream third_file(third_filename);
|
||
|
std::ofstream fourth_file(fourth_filename);
|
||
|
|
||
|
auto result = FindFilesBySuffix(td.path, {".b", ".c"});
|
||
|
ASSERT_TRUE(IsOk(result));
|
||
|
ASSERT_THAT(*result, UnorderedElementsAre(second_filename, third_filename,
|
||
|
fourth_filename));
|
||
|
}
|
||
|
|
||
|
TEST(ApexdTestUtilsTest, MountNamespaceRestorer) {
|
||
|
auto original_namespace = GetCurrentMountNamespace();
|
||
|
ASSERT_RESULT_OK(original_namespace);
|
||
|
{
|
||
|
MountNamespaceRestorer restorer;
|
||
|
// Switch to new mount namespace.
|
||
|
ASSERT_NE(-1, unshare(CLONE_NEWNS));
|
||
|
auto current_namespace = GetCurrentMountNamespace();
|
||
|
ASSERT_RESULT_OK(current_namespace);
|
||
|
ASSERT_NE(original_namespace, current_namespace);
|
||
|
}
|
||
|
// Check that we switched back to the original namespace upon exiting the
|
||
|
// scope.
|
||
|
auto current_namespace = GetCurrentMountNamespace();
|
||
|
ASSERT_RESULT_OK(current_namespace);
|
||
|
ASSERT_EQ(*original_namespace, *current_namespace);
|
||
|
}
|
||
|
|
||
|
TEST(ApexdTestUtilsTest, SetUpApexTestEnvironment) {
|
||
|
auto original_apex_mounts = GetApexMounts();
|
||
|
ASSERT_GT(original_apex_mounts.size(), 0u);
|
||
|
auto original_dir_content = ReadDir("/apex", [](auto _) { return true; });
|
||
|
ASSERT_TRUE(IsOk(original_dir_content));
|
||
|
{
|
||
|
MountNamespaceRestorer restorer;
|
||
|
ASSERT_TRUE(IsOk(SetUpApexTestEnvironment()));
|
||
|
// Check /apex is apex_mnt_dir.
|
||
|
char* context;
|
||
|
ASSERT_GT(getfilecon("/apex", &context), 0);
|
||
|
EXPECT_EQ(std::string(context), "u:object_r:apex_mnt_dir:s0");
|
||
|
freecon(context);
|
||
|
// Check no apexes are mounted in our test environment.
|
||
|
auto new_apex_mounts = GetApexMounts();
|
||
|
ASSERT_EQ(new_apex_mounts.size(), 0u);
|
||
|
// Check that /apex is empty.
|
||
|
auto dir_content = ReadDir("/apex", [](auto _) { return true; });
|
||
|
ASSERT_TRUE(IsOk(dir_content));
|
||
|
ASSERT_EQ(dir_content->size(), 0u)
|
||
|
<< "Found following entries: " << Join(*dir_content, ',');
|
||
|
// Check that we can still access /data.
|
||
|
std::string test_dir = android::base::GetExecutableDirectory();
|
||
|
ASSERT_TRUE(android::base::StartsWith(test_dir, "/data"));
|
||
|
TemporaryFile tf(test_dir);
|
||
|
// Check that we can write.
|
||
|
ASSERT_TRUE(android::base::WriteStringToFile("secret", tf.path));
|
||
|
// And check that we can still read it
|
||
|
std::string content;
|
||
|
ASSERT_TRUE(android::base::ReadFileToString(tf.path, &content));
|
||
|
ASSERT_EQ(content, "secret");
|
||
|
}
|
||
|
auto apex_mounts = GetApexMounts();
|
||
|
ASSERT_THAT(apex_mounts, UnorderedElementsAreArray(original_apex_mounts));
|
||
|
auto apex_dir_content = ReadDir("/apex", [](auto _) { return true; });
|
||
|
ASSERT_TRUE(IsOk(apex_dir_content));
|
||
|
ASSERT_EQ(apex_dir_content->size(), original_dir_content->size());
|
||
|
}
|
||
|
|
||
|
} // namespace
|
||
|
} // namespace apex
|
||
|
} // namespace android
|