build: Refactor the build structure of the project
Moved all header files to `include` and all cpp files to `src`.
Updated the meson.build accordingly.
Change-Id: I9e26197b9c73b5e284cfc9d0d78a234546c282ad
Signed-off-by: Willy Tu <wltu@google.com>
diff --git a/include/binarystore.hpp b/include/binarystore.hpp
new file mode 100644
index 0000000..792de4a
--- /dev/null
+++ b/include/binarystore.hpp
@@ -0,0 +1,128 @@
+#pragma once
+
+#include "binarystore_interface.hpp"
+#include "sys_file.hpp"
+
+#include <unistd.h>
+
+#include <blobs-ipmid/blobs.hpp>
+#include <cstdint>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include "binaryblob.pb.h"
+
+using std::size_t;
+using std::uint16_t;
+using std::uint32_t;
+using std::uint64_t;
+using std::uint8_t;
+
+namespace binstore
+{
+
+/**
+ * @class BinaryStore instantiates a concrete implementation of
+ * BinaryStoreInterface. The dependency on file is injected through its
+ * constructor.
+ */
+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,
+ std::optional<uint32_t> maxSize = std::nullopt) :
+ baseBlobId_(baseBlobId),
+ file_(std::move(file)), maxSize(maxSize)
+ {
+ blob_.set_blob_base_id(baseBlobId_);
+ if (maxSize)
+ {
+ blob_.set_max_size_bytes(*maxSize);
+ }
+ }
+
+ BinaryStore(std::unique_ptr<SysFile> file, bool readOnly = false,
+ std::optional<uint32_t> maxSize = std::nullopt) :
+ readOnly_{readOnly},
+ file_(std::move(file)), maxSize(maxSize)
+ {
+ if (maxSize)
+ {
+ blob_.set_max_size_bytes(*maxSize);
+ }
+ }
+
+ ~BinaryStore() = default;
+
+ BinaryStore(const BinaryStore&) = delete;
+ BinaryStore& operator=(const BinaryStore&) = delete;
+ BinaryStore(BinaryStore&&) = default;
+ BinaryStore& operator=(BinaryStore&&) = default;
+
+ std::string getBaseBlobId() const override;
+ std::vector<std::string> getBlobIds() const override;
+ bool openOrCreateBlob(const std::string& blobId, uint16_t flags) override;
+ bool deleteBlob(const std::string& blobId) override;
+ std::vector<uint8_t> read(uint32_t offset, uint32_t requestedSize) override;
+ std::vector<uint8_t> readBlob(const std::string& blobId) const override;
+ bool write(uint32_t offset, const std::vector<uint8_t>& data) override;
+ bool commit() override;
+ bool close() override;
+ bool stat(blobs::BlobMeta* meta) override;
+
+ /**
+ * Helper factory method to create a BinaryStore instance
+ * @param baseBlobId: base id for the created instance
+ * @param sysFile: system file object for storing binary
+ * @returns unique_ptr to constructed BinaryStore. Caller should take
+ * ownership of the instance.
+ */
+ static std::unique_ptr<BinaryStoreInterface>
+ createFromConfig(const std::string& baseBlobId,
+ std::unique_ptr<SysFile> file,
+ std::optional<uint32_t> maxSize = std::nullopt);
+
+ /**
+ * Helper factory method to create a BinaryStore instance
+ * This function should be used with existing stores. It reads
+ * the baseBlobId name from the storage.
+ * @param sysFile: system file object for storing binary
+ * @param readOnly: if true, open the store in read only mode
+ * @returns unique_ptr to constructed BinaryStore.
+ */
+ static std::unique_ptr<BinaryStoreInterface>
+ createFromFile(std::unique_ptr<SysFile> file, bool readOnly = true,
+ std::optional<uint32_t> maxSize = std::nullopt);
+
+ private:
+ /* Load the serialized data from sysfile if commit state is dirty.
+ * Returns False if encountered error when loading */
+ bool loadSerializedData();
+
+ std::string baseBlobId_;
+ binaryblobproto::BinaryBlobBase blob_;
+ binaryblobproto::BinaryBlob* currentBlob_ = nullptr;
+ /* True if current blob is writable */
+ bool writable_ = false;
+ /* True if the entire store (not just individual blobs) is read only */
+ bool readOnly_ = false;
+ std::unique_ptr<SysFile> file_ = nullptr;
+ CommitState commitState_ = CommitState::Dirty;
+ std::optional<uint32_t> maxSize;
+};
+
+} // namespace binstore
diff --git a/include/binarystore_interface.hpp b/include/binarystore_interface.hpp
new file mode 100644
index 0000000..ba6dca3
--- /dev/null
+++ b/include/binarystore_interface.hpp
@@ -0,0 +1,100 @@
+#pragma once
+
+#include <blobs-ipmid/blobs.hpp>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <vector>
+
+using std::size_t;
+using std::uint16_t;
+using std::uint32_t;
+using std::uint64_t;
+using std::uint8_t;
+
+namespace binstore
+{
+
+/**
+ * @class BinaryStoreInterface is an abstraction for a storage location.
+ * Each instance would be uniquely identified by a baseId string.
+ */
+class BinaryStoreInterface
+{
+ public:
+ virtual ~BinaryStoreInterface() = default;
+
+ /**
+ * @returns baseId string of the storage.
+ */
+ virtual std::string getBaseBlobId() const = 0;
+
+ /**
+ * @returns List of all open blob IDs, plus the base.
+ */
+ virtual std::vector<std::string> getBlobIds() const = 0;
+
+ /**
+ * Opens a blob given its name. If there is no one, create one.
+ * @param blobId: The blob id to operate on.
+ * @param flags: Either read flag or r/w flag has to be specified.
+ * @returns True if open/create successfully.
+ */
+ virtual bool openOrCreateBlob(const std::string& blobId,
+ uint16_t flags) = 0;
+
+ /**
+ * Deletes a blob given its name. If there is no one,
+ * @param blobId: The blob id to operate on.
+ * @returns True if deleted.
+ */
+ virtual bool deleteBlob(const std::string& blobId) = 0;
+
+ /**
+ * Reads data from the currently opened blob.
+ * @param offset: offset into the blob to read
+ * @param requestedSize: how many bytes to read
+ * @returns Bytes able to read. Returns empty if nothing can be read or
+ * if there is no open blob.
+ */
+ virtual std::vector<uint8_t> read(uint32_t offset,
+ uint32_t requestedSize) = 0;
+
+ /**
+ * Reads all data from the blob
+ * @param blobId: The blob id to operate on.
+ * @returns Bytes able to read. Returns empty if nothing can be read or
+ * if there is no such blob.
+ */
+ virtual std::vector<uint8_t> readBlob(const std::string& blobId) const = 0;
+
+ /**
+ * Writes data to the currently openend blob.
+ * @param offset: offset into the blob to write
+ * @param data: bytes to write
+ * @returns True if able to write the entire data successfully
+ */
+ virtual bool write(uint32_t offset, const std::vector<uint8_t>& data) = 0;
+
+ /**
+ * Commits data to the persistent storage specified during blob init.
+ * @returns True if able to write data to sysfile successfully
+ */
+ virtual bool commit() = 0;
+
+ /**
+ * Closes blob, which prevents further modifications. Uncommitted data will
+ * be lost.
+ * @returns True if able to close the blob successfully
+ */
+ virtual bool close() = 0;
+
+ /**
+ * 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(blobs::BlobMeta* meta) = 0;
+};
+
+} // namespace binstore
diff --git a/include/binarystore_mock.hpp b/include/binarystore_mock.hpp
new file mode 100644
index 0000000..9360dd3
--- /dev/null
+++ b/include/binarystore_mock.hpp
@@ -0,0 +1,65 @@
+#pragma once
+
+#include "binarystore.hpp"
+#include "binarystore_interface.hpp"
+#include "sys_file.hpp"
+
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include <gmock/gmock.h>
+
+using ::testing::Invoke;
+
+namespace binstore
+{
+
+class MockBinaryStore : public BinaryStoreInterface
+{
+ public:
+ MockBinaryStore(const std::string& baseBlobId,
+ std::unique_ptr<SysFile> file,
+ std::optional<uint32_t> maxSize = std::nullopt) :
+ real_store_(baseBlobId, std::move(file), maxSize)
+ {
+ // Implemented calls in BinaryStore will be directed to the real object.
+ ON_CALL(*this, getBaseBlobId)
+ .WillByDefault(Invoke(&real_store_, &BinaryStore::getBaseBlobId));
+ ON_CALL(*this, getBlobIds)
+ .WillByDefault(Invoke(&real_store_, &BinaryStore::getBlobIds));
+ ON_CALL(*this, openOrCreateBlob)
+ .WillByDefault(
+ Invoke(&real_store_, &BinaryStore::openOrCreateBlob));
+ ON_CALL(*this, close)
+ .WillByDefault(Invoke(&real_store_, &BinaryStore::close));
+ ON_CALL(*this, read)
+ .WillByDefault(Invoke(&real_store_, &BinaryStore::read));
+ ON_CALL(*this, write)
+ .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>());
+ MOCK_METHOD2(openOrCreateBlob, bool(const std::string&, uint16_t));
+ MOCK_METHOD1(deleteBlob, bool(const std::string&));
+ MOCK_METHOD2(read, std::vector<uint8_t>(uint32_t, uint32_t));
+ MOCK_METHOD2(write, bool(uint32_t, const std::vector<uint8_t>&));
+ MOCK_METHOD0(commit, bool());
+ MOCK_METHOD0(close, bool());
+ MOCK_METHOD1(stat, bool(blobs::BlobMeta* meta));
+
+ std::vector<uint8_t> readBlob(const std::string& blobId) const override
+ {
+ return real_store_.readBlob(blobId);
+ }
+
+ private:
+ BinaryStore real_store_;
+};
+
+} // namespace binstore
diff --git a/include/handler.hpp b/include/handler.hpp
new file mode 100644
index 0000000..2a75a57
--- /dev/null
+++ b/include/handler.hpp
@@ -0,0 +1,68 @@
+#pragma once
+
+#include "binarystore.hpp"
+
+#include <blobs-ipmid/blobs.hpp>
+#include <cstdint>
+#include <map>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+using std::size_t;
+using std::uint16_t;
+using std::uint32_t;
+using std::uint64_t;
+using std::uint8_t;
+
+namespace blobs
+{
+
+class BinaryStoreBlobHandler : public GenericBlobInterface
+{
+ public:
+ BinaryStoreBlobHandler() = default;
+ ~BinaryStoreBlobHandler() = default;
+ BinaryStoreBlobHandler(const BinaryStoreBlobHandler&) = delete;
+ BinaryStoreBlobHandler& operator=(const BinaryStoreBlobHandler&) = delete;
+ BinaryStoreBlobHandler(BinaryStoreBlobHandler&&) = default;
+ BinaryStoreBlobHandler& operator=(BinaryStoreBlobHandler&&) = default;
+
+ bool canHandleBlob(const std::string& path) override;
+ std::vector<std::string> getBlobIds() override;
+ bool deleteBlob(const std::string& path) override;
+ bool stat(const std::string& path, struct BlobMeta* meta) override;
+ bool open(uint16_t session, uint16_t flags,
+ const std::string& path) override;
+ std::vector<uint8_t> read(uint16_t session, uint32_t offset,
+ 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;
+ bool expire(uint16_t session) override;
+
+ /**
+ * Registers a binarystore in the main handler. Once called, handler will
+ * take over the ownership of of enclosed binary store.
+ *
+ * @param store: pointer to a valid BinaryStore.
+ * TODO: a minimal amount of error checking would be better
+ */
+ void addNewBinaryStore(
+ std::unique_ptr<binstore::BinaryStoreInterface> store);
+
+ private:
+ /* map of baseId: binaryStore, which has a 1:1 relationship. */
+ std::map<std::string, std::unique_ptr<binstore::BinaryStoreInterface>>
+ stores_;
+
+ /* map of sessionId: open binaryStore pointer. */
+ std::unordered_map<uint16_t, binstore::BinaryStoreInterface*> sessions_;
+};
+
+} // namespace blobs
diff --git a/include/meson.build b/include/meson.build
new file mode 100644
index 0000000..e4ada8f
--- /dev/null
+++ b/include/meson.build
@@ -0,0 +1 @@
+blobstore_includes = include_directories('.')
diff --git a/include/parse_config.hpp b/include/parse_config.hpp
new file mode 100644
index 0000000..b778923
--- /dev/null
+++ b/include/parse_config.hpp
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <cstdint>
+#include <nlohmann/json.hpp>
+#include <string>
+
+using std::uint32_t;
+using json = nlohmann::json;
+
+namespace conf
+{
+
+struct BinaryBlobConfig
+{
+ std::string blobBaseId; // Required
+ std::string sysFilePath; // Required
+ uint32_t offsetBytes; // Optional
+ uint32_t maxSizeBytes; // Optional
+};
+
+/**
+ * @brief Parse parameters from a config json
+ * @param j: input json object
+ * @param config: output BinaryBlobConfig
+ * @throws: exception if config doesn't have required fields
+ */
+static inline void parseFromConfigFile(const json& j, BinaryBlobConfig& config)
+{
+ j.at("blobBaseId").get_to(config.blobBaseId);
+ j.at("sysFilePath").get_to(config.sysFilePath);
+ config.offsetBytes = j.value("offsetBytes", 0);
+ config.maxSizeBytes = j.value("maxSizeBytes", 0);
+}
+
+} // namespace conf
diff --git a/include/sys.hpp b/include/sys.hpp
new file mode 100644
index 0000000..230665b
--- /dev/null
+++ b/include/sys.hpp
@@ -0,0 +1,46 @@
+#pragma once
+
+#include <unistd.h>
+
+namespace binstore
+{
+
+namespace internal
+{
+
+/** @class Sys
+ * @brief Overridable direct syscall interface
+ *
+ * TODO: factor this out into a syscall class shared by all upstream projects
+ */
+class Sys
+{
+ public:
+ virtual ~Sys() = default;
+ virtual int open(const char* pathname, int flags) const = 0;
+ virtual int close(int fd) const = 0;
+ virtual off_t lseek(int fd, off_t offset, int whence) const = 0;
+ virtual ssize_t read(int fd, void* buf, size_t count) const = 0;
+ virtual ssize_t write(int fd, const void* buf, size_t count) const = 0;
+};
+
+/** @class SysImpl
+ * @brief syscall concrete implementation
+ * @details Passes through all calls to the normal linux syscalls
+ */
+class SysImpl : public Sys
+{
+ public:
+ int open(const char* pathname, int flags) const override;
+ int close(int fd) const override;
+ off_t lseek(int fd, off_t offset, int whence) const override;
+ ssize_t read(int fd, void* buf, size_t count) const override;
+ ssize_t write(int fd, const void* buf, size_t count) const override;
+};
+
+/** @brief Default instantiation of sys */
+extern SysImpl sys_impl;
+
+} // namespace internal
+
+} // namespace binstore
diff --git a/include/sys_file.hpp b/include/sys_file.hpp
new file mode 100644
index 0000000..a45f881
--- /dev/null
+++ b/include/sys_file.hpp
@@ -0,0 +1,61 @@
+#pragma once
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <string>
+
+namespace binstore
+{
+
+/**
+ * @brief Represents a file that supports read/write semantics
+ * TODO: leverage stdplus's support for smart file descriptors when it's ready.
+ */
+class SysFile
+{
+ public:
+ virtual ~SysFile() = default;
+
+ /**
+ * @brief Reads content at pos to char* buffer
+ * @param pos The byte pos into the file to read from
+ * @param count How many bytes to read
+ * @param buf Output data
+ * @returns The size of data read
+ * @throws std::system_error if read operation cannot be completed
+ */
+ virtual size_t readToBuf(size_t pos, size_t count, char* buf) const = 0;
+
+ /**
+ * @brief Reads content at pos
+ * @param pos The byte pos into the file to read from
+ * @param count How many bytes to read
+ * @returns The data read in a vector, whose size might be smaller than
+ * count if there is not enough to read. Might be empty if the
+ * count specified is too large to even fit in a std::string.
+ * @throws std::system_error if read operation cannot be completed
+ * std::bad_alloc if cannot construct string with 'count' size
+ */
+ virtual std::string readAsStr(size_t pos, size_t count) const = 0;
+
+ /**
+ * @brief Reads all the content in file after pos
+ * @param pos The byte pos to read from
+ * @returns The data read in a vector, whose size might be smaller than
+ * count if there is not enough to read.
+ * @throws std::system_error if read operation cannot be completed
+ */
+ virtual std::string readRemainingAsStr(size_t pos) const = 0;
+
+ /**
+ * @brief Writes all of data into file at pos
+ * @param pos The byte pos to write
+ * @returns void
+ * @throws std::system_error if write operation cannot be completed or
+ * not all of the bytes can be written
+ */
+ virtual void writeStr(const std::string& data, size_t pos) = 0;
+};
+
+} // namespace binstore
diff --git a/include/sys_file_impl.hpp b/include/sys_file_impl.hpp
new file mode 100644
index 0000000..c74c965
--- /dev/null
+++ b/include/sys_file_impl.hpp
@@ -0,0 +1,43 @@
+#pragma once
+
+#include "sys.hpp"
+#include "sys_file.hpp"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <string>
+
+namespace binstore
+{
+
+class SysFileImpl : public SysFile
+{
+ public:
+ /**
+ * @brief Constructs sysFile specified by path and offset
+ * @param path The file path
+ * @param offset The byte offset relatively. Reading a sysfile at position 0
+ * actually reads underlying file at 'offset'
+ * @param sys Syscall operation interface
+ */
+ explicit SysFileImpl(const std::string& path, size_t offset = 0,
+ const internal::Sys* sys = &internal::sys_impl);
+ ~SysFileImpl();
+ SysFileImpl() = delete;
+ SysFileImpl(const SysFileImpl&) = delete;
+ SysFileImpl& operator=(SysFileImpl) = delete;
+
+ size_t readToBuf(size_t pos, size_t count, char* buf) const override;
+ std::string readAsStr(size_t pos, size_t count) const override;
+ std::string readRemainingAsStr(size_t pos) const override;
+ void writeStr(const std::string& data, size_t pos) override;
+
+ private:
+ int fd_;
+ size_t offset_;
+ void lseek(size_t pos) const;
+ const internal::Sys* sys;
+};
+
+} // namespace binstore