summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--Android.bp2
-rw-r--r--install/include/install/install.h4
-rw-r--r--install/install.cpp47
-rw-r--r--recovery.cpp25
-rw-r--r--tests/Android.bp1
-rw-r--r--tests/component/install_test.cpp28
6 files changed, 106 insertions, 1 deletions
diff --git a/Android.bp b/Android.bp
index f92078256..f367e5e7d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -69,6 +69,7 @@ cc_defaults {
],
static_libs: [
+ "libc++fs",
"libinstall",
"librecovery_fastboot",
"libminui",
@@ -94,6 +95,7 @@ cc_library_static {
],
shared_libs: [
+ "libfusesideload",
"librecovery_ui",
],
}
diff --git a/install/include/install/install.h b/install/include/install/install.h
index c0a8f1f4c..ed0f6c7da 100644
--- a/install/include/install/install.h
+++ b/install/include/install/install.h
@@ -69,3 +69,7 @@ bool verify_package_compatibility(ZipArchiveHandle package_zip);
// Mandatory checks: ota-type, pre-device and serial number(if presents)
// AB OTA specific checks: pre-build version, fingerprint, timestamp.
int CheckPackageMetadata(const std::map<std::string, std::string>& metadata, OtaType ota_type);
+
+// Ensures the path to the update package is mounted. Also set the |should_use_fuse| to true if the
+// package stays on a removable media.
+bool SetupPackageMount(const std::string& package_path, bool* should_use_fuse);
diff --git a/install/install.cpp b/install/install.cpp
index e2d470096..9203ef0e5 100644
--- a/install/install.cpp
+++ b/install/install.cpp
@@ -30,6 +30,7 @@
#include <atomic>
#include <chrono>
#include <condition_variable>
+#include <filesystem>
#include <functional>
#include <limits>
#include <mutex>
@@ -736,3 +737,49 @@ bool verify_package(Package* package, RecoveryUI* ui) {
}
return true;
}
+
+bool SetupPackageMount(const std::string& package_path, bool* should_use_fuse) {
+ CHECK(should_use_fuse != nullptr);
+
+ if (package_path.empty()) {
+ return false;
+ }
+
+ *should_use_fuse = true;
+ if (package_path[0] == '@') {
+ auto block_map_path = package_path.substr(1);
+ if (ensure_path_mounted(block_map_path) != 0) {
+ LOG(ERROR) << "Failed to mount " << block_map_path;
+ return false;
+ }
+ // uncrypt only produces block map only if the package stays on /data.
+ *should_use_fuse = false;
+ return true;
+ }
+
+ // Package is not a block map file.
+ if (ensure_path_mounted(package_path) != 0) {
+ LOG(ERROR) << "Failed to mount " << package_path;
+ return false;
+ }
+
+ // Reject the package if the input path doesn't equal the canonicalized path.
+ // e.g. /cache/../sdcard/update_package.
+ std::error_code ec;
+ auto canonical_path = std::filesystem::canonical(package_path, ec);
+ if (ec) {
+ LOG(ERROR) << "Failed to get canonical of " << package_path << ", " << ec.message();
+ return false;
+ }
+ if (canonical_path.string() != package_path) {
+ LOG(ERROR) << "Installation aborts. The canonical path " << canonical_path.string()
+ << " doesn't equal the original path " << package_path;
+ return false;
+ }
+
+ constexpr const char* CACHE_ROOT = "/cache";
+ if (android::base::StartsWith(package_path, CACHE_ROOT)) {
+ *should_use_fuse = false;
+ }
+ return true;
+}
diff --git a/recovery.cpp b/recovery.cpp
index 5fc673ec2..e51687a7e 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -50,6 +50,7 @@
#include "common.h"
#include "fsck_unshare_blocks.h"
+#include "fuse_sideload.h"
#include "install/adb_install.h"
#include "install/fuse_sdcard_install.h"
#include "install/install.h"
@@ -881,7 +882,29 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri
set_retry_bootloader_message(retry_count + 1, args);
}
- status = install_package(update_package, should_wipe_cache, true, retry_count, ui);
+ bool should_use_fuse = false;
+ if (!SetupPackageMount(update_package, &should_use_fuse)) {
+ LOG(INFO) << "Failed to set up the package access, skipping installation";
+ status = INSTALL_ERROR;
+ } else if (should_use_fuse) {
+ LOG(INFO) << "Installing package " << update_package << " with fuse";
+ auto file_data_reader = std::make_unique<FuseFileDataProvider>(update_package, 65536);
+ status = run_fuse_sideload(std::move(file_data_reader));
+ } else if (auto memory_package = Package::CreateMemoryPackage(
+ update_package,
+ std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1));
+ memory_package != nullptr) {
+ status = install_package(update_package, should_wipe_cache, true, retry_count, ui);
+ } else {
+ // We may fail to memory map the package on 32 bit builds for packages with 2GiB+ size.
+ // In such cases, we will try to install the package with fuse. This is not the default
+ // installation method because it introduces a layer of indirection from the kernel space.
+ LOG(WARNING) << "Failed to memory map package " << update_package
+ << "; falling back to install with fuse";
+ auto file_data_reader = std::make_unique<FuseFileDataProvider>(update_package, 65536);
+ status = run_fuse_sideload(std::move(file_data_reader));
+ }
+
if (status != INSTALL_SUCCESS) {
ui->Print("Installation aborted.\n");
diff --git a/tests/Android.bp b/tests/Android.bp
index 09ef716d6..a0d82d5fd 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -99,6 +99,7 @@ librecovery_static_libs = [
"liblp",
"libvndksupport",
"libtinyxml2",
+ "libc++fs",
]
cc_test {
diff --git a/tests/component/install_test.cpp b/tests/component/install_test.cpp
index 385132939..c1f0ca812 100644
--- a/tests/component/install_test.cpp
+++ b/tests/component/install_test.cpp
@@ -34,6 +34,7 @@
#include "install/install.h"
#include "otautil/paths.h"
+#include "otautil/roots.h"
#include "private/setup_commands.h"
static void BuildZipArchive(const std::map<std::string, std::string>& file_map, int fd,
@@ -595,3 +596,30 @@ TEST(InstallTest, CheckPackageMetadata_ab_post_timestamp) {
"\n");
test_check_package_metadata(metadata, OtaType::AB, 0);
}
+
+TEST(InstallTest, SetupPackageMount_package_path) {
+ load_volume_table();
+ bool install_with_fuse;
+
+ // Setup should fail if the input path doesn't exist.
+ ASSERT_FALSE(SetupPackageMount("/does_not_exist", &install_with_fuse));
+
+ // Package should be installed with fuse if it's not in /cache.
+ TemporaryDir temp_dir;
+ TemporaryFile update_package(temp_dir.path);
+ ASSERT_TRUE(SetupPackageMount(update_package.path, &install_with_fuse));
+ ASSERT_TRUE(install_with_fuse);
+
+ // Setup should fail if the input path isn't canonicalized.
+ std::string uncanonical_package_path = android::base::Join(
+ std::vector<std::string>{
+ temp_dir.path,
+ "..",
+ android::base::Basename(temp_dir.path),
+ android::base::Basename(update_package.path),
+ },
+ '/');
+
+ ASSERT_EQ(0, access(uncanonical_package_path.c_str(), R_OK));
+ ASSERT_FALSE(SetupPackageMount(uncanonical_package_path, &install_with_fuse));
+}