Reimplement install_zip.sh in fetch_cvd.

Converts android sparse images to regular image files, and unsparses
image files.

Based on install_zip.sh here:
https://github.com/google/android-cuttlefish/blob/02da94513b6bb9dd534c4e54f81d49df5a050621/host/deploy/install_zip.sh

install_zip.sh also delegates to unpack_boot_imgage.py. This doesn't
seem immediately necessary to implement considering the launcher already
has an implementation that looks like a conversion of the script:
https://github.com/google/android-cuttlefish/blob/9e8ffef/host/deploy/unpack_boot_image.py
https://android.googlesource.com/device/google/cuttlefish_common/+/e13a78a/host/commands/launch/boot_image_unpacker.cc

Bug: 137304531
Test: ./fetch_cvd -run -- -daemon
Change-Id: I9dc35e9a5862a601abceea8634611e098fe301d4
diff --git a/host/commands/fetcher/Android.bp b/host/commands/fetcher/Android.bp
index 0c88171..4ab6178 100644
--- a/host/commands/fetcher/Android.bp
+++ b/host/commands/fetcher/Android.bp
@@ -19,6 +19,7 @@
         "build_api.cc",
         "credential_source.cc",
         "curl_wrapper.cc",
+        "install_zip.cc",
         "main.cc",
     ],
     header_libs: [
@@ -29,6 +30,7 @@
         "cuttlefish_auto_resources",
         "libbase",
         "libcuttlefish_fs",
+        "libcuttlefish_strings",
         "libcuttlefish_utils",
         "libcurl",
         "libcrypto",
diff --git a/host/commands/fetcher/install_zip.cc b/host/commands/fetcher/install_zip.cc
new file mode 100644
index 0000000..ad7f08c
--- /dev/null
+++ b/host/commands/fetcher/install_zip.cc
@@ -0,0 +1,96 @@
+//
+// Copyright (C) 2019 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 "install_zip.h"
+
+#include <stdlib.h>
+
+#include <string>
+#include <vector>
+
+#include <glog/logging.h>
+
+#include "common/libs/strings/str_split.h"
+#include "common/libs/utils/subprocess.h"
+
+namespace {
+
+std::vector<std::string> ArchiveContents(const std::string& archive) {
+  std::string bsdtar_output;
+  auto bsdtar_ret =
+      cvd::execute_capture_output({"/usr/bin/bsdtar", "-tf", archive},
+                                  &bsdtar_output);
+  return bsdtar_ret == 0
+      ? cvd::StrSplit(bsdtar_output, '\n')
+      : std::vector<std::string>();
+}
+
+} // namespace
+
+bool ExtractImages(const std::string& archive,
+                   const std::string& target_directory,
+                   const std::vector<std::string>& images) {
+  std::vector<std::string> bsdtar_cmd = {
+      "/usr/bin/bsdtar",
+      "-x",
+      "-v",
+      "-C", target_directory,
+      "-f", archive,
+      "-S",
+  };
+  for (const auto& img : images) {
+    bsdtar_cmd.push_back(img);
+  }
+  auto bsdtar_ret = cvd::execute(bsdtar_cmd);
+  if (bsdtar_ret != 0) {
+    LOG(ERROR) << "Unable to extract images. bsdtar returned " << bsdtar_ret;
+    return false;
+  }
+
+  bool extraction_success = true;
+  std::vector<std::string> files =
+      images.size() > 0 ? images : ArchiveContents(archive);
+  for (const auto& file : files) {
+    if (file.find(".img") == std::string::npos) {
+      continue;
+    }
+    std::string extracted_file = target_directory + "/" + file;
+
+    std::string file_output;
+    auto file_ret = cvd::execute_capture_output(
+      {"/usr/bin/file", extracted_file}, &file_output);
+    if (file_ret != 0) {
+      LOG(ERROR) << "Unable to run file on " << file << ", returned" << file_ret;
+      extraction_success = false;
+      continue;
+    }
+    if (file_output.find("Android sparse image,") == std::string::npos) {
+      continue;
+    }
+    std::string inflated_file = extracted_file + ".inflated";
+    auto simg_ret = cvd::execute({"/usr/bin/simg2img", extracted_file, inflated_file});
+    if (simg_ret != 0) {
+      LOG(ERROR) << "Unable to run simg2img on " << file;
+      extraction_success = false;
+      continue;
+    }
+    auto rename_ret = rename(inflated_file.c_str(), extracted_file.c_str());
+    if (rename_ret != 0) {
+      LOG(ERROR) << "Unable to rename deflated version of " << file;
+      extraction_success = false;
+    }
+  }
+  return extraction_success;
+}
diff --git a/host/commands/fetcher/install_zip.h b/host/commands/fetcher/install_zip.h
new file mode 100644
index 0000000..0e36528
--- /dev/null
+++ b/host/commands/fetcher/install_zip.h
@@ -0,0 +1,23 @@
+//
+// Copyright (C) 2019 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.
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+bool ExtractImages(const std::string& archive,
+                   const std::string& target_directory,
+                   const std::vector<std::string>& images);
diff --git a/host/commands/fetcher/main.cc b/host/commands/fetcher/main.cc
index 3e555e5..d5c1cac 100644
--- a/host/commands/fetcher/main.cc
+++ b/host/commands/fetcher/main.cc
@@ -26,6 +26,7 @@
 
 #include "build_api.h"
 #include "credential_source.h"
+#include "install_zip.h"
 
 // TODO(schuffelen): Mixed builds.
 DEFINE_string(build_id, "latest", "Build ID for all artifacts");
@@ -72,18 +73,10 @@
   std::string local_path = target_directory + "/" + img_zip_name;
   build_api->ArtifactToFile(build_id, target, "latest",
                             img_zip_name, local_path);
-  // -o for "overwrite"
-  std::vector<std::string> command = {"/usr/bin/unzip", "-o", local_path,
-                                      "-d", target_directory};
-  for (const auto& image_file : images) {
-    command.push_back(image_file);
-  }
-  int result = cvd::execute(command);
-  if (result != 0) {
-    LOG(ERROR) << "Could not extract " << local_path << "; ran command";
-    for (const auto& argument : command) {
-      LOG(ERROR) << argument;
-    }
+
+  auto could_extract = ExtractImages(local_path, target_directory, images);
+  if (!could_extract) {
+    LOG(ERROR) << "Could not extract " << local_path;
     return false;
   }
   if (unlink(local_path.c_str()) != 0) {
@@ -122,6 +115,15 @@
   return true;
 }
 
+bool desparse(const std::string& file) {
+  LOG(INFO) << "Unsparsing " << file;
+  if (cvd::execute({"/bin/dd", "if=" + file, "of=" + file, "conv=notrunc"}) != 0) {
+    LOG(ERROR) << "Could not unsparse " << file;
+    return false;
+  }
+  return true;
+}
+
 } // namespace
 
 int main(int argc, char** argv) {
@@ -156,6 +158,7 @@
       LOG(FATAL) << "Could not download images with target "
           << FLAGS_target << " and build id " << build_id;
     }
+    desparse(target_dir + "/userdata.img");
     if (FLAGS_system_image_build_id != "") {
       std::string system_target = FLAGS_system_image_build_target == ""
           ? FLAGS_target