Move `ScopedUnroot` and `ScopedInaccessible` to common_art_test.
Various tests in artd need these functions, so they have to be in a
common place. They can also be used in other tests, such as
oat_file_assistant_test.
Bug: 229268202
Test: m test-art-host-gtest-art_artd_tests
Change-Id: Ie644fd9217cdf7de400cd525165141f950122f0d
Merged-In: Ia71296fb9963f9e79036da9d84a7dbbd733e06a3
diff --git a/libartbase/Android.bp b/libartbase/Android.bp
index 7ab7f5a..afad564 100644
--- a/libartbase/Android.bp
+++ b/libartbase/Android.bp
@@ -239,11 +239,13 @@
static: {
whole_static_libs: [
"libc++fs",
+ "libcap",
],
},
shared: {
static_libs: [
"libc++fs",
+ "libcap",
],
},
}
diff --git a/libartbase/base/common_art_test.cc b/libartbase/base/common_art_test.cc
index c74f3de..419b125 100644
--- a/libartbase/base/common_art_test.cc
+++ b/libartbase/base/common_art_test.cc
@@ -22,14 +22,17 @@
#include <ftw.h>
#include <libgen.h>
#include <stdlib.h>
+#include <sys/capability.h>
#include <unistd.h>
#include <cstdio>
#include <filesystem>
+#include <functional>
#include "android-base/file.h"
#include "android-base/logging.h"
#include "android-base/process.h"
+#include "android-base/scopeguard.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "android-base/unique_fd.h"
@@ -145,6 +148,46 @@
CHECK_EQ(0, unlink_result);
}
+// A wrapper of `cap_t` that automatically calls `cap_free`.
+class ScopedCap {
+ public:
+ explicit ScopedCap(cap_t cap) : cap_(cap) { CHECK_NE(cap, nullptr); }
+
+ ScopedCap(ScopedCap&& other) noexcept : cap_(std::exchange(other.cap_, nullptr)) {}
+
+ ~ScopedCap() {
+ if (cap_ != nullptr) {
+ CHECK_EQ(cap_free(cap_), 0);
+ }
+ }
+
+ cap_t Get() const { return cap_; }
+
+ private:
+ cap_t cap_;
+};
+
+// Temporarily drops all root capabilities when the test is run as root. This is a noop otherwise.
+android::base::ScopeGuard<std::function<void()>> ScopedUnroot() {
+ ScopedCap old_cap(cap_get_proc());
+ ScopedCap new_cap(cap_dup(old_cap.Get()));
+ CHECK_EQ(cap_clear_flag(new_cap.Get(), CAP_EFFECTIVE), 0);
+ CHECK_EQ(cap_set_proc(new_cap.Get()), 0);
+ // `old_cap` is actually not shared with anyone else, but we have to wrap it with a `shared_ptr`
+ // because `std::function` requires captures to be copyable.
+ return android::base::make_scope_guard(
+ [old_cap = std::make_shared<ScopedCap>(std::move(old_cap))]() {
+ CHECK_EQ(cap_set_proc(old_cap->Get()), 0);
+ });
+}
+
+// Temporarily drops write permission on a file/directory.
+android::base::ScopeGuard<std::function<void()>> ScopedInaccessible(const std::string& path) {
+ std::filesystem::perms old_perms = std::filesystem::status(path).permissions();
+ std::filesystem::permissions(path, std::filesystem::perms::none);
+ return android::base::make_scope_guard([=]() { std::filesystem::permissions(path, old_perms); });
+}
+
std::string CommonArtTestImpl::GetAndroidBuildTop() {
CHECK(IsHost());
std::string android_build_top;
diff --git a/libartbase/base/common_art_test.h b/libartbase/base/common_art_test.h
index 6124ed9..fccb217 100644
--- a/libartbase/base/common_art_test.h
+++ b/libartbase/base/common_art_test.h
@@ -25,6 +25,7 @@
#include <vector>
#include "android-base/logging.h"
+#include "android-base/scopeguard.h"
#include "base/file_utils.h"
#include "base/globals.h"
#include "base/memory_tool.h"
@@ -122,6 +123,12 @@
DISALLOW_COPY_AND_ASSIGN(ScopedUnsetEnvironmentVariable);
};
+// Temporarily drops all root capabilities when the test is run as root. This is a noop otherwise.
+android::base::ScopeGuard<std::function<void()>> ScopedUnroot();
+
+// Temporarily drops all permissions on a file/directory.
+android::base::ScopeGuard<std::function<void()>> ScopedInaccessible(const std::string& path);
+
class CommonArtTestImpl {
public:
CommonArtTestImpl() = default;