Blaze launcher: extract install_base_key to disk.
Check that it matches before using an existing install_base.

RELNOTES: None.
PiperOrigin-RevId: 279524481
diff --git a/src/main/cpp/archive_utils.cc b/src/main/cpp/archive_utils.cc
index 78395eb..aae4957 100644
--- a/src/main/cpp/archive_utils.cc
+++ b/src/main/cpp/archive_utils.cc
@@ -38,7 +38,7 @@
   // Scan the zip file "archive_path" until a file named "stop_entry" is seen,
   // then stop.
   // If entry_names is not null, it receives a list of all file members
-  // preceding "stop_entry".
+  // up to and including "stop_entry".
   // If a callback is given, it is run with the name and contents of
   // each such member.
   // Returns the contents of the "stop_entry" member.
@@ -73,23 +73,21 @@
   }
 
   bool Accept(const char *filename, devtools_ijar::u4 attr) override {
-    string fn = filename;
-    if (stop_name_ == fn) {
-      done_ = true;
-      return true;  // save content in stop_value_
-    }
     if (devtools_ijar::zipattr_is_dir(attr)) return false;
     // Sometimes that fails to detect directories.  Check the name too.
+    string fn = filename;
     if (fn.empty() || fn.back() == '/') return false;
+    if (stop_name_ == fn) done_ = true;
     seen_names_.push_back(std::move(fn));
-    return !!callback_;  // true if a callback was supplied
+    return done_ || !!callback_;  // true if a callback was supplied
   }
 
   void Process(const char *filename, devtools_ijar::u4 attr,
                const devtools_ijar::u1 *data, size_t size) override {
     if (done_) {
       stop_value_.assign(reinterpret_cast<const char *>(data), size);
-    } else {
+    }
+    if (callback_) {
       callback_(filename, reinterpret_cast<const char *>(data), size);
     }
   }
diff --git a/src/main/cpp/blaze.cc b/src/main/cpp/blaze.cc
index b8adee9..3334fef 100644
--- a/src/main/cpp/blaze.cc
+++ b/src/main/cpp/blaze.cc
@@ -950,12 +950,12 @@
                                   const string &expected_install_md5,
                                   const StartupOptions &startup_options,
                                   LoggingInfo *logging_info) {
+  const string &install_base = startup_options.install_base;
   // If the install dir doesn't exist, create it, if it does, we know it's good.
-  if (!blaze_util::PathExists(startup_options.install_base)) {
+  if (!blaze_util::PathExists(install_base)) {
     uint64_t st = GetMillisecondsMonotonic();
     // Work in a temp dir to avoid races.
-    string tmp_install = startup_options.install_base + ".tmp." +
-                         blaze::GetProcessIdAsString();
+    string tmp_install = install_base + ".tmp." + blaze::GetProcessIdAsString();
     ExtractArchiveOrDie(self_path, startup_options.product_name,
                         expected_install_md5, tmp_install);
     BlessFiles(tmp_install);
@@ -966,8 +966,7 @@
     // Now rename the completed installation to its final name.
     int attempts = 0;
     while (attempts < 120) {
-      int result = blaze_util::RenameDirectory(
-          tmp_install.c_str(), startup_options.install_base.c_str());
+      int result = blaze_util::RenameDirectory(tmp_install, install_base);
       if (result == blaze_util::kRenameDirectorySuccess ||
           result == blaze_util::kRenameDirectoryFailureNotEmpty) {
         // If renaming fails because the directory already exists and is not
@@ -993,25 +992,44 @@
     }
     return extract_data_duration;
   } else {
-    if (!blaze_util::IsDirectory(startup_options.install_base)) {
+    // This would be detected implicitly below, but checking explicitly lets
+    // us give a better error message.
+    if (!blaze_util::IsDirectory(install_base)) {
       BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
-          << "install base directory '" << startup_options.install_base
+          << "install base directory '" << install_base
           << "' could not be created. It exists but is not a directory.";
     }
-
+    blaze_util::Path install_dir(install_base);
+    // Check that all files are present and have timestamps from BlessFiles().
     std::unique_ptr<blaze_util::IFileMtime> mtime(
         blaze_util::CreateFileMtime());
-    blaze_util::Path real_install_dir =
-        blaze_util::Path(startup_options.install_base);
     for (const auto &it : archive_contents) {
-      blaze_util::Path path = real_install_dir.GetRelative(it);
+      blaze_util::Path path = install_dir.GetRelative(it);
       if (!mtime->IsUntampered(path)) {
         BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
             << "corrupt installation: file '" << path.AsPrintablePath()
-            << "' is missing or modified.  Please remove '"
-            << startup_options.install_base << "' and try again.";
+            << "' is missing or modified.  Please remove '" << install_base
+            << "' and try again.";
       }
     }
+    // Also check that the installed files claim to match this binary.
+    // We check this afterward because the above diagnostic is better
+    // for a missing install_base_key file.
+    blaze_util::Path key_path = install_dir.GetRelative("install_base_key");
+    string on_disk_key;
+    if (!blaze_util::ReadFile(key_path, &on_disk_key)) {
+      BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
+          << "cannot read '" << key_path.AsPrintablePath()
+          << "': " << GetLastErrorString();
+    }
+    if (on_disk_key != expected_install_md5) {
+      BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
+          << "The install_base directory '" << install_base
+          << "' contains a different " << startup_options.product_name
+          << " version (found " << on_disk_key << " but this binary is "
+          << expected_install_md5
+          << ").  Remove it or specify a different --install_base.";
+    }
     return DurationMillis();
   }
 }