Implement stat
Implement both session based and path based stat, and properly
set commit state to dirty when new content has not been committed.
Resolves: openbmc/phosphor-ipmi-blobs-binarystore#3
Tested: unit tests pass
Signed-off-by: Kun Yi <kunyi731@gmail.com>
Change-Id: I7c833cf4c7dcb0089db778caa60fc92edceb984e
diff --git a/binarystore.cpp b/binarystore.cpp
index b7c88a3..5b0e299 100644
--- a/binarystore.cpp
+++ b/binarystore.cpp
@@ -171,6 +171,7 @@
currentBlob_ = blob_.add_blobs();
currentBlob_->set_blob_id(blobId);
+ commitState_ = CommitState::Dirty;
log<level::NOTICE>("Created new blob", entry("BLOB_ID=%s", blobId.c_str()));
return true;
}
@@ -231,6 +232,7 @@
return false;
}
+ commitState_ = CommitState::Dirty;
/* Copy (overwrite) the data */
if (offset + data.size() > dataPtr->size())
{
@@ -273,9 +275,58 @@
return true;
}
-bool BinaryStore::stat()
+/*
+ * Sets |meta| with size and state of the blob. Returns |blobState| with
+ * standard definition from phosphor-ipmi-blobs header blob.hpp, plus OEM
+ * flag bits BinaryStore::CommitState.
+
+enum StateFlags
{
- return false;
+ open_read = (1 << 0),
+ open_write = (1 << 1),
+ committing = (1 << 2),
+ committed = (1 << 3),
+ commit_error = (1 << 4),
+};
+
+enum CommitState
+{
+ Dirty = (1 << 8), // In-memory data might not match persisted data
+ Clean = (1 << 9), // In-memory data matches persisted data
+ Uninitialized = (1 << 10), // Cannot find persisted data
+ CommitError = (1 << 11) // Error happened during committing
+};
+
+*/
+bool BinaryStore::stat(blobs::BlobMeta* meta)
+{
+ uint16_t blobState = blobs::StateFlags::open_read;
+ if (writable_)
+ {
+ blobState |= blobs::StateFlags::open_write;
+ }
+
+ if (commitState_ == CommitState::Clean)
+ {
+ blobState |= blobs::StateFlags::committed;
+ }
+ else if (commitState_ == CommitState::CommitError)
+ {
+ blobState |= blobs::StateFlags::commit_error;
+ }
+ blobState |= commitState_;
+
+ if (currentBlob_)
+ {
+ meta->size = currentBlob_->data().size();
+ }
+ else
+ {
+ meta->size = 0;
+ }
+ meta->blobState = blobState;
+
+ return true;
}
} // namespace binstore
diff --git a/binarystore.hpp b/binarystore.hpp
index 3a25581..9f95539 100644
--- a/binarystore.hpp
+++ b/binarystore.hpp
@@ -4,6 +4,7 @@
#include <unistd.h>
+#include <blobs-ipmid/blobs.hpp>
#include <cstdint>
#include <memory>
#include <string>
@@ -87,9 +88,11 @@
virtual bool close() = 0;
/**
- * TODO
+ * Returns blob stat flags.
+ * @param meta: output stat flags.
+ * @returns True if able to get the stat flags and write to *meta
*/
- virtual bool stat() = 0;
+ virtual bool stat(blobs::BlobMeta* meta) = 0;
};
/**
@@ -100,6 +103,17 @@
class BinaryStore : public BinaryStoreInterface
{
public:
+ /* |CommitState| differs slightly with |StateFlags| in blob.hpp,
+ * and thus is defined in the OEM space (bit 8 - 15). User can call stat()
+ * to query the |CommitState| of the blob path. */
+ enum CommitState
+ {
+ Dirty = (1 << 8), // In-memory data might not match persisted data
+ Clean = (1 << 9), // In-memory data matches persisted data
+ Uninitialized = (1 << 10), // Cannot find persisted data
+ CommitError = (1 << 11) // Error happened during committing
+ };
+
BinaryStore() = delete;
BinaryStore(const std::string& baseBlobId, std::unique_ptr<SysFile> file,
uint32_t maxSize) :
@@ -124,7 +138,7 @@
bool write(uint32_t offset, const std::vector<uint8_t>& data) override;
bool commit() override;
bool close() override;
- bool stat() override;
+ bool stat(blobs::BlobMeta* meta) override;
/**
* Helper factory method to create a BinaryStore instance
@@ -140,14 +154,6 @@
std::unique_ptr<SysFile> file, uint32_t maxSize);
private:
- enum class CommitState
- {
- Dirty, // In-memory data might not match persisted data
- Clean, // In-memory data matches persisted data
- Uninitialized, // Cannot find persisted data
- CommitError // Error happened during committing
- };
-
/* Load the serialized data from sysfile if commit state is dirty.
* Returns False if encountered error when loading */
bool loadSerializedData();
diff --git a/binarystore_mock.hpp b/binarystore_mock.hpp
index 3c7e6c8..9fa83e6 100644
--- a/binarystore_mock.hpp
+++ b/binarystore_mock.hpp
@@ -33,6 +33,8 @@
.WillByDefault(Invoke(&real_store_, &BinaryStore::write));
ON_CALL(*this, commit)
.WillByDefault(Invoke(&real_store_, &BinaryStore::commit));
+ ON_CALL(*this, stat)
+ .WillByDefault(Invoke(&real_store_, &BinaryStore::stat));
}
MOCK_CONST_METHOD0(getBaseBlobId, std::string());
MOCK_CONST_METHOD0(getBlobIds, std::vector<std::string>());
@@ -42,7 +44,7 @@
MOCK_METHOD2(write, bool(uint32_t, const std::vector<uint8_t>&));
MOCK_METHOD0(commit, bool());
MOCK_METHOD0(close, bool());
- MOCK_METHOD0(stat, bool());
+ MOCK_METHOD1(stat, bool(blobs::BlobMeta* meta));
private:
BinaryStore real_store_;
diff --git a/handler.cpp b/handler.cpp
index 0556633..66500f4 100644
--- a/handler.cpp
+++ b/handler.cpp
@@ -81,8 +81,13 @@
bool BinaryStoreBlobHandler::stat(const std::string& path,
struct BlobMeta* meta)
{
- // TODO: implement
- return false;
+ auto it = stores_.find(internal::getBaseFromId(path));
+ if (it == stores_.end())
+ {
+ return false;
+ }
+
+ return it->second->stat(meta);
}
bool BinaryStoreBlobHandler::open(uint16_t session, uint16_t flags,
@@ -179,8 +184,13 @@
bool BinaryStoreBlobHandler::stat(uint16_t session, struct BlobMeta* meta)
{
- // TODO: implement
- return false;
+ auto it = sessions_.find(session);
+ if (it == sessions_.end())
+ {
+ return false;
+ }
+
+ return it->second->stat(meta);
}
bool BinaryStoreBlobHandler::expire(uint16_t session)
diff --git a/test/Makefile.am b/test/Makefile.am
index 3d53916..08ef779 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -16,6 +16,7 @@
parse_config_unittest \
sys_file_unittest \
handler_commit_unittest \
+ handler_stat_unittest \
handler_open_unittest \
handler_readwrite_unittest \
handler_unittest
@@ -44,6 +45,15 @@
-lprotobuf
handler_commit_unittest_CXXFLAGS = $(PHOSPHOR_LOGGING_CFLAGS)
+handler_stat_unittest_SOURCES = handler_stat_unittest.cpp
+handler_stat_unittest_LDADD = $(PHOSPHOR_LOGGING_LIBS) \
+ $(top_builddir)/handler.o \
+ $(top_builddir)/binarystore.o \
+ $(top_builddir)/sys_file.o \
+ $(top_builddir)/libbinarystore_la-binaryblob.pb.o \
+ -lprotobuf
+handler_stat_unittest_CXXFLAGS = $(PHOSPHOR_LOGGING_CFLAGS)
+
handler_open_unittest_SOURCES = handler_open_unittest.cpp
handler_open_unittest_LDADD = $(PHOSPHOR_LOGGING_LIBS) \
$(top_builddir)/handler.o \
diff --git a/test/handler_stat_unittest.cpp b/test/handler_stat_unittest.cpp
new file mode 100644
index 0000000..253d02c
--- /dev/null
+++ b/test/handler_stat_unittest.cpp
@@ -0,0 +1,120 @@
+#include "handler_unittest.hpp"
+
+#include <gtest/gtest.h>
+
+using ::testing::_;
+using ::testing::ElementsAre;
+using ::testing::IsEmpty;
+using ::testing::Return;
+using ::testing::StartsWith;
+
+using namespace std::string_literals;
+
+namespace blobs
+{
+
+class BinaryStoreBlobHandlerStatTest : public BinaryStoreBlobHandlerTest
+{
+ protected:
+ BinaryStoreBlobHandlerStatTest()
+ {
+ addDefaultStore(statTestBaseId);
+ }
+
+ static inline std::string statTestBaseId = "/test/"s;
+ static inline std::string statTestBlobId = "/test/blob0"s;
+ static inline std::vector<uint8_t> statTestData = {0, 1, 2, 3};
+ static inline std::vector<uint8_t> statTestDataToOverwrite = {
+ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
+ static inline std::vector<uint8_t> commitMetaUnused;
+
+ static inline uint16_t statTestSessionId = 0;
+ static inline uint16_t statTestNewSessionId = 1;
+ static inline uint32_t statTestOffset = 0;
+
+ void openAndWriteTestData(
+ const std::vector<uint8_t>& testData = statTestData)
+ {
+ uint16_t flags = OpenFlags::read | OpenFlags::write;
+ EXPECT_TRUE(handler.open(statTestSessionId, flags, statTestBlobId));
+ EXPECT_TRUE(handler.write(statTestSessionId, statTestOffset, testData));
+ }
+
+ void commitData()
+ {
+ EXPECT_TRUE(handler.commit(statTestSessionId, commitMetaUnused));
+ }
+};
+
+TEST_F(BinaryStoreBlobHandlerStatTest, InitialStatIsValidQueriedWithBlobId)
+{
+ BlobMeta meta;
+
+ /* Querying stat fails if there is no open session */
+ EXPECT_FALSE(handler.stat(statTestSessionId, &meta));
+ /* However stat completes if queried using blobId. Returning default. */
+ EXPECT_TRUE(handler.stat(statTestBlobId, &meta));
+ EXPECT_EQ(meta.size, 0);
+};
+
+TEST_F(BinaryStoreBlobHandlerStatTest, StatShowsCommittedState)
+{
+ BlobMeta meta;
+ const int testIter = 2;
+
+ openAndWriteTestData();
+ for (int i = 0; i < testIter; ++i)
+ {
+ EXPECT_TRUE(handler.stat(statTestSessionId, &meta));
+ EXPECT_EQ(meta.size, statTestData.size());
+ EXPECT_TRUE(meta.blobState & OpenFlags::read);
+ EXPECT_TRUE(meta.blobState & OpenFlags::write);
+ EXPECT_FALSE(meta.blobState & StateFlags::committed);
+ EXPECT_TRUE(meta.blobState & BinaryStore::CommitState::Dirty);
+ }
+
+ commitData();
+ for (int i = 0; i < testIter; ++i)
+ {
+ EXPECT_TRUE(handler.stat(statTestSessionId, &meta));
+ EXPECT_EQ(meta.size, statTestData.size());
+ EXPECT_TRUE(meta.blobState & OpenFlags::read);
+ EXPECT_TRUE(meta.blobState & OpenFlags::write);
+ EXPECT_TRUE(meta.blobState & StateFlags::committed);
+ EXPECT_TRUE(meta.blobState & BinaryStore::CommitState::Clean);
+ }
+}
+
+TEST_F(BinaryStoreBlobHandlerStatTest, StatChangedWhenOverwriting)
+{
+ BlobMeta meta;
+ const int testIter = 2;
+
+ openAndWriteTestData();
+ commitData();
+ // Overwrite with different data.
+ EXPECT_TRUE(handler.write(statTestSessionId, statTestOffset,
+ statTestDataToOverwrite));
+ for (int i = 0; i < testIter; ++i)
+ {
+ EXPECT_TRUE(handler.stat(statTestSessionId, &meta));
+ EXPECT_EQ(meta.size, statTestDataToOverwrite.size());
+ EXPECT_TRUE(meta.blobState & OpenFlags::read);
+ EXPECT_TRUE(meta.blobState & OpenFlags::write);
+ EXPECT_FALSE(meta.blobState & StateFlags::committed);
+ EXPECT_TRUE(meta.blobState & BinaryStore::CommitState::Dirty);
+ }
+
+ commitData();
+ for (int i = 0; i < testIter; ++i)
+ {
+ EXPECT_TRUE(handler.stat(statTestSessionId, &meta));
+ EXPECT_EQ(meta.size, statTestDataToOverwrite.size());
+ EXPECT_TRUE(meta.blobState & OpenFlags::read);
+ EXPECT_TRUE(meta.blobState & OpenFlags::write);
+ EXPECT_TRUE(meta.blobState & StateFlags::committed);
+ EXPECT_TRUE(meta.blobState & BinaryStore::CommitState::Clean);
+ }
+}
+
+} // namespace blobs