summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCalin Juravle <calin@google.com>2017-09-04 15:57:10 -0700
committerCalin Juravle <calin@google.com>2017-11-14 01:36:09 +0000
commit7d7654646abb84bbae6b245de270ac087cdc8a95 (patch)
tree6e8fa8f52feb2705123ea899e07ba198ca373620
parent79ce1748ea4d3abc21929045da8ea337abadfd52 (diff)
downloadnative-7d7654646abb84bbae6b245de270ac087cdc8a95.tar.gz
Move secondary dex files processing in the app process
Run the validation and processing of secondary dex files with the app process capabilities. This drops the need to validate/use real paths since all file tests and accesses is done now with lower privileges. The oat files and profiles are now created relative to the given path instead of using realpath. This allows us to always know where the compiler artifacts are even if the original dex file was deleted. It enables a straightforward clean up during reconciliation. The validation will accept /data/user/0 as a valid secondary dex path since for user 0 this is the location that will be used most of the times. This CL also adds a test framework for dexopt related functionality. The current tests only cover the secondary dex files with more to come. Bug: 64460009 Test: adb shell cmd package reconcile-secondary-dex-files com.google.android.googlequicksearchbox (after removing some files) adb shell cmd package compile -m speed --secondary-dex com.google.android.googlequicksearchbox adb shell /data/nativetest64/installd_utils_test/installd_utils_test (cherry picked from commit 46e27bcc773f1c1df3a0ae8df67b49ab20e39b43) Merged-In: I0d49cd7f3c089bc5156178680bb29b75ab82092c Change-Id: I1c1feec6bc475ac5fd6e1339737626f5404ca6c9
-rw-r--r--cmds/installd/dexopt.cpp415
-rw-r--r--cmds/installd/tests/Android.bp20
-rw-r--r--cmds/installd/tests/installd_dexopt_test.cpp359
-rw-r--r--cmds/installd/tests/installd_utils_test.cpp3
-rw-r--r--cmds/installd/tests/test_utils.h107
-rw-r--r--cmds/installd/utils.cpp30
-rw-r--r--cmds/installd/utils.h4
7 files changed, 763 insertions, 175 deletions
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 20b7728921..af2c527056 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -1385,6 +1385,8 @@ bool maybe_open_oat_and_vdex_file(const std::string& apk_path,
oat_dir.c_str(),
is_secondary_dex,
oat_path)) {
+ LOG(ERROR) << "Could not create oat out path for "
+ << apk_path << " with oat dir " << oat_dir;
return false;
}
oat_file_fd->reset(open(oat_path, O_RDONLY));
@@ -1489,7 +1491,7 @@ static void exec_dexoptanalyzer(const std::string& dex_file, int vdex_fd, int oa
// Prepares the oat dir for the secondary dex files.
static bool prepare_secondary_dex_oat_dir(const std::string& dex_path, int uid,
- const char* instruction_set, std::string* oat_dir_out) {
+ const char* instruction_set) {
unsigned long dirIndex = dex_path.rfind('/');
if (dirIndex == std::string::npos) {
LOG(ERROR ) << "Unexpected dir structure for secondary dex " << dex_path;
@@ -1506,10 +1508,8 @@ static bool prepare_secondary_dex_oat_dir(const std::string& dex_path, int uid,
char oat_dir[PKG_PATH_MAX];
snprintf(oat_dir, PKG_PATH_MAX, "%s/oat", dex_dir.c_str());
- oat_dir_out->assign(oat_dir);
- // Create oat/isa output directory.
- if (prepare_app_cache_dir(*oat_dir_out, instruction_set, oat_dir_mode, uid, uid) != 0) {
+ if (prepare_app_cache_dir(oat_dir, instruction_set, oat_dir_mode, uid, uid) != 0) {
LOG(ERROR) << "Could not prepare oat/isa dir for secondary dex: " << dex_path;
return false;
}
@@ -1517,166 +1517,236 @@ static bool prepare_secondary_dex_oat_dir(const std::string& dex_path, int uid,
return true;
}
-static int constexpr DEXOPTANALYZER_BIN_EXEC_ERROR = 200;
+// Return codes for identifying the reason why dexoptanalyzer was not invoked when processing
+// secondary dex files. This return codes are returned by the child process created for
+// analyzing secondary dex files in process_secondary_dex_dexopt.
+
+// The dexoptanalyzer was not invoked because of validation or IO errors.
+static int constexpr SECONDARY_DEX_DEXOPTANALYZER_SKIPPED = 200;
+// The dexoptanalyzer was not invoked because the dex file does not exist anymore.
+static int constexpr SECONDARY_DEX_DEXOPTANALYZER_SKIPPED_NO_FILE = 201;
-// Verifies the result of dexoptanalyzer executed for the apk_path.
+// Verifies the result of analyzing secondary dex files from process_secondary_dex_dexopt.
// If the result is valid returns true and sets dexopt_needed_out to a valid value.
// Returns false for errors or unexpected result values.
-static bool process_dexoptanalyzer_result(const std::string& dex_path, int result,
+// The result is expected to be either one of SECONDARY_DEX_* codes or a valid exit code
+// of dexoptanalyzer.
+static bool process_secondary_dexoptanalyzer_result(const std::string& dex_path, int result,
int* dexopt_needed_out) {
// The result values are defined in dexoptanalyzer.
switch (result) {
- case 0: // no_dexopt_needed
+ case 0: // dexoptanalyzer: no_dexopt_needed
*dexopt_needed_out = NO_DEXOPT_NEEDED; return true;
- case 1: // dex2oat_from_scratch
+ case 1: // dexoptanalyzer: dex2oat_from_scratch
*dexopt_needed_out = DEX2OAT_FROM_SCRATCH; return true;
- case 5: // dex2oat_for_bootimage_odex
+ case 5: // dexoptanalyzer: dex2oat_for_bootimage_odex
*dexopt_needed_out = -DEX2OAT_FOR_BOOT_IMAGE; return true;
- case 6: // dex2oat_for_filter_odex
+ case 6: // dexoptanalyzer: dex2oat_for_filter_odex
*dexopt_needed_out = -DEX2OAT_FOR_FILTER; return true;
- case 7: // dex2oat_for_relocation_odex
+ case 7: // dexoptanalyzer: dex2oat_for_relocation_odex
*dexopt_needed_out = -DEX2OAT_FOR_RELOCATION; return true;
- case 2: // dex2oat_for_bootimage_oat
- case 3: // dex2oat_for_filter_oat
- case 4: // dex2oat_for_relocation_oat
+ case 2: // dexoptanalyzer: dex2oat_for_bootimage_oat
+ case 3: // dexoptanalyzer: dex2oat_for_filter_oat
+ case 4: // dexoptanalyzer: dex2oat_for_relocation_oat
LOG(ERROR) << "Dexoptnalyzer return the status of an oat file."
<< " Expected odex file status for secondary dex " << dex_path
<< " : dexoptanalyzer result=" << result;
return false;
+ case SECONDARY_DEX_DEXOPTANALYZER_SKIPPED_NO_FILE:
+ // If the file does not exist there's no need for dexopt.
+ *dexopt_needed_out = NO_DEXOPT_NEEDED;
+ return true;
+ case SECONDARY_DEX_DEXOPTANALYZER_SKIPPED:
+ return false;
default:
- LOG(ERROR) << "Unexpected result for dexoptanalyzer " << dex_path
- << " exec_dexoptanalyzer result=" << result;
+ LOG(ERROR) << "Unexpected result from analyzing secondary dex " << dex_path
+ << " result=" << result;
return false;
}
}
-// Processes the dex_path as a secondary dex files and return true if the path dex file should
-// be compiled. Returns false for errors (logged) or true if the secondary dex path was process
-// successfully.
-// When returning true, the output parameters will be:
-// - is_public_out: whether or not the oat file should not be made public
-// - dexopt_needed_out: valid OatFileAsssitant::DexOptNeeded
-// - oat_dir_out: the oat dir path where the oat file should be stored
-// - dex_path_out: the real path of the dex file
-static bool process_secondary_dex_dexopt(const char* original_dex_path, const char* pkgname,
- int dexopt_flags, const char* volume_uuid, int uid, const char* instruction_set,
- const char* compiler_filter, bool* is_public_out, int* dexopt_needed_out,
- std::string* oat_dir_out, std::string* dex_path_out, bool downgrade,
- const char* class_loader_context) {
- int storage_flag;
+enum SecondaryDexAccess {
+ kSecondaryDexAccessReadOk = 0,
+ kSecondaryDexAccessDoesNotExist = 1,
+ kSecondaryDexAccessPermissionError = 2,
+ kSecondaryDexAccessIOError = 3
+};
+
+static SecondaryDexAccess check_secondary_dex_access(const std::string& dex_path) {
+ // Check if the path exists and can be read. If not, there's nothing to do.
+ if (access(dex_path.c_str(), R_OK) == 0) {
+ return kSecondaryDexAccessReadOk;
+ } else {
+ if (errno == ENOENT) {
+ LOG(INFO) << "Secondary dex does not exist: " << dex_path;
+ return kSecondaryDexAccessDoesNotExist;
+ } else {
+ PLOG(ERROR) << "Could not access secondary dex " << dex_path;
+ return errno == EACCES
+ ? kSecondaryDexAccessPermissionError
+ : kSecondaryDexAccessIOError;
+ }
+ }
+}
+
+static bool is_file_public(const std::string& filename) {
+ struct stat file_stat;
+ if (stat(filename.c_str(), &file_stat) == 0) {
+ return (file_stat.st_mode & S_IROTH) != 0;
+ }
+ return false;
+}
+// Create the oat file structure for the secondary dex 'dex_path' and assign
+// the individual path component to the 'out_' parameters.
+static bool create_secondary_dex_oat_layout(const std::string& dex_path, const std::string& isa,
+ char* out_oat_dir, char* out_oat_isa_dir, char* out_oat_path) {
+ size_t dirIndex = dex_path.rfind('/');
+ if (dirIndex == std::string::npos) {
+ LOG(ERROR) << "Unexpected dir structure for dex file " << dex_path;
+ return false;
+ }
+ // TODO(calin): we have similar computations in at lest 3 other places
+ // (InstalldNativeService, otapropt and dexopt). Unify them and get rid of snprintf by
+ // using string append.
+ std::string apk_dir = dex_path.substr(0, dirIndex);
+ snprintf(out_oat_dir, PKG_PATH_MAX, "%s/oat", apk_dir.c_str());
+ snprintf(out_oat_isa_dir, PKG_PATH_MAX, "%s/%s", out_oat_dir, isa.c_str());
+
+ if (!create_oat_out_path(dex_path.c_str(), isa.c_str(), out_oat_dir,
+ /*is_secondary_dex*/true, out_oat_path)) {
+ LOG(ERROR) << "Could not create oat path for secondary dex " << dex_path;
+ return false;
+ }
+ return true;
+}
+
+// Validate that the dexopt_flags contain a valid storage flag and convert that to an installd
+// recognized storage flags (FLAG_STORAGE_CE or FLAG_STORAGE_DE).
+static bool validate_dexopt_storage_flags(int dexopt_flags, int* out_storage_flag) {
if ((dexopt_flags & DEXOPT_STORAGE_CE) != 0) {
- storage_flag = FLAG_STORAGE_CE;
+ *out_storage_flag = FLAG_STORAGE_CE;
if ((dexopt_flags & DEXOPT_STORAGE_DE) != 0) {
LOG(ERROR) << "Ambiguous secondary dex storage flag. Both, CE and DE, flags are set";
return false;
}
} else if ((dexopt_flags & DEXOPT_STORAGE_DE) != 0) {
- storage_flag = FLAG_STORAGE_DE;
+ *out_storage_flag = FLAG_STORAGE_DE;
} else {
LOG(ERROR) << "Secondary dex storage flag must be set";
return false;
}
+ return true;
+}
- {
- // As opposed to the primary apk, secondary dex files might contain symlinks.
- // Resolve the path before passing it to the validate method to
- // make sure the verification is done on the real location.
- UniqueCPtr<char> dex_real_path_cstr(realpath(original_dex_path, nullptr));
- if (dex_real_path_cstr == nullptr) {
- PLOG(ERROR) << "Could not get the real path of the secondary dex file "
- << original_dex_path;
- return false;
- } else {
- dex_path_out->assign(dex_real_path_cstr.get());
- }
- }
- const std::string& dex_path = *dex_path_out;
- if (!validate_dex_path_size(dex_path)) {
+// Processes the dex_path as a secondary dex files and return true if the path dex file should
+// be compiled. Returns false for errors (logged) or true if the secondary dex path was process
+// successfully.
+// When returning true, the output parameters will be:
+// - is_public_out: whether or not the oat file should not be made public
+// - dexopt_needed_out: valid OatFileAsssitant::DexOptNeeded
+// - oat_dir_out: the oat dir path where the oat file should be stored
+static bool process_secondary_dex_dexopt(const std::string& dex_path, const char* pkgname,
+ int dexopt_flags, const char* volume_uuid, int uid, const char* instruction_set,
+ const char* compiler_filter, bool* is_public_out, int* dexopt_needed_out,
+ std::string* oat_dir_out, bool downgrade, const char* class_loader_context) {
+ LOG(DEBUG) << "Processing secondary dex path " << dex_path;
+ int storage_flag;
+ if (!validate_dexopt_storage_flags(dexopt_flags, &storage_flag)) {
return false;
}
- if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid, uid, storage_flag)) {
- LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
+ // Compute the oat dir as it's not easy to extract it from the child computation.
+ char oat_path[PKG_PATH_MAX];
+ char oat_dir[PKG_PATH_MAX];
+ char oat_isa_dir[PKG_PATH_MAX];
+ if (!create_secondary_dex_oat_layout(
+ dex_path, instruction_set, oat_dir, oat_isa_dir, oat_path)) {
+ LOG(ERROR) << "Could not create secondary odex layout: " << dex_path;
return false;
}
+ oat_dir_out->assign(oat_dir);
- // Check if the path exist. If not, there's nothing to do.
- struct stat dex_path_stat;
- if (stat(dex_path.c_str(), &dex_path_stat) != 0) {
- if (errno == ENOENT) {
- // Secondary dex files might be deleted any time by the app.
- // Nothing to do if that's the case
- ALOGV("Secondary dex does not exist %s", dex_path.c_str());
- return NO_DEXOPT_NEEDED;
- } else {
- PLOG(ERROR) << "Could not access secondary dex " << dex_path;
+ pid_t pid = fork();
+ if (pid == 0) {
+ // child -- drop privileges before continuing.
+ drop_capabilities(uid);
+
+ // Validate the path structure.
+ if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid, uid, storage_flag)) {
+ LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
+ _exit(SECONDARY_DEX_DEXOPTANALYZER_SKIPPED);
}
- }
- // Check if we should make the oat file public.
- // Note that if the dex file is not public the compiled code cannot be made public.
- *is_public_out = ((dexopt_flags & DEXOPT_PUBLIC) != 0) &&
- ((dex_path_stat.st_mode & S_IROTH) != 0);
+ // Open the dex file.
+ unique_fd zip_fd;
+ zip_fd.reset(open(dex_path.c_str(), O_RDONLY));
+ if (zip_fd.get() < 0) {
+ if (errno == ENOENT) {
+ _exit(SECONDARY_DEX_DEXOPTANALYZER_SKIPPED_NO_FILE);
+ } else {
+ _exit(SECONDARY_DEX_DEXOPTANALYZER_SKIPPED);
+ }
+ }
- // Prepare the oat directories.
- if (!prepare_secondary_dex_oat_dir(dex_path, uid, instruction_set, oat_dir_out)) {
- return false;
- }
+ // Prepare the oat directories.
+ if (!prepare_secondary_dex_oat_dir(dex_path, uid, instruction_set)) {
+ _exit(SECONDARY_DEX_DEXOPTANALYZER_SKIPPED);
+ }
- // Analyze profiles.
- bool profile_was_updated = analyze_profiles(uid, dex_path, /*is_secondary_dex*/true);
+ // Open the vdex/oat files if any.
+ unique_fd oat_file_fd;
+ unique_fd vdex_file_fd;
+ if (!maybe_open_oat_and_vdex_file(dex_path,
+ *oat_dir_out,
+ instruction_set,
+ true /* is_secondary_dex */,
+ &oat_file_fd,
+ &vdex_file_fd)) {
+ _exit(SECONDARY_DEX_DEXOPTANALYZER_SKIPPED);
+ }
- unique_fd oat_file_fd;
- unique_fd vdex_file_fd;
- unique_fd zip_fd;
- zip_fd.reset(open(dex_path.c_str(), O_RDONLY));
- if (zip_fd.get() < 0) {
- PLOG(ERROR) << "installd cannot open " << dex_path << " for input during dexopt";
- return false;
- }
- if (!maybe_open_oat_and_vdex_file(dex_path,
- *oat_dir_out,
- instruction_set,
- true /* is_secondary_dex */,
- &oat_file_fd,
- &vdex_file_fd)) {
- return false;
- }
+ // Analyze profiles.
+ bool profile_was_updated = analyze_profiles(uid, dex_path, /*is_secondary_dex*/true);
- pid_t pid = fork();
- if (pid == 0) {
- // child -- drop privileges before continuing.
- drop_capabilities(uid);
- // Run dexoptanalyzer to get dexopt_needed code.
+ // Run dexoptanalyzer to get dexopt_needed code. This is not expected to return.
exec_dexoptanalyzer(dex_path,
vdex_file_fd.get(),
oat_file_fd.get(),
zip_fd.get(),
instruction_set,
- compiler_filter,
- profile_was_updated,
- downgrade, class_loader_context);
- exit(DEXOPTANALYZER_BIN_EXEC_ERROR);
+ compiler_filter, profile_was_updated,
+ downgrade,
+ class_loader_context);
+ PLOG(ERROR) << "Failed to exec dexoptanalyzer";
+ _exit(SECONDARY_DEX_DEXOPTANALYZER_SKIPPED);
}
/* parent */
-
int result = wait_child(pid);
if (!WIFEXITED(result)) {
LOG(ERROR) << "dexoptanalyzer failed for path " << dex_path << ": " << result;
return false;
}
result = WEXITSTATUS(result);
- bool success = process_dexoptanalyzer_result(dex_path, result, dexopt_needed_out);
+ // Check that we successfully executed dexoptanalyzer.
+ bool success = process_secondary_dexoptanalyzer_result(dex_path, result, dexopt_needed_out);
+
+ LOG(DEBUG) << "Processed secondary dex file " << dex_path << " result=" << result;
+
// Run dexopt only if needed or forced.
- // Note that dexoptanalyzer is executed even if force compilation is enabled.
- // We ignore its valid dexopNeeded result, but still check (in process_dexoptanalyzer_result)
- // that we only get results for odex files (apk_dir/oat/isa/code.odex) and not
- // for oat files from dalvik-cache.
- if (success && ((dexopt_flags & DEXOPT_FORCE) != 0)) {
+ // Note that dexoptanalyzer is executed even if force compilation is enabled (because it
+ // makes the code simpler; force compilation is only needed during tests).
+ if (success &&
+ (result != SECONDARY_DEX_DEXOPTANALYZER_SKIPPED_NO_FILE) &&
+ ((dexopt_flags & DEXOPT_FORCE) != 0)) {
*dexopt_needed_out = DEX2OAT_FROM_SCRATCH;
}
+ // Check if we should make the oat file public.
+ // Note that if the dex file is not public the compiled code cannot be made public.
+ // It is ok to check this flag outside in the parent process.
+ *is_public_out = ((dexopt_flags & DEXOPT_PUBLIC) != 0) && is_file_public(dex_path);
+
return success;
}
@@ -1708,13 +1778,11 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins
// Check if we're dealing with a secondary dex file and if we need to compile it.
std::string oat_dir_str;
- std::string dex_real_path;
if (is_secondary_dex) {
if (process_secondary_dex_dexopt(dex_path, pkgname, dexopt_flags, volume_uuid, uid,
instruction_set, compiler_filter, &is_public, &dexopt_needed, &oat_dir_str,
- &dex_real_path, downgrade, class_loader_context)) {
+ downgrade, class_loader_context)) {
oat_dir = oat_dir_str.c_str();
- dex_path = dex_real_path.c_str();
if (dexopt_needed == NO_DEXOPT_NEEDED) {
return 0; // Nothing to do, report success.
}
@@ -1852,29 +1920,13 @@ static bool unlink_if_exists(const std::string& file) {
return false;
}
-// Create the oat file structure for the secondary dex 'dex_path' and assign
-// the individual path component to the 'out_' parameters.
-static bool create_secondary_dex_oat_layout(const std::string& dex_path, const std::string& isa,
- /*out*/char* out_oat_dir, /*out*/char* out_oat_isa_dir, /*out*/char* out_oat_path) {
- size_t dirIndex = dex_path.rfind('/');
- if (dirIndex == std::string::npos) {
- LOG(ERROR) << "Unexpected dir structure for dex file " << dex_path;
- return false;
- }
- // TODO(calin): we have similar computations in at lest 3 other places
- // (InstalldNativeService, otapropt and dexopt). Unify them and get rid of snprintf by
- // use string append.
- std::string apk_dir = dex_path.substr(0, dirIndex);
- snprintf(out_oat_dir, PKG_PATH_MAX, "%s/oat", apk_dir.c_str());
- snprintf(out_oat_isa_dir, PKG_PATH_MAX, "%s/%s", out_oat_dir, isa.c_str());
-
- if (!create_oat_out_path(dex_path.c_str(), isa.c_str(), out_oat_dir,
- /*is_secondary_dex*/true, out_oat_path)) {
- LOG(ERROR) << "Could not create oat path for secondary dex " << dex_path;
- return false;
- }
- return true;
-}
+enum ReconcileSecondaryDexResult {
+ kReconcileSecondaryDexExists = 0,
+ kReconcileSecondaryDexCleanedUp = 1,
+ kReconcileSecondaryDexValidationError = 2,
+ kReconcileSecondaryDexCleanUpError = 3,
+ kReconcileSecondaryDexAccessIOError = 4,
+};
// Reconcile the secondary dex 'dex_path' and its generated oat files.
// Return true if all the parameters are valid and the secondary dex file was
@@ -1888,36 +1940,15 @@ bool reconcile_secondary_dex_file(const std::string& dex_path,
const std::string& pkgname, int uid, const std::vector<std::string>& isas,
const std::unique_ptr<std::string>& volume_uuid, int storage_flag,
/*out*/bool* out_secondary_dex_exists) {
- // Set out to false to start with, just in case we have validation errors.
- *out_secondary_dex_exists = false;
- if (!validate_dex_path_size(dex_path)) {
- return false;
- }
-
+ *out_secondary_dex_exists = false; // start by assuming the file does not exist.
if (isas.size() == 0) {
LOG(ERROR) << "reconcile_secondary_dex_file called with empty isas vector";
return false;
}
- const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str();
-
- // Note that we cannot validate the package path here because the file might not exist
- // and we cannot call realpath to resolve system symlinks. Since /data/user/0 symlinks to
- // /data/data/ a lot of validations will fail if we attempt to check the package path.
- // It is still ok to be more relaxed because any file removal is done after forking and
- // dropping capabilities.
- if (!validate_secondary_dex_path(pkgname.c_str(), dex_path.c_str(), volume_uuid_cstr,
- uid, storage_flag, /*validate_package_path*/ false)) {
- LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
- return false;
- }
-
- if (access(dex_path.c_str(), F_OK) == 0) {
- // The path exists, nothing to do. The odex files (if any) will be left untouched.
- *out_secondary_dex_exists = true;
- return true;
- } else if (errno != ENOENT) {
- PLOG(ERROR) << "Failed to check access to secondary dex " << dex_path;
+ if (storage_flag != FLAG_STORAGE_CE && storage_flag != FLAG_STORAGE_DE) {
+ LOG(ERROR) << "reconcile_secondary_dex_file called with invalid storage_flag: "
+ << storage_flag;
return false;
}
@@ -1925,23 +1956,39 @@ bool reconcile_secondary_dex_file(const std::string& dex_path,
// of the package user id. So we fork and drop capabilities in the child.
pid_t pid = fork();
if (pid == 0) {
- // The secondary dex does not exist anymore. Clear any generated files.
+ /* child -- drop privileges before continuing */
+ drop_capabilities(uid);
+
+ const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str();
+ if (!validate_secondary_dex_path(pkgname.c_str(), dex_path.c_str(), volume_uuid_cstr,
+ uid, storage_flag)) {
+ LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
+ _exit(kReconcileSecondaryDexValidationError);
+ }
+
+ SecondaryDexAccess access_check = check_secondary_dex_access(dex_path);
+ switch (access_check) {
+ case kSecondaryDexAccessDoesNotExist:
+ // File does not exist. Proceed with cleaning.
+ break;
+ case kSecondaryDexAccessReadOk: _exit(kReconcileSecondaryDexExists);
+ case kSecondaryDexAccessIOError: _exit(kReconcileSecondaryDexAccessIOError);
+ case kSecondaryDexAccessPermissionError: _exit(kReconcileSecondaryDexValidationError);
+ default:
+ LOG(ERROR) << "Unexpected result from check_secondary_dex_access: " << access_check;
+ _exit(kReconcileSecondaryDexValidationError);
+ }
+
+ // The secondary dex does not exist anymore or it's. Clear any generated files.
char oat_path[PKG_PATH_MAX];
char oat_dir[PKG_PATH_MAX];
char oat_isa_dir[PKG_PATH_MAX];
bool result = true;
- /* child -- drop privileges before continuing */
- drop_capabilities(uid);
for (size_t i = 0; i < isas.size(); i++) {
- if (!create_secondary_dex_oat_layout(dex_path,
- isas[i],
- oat_dir,
- oat_isa_dir,
- oat_path)) {
- LOG(ERROR) << "Could not create secondary odex layout: "
- << dex_path;
- result = false;
- continue;
+ if (!create_secondary_dex_oat_layout(
+ dex_path,isas[i], oat_dir, oat_isa_dir, oat_path)) {
+ LOG(ERROR) << "Could not create secondary odex layout: " << dex_path;
+ _exit(kReconcileSecondaryDexValidationError);
}
// Delete oat/vdex/art files.
@@ -1966,11 +2013,43 @@ bool reconcile_secondary_dex_file(const std::string& dex_path,
result = rmdir_if_empty(oat_isa_dir) && result;
result = rmdir_if_empty(oat_dir) && result;
}
- result ? _exit(0) : _exit(1);
+ if (!result) {
+ PLOG(ERROR) << "Failed to clean secondary dex artifacts for location " << dex_path;
+ }
+ _exit(result ? kReconcileSecondaryDexCleanedUp : kReconcileSecondaryDexAccessIOError);
}
int return_code = wait_child(pid);
- return return_code == 0;
+ if (!WIFEXITED(return_code)) {
+ LOG(WARNING) << "reconcile dex failed for location " << dex_path << ": " << return_code;
+ } else {
+ return_code = WEXITSTATUS(return_code);
+ }
+
+ LOG(DEBUG) << "Reconcile secondary dex path " << dex_path << " result=" << return_code;
+
+ switch (return_code) {
+ case kReconcileSecondaryDexCleanedUp:
+ case kReconcileSecondaryDexValidationError:
+ // If we couldn't validate assume the dex file does not exist.
+ // This will purge the entry from the PM records.
+ *out_secondary_dex_exists = false;
+ return true;
+ case kReconcileSecondaryDexExists:
+ *out_secondary_dex_exists = true;
+ return true;
+ case kReconcileSecondaryDexAccessIOError:
+ // We had an access IO error.
+ // Return false so that we can try again.
+ // The value of out_secondary_dex_exists does not matter in this case and by convention
+ // is set to false.
+ *out_secondary_dex_exists = false;
+ return false;
+ default:
+ LOG(ERROR) << "Unexpected code from reconcile_secondary_dex_file: " << return_code;
+ *out_secondary_dex_exists = false;
+ return false;
+ }
}
// Helper for move_ab, so that we can have common failure-case cleanup.
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index 89c11aa160..1a22992cb8 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -55,3 +55,23 @@ cc_test {
"liblogwrap",
],
}
+
+cc_test {
+ name: "installd_dexopt_test",
+ clang: true,
+ srcs: ["installd_dexopt_test.cpp"],
+ cflags: ["-Wall", "-Werror"],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libcutils",
+ "libselinux",
+ "libutils",
+ ],
+ static_libs: [
+ "libdiskusage",
+ "libinstalld",
+ "liblog",
+ "liblogwrap",
+ ],
+}
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
new file mode 100644
index 0000000000..821b96fc44
--- /dev/null
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <cutils/properties.h>
+#include <gtest/gtest.h>
+
+#include "dexopt.h"
+#include "InstalldNativeService.h"
+#include "globals.h"
+#include "tests/test_utils.h"
+#include "utils.h"
+
+namespace android {
+namespace installd {
+
+// TODO(calin): try to dedup this code.
+#if defined(__arm__)
+static const std::string kRuntimeIsa = "arm";
+#elif defined(__aarch64__)
+static const std::string kRuntimeIsa = "arm64";
+#elif defined(__mips__) && !defined(__LP64__)
+static const std::string kRuntimeIsa = "mips";
+#elif defined(__mips__) && defined(__LP64__)
+static const std::string kRuntimeIsa = "mips64";
+#elif defined(__i386__)
+static const std::string kRuntimeIsa = "x86";
+#elif defined(__x86_64__)
+static const std::string kRuntimeIsa = "x86_64";
+#else
+static const std::string kRuntimeIsa = "none";
+#endif
+
+int get_property(const char *key, char *value, const char *default_value) {
+ return property_get(key, value, default_value);
+}
+
+bool calculate_oat_file_path(char path[PKG_PATH_MAX], const char *oat_dir, const char *apk_path,
+ const char *instruction_set) {
+ return calculate_oat_file_path_default(path, oat_dir, apk_path, instruction_set);
+}
+
+bool calculate_odex_file_path(char path[PKG_PATH_MAX], const char *apk_path,
+ const char *instruction_set) {
+ return calculate_odex_file_path_default(path, apk_path, instruction_set);
+}
+
+bool create_cache_path(char path[PKG_PATH_MAX], const char *src, const char *instruction_set) {
+ return create_cache_path_default(path, src, instruction_set);
+}
+
+static void run_cmd(const std::string& cmd) {
+ system(cmd.c_str());
+}
+
+static void mkdir(const std::string& path, uid_t owner, gid_t group, mode_t mode) {
+ ::mkdir(path.c_str(), mode);
+ ::chown(path.c_str(), owner, group);
+ ::chmod(path.c_str(), mode);
+}
+
+// Base64 encoding of a simple dex files with 2 methods.
+static const char kDexFile[] =
+ "UEsDBBQAAAAIAOiOYUs9y6BLCgEAABQCAAALABwAY2xhc3Nlcy5kZXhVVAkAA/Ns+lkOHv1ZdXgL"
+ "AAEEI+UCAASIEwAAS0mt4DIwNmX4qpn7j/2wA7v7N+ZvoQpCJRlVx5SWa4YaiDAxMBQwMDBUhJkI"
+ "MUBBDyMDAzsDRJwFxAdioBDDHAYEYAbiFUAM1M5wAIhFGCGKDIDYAogdgNgDiH2BOAiI0xghekDm"
+ "sQIxGxQzM6ACRijNhCbOhCZfyohdPYyuh8szgtVkMkLsLhAAqeCDi+ejibPZZOZlltgxsDnqZSWW"
+ "JTKwOUFoZh9HayDhZM0g5AMS0M9JzEvX90/KSk0usWZgDAMaws5nAyXBzmpoYGlgAjsAyJoBMp0b"
+ "zQ8gGhbOTEhhzYwU3qxIYc2GFN6MClC/AhUyKUDMAYU9M1Qc5F8GKBscVgIQM0FxCwBQSwECHgMU"
+ "AAAACADojmFLPcugSwoBAAAUAgAACwAYAAAAAAAAAAAAoIEAAAAAY2xhc3Nlcy5kZXhVVAUAA/Ns"
+ "+ll1eAsAAQQj5QIABIgTAABQSwUGAAAAAAEAAQBRAAAATwEAAAAA";
+
+
+class DexoptTest : public testing::Test {
+protected:
+ static constexpr bool kDebug = false;
+ static constexpr uid_t kSystemUid = 1000;
+ static constexpr uid_t kSystemGid = 1000;
+ static constexpr int32_t kOSdkVersion = 25;
+ static constexpr int32_t kAppDataFlags = FLAG_STORAGE_CE | FLAG_STORAGE_DE;
+ static constexpr uid_t kTestAppUid = 19999;
+ static constexpr gid_t kTestAppGid = 19999;
+ static constexpr int32_t kTestUserId = 0;
+
+ InstalldNativeService* service_;
+ std::unique_ptr<std::string> volume_uuid_;
+ std::string package_name_;
+ std::string app_apk_dir_;
+ std::string app_private_dir_ce_;
+ std::string app_private_dir_de_;
+ std::string se_info_;
+
+ int64_t ce_data_inode_;
+
+ std::string secondary_dex_ce_;
+ std::string secondary_dex_ce_link_;
+ std::string secondary_dex_de_;
+
+ virtual void SetUp() {
+ setenv("ANDROID_LOG_TAGS", "*:v", 1);
+ android::base::InitLogging(nullptr);
+
+ service_ = new InstalldNativeService();
+
+ volume_uuid_ = nullptr;
+ package_name_ = "com.installd.test.dexopt";
+ se_info_ = "default";
+
+ init_globals_from_data_and_root();
+
+ app_apk_dir_ = android_app_dir + package_name_;
+
+ create_mock_app();
+ }
+
+ virtual void TearDown() {
+ if (!kDebug) {
+ service_->destroyAppData(
+ volume_uuid_, package_name_, kTestUserId, kAppDataFlags, ce_data_inode_);
+ run_cmd("rm -rf " + app_apk_dir_);
+ run_cmd("rm -rf " + app_private_dir_ce_);
+ run_cmd("rm -rf " + app_private_dir_de_);
+ }
+ delete service_;
+ }
+
+ void create_mock_app() {
+ // Create the oat dir.
+ std::string app_oat_dir = app_apk_dir_ + "/oat";
+ mkdir(app_apk_dir_, kSystemUid, kSystemGid, 0755);
+ 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));
+
+ // Create the app user data.
+ ASSERT_TRUE(service_->createAppData(
+ volume_uuid_,
+ package_name_,
+ kTestUserId,
+ kAppDataFlags,
+ kTestAppUid,
+ se_info_,
+ kOSdkVersion,
+ &ce_data_inode_).isOk());
+
+ // Create a secondary dex file on CE storage
+ const char* volume_uuid_cstr = volume_uuid_ == nullptr ? nullptr : volume_uuid_->c_str();
+ app_private_dir_ce_ = create_data_user_ce_package_path(
+ volume_uuid_cstr, kTestUserId, package_name_.c_str());
+ secondary_dex_ce_ = app_private_dir_ce_ + "/secondary_ce.jar";
+ ASSERT_TRUE(WriteBase64ToFile(kDexFile, secondary_dex_ce_, kTestAppUid, kTestAppGid, 0600));
+ std::string app_private_dir_ce_link = create_data_user_ce_package_path_as_user_link(
+ volume_uuid_cstr, kTestUserId, package_name_.c_str());
+ secondary_dex_ce_link_ = app_private_dir_ce_link + "/secondary_ce.jar";
+
+ // Create a secondary dex file on DE storage.
+ app_private_dir_de_ = create_data_user_de_package_path(
+ volume_uuid_cstr, kTestUserId, package_name_.c_str());
+ secondary_dex_de_ = app_private_dir_de_ + "/secondary_de.jar";
+ ASSERT_TRUE(WriteBase64ToFile(kDexFile, secondary_dex_de_, kTestAppUid, kTestAppGid, 0600));
+
+ // Fix app data uid.
+ ASSERT_TRUE(service_->fixupAppData(volume_uuid_, kTestUserId).isOk());
+ }
+
+
+ std::string get_secondary_dex_artifact(const std::string& path, const std::string& type) {
+ std::string::size_type end = path.rfind('.');
+ std::string::size_type start = path.rfind('/', end);
+ return path.substr(0, start) + "/oat/" + kRuntimeIsa + "/" +
+ path.substr(start + 1, end - start) + type;
+ }
+
+ void compile_secondary_dex(const std::string& path, int32_t dex_storage_flag,
+ bool should_binder_call_succeed, bool should_dex_be_compiled = true,
+ int uid = kTestAppUid) {
+ std::unique_ptr<std::string> package_name_ptr(new std::string(package_name_));
+ int32_t dexopt_needed = 0; // does not matter;
+ std::unique_ptr<std::string> out_path = nullptr; // does not matter
+ int32_t dex_flags = DEXOPT_SECONDARY_DEX | dex_storage_flag;
+ std::string compiler_filter = "speed-profile";
+ std::unique_ptr<std::string> class_loader_context_ptr(new std::string("&"));
+ std::unique_ptr<std::string> se_info_ptr(new std::string(se_info_));
+ bool downgrade = false;
+
+ binder::Status result = service_->dexopt(path,
+ uid,
+ package_name_ptr,
+ kRuntimeIsa,
+ dexopt_needed,
+ out_path,
+ dex_flags,
+ compiler_filter,
+ volume_uuid_,
+ class_loader_context_ptr,
+ se_info_ptr,
+ downgrade);
+ ASSERT_EQ(should_binder_call_succeed, result.isOk());
+ int expected_access = should_dex_be_compiled ? 0 : -1;
+ std::string odex = get_secondary_dex_artifact(path, "odex");
+ std::string vdex = get_secondary_dex_artifact(path, "vdex");
+ std::string art = get_secondary_dex_artifact(path, "art");
+ ASSERT_EQ(expected_access, access(odex.c_str(), R_OK));
+ ASSERT_EQ(expected_access, access(vdex.c_str(), R_OK));
+ ASSERT_EQ(-1, access(art.c_str(), R_OK)); // empty profiles do not generate an image.
+ }
+
+ void reconcile_secondary_dex(const std::string& path, int32_t storage_flag,
+ bool should_binder_call_succeed, bool should_dex_exist, bool should_dex_be_deleted,
+ int uid = kTestAppUid, std::string* package_override = nullptr) {
+ std::vector<std::string> isas;
+ isas.push_back(kRuntimeIsa);
+ bool out_secondary_dex_exists = false;
+ binder::Status result = service_->reconcileSecondaryDexFile(
+ path,
+ package_override == nullptr ? package_name_ : *package_override,
+ uid,
+ isas,
+ volume_uuid_,
+ storage_flag,
+ &out_secondary_dex_exists);
+
+ ASSERT_EQ(should_binder_call_succeed, result.isOk());
+ ASSERT_EQ(should_dex_exist, out_secondary_dex_exists);
+
+ int expected_access = should_dex_be_deleted ? -1 : 0;
+ std::string odex = get_secondary_dex_artifact(path, "odex");
+ std::string vdex = get_secondary_dex_artifact(path, "vdex");
+ std::string art = get_secondary_dex_artifact(path, "art");
+ ASSERT_EQ(expected_access, access(odex.c_str(), F_OK));
+ ASSERT_EQ(expected_access, access(vdex.c_str(), F_OK));
+ ASSERT_EQ(-1, access(art.c_str(), R_OK)); // empty profiles do not generate an image.
+ }
+};
+
+
+TEST_F(DexoptTest, DexoptSecondaryCe) {
+ LOG(INFO) << "DexoptSecondaryCe";
+ compile_secondary_dex(secondary_dex_ce_, DEXOPT_STORAGE_CE,
+ /*binder_ok*/ true, /*compile_ok*/ true);
+}
+
+TEST_F(DexoptTest, DexoptSecondaryCeLink) {
+ LOG(INFO) << "DexoptSecondaryCeLink";
+ compile_secondary_dex(secondary_dex_ce_link_, DEXOPT_STORAGE_CE,
+ /*binder_ok*/ true, /*compile_ok*/ true);
+}
+
+TEST_F(DexoptTest, DexoptSecondaryDe) {
+ LOG(INFO) << "DexoptSecondaryDe";
+ compile_secondary_dex(secondary_dex_de_, DEXOPT_STORAGE_DE,
+ /*binder_ok*/ true, /*compile_ok*/ true);
+}
+
+TEST_F(DexoptTest, DexoptSecondaryDoesNotExist) {
+ LOG(INFO) << "DexoptSecondaryDoesNotExist";
+ // If the file validates but does not exist we do not treat it as an error.
+ compile_secondary_dex(secondary_dex_ce_ + "not.there", DEXOPT_STORAGE_CE,
+ /*binder_ok*/ true, /*compile_ok*/ false);
+}
+
+TEST_F(DexoptTest, DexoptSecondaryStorageValidationError) {
+ LOG(INFO) << "DexoptSecondaryStorageValidationError";
+ compile_secondary_dex(secondary_dex_ce_, DEXOPT_STORAGE_DE,
+ /*binder_ok*/ false, /*compile_ok*/ false);
+}
+
+TEST_F(DexoptTest, DexoptSecondaryAppOwnershipValidationError) {
+ LOG(INFO) << "DexoptSecondaryAppOwnershipValidationError";
+ compile_secondary_dex("/data/data/random.app/secondary.jar", DEXOPT_STORAGE_CE,
+ /*binder_ok*/ false, /*compile_ok*/ false);
+}
+
+TEST_F(DexoptTest, DexoptSecondaryAcessViaDifferentUidError) {
+ LOG(INFO) << "DexoptSecondaryAcessViaDifferentUidError";
+ compile_secondary_dex(secondary_dex_ce_, DEXOPT_STORAGE_CE,
+ /*binder_ok*/ false, /*compile_ok*/ false, kSystemUid);
+}
+
+
+class ReconcileTest : public DexoptTest {
+ virtual void SetUp() {
+ DexoptTest::SetUp();
+ compile_secondary_dex(secondary_dex_ce_, DEXOPT_STORAGE_CE,
+ /*binder_ok*/ true, /*compile_ok*/ true);
+ compile_secondary_dex(secondary_dex_de_, DEXOPT_STORAGE_DE,
+ /*binder_ok*/ true, /*compile_ok*/ true);
+ }
+};
+
+TEST_F(ReconcileTest, ReconcileSecondaryCeExists) {
+ LOG(INFO) << "ReconcileSecondaryCeExists";
+ reconcile_secondary_dex(secondary_dex_ce_, FLAG_STORAGE_CE,
+ /*binder_ok*/ true, /*dex_ok */ true, /*odex_deleted*/ false);
+}
+
+TEST_F(ReconcileTest, ReconcileSecondaryCeLinkExists) {
+ LOG(INFO) << "ReconcileSecondaryCeLinkExists";
+ reconcile_secondary_dex(secondary_dex_ce_link_, FLAG_STORAGE_CE,
+ /*binder_ok*/ true, /*dex_ok */ true, /*odex_deleted*/ false);
+}
+
+TEST_F(ReconcileTest, ReconcileSecondaryDeExists) {
+ LOG(INFO) << "ReconcileSecondaryDeExists";
+ reconcile_secondary_dex(secondary_dex_de_, FLAG_STORAGE_DE,
+ /*binder_ok*/ true, /*dex_ok */ true, /*odex_deleted*/ false);
+}
+
+TEST_F(ReconcileTest, ReconcileSecondaryDeDoesNotExist) {
+ LOG(INFO) << "ReconcileSecondaryDeDoesNotExist";
+ run_cmd("rm -rf " + secondary_dex_de_);
+ reconcile_secondary_dex(secondary_dex_de_, FLAG_STORAGE_DE,
+ /*binder_ok*/ true, /*dex_ok */ false, /*odex_deleted*/ true);
+}
+
+TEST_F(ReconcileTest, ReconcileSecondaryStorageValidationError) {
+ // Validation errors will not clean the odex/vdex/art files but will mark
+ // the file as non existent so that the PM knows it should purge it from its
+ // records.
+ LOG(INFO) << "ReconcileSecondaryStorageValidationError";
+ reconcile_secondary_dex(secondary_dex_ce_, FLAG_STORAGE_DE,
+ /*binder_ok*/ true, /*dex_ok */ false, /*odex_deleted*/ false);
+}
+
+TEST_F(ReconcileTest, ReconcileSecondaryAppOwnershipValidationError) {
+ LOG(INFO) << "ReconcileSecondaryAppOwnershipValidationError";
+ // Attempt to reconcile the dex files of the test app from a different app.
+ std::string another_app = "another.app";
+ reconcile_secondary_dex(secondary_dex_ce_, FLAG_STORAGE_CE,
+ /*binder_ok*/ true, /*dex_ok */ false, /*odex_deleted*/ false, kSystemUid, &another_app);
+}
+
+TEST_F(ReconcileTest, ReconcileSecondaryAcessViaDifferentUidError) {
+ LOG(INFO) << "ReconcileSecondaryAcessViaDifferentUidError";
+ reconcile_secondary_dex(secondary_dex_ce_, FLAG_STORAGE_CE,
+ /*binder_ok*/ true, /*dex_ok */ false, /*odex_deleted*/ false, kSystemUid);
+}
+
+} // namespace installd
+} // namespace android
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index 2ca7ac2350..a6a4451d9d 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -379,6 +379,7 @@ static void fail_secondary_dex_validation(const std::string& package_name,
TEST_F(UtilsTest, ValidateSecondaryDexFilesPath) {
std::string package_name = "com.test.app";
std::string app_dir_ce_user_0 = "/data/data/" + package_name;
+ std::string app_dir_ce_user_0_link = "/data/user/0/" + package_name;
std::string app_dir_ce_user_10 = "/data/user/10/" + package_name;
std::string app_dir_de_user_0 = "/data/user_de/0/" + package_name;
@@ -400,6 +401,8 @@ TEST_F(UtilsTest, ValidateSecondaryDexFilesPath) {
// Standard path for user 0 on CE storage.
pass_secondary_dex_validation(
package_name, app_dir_ce_user_0 + "/ce0.dex", app_uid_for_user_0, FLAG_STORAGE_CE);
+ pass_secondary_dex_validation(
+ package_name, app_dir_ce_user_0_link + "/ce0.dex", app_uid_for_user_0, FLAG_STORAGE_CE);
// Standard path for user 10 on CE storage.
pass_secondary_dex_validation(
package_name, app_dir_ce_user_10 + "/ce10.dex", app_uid_for_user_10, FLAG_STORAGE_CE);
diff --git a/cmds/installd/tests/test_utils.h b/cmds/installd/tests/test_utils.h
new file mode 100644
index 0000000000..7d1162e673
--- /dev/null
+++ b/cmds/installd/tests/test_utils.h
@@ -0,0 +1,107 @@
+#include <android-base/logging.h>
+#include <stdlib.h>
+#include <string.h>
+
+uint8_t kBase64Map[256] = {
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255,
+ 255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
+ 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255
+};
+
+uint8_t* DecodeBase64(const char* src, size_t* dst_size) {
+ CHECK(dst_size != nullptr);
+ std::vector<uint8_t> tmp;
+ uint32_t t = 0, y = 0;
+ int g = 3;
+ for (size_t i = 0; src[i] != '\0'; ++i) {
+ uint8_t c = kBase64Map[src[i] & 0xFF];
+ if (c == 255) continue;
+ // the final = symbols are read and used to trim the remaining bytes
+ if (c == 254) {
+ c = 0;
+ // prevent g < 0 which would potentially allow an overflow later
+ if (--g < 0) {
+ *dst_size = 0;
+ return nullptr;
+ }
+ } else if (g != 3) {
+ // we only allow = to be at the end
+ *dst_size = 0;
+ return nullptr;
+ }
+ t = (t << 6) | c;
+ if (++y == 4) {
+ tmp.push_back((t >> 16) & 255);
+ if (g > 1) {
+ tmp.push_back((t >> 8) & 255);
+ }
+ if (g > 2) {
+ tmp.push_back(t & 255);
+ }
+ y = t = 0;
+ }
+ }
+ if (y != 0) {
+ *dst_size = 0;
+ return nullptr;
+ }
+ std::unique_ptr<uint8_t[]> dst(new uint8_t[tmp.size()]);
+ *dst_size = tmp.size();
+ std::copy(tmp.begin(), tmp.end(), dst.get());
+ return dst.release();
+}
+
+bool WriteBase64ToFile(const char* base64, const std::string& file,
+ uid_t uid, gid_t gid, int mode) {
+ CHECK(base64 != nullptr);
+ size_t length;
+ std::unique_ptr<uint8_t[]> bytes(DecodeBase64(base64, &length));
+ CHECK(bytes != nullptr);
+
+
+ int fd = open(file.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
+
+ if (fd < 0) {
+ PLOG(ERROR) << "Could not open file " << file;
+ return false;
+ }
+
+ size_t wrote = 0;
+ while (wrote < length) {
+ ssize_t cur = write(fd, bytes.get() + wrote, length - wrote);
+ if (cur == -1) {
+ PLOG(ERROR) << "Could not write file " << file;
+ return false;
+ }
+ wrote += cur;
+ }
+
+ if (::chown(file.c_str(), uid, gid) != 0) {
+ PLOG(ERROR) << "Could not chown file " << file;
+ return false;
+ }
+ if (::chmod(file.c_str(), mode) != 0) {
+ PLOG(ERROR) << "Could not chmod file " << file;
+ return false;
+ }
+ return true;
+}
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index ca0a82e066..7dca7c6422 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -87,6 +87,20 @@ std::string create_data_user_ce_package_path(const char* volume_uuid,
create_data_user_ce_path(volume_uuid, user).c_str(), package_name);
}
+/**
+ * Create the path name where package data should be stored for the given
+ * volume UUID, package name, and user ID. An empty UUID is assumed to be
+ * internal storage.
+ * Compared to create_data_user_ce_package_path this method always return the
+ * ".../user/..." directory.
+ */
+std::string create_data_user_ce_package_path_as_user_link(
+ const char* volume_uuid, userid_t userid, const char* package_name) {
+ check_package_name(package_name);
+ std::string data(create_data_path(volume_uuid));
+ return StringPrintf("%s/user/%u/%s", data.c_str(), userid, package_name);
+}
+
std::string create_data_user_ce_package_path(const char* volume_uuid, userid_t user,
const char* package_name, ino_t ce_data_inode) {
// For testing purposes, rely on the inode when defined; this could be
@@ -786,7 +800,7 @@ int validate_system_app_path(const char* path) {
}
bool validate_secondary_dex_path(const std::string& pkgname, const std::string& dex_path,
- const char* volume_uuid, int uid, int storage_flag, bool validate_package_path) {
+ const char* volume_uuid, int uid, int storage_flag) {
CHECK(storage_flag == FLAG_STORAGE_CE || storage_flag == FLAG_STORAGE_DE);
// Empty paths are not allowed.
@@ -800,16 +814,20 @@ bool validate_secondary_dex_path(const std::string& pkgname, const std::string&
// The path should be at most PKG_PATH_MAX long.
if (dex_path.size() > PKG_PATH_MAX) { return false; }
- if (validate_package_path) {
- // If we are asked to validate the package path check that
- // the dex_path is under the app data directory.
- std::string app_private_dir = storage_flag == FLAG_STORAGE_CE
+ // The dex_path should be under the app data directory.
+ std::string app_private_dir = storage_flag == FLAG_STORAGE_CE
? create_data_user_ce_package_path(
volume_uuid, multiuser_get_user_id(uid), pkgname.c_str())
: create_data_user_de_package_path(
volume_uuid, multiuser_get_user_id(uid), pkgname.c_str());
- if (strncmp(dex_path.c_str(), app_private_dir.c_str(), app_private_dir.size()) != 0) {
+ if (strncmp(dex_path.c_str(), app_private_dir.c_str(), app_private_dir.size()) != 0) {
+ // The check above might fail if the dex file is accessed via the /data/user/0 symlink.
+ // If that's the case, attempt to validate against the user data link.
+ std::string app_private_dir_symlink = create_data_user_ce_package_path_as_user_link(
+ volume_uuid, multiuser_get_user_id(uid), pkgname.c_str());
+ if (strncmp(dex_path.c_str(), app_private_dir_symlink.c_str(),
+ app_private_dir_symlink.size()) != 0) {
return false;
}
}
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 3e04af9178..b90caf9915 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -60,6 +60,8 @@ std::string create_data_user_ce_package_path(const char* volume_uuid,
userid_t user, const char* package_name, ino_t ce_data_inode);
std::string create_data_user_de_package_path(const char* volume_uuid,
userid_t user, const char* package_name);
+std::string create_data_user_ce_package_path_as_user_link(
+ const char* volume_uuid, userid_t userid, const char* package_name);
std::string create_data_media_path(const char* volume_uuid, userid_t userid);
std::string create_data_media_obb_path(const char* volume_uuid, const char* package_name);
@@ -114,7 +116,7 @@ void remove_path_xattr(const std::string& path, const char* inode_xattr);
int validate_system_app_path(const char* path);
bool validate_secondary_dex_path(const std::string& pkgname, const std::string& dex_path,
- const char* volume_uuid, int uid, int storage_flag, bool validate_package_path = true);
+ const char* volume_uuid, int uid, int storage_flag);
int validate_apk_path(const char *path);
int validate_apk_path_subdirs(const char *path);