Upon source hash validation failure, log ext4 remount information am: 4e13cf4743 am: 7e1e2f819f
am: 8cbf2b7525

Change-Id: Iecd492b72db16e6debadf37ca2ff42d9f1e73fff
diff --git a/Android.mk b/Android.mk
index 5a3129e..d79d89b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -141,6 +141,7 @@
     payload_consumer/file_writer.cc \
     payload_consumer/filesystem_verifier_action.cc \
     payload_consumer/install_plan.cc \
+    payload_consumer/mount_history.cc \
     payload_consumer/payload_constants.cc \
     payload_consumer/payload_verifier.cc \
     payload_consumer/postinstall_runner_action.cc \
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index 45ad58b..d5ae3e6 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -50,6 +50,7 @@
 #include "update_engine/payload_consumer/extent_reader.h"
 #include "update_engine/payload_consumer/extent_writer.h"
 #include "update_engine/payload_consumer/file_descriptor_utils.h"
+#include "update_engine/payload_consumer/mount_history.h"
 #if USE_MTD
 #include "update_engine/payload_consumer/mtd_file_descriptor.h"
 #endif
@@ -1079,8 +1080,10 @@
 
 // Compare |calculated_hash| with source hash in |operation|, return false and
 // dump hash and set |error| if don't match.
+// |source_fd| is the file descriptor of the source partition.
 bool ValidateSourceHash(const brillo::Blob& calculated_hash,
                         const InstallOperation& operation,
+                        const FileDescriptorPtr source_fd,
                         ErrorCode* error) {
   brillo::Blob expected_source_hash(operation.src_sha256_hash().begin(),
                                     operation.src_sha256_hash().end());
@@ -1107,6 +1110,9 @@
     LOG(ERROR) << "Operation source (offset:size) in blocks: "
                << base::JoinString(source_extents, ",");
 
+    // Log remount history if this device is an ext4 partition.
+    LogMountHistory(source_fd);
+
     *error = ErrorCode::kDownloadStateInitializationError;
     return false;
   }
@@ -1131,7 +1137,8 @@
                                                      &source_hash));
 
   if (operation.has_src_sha256_hash()) {
-    TEST_AND_RETURN_FALSE(ValidateSourceHash(source_hash, operation, error));
+    TEST_AND_RETURN_FALSE(
+        ValidateSourceHash(source_hash, operation, source_fd_, error));
   }
 
   return true;
@@ -1217,8 +1224,8 @@
     total_blocks -= read_blocks;
   }
   TEST_AND_RETURN_FALSE(source_hasher.Finalize());
-  TEST_AND_RETURN_FALSE(
-      ValidateSourceHash(source_hasher.raw_hash(), operation, error));
+  TEST_AND_RETURN_FALSE(ValidateSourceHash(
+      source_hasher.raw_hash(), operation, source_fd_, error));
   return true;
 }
 
diff --git a/payload_consumer/mount_history.cc b/payload_consumer/mount_history.cc
new file mode 100644
index 0000000..43a75b3
--- /dev/null
+++ b/payload_consumer/mount_history.cc
@@ -0,0 +1,72 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/payload_consumer/mount_history.h"
+
+#include <inttypes.h>
+
+#include <string>
+#include <vector>
+
+#include <base/logging.h>
+#include <base/time/time.h>
+
+#include "update_engine/common/utils.h"
+
+namespace chromeos_update_engine {
+void LogMountHistory(const FileDescriptorPtr blockdevice_fd) {
+  static constexpr ssize_t kBlockSize = 4096;
+
+  if (blockdevice_fd == nullptr) {
+    return;
+  }
+
+  brillo::Blob block0_buffer(kBlockSize);
+  ssize_t bytes_read;
+
+  if (!utils::PReadAll(
+          blockdevice_fd, block0_buffer.data(), kBlockSize, 0, &bytes_read)) {
+    LOG(WARNING) << "PReadAll failed";
+    return;
+  }
+
+  if (bytes_read != kBlockSize) {
+    LOG(WARNING) << "Could not read an entire block";
+    return;
+  }
+
+  // https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout
+  // Super block starts from block 0, offset 0x400
+  //   0x2C: len32 Mount time
+  //   0x30: len32 Write time
+  //   0x34: len16 Number of mounts since the last fsck
+  //   0x38: len16 Magic signature 0xEF53
+
+  time_t mount_time =
+      *reinterpret_cast<uint32_t*>(&block0_buffer[0x400 + 0x2C]);
+  uint16_t mount_count =
+      *reinterpret_cast<uint16_t*>(&block0_buffer[0x400 + 0x34]);
+  uint16_t magic = *reinterpret_cast<uint16_t*>(&block0_buffer[0x400 + 0x38]);
+
+  if (magic == 0xEF53) {
+    if (mount_count > 0) {
+      LOG(WARNING) << "Device was remounted R/W " << mount_count << " times. "
+                   << "Last remount happened on "
+                   << base::Time::FromTimeT(mount_time) << ".";
+    }
+  }
+}
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/mount_history.h b/payload_consumer/mount_history.h
new file mode 100644
index 0000000..ba0c65d
--- /dev/null
+++ b/payload_consumer/mount_history.h
@@ -0,0 +1,28 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_PAYLOAD_CONSUMER_MOUNT_HISTORY_H_
+#define UPDATE_ENGINE_PAYLOAD_CONSUMER_MOUNT_HISTORY_H_
+
+#include "update_engine/payload_consumer/file_descriptor.h"
+
+namespace chromeos_update_engine {
+// Try to parse an ext4 from the partition specified by |blockdevice_fd|.
+// If ext4 header exists and remount is detected, log mount count and date.
+void LogMountHistory(const FileDescriptorPtr blockdevice_fd);
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_CONSUMER_MOUNT_HISTORY_H_
diff --git a/update_engine.gyp b/update_engine.gyp
index f312a1d..f72ca14 100644
--- a/update_engine.gyp
+++ b/update_engine.gyp
@@ -181,6 +181,7 @@
         'payload_consumer/file_writer.cc',
         'payload_consumer/filesystem_verifier_action.cc',
         'payload_consumer/install_plan.cc',
+        'payload_consumer/mount_history.cc',
         'payload_consumer/payload_constants.cc',
         'payload_consumer/payload_verifier.cc',
         'payload_consumer/postinstall_runner_action.cc',