flash-ipmi: implement flashStartHash

Change-Id: Ia134b1faef99510bbed5eb4e36dada164c010305
Signed-off-by: Patrick Venture <venture@google.com>
diff --git a/configure.ac b/configure.ac
index dd05a59..12763e4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -72,6 +72,10 @@
 AS_IF([test "x$STAGING_PATH" == "x"], [STAGING_PATH="/run/initramfs/bmc-image"])
 AC_DEFINE_UNQUOTED([STAGING_PATH], ["$STAGING_PATH"], [The staging path for the flash image.])
 
+AC_ARG_VAR(HASH_PATH, [The path for the hash file.])
+AS_IF([test "x$HASH_PATH" == "x"], [HASH_PATH="/tmp/bmc.sig"])
+AC_DEFINE_UNQUOTED([HASH_PATH], ["$HASH_PATH"], [The path for the hash file.])
+
 # Create configured output
 AC_CONFIG_FILES([Makefile test/Makefile])
 AC_OUTPUT
diff --git a/flash-ipmi.cpp b/flash-ipmi.cpp
index 42333dd..f58363b 100644
--- a/flash-ipmi.cpp
+++ b/flash-ipmi.cpp
@@ -47,6 +47,7 @@
 void FlashUpdate::closeEverything()
 {
     closeFile(&flashFd);
+    closeFile(&hashFd);
 }
 
 FlashUpdate::~FlashUpdate()
@@ -76,6 +77,19 @@
         return false;
     }
 
+    /* hash path is basically optional. */
+    if (!hashPath.empty())
+    {
+        hashFd = std::fopen(hashPath.c_str(), "wb");
+        if (hashFd == nullptr)
+        {
+            log<level::INFO>("Unable to open hash storage path",
+                             entry("PATH=%s", hashPath.c_str()));
+            closeFile(&flashFd);
+            return false;
+        }
+    }
+
     return true;
 }
 
@@ -141,8 +155,13 @@
 
 bool FlashUpdate::startHash(uint32_t length)
 {
-    /* TODO: implement. */
-    return false;
+    if (!hashFd)
+    {
+        return false;
+    }
+
+    hashLength = length;
+    return true;
 }
 
 bool FlashUpdate::hashData(uint32_t offset, const std::vector<uint8_t>& bytes)
diff --git a/flash-ipmi.hpp b/flash-ipmi.hpp
index 2e6f6f0..2d7dcaa 100644
--- a/flash-ipmi.hpp
+++ b/flash-ipmi.hpp
@@ -150,8 +150,9 @@
 class FlashUpdate : public UpdateInterface
 {
   public:
-    FlashUpdate(const std::string& stagingPath) :
-        flashLength(0), flashFd(nullptr), tmpPath(stagingPath){};
+    FlashUpdate(const std::string& stagingPath, const std::string& hash = "") :
+        flashLength(0), flashFd(nullptr), tmpPath(stagingPath), hashLength(0),
+        hashFd(nullptr), hashPath(hash){};
     ~FlashUpdate();
 
     FlashUpdate(const FlashUpdate&) = default;
@@ -207,4 +208,15 @@
 
     /* Where the bytes are written before verification. */
     const std::string tmpPath;
+
+    /* The length of the hash in bytes. */
+    uint32_t hashLength;
+
+    /* The file handle to the hash file. */
+    std::FILE* hashFd;
+
+    /* Where we write the hash bytes.  Only required if your verification
+     * process uses a separate signature.
+     */
+    const std::string hashPath;
 };
diff --git a/main.cpp b/main.cpp
index 3cc3fb7..791cb22 100644
--- a/main.cpp
+++ b/main.cpp
@@ -24,6 +24,7 @@
 #include "ipmi.hpp"
 
 static constexpr auto stagingPath = STAGING_PATH;
+static constexpr auto hashPath = HASH_PATH;
 
 /* TODO: Once OEM IPMI number placement is settled, point to that. */
 namespace oem
@@ -84,7 +85,7 @@
 
 void setupGlobalOemFlashControl()
 {
-    flashUpdateSingleton = std::make_unique<FlashUpdate>(stagingPath);
+    flashUpdateSingleton = std::make_unique<FlashUpdate>(stagingPath, hashPath);
 
 #ifdef ENABLE_GOOGLE
     oem::Router* router = oem::mutableRouter();
diff --git a/test/Makefile.am b/test/Makefile.am
index d0b8b2a..9540688 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -22,7 +22,8 @@
 	ipmi_command_unittest \
 	flash_start_unittest \
 	flash_flashdata_unittest \
-	flash_flashfinish_unittest
+	flash_flashfinish_unittest \
+	flash_hashstart_unittest
 
 TESTS = $(check_PROGRAMS)
 
@@ -64,3 +65,6 @@
 
 flash_flashfinish_unittest_SOURCES = flash_flashfinish_unittest.cpp
 flash_flashfinish_unittest_LDADD = $(top_builddir)/flash-ipmi.o $(SDBUSPLUS_LIBS)
+
+flash_hashstart_unittest_SOURCES = flash_hashstart_unittest.cpp
+flash_hashstart_unittest_LDADD = $(top_builddir)/flash-ipmi.o $(SDBUSPLUS_LIBS)
diff --git a/test/flash_hashstart_unittest.cpp b/test/flash_hashstart_unittest.cpp
new file mode 100644
index 0000000..a3a1d9f
--- /dev/null
+++ b/test/flash_hashstart_unittest.cpp
@@ -0,0 +1,36 @@
+#include "flash-ipmi.hpp"
+
+#include <cstdio>
+#include <gtest/gtest.h>
+#include <string>
+
+#define THIRTYTWO_MIB 33554432
+
+TEST(FlashIpmiHashStartTest, OutofSequenceFails)
+{
+    // Verify that the image must be started first. (can change).
+
+    std::string name = std::tmpnam(nullptr);
+    std::string name2 = std::tmpnam(nullptr);
+
+    FlashUpdate updater(name, name2);
+    EXPECT_FALSE(updater.startHash(THIRTYTWO_MIB));
+
+    (void)std::remove(name.c_str());
+    (void)std::remove(name2.c_str());
+}
+
+TEST(FlashIpmiHashStartTest, VerifyHashFileCreated)
+{
+    // Verify that it's happy.
+
+    std::string name = std::tmpnam(nullptr);
+    std::string name2 = std::tmpnam(nullptr);
+
+    FlashUpdate updater(name, name2);
+    EXPECT_TRUE(updater.start(THIRTYTWO_MIB));
+    EXPECT_TRUE(updater.startHash(THIRTYTWO_MIB));
+
+    (void)std::remove(name.c_str());
+    (void)std::remove(name2.c_str());
+}