diff options
author | 2025-03-21 03:40:36 -0700 | |
---|---|---|
committer | 2025-03-21 03:40:36 -0700 | |
commit | a7652079683fc97ef40b793312c39a613523262c (patch) | |
tree | 55ac1df9ea1d449630016889fbf480dfb528777c | |
parent | 09ac8285d7ad56b01982c02fdcc81741a3697ade (diff) | |
parent | aa405ccd29b7317dfe499fa63cc9fdaef2f049fb (diff) |
Add an artd method to create an SDC file. am: aa405ccd29
Original change: https://android-review.googlesource.com/c/platform/art/+/3540979
Change-Id: I37d556aa308550170f8cf8ec8a0e8ccd2b99944e
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r-- | artd/artd.cc | 87 | ||||
-rw-r--r-- | artd/artd.h | 4 | ||||
-rw-r--r-- | artd/artd_test.cc | 150 | ||||
-rw-r--r-- | artd/binder/com/android/server/art/IArtd.aidl | 9 | ||||
-rw-r--r-- | artd/binder/com/android/server/art/OutputSecureDexMetadataCompanion.aidl | 30 | ||||
-rw-r--r-- | artd/binder/com/android/server/art/SecureDexMetadataWithCompanionPaths.aidl | 39 | ||||
-rw-r--r-- | artd/path_utils.cc | 58 | ||||
-rw-r--r-- | artd/path_utils.h | 10 | ||||
-rw-r--r-- | artd/path_utils_test.cc | 15 | ||||
-rw-r--r-- | libartbase/base/time_utils.h | 9 | ||||
-rw-r--r-- | runtime/oat/oat_file_assistant_context.h | 2 | ||||
-rw-r--r-- | runtime/oat/sdc_file.cc | 15 | ||||
-rw-r--r-- | runtime/oat/sdc_file.h | 15 | ||||
-rw-r--r-- | runtime/oat/sdc_file_test.cc | 26 |
14 files changed, 378 insertions, 91 deletions
diff --git a/artd/artd.cc b/artd/artd.cc index d781750306..974712eccf 100644 --- a/artd/artd.cc +++ b/artd/artd.cc @@ -84,6 +84,7 @@ #include "fstab/fstab.h" #include "oat/oat_file_assistant.h" #include "oat/oat_file_assistant_context.h" +#include "oat/sdc_file.h" #include "odrefresh/odrefresh.h" #include "path_utils.h" #include "profman/profman_result.h" @@ -115,6 +116,7 @@ using ::aidl::com::android::server::art::IArtdNotification; using ::aidl::com::android::server::art::MergeProfileOptions; using ::aidl::com::android::server::art::OutputArtifacts; using ::aidl::com::android::server::art::OutputProfile; +using ::aidl::com::android::server::art::OutputSecureDexMetadataCompanion; using ::aidl::com::android::server::art::PriorityClass; using ::aidl::com::android::server::art::ProfilePath; using ::aidl::com::android::server::art::RuntimeArtifactsPath; @@ -269,20 +271,18 @@ Result<void> PrepareArtifactsDir(const std::string& path, const FsPermission& fs return {}; } -Result<void> PrepareArtifactsDirs(const OutputArtifacts& output_artifacts, +Result<void> PrepareArtifactsDirs(const std::string& dex_path, + const std::string& isa_str, + const FsPermission& dir_fs_permission, /*out*/ std::string* oat_dir_path) { - if (output_artifacts.artifactsPath.isInDalvikCache) { - return {}; - } - std::filesystem::path oat_path( - OR_RETURN(BuildArtifactsPath(output_artifacts.artifactsPath)).oat_path); + OR_RETURN(BuildOatPath(dex_path, isa_str, /*is_in_dalvik_cache=*/false))); std::filesystem::path isa_dir = oat_path.parent_path(); std::filesystem::path oat_dir = isa_dir.parent_path(); DCHECK_EQ(oat_dir.filename(), "oat"); - OR_RETURN(PrepareArtifactsDir(oat_dir, output_artifacts.permissionSettings.dirFsPermission)); - OR_RETURN(PrepareArtifactsDir(isa_dir, output_artifacts.permissionSettings.dirFsPermission)); + OR_RETURN(PrepareArtifactsDir(oat_dir, dir_fs_permission)); + OR_RETURN(PrepareArtifactsDir(isa_dir, dir_fs_permission)); *oat_dir_path = oat_dir; return {}; } @@ -992,6 +992,64 @@ ndk::ScopedAStatus Artd::getDexoptNeeded(const std::string& in_dexFile, return ScopedAStatus::ok(); } +ndk::ScopedAStatus Artd::maybeCreateSdc(const OutputSecureDexMetadataCompanion& in_outputSdc) { + RETURN_FATAL_IF_PRE_REBOOT(options_); + + if (in_outputSdc.permissionSettings.seContext.has_value()) { + // SDM files are for primary dex files. + return Fatal("'seContext' must be null"); + } + + std::string sdm_path = OR_RETURN_FATAL(BuildSdmPath(in_outputSdc.sdcPath)); + std::string sdc_path = OR_RETURN_FATAL(BuildSdcPath(in_outputSdc.sdcPath)); + + Result<std::unique_ptr<File>> sdm_file = OpenFileForReading(sdm_path); + if (!sdm_file.ok()) { + if (sdm_file.error().code() == ENOENT) { + // No SDM file found. That's typical. + return ScopedAStatus::ok(); + } + return NonFatal(sdm_file.error().message()); + } + struct stat sdm_st = OR_RETURN_NON_FATAL(Fstat(*sdm_file.value())); + + std::string error_msg; + std::unique_ptr<SdcReader> sdc_reader = SdcReader::Load(sdc_path, &error_msg); + if (sdc_reader != nullptr && sdc_reader->GetSdmTimestampNs() == TimeSpecToNs(sdm_st.st_mtim)) { + // Already has an SDC file for the SDM file. + return ScopedAStatus::ok(); + } + + std::string oat_dir_path; // For restorecon, can be empty if the artifacts are in dalvik-cache. + if (!in_outputSdc.sdcPath.isInDalvikCache) { + OR_RETURN_NON_FATAL(PrepareArtifactsDirs(in_outputSdc.sdcPath.dexPath, + in_outputSdc.sdcPath.isa, + in_outputSdc.permissionSettings.dirFsPermission, + &oat_dir_path)); + + // Unlike the two `restorecon_` calls in `dexopt`, we only need one restorecon here because SDM + // files are for primary dex files, whose oat directory doesn't have an MLS label. + OR_RETURN_NON_FATAL(restorecon_(oat_dir_path, /*se_context=*/std::nullopt, /*recurse=*/true)); + } + + OatFileAssistantContext* ofa_context = OR_RETURN_NON_FATAL(GetOatFileAssistantContext()); + + std::unique_ptr<NewFile> sdc_file = OR_RETURN_NON_FATAL( + NewFile::Create(sdc_path, in_outputSdc.permissionSettings.fileFsPermission)); + SdcWriter writer(File(DupCloexec(sdc_file->Fd()), sdc_file->TempPath(), /*check_usage=*/true)); + + writer.SetSdmTimestampNs(TimeSpecToNs(sdm_st.st_mtim)); + writer.SetApexVersions(ofa_context->GetApexVersions()); + + if (!writer.Save(&error_msg)) { + return NonFatal(error_msg); + } + + OR_RETURN_NON_FATAL(sdc_file->CommitOrAbandon()); + + return ScopedAStatus::ok(); +} + ndk::ScopedAStatus Artd::dexopt( const OutputArtifacts& in_outputArtifacts, const std::string& in_dexFile, @@ -1029,12 +1087,15 @@ ndk::ScopedAStatus Artd::dexopt( } std::string oat_dir_path; // For restorecon, can be empty if the artifacts are in dalvik-cache. - OR_RETURN_NON_FATAL(PrepareArtifactsDirs(in_outputArtifacts, &oat_dir_path)); - - // First-round restorecon. artd doesn't have the permission to create files with the - // `apk_data_file` label, so we need to restorecon the "oat" directory first so that files will - // inherit `dalvikcache_data_file` rather than `apk_data_file`. if (!in_outputArtifacts.artifactsPath.isInDalvikCache) { + OR_RETURN_NON_FATAL(PrepareArtifactsDirs(in_outputArtifacts.artifactsPath.dexPath, + in_outputArtifacts.artifactsPath.isa, + in_outputArtifacts.permissionSettings.dirFsPermission, + &oat_dir_path)); + + // First-round restorecon. artd doesn't have the permission to create files with the + // `apk_data_file` label, so we need to restorecon the "oat" directory first so that files will + // inherit `dalvikcache_data_file` rather than `apk_data_file`. OR_RETURN_NON_FATAL(restorecon_( oat_dir_path, in_outputArtifacts.permissionSettings.seContext, /*recurse=*/true)); } diff --git a/artd/artd.h b/artd/artd.h index 7426901688..ba34ac4ebf 100644 --- a/artd/artd.h +++ b/artd/artd.h @@ -217,6 +217,10 @@ class Artd : public aidl::com::android::server::art::BnArtd { int32_t in_dexoptTrigger, aidl::com::android::server::art::GetDexoptNeededResult* _aidl_return) override; + ndk::ScopedAStatus maybeCreateSdc( + const aidl::com::android::server::art::OutputSecureDexMetadataCompanion& in_outputSdc) + override; + ndk::ScopedAStatus dexopt( const aidl::com::android::server::art::OutputArtifacts& in_outputArtifacts, const std::string& in_dexFile, diff --git a/artd/artd_test.cc b/artd/artd_test.cc index 76167c58fb..10116c7994 100644 --- a/artd/artd_test.cc +++ b/artd/artd_test.cc @@ -43,6 +43,7 @@ #include "aidl/com/android/server/art/ArtConstants.h" #include "aidl/com/android/server/art/BnArtd.h" +#include "aidl/com/android/server/art/OutputArtifacts.h" #include "android-base/collections.h" #include "android-base/errors.h" #include "android-base/file.h" @@ -58,6 +59,7 @@ #include "base/common_art_test.h" #include "base/macros.h" #include "base/pidfd.h" +#include "base/time_utils.h" #include "exec_utils.h" #include "file_utils.h" #include "gmock/gmock.h" @@ -94,6 +96,7 @@ 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::SecureDexMetadataWithCompanionPaths; using ::aidl::com::android::server::art::VdexPath; using ::android::base::Append; using ::android::base::Dirname; @@ -130,10 +133,12 @@ using ::testing::Property; using ::testing::ResultOf; using ::testing::Return; using ::testing::SetArgPointee; +using ::testing::StartsWith; using ::testing::StrEq; using ::testing::UnorderedElementsAreArray; using ::testing::WithArg; +using PermissionSettings = OutputArtifacts::PermissionSettings; using PrimaryCurProfilePath = ProfilePath::PrimaryCurProfilePath; using PrimaryRefProfilePath = ProfilePath::PrimaryRefProfilePath; using TmpProfilePath = ProfilePath::TmpProfilePath; @@ -153,10 +158,10 @@ ScopeGuard<std::function<void()>> ScopedSetLogger(android::base::LogFunction&& l }); } -void CheckContent(const std::string& path, const std::string& expected_content) { +void CheckContent(const std::string& path, const Matcher<std::string>& expected_content_matcher) { std::string actual_content; ASSERT_TRUE(ReadFileToString(path, &actual_content)); - EXPECT_EQ(actual_content, expected_content); + EXPECT_THAT(actual_content, expected_content_matcher); } void CheckOtherReadable(const std::string& path, bool expected_value) { @@ -384,24 +389,24 @@ class ArtdTest : public CommonArtTest { }; struct stat st; ASSERT_EQ(stat(scratch_path_.c_str(), &st), 0); + permission_settings_ = { + .dirFsPermission = + FsPermission{ + .uid = static_cast<int32_t>(st.st_uid), + .gid = static_cast<int32_t>(st.st_gid), + .isOtherReadable = true, + .isOtherExecutable = true, + }, + .fileFsPermission = + FsPermission{ + .uid = static_cast<int32_t>(st.st_uid), + .gid = static_cast<int32_t>(st.st_gid), + .isOtherReadable = true, + }, + }; output_artifacts_ = OutputArtifacts{ .artifactsPath = artifacts_path_, - .permissionSettings = - OutputArtifacts::PermissionSettings{ - .dirFsPermission = - FsPermission{ - .uid = static_cast<int32_t>(st.st_uid), - .gid = static_cast<int32_t>(st.st_gid), - .isOtherReadable = true, - .isOtherExecutable = true, - }, - .fileFsPermission = - FsPermission{ - .uid = static_cast<int32_t>(st.st_uid), - .gid = static_cast<int32_t>(st.st_gid), - .isOtherReadable = true, - }, - }, + .permissionSettings = permission_settings_, }; clc_1_ = GetTestDexFileName("Main"); clc_2_ = GetTestDexFileName("Nested"); @@ -417,6 +422,12 @@ class ArtdTest : public CommonArtTest { dm_path_ = DexMetadataPath{.dexPath = dex_file_}; std::filesystem::create_directories( std::filesystem::path(OR_FATAL(BuildFinalProfilePath(tmp_profile_path_))).parent_path()); + + sdm_sdc_paths_ = { + .dexPath = dex_file_, + .isa = isa_, + .isInDalvikCache = false, + }; } void TearDown() override { @@ -547,6 +558,7 @@ class ArtdTest : public CommonArtTest { std::string dex_file_; std::string isa_; ArtifactsPath artifacts_path_; + PermissionSettings permission_settings_; OutputArtifacts output_artifacts_; std::string clc_1_; std::string clc_2_; @@ -561,6 +573,8 @@ class ArtdTest : public CommonArtTest { bool dex_file_other_readable_ = true; bool profile_other_readable_ = true; + SecureDexMetadataWithCompanionPaths sdm_sdc_paths_; + private: void InitFilesBeforeDexopt() { // Required files. @@ -701,6 +715,79 @@ TEST_F(ArtdTest, deleteArtifactsFileIsDir) { EXPECT_FALSE(std::filesystem::exists(oat_dir + "/b.art")); } +TEST_F(ArtdTest, maybeCreateSdc) { + // Unable to create OatFileAssistantContext on host to get APEX versions. + TEST_DISABLED_FOR_HOST(); + + std::string sdm_file = OR_FAIL(BuildSdmPath(sdm_sdc_paths_)); + std::string sdc_file = OR_FAIL(BuildSdcPath(sdm_sdc_paths_)); + CreateFile(sdm_file); + + ASSERT_STATUS_OK(artd_->maybeCreateSdc( + {.sdcPath = sdm_sdc_paths_, .permissionSettings = permission_settings_})); + + CheckContent(sdc_file, StartsWith("sdm-timestamp-ns=")); +} + +TEST_F(ArtdTest, maybeCreateSdcAlreadyCreated) { + // Unable to create OatFileAssistantContext on host to get APEX versions. + TEST_DISABLED_FOR_HOST(); + + std::string sdm_file = OR_FAIL(BuildSdmPath(sdm_sdc_paths_)); + std::string sdc_file = OR_FAIL(BuildSdcPath(sdm_sdc_paths_)); + CreateFile(sdm_file); + + ASSERT_STATUS_OK(artd_->maybeCreateSdc( + {.sdcPath = sdm_sdc_paths_, .permissionSettings = permission_settings_})); + + struct stat sdc_st; + ASSERT_EQ(stat(sdc_file.c_str(), &sdc_st), 0); + + ASSERT_STATUS_OK(artd_->maybeCreateSdc( + {.sdcPath = sdm_sdc_paths_, .permissionSettings = permission_settings_})); + + struct stat new_sdc_st; + ASSERT_EQ(stat(sdc_file.c_str(), &new_sdc_st), 0); + + EXPECT_EQ(TimeSpecToNs(sdc_st.st_mtim), TimeSpecToNs(new_sdc_st.st_mtim)); +} + +TEST_F(ArtdTest, maybeCreateSdcOutdatedTimestamp) { + // Unable to create OatFileAssistantContext on host to get APEX versions. + TEST_DISABLED_FOR_HOST(); + + std::string sdm_file = OR_FAIL(BuildSdmPath(sdm_sdc_paths_)); + std::string sdc_file = OR_FAIL(BuildSdcPath(sdm_sdc_paths_)); + CreateFile(sdm_file); + + ASSERT_STATUS_OK(artd_->maybeCreateSdc( + {.sdcPath = sdm_sdc_paths_, .permissionSettings = permission_settings_})); + + struct stat sdc_st; + ASSERT_EQ(stat(sdc_file.c_str(), &sdc_st), 0); + + // Simulate that the SDM file is updated. + CreateFile(sdm_file); + + ASSERT_STATUS_OK(artd_->maybeCreateSdc( + {.sdcPath = sdm_sdc_paths_, .permissionSettings = permission_settings_})); + + struct stat new_sdc_st; + ASSERT_EQ(stat(sdc_file.c_str(), &new_sdc_st), 0); + + // The SDC file should be updated. + EXPECT_LT(TimeSpecToNs(sdc_st.st_mtim), TimeSpecToNs(new_sdc_st.st_mtim)); +} + +TEST_F(ArtdTest, maybeCreateSdcNoSdm) { + std::string sdc_file = OR_FAIL(BuildSdcPath(sdm_sdc_paths_)); + + ASSERT_STATUS_OK(artd_->maybeCreateSdc( + {.sdcPath = sdm_sdc_paths_, .permissionSettings = permission_settings_})); + + EXPECT_FALSE(std::filesystem::exists(sdc_file)); +} + TEST_F(ArtdTest, dexopt) { dexopt_options_.generateAppImage = true; @@ -2413,6 +2500,8 @@ TEST_F(ArtdTest, deleteRuntimeArtifactsAndroidDataNotExist) { EXPECT_EQ(aidl_return, 0); } +// Verifies that `deleteRuntimeArtifacts` doesn't treat "*" as a wildcard. It should either treat it +// as a normal character in the path or reject it. The caller is never supposed to use a wildcard. TEST_F(ArtdTest, deleteRuntimeArtifactsSpecialChars) { std::vector<std::string> removed_files; std::vector<std::string> kept_files; @@ -2430,25 +2519,18 @@ TEST_F(ArtdTest, deleteRuntimeArtifactsSpecialChars) { 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()); + ASSERT_STATUS_OK(artd_->deleteRuntimeArtifacts( + {.packageName = "*", .dexPath = "/a/b/base.apk", .isa = "arm64"}, &aidl_return)); + ASSERT_STATUS_OK(artd_->deleteRuntimeArtifacts( + {.packageName = "com.android.foo", .dexPath = "/a/b/*.apk", .isa = "arm64"}, &aidl_return)); + ASSERT_FALSE(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); diff --git a/artd/binder/com/android/server/art/IArtd.aidl b/artd/binder/com/android/server/art/IArtd.aidl index 7e9cf9da58..12932a345b 100644 --- a/artd/binder/com/android/server/art/IArtd.aidl +++ b/artd/binder/com/android/server/art/IArtd.aidl @@ -153,6 +153,15 @@ interface IArtd { int dexoptTrigger); /** + * Creates a secure dex metadata companion (SDC) file for the secure dex metadata (SDM) file, if + * the SDM file exists while the SDC file doesn't exist (meaning the SDM file is seen the first + * time). + * + * Throws fatal and non-fatal errors. + */ + void maybeCreateSdc(in com.android.server.art.OutputSecureDexMetadataCompanion outputSdc); + + /** * Dexopts a dex file for the given instruction set. * * Throws fatal and non-fatal errors. When dexopt fails, the non-fatal status includes an error diff --git a/artd/binder/com/android/server/art/OutputSecureDexMetadataCompanion.aidl b/artd/binder/com/android/server/art/OutputSecureDexMetadataCompanion.aidl new file mode 100644 index 0000000000..df5c84df7f --- /dev/null +++ b/artd/binder/com/android/server/art/OutputSecureDexMetadataCompanion.aidl @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2025 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 output secure dex metadata companion (SDC) file. + * + * @hide + */ +parcelable OutputSecureDexMetadataCompanion { + /** The path to the output. */ + com.android.server.art.SecureDexMetadataWithCompanionPaths sdcPath; + + /** The permissions settings of the output. */ + com.android.server.art.OutputArtifacts.PermissionSettings permissionSettings; +} diff --git a/artd/binder/com/android/server/art/SecureDexMetadataWithCompanionPaths.aidl b/artd/binder/com/android/server/art/SecureDexMetadataWithCompanionPaths.aidl new file mode 100644 index 0000000000..db0571100e --- /dev/null +++ b/artd/binder/com/android/server/art/SecureDexMetadataWithCompanionPaths.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2025 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 paths to a secure dex metadata (SDM) file and its companion (SDC) file. + * + * @hide + */ +parcelable SecureDexMetadataWithCompanionPaths { + /** + * The absolute path starting with '/' to the dex file that the SDM file is next to. + */ + @utf8InCpp String dexPath; + /** The instruction set of the dexopt artifacts. */ + @utf8InCpp String isa; + /** + * Whether the SDC file in the dalvik-cache folder. This is true typically when the app is in + * incremental-fs. + * + * Only applicable to the SDC file, because the SDM file is installed with the app and therefore + * always to the dex file regardlessly. + */ + boolean isInDalvikCache; +} diff --git a/artd/path_utils.cc b/artd/path_utils.cc index 52bae7097e..bb752d00c8 100644 --- a/artd/path_utils.cc +++ b/artd/path_utils.cc @@ -45,6 +45,7 @@ using ::aidl::com::android::server::art::OutputArtifacts; using ::aidl::com::android::server::art::OutputProfile; using ::aidl::com::android::server::art::ProfilePath; using ::aidl::com::android::server::art::RuntimeArtifactsPath; +using ::aidl::com::android::server::art::SecureDexMetadataWithCompanionPaths; using ::aidl::com::android::server::art::VdexPath; using ::android::base::Error; using ::android::base::Result; @@ -148,9 +149,17 @@ std::vector<std::string> ListRuntimeArtifactsFiles( return tools::Glob(patterns, gListRootDir); } +static Result<InstructionSet> ValidateAndGetIsa(const std::string& isa_str) { + InstructionSet isa = GetInstructionSetFromString(isa_str.c_str()); + if (isa == InstructionSet::kNone) { + return Errorf("Instruction set '{}' is invalid", isa_str); + } + return isa; +} + 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(ValidateAndGetIsa(runtime_artifacts_path.isa)); OR_RETURN(ValidateDexPath(runtime_artifacts_path.dexPath)); return {}; } @@ -159,32 +168,36 @@ Result<std::string> BuildArtBinPath(const std::string& binary_name) { return ART_FORMAT("{}/bin/{}", OR_RETURN(GetArtRootOrError()), binary_name); } -Result<RawArtifactsPath> BuildArtifactsPath(const ArtifactsPath& artifacts_path) { - OR_RETURN(ValidateDexPath(artifacts_path.dexPath)); - - InstructionSet isa = GetInstructionSetFromString(artifacts_path.isa.c_str()); - if (isa == InstructionSet::kNone) { - return Errorf("Instruction set '{}' is invalid", artifacts_path.isa); - } +Result<std::string> BuildOatPath(const std::string& dex_path, + const std::string& isa_str, + bool is_in_dalvik_cache) { + OR_RETURN(ValidateDexPath(dex_path)); + InstructionSet isa = OR_RETURN(ValidateAndGetIsa(isa_str)); + std::string oat_path; std::string error_msg; - RawArtifactsPath path; - if (artifacts_path.isInDalvikCache) { + if (is_in_dalvik_cache) { // Apps' OAT files are never in ART APEX data. - if (!OatFileAssistant::DexLocationToOatFilename(artifacts_path.dexPath, + if (!OatFileAssistant::DexLocationToOatFilename(dex_path, isa, /*deny_art_apex_data_files=*/true, - &path.oat_path, + &oat_path, &error_msg)) { - return Error() << error_msg; + return Errorf("{}", error_msg); } } else { - if (!OatFileAssistant::DexLocationToOdexFilename( - artifacts_path.dexPath, isa, &path.oat_path, &error_msg)) { - return Error() << error_msg; + if (!OatFileAssistant::DexLocationToOdexFilename(dex_path, isa, &oat_path, &error_msg)) { + return Errorf("{}", error_msg); } } + return oat_path; +} + +Result<RawArtifactsPath> BuildArtifactsPath(const ArtifactsPath& artifacts_path) { + RawArtifactsPath path; + path.oat_path = OR_RETURN( + BuildOatPath(artifacts_path.dexPath, artifacts_path.isa, artifacts_path.isInDalvikCache)); path.vdex_path = ReplaceFileExtension(path.oat_path, kVdexExtension); path.art_path = ReplaceFileExtension(path.oat_path, kArtExtension); @@ -303,6 +316,19 @@ Result<std::string> BuildVdexPath(const VdexPath& vdex_path) { return OR_RETURN(BuildArtifactsPath(vdex_path.get<VdexPath::artifactsPath>())).vdex_path; } +Result<std::string> BuildSdmPath(const SecureDexMetadataWithCompanionPaths& sdm_path) { + // `sdm_path.isInDalvikCache` is intentionally ignored because it's only applicable to SDC files. + OR_RETURN(ValidateDexPath(sdm_path.dexPath)); + OR_RETURN(ValidateAndGetIsa(sdm_path.isa)); + return ReplaceFileExtension(sdm_path.dexPath, ART_FORMAT(".{}{}", sdm_path.isa, kSdmExtension)); +} + +Result<std::string> BuildSdcPath(const SecureDexMetadataWithCompanionPaths& sdc_path) { + std::string oat_path = + OR_RETURN(BuildOatPath(sdc_path.dexPath, sdc_path.isa, sdc_path.isInDalvikCache)); + return ReplaceFileExtension(oat_path, ".sdc"); +} + bool PreRebootFlag(const ProfilePath& profile_path) { switch (profile_path.getTag()) { case ProfilePath::primaryRefProfilePath: diff --git a/artd/path_utils.h b/artd/path_utils.h index 1528d0610b..e31115683b 100644 --- a/artd/path_utils.h +++ b/artd/path_utils.h @@ -55,6 +55,10 @@ android::base::Result<void> ValidateRuntimeArtifactsPath( android::base::Result<std::string> BuildArtBinPath(const std::string& binary_name); +android::base::Result<std::string> BuildOatPath(const std::string& dex_path, + const std::string& isa_str, + bool is_in_dalvik_cache); + // Returns the absolute paths to files built from the `ArtifactsPath`. android::base::Result<RawArtifactsPath> BuildArtifactsPath( const aidl::com::android::server::art::ArtifactsPath& artifacts_path); @@ -96,6 +100,12 @@ android::base::Result<std::string> BuildProfileOrDmPath( android::base::Result<std::string> BuildVdexPath( const aidl::com::android::server::art::VdexPath& vdex_path); +android::base::Result<std::string> BuildSdmPath( + const aidl::com::android::server::art::SecureDexMetadataWithCompanionPaths& sdm_path); + +android::base::Result<std::string> BuildSdcPath( + const aidl::com::android::server::art::SecureDexMetadataWithCompanionPaths& sdc_path); + // Takes an argument of type `WritableProfilePath`. Returns the pre-reboot flag by value if the // argument is const, or by reference otherwise. template <typename T, diff --git a/artd/path_utils_test.cc b/artd/path_utils_test.cc index 116177a7a7..8b50ca3549 100644 --- a/artd/path_utils_test.cc +++ b/artd/path_utils_test.cc @@ -255,6 +255,21 @@ TEST_F(PathUtilsTest, BuildVdexPath) { HasValue("/a/oat/arm64/b.vdex")); } +TEST_F(PathUtilsTest, BuildSdmPath) { + EXPECT_THAT(BuildSdmPath({.dexPath = "/a/b.apk", .isa = "arm64", .isInDalvikCache = false}), + HasValue("/a/b.arm64.sdm")); +} + +TEST_F(PathUtilsTest, BuildSdcPath) { + EXPECT_THAT(BuildSdcPath({.dexPath = "/a/b.apk", .isa = "arm64", .isInDalvikCache = false}), + HasValue("/a/oat/arm64/b.sdc")); +} + +TEST_F(PathUtilsTest, BuildSdcPathDalvikCache) { + EXPECT_THAT(BuildSdcPath({.dexPath = "/a/b.apk", .isa = "arm64", .isInDalvikCache = true}), + HasValue(android_data_ + "/dalvik-cache/arm64/a@b.apk@classes.sdc")); +} + } // namespace } // namespace artd } // namespace art diff --git a/libartbase/base/time_utils.h b/libartbase/base/time_utils.h index dd73b1c951..ddabb1289f 100644 --- a/libartbase/base/time_utils.h +++ b/libartbase/base/time_utils.h @@ -26,6 +26,8 @@ #include <cstdint> #include <string> +#include "android-base/logging.h" + namespace art { enum TimeUnit { @@ -123,6 +125,13 @@ void NanoSleep(uint64_t ns); // time corresponding to the indicated clock value plus the supplied offset. void InitTimeSpec(bool absolute, int clock, int64_t ms, int32_t ns, timespec* ts); +// Converts `timespec` to nanoseconds. The return value can be negative, which should be interpreted +// as a time before the epoch. +static constexpr int64_t TimeSpecToNs(timespec ts) { + DCHECK_GE(ts.tv_nsec, 0); // According to POSIX. + return static_cast<int64_t>(ts.tv_sec) * INT64_C(1000000000) + ts.tv_nsec; +} + } // namespace art #endif // ART_LIBARTBASE_BASE_TIME_UTILS_H_ diff --git a/runtime/oat/oat_file_assistant_context.h b/runtime/oat/oat_file_assistant_context.h index 82b79edef0..28d5b49dc2 100644 --- a/runtime/oat/oat_file_assistant_context.h +++ b/runtime/oat/oat_file_assistant_context.h @@ -76,7 +76,7 @@ class OatFileAssistantContext { const std::vector<std::string>* GetBcpChecksums(size_t bcp_index, std::string* error_msg); // Returns a string that represents the apex versions of boot classpath jars. See // `Runtime::apex_versions_` for the encoding format. - const std::string& GetApexVersions(); + EXPORT const std::string& GetApexVersions(); private: std::unique_ptr<RuntimeOptions> runtime_options_; diff --git a/runtime/oat/sdc_file.cc b/runtime/oat/sdc_file.cc index 35a759e576..59b9655ac2 100644 --- a/runtime/oat/sdc_file.cc +++ b/runtime/oat/sdc_file.cc @@ -16,6 +16,7 @@ #include "sdc_file.h" +#include <cstdint> #include <memory> #include <regex> #include <string> @@ -60,12 +61,12 @@ std::unique_ptr<SdcReader> SdcReader::Load(const std::string filename, std::stri } decltype(map)::iterator it; - if ((it = map.find("sdm-timestamp")) == map.end()) { - *error_msg = ART_FORMAT("Missing key 'sdm-timestamp' in sdc file '{}'", filename); + if ((it = map.find("sdm-timestamp-ns")) == map.end()) { + *error_msg = ART_FORMAT("Missing key 'sdm-timestamp-ns' in sdc file '{}'", filename); return nullptr; } - if (!ParseInt(std::string(it->second), &reader->sdm_timestamp_, /*min=*/1l)) { - *error_msg = ART_FORMAT("Invalid 'sdm-timestamp' {}", it->second); + if (!ParseInt(std::string(it->second), &reader->sdm_timestamp_ns_, /*min=*/INT64_C(1))) { + *error_msg = ART_FORMAT("Invalid 'sdm-timestamp-ns' {}", it->second); return nullptr; } @@ -89,13 +90,13 @@ std::unique_ptr<SdcReader> SdcReader::Load(const std::string filename, std::stri bool SdcWriter::Save(std::string* error_msg) { auto cleanup = android::base::make_scope_guard([this] { (void)file_.FlushClose(); }); - if (sdm_timestamp_ <= 0) { - *error_msg = ART_FORMAT("Invalid 'sdm-timestamp' {}", sdm_timestamp_); + if (sdm_timestamp_ns_ <= 0) { + *error_msg = ART_FORMAT("Invalid 'sdm-timestamp-ns' {}", sdm_timestamp_ns_); return false; } DCHECK_EQ(file_.GetLength(), 0); std::string content = - ART_FORMAT("sdm-timestamp={}\napex-versions={}\n", sdm_timestamp_, apex_versions_); + ART_FORMAT("sdm-timestamp-ns={}\napex-versions={}\n", sdm_timestamp_ns_, apex_versions_); if (!WriteStringToFd(content, file_.Fd())) { *error_msg = ART_FORMAT("Failed to write sdc file '{}': {}", file_.GetPath(), strerror(errno)); return false; diff --git a/runtime/oat/sdc_file.h b/runtime/oat/sdc_file.h index 18c34a3527..73917f5316 100644 --- a/runtime/oat/sdc_file.h +++ b/runtime/oat/sdc_file.h @@ -17,7 +17,6 @@ #ifndef ART_RUNTIME_OAT_SDC_FILE_H_ #define ART_RUNTIME_OAT_SDC_FILE_H_ -#include <ctime> #include <memory> #include <string> #include <string_view> @@ -44,13 +43,13 @@ namespace art HIDDEN { // key2=value2\n // ... // Repeated keys are not allowed. This is an extensible format, so versioning is not needed. -class SdcReader { +class EXPORT SdcReader { public: static std::unique_ptr<SdcReader> Load(const std::string filename, std::string* error_msg); - // The mtime of the SDM file on device. + // The mtime of the SDM file on device, in nanoseconds. // This is for detecting obsolete SDC files. - time_t GetSdmTimestamp() const { return sdm_timestamp_; } + int64_t GetSdmTimestampNs() const { return sdm_timestamp_ns_; } // The value of `Runtime::GetApexVersions` at the time where the SDM file was first seen on // device. This is for detecting samegrade placebos. @@ -60,7 +59,7 @@ class SdcReader { SdcReader() = default; std::string content_; - time_t sdm_timestamp_; + int64_t sdm_timestamp_ns_; std::string_view apex_versions_; }; @@ -70,8 +69,8 @@ class EXPORT SdcWriter { // Takes ownership of the file. explicit SdcWriter(File&& file) : file_(std::move(file)) {} - // See `SdcReader::GetSdmTimestamp`. - void SetSdmTimestamp(time_t value) { sdm_timestamp_ = value; } + // See `SdcReader::GetSdmTimestampNs`. + void SetSdmTimestampNs(int64_t value) { sdm_timestamp_ns_ = value; } // See `SdcReader::GetApexVersions`. void SetApexVersions(std::string_view value) { apex_versions_ = value; } @@ -80,7 +79,7 @@ class EXPORT SdcWriter { private: File file_; - time_t sdm_timestamp_ = 0; + int64_t sdm_timestamp_ns_ = 0; std::string apex_versions_; }; diff --git a/runtime/oat/sdc_file_test.cc b/runtime/oat/sdc_file_test.cc index f3bf1bc36d..eb1d3d6cb2 100644 --- a/runtime/oat/sdc_file_test.cc +++ b/runtime/oat/sdc_file_test.cc @@ -16,6 +16,7 @@ #include "sdc_file.h" +#include <cstdint> #include <memory> #include <string> @@ -54,15 +55,15 @@ class SdcFileTestBase : public CommonArtTest { class SdcReaderTest : public SdcFileTestBase {}; TEST_F(SdcReaderTest, Success) { - ASSERT_TRUE( - WriteStringToFile("sdm-timestamp=987654321\napex-versions=/12345678/12345679\n", test_file_)); + ASSERT_TRUE(WriteStringToFile( + "sdm-timestamp-ns=987654321000000003\napex-versions=/12345678/12345679\n", test_file_)); std::string error_msg; std::unique_ptr<SdcReader> reader = SdcReader::Load(test_file_, &error_msg); ASSERT_NE(reader, nullptr) << error_msg; EXPECT_EQ(reader->GetApexVersions(), "/12345678/12345679"); - EXPECT_EQ(reader->GetSdmTimestamp(), 987654321l); + EXPECT_EQ(reader->GetSdmTimestampNs(), INT64_C(987654321000000003)); } TEST_F(SdcReaderTest, NotFound) { @@ -74,7 +75,7 @@ TEST_F(SdcReaderTest, NotFound) { } TEST_F(SdcReaderTest, MissingApexVersions) { - ASSERT_TRUE(WriteStringToFile("sdm-timestamp=987654321\n", test_file_)); + ASSERT_TRUE(WriteStringToFile("sdm-timestamp-ns=987654321\n", test_file_)); std::string error_msg; std::unique_ptr<SdcReader> reader = SdcReader::Load(test_file_, &error_msg); @@ -84,17 +85,18 @@ TEST_F(SdcReaderTest, MissingApexVersions) { } TEST_F(SdcReaderTest, InvalidSdmTimestamp) { - ASSERT_TRUE(WriteStringToFile("sdm-timestamp=0\napex-versions=/12345678/12345679\n", test_file_)); + ASSERT_TRUE( + WriteStringToFile("sdm-timestamp-ns=0\napex-versions=/12345678/12345679\n", test_file_)); std::string error_msg; std::unique_ptr<SdcReader> reader = SdcReader::Load(test_file_, &error_msg); ASSERT_EQ(reader, nullptr); - EXPECT_THAT(error_msg, HasSubstr("Invalid 'sdm-timestamp'")); + EXPECT_THAT(error_msg, HasSubstr("Invalid 'sdm-timestamp-ns'")); } TEST_F(SdcReaderTest, InvalidApexVersions) { - ASSERT_TRUE(WriteStringToFile("sdm-timestamp=987654321\napex-versions=abc\n", test_file_)); + ASSERT_TRUE(WriteStringToFile("sdm-timestamp-ns=987654321\napex-versions=abc\n", test_file_)); std::string error_msg; std::unique_ptr<SdcReader> reader = SdcReader::Load(test_file_, &error_msg); @@ -105,7 +107,7 @@ TEST_F(SdcReaderTest, InvalidApexVersions) { TEST_F(SdcReaderTest, UnrecognizedKey) { ASSERT_TRUE(WriteStringToFile( - "sdm-timestamp=987654321\napex-versions=/12345678/12345679\nwrong-key=12345678\n", + "sdm-timestamp-ns=987654321\napex-versions=/12345678/12345679\nwrong-key=12345678\n", test_file_)); std::string error_msg; @@ -123,7 +125,7 @@ TEST_F(SdcWriterTest, Success) { SdcWriter writer(std::move(*file)); writer.SetApexVersions("/12345678/12345679"); - writer.SetSdmTimestamp(987654321l); + writer.SetSdmTimestampNs(987654321l); std::string error_msg; ASSERT_TRUE(writer.Save(&error_msg)) << error_msg; @@ -131,7 +133,7 @@ TEST_F(SdcWriterTest, Success) { std::string content; ASSERT_TRUE(ReadFileToString(test_file_, &content)); - EXPECT_EQ(content, "sdm-timestamp=987654321\napex-versions=/12345678/12345679\n"); + EXPECT_EQ(content, "sdm-timestamp-ns=987654321\napex-versions=/12345678/12345679\n"); } TEST_F(SdcWriterTest, SaveFailed) { @@ -143,7 +145,7 @@ TEST_F(SdcWriterTest, SaveFailed) { File(file->Release(), file->GetPath(), /*check_usage=*/false, /*read_only_mode=*/false)); writer.SetApexVersions("/12345678/12345679"); - writer.SetSdmTimestamp(987654321l); + writer.SetSdmTimestampNs(987654321l); std::string error_msg; EXPECT_FALSE(writer.Save(&error_msg)); @@ -161,7 +163,7 @@ TEST_F(SdcWriterTest, InvalidSdmTimestamp) { std::string error_msg; EXPECT_FALSE(writer.Save(&error_msg)); - EXPECT_THAT(error_msg, StartsWith("Invalid 'sdm-timestamp'")); + EXPECT_THAT(error_msg, StartsWith("Invalid 'sdm-timestamp-ns'")); } } // namespace art |