Merge "Drop the dependency on 'ui' in verify_file()."
diff --git a/tests/Android.mk b/tests/Android.mk
index ec971b3..65f736d 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -81,7 +81,10 @@
 
 # Component tests
 include $(CLEAR_VARS)
-LOCAL_CFLAGS := -Werror
+LOCAL_CFLAGS := \
+    -Werror \
+    -D_FILE_OFFSET_BITS=64
+
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 LOCAL_MODULE := recovery_component_test
 LOCAL_C_INCLUDES := bootable/recovery
@@ -136,6 +139,10 @@
     libz \
     libbase \
     libtune2fs \
+    libfec \
+    libfec_rs \
+    libsquashfs_utils \
+    libcutils \
     $(tune2fs_static_libraries)
 
 testdata_files := $(call find-subdir-files, testdata/*)
diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp
index 8c4bdba..4f8349e 100644
--- a/tests/component/updater_test.cpp
+++ b/tests/component/updater_test.cpp
@@ -20,6 +20,7 @@
 #include <unistd.h>
 
 #include <string>
+#include <vector>
 
 #include <android-base/file.h>
 #include <android-base/properties.h>
@@ -27,12 +28,17 @@
 #include <android-base/strings.h>
 #include <android-base/test_utils.h>
 #include <bootloader_message/bootloader_message.h>
+#include <bsdiff.h>
 #include <gtest/gtest.h>
 #include <ziparchive/zip_archive.h>
+#include <ziparchive/zip_writer.h>
 
 #include "common/test_constants.h"
 #include "edify/expr.h"
 #include "error_code.h"
+#include "otautil/SysUtil.h"
+#include "print_sha1.h"
+#include "updater/blockimg.h"
 #include "updater/install.h"
 #include "updater/updater.h"
 
@@ -64,12 +70,19 @@
   ASSERT_EQ(cause_code, state.cause_code);
 }
 
+static std::string get_sha1(const std::string& content) {
+  uint8_t digest[SHA_DIGEST_LENGTH];
+  SHA1(reinterpret_cast<const uint8_t*>(content.c_str()), content.size(), digest);
+  return print_sha1(digest);
+}
+
 class UpdaterTest : public ::testing::Test {
-  protected:
-    virtual void SetUp() {
-        RegisterBuiltins();
-        RegisterInstallFunctions();
-    }
+ protected:
+  virtual void SetUp() override {
+    RegisterBuiltins();
+    RegisterInstallFunctions();
+    RegisterBlockImageFunctions();
+  }
 };
 
 TEST_F(UpdaterTest, getprop) {
@@ -447,3 +460,100 @@
   // recovery-updater protocol expects 3 tokens ("progress <frac> <secs>").
   ASSERT_EQ(3U, android::base::Split(cmd, " ").size());
 }
+
+TEST_F(UpdaterTest, block_image_update) {
+  // Create a zip file with new_data and patch_data.
+  TemporaryFile zip_file;
+  FILE* zip_file_ptr = fdopen(zip_file.fd, "wb");
+  ZipWriter zip_writer(zip_file_ptr);
+
+  // Add a dummy new data.
+  ASSERT_EQ(0, zip_writer.StartEntry("new_data", 0));
+  ASSERT_EQ(0, zip_writer.FinishEntry());
+
+  // Generate and add the patch data.
+  std::string src_content = std::string(4096, 'a') + std::string(4096, 'c');
+  std::string tgt_content = std::string(4096, 'b') + std::string(4096, 'd');
+  TemporaryFile patch_file;
+  ASSERT_EQ(0, bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(src_content.data()),
+      src_content.size(), reinterpret_cast<const uint8_t*>(tgt_content.data()),
+      tgt_content.size(), patch_file.path, nullptr));
+  std::string patch_content;
+  ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch_content));
+  ASSERT_EQ(0, zip_writer.StartEntry("patch_data", 0));
+  ASSERT_EQ(0, zip_writer.WriteBytes(patch_content.data(), patch_content.size()));
+  ASSERT_EQ(0, zip_writer.FinishEntry());
+
+  // Add two transfer lists. The first one contains a bsdiff; and we expect the update to succeed.
+  std::string src_hash = get_sha1(src_content);
+  std::string tgt_hash = get_sha1(tgt_content);
+  std::vector<std::string> transfer_list = {
+    "4",
+    "2",
+    "0",
+    "2",
+    "stash " + src_hash + " 2,0,2",
+    android::base::StringPrintf("bsdiff 0 %zu %s %s 2,0,2 2 - %s:2,0,2", patch_content.size(),
+                                src_hash.c_str(), tgt_hash.c_str(), src_hash.c_str()),
+    "free " + src_hash,
+  };
+  ASSERT_EQ(0, zip_writer.StartEntry("transfer_list", 0));
+  std::string commands = android::base::Join(transfer_list, '\n');
+  ASSERT_EQ(0, zip_writer.WriteBytes(commands.data(), commands.size()));
+  ASSERT_EQ(0, zip_writer.FinishEntry());
+
+  // Stash and free some blocks, then fail the 2nd update intentionally.
+  std::vector<std::string> fail_transfer_list = {
+    "4",
+    "2",
+    "0",
+    "2",
+    "stash " + tgt_hash + " 2,0,2",
+    "free " + tgt_hash,
+    "fail",
+  };
+  ASSERT_EQ(0, zip_writer.StartEntry("fail_transfer_list", 0));
+  std::string fail_commands = android::base::Join(fail_transfer_list, '\n');
+  ASSERT_EQ(0, zip_writer.WriteBytes(fail_commands.data(), fail_commands.size()));
+  ASSERT_EQ(0, zip_writer.FinishEntry());
+  ASSERT_EQ(0, zip_writer.Finish());
+  ASSERT_EQ(0, fclose(zip_file_ptr));
+
+  MemMapping map;
+  ASSERT_EQ(0, sysMapFile(zip_file.path, &map));
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_file.path, &handle));
+
+  // Set up the handler, command_pipe, patch offset & length.
+  UpdaterInfo updater_info;
+  updater_info.package_zip = handle;
+  TemporaryFile temp_pipe;
+  updater_info.cmd_pipe = fopen(temp_pipe.path, "wb");
+  updater_info.package_zip_addr = map.addr;
+  updater_info.package_zip_len = map.length;
+
+  // Execute the commands in the 1st transfer list.
+  TemporaryFile update_file;
+  ASSERT_TRUE(android::base::WriteStringToFile(src_content, update_file.path));
+  std::string script = "block_image_update(\"" + std::string(update_file.path) +
+      R"(", package_extract_file("transfer_list"), "new_data", "patch_data"))";
+  expect("t", script.c_str(), kNoCause, &updater_info);
+  // The update_file should be patched correctly.
+  std::string updated_content;
+  ASSERT_TRUE(android::base::ReadFileToString(update_file.path, &updated_content));
+  ASSERT_EQ(tgt_hash, get_sha1(updated_content));
+
+  // Expect the 2nd update to fail, but expect the stashed blocks to be freed.
+  script = "block_image_update(\"" + std::string(update_file.path) +
+      R"(", package_extract_file("fail_transfer_list"), "new_data", "patch_data"))";
+  expect("", script.c_str(), kNoCause, &updater_info);
+  // Updater generates the stash name based on the input file name.
+  std::string name_digest = get_sha1(update_file.path);
+  std::string stash_base = "/cache/recovery/" + name_digest;
+  ASSERT_EQ(0, access(stash_base.c_str(), F_OK));
+  ASSERT_EQ(-1, access((stash_base + tgt_hash).c_str(), F_OK));
+  ASSERT_EQ(0, rmdir(stash_base.c_str()));
+
+  ASSERT_EQ(0, fclose(updater_info.cmd_pipe));
+  CloseArchive(handle);
+}
diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp
index 6227154..0fa83d9 100644
--- a/updater/blockimg.cpp
+++ b/updater/blockimg.cpp
@@ -67,6 +67,21 @@
   size_t count;  // Limit is INT_MAX.
   size_t size;
   std::vector<size_t> pos;  // Actual limit is INT_MAX.
+
+  // Get the block number for the ith(starting from 0) block in the range set.
+  int get_block(size_t idx) const {
+    if (idx >= size) {
+      LOG(ERROR) << "index: " << idx << " is greater than range set size: " << size;
+      return -1;
+    }
+    for (size_t i = 0; i < pos.size(); i += 2) {
+      if (idx < pos[i + 1] - pos[i]) {
+        return pos[i] + idx;
+      }
+      idx -= (pos[i + 1] - pos[i]);
+    }
+    return -1;
+  }
 };
 
 static CauseCode failure_type = kNoCause;
@@ -442,6 +457,117 @@
     return rc;
 }
 
+// Print the hash in hex for corrupted source blocks (excluding the stashed blocks which is
+// handled separately).
+static void PrintHashForCorruptedSourceBlocks(const CommandParameters& params,
+                                              const std::vector<uint8_t>& buffer) {
+  LOG(INFO) << "unexpected contents of source blocks in cmd:\n" << params.cmdline;
+  if (params.version < 3) {
+    // TODO handle version 1,2
+    LOG(WARNING) << "version number " << params.version << " is not supported to print hashes";
+    return;
+  }
+
+  CHECK(params.tokens[0] == "move" || params.tokens[0] == "bsdiff" ||
+        params.tokens[0] == "imgdiff");
+
+  size_t pos = 0;
+  // Command example:
+  // move <onehash> <tgt_range> <src_blk_count> <src_range> [<loc_range> <stashed_blocks>]
+  // bsdiff <offset> <len> <src_hash> <tgt_hash> <tgt_range> <src_blk_count> <src_range>
+  //        [<loc_range> <stashed_blocks>]
+  if (params.tokens[0] == "move") {
+    // src_range for move starts at the 4th position.
+    if (params.tokens.size() < 5) {
+      LOG(ERROR) << "failed to parse source range in cmd:\n" << params.cmdline;
+      return;
+    }
+    pos = 4;
+  } else {
+    // src_range for diff starts at the 7th position.
+    if (params.tokens.size() < 8) {
+      LOG(ERROR) << "failed to parse source range in cmd:\n" << params.cmdline;
+      return;
+    }
+    pos = 7;
+  }
+
+  // Source blocks in stash only, no work to do.
+  if (params.tokens[pos] == "-") {
+    return;
+  }
+
+  RangeSet src = parse_range(params.tokens[pos++]);
+
+  RangeSet locs;
+  // If there's no stashed blocks, content in the buffer is consecutive and has the same
+  // order as the source blocks.
+  if (pos == params.tokens.size()) {
+    locs.count = 1;
+    locs.size = src.size;
+    locs.pos = { 0, src.size };
+  } else {
+    // Otherwise, the next token is the offset of the source blocks in the target range.
+    // Example: for the tokens <4,63946,63947,63948,63979> <4,6,7,8,39> <stashed_blocks>;
+    // We want to print SHA-1 for the data in buffer[6], buffer[8], buffer[9] ... buffer[38];
+    // this corresponds to the 32 src blocks #63946, #63948, #63949 ... #63978.
+    locs = parse_range(params.tokens[pos++]);
+    CHECK_EQ(src.size, locs.size);
+    CHECK_EQ(locs.pos.size() % 2, static_cast<size_t>(0));
+  }
+
+  LOG(INFO) << "printing hash in hex for " << src.size << " source blocks";
+  for (size_t i = 0; i < src.size; i++) {
+    int block_num = src.get_block(i);
+    CHECK_NE(block_num, -1);
+    int buffer_index = locs.get_block(i);
+    CHECK_NE(buffer_index, -1);
+    CHECK_LE((buffer_index + 1) * BLOCKSIZE, buffer.size());
+
+    uint8_t digest[SHA_DIGEST_LENGTH];
+    SHA1(buffer.data() + buffer_index * BLOCKSIZE, BLOCKSIZE, digest);
+    std::string hexdigest = print_sha1(digest);
+    LOG(INFO) << "  block number: " << block_num << ", SHA-1: " << hexdigest;
+  }
+}
+
+// If the calculated hash for the whole stash doesn't match the stash id, print the SHA-1
+// in hex for each block.
+static void PrintHashForCorruptedStashedBlocks(const std::string& id,
+                                               const std::vector<uint8_t>& buffer,
+                                               const RangeSet& src) {
+  LOG(INFO) << "printing hash in hex for stash_id: " << id;
+  CHECK_EQ(src.size * BLOCKSIZE, buffer.size());
+
+  for (size_t i = 0; i < src.size; i++) {
+    int block_num = src.get_block(i);
+    CHECK_NE(block_num, -1);
+
+    uint8_t digest[SHA_DIGEST_LENGTH];
+    SHA1(buffer.data() + i * BLOCKSIZE, BLOCKSIZE, digest);
+    std::string hexdigest = print_sha1(digest);
+    LOG(INFO) << "  block number: " << block_num << ", SHA-1: " << hexdigest;
+  }
+}
+
+// If the stash file doesn't exist, read the source blocks this stash contains and print the
+// SHA-1 for these blocks.
+static void PrintHashForMissingStashedBlocks(const std::string& id, int fd) {
+  if (stash_map.find(id) == stash_map.end()) {
+    LOG(ERROR) << "No stash saved for id: " << id;
+    return;
+  }
+
+  LOG(INFO) << "print hash in hex for source blocks in missing stash: " << id;
+  const RangeSet& src = stash_map[id];
+  std::vector<uint8_t> buffer(src.size * BLOCKSIZE);
+  if (ReadBlocks(src, buffer, fd) == -1) {
+      LOG(ERROR) << "failed to read source blocks for stash: " << id;
+      return;
+  }
+  PrintHashForCorruptedStashedBlocks(id, buffer, src);
+}
+
 static int VerifyBlocks(const std::string& expected, const std::vector<uint8_t>& buffer,
         const size_t blocks, bool printerror) {
     uint8_t digest[SHA_DIGEST_LENGTH];
@@ -540,6 +666,7 @@
             }
             if (VerifyBlocks(id, buffer, src.size, true) != 0) {
                 LOG(ERROR) << "failed to verify loaded source blocks in stash map.";
+                PrintHashForCorruptedStashedBlocks(id, buffer, src);
                 return -1;
             }
             return 0;
@@ -564,6 +691,7 @@
     if (res == -1) {
         if (errno != ENOENT || printnoent) {
             PLOG(ERROR) << "stat \"" << fn << "\" failed";
+            PrintHashForMissingStashedBlocks(id, params.fd);
         }
         return -1;
     }
@@ -591,6 +719,13 @@
 
     if (verify && VerifyBlocks(id, buffer, *blocks, true) != 0) {
         LOG(ERROR) << "unexpected contents in " << fn;
+        if (stash_map.find(id) == stash_map.end()) {
+            LOG(ERROR) << "failed to find source blocks number for stash " << id
+                       << " when executing command: " << params.cmdname;
+        } else {
+            const RangeSet& src = stash_map[id];
+            PrintHashForCorruptedStashedBlocks(id, buffer, src);
+        }
         DeleteFile(fn);
         return -1;
     }
@@ -773,6 +908,7 @@
         return -1;
     }
     blocks = src.size;
+    stash_map[id] = src;
 
     if (usehash && VerifyBlocks(id, buffer, blocks, true) != 0) {
         // Source blocks have unexpected contents. If we actually need this
@@ -783,9 +919,8 @@
         return 0;
     }
 
-    // In verify mode, save source range_set instead of stashing blocks.
+    // In verify mode, we don't need to stash any blocks.
     if (!params.canwrite && usehash) {
-        stash_map[id] = src;
         return 0;
     }
 
@@ -1003,6 +1138,8 @@
 
     // Valid source data not available, update cannot be resumed
     LOG(ERROR) << "partition has unexpected contents";
+    PrintHashForCorruptedSourceBlocks(params, params.buffer);
+
     params.isunresumable = true;
 
     return -1;
@@ -1071,9 +1208,8 @@
 
     const std::string& id = params.tokens[params.cpos++];
 
-    if (!params.canwrite && stash_map.find(id) != stash_map.end()) {
+    if (stash_map.find(id) != stash_map.end()) {
         stash_map.erase(id);
-        return 0;
     }
 
     if (params.createdstash || params.canwrite) {
@@ -1215,10 +1351,8 @@
     if (params.canwrite) {
         if (status == 0) {
             LOG(INFO) << "patching " << blocks << " blocks to " << tgt.size;
-
             Value patch_value(VAL_BLOB,
                     std::string(reinterpret_cast<const char*>(params.patch_start + offset), len));
-
             RangeSinkState rss(tgt);
             rss.fd = params.fd;
             rss.p_block = 0;