blob: 9c37e47634891e5dba920ff398e55f5f47e09529 [file] [log] [blame]
Kun Yi64dc05c2018-12-19 13:19:03 -08001#include "binarystore.hpp"
2
Kun Yid2d8cb62019-01-10 15:06:12 -08003#include <unistd.h>
4
Kun Yi6baa7132019-01-08 21:21:02 -08005#include <algorithm>
6#include <blobs-ipmid/blobs.hpp>
Kun Yi70059172019-02-22 16:31:42 -08007#include <boost/endian/arithmetic.hpp>
Kun Yid2d8cb62019-01-10 15:06:12 -08008#include <cstdint>
9#include <memory>
Kun Yi8bcf79d2019-01-16 15:17:57 -080010#include <phosphor-logging/elog.hpp>
Kun Yid2d8cb62019-01-10 15:06:12 -080011#include <string>
12#include <vector>
13
14#include "binaryblob.pb.h"
15
16using std::size_t;
17using std::uint16_t;
18using std::uint32_t;
19using std::uint64_t;
20using std::uint8_t;
Kun Yi6baa7132019-01-08 21:21:02 -080021
Kun Yi64dc05c2018-12-19 13:19:03 -080022namespace binstore
23{
24
Kun Yi8bcf79d2019-01-16 15:17:57 -080025using namespace phosphor::logging;
26
Kun Yi64dc05c2018-12-19 13:19:03 -080027std::unique_ptr<BinaryStoreInterface>
28 BinaryStore::createFromConfig(const std::string& baseBlobId,
Kun Yi2765b642019-01-16 11:11:24 -080029 std::unique_ptr<SysFile> file,
30 uint32_t maxSize)
Kun Yi64dc05c2018-12-19 13:19:03 -080031{
Kun Yi2765b642019-01-16 11:11:24 -080032 if (baseBlobId.empty() || !file)
33 {
Kun Yi8bcf79d2019-01-16 15:17:57 -080034 log<level::ERR>("Unable to create binarystore from invalid config",
35 entry("BASE_ID=%s", baseBlobId.c_str()));
Kun Yi2765b642019-01-16 11:11:24 -080036 return nullptr;
37 }
38
39 auto store =
40 std::make_unique<BinaryStore>(baseBlobId, std::move(file), maxSize);
41
42 store->blob_.set_blob_base_id(store->baseBlobId_);
43
44 return std::move(store);
Kun Yi64dc05c2018-12-19 13:19:03 -080045}
46
47std::string BinaryStore::getBaseBlobId() const
48{
49 return baseBlobId_;
50}
51
52std::vector<std::string> BinaryStore::getBlobIds() const
53{
54 std::vector<std::string> result;
55 result.push_back(baseBlobId_);
56
Kun Yi0a940b92019-01-07 16:33:11 -080057 for (const auto& blob : blob_.blobs())
Kun Yi64dc05c2018-12-19 13:19:03 -080058 {
Kun Yi0a940b92019-01-07 16:33:11 -080059 result.push_back(blob.blob_id());
Kun Yi64dc05c2018-12-19 13:19:03 -080060 }
61
62 return result;
63}
64
65bool BinaryStore::openOrCreateBlob(const std::string& blobId, uint16_t flags)
66{
Kun Yi6baa7132019-01-08 21:21:02 -080067 if (!(flags & blobs::OpenFlags::read))
68 {
Kun Yi8bcf79d2019-01-16 15:17:57 -080069 log<level::ERR>("OpenFlags::read not specified when opening",
70 entry("BLOB_ID=%s", blobId.c_str()));
Kun Yi6baa7132019-01-08 21:21:02 -080071 return false;
72 }
73
74 if (currentBlob_ && (currentBlob_->blob_id() != blobId))
75 {
Kun Yi8bcf79d2019-01-16 15:17:57 -080076 log<level::ERR>("Already handling a different blob",
77 entry("EXPECTED=%s", currentBlob_->blob_id().c_str()),
78 entry("RECEIVED=%s", blobId.c_str()));
Kun Yi6baa7132019-01-08 21:21:02 -080079 return false;
80 }
81
82 writable_ = flags & blobs::OpenFlags::write;
83
Kun Yid297c9f2019-01-09 13:52:30 -080084 /* Load blob from sysfile if we know it might not match what we have.
85 * Note it will overwrite existing unsaved data per design. */
86 if (commitState_ != CommitState::Clean)
87 {
Kun Yi8bcf79d2019-01-16 15:17:57 -080088 log<level::NOTICE>("Try loading blob from persistent data",
89 entry("BLOB_ID=%s", blobId.c_str()));
Kun Yid297c9f2019-01-09 13:52:30 -080090 try
91 {
92 /* Parse length-prefixed format to protobuf */
Kun Yi70059172019-02-22 16:31:42 -080093 boost::endian::little_uint64_t size = 0;
94 file_->readToBuf(0, sizeof(size), reinterpret_cast<char*>(&size));
95
Kun Yid297c9f2019-01-09 13:52:30 -080096 if (!blob_.ParseFromString(
97 file_->readAsStr(sizeof(uint64_t), size)))
98 {
99 /* Fail to parse the data, which might mean no preexsiting data
100 * and is a valid case to handle */
Kun Yi8bcf79d2019-01-16 15:17:57 -0800101 log<level::WARNING>(
102 "Fail to parse. There might be no persisted blobs",
103 entry("BLOB_ID=%s", blobId.c_str()));
Kun Yid297c9f2019-01-09 13:52:30 -0800104 }
105 }
106 catch (const std::exception& e)
107 {
108 /* Read causes unexpected system-level failure */
Kun Yi8bcf79d2019-01-16 15:17:57 -0800109 log<level::ERR>("Reading from sysfile failed",
110 entry("ERROR=%s", e.what()));
Kun Yid297c9f2019-01-09 13:52:30 -0800111 return false;
112 }
113 }
114
Kun Yi6baa7132019-01-08 21:21:02 -0800115 /* Iterate and find if there is an existing blob with this id.
116 * blobsPtr points to a BinaryBlob container with STL-like semantics*/
117 auto blobsPtr = blob_.mutable_blobs();
118 auto blobIt =
119 std::find_if(blobsPtr->begin(), blobsPtr->end(),
120 [&](const auto& b) { return b.blob_id() == blobId; });
121
122 if (blobIt != blobsPtr->end())
123 {
124 currentBlob_ = &(*blobIt);
125 return true;
126 }
127
128 /* Otherwise, create the blob and append it */
129 currentBlob_ = blob_.add_blobs();
130 currentBlob_->set_blob_id(blobId);
131
Kun Yi8bcf79d2019-01-16 15:17:57 -0800132 log<level::NOTICE>("Created new blob", entry("BLOB_ID=%s", blobId.c_str()));
Kun Yi6baa7132019-01-08 21:21:02 -0800133 return true;
Kun Yi64dc05c2018-12-19 13:19:03 -0800134}
135
136bool BinaryStore::deleteBlob(const std::string& blobId)
137{
138 return false;
139}
140
141std::vector<uint8_t> BinaryStore::read(uint32_t offset, uint32_t requestedSize)
142{
Kun Yi6967b772019-02-22 13:48:12 -0800143 std::vector<uint8_t> result;
144
145 if (!currentBlob_)
146 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800147 log<level::ERR>("No open blob to read");
Kun Yi6967b772019-02-22 13:48:12 -0800148 return result;
149 }
150
151 auto dataPtr = currentBlob_->mutable_data();
152
153 /* If it is out of bound, return empty vector */
154 if (offset >= dataPtr->size())
155 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800156 log<level::ERR>("Read offset is beyond data size",
157 entry("MAX_SIZE=0x%x", dataPtr->size()),
158 entry("RECEIVED_OFFSET=0x%x", offset));
Kun Yi6967b772019-02-22 13:48:12 -0800159 return result;
160 }
161
162 uint32_t requestedEndPos = offset + requestedSize;
163
164 result = std::vector<uint8_t>(
165 dataPtr->begin() + offset,
166 std::min(dataPtr->begin() + requestedEndPos, dataPtr->end()));
Kun Yi64dc05c2018-12-19 13:19:03 -0800167 return result;
168}
169
170bool BinaryStore::write(uint32_t offset, const std::vector<uint8_t>& data)
171{
Kun Yi6967b772019-02-22 13:48:12 -0800172 if (!currentBlob_)
173 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800174 log<level::ERR>("No open blob to write");
Kun Yi6967b772019-02-22 13:48:12 -0800175 return false;
176 }
177
178 if (!writable_)
179 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800180 log<level::ERR>("Open blob is not writable");
Kun Yi6967b772019-02-22 13:48:12 -0800181 return false;
182 }
183
184 auto dataPtr = currentBlob_->mutable_data();
185
186 if (offset > dataPtr->size())
187 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800188 log<level::ERR>("Write would leave a gap with undefined data. Return.");
Kun Yi6967b772019-02-22 13:48:12 -0800189 return false;
190 }
191
192 /* Copy (overwrite) the data */
193 if (offset + data.size() > dataPtr->size())
194 {
195 dataPtr->resize(offset + data.size()); // not enough space, extend
196 }
197 std::copy(data.begin(), data.end(), dataPtr->begin() + offset);
198 return true;
Kun Yi64dc05c2018-12-19 13:19:03 -0800199}
200
201bool BinaryStore::commit()
202{
Kun Yi70059172019-02-22 16:31:42 -0800203 /* Store as little endian to be platform agnostic. Consistent with read. */
Kun Yid297c9f2019-01-09 13:52:30 -0800204 auto blobData = blob_.SerializeAsString();
Kun Yi70059172019-02-22 16:31:42 -0800205 boost::endian::little_uint64_t sizeLE = blobData.size();
206 std::string commitData(sizeLE.data(), sizeof(sizeLE));
Kun Yid297c9f2019-01-09 13:52:30 -0800207 commitData += blobData;
208
209 try
210 {
211 file_->writeStr(commitData, 0);
212 }
213 catch (const std::exception& e)
214 {
Kun Yid297c9f2019-01-09 13:52:30 -0800215 commitState_ = CommitState::CommitError;
Kun Yi8bcf79d2019-01-16 15:17:57 -0800216 log<level::ERR>("Writing to sysfile failed",
217 entry("ERROR=%s", e.what()));
Kun Yid297c9f2019-01-09 13:52:30 -0800218 return false;
219 };
220
221 commitState_ = CommitState::Clean;
222 return true;
Kun Yi64dc05c2018-12-19 13:19:03 -0800223}
224
225bool BinaryStore::close()
226{
Kun Yi6baa7132019-01-08 21:21:02 -0800227 currentBlob_ = nullptr;
228 writable_ = false;
Kun Yid297c9f2019-01-09 13:52:30 -0800229 commitState_ = CommitState::Dirty;
Kun Yi6baa7132019-01-08 21:21:02 -0800230 return true;
Kun Yi64dc05c2018-12-19 13:19:03 -0800231}
232
233bool BinaryStore::stat()
234{
235 return false;
236}
237
238} // namespace binstore