Add integration test for XOR ops

Test: th
Bug: 201099341
Change-Id: I36f4c2da25f23b3dcdfa0852a9dc01f896a292f1
diff --git a/common/utils.cc b/common/utils.cc
index 7a4a836..e47b70b 100644
--- a/common/utils.cc
+++ b/common/utils.cc
@@ -397,6 +397,19 @@
   return size;
 }
 
+bool SendFile(int out_fd, int in_fd, size_t count) {
+  off64_t offset = lseek(in_fd, 0, SEEK_CUR);
+  TEST_AND_RETURN_FALSE_ERRNO(offset >= 0);
+  constexpr size_t BUFFER_SIZE = 4096;
+  while (count > 0) {
+    const auto bytes_written =
+        sendfile(out_fd, in_fd, &offset, std::min(count, BUFFER_SIZE));
+    TEST_AND_RETURN_FALSE_ERRNO(bytes_written > 0);
+    count -= bytes_written;
+  }
+  return true;
+}
+
 void HexDumpArray(const uint8_t* const arr, const size_t length) {
   LOG(INFO) << "Logging array of length: " << length;
   const unsigned int bytes_per_line = 16;
@@ -892,6 +905,34 @@
   return false;
 }
 
+bool ReadExtents(const std::string& path,
+                 const google::protobuf::RepeatedPtrField<Extent>& extents,
+                 brillo::Blob* out_data,
+                 size_t block_size) {
+  return ReadExtents(path,
+                     {extents.begin(), extents.end()},
+                     out_data,
+                     utils::BlocksInExtents(extents) * block_size,
+                     block_size);
+}
+
+bool WriteExtents(const std::string& path,
+                  const google::protobuf::RepeatedPtrField<Extent>& extents,
+                  const brillo::Blob& data,
+                  size_t block_size) {
+  EintrSafeFileDescriptor fd;
+  TEST_AND_RETURN_FALSE(fd.Open(path.c_str(), O_RDWR));
+  size_t bytes_written = 0;
+  for (const auto& ext : extents) {
+    TEST_AND_RETURN_FALSE_ERRNO(
+        fd.Seek(ext.start_block() * block_size, SEEK_SET));
+    TEST_AND_RETURN_FALSE_ERRNO(
+        fd.Write(data.data() + bytes_written, ext.num_blocks() * block_size));
+    bytes_written += ext.num_blocks() * block_size;
+  }
+  return true;
+}
+
 bool ReadExtents(const string& path,
                  const vector<Extent>& extents,
                  brillo::Blob* out_data,
@@ -906,7 +947,7 @@
   for (const Extent& extent : extents) {
     ssize_t bytes_read_this_iteration = 0;
     ssize_t bytes = extent.num_blocks() * block_size;
-    TEST_AND_RETURN_FALSE(bytes_read + bytes <= out_data_size);
+    TEST_LE(bytes_read + bytes, out_data_size);
     TEST_AND_RETURN_FALSE(utils::PReadAll(fd,
                                           &data[bytes_read],
                                           bytes,
diff --git a/common/utils.h b/common/utils.h
index 50b6cb1..4ff4050 100644
--- a/common/utils.h
+++ b/common/utils.h
@@ -38,6 +38,7 @@
 #include <brillo/secure_blob.h>
 
 #include "android-base/mapped_file.h"
+#include "google/protobuf/repeated_field.h"
 #include "update_engine/common/action.h"
 #include "update_engine/common/action_processor.h"
 #include "update_engine/common/constants.h"
@@ -137,6 +138,8 @@
 off_t FileSize(const std::string& path);
 off_t FileSize(int fd);
 
+bool SendFile(int out_fd, int in_fd, size_t count);
+
 std::string ErrnoNumberAsString(int err);
 
 // Returns true if the file exists for sure. Returns false if it doesn't exist,
@@ -308,6 +311,27 @@
                  ssize_t out_data_size,
                  size_t block_size);
 
+bool WriteExtents(const std::string& path,
+                  const google::protobuf::RepeatedPtrField<Extent>& extents,
+                  const brillo::Blob& data,
+                  size_t block_size);
+
+constexpr bool ReadExtents(const std::string& path,
+                           const std::vector<Extent>& extents,
+                           brillo::Blob* out_data,
+                           size_t block_size) {
+  return ReadExtents(path,
+                     extents,
+                     out_data,
+                     utils::BlocksInExtents(extents) * block_size,
+                     block_size);
+}
+
+bool ReadExtents(const std::string& path,
+                 const google::protobuf::RepeatedPtrField<Extent>& extents,
+                 brillo::Blob* out_data,
+                 size_t block_size);
+
 // Read the current boot identifier and store it in |boot_id|. This identifier
 // is constants during the same boot of the kernel and is regenerated after
 // reboot. Returns whether it succeeded getting the boot_id.
@@ -485,6 +509,11 @@
 std::string HexEncode(const brillo::Blob& blob) noexcept;
 std::string HexEncode(const std::string_view blob) noexcept;
 
+template <size_t kSize>
+std::string HexEncode(const std::array<uint8_t, kSize> blob) noexcept {
+  return base::HexEncode(blob.data(), blob.size());
+}
+
 }  // namespace chromeos_update_engine
 
 #define TEST_AND_RETURN_FALSE_ERRNO(_x)                              \