Merge changes Ie68aed2f,Ic40696b3

* changes:
  fastboot: Handle libsparse failures better.
  libsparse: Fix allocation failures on 32-bit systems.
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 0979fd2..e76cbc5 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -988,7 +988,7 @@
     }
 
     const int files = sparse_file_resparse(s, max_size, nullptr, 0);
-    if (files < 0) die("Failed to resparse");
+    if (files < 0) die("Failed to compute resparse boundaries");
 
     auto temp = std::make_unique<sparse_file*[]>(files);
     const int rv = sparse_file_resparse(s, max_size, temp.get(), files);
@@ -1057,6 +1057,10 @@
 
     if (sparse_file* s = sparse_file_import(fd.get(), false, false)) {
         buf->image_size = sparse_file_len(s, false, false);
+        if (buf->image_size < 0) {
+            LOG(ERROR) << "Could not compute length of sparse file";
+            return false;
+        }
         sparse_file_destroy(s);
     } else {
         buf->image_size = sz;
@@ -1172,15 +1176,6 @@
     return fb->GetVar("is-logical:" + partition, &value) == fastboot::SUCCESS && value == "yes";
 }
 
-static std::string fb_fix_numeric_var(std::string var) {
-    // Some bootloaders (angler, for example), send spurious leading whitespace.
-    var = android::base::Trim(var);
-    // Some bootloaders (hammerhead, for example) use implicit hex.
-    // This code used to use strtol with base 16.
-    if (!android::base::StartsWith(var, "0x")) var = "0x" + var;
-    return var;
-}
-
 static uint64_t get_partition_size(const std::string& partition) {
     std::string partition_size_str;
     if (fb->GetVar("partition-size:" + partition, &partition_size_str) != fastboot::SUCCESS) {
@@ -1252,6 +1247,9 @@
     for (size_t i = 0; i < files.size(); i++) {
         sparse_file* s = files[i].get();
         int64_t sz = sparse_file_len(s, true, false);
+        if (sz < 0) {
+            LOG(FATAL) << "Could not compute length of sparse image for " << partition;
+        }
         fb->FlashPartition(partition, s, sz, i + 1, files.size());
     }
 }
diff --git a/fastboot/task.cpp b/fastboot/task.cpp
index 29befb7..9268b29 100644
--- a/fastboot/task.cpp
+++ b/fastboot/task.cpp
@@ -19,6 +19,8 @@
 #include "filesystem.h"
 #include "super_flash_helper.h"
 
+#include <android-base/parseint.h>
+
 using namespace std::string_literals;
 FlashTask::FlashTask(const std::string& _slot, const std::string& _pname, const std::string& _fname,
                      const bool apply_vbmeta)
@@ -66,14 +68,18 @@
 
 FlashSuperLayoutTask::FlashSuperLayoutTask(const std::string& super_name,
                                            std::unique_ptr<SuperFlashHelper> helper,
-                                           SparsePtr sparse_layout)
+                                           SparsePtr sparse_layout, uint64_t super_size)
     : super_name_(super_name),
       helper_(std::move(helper)),
