firmare: start implementing session stat

Session-specific stat is a call required by the P2A bridge to allow the
host to configure its side of the equation.

Change-Id: I90570641cf9a6816fb91e8329582b882aef212cf
Signed-off-by: Patrick Venture <venture@google.com>
diff --git a/file_handler.cpp b/file_handler.cpp
index 0647b65..d47e222 100644
--- a/file_handler.cpp
+++ b/file_handler.cpp
@@ -88,4 +88,10 @@
     return true;
 }
 
+int FileHandler::getSize()
+{
+    /* TODO: implement (maybe just stat on the file). */
+    return 0;
+}
+
 } // namespace blobs
diff --git a/file_handler.hpp b/file_handler.hpp
index c419949..64be8bf 100644
--- a/file_handler.hpp
+++ b/file_handler.hpp
@@ -28,6 +28,7 @@
     void close() override;
     bool write(std::uint32_t offset,
                const std::vector<std::uint8_t>& data) override;
+    int getSize() override;
 
   private:
     /** the active hash path, ignore. */
diff --git a/firmware_handler.cpp b/firmware_handler.cpp
index 5578b18..fda533e 100644
--- a/firmware_handler.cpp
+++ b/firmware_handler.cpp
@@ -180,10 +180,37 @@
  */
 bool FirmwareBlobHandler::stat(uint16_t session, struct BlobMeta* meta)
 {
-    /*
-     * Return session specific information.
+    auto item = lookup.find(session);
+    if (item == lookup.end())
+    {
+        return false;
+    }
+
+    /* The blobState here relates to an active sesion, so we should return the
+     * flags used to open this session.
      */
-    return false;
+    meta->blobState = item->second->flags;
+    /* The size here refers to the size of the file -- of something analagous.
+     */
+    meta->size = item->second->imageHandler->getSize();
+
+    /* The metadata blob returned comes from the data handler... it's used for
+     * instance, in P2A bridging to get required information about the mapping,
+     * and is the "opposite" of the lpc writemeta requirement.
+     */
+    meta->metadata.clear();
+    if (item->second->dataHandler)
+    {
+        auto bytes = item->second->dataHandler->read();
+        meta->metadata.insert(meta->metadata.begin(), bytes.begin(),
+                              bytes.end());
+    }
+
+    /* TODO: During things like verification, etc, we can report the state as
+     * committed, etc, so we'll need to do that.
+     */
+
+    return true;
 }
 
 /*
diff --git a/image_handler.hpp b/image_handler.hpp
index ed3455b..0be4590 100644
--- a/image_handler.hpp
+++ b/image_handler.hpp
@@ -39,6 +39,13 @@
      */
     virtual bool write(std::uint32_t offset,
                        const std::vector<std::uint8_t>& data) = 0;
+
+    /**
+     * return the size of the file (if that notion makes sense).
+     *
+     * @return the size in bytes of the image staged.
+     */
+    virtual int getSize() = 0;
 };
 
 struct HandlerPack
diff --git a/test/Makefile.am b/test/Makefile.am
index d7ca5a0..6f829b4 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -18,6 +18,7 @@
 	firmware_writemeta_unittest \
 	firmware_close_unittest \
 	firmware_delete_unittest \
+	firmware_sessionstat_unittest \
 	file_handler_unittest
 
 TESTS = $(check_PROGRAMS)
@@ -46,5 +47,8 @@
 firmware_delete_unittest_SOURCES = firmware_delete_unittest.cpp
 firmware_delete_unittest_LDADD = $(top_builddir)/firmware_handler.o
 
+firmware_sessionstat_unittest_SOURCES = firmware_sessionstat_unittest.cpp
+firmware_sessionstat_unittest_LDADD = $(top_builddir)/firmware_handler.o
+
 file_handler_unittest_SOURCES = file_handler_unittest.cpp
 file_handler_unittest_LDADD = $(top_builddir)/file_handler.o
