blob: 1307c3a008b543b6a305dc6e09b7f52bf5e732a8 [file] [log] [blame]
Kun Yi64dc05c2018-12-19 13:19:03 -08001#include "binarystore.hpp"
2
Kun Yi97be3af2019-03-05 22:43:41 -08003#include "sys_file.hpp"
4
5#include <sys/types.h>
Kun Yid2d8cb62019-01-10 15:06:12 -08006#include <unistd.h>
7
Kun Yi6baa7132019-01-08 21:21:02 -08008#include <algorithm>
9#include <blobs-ipmid/blobs.hpp>
Kun Yi70059172019-02-22 16:31:42 -080010#include <boost/endian/arithmetic.hpp>
Kun Yid2d8cb62019-01-10 15:06:12 -080011#include <cstdint>
12#include <memory>
Kun Yi8bcf79d2019-01-16 15:17:57 -080013#include <phosphor-logging/elog.hpp>
Kun Yid2d8cb62019-01-10 15:06:12 -080014#include <string>
15#include <vector>
16
17#include "binaryblob.pb.h"
18
19using std::size_t;
20using std::uint16_t;
21using std::uint32_t;
22using std::uint64_t;
23using std::uint8_t;
Kun Yi6baa7132019-01-08 21:21:02 -080024
Kun Yi64dc05c2018-12-19 13:19:03 -080025namespace binstore
26{
27
Kun Yi8bcf79d2019-01-16 15:17:57 -080028using namespace phosphor::logging;
29
Kun Yi64dc05c2018-12-19 13:19:03 -080030std::unique_ptr<BinaryStoreInterface>
31 BinaryStore::createFromConfig(const std::string& baseBlobId,
Patrick Venturee496b2b2020-07-09 13:49:05 -070032 std::unique_ptr<SysFile> file)
Kun Yi64dc05c2018-12-19 13:19:03 -080033{
Kun Yi2765b642019-01-16 11:11:24 -080034 if (baseBlobId.empty() || !file)
35 {
Kun Yi8bcf79d2019-01-16 15:17:57 -080036 log<level::ERR>("Unable to create binarystore from invalid config",
37 entry("BASE_ID=%s", baseBlobId.c_str()));
Kun Yi2765b642019-01-16 11:11:24 -080038 return nullptr;
39 }
40
Patrick Venturee496b2b2020-07-09 13:49:05 -070041 auto store = std::make_unique<BinaryStore>(baseBlobId, std::move(file));
Kun Yi2765b642019-01-16 11:11:24 -080042
Kun Yi8ca234e2019-03-04 10:14:45 -080043 if (!store->loadSerializedData())
44 {
45 return nullptr;
46 }
47
Kun Yi2765b642019-01-16 11:11:24 -080048 return std::move(store);
Kun Yi64dc05c2018-12-19 13:19:03 -080049}
50
Kun Yi97be3af2019-03-05 22:43:41 -080051bool BinaryStore::loadSerializedData()
52{
53 /* Load blob from sysfile if we know it might not match what we have.
54 * Note it will overwrite existing unsaved data per design. */
Kun Yie535a732019-04-03 19:03:25 -070055 if (commitState_ == CommitState::Clean ||
56 commitState_ == CommitState::Uninitialized)
Kun Yi97be3af2019-03-05 22:43:41 -080057 {
58 return true;
59 }
60
61 log<level::NOTICE>("Try loading blob from persistent data",
62 entry("BASE_ID=%s", baseBlobId_.c_str()));
63 try
64 {
65 /* Parse length-prefixed format to protobuf */
66 boost::endian::little_uint64_t size = 0;
67 file_->readToBuf(0, sizeof(size), reinterpret_cast<char*>(&size));
68
69 if (!blob_.ParseFromString(file_->readAsStr(sizeof(uint64_t), size)))
70 {
71 /* Fail to parse the data, which might mean no preexsiting blobs
72 * and is a valid case to handle. Simply init an empty binstore. */
Kun Yie535a732019-04-03 19:03:25 -070073 commitState_ = CommitState::Uninitialized;
Kun Yi97be3af2019-03-05 22:43:41 -080074 }
75 }
Kun Yic83d2fa2019-04-03 18:40:19 -070076 catch (const std::system_error& e)
Kun Yi97be3af2019-03-05 22:43:41 -080077 {
78 /* Read causes unexpected system-level failure */
79 log<level::ERR>("Reading from sysfile failed",
80 entry("ERROR=%s", e.what()));
81 return false;
82 }
Kun Yic83d2fa2019-04-03 18:40:19 -070083 catch (const std::exception& e)
84 {
Kun Yie535a732019-04-03 19:03:25 -070085 /* Non system error originates from junk value in 'size' */
86 commitState_ = CommitState::Uninitialized;
87 }
88
89 if (commitState_ == CommitState::Uninitialized)
90 {
91 log<level::WARNING>("Fail to parse. There might be no persisted blobs",
92 entry("BASE_ID=%s", baseBlobId_.c_str()));
Kun Yic83d2fa2019-04-03 18:40:19 -070093 return true;
94 }
Kun Yi97be3af2019-03-05 22:43:41 -080095
Kun Yi8ca234e2019-03-04 10:14:45 -080096 if (blob_.blob_base_id() != baseBlobId_)
97 {
98 /* Uh oh, stale data loaded. Clean it and commit. */
99 // TODO: it might be safer to add an option in config to error out
100 // instead of to overwrite.
101 log<level::ERR>("Stale blob data, resetting internals...",
102 entry("LOADED=%s", blob_.blob_base_id().c_str()),
103 entry("EXPECTED=%s", baseBlobId_.c_str()));
104 blob_.Clear();
105 blob_.set_blob_base_id(baseBlobId_);
106 return this->commit();
107 }
108
Kun Yi97be3af2019-03-05 22:43:41 -0800109 return true;
110}
111
Kun Yi64dc05c2018-12-19 13:19:03 -0800112std::string BinaryStore::getBaseBlobId() const
113{
114 return baseBlobId_;
115}
116
117std::vector<std::string> BinaryStore::getBlobIds() const
118{
119 std::vector<std::string> result;
120 result.push_back(baseBlobId_);
121
Kun Yi0a940b92019-01-07 16:33:11 -0800122 for (const auto& blob : blob_.blobs())
Kun Yi64dc05c2018-12-19 13:19:03 -0800123 {
Kun Yi0a940b92019-01-07 16:33:11 -0800124 result.push_back(blob.blob_id());
Kun Yi64dc05c2018-12-19 13:19:03 -0800125 }
126
127 return result;
128}
129
130bool BinaryStore::openOrCreateBlob(const std::string& blobId, uint16_t flags)
131{
Kun Yi6baa7132019-01-08 21:21:02 -0800132 if (!(flags & blobs::OpenFlags::read))
133 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800134 log<level::ERR>("OpenFlags::read not specified when opening",
135 entry("BLOB_ID=%s", blobId.c_str()));
Kun Yi6baa7132019-01-08 21:21:02 -0800136 return false;
137 }
138
139 if (currentBlob_ && (currentBlob_->blob_id() != blobId))
140 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800141 log<level::ERR>("Already handling a different blob",
142 entry("EXPECTED=%s", currentBlob_->blob_id().c_str()),
143 entry("RECEIVED=%s", blobId.c_str()));
Kun Yi6baa7132019-01-08 21:21:02 -0800144 return false;
145 }
146
147 writable_ = flags & blobs::OpenFlags::write;
148
Kun Yi97be3af2019-03-05 22:43:41 -0800149 /* If there are uncommitted data, discard them. */
150 if (!this->loadSerializedData())
Kun Yid297c9f2019-01-09 13:52:30 -0800151 {
Kun Yi97be3af2019-03-05 22:43:41 -0800152 return false;
Kun Yid297c9f2019-01-09 13:52:30 -0800153 }
154
Kun Yi6baa7132019-01-08 21:21:02 -0800155 /* Iterate and find if there is an existing blob with this id.
156 * blobsPtr points to a BinaryBlob container with STL-like semantics*/
157 auto blobsPtr = blob_.mutable_blobs();
158 auto blobIt =
159 std::find_if(blobsPtr->begin(), blobsPtr->end(),
160 [&](const auto& b) { return b.blob_id() == blobId; });
161
162 if (blobIt != blobsPtr->end())
163 {
164 currentBlob_ = &(*blobIt);
165 return true;
166 }
167
168 /* Otherwise, create the blob and append it */
169 currentBlob_ = blob_.add_blobs();
170 currentBlob_->set_blob_id(blobId);
171
Kun Yi1a25e0d2020-05-11 12:28:53 -0700172 commitState_ = CommitState::Dirty;
Kun Yi8bcf79d2019-01-16 15:17:57 -0800173 log<level::NOTICE>("Created new blob", entry("BLOB_ID=%s", blobId.c_str()));
Kun Yi6baa7132019-01-08 21:21:02 -0800174 return true;
Kun Yi64dc05c2018-12-19 13:19:03 -0800175}
176
177bool BinaryStore::deleteBlob(const std::string& blobId)
178{
179 return false;
180}
181
182std::vector<uint8_t> BinaryStore::read(uint32_t offset, uint32_t requestedSize)
183{
Kun Yi6967b772019-02-22 13:48:12 -0800184 std::vector<uint8_t> result;
185
186 if (!currentBlob_)
187 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800188 log<level::ERR>("No open blob to read");
Kun Yi6967b772019-02-22 13:48:12 -0800189 return result;
190 }
191
192 auto dataPtr = currentBlob_->mutable_data();
193
194 /* If it is out of bound, return empty vector */
195 if (offset >= dataPtr->size())
196 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800197 log<level::ERR>("Read offset is beyond data size",
198 entry("MAX_SIZE=0x%x", dataPtr->size()),
199 entry("RECEIVED_OFFSET=0x%x", offset));
Kun Yi6967b772019-02-22 13:48:12 -0800200 return result;
201 }
202
203 uint32_t requestedEndPos = offset + requestedSize;
204
205 result = std::vector<uint8_t>(
206 dataPtr->begin() + offset,
207 std::min(dataPtr->begin() + requestedEndPos, dataPtr->end()));
Kun Yi64dc05c2018-12-19 13:19:03 -0800208 return result;
209}
210
211bool BinaryStore::write(uint32_t offset, const std::vector<uint8_t>& data)
212{
Kun Yi6967b772019-02-22 13:48:12 -0800213 if (!currentBlob_)
214 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800215 log<level::ERR>("No open blob to write");
Kun Yi6967b772019-02-22 13:48:12 -0800216 return false;
217 }
218
219 if (!writable_)
220 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800221 log<level::ERR>("Open blob is not writable");
Kun Yi6967b772019-02-22 13:48:12 -0800222 return false;
223 }
224
225 auto dataPtr = currentBlob_->mutable_data();
226
227 if (offset > dataPtr->size())
228 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800229 log<level::ERR>("Write would leave a gap with undefined data. Return.");
Kun Yi6967b772019-02-22 13:48:12 -0800230 return false;
231 }
232
Kun Yi1a25e0d2020-05-11 12:28:53 -0700233 commitState_ = CommitState::Dirty;
Kun Yi6967b772019-02-22 13:48:12 -0800234 /* Copy (overwrite) the data */
235 if (offset + data.size() > dataPtr->size())
236 {
237 dataPtr->resize(offset + data.size()); // not enough space, extend
238 }
239 std::copy(data.begin(), data.end(), dataPtr->begin() + offset);
240 return true;
Kun Yi64dc05c2018-12-19 13:19:03 -0800241}
242
243bool BinaryStore::commit()
244{
Kun Yi70059172019-02-22 16:31:42 -0800245 /* Store as little endian to be platform agnostic. Consistent with read. */
Kun Yid297c9f2019-01-09 13:52:30 -0800246 auto blobData = blob_.SerializeAsString();
Kun Yi70059172019-02-22 16:31:42 -0800247 boost::endian::little_uint64_t sizeLE = blobData.size();
William A. Kennington III805ade32020-05-06 20:39:05 -0700248 std::string commitData(reinterpret_cast<const char*>(sizeLE.data()),
249 sizeof(sizeLE));
Kun Yid297c9f2019-01-09 13:52:30 -0800250 commitData += blobData;
251
252 try
253 {
254 file_->writeStr(commitData, 0);
255 }
256 catch (const std::exception& e)
257 {
Kun Yid297c9f2019-01-09 13:52:30 -0800258 commitState_ = CommitState::CommitError;
Kun Yi8bcf79d2019-01-16 15:17:57 -0800259 log<level::ERR>("Writing to sysfile failed",
260 entry("ERROR=%s", e.what()));
Kun Yid297c9f2019-01-09 13:52:30 -0800261 return false;
262 };
263
264 commitState_ = CommitState::Clean;
265 return true;
Kun Yi64dc05c2018-12-19 13:19:03 -0800266}
267
268bool BinaryStore::close()
269{
Kun Yi6baa7132019-01-08 21:21:02 -0800270 currentBlob_ = nullptr;
271 writable_ = false;
Kun Yid297c9f2019-01-09 13:52:30 -0800272 commitState_ = CommitState::Dirty;
Kun Yi6baa7132019-01-08 21:21:02 -0800273 return true;
Kun Yi64dc05c2018-12-19 13:19:03 -0800274}
275
Kun Yi1a25e0d2020-05-11 12:28:53 -0700276/*
277 * Sets |meta| with size and state of the blob. Returns |blobState| with
278 * standard definition from phosphor-ipmi-blobs header blob.hpp, plus OEM
279 * flag bits BinaryStore::CommitState.
280
281enum StateFlags
Kun Yi64dc05c2018-12-19 13:19:03 -0800282{
Kun Yi1a25e0d2020-05-11 12:28:53 -0700283 open_read = (1 << 0),
284 open_write = (1 << 1),
285 committing = (1 << 2),
286 committed = (1 << 3),
287 commit_error = (1 << 4),
288};
289
290enum CommitState
291{
292 Dirty = (1 << 8), // In-memory data might not match persisted data
293 Clean = (1 << 9), // In-memory data matches persisted data
294 Uninitialized = (1 << 10), // Cannot find persisted data
295 CommitError = (1 << 11) // Error happened during committing
296};
297
298*/
299bool BinaryStore::stat(blobs::BlobMeta* meta)
300{
301 uint16_t blobState = blobs::StateFlags::open_read;
302 if (writable_)
303 {
304 blobState |= blobs::StateFlags::open_write;
305 }
306
307 if (commitState_ == CommitState::Clean)
308 {
309 blobState |= blobs::StateFlags::committed;
310 }
311 else if (commitState_ == CommitState::CommitError)
312 {
313 blobState |= blobs::StateFlags::commit_error;
314 }
315 blobState |= commitState_;
316
317 if (currentBlob_)
318 {
319 meta->size = currentBlob_->data().size();
320 }
321 else
322 {
323 meta->size = 0;
324 }
325 meta->blobState = blobState;
326
327 return true;
Kun Yi64dc05c2018-12-19 13:19:03 -0800328}
329
330} // namespace binstore