Support sparse images in map_file_generator

Some targets sparse system images in $OUT dir, when such images are
detected, unsparse them using libsparse.

Test: th, map_file_generator on a sparse image
Bug: 281960439
Change-Id: I3582c6ace36205079050f137aa17afdea682bca3
diff --git a/aosp/map_file_generator.cc b/aosp/map_file_generator.cc
index 1943a66..c4c9b51 100644
--- a/aosp/map_file_generator.cc
+++ b/aosp/map_file_generator.cc
@@ -14,8 +14,14 @@
 // limitations under the License.
 //
 
-#include "android-base/stringprintf.h"
-#include "android-base/unique_fd.h"
+#include <array>
+
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <fcntl.h>
+#include <sparse/sparse.h>
+
+#include "android-base/file.h"
 #include "common/utils.h"
 #include "update_engine/payload_generator/ext2_filesystem.h"
 #include "update_engine/payload_generator/erofs_filesystem.h"
@@ -67,9 +73,38 @@
   return 0;
 }
 
+bool IsSparseImage(int fd) {
+  static constexpr std::string_view kSparseMagic = "\x3A\xFF\x26\xED";
+
+  std::array<char, kSparseMagic.size()> buf{};
+  if (pread(fd, buf.data(), kSparseMagic.size(), 0) != 4) {
+    return false;
+  }
+  return memcmp(buf.data(), kSparseMagic.data(), kSparseMagic.size()) == 0;
+}
+
 int Main(int argc, const char* argv[]) {
   const char* img = argv[1];
   const char* output_file = argv[2];
+  android::base::unique_fd fd(open(img, O_RDONLY | O_CLOEXEC));
+  if (!fd.ok()) {
+    PLOG(ERROR) << "Failed to open " << img;
+    return -errno;
+  }
+  TemporaryFile tmpfile;
+  if (IsSparseImage(fd.get())) {
+    LOG(INFO) << "Detected sparse image " << img << ", unsparsing...";
+    struct sparse_file* s = sparse_file_import(fd, true, false);
+    if (s == nullptr) {
+      LOG(ERROR) << "Failed to unsparse " << img;
+      return -2;
+    }
+    if (sparse_file_write(s, tmpfile.fd, false, false, false) < 0) {
+      LOG(ERROR) << "Failed to write unsparsed output to " << tmpfile.path;
+      return -1;
+    }
+    img = tmpfile.path;
+  }
   std::unique_ptr<FilesystemInterface> fs;
   fs = ErofsFilesystem::CreateFromFile(img);
   if (fs != nullptr) {