Discard the tail of the target partitions when updating.

The partition is normally way bigger than the data it holds and the
remainder of the partition is often ignored by the system. This patch
discards the tail of the partition, past the end of the updated data.

This would ensure that the unused blocks in these partitions can be
reallocated by the SSD controller to other blocks, and that data from
previous updates does not interfere with the current version.

Bug: 28744609
TEST=strace -e trace=file,ioctl shows the device discarding the end of the updated partitions.

(cherry picked from commit b86787cdb2ca619c8e2ef0a2e38af8353f4cf4ac)

Change-Id: I5e815f4fc4d1fbc24a27a3d8dd0b22ee0456e09d
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index faf67a0..e4ba78b 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -129,6 +129,35 @@
   *err = 0;
   return fd;
 }
+
+// Discard the tail of the block device referenced by |fd|, from the offset
+// |data_size| until the end of the block device. Returns whether the data was
+// discarded.
+bool DiscardPartitionTail(FileDescriptorPtr fd, uint64_t data_size) {
+  uint64_t part_size = fd->BlockDevSize();
+  if (!part_size || part_size <= data_size)
+    return false;
+
+  const vector<int> requests = {
+      BLKSECDISCARD,
+      BLKDISCARD,
+#ifdef BLKZEROOUT
+      BLKZEROOUT,
+#endif
+  };
+  for (int request : requests) {
+    int error = 0;
+    if (fd->BlkIoctl(request, data_size, part_size - data_size, &error) &&
+        error == 0) {
+      return true;
+    }
+    LOG(WARNING) << "Error discarding the last "
+                 << (part_size - data_size) / 1024 << " KiB using ioctl("
+                 << request << ")";
+  }
+  return false;
+}
+
 }  // namespace
 
 
@@ -319,6 +348,15 @@
                << ", file " << target_path_;
     return false;
   }
+
+  LOG(INFO) << "Applying " << partition.operations().size()
+            << " operations to partition \"" << partition.partition_name()
+            << "\"";
+
+  // Discard the end of the partition, but ignore failures.
+  DiscardPartitionTail(
+      target_fd_, install_plan_->partitions[current_partition_].target_size);
+
   return true;
 }
 
diff --git a/payload_consumer/file_descriptor.cc b/payload_consumer/file_descriptor.cc
index 309c60d..8a23dea 100644
--- a/payload_consumer/file_descriptor.cc
+++ b/payload_consumer/file_descriptor.cc
@@ -24,6 +24,8 @@
 
 #include <base/posix/eintr_wrapper.h>
 
+#include "update_engine/common/utils.h"
+
 namespace chromeos_update_engine {
 
 bool EintrSafeFileDescriptor::Open(const char* path, int flags, mode_t mode) {
@@ -65,6 +67,20 @@
   return lseek64(fd_, offset, whence);
 }
 
+uint64_t EintrSafeFileDescriptor::BlockDevSize() {
+  if (fd_ < 0)
+    return 0;
+  struct stat stbuf;
+  if (fstat(fd_, &stbuf) < 0) {
+    PLOG(ERROR) << "Error stat-ing fd " << fd_;
+    return 0;
+  }
+  if (!S_ISBLK(stbuf.st_mode))
+    return 0;
+  off_t block_size = utils::BlockDevSize(fd_);
+  return block_size < 0 ? 0 : block_size;
+}
+
 bool EintrSafeFileDescriptor::BlkIoctl(int request,
                                        uint64_t start,
                                        uint64_t length,
diff --git a/payload_consumer/file_descriptor.h b/payload_consumer/file_descriptor.h
index 3c15415..7bb2974 100644
--- a/payload_consumer/file_descriptor.h
+++ b/payload_consumer/file_descriptor.h
@@ -78,6 +78,10 @@
   // may set errno accordingly.
   virtual off64_t Seek(off64_t offset, int whence) = 0;
 
+  // Return the size of the block device in bytes, or 0 if the device is not a
+  // block device or an error occurred.
+  virtual uint64_t BlockDevSize() = 0;
+
   // Runs a ioctl() on the file descriptor if supported. Returns whether
   // the operation is supported. The |request| can be one of BLKDISCARD,
   // BLKZEROOUT and BLKSECDISCARD to discard, write zeros or securely discard
@@ -119,6 +123,7 @@
   ssize_t Read(void* buf, size_t count) override;
   ssize_t Write(const void* buf, size_t count) override;
   off64_t Seek(off64_t offset, int whence) override;
+  uint64_t BlockDevSize() override;
   bool BlkIoctl(int request,
                 uint64_t start,
                 uint64_t length,
diff --git a/payload_consumer/mtd_file_descriptor.h b/payload_consumer/mtd_file_descriptor.h
index 9ac1ec1..6c945b2 100644
--- a/payload_consumer/mtd_file_descriptor.h
+++ b/payload_consumer/mtd_file_descriptor.h
@@ -40,6 +40,7 @@
   ssize_t Read(void* buf, size_t count) override;
   ssize_t Write(const void* buf, size_t count) override;
   off64_t Seek(off64_t offset, int whence) override;
+  uint64_t BlockDevSize() override { return 0; }
   bool BlkIoctl(int request,
                 uint64_t start,
                 uint64_t length,
@@ -75,6 +76,7 @@
   ssize_t Read(void* buf, size_t count) override;
   ssize_t Write(const void* buf, size_t count) override;
   off64_t Seek(off64_t offset, int whence) override;
+  uint64_t BlockDevSize() override { return 0; }
   bool BlkIoctl(int request,
                 uint64_t start,
                 uint64_t length,