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;