Merge changes from topic "tombstone_proto_electric_boogaloo"
* changes:
Remove use of libbase logging in libdebuggerd.
Reland protobuf tombstones.
diff --git a/bootstat/OWNERS b/bootstat/OWNERS
index 50b2097..f66b309 100644
--- a/bootstat/OWNERS
+++ b/bootstat/OWNERS
@@ -1,2 +1,2 @@
jhawkins@google.com
-salyzyn@google.com
+dvander@google.com
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 2876094..1efe793 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -2143,6 +2143,41 @@
return false;
}
+std::string fs_mgr_get_hashtree_algorithm(const android::fs_mgr::FstabEntry& entry) {
+ if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) {
+ return "";
+ }
+ DeviceMapper& dm = DeviceMapper::Instance();
+ std::string device = GetVerityDeviceName(entry);
+
+ std::vector<DeviceMapper::TargetInfo> table;
+ if (dm.GetState(device) == DmDeviceState::INVALID || !dm.GetTableInfo(device, &table)) {
+ return "";
+ }
+ for (const auto& target : table) {
+ if (strcmp(target.spec.target_type, "verity") != 0) {
+ continue;
+ }
+
+ // The format is stable for dm-verity version 0 & 1. And the data is expected to have
+ // the fixed format:
+ // <version> <dev> <hash_dev> <data_block_size> <hash_block_size> <num_data_blocks>
+ // <hash_start_block> <algorithm> <digest> <salt>
+ // Details in https://www.kernel.org/doc/html/latest/admin-guide/device-mapper/verity.html
+
+ std::vector<std::string> tokens = android::base::Split(target.data, " \t\r\n");
+ if (tokens[0] != "0" && tokens[0] != "1") {
+ LOG(WARNING) << "Unrecognized device mapper version in " << target.data;
+ return "";
+ }
+
+ // Hashtree algorithm is the 8th token in the output
+ return android::base::Trim(tokens[7]);
+ }
+
+ return "";
+}
+
bool fs_mgr_verity_is_check_at_most_once(const android::fs_mgr::FstabEntry& entry) {
if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) {
return false;
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 11e3664..22c02cc 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -88,6 +88,10 @@
bool fs_mgr_load_verity_state(int* mode);
// Returns true if verity is enabled on this particular FstabEntry.
bool fs_mgr_is_verity_enabled(const android::fs_mgr::FstabEntry& entry);
+// Returns the hash algorithm used to build the hashtree of this particular FstabEntry. Returns an
+// empty string if the input isn't a dm-verity entry, or if there is an error.
+std::string fs_mgr_get_hashtree_algorithm(const android::fs_mgr::FstabEntry& entry);
+
bool fs_mgr_swapon_all(const android::fs_mgr::Fstab& fstab);
bool fs_mgr_update_logical_partition(android::fs_mgr::FstabEntry* entry);
diff --git a/fs_mgr/libfs_avb/Android.bp b/fs_mgr/libfs_avb/Android.bp
index 2f1ca70..45e7a30 100644
--- a/fs_mgr/libfs_avb/Android.bp
+++ b/fs_mgr/libfs_avb/Android.bp
@@ -100,6 +100,7 @@
shared_libs: [
"libcrypto",
],
+ compile_multilib: "first",
data: [
":avbtool",
":fec",
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index d2ffaa7..ff7a727 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -379,6 +379,7 @@
FRIEND_TEST(SnapshotUpdateTest, MergeCannotRemoveCow);
FRIEND_TEST(SnapshotUpdateTest, MergeInRecovery);
FRIEND_TEST(SnapshotUpdateTest, SnapshotStatusFileWithoutCow);
+ FRIEND_TEST(SnapshotUpdateTest, SpaceSwapUpdate);
friend class SnapshotTest;
friend class SnapshotUpdateTest;
friend class FlashAfterUpdateTest;
@@ -717,6 +718,8 @@
bool PerformInitTransition(InitTransition transition,
std::vector<std::string>* snapuserd_argv = nullptr);
+ SnapuserdClient* snapuserd_client() const { return snapuserd_client_.get(); }
+
std::string gsid_dir_;
std::string metadata_dir_;
std::unique_ptr<IDeviceInfo> device_;
diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index ff0047e..b038527 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -146,6 +146,7 @@
bool WriteRandomData(const std::string& path, std::optional<size_t> expect_size = std::nullopt,
std::string* hash = nullptr);
bool WriteRandomData(ICowWriter* writer, std::string* hash = nullptr);
+std::string HashSnapshot(ISnapshotWriter* writer);
std::optional<std::string> GetHash(const std::string& path);
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 0b8a03a..95e7d89 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -225,7 +225,7 @@
}
AssertionResult MapUpdateSnapshot(const std::string& name,
- std::unique_ptr<ICowWriter>* writer) {
+ std::unique_ptr<ISnapshotWriter>* writer) {
TestPartitionOpener opener(fake_super);
CreateLogicalPartitionParams params{
.block_device = fake_super,
@@ -279,6 +279,7 @@
return AssertionFailure() << "Cannot unmap image " << snapshot << "-cow-img";
}
if (!(res = DeleteDevice(snapshot + "-base"))) return res;
+ if (!(res = DeleteDevice(snapshot + "-src"))) return res;
return AssertionSuccess();
}
@@ -319,7 +320,7 @@
// Prepare A/B slot for a partition named "test_partition".
AssertionResult PrepareOneSnapshot(uint64_t device_size,
- std::unique_ptr<ICowWriter>* writer = nullptr) {
+ std::unique_ptr<ISnapshotWriter>* writer = nullptr) {
lock_ = nullptr;
DeltaArchiveManifest manifest;
@@ -511,7 +512,7 @@
static const uint64_t kDeviceSize = 1024 * 1024;
- std::unique_ptr<ICowWriter> writer;
+ std::unique_ptr<ISnapshotWriter> writer;
ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize, &writer));
// Release the lock.
@@ -827,11 +828,14 @@
opener_ = std::make_unique<TestPartitionOpener>(fake_super);
+ auto dynamic_partition_metadata = manifest_.mutable_dynamic_partition_metadata();
+ dynamic_partition_metadata->set_vabc_enabled(IsCompressionEnabled());
+
// Create a fake update package metadata.
// Not using full name "system", "vendor", "product" because these names collide with the
// mapped partitions on the running device.
// Each test modifies manifest_ slightly to indicate changes to the partition layout.
- group_ = manifest_.mutable_dynamic_partition_metadata()->add_groups();
+ group_ = dynamic_partition_metadata->add_groups();
group_->set_name("group");
group_->set_size(kGroupSize);
group_->add_partition_names("sys");
@@ -945,7 +949,7 @@
AssertionResult MapOneUpdateSnapshot(const std::string& name) {
if (IsCompressionEnabled()) {
- std::unique_ptr<ICowWriter> writer;
+ std::unique_ptr<ISnapshotWriter> writer;
return MapUpdateSnapshot(name, &writer);
} else {
std::string path;
@@ -955,7 +959,7 @@
AssertionResult WriteSnapshotAndHash(const std::string& name) {
if (IsCompressionEnabled()) {
- std::unique_ptr<ICowWriter> writer;
+ std::unique_ptr<ISnapshotWriter> writer;
auto res = MapUpdateSnapshot(name, &writer);
if (!res) {
return res;
@@ -984,6 +988,42 @@
<< ", hash: " << hashes_[name];
}
+ // Generate a snapshot that moves all the upper blocks down to the start.
+ // It doesn't really matter the order, we just want copies that reference
+ // blocks that won't exist if the partition shrinks.
+ AssertionResult ShiftAllSnapshotBlocks(const std::string& name, uint64_t old_size) {
+ std::unique_ptr<ISnapshotWriter> writer;
+ if (auto res = MapUpdateSnapshot(name, &writer); !res) {
+ return res;
+ }
+ if (!writer->options().max_blocks || !*writer->options().max_blocks) {
+ return AssertionFailure() << "No max blocks set for " << name << " writer";
+ }
+
+ uint64_t src_block = (old_size / writer->options().block_size) - 1;
+ uint64_t dst_block = 0;
+ uint64_t max_blocks = *writer->options().max_blocks;
+ while (dst_block < max_blocks && dst_block < src_block) {
+ if (!writer->AddCopy(dst_block, src_block)) {
+ return AssertionFailure() << "Unable to add copy for " << name << " for blocks "
+ << src_block << ", " << dst_block;
+ }
+ dst_block++;
+ src_block--;
+ }
+ if (!writer->Finalize()) {
+ return AssertionFailure() << "Unable to finalize writer for " << name;
+ }
+
+ auto hash = HashSnapshot(writer.get());
+ if (hash.empty()) {
+ return AssertionFailure() << "Unable to hash snapshot writer for " << name;
+ }
+ hashes_[name] = hash;
+
+ return AssertionSuccess();
+ }
+
AssertionResult MapUpdateSnapshots(const std::vector<std::string>& names = {"sys_b", "vnd_b",
"prd_b"}) {
for (const auto& name : names) {
@@ -1092,8 +1132,132 @@
// Initiate the merge and wait for it to be completed.
ASSERT_TRUE(init->InitiateMerge());
ASSERT_EQ(init->IsSnapuserdRequired(), IsCompressionEnabled());
+ {
+ // We should have started in SECOND_PHASE since nothing shrinks.
+ ASSERT_TRUE(AcquireLock());
+ auto local_lock = std::move(lock_);
+ auto status = init->ReadSnapshotUpdateStatus(local_lock.get());
+ ASSERT_EQ(status.merge_phase(), MergePhase::SECOND_PHASE);
+ }
ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
+ // Make sure the second phase ran and deleted snapshots.
+ {
+ ASSERT_TRUE(AcquireLock());
+ auto local_lock = std::move(lock_);
+ std::vector<std::string> snapshots;
+ ASSERT_TRUE(init->ListSnapshots(local_lock.get(), &snapshots));
+ ASSERT_TRUE(snapshots.empty());
+ }
+
+ // Check that the target partitions have the same content after the merge.
+ for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+ ASSERT_TRUE(IsPartitionUnchanged(name))
+ << "Content of " << name << " changes after the merge";
+ }
+}
+
+// Test that shrinking and growing partitions at the same time is handled
+// correctly in VABC.
+TEST_F(SnapshotUpdateTest, SpaceSwapUpdate) {
+ // OTA client blindly unmaps all partitions that are possibly mapped.
+ for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+ ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
+ }
+
+ auto old_sys_size = GetSize(sys_);
+ auto old_prd_size = GetSize(prd_);
+
+ // Grow |sys| but shrink |prd|.
+ SetSize(sys_, old_sys_size * 2);
+ sys_->set_estimate_cow_size(8_MiB);
+ SetSize(prd_, old_prd_size / 2);
+ prd_->set_estimate_cow_size(1_MiB);
+
+ AddOperationForPartitions();
+
+ ASSERT_TRUE(sm->BeginUpdate());
+ ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+
+ // Check that the old partition sizes were saved correctly.
+ {
+ ASSERT_TRUE(AcquireLock());
+ auto local_lock = std::move(lock_);
+
+ SnapshotStatus status;
+ ASSERT_TRUE(sm->ReadSnapshotStatus(local_lock.get(), "prd_b", &status));
+ ASSERT_EQ(status.old_partition_size(), 3145728);
+ ASSERT_TRUE(sm->ReadSnapshotStatus(local_lock.get(), "sys_b", &status));
+ ASSERT_EQ(status.old_partition_size(), 3145728);
+ }
+
+ ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
+ ASSERT_TRUE(WriteSnapshotAndHash("vnd_b"));
+ ASSERT_TRUE(ShiftAllSnapshotBlocks("prd_b", old_prd_size));
+
+ sync();
+
+ // Assert that source partitions aren't affected.
+ for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
+ ASSERT_TRUE(IsPartitionUnchanged(name));
+ }
+
+ ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
+
+ // Simulate shutting down the device.
+ ASSERT_TRUE(UnmapAll());
+
+ // After reboot, init does first stage mount.
+ auto init = NewManagerForFirstStageMount("_b");
+ ASSERT_NE(init, nullptr);
+ ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+ ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+
+ auto indicator = sm->GetRollbackIndicatorPath();
+ ASSERT_NE(access(indicator.c_str(), R_OK), 0);
+
+ // Check that the target partitions have the same content.
+ for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+ ASSERT_TRUE(IsPartitionUnchanged(name));
+ }
+
+ // Initiate the merge and wait for it to be completed.
+ ASSERT_TRUE(init->InitiateMerge());
+ ASSERT_EQ(init->IsSnapuserdRequired(), IsCompressionEnabled());
+ {
+ // Check that the merge phase is FIRST_PHASE until at least one call
+ // to ProcessUpdateState() occurs.
+ ASSERT_TRUE(AcquireLock());
+ auto local_lock = std::move(lock_);
+ auto status = init->ReadSnapshotUpdateStatus(local_lock.get());
+ ASSERT_EQ(status.merge_phase(), MergePhase::FIRST_PHASE);
+ }
+
+ // Simulate shutting down the device and creating partitions again.
+ ASSERT_TRUE(UnmapAll());
+ ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+
+ // Check that we used the correct types after rebooting mid-merge.
+ DeviceMapper::TargetInfo target;
+ ASSERT_TRUE(init->IsSnapshotDevice("prd_b", &target));
+ ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot-merge");
+ ASSERT_TRUE(init->IsSnapshotDevice("sys_b", &target));
+ ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
+ ASSERT_TRUE(init->IsSnapshotDevice("vnd_b", &target));
+ ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
+
+ // Complete the merge.
+ ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
+
+ // Make sure the second phase ran and deleted snapshots.
+ {
+ ASSERT_TRUE(AcquireLock());
+ auto local_lock = std::move(lock_);
+ std::vector<std::string> snapshots;
+ ASSERT_TRUE(init->ListSnapshots(local_lock.get(), &snapshots));
+ ASSERT_TRUE(snapshots.empty());
+ }
+
// Check that the target partitions have the same content after the merge.
for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
ASSERT_TRUE(IsPartitionUnchanged(name))
@@ -1814,6 +1978,13 @@
ASSERT_TRUE(init->PerformInitTransition(SnapshotManager::InitTransition::SECOND_STAGE));
+ // :TODO: this is a workaround to ensure the handler list stays empty. We
+ // should make this test more like actual init, and spawn two copies of
+ // snapuserd, given how many other tests we now have for normal snapuserd.
+ ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("sys_b-user-cow-init"));
+ ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("vnd_b-user-cow-init"));
+ ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("prd_b-user-cow-init"));
+
// The control device should have been renamed.
ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted("/dev/dm-user/sys_b-user-cow-init", 10s));
ASSERT_EQ(access("/dev/dm-user/sys_b-user-cow", F_OK), 0);
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index 6104c82..e3e3af8 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -23,6 +23,7 @@
#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
#include <openssl/sha.h>
+#include <payload_consumer/file_descriptor.h>
namespace android {
namespace snapshot {
@@ -169,6 +170,37 @@
return true;
}
+std::string HashSnapshot(ISnapshotWriter* writer) {
+ auto reader = writer->OpenReader();
+ if (!reader) {
+ return {};
+ }
+
+ SHA256_CTX ctx;
+ SHA256_Init(&ctx);
+
+ uint64_t remaining = reader->BlockDevSize();
+ char buffer[4096];
+ while (remaining) {
+ size_t to_read =
+ static_cast<size_t>(std::min(remaining, static_cast<uint64_t>(sizeof(buffer))));
+ ssize_t read = reader->Read(&buffer, to_read);
+ if (read <= 0) {
+ if (read < 0) {
+ LOG(ERROR) << "Failed to read from snapshot writer";
+ return {};
+ }
+ break;
+ }
+ SHA256_Update(&ctx, buffer, to_read);
+ remaining -= static_cast<size_t>(read);
+ }
+
+ uint8_t out[32];
+ SHA256_Final(out, &ctx);
+ return ToHexString(out, sizeof(out));
+}
+
std::optional<std::string> GetHash(const std::string& path) {
std::string content;
if (!android::base::ReadFileToString(path, &content, true)) {
diff --git a/init/builtins.cpp b/init/builtins.cpp
index c44e03e..6b7d1e9 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -861,6 +861,11 @@
// for system as root, so it has property [partition.system.verified].
std::string partition = entry.mount_point == "/" ? "system" : Basename(entry.mount_point);
SetProperty("partition." + partition + ".verified", std::to_string(mode));
+
+ std::string hash_alg = fs_mgr_get_hashtree_algorithm(entry);
+ if (!hash_alg.empty()) {
+ SetProperty("partition." + partition + ".verified.hash_alg", hash_alg);
+ }
}
return {};
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 923d769..331255b 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -16,6 +16,7 @@
#include "ueventd.h"
+#include <android/api-level.h>
#include <ctype.h>
#include <dirent.h>
#include <fcntl.h>
@@ -266,6 +267,17 @@
LOG(INFO) << "Coldboot took " << cold_boot_timer.duration().count() / 1000.0f << " seconds";
}
+static UeventdConfiguration GetConfiguration() {
+ // TODO: Remove these legacy paths once Android S is no longer supported.
+ if (android::base::GetIntProperty("ro.product.first_api_level", 10000) <= __ANDROID_API_S__) {
+ auto hardware = android::base::GetProperty("ro.hardware", "");
+ return ParseConfig({"/system/etc/ueventd.rc", "/vendor/ueventd.rc", "/odm/ueventd.rc",
+ "/ueventd." + hardware + ".rc"});
+ }
+
+ return ParseConfig({"/system/etc/ueventd.rc"});
+}
+
int ueventd_main(int argc, char** argv) {
/*
* init sets the umask to 077 for forked processes. We need to
@@ -283,7 +295,7 @@
std::vector<std::unique_ptr<UeventHandler>> uevent_handlers;
- auto ueventd_configuration = ParseConfig("/system/etc/ueventd.rc");
+ auto ueventd_configuration = GetConfiguration();
uevent_handlers.emplace_back(std::make_unique<DeviceHandler>(
std::move(ueventd_configuration.dev_permissions),
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index 2605158..cab988b 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -230,7 +230,7 @@
return {};
}
-UeventdConfiguration ParseConfig(const std::string& config) {
+UeventdConfiguration ParseConfig(const std::vector<std::string>& configs) {
Parser parser;
UeventdConfiguration ueventd_configuration;
@@ -260,7 +260,9 @@
std::bind(ParseEnabledDisabledLine, _1,
&ueventd_configuration.enable_parallel_restorecon));
- parser.ParseConfig(config);
+ for (const auto& config : configs) {
+ parser.ParseConfig(config);
+ }
return ueventd_configuration;
}
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index 2672626..eaafa5a 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -36,7 +36,7 @@
bool enable_parallel_restorecon = false;
};
-UeventdConfiguration ParseConfig(const std::string& config);
+UeventdConfiguration ParseConfig(const std::vector<std::string>& configs);
} // namespace init
} // namespace android
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index d669ebe..2bf48fc 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -131,13 +131,25 @@
return StringPrintf("%s/uid_%d/pid_%d", cgroup, uid, pid);
}
-static int RemoveProcessGroup(const char* cgroup, uid_t uid, int pid) {
- int ret;
-
+static int RemoveProcessGroup(const char* cgroup, uid_t uid, int pid, unsigned int retries) {
+ int ret = 0;
auto uid_pid_path = ConvertUidPidToPath(cgroup, uid, pid);
- ret = rmdir(uid_pid_path.c_str());
-
auto uid_path = ConvertUidToPath(cgroup, uid);
+
+ if (retries == 0) {
+ retries = 1;
+ }
+
+ while (retries--) {
+ ret = rmdir(uid_pid_path.c_str());
+ if (!ret || errno != EBUSY) break;
+ std::this_thread::sleep_for(5ms);
+ }
+
+ // With the exception of boot or shutdown, system uid_ folders are always populated. Spinning
+ // here would needlessly delay most pid removals. Additionally, once empty a uid_ cgroup won't
+ // have processes hanging on it (we've already spun for all its pid_), so there's no need to
+ // spin anyway.
rmdir(uid_path.c_str());
return ret;
@@ -176,7 +188,7 @@
std::vector<std::string> cgroups;
std::string path;
- if (CgroupGetControllerPath("cpuacct", &path)) {
+ if (CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &path)) {
cgroups.push_back(path);
}
if (CgroupGetControllerPath("memory", &path)) {
@@ -212,19 +224,49 @@
}
}
+/**
+ * Process groups are primarily created by the Zygote, meaning that uid/pid groups are created by
+ * the user root. Ownership for the newly created cgroup and all of its files must thus be
+ * transferred for the user/group passed as uid/gid before system_server can properly access them.
+ */
static bool MkdirAndChown(const std::string& path, mode_t mode, uid_t uid, gid_t gid) {
if (mkdir(path.c_str(), mode) == -1 && errno != EEXIST) {
return false;
}
- if (chown(path.c_str(), uid, gid) == -1) {
- int saved_errno = errno;
- rmdir(path.c_str());
- errno = saved_errno;
- return false;
+ auto dir = std::unique_ptr<DIR, decltype(&closedir)>(opendir(path.c_str()), closedir);
+
+ if (dir == NULL) {
+ PLOG(ERROR) << "opendir failed for " << path;
+ goto err;
+ }
+
+ struct dirent* dir_entry;
+ while ((dir_entry = readdir(dir.get()))) {
+ if (!strcmp("..", dir_entry->d_name)) {
+ continue;
+ }
+
+ std::string file_path = path + "/" + dir_entry->d_name;
+
+ if (lchown(file_path.c_str(), uid, gid) < 0) {
+ PLOG(ERROR) << "lchown failed for " << file_path;
+ goto err;
+ }
+
+ if (fchmodat(AT_FDCWD, file_path.c_str(), mode, AT_SYMLINK_NOFOLLOW) != 0) {
+ PLOG(ERROR) << "fchmodat failed for " << file_path;
+ goto err;
+ }
}
return true;
+err:
+ int saved_errno = errno;
+ rmdir(path.c_str());
+ errno = saved_errno;
+
+ return false;
}
// Returns number of processes killed on success
@@ -302,17 +344,9 @@
static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries,
int* max_processes) {
- std::string cpuacct_path;
- std::string memory_path;
-
- CgroupGetControllerPath("cpuacct", &cpuacct_path);
- CgroupGetControllerPath("memory", &memory_path);
- memory_path += "/apps";
-
- const char* cgroup =
- (!access(ConvertUidPidToPath(cpuacct_path.c_str(), uid, initialPid).c_str(), F_OK))
- ? cpuacct_path.c_str()
- : memory_path.c_str();
+ std::string hierarchy_root_path;
+ CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &hierarchy_root_path);
+ const char* cgroup = hierarchy_root_path.c_str();
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
@@ -355,7 +389,17 @@
LOG(INFO) << "Successfully killed process cgroup uid " << uid << " pid " << initialPid
<< " in " << static_cast<int>(ms) << "ms";
}
- return RemoveProcessGroup(cgroup, uid, initialPid);
+
+ int err = RemoveProcessGroup(cgroup, uid, initialPid, retries);
+
+ if (isMemoryCgroupSupported() && UsePerAppMemcg()) {
+ std::string memory_path;
+ CgroupGetControllerPath("memory", &memory_path);
+ memory_path += "/apps";
+ if (RemoveProcessGroup(memory_path.c_str(), uid, initialPid, retries)) return -1;
+ }
+
+ return err;
} else {
if (retries > 0) {
LOG(ERROR) << "Failed to kill process cgroup uid " << uid << " pid " << initialPid
@@ -374,15 +418,7 @@
return KillProcessGroup(uid, initialPid, signal, 0 /*retries*/, max_processes);
}
-int createProcessGroup(uid_t uid, int initialPid, bool memControl) {
- std::string cgroup;
- if (isMemoryCgroupSupported() && (memControl || UsePerAppMemcg())) {
- CgroupGetControllerPath("memory", &cgroup);
- cgroup += "/apps";
- } else {
- CgroupGetControllerPath("cpuacct", &cgroup);
- }
-
+static int createProcessGroupInternal(uid_t uid, int initialPid, std::string cgroup) {
auto uid_path = ConvertUidToPath(cgroup.c_str(), uid);
if (!MkdirAndChown(uid_path, 0750, AID_SYSTEM, AID_SYSTEM)) {
@@ -408,6 +444,27 @@
return ret;
}
+int createProcessGroup(uid_t uid, int initialPid, bool memControl) {
+ std::string cgroup;
+
+ if (memControl && !UsePerAppMemcg()) {
+ PLOG(ERROR) << "service memory controls are used without per-process memory cgroup support";
+ return -EINVAL;
+ }
+
+ if (isMemoryCgroupSupported() && UsePerAppMemcg()) {
+ CgroupGetControllerPath("memory", &cgroup);
+ cgroup += "/apps";
+ int ret = createProcessGroupInternal(uid, initialPid, cgroup);
+ if (ret != 0) {
+ return ret;
+ }
+ }
+
+ CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &cgroup);
+ return createProcessGroupInternal(uid, initialPid, cgroup);
+}
+
static bool SetProcessGroupValue(int tid, const std::string& attr_name, int64_t value) {
if (!isMemoryCgroupSupported()) {
PLOG(ERROR) << "Memcg is not mounted.";
diff --git a/libprocessgroup/profiles/cgroups.json b/libprocessgroup/profiles/cgroups.json
index 5b7a28a..962d2ba 100644
--- a/libprocessgroup/profiles/cgroups.json
+++ b/libprocessgroup/profiles/cgroups.json
@@ -15,11 +15,6 @@
"GID": "system"
},
{
- "Controller": "cpuacct",
- "Path": "/acct",
- "Mode": "0555"
- },
- {
"Controller": "cpuset",
"Path": "/dev/cpuset",
"Mode": "0755",
@@ -42,7 +37,7 @@
"Controllers": [
{
"Controller": "freezer",
- "Path": "freezer",
+ "Path": ".",
"Mode": "0755",
"UID": "system",
"GID": "system"
diff --git a/libprocessgroup/profiles/cgroups.recovery.json b/libprocessgroup/profiles/cgroups.recovery.json
index f0bf5fd..2c63c08 100644
--- a/libprocessgroup/profiles/cgroups.recovery.json
+++ b/libprocessgroup/profiles/cgroups.recovery.json
@@ -1,9 +1,2 @@
{
- "Cgroups": [
- {
- "Controller": "cpuacct",
- "Path": "/acct",
- "Mode": "0555"
- }
- ]
}
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 5b57bdd..628098b 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -46,7 +46,7 @@
"File": "cpu.uclamp.latency_sensitive"
},
{
- "Name": "FreezerState",
+ "Name": "Freezer",
"Controller": "freezer",
"File": "cgroup.freeze"
}
@@ -70,11 +70,11 @@
"Name": "Frozen",
"Actions": [
{
- "Name": "JoinCgroup",
+ "Name": "SetAttribute",
"Params":
{
- "Controller": "freezer",
- "Path": ""
+ "Name": "Freezer",
+ "Value": "1"
}
}
]
@@ -83,11 +83,11 @@
"Name": "Unfrozen",
"Actions": [
{
- "Name": "JoinCgroup",
+ "Name": "SetAttribute",
"Params":
{
- "Controller": "freezer",
- "Path": "../"
+ "Name": "Freezer",
+ "Value": "0"
}
}
]
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 1311306..8d4ce25 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -273,7 +273,7 @@
value = StringReplace(value, "<pid>", std::to_string(pid), true);
if (!WriteStringToFile(value, filepath)) {
- PLOG(ERROR) << "Failed to write '" << value << "' to " << filepath;
+ if (logfailures_) PLOG(ERROR) << "Failed to write '" << value << "' to " << filepath;
return false;
}
@@ -290,7 +290,7 @@
value = StringReplace(value, "<pid>", std::to_string(tid), true);
if (!WriteStringToFile(value, filepath)) {
- PLOG(ERROR) << "Failed to write '" << value << "' to " << filepath;
+ if (logfailures_) PLOG(ERROR) << "Failed to write '" << value << "' to " << filepath;
return false;
}
@@ -516,7 +516,10 @@
std::string attr_filepath = params_val["FilePath"].asString();
std::string attr_value = params_val["Value"].asString();
if (!attr_filepath.empty() && !attr_value.empty()) {
- profile->Add(std::make_unique<WriteFileAction>(attr_filepath, attr_value));
+ const Json::Value& logfailures = params_val["LogFailures"];
+ bool attr_logfailures = logfailures.isNull() || logfailures.asBool();
+ profile->Add(std::make_unique<WriteFileAction>(attr_filepath, attr_value,
+ attr_logfailures));
} else if (attr_filepath.empty()) {
LOG(WARNING) << "WriteFile: invalid parameter: "
<< "empty filepath";
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 98bcb0e..25a84b0 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -142,14 +142,16 @@
// Write to file action
class WriteFileAction : public ProfileAction {
public:
- WriteFileAction(const std::string& filepath, const std::string& value) noexcept
- : filepath_(filepath), value_(value) {}
+ WriteFileAction(const std::string& filepath, const std::string& value,
+ bool logfailures) noexcept
+ : filepath_(filepath), value_(value), logfailures_(logfailures) {}
virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
virtual bool ExecuteForTask(int tid) const;
private:
std::string filepath_, value_;
+ bool logfailures_;
};
class TaskProfile {
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index aebcd5f..e9293b5 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -1,6 +1,5 @@
-import /vendor/ueventd.rc
-import /odm/ueventd.rc
-import /ueventd.${ro.hardware}.rc
+import /vendor/etc/ueventd.rc
+import /odm/etc/ueventd.rc
firmware_directories /etc/firmware/ /odm/firmware/ /vendor/firmware/ /firmware/image/
uevent_socket_rcvbuf_size 16M