blob: e1cd03d85d21b345e1798a82d97e99820fbba392 [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>
Maksym Sloykoeb274112021-10-27 23:48:32 +000012#include <ipmid/handler.hpp>
Kun Yid2d8cb62019-01-10 15:06:12 -080013#include <memory>
Kun Yi8bcf79d2019-01-16 15:17:57 -080014#include <phosphor-logging/elog.hpp>
Kun Yid2d8cb62019-01-10 15:06:12 -080015#include <string>
16#include <vector>
17
18#include "binaryblob.pb.h"
19
20using std::size_t;
21using std::uint16_t;
22using std::uint32_t;
23using std::uint64_t;
24using std::uint8_t;
Kun Yi6baa7132019-01-08 21:21:02 -080025
Kun Yi64dc05c2018-12-19 13:19:03 -080026namespace binstore
27{
28
Kun Yi8bcf79d2019-01-16 15:17:57 -080029using namespace phosphor::logging;
30
Kun Yi64dc05c2018-12-19 13:19:03 -080031std::unique_ptr<BinaryStoreInterface>
32 BinaryStore::createFromConfig(const std::string& baseBlobId,
Patrick Venturee496b2b2020-07-09 13:49:05 -070033 std::unique_ptr<SysFile> file)
Kun Yi64dc05c2018-12-19 13:19:03 -080034{
Kun Yi2765b642019-01-16 11:11:24 -080035 if (baseBlobId.empty() || !file)
36 {
Kun Yi8bcf79d2019-01-16 15:17:57 -080037 log<level::ERR>("Unable to create binarystore from invalid config",
38 entry("BASE_ID=%s", baseBlobId.c_str()));
Kun Yi2765b642019-01-16 11:11:24 -080039 return nullptr;
40 }
41
Patrick Venturee496b2b2020-07-09 13:49:05 -070042 auto store = std::make_unique<BinaryStore>(baseBlobId, std::move(file));
Kun Yi2765b642019-01-16 11:11:24 -080043
Kun Yi8ca234e2019-03-04 10:14:45 -080044 if (!store->loadSerializedData())
45 {
46 return nullptr;
47 }
48
Willy Tu351a5d02021-12-07 19:48:40 -080049 return store;
Kun Yi64dc05c2018-12-19 13:19:03 -080050}
51
Maksym Sloykoeb274112021-10-27 23:48:32 +000052std::unique_ptr<BinaryStoreInterface>
53 BinaryStore::createFromFile(std::unique_ptr<SysFile> file, bool readOnly)
54{
55 if (!file)
56 {
57 log<level::ERR>("Unable to create binarystore from invalid file");
58 return nullptr;
59 }
60
61 auto store = std::make_unique<BinaryStore>(std::move(file), readOnly);
62
63 if (!store->loadSerializedData())
64 {
65 return nullptr;
66 }
67
Willy Tu351a5d02021-12-07 19:48:40 -080068 return store;
Maksym Sloykoeb274112021-10-27 23:48:32 +000069}
70
Kun Yi97be3af2019-03-05 22:43:41 -080071bool BinaryStore::loadSerializedData()
72{
73 /* Load blob from sysfile if we know it might not match what we have.
74 * Note it will overwrite existing unsaved data per design. */
Kun Yie535a732019-04-03 19:03:25 -070075 if (commitState_ == CommitState::Clean ||
76 commitState_ == CommitState::Uninitialized)
Kun Yi97be3af2019-03-05 22:43:41 -080077 {
78 return true;
79 }
80
81 log<level::NOTICE>("Try loading blob from persistent data",
82 entry("BASE_ID=%s", baseBlobId_.c_str()));
83 try
84 {
85 /* Parse length-prefixed format to protobuf */
86 boost::endian::little_uint64_t size = 0;
87 file_->readToBuf(0, sizeof(size), reinterpret_cast<char*>(&size));
88
89 if (!blob_.ParseFromString(file_->readAsStr(sizeof(uint64_t), size)))
90 {
91 /* Fail to parse the data, which might mean no preexsiting blobs
92 * and is a valid case to handle. Simply init an empty binstore. */
Kun Yie535a732019-04-03 19:03:25 -070093 commitState_ = CommitState::Uninitialized;
Kun Yi97be3af2019-03-05 22:43:41 -080094 }
95 }
Kun Yic83d2fa2019-04-03 18:40:19 -070096 catch (const std::system_error& e)
Kun Yi97be3af2019-03-05 22:43:41 -080097 {
98 /* Read causes unexpected system-level failure */
99 log<level::ERR>("Reading from sysfile failed",
100 entry("ERROR=%s", e.what()));
101 return false;
102 }
Kun Yic83d2fa2019-04-03 18:40:19 -0700103 catch (const std::exception& e)
104 {
Kun Yie535a732019-04-03 19:03:25 -0700105 /* Non system error originates from junk value in 'size' */
106 commitState_ = CommitState::Uninitialized;
107 }
108
109 if (commitState_ == CommitState::Uninitialized)
110 {
111 log<level::WARNING>("Fail to parse. There might be no persisted blobs",
112 entry("BASE_ID=%s", baseBlobId_.c_str()));
Kun Yic83d2fa2019-04-03 18:40:19 -0700113 return true;
114 }
Kun Yi97be3af2019-03-05 22:43:41 -0800115
Maksym Sloykoeb274112021-10-27 23:48:32 +0000116 if (blob_.blob_base_id() != baseBlobId_ && !readOnly_)
Kun Yi8ca234e2019-03-04 10:14:45 -0800117 {
118 /* Uh oh, stale data loaded. Clean it and commit. */
119 // TODO: it might be safer to add an option in config to error out
120 // instead of to overwrite.
121 log<level::ERR>("Stale blob data, resetting internals...",
122 entry("LOADED=%s", blob_.blob_base_id().c_str()),
123 entry("EXPECTED=%s", baseBlobId_.c_str()));
124 blob_.Clear();
125 blob_.set_blob_base_id(baseBlobId_);
126 return this->commit();
127 }
128
Kun Yi97be3af2019-03-05 22:43:41 -0800129 return true;
130}
131
Kun Yi64dc05c2018-12-19 13:19:03 -0800132std::string BinaryStore::getBaseBlobId() const
133{
Maksym Sloykoeb274112021-10-27 23:48:32 +0000134 if (!baseBlobId_.empty())
135 {
136 return baseBlobId_;
137 }
138
139 return blob_.blob_base_id();
Kun Yi64dc05c2018-12-19 13:19:03 -0800140}
141
142std::vector<std::string> BinaryStore::getBlobIds() const
143{
144 std::vector<std::string> result;
Maksym Sloykoeb274112021-10-27 23:48:32 +0000145 result.push_back(getBaseBlobId());
Kun Yi64dc05c2018-12-19 13:19:03 -0800146
Kun Yi0a940b92019-01-07 16:33:11 -0800147 for (const auto& blob : blob_.blobs())
Kun Yi64dc05c2018-12-19 13:19:03 -0800148 {
Kun Yi0a940b92019-01-07 16:33:11 -0800149 result.push_back(blob.blob_id());
Kun Yi64dc05c2018-12-19 13:19:03 -0800150 }
151
152 return result;
153}
154
155bool BinaryStore::openOrCreateBlob(const std::string& blobId, uint16_t flags)
156{
Kun Yi6baa7132019-01-08 21:21:02 -0800157 if (!(flags & blobs::OpenFlags::read))
158 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800159 log<level::ERR>("OpenFlags::read not specified when opening",
160 entry("BLOB_ID=%s", blobId.c_str()));
Kun Yi6baa7132019-01-08 21:21:02 -0800161 return false;
162 }
163
164 if (currentBlob_ && (currentBlob_->blob_id() != blobId))
165 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800166 log<level::ERR>("Already handling a different blob",
167 entry("EXPECTED=%s", currentBlob_->blob_id().c_str()),
168 entry("RECEIVED=%s", blobId.c_str()));
Kun Yi6baa7132019-01-08 21:21:02 -0800169 return false;
170 }
171
Maksym Sloykoeb274112021-10-27 23:48:32 +0000172 if (readOnly_ && (flags & blobs::OpenFlags::write))
173 {
174 log<level::ERR>("Can't open the blob for writing: read-only store",
175 entry("BLOB_ID=%s", blobId.c_str()));
176 return false;
177 }
178
Kun Yi6baa7132019-01-08 21:21:02 -0800179 writable_ = flags & blobs::OpenFlags::write;
180
Kun Yi97be3af2019-03-05 22:43:41 -0800181 /* If there are uncommitted data, discard them. */
182 if (!this->loadSerializedData())
Kun Yid297c9f2019-01-09 13:52:30 -0800183 {
Kun Yi97be3af2019-03-05 22:43:41 -0800184 return false;
Kun Yid297c9f2019-01-09 13:52:30 -0800185 }
186
Kun Yi6baa7132019-01-08 21:21:02 -0800187 /* Iterate and find if there is an existing blob with this id.
188 * blobsPtr points to a BinaryBlob container with STL-like semantics*/
189 auto blobsPtr = blob_.mutable_blobs();
190 auto blobIt =
191 std::find_if(blobsPtr->begin(), blobsPtr->end(),
192 [&](const auto& b) { return b.blob_id() == blobId; });
193
194 if (blobIt != blobsPtr->end())
195 {
196 currentBlob_ = &(*blobIt);
197 return true;
198 }
199
200 /* Otherwise, create the blob and append it */
Maksym Sloykoeb274112021-10-27 23:48:32 +0000201 if (readOnly_)
202 {
203 return false;
204 }
205 else
206 {
207 currentBlob_ = blob_.add_blobs();
208 currentBlob_->set_blob_id(blobId);
Kun Yi6baa7132019-01-08 21:21:02 -0800209
Maksym Sloykoeb274112021-10-27 23:48:32 +0000210 commitState_ = CommitState::Dirty;
211 log<level::NOTICE>("Created new blob",
212 entry("BLOB_ID=%s", blobId.c_str()));
213 }
214
Kun Yi6baa7132019-01-08 21:21:02 -0800215 return true;
Kun Yi64dc05c2018-12-19 13:19:03 -0800216}
217
Willy Tu351a5d02021-12-07 19:48:40 -0800218bool BinaryStore::deleteBlob(const std::string&)
Kun Yi64dc05c2018-12-19 13:19:03 -0800219{
220 return false;
221}
222
223std::vector<uint8_t> BinaryStore::read(uint32_t offset, uint32_t requestedSize)
224{
Kun Yi6967b772019-02-22 13:48:12 -0800225 std::vector<uint8_t> result;
226
227 if (!currentBlob_)
228 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800229 log<level::ERR>("No open blob to read");
Kun Yi6967b772019-02-22 13:48:12 -0800230 return result;
231 }
232
233 auto dataPtr = currentBlob_->mutable_data();
234
235 /* If it is out of bound, return empty vector */
236 if (offset >= dataPtr->size())
237 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800238 log<level::ERR>("Read offset is beyond data size",
239 entry("MAX_SIZE=0x%x", dataPtr->size()),
240 entry("RECEIVED_OFFSET=0x%x", offset));
Kun Yi6967b772019-02-22 13:48:12 -0800241 return result;
242 }
243
244 uint32_t requestedEndPos = offset + requestedSize;
245
246 result = std::vector<uint8_t>(
247 dataPtr->begin() + offset,
248 std::min(dataPtr->begin() + requestedEndPos, dataPtr->end()));
Kun Yi64dc05c2018-12-19 13:19:03 -0800249 return result;
250}
251
Maksym Sloykoeb274112021-10-27 23:48:32 +0000252std::vector<uint8_t> BinaryStore::readBlob(const std::string& blobId) const
253{
254 const auto blobs = blob_.blobs();
255 const auto blobIt =
256 std::find_if(blobs.begin(), blobs.end(),
257 [&](const auto& b) { return b.blob_id() == blobId; });
258
259 if (blobIt == blobs.end())
260 {
261 throw ipmi::HandlerCompletion(ipmi::ccUnspecifiedError);
262 }
263
264 const auto blobData = blobIt->data();
265
266 return std::vector<uint8_t>(blobData.begin(), blobData.end());
267}
268
Kun Yi64dc05c2018-12-19 13:19:03 -0800269bool BinaryStore::write(uint32_t offset, const std::vector<uint8_t>& data)
270{
Kun Yi6967b772019-02-22 13:48:12 -0800271 if (!currentBlob_)
272 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800273 log<level::ERR>("No open blob to write");
Kun Yi6967b772019-02-22 13:48:12 -0800274 return false;
275 }
276
277 if (!writable_)
278 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800279 log<level::ERR>("Open blob is not writable");
Kun Yi6967b772019-02-22 13:48:12 -0800280 return false;
281 }
282
283 auto dataPtr = currentBlob_->mutable_data();
284
285 if (offset > dataPtr->size())
286 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800287 log<level::ERR>("Write would leave a gap with undefined data. Return.");
Kun Yi6967b772019-02-22 13:48:12 -0800288 return false;
289 }
290
Kun Yi1a25e0d2020-05-11 12:28:53 -0700291 commitState_ = CommitState::Dirty;
Kun Yi6967b772019-02-22 13:48:12 -0800292 /* Copy (overwrite) the data */
293 if (offset + data.size() > dataPtr->size())
294 {
295 dataPtr->resize(offset + data.size()); // not enough space, extend
296 }
297 std::copy(data.begin(), data.end(), dataPtr->begin() + offset);
298 return true;
Kun Yi64dc05c2018-12-19 13:19:03 -0800299}
300
301bool BinaryStore::commit()
302{
Maksym Sloykoeb274112021-10-27 23:48:32 +0000303 if (readOnly_)
304 {
305 log<level::ERR>("ReadOnly blob, not committing");
306 return false;
307 }
308
Kun Yi70059172019-02-22 16:31:42 -0800309 /* Store as little endian to be platform agnostic. Consistent with read. */
Kun Yid297c9f2019-01-09 13:52:30 -0800310 auto blobData = blob_.SerializeAsString();
Kun Yi70059172019-02-22 16:31:42 -0800311 boost::endian::little_uint64_t sizeLE = blobData.size();
William A. Kennington III805ade32020-05-06 20:39:05 -0700312 std::string commitData(reinterpret_cast<const char*>(sizeLE.data()),
313 sizeof(sizeLE));
Kun Yid297c9f2019-01-09 13:52:30 -0800314 commitData += blobData;
315
316 try
317 {
318 file_->writeStr(commitData, 0);
319 }
320 catch (const std::exception& e)
321 {
Kun Yid297c9f2019-01-09 13:52:30 -0800322 commitState_ = CommitState::CommitError;
Kun Yi8bcf79d2019-01-16 15:17:57 -0800323 log<level::ERR>("Writing to sysfile failed",
324 entry("ERROR=%s", e.what()));
Kun Yid297c9f2019-01-09 13:52:30 -0800325 return false;
326 };
327
328 commitState_ = CommitState::Clean;
329 return true;
Kun Yi64dc05c2018-12-19 13:19:03 -0800330}
331
332bool BinaryStore::close()
333{
Kun Yi6baa7132019-01-08 21:21:02 -0800334 currentBlob_ = nullptr;
335 writable_ = false;
Kun Yid297c9f2019-01-09 13:52:30 -0800336 commitState_ = CommitState::Dirty;
Kun Yi6baa7132019-01-08 21:21:02 -0800337 return true;
Kun Yi64dc05c2018-12-19 13:19:03 -0800338}
339
Kun Yi1a25e0d2020-05-11 12:28:53 -0700340/*
341 * Sets |meta| with size and state of the blob. Returns |blobState| with
342 * standard definition from phosphor-ipmi-blobs header blob.hpp, plus OEM
343 * flag bits BinaryStore::CommitState.
344
345enum StateFlags
Kun Yi64dc05c2018-12-19 13:19:03 -0800346{
Kun Yi1a25e0d2020-05-11 12:28:53 -0700347 open_read = (1 << 0),
348 open_write = (1 << 1),
349 committing = (1 << 2),
350 committed = (1 << 3),
351 commit_error = (1 << 4),
352};
353
354enum CommitState
355{
356 Dirty = (1 << 8), // In-memory data might not match persisted data
357 Clean = (1 << 9), // In-memory data matches persisted data
358 Uninitialized = (1 << 10), // Cannot find persisted data
359 CommitError = (1 << 11) // Error happened during committing
360};
361
362*/
363bool BinaryStore::stat(blobs::BlobMeta* meta)
364{
365 uint16_t blobState = blobs::StateFlags::open_read;
366 if (writable_)
367 {
368 blobState |= blobs::StateFlags::open_write;
369 }
370
371 if (commitState_ == CommitState::Clean)
372 {
373 blobState |= blobs::StateFlags::committed;
374 }
375 else if (commitState_ == CommitState::CommitError)
376 {
377 blobState |= blobs::StateFlags::commit_error;
378 }
379 blobState |= commitState_;
380
381 if (currentBlob_)
382 {
383 meta->size = currentBlob_->data().size();
384 }
385 else
386 {
387 meta->size = 0;
388 }
389 meta->blobState = blobState;
390
391 return true;
Kun Yi64dc05c2018-12-19 13:19:03 -0800392}
393
394} // namespace binstore