Add snapshotProfile APIs to installd
Add APIs that will snapshot the profile information of packages
in a well known location.
The snapshot ownership is given to AID_SYSTEM. The location is
next to the application reference profile:
/data/misc/profiles/ref/pkg_name/primary.prof.snapshot.
The intended flow for snapshotting profiles is:
mInstaller.snapshotProfile(appId, packageName, codePath);
// open profile snapshot
mInstaller.destroyProfileSnapshot(packageName, codePath);
The reference profile directory is made searchable by others
(in order for the system to be able to open the snapshot profile).
Test: installd_dexopt_test installd_utils_test
Bug: 30934496
Change-Id: Ic4973d5c67243d7724ecd24a238ed0ae8baadcc6
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index af2c527..8917684 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -561,14 +561,12 @@
}
}
-static bool create_profile(int uid, const std::string& profile) {
- unique_fd fd(TEMP_FAILURE_RETRY(open(profile.c_str(), O_CREAT | O_NOFOLLOW, 0600)));
+static unique_fd create_profile(uid_t uid, const std::string& profile, int32_t flags) {
+ unique_fd fd(TEMP_FAILURE_RETRY(open(profile.c_str(), flags, 0600)));
if (fd.get() < 0) {
- if (errno == EEXIST) {
- return true;
- } else {
+ if (errno != EEXIST) {
PLOG(ERROR) << "Failed to create profile " << profile;
- return false;
+ return invalid_unique_fd();
}
}
// Profiles should belong to the app; make sure of that by giving ownership to
@@ -576,27 +574,26 @@
// since dex2oat/profman will fail with SElinux denials.
if (fchown(fd.get(), uid, uid) < 0) {
PLOG(ERROR) << "Could not chwon profile " << profile;
- return false;
+ return invalid_unique_fd();
}
- return true;
+ return fd;
}
-static unique_fd open_profile(int uid, const std::string& profile, bool read_write) {
- // Check if we need to open the profile for a read-write operation. If so, we
- // might need to create the profile since the file might not be there. Reference
- // profiles are created on the fly so they might not exist beforehand.
- if (read_write) {
- if (!create_profile(uid, profile)) {
- return invalid_unique_fd();
- }
- }
- int flags = read_write ? O_RDWR : O_RDONLY;
+static unique_fd open_profile(uid_t uid, const std::string& profile, int32_t flags) {
// Do not follow symlinks when opening a profile:
// - primary profiles should not contain symlinks in their paths
// - secondary dex paths should have been already resolved and validated
flags |= O_NOFOLLOW;
- unique_fd fd(TEMP_FAILURE_RETRY(open(profile.c_str(), flags)));
+ // Check if we need to create the profile
+ // Reference profiles and snapshots are created on the fly; so they might not exist beforehand.
+ unique_fd fd;
+ if ((flags & O_CREAT) != 0) {
+ fd = create_profile(uid, profile, flags);
+ } else {
+ fd.reset(TEMP_FAILURE_RETRY(open(profile.c_str(), flags)));
+ }
+
if (fd.get() < 0) {
if (errno != ENOENT) {
// Profiles might be missing for various reasons. For example, in a
@@ -616,13 +613,19 @@
static unique_fd open_current_profile(uid_t uid, userid_t user, const std::string& location,
bool is_secondary_dex) {
std::string profile = create_current_profile_path(user, location, is_secondary_dex);
- return open_profile(uid, profile, /*read_write*/false);
+ return open_profile(uid, profile, O_RDONLY);
}
static unique_fd open_reference_profile(uid_t uid, const std::string& location, bool read_write,
bool is_secondary_dex) {
std::string profile = create_reference_profile_path(location, is_secondary_dex);
- return open_profile(uid, profile, read_write);
+ return open_profile(uid, profile, read_write ? (O_CREAT | O_RDWR) : O_RDONLY);
+}
+
+static unique_fd open_spnashot_profile(uid_t uid, const std::string& package_name,
+ const std::string& code_path) {
+ std::string profile = create_snapshot_profile_path(package_name, code_path);
+ return open_profile(uid, profile, O_CREAT | O_RDWR | O_TRUNC);
}
static void open_profile_files(uid_t uid, const std::string& location, bool is_secondary_dex,
@@ -2307,5 +2310,42 @@
}
}
+bool snapshot_profile(int32_t app_id, const std::string& package_name,
+ const std::string& code_path) {
+ int app_shared_gid = multiuser_get_shared_gid(/*user_id*/ 0, app_id);
+
+ unique_fd snapshot_fd = open_spnashot_profile(AID_SYSTEM, package_name, code_path);
+ if (snapshot_fd < 0) {
+ return false;
+ }
+
+ std::vector<unique_fd> profiles_fd;
+ unique_fd reference_profile_fd;
+ open_profile_files(app_shared_gid, package_name, /*is_secondary_dex*/ false, &profiles_fd,
+ &reference_profile_fd);
+ if (profiles_fd.empty() || (reference_profile_fd.get() < 0)) {
+ return false;
+ }
+
+ profiles_fd.push_back(std::move(reference_profile_fd));
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ /* child -- drop privileges before continuing */
+ drop_capabilities(app_shared_gid);
+ run_profman_merge(profiles_fd, snapshot_fd);
+ exit(42); /* only get here on exec failure */
+ }
+
+ /* parent */
+ int return_code = wait_child(pid);
+ if (!WIFEXITED(return_code)) {
+ LOG(WARNING) << "profman failed for " << package_name << ":" << code_path;
+ return false;
+ }
+
+ return true;
+}
+
} // namespace installd
} // namespace android