From cfcd6aa28593a227d39868f6f567264fca1360a1 Mon Sep 17 00:00:00 2001 From: Calin Juravle Date: Thu, 18 Jan 2018 20:23:17 -0800 Subject: Prepare installd to handle profiles per code path Transform each profile operation to accept the pair (package_name, profile_name). This will allow installd to get the profile name from the PackageManager instead of assuming a predefined name. The PackageManager will compute and assign unique profile names for each code path. The end goal is to support installation and extraction of profiles per code path. Also, add a few extra tests for merging the profiles. Test: installd_dexopt_test, installd_utils_test Bug: 30934496 Change-Id: I580f037d9d3e5537032e815dc803b9ddeff343cb --- cmds/installd/utils.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'cmds/installd/utils.cpp') diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index 61c9c8ff08..f3d94597e8 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -235,7 +236,6 @@ std::string create_data_dalvik_cache_path() { // Keep profile paths in sync with ActivityThread and LoadedApk. const std::string PROFILE_EXT = ".prof"; const std::string CURRENT_PROFILE_EXT = ".cur"; -const std::string PRIMARY_PROFILE_NAME = "primary" + PROFILE_EXT; const std::string SNAPSHOT_PROFILE_EXT = ".snapshot"; // Gets the parent directory and the file name for the given secondary dex path. @@ -256,8 +256,8 @@ static bool get_secondary_dex_location(const std::string& dex_path, return true; } -std::string create_current_profile_path(userid_t user, const std::string& location, - bool is_secondary_dex) { +std::string create_current_profile_path(userid_t user, const std::string& package_name, + const std::string& location, bool is_secondary_dex) { if (is_secondary_dex) { // Secondary dex current profiles are stored next to the dex files under the oat folder. std::string dex_dir; @@ -269,12 +269,14 @@ std::string create_current_profile_path(userid_t user, const std::string& locati PROFILE_EXT.c_str()); } else { // Profiles for primary apks are under /data/misc/profiles/cur. - std::string profile_dir = create_primary_current_profile_package_dir_path(user, location); - return StringPrintf("%s/%s", profile_dir.c_str(), PRIMARY_PROFILE_NAME.c_str()); + std::string profile_dir = create_primary_current_profile_package_dir_path( + user, package_name); + return StringPrintf("%s/%s", profile_dir.c_str(), location.c_str()); } } -std::string create_reference_profile_path(const std::string& location, bool is_secondary_dex) { +std::string create_reference_profile_path(const std::string& package_name, + const std::string& location, bool is_secondary_dex) { if (is_secondary_dex) { // Secondary dex reference profiles are stored next to the dex files under the oat folder. std::string dex_dir; @@ -285,16 +287,15 @@ std::string create_reference_profile_path(const std::string& location, bool is_s dex_dir.c_str(), dex_name.c_str(), PROFILE_EXT.c_str()); } else { // Reference profiles for primary apks are stored in /data/misc/profile/ref. - std::string profile_dir = create_primary_reference_profile_package_dir_path(location); - return StringPrintf("%s/%s", profile_dir.c_str(), PRIMARY_PROFILE_NAME.c_str()); + std::string profile_dir = create_primary_reference_profile_package_dir_path(package_name); + return StringPrintf("%s/%s", profile_dir.c_str(), location.c_str()); } } std::string create_snapshot_profile_path(const std::string& package, - const std::string& code_path ATTRIBUTE_UNUSED) { - // TODD(calin): code_path is ignored for now. It will be used when each split gets its own - // profile file. - std::string ref_profile = create_reference_profile_path(package, /*is_secondary_dex*/ false); + const std::string& profile_name) { + std::string ref_profile = create_reference_profile_path(package, profile_name, + /*is_secondary_dex*/ false); return ref_profile + SNAPSHOT_PROFILE_EXT; } -- cgit v1.2.3-59-g8ed1b From e61189e0ad2104360a188d36210bd1ee43803a64 Mon Sep 17 00:00:00 2001 From: Calin Juravle Date: Tue, 23 Jan 2018 19:54:11 -0800 Subject: [installd] Create profile snaphots for boot image Create the profile snapshot for the boot image by aggregating all primary profiles. During aggregation data that does not belong to the boot image is filtered out. The matching is done based on the dex files provided in the classpath argument. Test: installd_dexopt_test Bug: 30934496 Change-Id: Ib980ab3feb9f9838dff81a3861693cd08b1df9ab --- cmds/installd/InstalldNativeService.cpp | 5 +- cmds/installd/InstalldNativeService.h | 2 +- cmds/installd/binder/android/os/IInstalld.aidl | 2 +- cmds/installd/dexopt.cpp | 142 ++++++++++++++++++++++--- cmds/installd/dexopt.h | 11 +- cmds/installd/tests/installd_dexopt_test.cpp | 116 +++++++++++++++++++- cmds/installd/utils.cpp | 73 +++++++++++++ cmds/installd/utils.h | 6 ++ 8 files changed, 335 insertions(+), 22 deletions(-) (limited to 'cmds/installd/utils.cpp') diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index a6c93836aa..9c9b75b8a9 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -1872,12 +1872,13 @@ binder::Status InstalldNativeService::mergeProfiles(int32_t uid, const std::stri } binder::Status InstalldNativeService::createProfileSnapshot(int32_t appId, - const std::string& packageName, const std::string& profileName, bool* _aidl_return) { + const std::string& packageName, const std::string& profileName, + const std::string& classpath, bool* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard lock(mLock); - *_aidl_return = create_profile_snapshot(appId, packageName, profileName); + *_aidl_return = create_profile_snapshot(appId, packageName, profileName, classpath); return ok(); } diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h index ce5c1a0368..5bf8ae2a54 100644 --- a/cmds/installd/InstalldNativeService.h +++ b/cmds/installd/InstalldNativeService.h @@ -100,7 +100,7 @@ public: binder::Status destroyAppProfiles(const std::string& packageName); binder::Status createProfileSnapshot(int32_t appId, const std::string& packageName, - const std::string& profileName, bool* _aidl_return); + const std::string& profileName, const std::string& classpath, bool* _aidl_return); binder::Status destroyProfileSnapshot(const std::string& packageName, const std::string& profileName); diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl index d457a2ad41..3133b4f15c 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl @@ -65,7 +65,7 @@ interface IInstalld { void destroyAppProfiles(@utf8InCpp String packageName); boolean createProfileSnapshot(int appId, @utf8InCpp String packageName, - @utf8InCpp String profileName); + @utf8InCpp String profileName, @utf8InCpp String classpath); void destroyProfileSnapshot(@utf8InCpp String packageName, @utf8InCpp String profileName); void idmap(@utf8InCpp String targetApkPath, @utf8InCpp String overlayApkPath, int uid); diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp index 0549a46f13..df87ab6db7 100644 --- a/cmds/installd/dexopt.cpp +++ b/cmds/installd/dexopt.cpp @@ -716,27 +716,34 @@ static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_IO = 3; static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING = 4; static void run_profman_merge(const std::vector& profiles_fd, - const unique_fd& reference_profile_fd) { - static const size_t MAX_INT_LEN = 32; + const unique_fd& reference_profile_fd, const std::vector* apk_fds = nullptr) { const char* profman_bin = is_debug_runtime() ? "/system/bin/profmand" : "/system/bin/profman"; std::vector profile_args(profiles_fd.size()); - char profile_buf[strlen("--profile-file-fd=") + MAX_INT_LEN]; for (size_t k = 0; k < profiles_fd.size(); k++) { - sprintf(profile_buf, "--profile-file-fd=%d", profiles_fd[k].get()); - profile_args[k].assign(profile_buf); + profile_args[k] = "--profile-file-fd=" + std::to_string(profiles_fd[k].get()); + } + std::string reference_profile_arg = "--reference-profile-file-fd=" + + std::to_string(reference_profile_fd.get()); + + std::vector apk_args; + if (apk_fds != nullptr) { + for (size_t k = 0; k < apk_fds->size(); k++) { + apk_args.push_back("--apk-fd=" + std::to_string((*apk_fds)[k].get())); + } } - char reference_profile_arg[strlen("--reference-profile-file-fd=") + MAX_INT_LEN]; - sprintf(reference_profile_arg, "--reference-profile-file-fd=%d", reference_profile_fd.get()); // program name, reference profile fd, the final NULL and the profile fds - const char* argv[3 + profiles_fd.size()]; + const char* argv[3 + profile_args.size() + apk_args.size()]; int i = 0; argv[i++] = profman_bin; - argv[i++] = reference_profile_arg; + argv[i++] = reference_profile_arg.c_str(); for (size_t k = 0; k < profile_args.size(); k++) { argv[i++] = profile_args[k].c_str(); } + for (size_t k = 0; k < apk_args.size(); k++) { + argv[i++] = apk_args[k].c_str(); + } // Do not add after dex2oat_flags, they should override others for debugging. argv[i] = NULL; @@ -2441,8 +2448,24 @@ bool create_cache_path_default(char path[PKG_PATH_MAX], const char *src, } } -bool create_profile_snapshot(int32_t app_id, const std::string& package_name, - const std::string& profile_name) { +bool open_classpath_files(const std::string& classpath, std::vector* apk_fds) { + std::vector classpaths_elems = base::Split(classpath, ":"); + for (const std::string& elem : classpaths_elems) { + unique_fd fd(TEMP_FAILURE_RETRY(open(elem.c_str(), O_RDONLY))); + if (fd < 0) { + PLOG(ERROR) << "Could not open classpath elem " << elem; + return false; + } else { + apk_fds->push_back(std::move(fd)); + } + } + return true; +} + +static bool create_app_profile_snapshot(int32_t app_id, + const std::string& package_name, + const std::string& profile_name, + const std::string& classpath) { int app_shared_gid = multiuser_get_shared_gid(/*user_id*/ 0, app_id); unique_fd snapshot_fd = open_spnashot_profile(AID_SYSTEM, package_name, profile_name); @@ -2460,11 +2483,18 @@ bool create_profile_snapshot(int32_t app_id, const std::string& package_name, profiles_fd.push_back(std::move(reference_profile_fd)); + // Open the class paths elements. These will be used to filter out profile data that does + // not belong to the classpath during merge. + std::vector apk_fds; + if (!open_classpath_files(classpath, &apk_fds)) { + return false; + } + pid_t pid = fork(); if (pid == 0) { /* child -- drop privileges before continuing */ drop_capabilities(app_shared_gid); - run_profman_merge(profiles_fd, snapshot_fd); + run_profman_merge(profiles_fd, snapshot_fd, &apk_fds); exit(42); /* only get here on exec failure */ } @@ -2478,6 +2508,94 @@ bool create_profile_snapshot(int32_t app_id, const std::string& package_name, return true; } +static bool create_boot_image_profile_snapshot(const std::string& package_name, + const std::string& profile_name, + const std::string& classpath) { + // The reference profile directory for the android package might not be prepared. Do it now. + const std::string ref_profile_dir = + create_primary_reference_profile_package_dir_path(package_name); + if (fs_prepare_dir(ref_profile_dir.c_str(), 0770, AID_SYSTEM, AID_SYSTEM) != 0) { + PLOG(ERROR) << "Failed to prepare " << ref_profile_dir; + return false; + } + + // Open and create the snapshot profile. + unique_fd snapshot_fd = open_spnashot_profile(AID_SYSTEM, package_name, profile_name); + + // Collect all non empty profiles. + // The collection will traverse all applications profiles and find the non empty files. + // This has the potential of inspecting a large number of files and directories (depending + // on the number of applications and users). So there is a slight increase in the chance + // to get get occasionally I/O errors (e.g. for opening the file). When that happens do not + // fail the snapshot and aggregate whatever profile we could open. + // + // The profile snapshot is a best effort based on available data it's ok if some data + // from some apps is missing. It will be counter productive for the snapshot to fail + // because we could not open or read some of the files. + std::vector profiles; + if (!collect_profiles(&profiles)) { + LOG(WARNING) << "There were errors while collecting the profiles for the boot image."; + } + + // If we have no profiles return early. + if (profiles.empty()) { + return true; + } + + // Open the classpath elements. These will be used to filter out profile data that does + // not belong to the classpath during merge. + std::vector apk_fds; + if (!open_classpath_files(classpath, &apk_fds)) { + return false; + } + + // If we could not open any files from the classpath return an error. + if (apk_fds.empty()) { + LOG(ERROR) << "Could not open any of the classpath elements."; + return false; + } + + // Aggregate the profiles in batches of kAggregationBatchSize. + // We do this to avoid opening a huge a amount of files. + static constexpr size_t kAggregationBatchSize = 10; + + std::vector profiles_fd; + for (size_t i = 0; i < profiles.size(); ) { + for (size_t k = 0; k < kAggregationBatchSize && i < profiles.size(); k++, i++) { + unique_fd fd = open_profile(AID_SYSTEM, profiles[i], O_RDONLY); + if (fd.get() >= 0) { + profiles_fd.push_back(std::move(fd)); + } + } + pid_t pid = fork(); + if (pid == 0) { + /* child -- drop privileges before continuing */ + drop_capabilities(AID_SYSTEM); + + run_profman_merge(profiles_fd, snapshot_fd, &apk_fds); + exit(42); /* only get here on exec failure */ + } + + /* parent */ + int return_code = wait_child(pid); + if (!WIFEXITED(return_code)) { + PLOG(WARNING) << "profman failed for " << package_name << ":" << profile_name; + return false; + } + return true; + } + return true; +} + +bool create_profile_snapshot(int32_t app_id, const std::string& package_name, + const std::string& profile_name, const std::string& classpath) { + if (app_id == -1) { + return create_boot_image_profile_snapshot(package_name, profile_name, classpath); + } else { + return create_app_profile_snapshot(app_id, package_name, profile_name, classpath); + } +} + bool prepare_app_profile(const std::string& package_name, userid_t user_id, appid_t app_id, diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h index b96351bcbf..93cf545c76 100644 --- a/cmds/installd/dexopt.h +++ b/cmds/installd/dexopt.h @@ -52,6 +52,8 @@ bool analyze_primary_profiles(uid_t uid, const std::string& profile_name); // Create a snapshot of the profile information for the given package profile. +// If appId is -1, the method creates the profile snapshot for the boot image. +// // The profile snapshot is the aggregation of all existing profiles (all current user // profiles & the reference profile) and is meant to capture the all the profile information // without performing a merge into the reference profile which might impact future dex2oat @@ -60,8 +62,13 @@ bool analyze_primary_profiles(uid_t uid, // ownership is assigned to AID_SYSTEM. // The snapshot location is reference_profile_location.snapshot. If a snapshot is already // there, it will be truncated and overwritten. -bool create_profile_snapshot(int32_t app_id, const std::string& package, - const std::string& profile_name); +// +// The classpath acts as filter: only profiling data belonging to elements of the classpath +// will end up in the snapshot. +bool create_profile_snapshot(int32_t app_id, + const std::string& package, + const std::string& profile_name, + const std::string& classpath); bool dump_profiles(int32_t uid, const std::string& pkgname, diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp index 81135422b6..eac61f490f 100644 --- a/cmds/installd/tests/installd_dexopt_test.cpp +++ b/cmds/installd/tests/installd_dexopt_test.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include #include #include #include @@ -153,6 +154,7 @@ protected: InstalldNativeService* service_; std::unique_ptr volume_uuid_; std::string package_name_; + std::string apk_path_; std::string app_apk_dir_; std::string app_private_dir_ce_; std::string app_private_dir_de_; @@ -202,8 +204,8 @@ protected: service_->createOatDir(app_oat_dir, kRuntimeIsa); // Copy the primary apk. - std::string apk_path = app_apk_dir_ + "/base.jar"; - ASSERT_TRUE(WriteBase64ToFile(kDexFile, apk_path, kSystemUid, kSystemGid, 0644)); + apk_path_ = app_apk_dir_ + "/base.jar"; + ASSERT_TRUE(WriteBase64ToFile(kDexFile, apk_path_, kSystemUid, kSystemGid, 0644)); // Create the app user data. ASSERT_TRUE(service_->createAppData( @@ -470,7 +472,7 @@ class ProfileTest : public DexoptTest { bool expected_result) { bool result; binder::Status binder_result = service_->createProfileSnapshot( - appid, package_name, kPrimaryProfile, &result); + appid, package_name, kPrimaryProfile, apk_path_, &result); ASSERT_TRUE(binder_result.isOk()); ASSERT_EQ(expected_result, result); @@ -561,7 +563,7 @@ class ProfileTest : public DexoptTest { ASSERT_EQ(-1, access(code_path_ref_profile.c_str(), R_OK)); } - private: + protected: void TransitionToSystemServer() { ASSERT_TRUE(DropCapabilities(kSystemUid, kSystemGid)); int32_t res = selinux_android_setcontext( @@ -719,5 +721,111 @@ TEST_F(ProfileTest, ProfilePrepareFailProfileChangedUid) { preparePackageProfile(package_name_, "primary.prof", /*expected_result*/ false); } + +class BootProfileTest : public ProfileTest { + public: + virtual void setup() { + ProfileTest::SetUp(); + intial_android_profiles_dir = android_profiles_dir; + } + + virtual void TearDown() { + android_profiles_dir = intial_android_profiles_dir; + ProfileTest::TearDown(); + } + + void UpdateAndroidProfilesDir(const std::string& profile_dir) { + android_profiles_dir = profile_dir; + // We need to create the reference profile directory in the new profile dir. + run_cmd("mkdir -p " + profile_dir + "/ref"); + } + + void createBootImageProfileSnapshot(const std::string& classpath, bool expected_result) { + bool result; + binder::Status binder_result = service_->createProfileSnapshot( + -1, "android", "android.prof", classpath, &result); + ASSERT_TRUE(binder_result.isOk()); + ASSERT_EQ(expected_result, result); + + if (!expected_result) { + // Do not check the files if we expect to fail. + return; + } + + // Check that the snapshot was created with he expected access flags. + const std::string boot_profile = create_snapshot_profile_path("android", "android.prof"); + CheckFileAccess(boot_profile, kSystemUid, kSystemGid, 0600 | S_IFREG); + + pid_t pid = fork(); + if (pid == 0) { + /* child */ + TransitionToSystemServer(); + + // System server should be able to open the snapshot. + unique_fd fd(open(boot_profile.c_str(), O_RDONLY)); + ASSERT_TRUE(fd > -1) << "Failed to open profile as kSystemUid: " << strerror(errno); + _exit(0); + } + /* parent */ + ASSERT_TRUE(WIFEXITED(wait_child(pid))); + } + protected: + std::string intial_android_profiles_dir; +}; + +TEST_F(BootProfileTest, BootProfileSnapshotOk) { + LOG(INFO) << "BootProfileSnapshotOk"; + char* boot_classpath = getenv("BOOTCLASSPATH"); + ASSERT_TRUE(boot_classpath != nullptr); + createBootImageProfileSnapshot(boot_classpath, /*expected_result*/ true); +} + +TEST_F(BootProfileTest, BootProfileSnapshotFailEmptyClasspath) { + LOG(INFO) << "BootProfileSnapshotFailEmptyClasspath"; + + createBootImageProfileSnapshot(/*boot_classpath*/ "", /*expected_result*/ false); +} + +TEST_F(BootProfileTest, BootProfileSnapshotOkNoProfiles) { + LOG(INFO) << "BootProfileSnapshotOkNoProfiles"; + char* boot_classpath = getenv("BOOTCLASSPATH"); + ASSERT_TRUE(boot_classpath != nullptr); + + // The app_apk_dir has no profiles. So we shouldn't be able to merge anything. + // Still, this is not a failure case. + UpdateAndroidProfilesDir(app_apk_dir_); + createBootImageProfileSnapshot(boot_classpath, /*expected_result*/ true); +} + +// Verify that profile collection. +TEST_F(BootProfileTest, CollectProfiles) { + LOG(INFO) << "CollectProfiles"; + + // Create some profile directories mimicking the real profile structure. + run_cmd("mkdir -p " + app_private_dir_de_ + "/profiles/ref"); + run_cmd("mkdir -p " + app_private_dir_de_ + "/profiles/cur/0/"); + run_cmd("mkdir -p " + app_private_dir_de_ + "/profiles/cur/1/"); + // Create an empty profile. + run_cmd("touch " + app_private_dir_de_ + "/profiles/cur/1/primary.prof"); + // Create a random file. + run_cmd("touch " + app_private_dir_de_ + "/profiles/cur/0/non.profile.file"); + + // Create some non-empty profiles. + std::string current_prof = app_private_dir_de_ + "/profiles/cur/0/primary.prof"; + run_cmd("echo 1 > " + current_prof); + std::string ref_prof = app_private_dir_de_ + "/profiles/ref/primary.prof"; + run_cmd("echo 1 > " + ref_prof); + + UpdateAndroidProfilesDir(app_private_dir_de_ + "/profiles"); + + std::vector profiles; + collect_profiles(&profiles); + + // Only two profiles should be in the output. + ASSERT_EQ(2u, profiles.size()); + ASSERT_TRUE(std::find(profiles.begin(), profiles.end(), current_prof) != profiles.end()); + ASSERT_TRUE(std::find(profiles.begin(), profiles.end(), ref_prof) != profiles.end()); +} + } // namespace installd } // namespace android diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index f3d94597e8..a8c32ed2a5 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -41,7 +42,9 @@ #define DEBUG_XATTRS 0 +using android::base::EndsWith; using android::base::StringPrintf; +using android::base::unique_fd; namespace android { namespace installd { @@ -985,5 +988,75 @@ int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t ta return 0; } +// Collect all non empty profiles from the given directory and puts then into profile_paths. +// The profiles are identified based on PROFILE_EXT extension. +// If a subdirectory or profile file cannot be opened the method logs a warning and moves on. +// It returns true if there were no errors at all, and false otherwise. +static bool collect_profiles(DIR* d, + const std::string& current_path, + std::vector* profiles_paths) { + int32_t dir_fd = dirfd(d); + if (dir_fd < 0) { + return false; + } + + bool result = true; + struct dirent* dir_entry; + while ((dir_entry = readdir(d))) { + std::string name = dir_entry->d_name; + std::string local_path = current_path + "/" + name; + + if (dir_entry->d_type == DT_REG) { + // Check if this is a non empty profile file. + if (EndsWith(name, PROFILE_EXT)) { + struct stat st; + if (stat(local_path.c_str(), &st) != 0) { + PLOG(WARNING) << "Cannot stat local path " << local_path; + result = false; + continue; + } else if (st.st_size > 0) { + profiles_paths->push_back(local_path); + } + } + } else if (dir_entry->d_type == DT_DIR) { + // always skip "." and ".." + if (name == "." || name == "..") { + continue; + } + + unique_fd subdir_fd(openat(dir_fd, name.c_str(), + O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC)); + if (subdir_fd < 0) { + PLOG(WARNING) << "Could not open dir path " << local_path; + result = false; + continue; + } + + DIR* subdir = fdopendir(subdir_fd); + if (subdir == NULL) { + PLOG(WARNING) << "Could not open dir path " << local_path; + result = false; + continue; + } + bool new_result = collect_profiles(subdir, local_path, profiles_paths); + result = result && new_result; + if (closedir(subdir) != 0) { + PLOG(WARNING) << "Could not close dir path " << local_path; + } + } + } + + return result; +} + +bool collect_profiles(std::vector* profiles_paths) { + DIR* d = opendir(android_profiles_dir.c_str()); + if (d == NULL) { + return false; + } else { + return collect_profiles(d, android_profiles_dir, profiles_paths); + } +} + } // namespace installd } // namespace android diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h index b18e7ddf2e..b74073cb29 100644 --- a/cmds/installd/utils.h +++ b/cmds/installd/utils.h @@ -136,6 +136,12 @@ int wait_child(pid_t pid); int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode, uid_t uid, gid_t gid); +// Collect all non empty profiles from the global profile directory and +// put then into profile_paths. The profiles are identified based on PROFILE_EXT extension. +// If a subdirectory or profile file cannot be opened the method logs a warning and moves on. +// It returns true if there were no errors at all, and false otherwise. +bool collect_profiles(std::vector* profiles_paths); + } // namespace installd } // namespace android -- cgit v1.2.3-59-g8ed1b From 8fa803a1f1d351cf65b5350e57797ecff20fe45a Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Mon, 9 Apr 2018 18:46:45 -0600 Subject: Check /mnt/expand paths as if /data. The layout of adopted storage devices is identical to the internal userdata partition, so we should re-use the same logic to determine if a given path is legitimate. Bug: 77654971 Test: adb shell /data/nativetest64/installd_utils_test/installd_utils_test Change-Id: I856788a8e9910f618a4f259a20a264e14b46e079 --- cmds/installd/tests/installd_utils_test.cpp | 37 ++++++++++++++++++++++++++++- cmds/installd/utils.cpp | 24 +++++++++++-------- 2 files changed, 50 insertions(+), 11 deletions(-) (limited to 'cmds/installd/utils.cpp') diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp index bbff6fb0d7..bcdd03efad 100644 --- a/cmds/installd/tests/installd_utils_test.cpp +++ b/cmds/installd/tests/installd_utils_test.cpp @@ -33,7 +33,7 @@ #define TEST_APP_PRIVATE_DIR "/data/app-private/" #define TEST_APP_EPHEMERAL_DIR "/data/app-ephemeral/" #define TEST_ASEC_DIR "/mnt/asec/" -#define TEST_EXPAND_DIR "/mnt/expand/" +#define TEST_EXPAND_DIR "/mnt/expand/00000000-0000-0000-0000-000000000000/" #define TEST_SYSTEM_DIR1 "/system/app/" #define TEST_SYSTEM_DIR2 "/vendor/app/" @@ -116,6 +116,41 @@ TEST_F(UtilsTest, IsValidApkPath_Internal) { << bad_path5 << " should be rejected as a invalid path"; } +TEST_F(UtilsTest, IsValidApkPath_TopDir) { + EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/com.example")); + EXPECT_EQ(0, validate_apk_path(TEST_EXPAND_DIR "app/com.example")); + EXPECT_EQ(-1, validate_apk_path(TEST_DATA_DIR "data/com.example")); + EXPECT_EQ(-1, validate_apk_path(TEST_EXPAND_DIR "data/com.example")); +} + +TEST_F(UtilsTest, IsValidApkPath_TopFile) { + EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/com.example/base.apk")); + EXPECT_EQ(0, validate_apk_path(TEST_EXPAND_DIR "app/com.example/base.apk")); + EXPECT_EQ(-1, validate_apk_path(TEST_DATA_DIR "data/com.example/base.apk")); + EXPECT_EQ(-1, validate_apk_path(TEST_EXPAND_DIR "data/com.example/base.apk")); +} + +TEST_F(UtilsTest, IsValidApkPath_OatDir) { + EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat")); + EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat")); + EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat")); + EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat")); +} + +TEST_F(UtilsTest, IsValidApkPath_OatDirDir) { + EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat/arm64")); + EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat/arm64")); + EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat/arm64")); + EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat/arm64")); +} + +TEST_F(UtilsTest, IsValidApkPath_OatDirDirFile) { + EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat/arm64/base.odex")); + EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat/arm64/base.odex")); + EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat/arm64/base.odex")); + EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat/arm64/base.odex")); +} + TEST_F(UtilsTest, IsValidApkPath_Private) { // Internal directories const char *private1 = TEST_APP_PRIVATE_DIR "example.apk"; diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index a8c32ed2a5..1ff45e4845 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -855,21 +855,25 @@ bool validate_secondary_dex_path(const std::string& pkgname, const std::string& * that path. Returns -1 when an invalid path is encountered and 0 when a valid path * is encountered. */ -static int validate_apk_path_internal(const char *path, int maxSubdirs) { - std::string path_ = path; - if (validate_path(android_app_dir, path_, maxSubdirs) == 0) { - return 0; - } else if (validate_path(android_app_private_dir, path_, maxSubdirs) == 0) { +static int validate_apk_path_internal(const std::string& path, int maxSubdirs) { + if (validate_path(android_app_dir, path, maxSubdirs) == 0) { return 0; - } else if (validate_path(android_app_ephemeral_dir, path_, maxSubdirs) == 0) { + } else if (validate_path(android_app_private_dir, path, maxSubdirs) == 0) { return 0; - } else if (validate_path(android_asec_dir, path_, maxSubdirs) == 0) { + } else if (validate_path(android_app_ephemeral_dir, path, maxSubdirs) == 0) { return 0; - } else if (validate_path(android_mnt_expand_dir, path_, std::max(maxSubdirs, 2)) == 0) { + } else if (validate_path(android_asec_dir, path, maxSubdirs) == 0) { return 0; - } else { - return -1; + } else if (android::base::StartsWith(path, android_mnt_expand_dir)) { + // Rewrite the path as if it were on internal storage, and test that + size_t end = path.find('/', android_mnt_expand_dir.size() + 1); + if (end != std::string::npos) { + auto modified = path; + modified.replace(0, end + 1, android_data_dir); + return validate_apk_path_internal(modified, maxSubdirs); + } } + return -1; } int validate_apk_path(const char* path) { -- cgit v1.2.3-59-g8ed1b