new command: BmcBlobWriteMeta
Implement new command BmcBlobWriteMeta.
Change-Id: I2e148f4bde4ef5d24db7e30bb02bdde024d9166a
Signed-off-by: Patrick Venture <venture@google.com>
diff --git a/blobs-ipmid/blobs.hpp b/blobs-ipmid/blobs.hpp
index b6672b7..0014fd4 100644
--- a/blobs-ipmid/blobs.hpp
+++ b/blobs-ipmid/blobs.hpp
@@ -106,6 +106,17 @@
const std::vector<uint8_t>& data) = 0;
/**
+ * Attempt to write metadata to a blob.
+ *
+ * @param[in] session - the session id.
+ * @param[in] offset - offset into the blob.
+ * @param[in] data - the data to write.
+ * @return bool - was able to write.
+ */
+ virtual bool writeMeta(uint16_t session, uint32_t offset,
+ const std::vector<uint8_t>& data) = 0;
+
+ /**
* Attempt to commit to a blob.
*
* @param[in] session - the session id.
diff --git a/blobs-ipmid/manager.hpp b/blobs-ipmid/manager.hpp
index 884271c..d7282e6 100644
--- a/blobs-ipmid/manager.hpp
+++ b/blobs-ipmid/manager.hpp
@@ -56,6 +56,9 @@
const std::vector<uint8_t>& data) = 0;
virtual bool deleteBlob(const std::string& path) = 0;
+
+ virtual bool writeMeta(uint16_t session, uint32_t offset,
+ const std::vector<uint8_t>& data) = 0;
};
/**
@@ -192,6 +195,17 @@
bool deleteBlob(const std::string& path) override;
/**
+ * Attempt to write Metadata to a blob.
+ *
+ * @param[in] session - the session for this command.
+ * @param[in] offset - the offset into the blob to write.
+ * @param[in] data - the bytes to write to the blob.
+ * @return bool - true if the write succeeded.
+ */
+ bool writeMeta(uint16_t session, uint32_t offset,
+ const std::vector<uint8_t>& data) override;
+
+ /**
* Attempts to return a valid unique session id.
*
* @param[in,out] - pointer to the session.
diff --git a/blobs-ipmid/test/blob_mock.hpp b/blobs-ipmid/test/blob_mock.hpp
index 3396048..b70d3d1 100644
--- a/blobs-ipmid/test/blob_mock.hpp
+++ b/blobs-ipmid/test/blob_mock.hpp
@@ -19,6 +19,8 @@
MOCK_METHOD3(open, bool(uint16_t, uint16_t, const std::string&));
MOCK_METHOD3(read, std::vector<uint8_t>(uint16_t, uint32_t, uint32_t));
MOCK_METHOD3(write, bool(uint16_t, uint32_t, const std::vector<uint8_t>&));
+ MOCK_METHOD3(writeMeta,
+ bool(uint16_t, uint32_t, const std::vector<uint8_t>&));
MOCK_METHOD2(commit, bool(uint16_t, const std::vector<uint8_t>&));
MOCK_METHOD1(close, bool(uint16_t));
MOCK_METHOD2(stat, bool(uint16_t, struct BlobMeta*));
diff --git a/blobs-ipmid/test/manager_mock.hpp b/blobs-ipmid/test/manager_mock.hpp
index a4714f8..00eeb6b 100644
--- a/blobs-ipmid/test/manager_mock.hpp
+++ b/blobs-ipmid/test/manager_mock.hpp
@@ -26,6 +26,8 @@
MOCK_METHOD3(read, std::vector<uint8_t>(uint16_t, uint32_t, uint32_t));
MOCK_METHOD3(write, bool(uint16_t, uint32_t, const std::vector<uint8_t>&));
MOCK_METHOD1(deleteBlob, bool(const std::string&));
+ MOCK_METHOD3(writeMeta,
+ bool(uint16_t, uint32_t, const std::vector<uint8_t>&));
};
/*
diff --git a/example/example.cpp b/example/example.cpp
index c17d71d..929e4d1 100644
--- a/example/example.cpp
+++ b/example/example.cpp
@@ -117,6 +117,13 @@
return true;
}
+bool ExampleBlobHandler::writeMeta(uint16_t session, uint32_t offset,
+ const std::vector<uint8_t>& data)
+{
+ /* Not supported. */
+ return false;
+}
+
bool ExampleBlobHandler::commit(uint16_t session,
const std::vector<uint8_t>& data)
{
diff --git a/example/example.hpp b/example/example.hpp
index 85f5b17..a85c275 100644
--- a/example/example.hpp
+++ b/example/example.hpp
@@ -58,6 +58,8 @@
uint32_t requestedSize) override;
bool write(uint16_t session, uint32_t offset,
const std::vector<uint8_t>& data) override;
+ bool writeMeta(uint16_t session, uint32_t offset,
+ const std::vector<uint8_t>& data) override;
bool commit(uint16_t session, const std::vector<uint8_t>& data) override;
bool close(uint16_t session) override;
bool stat(uint16_t session, struct BlobMeta* meta) override;
diff --git a/ipmi.cpp b/ipmi.cpp
index 6942b11..fb5f120 100644
--- a/ipmi.cpp
+++ b/ipmi.cpp
@@ -43,6 +43,8 @@
{BlobOEMCommands::bmcBlobRead, sizeof(struct BmcBlobReadTx)},
{BlobOEMCommands::bmcBlobWrite,
sizeof(struct BmcBlobWriteTx) + sizeof(uint8_t)},
+ {BlobOEMCommands::bmcBlobWriteMeta,
+ sizeof(struct BmcBlobWriteMetaTx) + sizeof(uint8_t)},
};
auto results = minimumLengths.find(command);
@@ -321,4 +323,29 @@
return IPMI_CC_OK;
}
+ipmi_ret_t writeMeta(ManagerInterface* mgr, const uint8_t* reqBuf,
+ uint8_t* replyCmdBuf, size_t* dataLen)
+{
+ size_t requestLen = (*dataLen);
+ struct BmcBlobWriteMetaTx request;
+
+ /* Copy over the request. */
+ std::memcpy(&request, reqBuf, sizeof(request));
+
+ /* Determine number of bytes of metadata to write. */
+ uint32_t size = requestLen - sizeof(request);
+
+ /* Nothing really else to validate, we just copy those bytes. */
+ std::vector<uint8_t> data(size);
+ std::memcpy(data.data(), &reqBuf[sizeof(request)], size);
+
+ /* Attempt to write the bytes. */
+ if (!mgr->writeMeta(request.sessionId, request.offset, data))
+ {
+ return IPMI_CC_INVALID;
+ }
+
+ return IPMI_CC_OK;
+}
+
} // namespace blobs
diff --git a/ipmi.hpp b/ipmi.hpp
index 3b99e6e..a09d347 100644
--- a/ipmi.hpp
+++ b/ipmi.hpp
@@ -20,6 +20,7 @@
bmcBlobDelete = 7,
bmcBlobStat = 8,
bmcBlobSessionStat = 9,
+ bmcBlobWriteMeta = 10,
};
/* Used by bmcBlobGetCount */
@@ -140,6 +141,16 @@
uint8_t data[];
} __attribute__((packed));
+/* Used by bmcBlobWriteMeta */
+struct BmcBlobWriteMetaTx
+{
+ uint8_t cmd; /* bmcBlobWriteMeta */
+ uint16_t crc;
+ uint16_t sessionId; /* Returned from BmcBlobOpen. */
+ uint32_t offset; /* The byte sequence start, 0-based. */
+ uint8_t data[];
+} __attribute__((packed));
+
/**
* Validate the minimum request length if there is one.
*
@@ -223,4 +234,11 @@
*/
ipmi_ret_t writeBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
uint8_t* replyCmdBuf, size_t* dataLen);
+
+/**
+ * Attempt to write metadata to the blob.
+ */
+ipmi_ret_t writeMeta(ManagerInterface* mgr, const uint8_t* reqBuf,
+ uint8_t* replyCmdBuf, size_t* dataLen);
+
} // namespace blobs
diff --git a/manager.cpp b/manager.cpp
index 6a94c1a..32fdd74 100644
--- a/manager.cpp
+++ b/manager.cpp
@@ -316,6 +316,21 @@
return handler->deleteBlob(path);
}
+bool BlobManager::writeMeta(uint16_t session, uint32_t offset,
+ const std::vector<uint8_t>& data)
+{
+ SessionInfo* info = getSessionInfo(session);
+
+ /* No session found. */
+ if (!info)
+ {
+ return false;
+ }
+
+ /* Try writing metadata to it. */
+ return info->handler->writeMeta(session, offset, data);
+}
+
bool BlobManager::getSession(uint16_t* sess)
{
uint16_t tries = 0;
diff --git a/process.cpp b/process.cpp
index b3939eb..245da69 100644
--- a/process.cpp
+++ b/process.cpp
@@ -44,6 +44,7 @@
{BlobOEMCommands::bmcBlobDelete, deleteBlob},
{BlobOEMCommands::bmcBlobStat, statBlob},
{BlobOEMCommands::bmcBlobSessionStat, sessionStatBlob},
+ {BlobOEMCommands::bmcBlobWriteMeta, writeMeta},
};
IpmiBlobHandler validateBlobCommand(CrcInterface* crc, const uint8_t* reqBuf,
diff --git a/test/Makefile.am b/test/Makefile.am
index 29586bf..5e0cdb0 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -21,6 +21,7 @@
ipmi_commit_unittest \
ipmi_read_unittest \
ipmi_write_unittest \
+ ipmi_writemeta_unittest \
ipmi_validate_unittest \
manager_unittest \
manager_getsession_unittest \
@@ -32,6 +33,7 @@
manager_delete_unittest \
manager_write_unittest \
manager_read_unittest \
+ manager_writemeta_unittest \
process_unittest \
crc_unittest
TESTS = $(check_PROGRAMS)
@@ -69,6 +71,9 @@
ipmi_write_unittest_SOURCES = ipmi_write_unittest.cpp
ipmi_write_unittest_LDADD = $(top_builddir)/ipmi.o
+ipmi_writemeta_unittest_SOURCES = ipmi_writemeta_unittest.cpp
+ipmi_writemeta_unittest_LDADD = $(top_builddir)/ipmi.o
+
ipmi_validate_unittest_SOURCES = ipmi_validate_unittest.cpp
ipmi_validate_unittest_LDADD = $(top_builddir)/ipmi.o
@@ -102,6 +107,9 @@
manager_read_unittest_SOURCES = manager_read_unittest.cpp
manager_read_unittest_LDADD = $(top_builddir)/manager.o
+manager_writemeta_unittest_SOURCES = manager_writemeta_unittest.cpp
+manager_writemeta_unittest_LDADD = $(top_builddir)/manager.o
+
process_unittest_SOURCES = process_unittest.cpp
process_unittest_LDADD = $(top_builddir)/process.o $(top_builddir)/ipmi.o \
$(top_builddir)/crc.o
diff --git a/test/ipmi_writemeta_unittest.cpp b/test/ipmi_writemeta_unittest.cpp
new file mode 100644
index 0000000..2dfbe59
--- /dev/null
+++ b/test/ipmi_writemeta_unittest.cpp
@@ -0,0 +1,72 @@
+#include "ipmi.hpp"
+
+#include <blobs-ipmid/test/manager_mock.hpp>
+#include <cstring>
+
+#include <gtest/gtest.h>
+
+namespace blobs
+{
+using ::testing::ElementsAreArray;
+using ::testing::Return;
+
+// ipmid.hpp isn't installed where we can grab it and this value is per BMC
+// SoC.
+#define MAX_IPMI_BUFFER 64
+
+TEST(BlobWriteMetaTest, ManagerReturnsFailureReturnsFailure)
+{
+ // This verifies a failure from the manager is passed back.
+
+ ManagerMock mgr;
+ size_t dataLen;
+ uint8_t request[MAX_IPMI_BUFFER] = {0};
+ uint8_t reply[MAX_IPMI_BUFFER] = {0};
+ auto req = reinterpret_cast<struct BmcBlobWriteMetaTx*>(request);
+
+ req->cmd = BlobOEMCommands::bmcBlobWrite;
+ req->crc = 0;
+ req->sessionId = 0x54;
+ req->offset = 0x100;
+
+ uint8_t expectedBytes[2] = {0x66, 0x67};
+ std::memcpy(req->data, &expectedBytes[0], sizeof(expectedBytes));
+
+ dataLen = sizeof(struct BmcBlobWriteMetaTx) + sizeof(expectedBytes);
+
+ EXPECT_CALL(
+ mgr, writeMeta(req->sessionId, req->offset,
+ ElementsAreArray(expectedBytes, sizeof(expectedBytes))))
+ .WillOnce(Return(false));
+
+ EXPECT_EQ(IPMI_CC_INVALID, writeMeta(&mgr, request, reply, &dataLen));
+}
+
+TEST(BlobWriteMetaTest, ManagerReturnsTrueWriteSucceeds)
+{
+ // The case where everything works.
+
+ ManagerMock mgr;
+ size_t dataLen;
+ uint8_t request[MAX_IPMI_BUFFER] = {0};
+ uint8_t reply[MAX_IPMI_BUFFER] = {0};
+ auto req = reinterpret_cast<struct BmcBlobWriteMetaTx*>(request);
+
+ req->cmd = BlobOEMCommands::bmcBlobWrite;
+ req->crc = 0;
+ req->sessionId = 0x54;
+ req->offset = 0x100;
+
+ uint8_t expectedBytes[2] = {0x66, 0x67};
+ std::memcpy(req->data, &expectedBytes[0], sizeof(expectedBytes));
+
+ dataLen = sizeof(struct BmcBlobWriteMetaTx) + sizeof(expectedBytes);
+
+ EXPECT_CALL(
+ mgr, writeMeta(req->sessionId, req->offset,
+ ElementsAreArray(expectedBytes, sizeof(expectedBytes))))
+ .WillOnce(Return(true));
+
+ EXPECT_EQ(IPMI_CC_OK, writeMeta(&mgr, request, reply, &dataLen));
+}
+} // namespace blobs
diff --git a/test/manager_writemeta_unittest.cpp b/test/manager_writemeta_unittest.cpp
new file mode 100644
index 0000000..e83c904
--- /dev/null
+++ b/test/manager_writemeta_unittest.cpp
@@ -0,0 +1,92 @@
+#include <blobs-ipmid/manager.hpp>
+#include <blobs-ipmid/test/blob_mock.hpp>
+
+#include <gtest/gtest.h>
+
+using ::testing::_;
+using ::testing::Return;
+
+namespace blobs
+{
+
+TEST(ManagerWriteMetaTest, WriteMetaNoSessionReturnsFalse)
+{
+ // Calling WriteMeta on a session that doesn't exist should return false.
+
+ BlobManager mgr;
+ uint16_t sess = 1;
+ uint32_t ofs = 0x54;
+ std::vector<uint8_t> data = {0x11, 0x22};
+
+ EXPECT_FALSE(mgr.writeMeta(sess, ofs, data));
+}
+
+TEST(ManagerWriteMetaTest, WriteMetaSessionFoundButHandlerReturnsFalse)
+{
+ // The handler was found but it returned failure.
+
+ BlobManager mgr;
+ std::unique_ptr<BlobMock> m1 = std::make_unique<BlobMock>();
+ auto m1ptr = m1.get();
+ EXPECT_TRUE(mgr.registerHandler(std::move(m1)));
+
+ uint16_t flags = OpenFlags::write, sess;
+ std::string path = "/asdf/asdf";
+ uint32_t ofs = 0x54;
+ std::vector<uint8_t> data = {0x11, 0x22};
+
+ EXPECT_CALL(*m1ptr, canHandleBlob(path)).WillOnce(Return(true));
+ EXPECT_CALL(*m1ptr, open(_, flags, path)).WillOnce(Return(true));
+ EXPECT_TRUE(mgr.open(flags, path, &sess));
+
+ EXPECT_CALL(*m1ptr, writeMeta(sess, ofs, data)).WillOnce(Return(false));
+
+ EXPECT_FALSE(mgr.writeMeta(sess, ofs, data));
+}
+
+TEST(ManagerWriteMetaTest, WriteMetaSucceedsEvenIfFileOpenedReadOnly)
+{
+ // The manager will not route a WriteMeta call to a file opened read-only.
+
+ BlobManager mgr;
+ std::unique_ptr<BlobMock> m1 = std::make_unique<BlobMock>();
+ auto m1ptr = m1.get();
+ EXPECT_TRUE(mgr.registerHandler(std::move(m1)));
+
+ uint16_t flags = OpenFlags::read, sess;
+ std::string path = "/asdf/asdf";
+ uint32_t ofs = 0x54;
+ std::vector<uint8_t> data = {0x11, 0x22};
+
+ EXPECT_CALL(*m1ptr, canHandleBlob(path)).WillOnce(Return(true));
+ EXPECT_CALL(*m1ptr, open(_, flags, path)).WillOnce(Return(true));
+ EXPECT_TRUE(mgr.open(flags, path, &sess));
+
+ EXPECT_CALL(*m1ptr, writeMeta(sess, ofs, data)).WillOnce(Return(true));
+
+ EXPECT_TRUE(mgr.writeMeta(sess, ofs, data));
+}
+
+TEST(ManagerWriteMetaTest, WriteMetaMetaSessionFoundAndHandlerReturnsSuccess)
+{
+ // The handler was found and returned success.
+
+ BlobManager mgr;
+ std::unique_ptr<BlobMock> m1 = std::make_unique<BlobMock>();
+ auto m1ptr = m1.get();
+ EXPECT_TRUE(mgr.registerHandler(std::move(m1)));
+
+ uint16_t flags = OpenFlags::write, sess;
+ std::string path = "/asdf/asdf";
+ uint32_t ofs = 0x54;
+ std::vector<uint8_t> data = {0x11, 0x22};
+
+ EXPECT_CALL(*m1ptr, canHandleBlob(path)).WillOnce(Return(true));
+ EXPECT_CALL(*m1ptr, open(_, flags, path)).WillOnce(Return(true));
+ EXPECT_TRUE(mgr.open(flags, path, &sess));
+
+ EXPECT_CALL(*m1ptr, writeMeta(sess, ofs, data)).WillOnce(Return(true));
+
+ EXPECT_TRUE(mgr.writeMeta(sess, ofs, data));
+}
+} // namespace blobs