diff --git a/test/firmware_sessionstat_unittest.cpp b/test/firmware_sessionstat_unittest.cpp
new file mode 100644
index 0000000..9dbf909
--- /dev/null
+++ b/test/firmware_sessionstat_unittest.cpp
@@ -0,0 +1,89 @@
+#include "data_mock.hpp"
+#include "firmware_handler.hpp"
+#include "image_mock.hpp"
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+namespace blobs
+{
+using ::testing::Eq;
+using ::testing::Return;
+
+TEST(FirmwareSessionStateTest, DataTypeIpmiNoMetadata)
+{
+    /* Verifying running stat if the type of data session is IPMI returns no
+     * metadata.
+     */
+    ImageHandlerMock imageMock1, imageMock2;
+    std::vector<HandlerPack> blobs = {
+        {FirmwareBlobHandler::hashBlobID, &imageMock1},
+        {"asdf", &imageMock2},
+    };
+
+    std::vector<DataHandlerPack> data = {
+        {FirmwareBlobHandler::UpdateFlags::ipmi, nullptr},
+    };
+
+    auto handler = FirmwareBlobHandler::CreateFirmwareBlobHandler(blobs, data);
+
+    EXPECT_CALL(imageMock2, open("asdf")).WillOnce(Return(true));
+
+    EXPECT_TRUE(handler->open(
+        0, OpenFlags::write | FirmwareBlobHandler::UpdateFlags::ipmi, "asdf"));
+
+    int size = 512;
+    EXPECT_CALL(imageMock2, getSize()).WillOnce(Return(size));
+
+    struct BlobMeta meta;
+    EXPECT_TRUE(handler->stat(0, &meta));
+    EXPECT_EQ(meta.blobState,
+              OpenFlags::write | FirmwareBlobHandler::UpdateFlags::ipmi);
+    EXPECT_EQ(meta.size, size);
+    EXPECT_EQ(meta.metadata.size(), 0);
+}
+
+TEST(FirmwareSessionStateTest, DataTypeP2AReturnsMetadata)
+{
+    /* Really any type that isn't IPMI can return metadata, but we only expect
+     * P2A to for now.  Later, LPC may have reason to provide data, and can by
+     * simply implementing read().
+     */
+    ImageHandlerMock imageMock1, imageMock2;
+    std::vector<HandlerPack> blobs = {
+        {FirmwareBlobHandler::hashBlobID, &imageMock1},
+        {"asdf", &imageMock2},
+    };
+
+    DataHandlerMock dataMock;
+
+    std::vector<DataHandlerPack> data = {
+        {FirmwareBlobHandler::UpdateFlags::ipmi, nullptr},
+        {FirmwareBlobHandler::UpdateFlags::lpc, &dataMock},
+    };
+
+    auto handler = FirmwareBlobHandler::CreateFirmwareBlobHandler(blobs, data);
+
+    EXPECT_CALL(dataMock, open()).WillOnce(Return(true));
+    EXPECT_CALL(imageMock2, open("asdf")).WillOnce(Return(true));
+
+    EXPECT_TRUE(handler->open(
+        0, OpenFlags::write | FirmwareBlobHandler::UpdateFlags::lpc, "asdf"));
+
+    int size = 512;
+    EXPECT_CALL(imageMock2, getSize()).WillOnce(Return(size));
+    std::vector<std::uint8_t> mBytes = {0x01, 0x02};
+    EXPECT_CALL(dataMock, read()).WillOnce(Return(mBytes));
+
+    struct BlobMeta meta;
+    EXPECT_TRUE(handler->stat(0, &meta));
+    EXPECT_EQ(meta.blobState,
+              OpenFlags::write | FirmwareBlobHandler::UpdateFlags::lpc);
+    EXPECT_EQ(meta.size, size);
+    EXPECT_EQ(meta.metadata.size(), mBytes.size());
+    EXPECT_EQ(meta.metadata[0], mBytes[0]);
+    EXPECT_EQ(meta.metadata[1], mBytes[1]);
+}
+
+} // namespace blobs
diff --git a/test/image_mock.hpp b/test/image_mock.hpp
index 8259aa9..c831c45 100644
--- a/test/image_mock.hpp
+++ b/test/image_mock.hpp
@@ -15,6 +15,7 @@
     MOCK_METHOD1(open, bool(const std::string&));
     MOCK_METHOD0(close, void());
     MOCK_METHOD2(write, bool(std::uint32_t, const std::vector<std::uint8_t>&));
+    MOCK_METHOD0(getSize, int());
 };
 
 } // namespace blobs