diff options
-rw-r--r-- | blobs-ipmid/blobs.hpp | 11 | ||||
-rw-r--r-- | blobs-ipmid/manager.hpp | 14 | ||||
-rw-r--r-- | blobs-ipmid/test/blob_mock.hpp | 2 | ||||
-rw-r--r-- | blobs-ipmid/test/manager_mock.hpp | 2 | ||||
-rw-r--r-- | example/example.cpp | 7 | ||||
-rw-r--r-- | example/example.hpp | 2 | ||||
-rw-r--r-- | ipmi.cpp | 27 | ||||
-rw-r--r-- | ipmi.hpp | 18 | ||||
-rw-r--r-- | manager.cpp | 15 | ||||
-rw-r--r-- | process.cpp | 1 | ||||
-rw-r--r-- | test/Makefile.am | 8 | ||||
-rw-r--r-- | test/ipmi_writemeta_unittest.cpp | 72 | ||||
-rw-r--r-- | test/manager_writemeta_unittest.cpp | 92 |
13 files changed, 271 insertions, 0 deletions
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 @@ class GenericBlobInterface 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 @@ class ManagerInterface 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 @@ class BlobManager : public ManagerInterface 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 @@ class BlobMock : public GenericBlobInterface 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 @@ class ManagerMock : public ManagerInterface 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 @@ bool ExampleBlobHandler::write(uint16_t session, uint32_t offset, 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 @@ class ExampleBlobHandler : public GenericBlobInterface 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; @@ -43,6 +43,8 @@ bool validateRequestLength(BlobOEMCommands command, size_t requestLen) {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 @@ ipmi_ret_t writeBlob(ManagerInterface* mgr, const uint8_t* reqBuf, 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 @@ -20,6 +20,7 @@ enum BlobOEMCommands bmcBlobDelete = 7, bmcBlobStat = 8, bmcBlobSessionStat = 9, + bmcBlobWriteMeta = 10, }; /* Used by bmcBlobGetCount */ @@ -140,6 +141,16 @@ struct BmcBlobWriteTx 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 readBlob(ManagerInterface* mgr, const uint8_t* reqBuf, */ 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 @@ bool BlobManager::deleteBlob(const std::string& path) 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 @@ static const std::unordered_map<BlobOEMCommands, IpmiBlobHandler> handlers = { {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 @@ check_PROGRAMS = \ ipmi_commit_unittest \ ipmi_read_unittest \ ipmi_write_unittest \ + ipmi_writemeta_unittest \ ipmi_validate_unittest \ manager_unittest \ manager_getsession_unittest \ @@ -32,6 +33,7 @@ check_PROGRAMS = \ manager_delete_unittest \ manager_write_unittest \ manager_read_unittest \ + manager_writemeta_unittest \ process_unittest \ crc_unittest TESTS = $(check_PROGRAMS) @@ -69,6 +71,9 @@ ipmi_read_unittest_LDADD = $(top_builddir)/ipmi.o 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_write_unittest_LDADD = $(top_builddir)/manager.o 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 |