-      sparse_layout_(std::move(sparse_layout)) {}
+      sparse_layout_(std::move(sparse_layout)),
+      super_size_(super_size) {}
 
 void FlashSuperLayoutTask::Run() {
+    // Use the reported super partition size as the upper limit, rather than
+    // sparse_file_len, which (1) can fail and (2) is kind of expensive, since
+    // it will map in all of the embedded fds.
     std::vector<SparsePtr> files;
-    if (int limit = get_sparse_limit(sparse_file_len(sparse_layout_.get(), false, false))) {
+    if (int limit = get_sparse_limit(super_size_)) {
         files = resparse_file(sparse_layout_.get(), limit);
     } else {
         files.emplace_back(std::move(sparse_layout_));
@@ -107,12 +113,19 @@
     if (fp->fb->GetVar("super-partition-name", &super_name) != fastboot::SUCCESS) {
         super_name = "super";
     }
-    std::string partition_size_str;
 
+    uint64_t partition_size;
+    std::string partition_size_str;
     if (fp->fb->GetVar("partition-size:" + super_name, &partition_size_str) != fastboot::SUCCESS) {
         LOG(VERBOSE) << "Cannot optimize super flashing: could not determine super partition";
         return nullptr;
     }
+    partition_size_str = fb_fix_numeric_var(partition_size_str);
+    if (!android::base::ParseUint(partition_size_str, &partition_size)) {
+        LOG(VERBOSE) << "Could not parse " << super_name << " size: " << partition_size_str;
+        return nullptr;
+    }
+
     std::unique_ptr<SuperFlashHelper> helper = std::make_unique<SuperFlashHelper>(*fp->source);
     if (!helper->Open(fd)) {
         return nullptr;
@@ -136,7 +149,8 @@
     };
     os_images.erase(std::remove_if(os_images.begin(), os_images.end(), remove_if_callback),
                     os_images.end());
-    return std::make_unique<FlashSuperLayoutTask>(super_name, std::move(helper), std::move(s));
+    return std::make_unique<FlashSuperLayoutTask>(super_name, std::move(helper), std::move(s),
+                                                  partition_size);
 }
 
 UpdateSuperTask::UpdateSuperTask(FlashingPlan* fp) : fp_(fp) {}
diff --git a/fastboot/task.h b/fastboot/task.h
index 8aa4d2f..e89f85b 100644
--- a/fastboot/task.h
+++ b/fastboot/task.h
@@ -58,7 +58,7 @@
 class FlashSuperLayoutTask : public Task {
   public:
     FlashSuperLayoutTask(const std::string& super_name, std::unique_ptr<SuperFlashHelper> helper,
-                         SparsePtr sparse_layout);
+                         SparsePtr sparse_layout, uint64_t super_size);
     static std::unique_ptr<FlashSuperLayoutTask> Initialize(FlashingPlan* fp,
                                                             std::vector<ImageEntry>& os_images);
     using ImageEntry = std::pair<const Image*, std::string>;
@@ -68,6 +68,7 @@
     const std::string super_name_;
     std::unique_ptr<SuperFlashHelper> helper_;
     SparsePtr sparse_layout_;
+    uint64_t super_size_;
 };
 
 class UpdateSuperTask : public Task {
diff --git a/fastboot/util.cpp b/fastboot/util.cpp
index ded54a5..e03012a 100644
--- a/fastboot/util.cpp
+++ b/fastboot/util.cpp
@@ -33,6 +33,9 @@
 #include <sys/stat.h>
 #include <sys/time.h>
 
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+
 #include "util.h"
 
 using android::base::borrowed_fd;
@@ -106,3 +109,12 @@
     }
     return sb.st_size;
 }
+
+std::string fb_fix_numeric_var(std::string var) {
+    // Some bootloaders (angler, for example), send spurious leading whitespace.
+    var = android::base::Trim(var);
+    // Some bootloaders (hammerhead, for example) use implicit hex.
+    // This code used to use strtol with base 16.
+    if (!android::base::StartsWith(var, "0x")) var = "0x" + var;
+    return var;
+}
diff --git a/fastboot/util.h b/fastboot/util.h
index 290d0d5..fdbc1d6 100644
--- a/fastboot/util.h
+++ b/fastboot/util.h
@@ -30,6 +30,7 @@
                                const std::string& partition_name);
 bool is_sparse_file(android::base::borrowed_fd fd);
 int64_t get_file_size(android::base::borrowed_fd fd);
