Snap for 6227550 from ba27adbbb6db81babc1ee3ac070ea6b6189dd2e1 to qt-d4-release

Change-Id: I54831e79405142f004f5f515c334d5c67495063d
diff --git a/Android.bp b/Android.bp
index f920782..f367e5e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -69,6 +69,7 @@
     ],
 
     static_libs: [
+        "libc++fs",
         "libinstall",
         "librecovery_fastboot",
         "libminui",
@@ -94,6 +95,7 @@
     ],
 
     shared_libs: [
+        "libfusesideload",
         "librecovery_ui",
     ],
 }
diff --git a/install/include/install/install.h b/install/include/install/install.h
index c0a8f1f..ed0f6c7 100644
--- a/install/include/install/install.h
+++ b/install/include/install/install.h
@@ -69,3 +69,7 @@
 // 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 e2d4700..9203ef0 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 @@
   }
   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 5fc673e..e51687a 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 @@
         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 09ef716..a0d82d5 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -99,6 +99,7 @@
     "liblp",
     "libvndksupport",
     "libtinyxml2",
+    "libc++fs",
 ]
 
 cc_test {
diff --git a/tests/component/install_test.cpp b/tests/component/install_test.cpp
index 3851329..c1f0ca8 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 @@
       "\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));
+}