diff options
author | 2023-09-07 19:05:10 +0100 | |
---|---|---|
committer | 2023-10-10 19:01:06 +0000 | |
commit | 356505949dc46612f64b13424d2496f4e9ef9ace (patch) | |
tree | 5bdc185e46dd1397ca09199965f41ac8c82c03c4 | |
parent | 6ab6ca740ec15717d8ebbbfcdea72f9f7a1c0731 (diff) |
Add an artd method to delete runtime image files.
Bug: 299442352
Test: atest art_standalone_artd_tests
Change-Id: Ibc31bd5c0ad2d5d3ea1cfa427f76e1dc6fc93b11
Merged-In: Ibc31bd5c0ad2d5d3ea1cfa427f76e1dc6fc93b11
-rw-r--r-- | artd/artd.cc | 11 | ||||
-rw-r--r-- | artd/artd.h | 4 | ||||
-rw-r--r-- | artd/artd_test.cc | 103 | ||||
-rw-r--r-- | artd/binder/com/android/server/art/IArtd.aidl | 13 | ||||
-rw-r--r-- | artd/binder/com/android/server/art/RuntimeArtifactsPath.aidl | 32 | ||||
-rw-r--r-- | artd/path_utils.cc | 31 | ||||
-rw-r--r-- | artd/path_utils.h | 6 | ||||
-rw-r--r-- | libarttools/tools/tools.cc | 5 | ||||
-rw-r--r-- | libarttools/tools/tools.h | 3 | ||||
-rw-r--r-- | libarttools/tools/tools_test.cc | 31 | ||||
-rw-r--r-- | runtime/runtime_image.cc | 26 | ||||
-rw-r--r-- | runtime/runtime_image.h | 8 |
12 files changed, 262 insertions, 11 deletions
diff --git a/artd/artd.cc b/artd/artd.cc index a4e0f324fc..89156c28f8 100644 --- a/artd/artd.cc +++ b/artd/artd.cc @@ -96,6 +96,7 @@ using ::aidl::com::android::server::art::OutputArtifacts; using ::aidl::com::android::server::art::OutputProfile; using ::aidl::com::android::server::art::PriorityClass; using ::aidl::com::android::server::art::ProfilePath; +using ::aidl::com::android::server::art::RuntimeArtifactsPath; using ::aidl::com::android::server::art::VdexPath; using ::android::base::Dirname; using ::android::base::Error; @@ -1154,6 +1155,16 @@ ScopedAStatus Artd::isInDalvikCache(const std::string& in_dexFile, bool* _aidl_r return NonFatal(ART_FORMAT("Fstab entries not found for '{}'", in_dexFile)); } +ScopedAStatus Artd::deleteRuntimeArtifacts(const RuntimeArtifactsPath& in_runtimeArtifactsPath, + int64_t* _aidl_return) { + OR_RETURN_FATAL(ValidateRuntimeArtifactsPath(in_runtimeArtifactsPath)); + for (const std::string& file : + OR_RETURN_NON_FATAL(ListRuntimeArtifactsFiles(in_runtimeArtifactsPath))) { + *_aidl_return += GetSizeAndDeleteFile(file); + } + return ScopedAStatus::ok(); +} + ScopedAStatus Artd::validateDexPath(const std::string& in_dexPath, std::optional<std::string>* _aidl_return) { if (Result<void> result = ValidateDexPath(in_dexPath); !result.ok()) { diff --git a/artd/artd.h b/artd/artd.h index 774f11a1d1..c6bce721a7 100644 --- a/artd/artd.h +++ b/artd/artd.h @@ -166,6 +166,10 @@ class Artd : public aidl::com::android::server::art::BnArtd { ndk::ScopedAStatus isInDalvikCache(const std::string& in_dexFile, bool* _aidl_return) override; + ndk::ScopedAStatus deleteRuntimeArtifacts( + const aidl::com::android::server::art::RuntimeArtifactsPath& in_runtimeArtifactsPath, + int64_t* _aidl_return) override; + ndk::ScopedAStatus validateDexPath(const std::string& in_dexPath, std::optional<std::string>* _aidl_return) override; diff --git a/artd/artd_test.cc b/artd/artd_test.cc index ae18b1af91..41c175543c 100644 --- a/artd/artd_test.cc +++ b/artd/artd_test.cc @@ -81,6 +81,7 @@ using ::aidl::com::android::server::art::OutputArtifacts; using ::aidl::com::android::server::art::OutputProfile; using ::aidl::com::android::server::art::PriorityClass; using ::aidl::com::android::server::art::ProfilePath; +using ::aidl::com::android::server::art::RuntimeArtifactsPath; using ::aidl::com::android::server::art::VdexPath; using ::android::base::Append; using ::android::base::Error; @@ -2118,6 +2119,108 @@ TEST_F(ArtdTest, isInDalvikCache) { EXPECT_THAT(is_in_dalvik_cache("/foo"), HasValue(true)); } +TEST_F(ArtdTest, deleteRuntimeArtifacts) { + // TODO(b/289037540): Fix this. + if (getuid() != kRootUid) { + GTEST_SKIP() << "This test requires root access"; + } + + std::vector<std::string> removed_files; + std::vector<std::string> kept_files; + + auto CreateRemovedFile = [&](const std::string& path) { + CreateFile(path); + removed_files.push_back(path); + }; + + auto CreateKeptFile = [&](const std::string& path) { + CreateFile(path); + kept_files.push_back(path); + }; + + CreateKeptFile(android_data_ + + "/user/0/com.android.different_package/cache/oat_primary/arm64/base.art"); + CreateKeptFile(android_data_ + + "/user/0/com.android.foo/cache/oat_primary/arm64/different_dex.art"); + CreateKeptFile(android_data_ + + "/user/0/com.android.foo/cache/oat_primary/different_isa/base.art"); + CreateKeptFile(android_data_ + + "/user/0/com.android.foo/cache/not_oat_dir/oat_primary/arm64/base.art"); + + CreateRemovedFile(android_data_ + "/user_de/0/com.android.foo/cache/oat_primary/arm64/base.art"); + CreateRemovedFile(android_data_ + "/user/0/com.android.foo/cache/oat_primary/arm64/base.art"); + CreateRemovedFile(android_data_ + "/user/1/com.android.foo/cache/oat_primary/arm64/base.art"); + CreateRemovedFile(android_expand_ + + "/123456-7890/user/1/com.android.foo/cache/oat_primary/arm64/base.art"); + + int64_t aidl_return; + ASSERT_TRUE( + artd_ + ->deleteRuntimeArtifacts( + {.packageName = "com.android.foo", .dexPath = "/a/b/base.apk", .isa = "arm64"}, + &aidl_return) + .isOk()); + + for (const std::string& path : removed_files) { + EXPECT_FALSE(std::filesystem::exists(path)) << ART_FORMAT("'{}' should be removed", path); + } + + for (const std::string& path : kept_files) { + EXPECT_TRUE(std::filesystem::exists(path)) << ART_FORMAT("'{}' should be kept", path); + } +} + +TEST_F(ArtdTest, deleteRuntimeArtifactsSpecialChars) { + // TODO(b/289037540): Fix this. + if (getuid() != kRootUid) { + GTEST_SKIP() << "This test requires root access"; + } + + std::vector<std::string> removed_files; + std::vector<std::string> kept_files; + + auto CreateRemovedFile = [&](const std::string& path) { + CreateFile(path); + removed_files.push_back(path); + }; + + auto CreateKeptFile = [&](const std::string& path) { + CreateFile(path); + kept_files.push_back(path); + }; + + CreateKeptFile(android_data_ + "/user/0/com.android.foo/cache/oat_primary/arm64/base.art"); + + CreateRemovedFile(android_data_ + "/user/0/*/cache/oat_primary/arm64/base.art"); + CreateRemovedFile(android_data_ + "/user/0/com.android.foo/cache/oat_primary/*/base.art"); + CreateRemovedFile(android_data_ + "/user/0/com.android.foo/cache/oat_primary/arm64/*.art"); + + int64_t aidl_return; + ASSERT_TRUE( + artd_ + ->deleteRuntimeArtifacts({.packageName = "*", .dexPath = "/a/b/base.apk", .isa = "arm64"}, + &aidl_return) + .isOk()); + ASSERT_TRUE(artd_ + ->deleteRuntimeArtifacts( + {.packageName = "com.android.foo", .dexPath = "/a/b/*.apk", .isa = "arm64"}, + &aidl_return) + .isOk()); + ASSERT_TRUE(artd_ + ->deleteRuntimeArtifacts( + {.packageName = "com.android.foo", .dexPath = "/a/b/base.apk", .isa = "*"}, + &aidl_return) + .isOk()); + + for (const std::string& path : removed_files) { + EXPECT_FALSE(std::filesystem::exists(path)) << ART_FORMAT("'{}' should be removed", path); + } + + for (const std::string& path : kept_files) { + EXPECT_TRUE(std::filesystem::exists(path)) << ART_FORMAT("'{}' should be kept", path); + } +} + } // namespace } // namespace artd } // namespace art diff --git a/artd/binder/com/android/server/art/IArtd.aidl b/artd/binder/com/android/server/art/IArtd.aidl index 3b55297ab2..f7b2251917 100644 --- a/artd/binder/com/android/server/art/IArtd.aidl +++ b/artd/binder/com/android/server/art/IArtd.aidl @@ -22,7 +22,10 @@ interface IArtd { boolean isAlive(); /** - * Deletes artifacts and returns the released space, in bytes. + * Deletes dexopt artifacts and returns the released space, in bytes. + * + * Note that this method doesn't delete runtime artifacts. To delete them, call + * `deleteRuntimeArtifacts`. * * Throws fatal errors. Logs and ignores non-fatal errors. */ @@ -180,6 +183,14 @@ interface IArtd { boolean isInDalvikCache(@utf8InCpp String dexFile); /** + * Deletes runtime artifacts and returns the released space, in bytes. + * + * Throws fatal errors. Logs and ignores non-fatal errors. + */ + long deleteRuntimeArtifacts( + in com.android.server.art.RuntimeArtifactsPath runtimeArtifactsPath); + + /** * Returns an error message if the given dex path is invalid, or null if the validation * passes. */ diff --git a/artd/binder/com/android/server/art/RuntimeArtifactsPath.aidl b/artd/binder/com/android/server/art/RuntimeArtifactsPath.aidl new file mode 100644 index 0000000000..4096ea90ab --- /dev/null +++ b/artd/binder/com/android/server/art/RuntimeArtifactsPath.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2023 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. + */ + +package com.android.server.art; + +/** + * Represents the path to the runtime artifacts of a dex file (i.e., ART files generated by the + * runtime, not by dexopt). + * + * @hide + */ +parcelable RuntimeArtifactsPath { + /** The name of the package. */ + @utf8InCpp String packageName; + /** The absolute path starting with '/' to the dex file (i.e., APK or JAR file). */ + @utf8InCpp String dexPath; + /** The instruction set of the dexopt artifacts. */ + @utf8InCpp String isa; +} diff --git a/artd/path_utils.cc b/artd/path_utils.cc index 6ff9b95bc1..0a77d9d0c6 100644 --- a/artd/path_utils.cc +++ b/artd/path_utils.cc @@ -30,6 +30,7 @@ #include "file_utils.h" #include "fstab/fstab.h" #include "oat_file_assistant.h" +#include "runtime_image.h" #include "tools/tools.h" namespace art { @@ -40,6 +41,7 @@ namespace { using ::aidl::com::android::server::art::ArtifactsPath; using ::aidl::com::android::server::art::DexMetadataPath; using ::aidl::com::android::server::art::ProfilePath; +using ::aidl::com::android::server::art::RuntimeArtifactsPath; using ::aidl::com::android::server::art::VdexPath; using ::android::base::Error; using ::android::base::Result; @@ -155,6 +157,35 @@ Result<std::vector<std::string>> ListManagedFiles() { return tools::Glob(patterns); } +Result<std::vector<std::string>> ListRuntimeArtifactsFiles( + const RuntimeArtifactsPath& runtime_artifacts_path) { + std::string android_data = OR_RETURN(GetAndroidDataOrError()); + std::string android_expand = OR_RETURN(GetAndroidExpandOrError()); + + // See `art::tools::Glob` for the syntax. + std::vector<std::string> patterns; + + for (const std::string& data_root : {android_data, android_expand + "/*"}) { + for (const char* user_dir : {"/user", "/user_de"}) { + std::string data_dir = + data_root + user_dir + "/*/" + tools::EscapeGlob(runtime_artifacts_path.packageName); + patterns.push_back( + RuntimeImage::GetRuntimeImagePath(data_dir, + tools::EscapeGlob(runtime_artifacts_path.dexPath), + tools::EscapeGlob(runtime_artifacts_path.isa))); + } + } + + return tools::Glob(patterns); +} + +Result<void> ValidateRuntimeArtifactsPath(const RuntimeArtifactsPath& runtime_artifacts_path) { + OR_RETURN(ValidatePathElement(runtime_artifacts_path.packageName, "packageName")); + OR_RETURN(ValidatePathElement(runtime_artifacts_path.isa, "isa")); + OR_RETURN(ValidateDexPath(runtime_artifacts_path.dexPath)); + return {}; +} + Result<void> ValidateDexPath(const std::string& dex_path) { OR_RETURN(ValidateAbsoluteNormalPath(dex_path)); return {}; diff --git a/artd/path_utils.h b/artd/path_utils.h index a126118137..48640d0348 100644 --- a/artd/path_utils.h +++ b/artd/path_utils.h @@ -31,6 +31,12 @@ namespace artd { // Returns all existing files that are managed by artd. android::base::Result<std::vector<std::string>> ListManagedFiles(); +android::base::Result<std::vector<std::string>> ListRuntimeArtifactsFiles( + const aidl::com::android::server::art::RuntimeArtifactsPath& runtime_artifacts_path); + +android::base::Result<void> ValidateRuntimeArtifactsPath( + const aidl::com::android::server::art::RuntimeArtifactsPath& runtime_artifacts_path); + android::base::Result<void> ValidateDexPath(const std::string& dex_path); android::base::Result<std::string> BuildArtBinPath(const std::string& binary_name); diff --git a/libarttools/tools/tools.cc b/libarttools/tools/tools.cc index 4ec9d9a750..3a59321b4e 100644 --- a/libarttools/tools/tools.cc +++ b/libarttools/tools/tools.cc @@ -22,6 +22,7 @@ #include <algorithm> #include <filesystem> #include <functional> +#include <regex> #include <string> #include <string_view> #include <system_error> @@ -138,5 +139,9 @@ std::vector<std::string> Glob(const std::vector<std::string>& patterns, std::str return results; } +std::string EscapeGlob(const std::string& str) { + return std::regex_replace(str, std::regex(R"re(\*|\?|\[)re"), "[$&]"); +} + } // namespace tools } // namespace art diff --git a/libarttools/tools/tools.h b/libarttools/tools/tools.h index c2bcee77a6..142d19b239 100644 --- a/libarttools/tools/tools.h +++ b/libarttools/tools/tools.h @@ -37,6 +37,9 @@ namespace tools { std::vector<std::string> Glob(const std::vector<std::string>& patterns, std::string_view root_dir = "/"); +// Escapes a string so that it's not recognized as a wildcard pattern for `Glob`. +std::string EscapeGlob(const std::string& str); + } // namespace tools } // namespace art diff --git a/libarttools/tools/tools_test.cc b/libarttools/tools/tools_test.cc index 2f61181c73..09ec35fba3 100644 --- a/libarttools/tools/tools_test.cc +++ b/libarttools/tools/tools_test.cc @@ -110,6 +110,37 @@ TEST_F(ArtToolsTest, Glob) { scratch_path_ + "/abc/aaa/bbb/pqr/ccc/ddd/123.txt")); } +TEST_F(ArtToolsTest, EscapeGlob) { + CreateFile(scratch_path_ + "/**"); + CreateFile(scratch_path_ + "/*.txt"); + CreateFile(scratch_path_ + "/?.txt"); + CreateFile(scratch_path_ + "/[a-z].txt"); + CreateFile(scratch_path_ + "/**.txt"); + CreateFile(scratch_path_ + "/??.txt"); + CreateFile(scratch_path_ + "/[a-z[a-z]][a-z].txt"); + + // Paths that shouldn't be matched if the paths above are escaped. + CreateFile(scratch_path_ + "/abc/b.txt"); + CreateFile(scratch_path_ + "/b.txt"); + CreateFile(scratch_path_ + "/*b.txt"); + CreateFile(scratch_path_ + "/?b.txt"); + CreateFile(scratch_path_ + "/[a-zb]b.txt"); + + // Verifies that the escaped path only matches the given path. + auto verify_escape = [this](const std::string& file) { + EXPECT_THAT(Glob({EscapeGlob(file)}, scratch_path_), UnorderedElementsAre(file)); + }; + + verify_escape(scratch_path_ + "/**"); + verify_escape(scratch_path_ + "/*.txt"); + verify_escape(scratch_path_ + "/?.txt"); + verify_escape(scratch_path_ + "/[a-z].txt"); + verify_escape(scratch_path_ + "/**.txt"); + verify_escape(scratch_path_ + "/**"); + verify_escape(scratch_path_ + "/??.txt"); + verify_escape(scratch_path_ + "/[a-z[a-z]][a-z].txt"); +} + } // namespace } // namespace tools } // namespace art diff --git a/runtime/runtime_image.cc b/runtime/runtime_image.cc index 34a9c08307..23b7354744 100644 --- a/runtime/runtime_image.cc +++ b/runtime/runtime_image.cc @@ -17,13 +17,12 @@ #include "runtime_image.h" #include <lz4.h> -#include <sstream> #include <unistd.h> #include "android-base/file.h" #include "android-base/stringprintf.h" #include "android-base/strings.h" - +#include "arch/instruction_set.h" #include "base/arena_allocator.h" #include "base/arena_containers.h" #include "base/bit_utils.h" @@ -1813,21 +1812,28 @@ class RuntimeImageHelper { friend class NativePointerVisitor; }; -static std::string GetOatPath() { - const std::string& data_dir = Runtime::Current()->GetProcessDataDirectory(); - if (data_dir.empty()) { - // The data ditectory is empty for tests. +static std::string GetRuntimeImageDir(const std::string& app_data_dir) { + if (app_data_dir.empty()) { + // The data directory is empty for tests. return ""; } - return data_dir + "/cache/oat_primary/"; + return app_data_dir + "/cache/oat_primary/"; } // Note: this may return a relative path for tests. -std::string RuntimeImage::GetRuntimeImagePath(const std::string& dex_location) { +std::string RuntimeImage::GetRuntimeImagePath(const std::string& app_data_dir, + const std::string& dex_location, + const std::string& isa) { std::string basename = android::base::Basename(dex_location); std::string filename = ReplaceFileExtension(basename, "art"); - return GetOatPath() + GetInstructionSetString(kRuntimeISA) + "/" + filename; + return GetRuntimeImageDir(app_data_dir) + isa + "/" + filename; +} + +std::string RuntimeImage::GetRuntimeImagePath(const std::string& dex_location) { + return GetRuntimeImagePath(Runtime::Current()->GetProcessDataDirectory(), + dex_location, + GetInstructionSetString(kRuntimeISA)); } static bool EnsureDirectoryExists(const std::string& directory, std::string* error_msg) { @@ -1848,7 +1854,7 @@ bool RuntimeImage::WriteImageToDisk(std::string* error_msg) { *error_msg = "Cannot generate an app image without a boot image"; return false; } - std::string oat_path = GetOatPath(); + std::string oat_path = GetRuntimeImageDir(Runtime::Current()->GetProcessDataDirectory()); if (!oat_path.empty() && !EnsureDirectoryExists(oat_path, error_msg)) { return false; } diff --git a/runtime/runtime_image.h b/runtime/runtime_image.h index d494e1cc74..ed891b4995 100644 --- a/runtime/runtime_image.h +++ b/runtime/runtime_image.h @@ -27,6 +27,14 @@ class RuntimeImage { static bool WriteImageToDisk(std::string* error_msg); // Gets the path where a runtime-generated app image is stored. + // + // If any of the arguments is a valid glob (a pattern that contains '**' or those documented in + // glob(7)), returns a valid glob. + static std::string GetRuntimeImagePath(const std::string& app_data_dir, + const std::string& dex_location, + const std::string& isa); + + // Same as above, but takes data dir and ISA from the runtime. static std::string GetRuntimeImagePath(const std::string& dex_location); }; |