Merge "Track rename of adbd_system_binaries."
diff --git a/Android.bp b/Android.bp
index d6cd9cf..c0c58dd 100644
--- a/Android.bp
+++ b/Android.bp
@@ -72,6 +72,7 @@
     ],
 
     static_libs: [
+        "libc++fs",
         "libinstall",
         "librecovery_fastboot",
         "libminui",
@@ -95,6 +96,7 @@
     ],
 
     shared_libs: [
+        "libfusesideload",
         "librecovery_ui",
     ],
 }
diff --git a/install/include/install/install.h b/install/include/install/install.h
index 87d43ab..bef23e9 100644
--- a/install/include/install/install.h
+++ b/install/include/install/install.h
@@ -63,3 +63,7 @@
 // pre-device and serial number (if presents). A/B OTA specific checks: pre-build version,
 // fingerprint, timestamp.
 bool 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 56c1e68..d404997 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>
@@ -652,3 +653,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 b1f106b..f41ac6a 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -702,7 +702,7 @@
   }
 
   std::vector<std::string> title_lines =
-      android::base::Split(android::base::GetProperty("ro.bootimage.build.fingerprint", ""), ":");
+      android::base::Split(android::base::GetProperty("ro.build.fingerprint", ""), ":");
   title_lines.insert(std::begin(title_lines), "Android Recovery");
   ui->SetTitle(title_lines);
 
@@ -752,7 +752,11 @@
         ensure_path_mounted(update_package);
       }
 
-      if (install_with_fuse) {
+      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 (install_with_fuse || should_use_fuse) {
         LOG(INFO) << "Installing package " << update_package << " with fuse";
         status = InstallWithFuseFromPath(update_package, ui);
       } else if (auto memory_package = Package::CreateMemoryPackage(
diff --git a/tests/Android.bp b/tests/Android.bp
index 3d22390..a9a088a 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -92,6 +92,7 @@
     "libhidlbase",
     "liblp",
     "libtinyxml2",
+    "libc++fs",
 ]
 
 // recovery image for unittests.
diff --git a/tests/unit/install_test.cpp b/tests/unit/install_test.cpp
index 370fbdc..ee75349 100644
--- a/tests/unit/install_test.cpp
+++ b/tests/unit/install_test.cpp
@@ -35,6 +35,7 @@
 #include "install/wipe_device.h"
 #include "otautil/paths.h"
 #include "private/setup_commands.h"
+#include "recovery_utils/roots.h"
 
 static void BuildZipArchive(const std::map<std::string, std::string>& file_map, int fd,
                             int compression_type) {
@@ -513,3 +514,30 @@
       "\n");
   TestCheckPackageMetadata(metadata, OtaType::AB, true);
 }
+
+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));
+}