blob: implement commit command

Implement the commit command for the blob handler.

Change-Id: Ia3be86083991cbdf7fe85c15986f4e1cb60971f5
Signed-off-by: Patrick Venture <venture@google.com>
diff --git a/src/ipmiblob/blob_handler.cpp b/src/ipmiblob/blob_handler.cpp
index 77fcb6c..e5b78e1 100644
--- a/src/ipmiblob/blob_handler.cpp
+++ b/src/ipmiblob/blob_handler.cpp
@@ -162,6 +162,30 @@
     }
 }
 
+void BlobHandler::commit(std::uint16_t session,
+                         const std::vector<std::uint8_t>& bytes)
+{
+    std::vector<std::uint8_t> request;
+    auto addrSession = reinterpret_cast<const std::uint8_t*>(&session);
+    std::copy(addrSession, addrSession + sizeof(session),
+              std::back_inserter(request));
+
+    /* You have one byte to describe the length. */
+    if (bytes.size() > std::numeric_limits<std::uint8_t>::max())
+    {
+        throw BlobException("Commit data length greater than 8-bit limit\n");
+    }
+
+    std::uint8_t length = static_cast<std::uint8_t>(bytes.size());
+    auto addrLength = reinterpret_cast<const std::uint8_t*>(&length);
+    std::copy(addrLength, addrLength + sizeof(length),
+              std::back_inserter(request));
+
+    std::copy(bytes.begin(), bytes.end(), std::back_inserter(request));
+
+    sendIpmiPayload(BlobOEMCommands::bmcBlobCommit, request);
+}
+
 void BlobHandler::writeGeneric(BlobOEMCommands command, std::uint16_t session,
                                std::uint32_t offset,
                                const std::vector<std::uint8_t>& bytes)
diff --git a/src/ipmiblob/blob_handler.hpp b/src/ipmiblob/blob_handler.hpp
index 0b6db17..200dfd5 100644
--- a/src/ipmiblob/blob_handler.hpp
+++ b/src/ipmiblob/blob_handler.hpp
@@ -62,6 +62,12 @@
     /**
      * @throws BlobException.
      */
+    void commit(std::uint16_t session,
+                const std::vector<std::uint8_t>& bytes) override;
+
+    /**
+     * @throws BlobException.
+     */
     void writeMeta(std::uint16_t session, std::uint32_t offset,
                    const std::vector<std::uint8_t>& bytes) override;
 
diff --git a/src/ipmiblob/blob_interface.hpp b/src/ipmiblob/blob_interface.hpp
index ece5c34..989d461 100644
--- a/src/ipmiblob/blob_interface.hpp
+++ b/src/ipmiblob/blob_interface.hpp
@@ -20,6 +20,16 @@
     virtual ~BlobInterface() = default;
 
     /**
+     * Call commit on a blob.  The behavior here is up to the blob itself.
+     *
+     * @param[in] session - the session id.
+     * @param[in] bytes - the bytes to send.
+     * @throws BlobException on failure.
+     */
+    virtual void commit(std::uint16_t session,
+                        const std::vector<std::uint8_t>& bytes) = 0;
+
+    /**
      * Write metadata to a blob.
      *
      * @param[in] session - the session id.
diff --git a/src/ipmiblob/test/blob_interface_mock.hpp b/src/ipmiblob/test/blob_interface_mock.hpp
index 0faa6fe..7fb94c5 100644
--- a/src/ipmiblob/test/blob_interface_mock.hpp
+++ b/src/ipmiblob/test/blob_interface_mock.hpp
@@ -9,6 +9,7 @@
 {
   public:
     virtual ~BlobInterfaceMock() = default;
+    MOCK_METHOD2(commit, void(std::uint16_t, const std::vector<std::uint8_t>&));
     MOCK_METHOD3(writeMeta, void(std::uint16_t, std::uint32_t,
                                  const std::vector<std::uint8_t>&));
     MOCK_METHOD3(writeBytes, void(std::uint16_t, std::uint32_t,
diff --git a/test/tools_blob_unittest.cpp b/test/tools_blob_unittest.cpp
index 75e43cb..e9f2c96 100644
--- a/test/tools_blob_unittest.cpp
+++ b/test/tools_blob_unittest.cpp
@@ -295,6 +295,28 @@
     blob.closeBlob(0x0001);
 }
 
+TEST_F(BlobHandlerTest, commitSucceedsNoData)
+{
+    /* The commit succeeds. */
+    auto ipmi = CreateIpmiMock();
+    IpmiInterfaceMock* ipmiMock =
+        reinterpret_cast<IpmiInterfaceMock*>(ipmi.get());
+    BlobHandler blob(std::move(ipmi));
+
+    std::vector<std::uint8_t> request = {
+        0xcf, 0xc2, 0x00, BlobHandler::BlobOEMCommands::bmcBlobCommit,
+        0x00, 0x00, 0x01, 0x00,
+        0x00};
+
+    std::vector<std::uint8_t> resp = {0xcf, 0xc2, 0x00};
+    std::vector<std::uint8_t> reqCrc = {0x01, 0x00, 0x00};
+    EXPECT_CALL(crcMock, generateCrc(Eq(reqCrc))).WillOnce(Return(0x00));
+
+    EXPECT_CALL(*ipmiMock, sendPacket(Eq(request))).WillOnce(Return(resp));
+
+    blob.commit(0x0001, {});
+}
+
 TEST_F(BlobHandlerTest, writeBytesSucceeds)
 {
     /* The write bytes succeeds. */