blob: 6b8e831d98b80c0fc7958bce22b374ea4aaefd14 [file] [log] [blame]
// Copyright 2016 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// How to build and run the tests:
//
// chroot$ cros_run_unit_tests --board=$BOARD --packages arc-setup
//
// Note: only x86 $BOARDs like cyan are supported.
#include "arc/setup/arc_setup_util.h"
#include <fcntl.h>
#include <ifaddrs.h>
#include <inttypes.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <base/bind.h>
#include <base/environment.h>
#include <base/files/file_enumerator.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/rand_util.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include <base/time/time.h>
#include <base/timer/elapsed_timer.h>
#include <chromeos-config/libcros_config/fake_cros_config.h>
#include <gtest/gtest.h>
namespace arc {
namespace {
bool FindLineCallback(const std::string& line, std::string* out_prop) {
if (line != "string_to_find")
return false;
*out_prop = "FOUND";
return true;
}
bool IsNonBlockingFD(int fd) {
return fcntl(fd, F_GETFL) & O_NONBLOCK;
}
void ValidateResourcesMatch(const base::FilePath& path1,
const base::FilePath& path2) {
struct stat stat1;
struct stat stat2;
EXPECT_GE(lstat(path1.value().c_str(), &stat1), 0);
EXPECT_GE(lstat(path2.value().c_str(), &stat2), 0);
EXPECT_EQ(stat1.st_mode, stat2.st_mode);
EXPECT_EQ(stat1.st_uid, stat2.st_uid);
EXPECT_EQ(stat1.st_gid, stat2.st_gid);
if (S_ISREG(stat1.st_mode)) {
std::string data1;
std::string data2;
EXPECT_TRUE(base::ReadFileToString(path1, &data1));
EXPECT_TRUE(base::ReadFileToString(path2, &data2));
EXPECT_EQ(data1, data2);
} else if (S_ISLNK(stat1.st_mode)) {
base::FilePath link1;
base::FilePath link2;
EXPECT_TRUE(base::ReadSymbolicLink(path1, &link1));
EXPECT_TRUE(base::ReadSymbolicLink(path2, &link2));
EXPECT_EQ(link1, link2);
}
}
} // namespace
TEST(ArcSetupUtil, TestEnvironmentUtils) {
std::unique_ptr<base::Environment> env(base::Environment::Create());
const uint64_t random = base::RandUint64();
const std::string random_var =
base::StringPrintf("ARC_SETUP_UTIL_UNITTEST_%" PRIu64 "", random);
EXPECT_FALSE(env->HasVar(random_var.c_str()));
EXPECT_DEATH(GetEnvOrDie(env.get(), random_var.c_str()), ".*");
EXPECT_DEATH(GetFilePathOrDie(env.get(), random_var.c_str()), ".*");
const base::FilePath test_path("/tmp");
EXPECT_TRUE(env->SetVar(random_var.c_str(), test_path.value()));
EXPECT_EQ(test_path.value(), GetEnvOrDie(env.get(), random_var.c_str()));
EXPECT_EQ(test_path.value(),
GetFilePathOrDie(env.get(), random_var.c_str()).value());
EXPECT_TRUE(env->SetVar(random_var.c_str(), "0"));
EXPECT_FALSE(GetBooleanEnvOrDie(env.get(), random_var.c_str()));
EXPECT_TRUE(env->SetVar(random_var.c_str(), "1"));
EXPECT_TRUE(GetBooleanEnvOrDie(env.get(), random_var.c_str()));
// Unexpected values evaluate to false.
EXPECT_TRUE(env->SetVar(random_var.c_str(), "2"));
EXPECT_FALSE(GetBooleanEnvOrDie(env.get(), random_var.c_str()));
EXPECT_TRUE(env->SetVar(random_var.c_str(), "bad value"));
EXPECT_FALSE(GetBooleanEnvOrDie(env.get(), random_var.c_str()));
EXPECT_TRUE(env->SetVar(random_var.c_str(), ""));
EXPECT_FALSE(GetBooleanEnvOrDie(env.get(), random_var.c_str()));
env->UnSetVar(random_var.c_str());
}
TEST(ArcSetupUtil, TestCreateOrTruncate) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
// Create a new empty file.
EXPECT_TRUE(CreateOrTruncate(temp_directory.GetPath().Append("file"), 0777));
// Confirm that the mode of the file is 0777.
int mode = 0;
EXPECT_TRUE(base::GetPosixFilePermissions(
temp_directory.GetPath().Append("file"), &mode));
EXPECT_EQ(0777, mode);
// Confirm that the size of the file is 0.
int64_t size = -1;
EXPECT_TRUE(
base::GetFileSize(temp_directory.GetPath().Append("file"), &size));
EXPECT_EQ(0, size);
// Make the file non-empty.
EXPECT_TRUE(
WriteToFile(temp_directory.GetPath().Append("file"), 0777, "abc"));
EXPECT_TRUE(
base::GetFileSize(temp_directory.GetPath().Append("file"), &size));
EXPECT_EQ(3, size);
// Call the API again with a different mode.
EXPECT_TRUE(CreateOrTruncate(temp_directory.GetPath().Append("file"), 0700));
// Confirm that the mode of the file is now 0700.
mode = 0;
EXPECT_TRUE(base::GetPosixFilePermissions(
temp_directory.GetPath().Append("file"), &mode));
EXPECT_EQ(0700, mode);
// Confirm that the size of the file is still 0.
size = -1;
EXPECT_TRUE(
base::GetFileSize(temp_directory.GetPath().Append("file"), &size));
EXPECT_EQ(0, size);
}
TEST(ArcSetupUtil, TestWaitForPaths) {
constexpr base::TimeDelta timeout = base::TimeDelta::FromSeconds(1);
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::ScopedTempDir temp_dir2;
ASSERT_TRUE(temp_dir2.CreateUniqueTempDir());
// Confirm that when the first argument is empty, it returns true.
// Also confirm that the third argument can be nullptr.
EXPECT_TRUE(WaitForPaths({}, timeout, nullptr));
// Confirm that the function can handle one path.
base::TimeDelta elapsed;
EXPECT_TRUE(WaitForPaths({temp_dir.GetPath()}, timeout, &elapsed));
EXPECT_GT(elapsed, base::TimeDelta());
// Strictly speaking, WaitForPaths does not guarantee this, but in practice,
// this check passes.
EXPECT_LE(elapsed, timeout);
elapsed = base::TimeDelta();
// Does the same with a nonexistent file.
EXPECT_FALSE(WaitForPaths({temp_dir.GetPath().Append("nonexistent")}, timeout,
&elapsed));
EXPECT_GT(elapsed, timeout);
elapsed = base::TimeDelta();
// Confirm that the function can handle two paths.
EXPECT_TRUE(WaitForPaths({temp_dir.GetPath(), temp_dir2.GetPath()}, timeout,
&elapsed));
EXPECT_GT(elapsed, base::TimeDelta());
EXPECT_LE(elapsed, timeout); // same
elapsed = base::TimeDelta();
EXPECT_FALSE(WaitForPaths(
{temp_dir.GetPath().Append("nonexistent"), temp_dir2.GetPath()}, timeout,
&elapsed));
EXPECT_GT(elapsed, timeout);
elapsed = base::TimeDelta();
EXPECT_FALSE(WaitForPaths(
{temp_dir.GetPath(), temp_dir2.GetPath().Append("nonexistent")}, timeout,
&elapsed));
EXPECT_GT(elapsed, timeout);
elapsed = base::TimeDelta();
EXPECT_FALSE(WaitForPaths({temp_dir.GetPath().Append("nonexistent"),
temp_dir2.GetPath().Append("nonexistent")},
timeout, &elapsed));
EXPECT_GT(elapsed, timeout);
}
TEST(ArcSetupUtil, TestWriteToFile) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
// Create a non-empty file.
EXPECT_TRUE(
WriteToFile(temp_directory.GetPath().Append("file"), 0700, "abcde"));
// Confirm that the mode of the file is now 0700.
int mode = 0;
EXPECT_TRUE(base::GetPosixFilePermissions(
temp_directory.GetPath().Append("file"), &mode));
EXPECT_EQ(0700, mode);
// Confirm that the size of the file is still 0.
int64_t size = -1;
EXPECT_TRUE(
base::GetFileSize(temp_directory.GetPath().Append("file"), &size));
EXPECT_EQ(5, size);
// Call the API again with a different mode and content.
EXPECT_TRUE(
WriteToFile(temp_directory.GetPath().Append("file"), 0777, "xyz"));
// Confirm that the mode of the file is now 0700.
mode = 0;
EXPECT_TRUE(base::GetPosixFilePermissions(
temp_directory.GetPath().Append("file"), &mode));
EXPECT_EQ(0777, mode);
// Confirm that the size of the file is still 0.
size = -1;
EXPECT_TRUE(
base::GetFileSize(temp_directory.GetPath().Append("file"), &size));
EXPECT_EQ(3, size);
}
TEST(ArcSetupUtil, TestWriteToFileWithSymlink) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
base::ScopedTempDir temp_directory2;
ASSERT_TRUE(temp_directory2.CreateUniqueTempDir());
const base::FilePath symlink = temp_directory.GetPath().Append("symlink");
ASSERT_TRUE(base::CreateSymbolicLink(temp_directory2.GetPath(), symlink));
// WriteToFile should fail when the path points to a symlink.
EXPECT_FALSE(WriteToFile(symlink, 0777, "abc"));
// WriteToFile should also fail when a path component in the middle is a
// symlink.
const base::FilePath path_with_symlink = symlink.Append("not-a-symlink");
EXPECT_FALSE(WriteToFile(path_with_symlink, 0777, "abcde"));
}
TEST(ArcSetupUtil, TestWriteToFileWithFifo) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
const base::FilePath fifo = temp_directory.GetPath().Append("fifo");
ASSERT_EQ(0, mkfifo(fifo.value().c_str(), 0700));
// WriteToFile should fail when the path points to a fifo.
EXPECT_FALSE(WriteToFile(fifo, 0777, "abc"));
}
TEST(ArcSetupUtil, TestGetPropertyFromFile) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
base::FilePath prop_file = temp_directory.GetPath().Append("test.prop");
// Create a new prop file and read it.
ASSERT_TRUE(WriteToFile(prop_file, 0700, "key=val"));
std::string v;
EXPECT_TRUE(GetPropertyFromFile(prop_file, "key", &v));
EXPECT_EQ("val", v);
EXPECT_FALSE(GetPropertyFromFile(prop_file, "k", &v));
EXPECT_FALSE(GetPropertyFromFile(prop_file, "ke", &v));
EXPECT_FALSE(GetPropertyFromFile(prop_file, "keyX", &v));
// Retry with an empty file.
ASSERT_TRUE(WriteToFile(prop_file, 0700, ""));
EXPECT_FALSE(GetPropertyFromFile(prop_file, "", &v));
EXPECT_FALSE(GetPropertyFromFile(prop_file, "key", &v));
// Retry with a multi-line file.
ASSERT_TRUE(WriteToFile(prop_file, 0700, "k1=v1\nk2=v2\nk3=v3"));
EXPECT_TRUE(GetPropertyFromFile(prop_file, "k1", &v));
EXPECT_EQ("v1", v);
EXPECT_TRUE(GetPropertyFromFile(prop_file, "k2", &v));
EXPECT_EQ("v2", v);
EXPECT_TRUE(GetPropertyFromFile(prop_file, "k3", &v));
EXPECT_EQ("v3", v);
EXPECT_FALSE(GetPropertyFromFile(prop_file, "", &v));
EXPECT_FALSE(GetPropertyFromFile(prop_file, "=", &v));
EXPECT_FALSE(GetPropertyFromFile(prop_file, "1", &v));
EXPECT_FALSE(GetPropertyFromFile(prop_file, "k", &v));
EXPECT_FALSE(GetPropertyFromFile(prop_file, "k4", &v));
}
TEST(ArcSetupUtil, TestGetFingerprintFromPackagesXml) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
base::FilePath packages_file =
temp_directory.GetPath().Append("packages.xml");
// Create a new file and read it.
ASSERT_TRUE(WriteToFile(
packages_file, 0700,
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
"<packages>\n"
" <version sdkVersion=\"25\" databaseVersion=\"3\" fingerprint=\"f1\">\n"
" <version volumeUuid=\"primary_physical\" "
"sdkVersion=\"25\" databaseVersion=\"25\" fingerprint=\"f2\">\n"
"</packages>"));
std::string fingerprint;
EXPECT_TRUE(GetFingerprintFromPackagesXml(packages_file, &fingerprint));
EXPECT_EQ("f1", fingerprint);
ASSERT_TRUE(WriteToFile(
packages_file, 0700,
// Reverse the order of the version elements.
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
"<packages>\n"
" <version volumeUuid=\"primary_physical\" "
"sdkVersion=\"25\" databaseVersion=\"25\" fingerprint=\"f2\">\n"
" <version sdkVersion=\"25\" databaseVersion=\"3\" fingerprint=\"f1\">\n"
"</packages>"));
fingerprint.clear();
EXPECT_TRUE(GetFingerprintFromPackagesXml(packages_file, &fingerprint));
EXPECT_EQ("f1", fingerprint);
// Test invalid <version>s.
ASSERT_TRUE(WriteToFile(
packages_file, 0700,
// "external" version only.
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
"<packages>\n"
" <version volumeUuid=\"primary_physical\" "
"sdkVersion=\"25\" databaseVersion=\"25\" fingerprint=\"f2\">\n"
"</packages>"));
EXPECT_FALSE(GetFingerprintFromPackagesXml(packages_file, &fingerprint));
ASSERT_TRUE(
WriteToFile(packages_file, 0700,
// No sdkVersion.
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
"<packages>\n"
" <version databaseVersion=\"3\" fingerprint=\"f1\">\n"
"</packages>"));
EXPECT_FALSE(GetFingerprintFromPackagesXml(packages_file, &fingerprint));
ASSERT_TRUE(
WriteToFile(packages_file, 0700,
// No databaseVersion.
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
"<packages>\n"
" <version sdkVersion=\"25\" fingerprint=\"f1\">\n"
"</packages>"));
EXPECT_FALSE(GetFingerprintFromPackagesXml(packages_file, &fingerprint));
ASSERT_TRUE(
WriteToFile(packages_file, 0700,
// No fingerprint.
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
"<packages>\n"
" <version sdkVersion=\"25\" databaseVersion=\"3\">\n"
"</packages>"));
EXPECT_FALSE(GetFingerprintFromPackagesXml(packages_file, &fingerprint));
ASSERT_TRUE(WriteToFile(
packages_file, 0700,
// No valid fingerprint.
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
"<packages>\n"
" <version sdkVersion=\"25\" databaseVersion=\"3\" fingerprint=\"X>\n"
"</packages>"));
EXPECT_FALSE(GetFingerprintFromPackagesXml(packages_file, &fingerprint));
ASSERT_TRUE(
WriteToFile(packages_file, 0700,
// No <version> elements.
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
"<packages/>\n"));
EXPECT_FALSE(GetFingerprintFromPackagesXml(packages_file, &fingerprint));
ASSERT_TRUE(WriteToFile(packages_file, 0700,
// Empty file.
""));
EXPECT_FALSE(GetFingerprintFromPackagesXml(packages_file, &fingerprint));
}
// Tests the internal function directly.
TEST(ArcSetupUtil, TestFindLine) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
base::FilePath file = temp_directory.GetPath().Append("test.file");
// Create a new prop file and read it.
ASSERT_TRUE(WriteToFile(file, 0700, "string_to_find"));
std::string v;
EXPECT_TRUE(FindLineForTesting(file, base::Bind(&FindLineCallback), &v));
EXPECT_EQ("FOUND", v);
// Test with multi-line files.
v.clear();
ASSERT_TRUE(WriteToFile(file, 0700, "string_to_find\nline"));
EXPECT_TRUE(FindLineForTesting(file, base::Bind(&FindLineCallback), &v));
EXPECT_EQ("FOUND", v);
v.clear();
ASSERT_TRUE(WriteToFile(file, 0700, "line\nstring_to_find\nline"));
EXPECT_TRUE(FindLineForTesting(file, base::Bind(&FindLineCallback), &v));
EXPECT_EQ("FOUND", v);
v.clear();
ASSERT_TRUE(WriteToFile(file, 0700, "line\nstring_to_find"));
EXPECT_TRUE(FindLineForTesting(file, base::Bind(&FindLineCallback), &v));
EXPECT_EQ("FOUND", v);
v.clear();
ASSERT_TRUE(WriteToFile(file, 0700, "line\nstring_to_find\n"));
EXPECT_TRUE(FindLineForTesting(file, base::Bind(&FindLineCallback), &v));
EXPECT_EQ("FOUND", v);
// Test without the target string.
ASSERT_TRUE(WriteToFile(file, 0700, "string_to_findX"));
EXPECT_FALSE(FindLineForTesting(file, base::Bind(&FindLineCallback), &v));
ASSERT_TRUE(WriteToFile(file, 0700, "string_to_fin"));
EXPECT_FALSE(FindLineForTesting(file, base::Bind(&FindLineCallback), &v));
ASSERT_TRUE(WriteToFile(file, 0700, "string_to_fin\nd"));
EXPECT_FALSE(FindLineForTesting(file, base::Bind(&FindLineCallback), &v));
ASSERT_TRUE(WriteToFile(file, 0700, "s\ntring_to_find"));
EXPECT_FALSE(FindLineForTesting(file, base::Bind(&FindLineCallback), &v));
ASSERT_TRUE(WriteToFile(file, 0700, ""));
EXPECT_FALSE(FindLineForTesting(file, base::Bind(&FindLineCallback), &v));
}
TEST(ArcSetupUtil, TestMkdirRecursively) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
// Set |temp_directory| to 0707.
EXPECT_TRUE(base::SetPosixFilePermissions(temp_directory.GetPath(), 0707));
EXPECT_TRUE(MkdirRecursively(temp_directory.GetPath().Append("a/b/c")));
// Confirm the 3 directories are there.
EXPECT_TRUE(base::DirectoryExists(temp_directory.GetPath().Append("a")));
EXPECT_TRUE(base::DirectoryExists(temp_directory.GetPath().Append("a/b")));
EXPECT_TRUE(base::DirectoryExists(temp_directory.GetPath().Append("a/b/c")));
// Confirm that the newly created directories have 0755 mode.
int mode = 0;
EXPECT_TRUE(base::GetPosixFilePermissions(
temp_directory.GetPath().Append("a"), &mode));
EXPECT_EQ(0755, mode);
mode = 0;
EXPECT_TRUE(base::GetPosixFilePermissions(
temp_directory.GetPath().Append("a/b"), &mode));
EXPECT_EQ(0755, mode);
mode = 0;
EXPECT_TRUE(base::GetPosixFilePermissions(
temp_directory.GetPath().Append("a/b/c"), &mode));
EXPECT_EQ(0755, mode);
// Confirm that the existing directory |temp_directory| still has 0707 mode.
mode = 0;
EXPECT_TRUE(base::GetPosixFilePermissions(temp_directory.GetPath(), &mode));
EXPECT_EQ(0707, mode);
// Call the API again which should still succeed.
EXPECT_TRUE(MkdirRecursively(temp_directory.GetPath().Append("a/b/c")));
EXPECT_TRUE(MkdirRecursively(temp_directory.GetPath().Append("a/b/c/d")));
EXPECT_TRUE(
base::DirectoryExists(temp_directory.GetPath().Append("a/b/c/d")));
mode = 0;
EXPECT_TRUE(base::GetPosixFilePermissions(
temp_directory.GetPath().Append("a/b/c/d"), &mode));
EXPECT_EQ(0755, mode);
// Call the API again which should still succeed.
EXPECT_TRUE(MkdirRecursively(temp_directory.GetPath().Append("a/b")));
EXPECT_TRUE(MkdirRecursively(temp_directory.GetPath().Append("a")));
// Try to create an existing directory ("/") should still succeed.
EXPECT_TRUE(MkdirRecursively(base::FilePath("/")));
// Try to pass a relative or empty directory. They should all fail.
EXPECT_FALSE(MkdirRecursively(base::FilePath("foo")));
EXPECT_FALSE(MkdirRecursively(base::FilePath("bar/")));
EXPECT_FALSE(MkdirRecursively(base::FilePath()));
}
TEST(ArcSetupUtil, TestInstallDirectory) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
// Set |temp_directory| to 0707.
EXPECT_TRUE(base::SetPosixFilePermissions(temp_directory.GetPath(), 0707));
// Create a new directory.
EXPECT_TRUE(InstallDirectory(0777, getuid(), getgid(),
temp_directory.GetPath().Append("sub1/sub2")));
// Confirm that the mode for sub2 is 0777.
int mode_sub2 = 0;
EXPECT_TRUE(base::GetPosixFilePermissions(
temp_directory.GetPath().Append("sub1/sub2"), &mode_sub2));
EXPECT_EQ(0777, mode_sub2);
// Confirm that the mode for sub1 is NOT 0777 but the secure default, 0755.
int mode_sub1 = 0;
EXPECT_TRUE(base::GetPosixFilePermissions(
temp_directory.GetPath().Append("sub1"), &mode_sub1));
EXPECT_EQ(0755, mode_sub1);
// Confirm that the existing directory |temp_directory| still has 0707 mode.
int mode = 0;
EXPECT_TRUE(base::GetPosixFilePermissions(temp_directory.GetPath(), &mode));
EXPECT_EQ(0707, mode);
// Call InstallDirectory again with the same path but a different mode, 01700.
EXPECT_TRUE(InstallDirectory(0700 | S_ISVTX, getuid(), getgid(),
temp_directory.GetPath().Append("sub1/sub2")));
// Confirm that the mode for sub2 is now 01700.
struct stat st;
EXPECT_EQ(
0,
stat(temp_directory.GetPath().Append("sub1/sub2").value().c_str(), &st));
EXPECT_EQ(0700 | S_ISVTX, st.st_mode & ~S_IFMT);
mode_sub2 = 0;
EXPECT_TRUE(base::GetPosixFilePermissions(
temp_directory.GetPath().Append("sub1/sub2"), &mode_sub2));
EXPECT_EQ(0700, mode_sub2); // base's function masks the mode with 0777.
// Confirm that the mode for sub1 is still 0755.
mode_sub1 = 0;
EXPECT_TRUE(base::GetPosixFilePermissions(
temp_directory.GetPath().Append("sub1"), &mode_sub1));
EXPECT_EQ(0755, mode_sub1);
// Confirm that the existing directory |temp_directory| still has 0707 mode.
mode = 0;
EXPECT_TRUE(base::GetPosixFilePermissions(temp_directory.GetPath(), &mode));
EXPECT_EQ(0707, mode);
}
TEST(ArcSetupUtil, TestInstallDirectoryWithSymlink) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
base::ScopedTempDir temp_directory2;
ASSERT_TRUE(temp_directory2.CreateUniqueTempDir());
const base::FilePath symlink = temp_directory.GetPath().Append("symlink");
ASSERT_TRUE(base::CreateSymbolicLink(temp_directory2.GetPath(), symlink));
// InstallDirectory should fail when the path points to a symlink.
EXPECT_FALSE(InstallDirectory(0777, getuid(), getgid(), symlink));
// InstallDirectory should also fail when a path component in the middle
// is a symlink.
const base::FilePath path_with_symlink = symlink.Append("not-a-symlink");
EXPECT_FALSE(InstallDirectory(0777, getuid(), getgid(), path_with_symlink));
}
TEST(ArcSetupUtil, TestInstallDirectoryWithFifo) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
const base::FilePath fifo = temp_directory.GetPath().Append("fifo");
ASSERT_EQ(0, mkfifo(fifo.value().c_str(), 0700));
// InstallDirectory should fail when the path points to a fifo.
EXPECT_FALSE(InstallDirectory(0777, getuid(), getgid(), fifo));
}
TEST(ArcSetupUtil, TestDeleteFilesInDir) {
base::ScopedTempDir directory;
ASSERT_TRUE(directory.CreateUniqueTempDir());
ASSERT_TRUE(MkdirRecursively(directory.GetPath().Append("arm")));
ASSERT_TRUE(MkdirRecursively(directory.GetPath().Append("arm64")));
ASSERT_TRUE(CreateOrTruncate(
directory.GetPath().Append("arm/system@framework@boot.art"), 0755));
ASSERT_TRUE(CreateOrTruncate(
directory.GetPath().Append("arm64/system@framework@boot.art"), 0755));
EXPECT_TRUE(base::PathExists(
directory.GetPath().Append("arm/system@framework@boot.art")));
EXPECT_TRUE(base::PathExists(
directory.GetPath().Append("arm/system@framework@boot.art")));
EXPECT_TRUE(arc::DeleteFilesInDir(directory.GetPath()));
EXPECT_TRUE(base::PathExists(directory.GetPath().Append("arm")));
EXPECT_TRUE(base::PathExists(directory.GetPath().Append("arm64")));
EXPECT_FALSE(base::PathExists(
directory.GetPath().Append("arm/system@framework@boot.art")));
EXPECT_FALSE(base::PathExists(
directory.GetPath().Append("arm/system@framework@boot.art")));
}
TEST(ArcSetupUtil, TestLaunchAndWait) {
base::ElapsedTimer timer;
// Check that LaunchAndWait actually blocks until sleep returns.
EXPECT_TRUE(LaunchAndWait({"/usr/bin/sleep", "1"}));
EXPECT_LE(1, timer.Elapsed().InSeconds());
EXPECT_FALSE(LaunchAndWait({"/bin/false"}));
EXPECT_FALSE(LaunchAndWait({"/no_such_binary"}));
}
TEST(ArcSetupUtil, TestGenerateFakeSerialNumber) {
// Check that the function always returns 20-character string.
EXPECT_EQ(20U,
GenerateFakeSerialNumber("mytestaccount@gmail.com", "001122aabbcc")
.size());
EXPECT_EQ(20U, GenerateFakeSerialNumber("", "").size());
EXPECT_EQ(20U, GenerateFakeSerialNumber("a", "b").size());
// Check that the function always returns the same ID for the same
// account and hwid_raw.
const std::string id_1 =
GenerateFakeSerialNumber("mytestaccount@gmail.com", "001122aabbcc");
const std::string id_2 =
GenerateFakeSerialNumber("mytestaccount@gmail.com", "001122aabbcc");
EXPECT_EQ(id_1, id_2);
// Generate an ID for a different account but for the same machine.
// Check that the ID is not the same as |id_1|.
const std::string id_3 = GenerateFakeSerialNumber("mytestaccount2@gmail.com",
// ^
"001122aabbcc");
EXPECT_NE(id_1, id_3);
// Generate an ID for a different machine but for the same account.
// Check that the ID is not the same as |id_1|.
const std::string id_4 =
GenerateFakeSerialNumber("mytestaccount@gmail.com", "001122aaddcc");
// ^^
EXPECT_NE(id_1, id_4);
// Check that the function treats '\0' in |salt| properly.
const std::string id_5 =
GenerateFakeSerialNumber("mytestaccount@gmail.com", {'a', '\0', 'b'});
const std::string id_6 =
GenerateFakeSerialNumber("mytestaccount@gmail.com", {'a', '\0', 'c'});
EXPECT_NE(id_5, id_6);
}
TEST(ArcSetupUtil, TestGetArtCompilationOffsetSeed) {
const uint64_t seed1 = GetArtCompilationOffsetSeed("salt1", "build1");
const uint64_t seed2 = GetArtCompilationOffsetSeed("salt2", "build1");
const uint64_t seed3 = GetArtCompilationOffsetSeed("salt1", "build2");
EXPECT_NE(0ULL, seed1);
EXPECT_NE(0ULL, seed2);
EXPECT_NE(0ULL, seed3);
EXPECT_NE(seed1, seed2);
EXPECT_NE(seed2, seed3);
EXPECT_NE(seed3, seed1);
}
TEST(ArcSetupUtil, TestMoveDataAppOatDirectory) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
base::ScopedTempDir temp_target_directory;
ASSERT_TRUE(temp_target_directory.CreateUniqueTempDir());
// Create cache files.
ASSERT_TRUE(
MkdirRecursively(temp_directory.GetPath().Append("com.a/oat/arm")));
ASSERT_TRUE(
MkdirRecursively(temp_directory.GetPath().Append("com.a/oat/arm64")));
ASSERT_TRUE(CreateOrTruncate(
temp_directory.GetPath().Append("com.a/oat/arm/a.dex"), 0755));
ASSERT_TRUE(CreateOrTruncate(
temp_directory.GetPath().Append("com.a/oat/arm64/a.dex"), 0755));
ASSERT_TRUE(MkdirRecursively(temp_directory.GetPath().Append("com.a/apk")));
EXPECT_TRUE(
base::PathExists(temp_directory.GetPath().Append("com.a/oat/arm/a.dex")));
EXPECT_TRUE(base::PathExists(
temp_directory.GetPath().Append("com.a/oat/arm64/a.dex")));
EXPECT_TRUE(base::PathExists(temp_directory.GetPath().Append("com.a/apk")));
MoveDataAppOatDirectory(temp_directory.GetPath(),
temp_target_directory.GetPath());
EXPECT_FALSE(
base::PathExists(temp_directory.GetPath().Append("com.a/oat/arm/a.dex")));
EXPECT_FALSE(base::PathExists(
temp_directory.GetPath().Append("com.a/oat/arm64/a.dex")));
EXPECT_FALSE(base::PathExists(temp_directory.GetPath().Append("com.a/oat")));
EXPECT_TRUE(base::PathExists(temp_directory.GetPath().Append("com.a/apk")));
EXPECT_TRUE(base::PathExists(
temp_target_directory.GetPath().Append("oat-com.a/arm/a.dex")));
EXPECT_TRUE(base::PathExists(
temp_target_directory.GetPath().Append("oat-com.a/arm64/a.dex")));
}
TEST(ArcSetupUtil, TestGetChromeOsChannelFromFile) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
base::FilePath prop_file = temp_directory.GetPath().Append("test.prop");
ASSERT_TRUE(
WriteToFile(prop_file, 0700, "CHROMEOS_RELEASE_TRACK=beta-channel"));
EXPECT_EQ("beta", GetChromeOsChannelFromFile(prop_file));
ASSERT_TRUE(
WriteToFile(prop_file, 0700, "CHROMEOS_RELEASE_TRACK=canary-channel"));
EXPECT_EQ("canary", GetChromeOsChannelFromFile(prop_file));
ASSERT_TRUE(
WriteToFile(prop_file, 0700, "CHROMEOS_RELEASE_TRACK=dev-channel"));
EXPECT_EQ("dev", GetChromeOsChannelFromFile(prop_file));
ASSERT_TRUE(
WriteToFile(prop_file, 0700, "CHROMEOS_RELEASE_TRACK=dogfood-channel"));
EXPECT_EQ("dogfood", GetChromeOsChannelFromFile(prop_file));
ASSERT_TRUE(
WriteToFile(prop_file, 0700, "CHROMEOS_RELEASE_TRACK=stable-channel"));
EXPECT_EQ("stable", GetChromeOsChannelFromFile(prop_file));
ASSERT_TRUE(
WriteToFile(prop_file, 0700, "CHROMEOS_RELEASE_TRACK=testimage-channel"));
EXPECT_EQ("testimage", GetChromeOsChannelFromFile(prop_file));
// Checked "unknown" is returned if no value is set
ASSERT_TRUE(WriteToFile(prop_file, 0700, "CHROMEOS_RELEASE_TRACK="));
EXPECT_EQ("unknown", GetChromeOsChannelFromFile(prop_file));
// Checked "unknown" is returned if some unknown string is set
ASSERT_TRUE(WriteToFile(prop_file, 0700, "CHROMEOS_RELEASE_TRACK=foo_bar"));
EXPECT_EQ("unknown", GetChromeOsChannelFromFile(prop_file));
// Checked "unknown" is returned if CHROMEOS_RELEASE_TRACK is not in the file
ASSERT_TRUE(WriteToFile(prop_file, 0700, " "));
EXPECT_EQ("unknown", GetChromeOsChannelFromFile(prop_file));
// Checked "unknown" is returned if file is not present
EXPECT_EQ("unknown", GetChromeOsChannelFromFile(base::FilePath("foo")));
}
TEST(ArcSetupUtil, TestParseContainerState) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
base::FilePath json_file = temp_directory.GetPath().Append("state.json");
const base::FilePath kRootfsPath(
"/opt/google/containers/android/rootfs/root");
constexpr char kJsonTemplate[] = R"json(
{
"ociVersion": "1.0",
"id": "android-container",
"status": "created",
"pid": 4422,
"bundle": "/opt/google/containers/android",
"annotations": {
"org.chromium.run_oci.container_root": "%s"
}
}
)json";
ASSERT_TRUE(WriteToFile(
json_file, 0700,
base::StringPrintf(kJsonTemplate,
temp_directory.GetPath().value().c_str())));
ASSERT_TRUE(MkdirRecursively(temp_directory.GetPath().Append("mountpoints")));
ASSERT_TRUE(base::CreateSymbolicLink(
kRootfsPath,
temp_directory.GetPath().Append("mountpoints/container-root")));
pid_t container_pid;
base::FilePath rootfs;
EXPECT_TRUE(GetOciContainerState(json_file, &container_pid, &rootfs));
EXPECT_EQ(4422, container_pid);
EXPECT_EQ(kRootfsPath, rootfs);
}
TEST(ArcSetupUtil, TestPropertyExpansions) {
brillo::FakeCrosConfig config;
config.SetString("/arc/build-properties", "brand", "alphabet");
std::string expanded;
EXPECT_TRUE(ExpandPropertyContents("line1\n{brand}\nline3\n{brand} {brand}",
&config, &expanded));
EXPECT_EQ("line1\nalphabet\nline3\nalphabet alphabet\n", expanded);
}
TEST(ArcSetupUtil, TestPropertyExpansionsUnmatchedBrace) {
brillo::FakeCrosConfig config;
config.SetString("/arc/build-properties", "brand", "alphabet");
std::string expanded;
EXPECT_FALSE(
ExpandPropertyContents("line{1\nline}2\nline3", &config, &expanded));
}
TEST(ArcSetupUtil, TestPropertyExpansionsRecursive) {
brillo::FakeCrosConfig config;
config.SetString("/arc/build-properties", "brand", "alphabet");
config.SetString("/arc/build-properties", "model", "{brand} soup");
std::string expanded;
EXPECT_TRUE(ExpandPropertyContents("{model}", &config, &expanded));
EXPECT_EQ("alphabet soup\n", expanded);
}
TEST(ArcSetupUtil, TestPropertyExpansionsMissingProperty) {
brillo::FakeCrosConfig config;
config.SetString("/arc/build-properties", "model", "{brand} soup");
std::string expanded;
EXPECT_FALSE(
ExpandPropertyContents("{missing-property}", &config, &expanded));
EXPECT_FALSE(ExpandPropertyContents("{model}", &config, &expanded));
}
// Non-fingerprint property should do simple truncation.
TEST(ArcSetupUtil, TestPropertyTruncation) {
std::string truncated = TruncateAndroidProperty(
"property.name="
"012345678901234567890123456789012345678901234567890123456789"
"01234567890123456789012345678901");
EXPECT_EQ(
"property.name=0123456789012345678901234567890123456789"
"012345678901234567890123456789012345678901234567890",
truncated);
}
// Fingerprint truncation with /release-keys should do simple truncation.
TEST(ArcSetupUtil, TestPropertyTruncationFingerprintRelease) {
std::string truncated = TruncateAndroidProperty(
"ro.bootimage.build.fingerprint=google/toolongdevicename/"
"toolongdevicename_cheets:7.1.1/R65-10299.0.9999/4538390:user/"
"release-keys");
EXPECT_EQ(
"ro.bootimage.build.fingerprint=google/toolongdevicename/"
"toolongdevicename_cheets:7.1.1/R65-10299.0.9999/4538390:user/relea",
truncated);
}
// Fingerprint truncation with /dev-keys needs to preserve the /dev-keys.
TEST(ArcSetupUtil, TestPropertyTruncationFingerprintDev) {
std::string truncated = TruncateAndroidProperty(
"ro.bootimage.build.fingerprint=google/toolongdevicename/"
"toolongdevicename_cheets:7.1.1/R65-10299.0.9999/4538390:user/dev-keys");
EXPECT_EQ(
"ro.bootimage.build.fingerprint=google/toolongdevicena/"
"toolongdevicena_cheets/R65-10299.0.9999/4538390:user/dev-keys",
truncated);
}
// Tests if the O_NONBLOCK removal feature is working well. Other part of the
// function is tested in TestInstallDirectory*.
TEST(ArcSetupUtil, TestOpenSafely) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
const base::FilePath file = temp_directory.GetPath().Append("file");
ASSERT_TRUE(CreateOrTruncate(file, 0700));
base::ScopedFD fd(OpenSafelyForTesting(file, O_RDONLY, 0));
EXPECT_TRUE(fd.is_valid());
EXPECT_FALSE(IsNonBlockingFD(fd.get()));
fd = OpenSafelyForTesting(file, O_RDONLY | O_NONBLOCK, 0);
EXPECT_TRUE(fd.is_valid());
EXPECT_TRUE(IsNonBlockingFD(fd.get()));
}
TEST(ArcSetupUtil, TestOpenFifoSafely) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
const base::FilePath fifo = temp_directory.GetPath().Append("fifo");
ASSERT_EQ(0, mkfifo(fifo.value().c_str(), 0700));
const base::FilePath file = temp_directory.GetPath().Append("file");
ASSERT_TRUE(CreateOrTruncate(file, 0700));
base::ScopedFD fd(OpenFifoSafely(fifo, O_RDONLY, 0));
EXPECT_TRUE(fd.is_valid());
EXPECT_FALSE(IsNonBlockingFD(fd.get()));
// Opening a file should fail.
fd = OpenFifoSafely(file, O_RDONLY, 0);
EXPECT_FALSE(fd.is_valid());
}
TEST(ArcSetupUtil, TestCopyWithAttributes) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
// Note, actual owner change is not covered due permission restrictions for
// unit test. selinux context is also not possible to test due the
// permissions.
const uid_t kTestUid = getuid();
const gid_t kTestGid = getgid();
const base::FilePath root = temp_directory.GetPath();
// Create test directory structure.
const base::FilePath from_path = root.Append("from");
const base::FilePath from_sub_dir1 = from_path.Append("dir1");
const base::FilePath from_sub_dir2 = from_path.Append("dir2");
const base::FilePath from_test_file = from_sub_dir1.Append("test.txt");
const base::FilePath from_test_link = from_sub_dir2.Append("test.lnk");
const base::FilePath from_fifo = from_sub_dir1.Append("fifo");
EXPECT_TRUE(InstallDirectory(0751, kTestUid, kTestGid, from_path));
EXPECT_TRUE(InstallDirectory(0771, kTestUid, kTestGid, from_sub_dir1));
EXPECT_TRUE(InstallDirectory(0700, kTestUid, kTestGid, from_sub_dir2));
EXPECT_TRUE(WriteToFile(from_test_file, 0660, "testme"));
EXPECT_TRUE(base::CreateSymbolicLink(from_test_file, from_test_link));
EXPECT_EQ(0, mkfifo(from_fifo.value().c_str(), 0700));
// Copy directory.
const base::FilePath to_path = root.Append("to");
EXPECT_TRUE(CopyWithAttributes(from_path, to_path));
// Validate each resource to match.
int resource_count = 1;
ValidateResourcesMatch(from_path, to_path);
base::FileEnumerator traversal(from_path, true /* recursive */,
base::FileEnumerator::FILES |
base::FileEnumerator::SHOW_SYM_LINKS |
base::FileEnumerator::DIRECTORIES);
while (true) {
const base::FilePath test = traversal.Next();
if (test.empty())
break;
base::FilePath target_path(to_path);
EXPECT_TRUE(from_path.AppendRelativePath(test, &target_path));
if (test != from_fifo) {
ValidateResourcesMatch(test, target_path);
++resource_count;
} else {
// Unsupported types.
EXPECT_FALSE(base::PathExists(target_path));
}
}
EXPECT_EQ(5, resource_count);
// Copy file.
const base::FilePath to_test_file = from_sub_dir2.Append("test2.txt");
EXPECT_TRUE(CopyWithAttributes(from_test_file, to_test_file));
ValidateResourcesMatch(from_test_file, to_test_file);
EXPECT_TRUE(CopyWithAttributes(from_test_file, to_test_file));
ValidateResourcesMatch(from_test_file, to_test_file);
// Copy link.
const base::FilePath to_test_link = from_sub_dir2.Append("test2.lnk");
EXPECT_TRUE(CopyWithAttributes(from_test_link, to_test_link));
ValidateResourcesMatch(from_test_file, to_test_file);
// Copy fifo
EXPECT_FALSE(CopyWithAttributes(from_fifo, from_sub_dir1.Append("fifo2")));
}
TEST(ArcSetupUtil, TestSetFingerprintForPackagesCache) {
constexpr char kFingerintBefore[] =
"<packages>\n"
" <version sdkVersion=\"25\" databaseVersion=\"3\" "
"fingerprint=\"google/coral/{product}_cheets:7.1.1/R67-10545.0.0/"
"4697494:user/release-keys\" />\n"
" <version volumeUuid=\"primary_physical\" sdkVersion=\"25\" "
"databaseVersion=\"3\" fingerprint=\"google/coral/{product}_cheets:"
"7.1.1/R67-10545.0.0/4697494:user/release-keys\" />\n"
"</packages>\n";
constexpr char kFingerintAfter[] =
"<packages>\n"
" <version sdkVersion=\"25\" databaseVersion=\"3\" "
"fingerprint=\"google/coral/coral_cheets:7.1.1/R67-10545.0.0/"
"4697494:user/release-keys\" />\n"
" <version volumeUuid=\"primary_physical\" sdkVersion=\"25\" "
"databaseVersion=\"3\" fingerprint=\"google/coral/coral_cheets:"
"7.1.1/R67-10545.0.0/4697494:user/release-keys\" />\n"
"</packages>\n";
std::string new_content;
SetFingerprintsForPackagesCache(
kFingerintBefore,
"google/coral/coral_cheets:7.1.1/R67-10545.0.0/4697494:user/release-keys",
&new_content);
EXPECT_EQ(strlen(kFingerintAfter), new_content.length());
EXPECT_EQ(kFingerintAfter, new_content);
}
} // namespace arc