+std::string fb_fix_numeric_var(std::string var);
 
 class ImageSource {
   public:
diff --git a/libsparse/output_file.cpp b/libsparse/output_file.cpp
index cb5d730..08312e4 100644
--- a/libsparse/output_file.cpp
+++ b/libsparse/output_file.cpp
@@ -58,6 +58,8 @@
 
 #define container_of(inner, outer_t, elem) ((outer_t*)((char*)(inner)-offsetof(outer_t, elem)))
 
+static constexpr size_t kMaxMmapSize = 256 * 1024 * 1024;
+
 struct output_file_ops {
   int (*open)(struct output_file*, int fd);
   int (*skip)(struct output_file*, int64_t);
@@ -71,6 +73,7 @@
   int (*write_fill_chunk)(struct output_file* out, uint64_t len, uint32_t fill_val);
   int (*write_skip_chunk)(struct output_file* out, uint64_t len);
   int (*write_end_chunk)(struct output_file* out);
+  int (*write_fd_chunk)(struct output_file* out, uint64_t len, int fd, int64_t offset);
 };
 
 struct output_file {
@@ -318,6 +321,26 @@
   return 0;
 }
 
+template <typename T>
+static bool write_fd_chunk_range(int fd, int64_t offset, uint64_t len, T callback) {
+  uint64_t bytes_written = 0;
+  int64_t current_offset = offset;
+  while (bytes_written < len) {
+    size_t mmap_size = std::min(static_cast<uint64_t>(kMaxMmapSize), len - bytes_written);
+    auto m = android::base::MappedFile::FromFd(fd, current_offset, mmap_size, PROT_READ);
+    if (!m) {
+      error("failed to mmap region of length %zu", mmap_size);
+      return false;
+    }
+    if (!callback(m->data(), mmap_size)) {
+      return false;
+    }
+    bytes_written += mmap_size;
+    current_offset += mmap_size;
+  }
+  return true;
+}
+
 static int write_sparse_skip_chunk(struct output_file* out, uint64_t skip_len) {
   chunk_header_t chunk_header;
   int ret;
@@ -424,6 +447,61 @@
   return 0;
 }
 
+static int write_sparse_fd_chunk(struct output_file* out, uint64_t len, int fd, int64_t offset) {
+  chunk_header_t chunk_header;
+  uint64_t rnd_up_len, zero_len;
+  int ret;
+
+  /* Round up the data length to a multiple of the block size */
+  rnd_up_len = ALIGN(len, out->block_size);
+  zero_len = rnd_up_len - len;
+
+  /* Finally we can safely emit a chunk of data */
+  chunk_header.chunk_type = CHUNK_TYPE_RAW;
+  chunk_header.reserved1 = 0;
+  chunk_header.chunk_sz = rnd_up_len / out->block_size;
+  chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len;
+  ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
+
+  if (ret < 0) return -1;
+  bool ok = write_fd_chunk_range(fd, offset, len, [&ret, out](char* data, size_t size) -> bool {
+    ret = out->ops->write(out, data, size);
+    if (ret < 0) return false;
+    if (out->use_crc) {
+      out->crc32 = sparse_crc32(out->crc32, data, size);
+    }
+    return true;
+  });
+  if (!ok) return -1;
+  if (zero_len) {
+    uint64_t len = zero_len;
+    uint64_t write_len;
+    while (len) {
+      write_len = std::min(len, (uint64_t)FILL_ZERO_BUFSIZE);
+      ret = out->ops->write(out, out->zero_buf, write_len);
+      if (ret < 0) {
+        return ret;
+      }
+      len -= write_len;
+    }
+
+    if (out->use_crc) {
+      uint64_t len = zero_len;
+      uint64_t write_len;
+      while (len) {
+        write_len = std::min(len, (uint64_t)FILL_ZERO_BUFSIZE);
+        out->crc32 = sparse_crc32(out->crc32, out->zero_buf, write_len);
+        len -= write_len;
+      }
+    }
+  }
+
+  out->cur_out_ptr += rnd_up_len;
+  out->chunk_cnt++;
+
+  return 0;
+}
+
 int write_sparse_end_chunk(struct output_file* out) {
   chunk_header_t chunk_header;
   int ret;
@@ -454,6 +532,7 @@
     .write_fill_chunk = write_sparse_fill_chunk,
     .write_skip_chunk = write_sparse_skip_chunk,
     .write_end_chunk = write_sparse_end_chunk,
+    .write_fd_chunk = write_sparse_fd_chunk,
 };
 
 static int write_normal_data_chunk(struct output_file* out, uint64_t len, void* data) {
@@ -495,6 +574,23 @@
   return 0;
 }
 
+static int write_normal_fd_chunk(struct output_file* out, uint64_t len, int fd, int64_t offset) {
+  int ret;
+  uint64_t rnd_up_len = ALIGN(len, out->block_size);
+
+  bool ok = write_fd_chunk_range(fd, offset, len, [&ret, out](char* data, size_t size) -> bool {
+    ret = out->ops->write(out, data, size);
+    return ret >= 0;
+  });
+  if (!ok) return ret;
+
+  if (rnd_up_len > len) {
+    ret = out->ops->skip(out, rnd_up_len - len);
+  }
+
+  return ret;
+}
+
 static int write_normal_skip_chunk(struct output_file* out, uint64_t len) {
   return out->ops->skip(out, len);
 }
@@ -508,6 +604,7 @@
     .write_fill_chunk = write_normal_fill_chunk,
     .write_skip_chunk = write_normal_skip_chunk,
     .write_end_chunk = write_normal_end_chunk,
+    .write_fd_chunk = write_normal_fd_chunk,
 };
 
 void output_file_close(struct output_file* out) {
@@ -670,10 +767,7 @@
 }
 
 int write_fd_chunk(struct output_file* out, uint64_t len, int fd, int64_t offset) {
-  auto m = android::base::MappedFile::FromFd(fd, offset, len, PROT_READ);
-  if (!m) return -errno;
-
-  return out->sparse_ops->write_data_chunk(out, m->size(), m->data());
+  return out->sparse_ops->write_fd_chunk(out, len, fd, offset);
 }
 
 /* Write a contiguous region of data blocks from a file */