blob: 5b0e2995d8dd3ed0bd4074653106e89b55f46652 [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,
Kun Yi2765b642019-01-16 11:11:24 -080032 std::unique_ptr<SysFile> file,
33 uint32_t maxSize)
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
42 auto store =
43 std::make_unique<BinaryStore>(baseBlobId, std::move(file), maxSize);
44
Kun Yi8ca234e2019-03-04 10:14:45 -080045 if (!store->loadSerializedData())
46 {
47 return nullptr;
48 }
49
Kun Yi2765b642019-01-16 11:11:24 -080050 return std::move(store);
Kun Yi64dc05c2018-12-19 13:19:03 -080051}
52
Kun Yi97be3af2019-03-05 22:43:41 -080053bool BinaryStore::loadSerializedData()
54{
55 /* Load blob from sysfile if we know it might not match what we have.
56 * Note it will overwrite existing unsaved data per design. */
Kun Yie535a732019-04-03 19:03:25 -070057 if (commitState_ == CommitState::Clean ||
58 commitState_ == CommitState::Uninitialized)
Kun Yi97be3af2019-03-05 22:43:41 -080059 {
60 return true;
61 }
62
63 log<level::NOTICE>("Try loading blob from persistent data",
64 entry("BASE_ID=%s", baseBlobId_.c_str()));
65 try
66 {
67 /* Parse length-prefixed format to protobuf */
68 boost::endian::little_uint64_t size = 0;
69 file_->readToBuf(0, sizeof(size), reinterpret_cast<char*>(&size));
70
71 if (!blob_.ParseFromString(file_->readAsStr(sizeof(uint64_t), size)))
72 {
73 /* Fail to parse the data, which might mean no preexsiting blobs
74 * and is a valid case to handle. Simply init an empty binstore. */
Kun Yie535a732019-04-03 19:03:25 -070075 commitState_ = CommitState::Uninitialized;
Kun Yi97be3af2019-03-05 22:43:41 -080076 }
77 }
Kun Yic83d2fa2019-04-03 18:40:19 -070078 catch (const std::system_error& e)
Kun Yi97be3af2019-03-05 22:43:41 -080079 {
80 /* Read causes unexpected system-level failure */
81 log<level::ERR>("Reading from sysfile failed",
82 entry("ERROR=%s", e.what()));
83 return false;
84 }
Kun Yic83d2fa2019-04-03 18:40:19 -070085 catch (const std::exception& e)
86 {
Kun Yie535a732019-04-03 19:03:25 -070087 /* Non system error originates from junk value in 'size' */
88 commitState_ = CommitState::Uninitialized;
89 }
90
91 if (commitState_ == CommitState::Uninitialized)
92 {
93 log<level::WARNING>("Fail to parse. There might be no persisted blobs",
94 entry("BASE_ID=%s", baseBlobId_.c_str()));
Kun Yic83d2fa2019-04-03 18:40:19 -070095 return true;
96 }
Kun Yi97be3af2019-03-05 22:43:41 -080097
Kun Yi8ca234e2019-03-04 10:14:45 -080098 if (blob_.blob_base_id() != baseBlobId_)
99 {
100 /* Uh oh, stale data loaded. Clean it and commit. */
101 // TODO: it might be safer to add an option in config to error out
102 // instead of to overwrite.
103 log<level::ERR>("Stale blob data, resetting internals...",
104 entry("LOADED=%s", blob_.blob_base_id().c_str()),
105 entry("EXPECTED=%s", baseBlobId_.c_str()));
106 blob_.Clear();
107 blob_.set_blob_base_id(baseBlobId_);
108 return this->commit();
109 }
110
Kun Yi97be3af2019-03-05 22:43:41 -0800111 return true;
112}
113
Kun Yi64dc05c2018-12-19 13:19:03 -0800114std::string BinaryStore::getBaseBlobId() const
115{
116 return baseBlobId_;
117}
118
119std::vector<std::string> BinaryStore::getBlobIds() const
120{
121 std::vector<std::string> result;
122 result.push_back(baseBlobId_);
123
Kun Yi0a940b92019-01-07 16:33:11 -0800124 for (const auto& blob : blob_.blobs())
Kun Yi64dc05c2018-12-19 13:19:03 -0800125 {
Kun Yi0a940b92019-01-07 16:33:11 -0800126 result.push_back(blob.blob_id());
Kun Yi64dc05c2018-12-19 13:19:03 -0800127 }
128
129 return result;
130}
131
132bool BinaryStore::openOrCreateBlob(const std::string& blobId, uint16_t flags)
133{
Kun Yi6baa7132019-01-08 21:21:02 -0800134 if (!(flags & blobs::OpenFlags::read))
135 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800136 log<level::ERR>("OpenFlags::read not specified when opening",
137 entry("BLOB_ID=%s", blobId.c_str()));
Kun Yi6baa7132019-01-08 21:21:02 -0800138 return false;
139 }
140
141 if (currentBlob_ && (currentBlob_->blob_id() != blobId))
142 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800143 log<level::ERR>("Already handling a different blob",
144 entry("EXPECTED=%s", currentBlob_->blob_id().c_str()),
145 entry("RECEIVED=%s", blobId.c_str()));
Kun Yi6baa7132019-01-08 21:21:02 -0800146 return false;
147 }
148
149 writable_ = flags & blobs::OpenFlags::write;
150
Kun Yi97be3af2019-03-05 22:43:41 -0800151 /* If there are uncommitted data, discard them. */
152 if (!this->loadSerializedData())
Kun Yid297c9f2019-01-09 13:52:30 -0800153 {
Kun Yi97be3af2019-03-05 22:43:41 -0800154 return false;
Kun Yid297c9f2019-01-09 13:52:30 -0800155 }
156
Kun Yi6baa7132019-01-08 21:21:02 -0800157 /* Iterate and find if there is an existing blob with this id.
158 * blobsPtr points to a BinaryBlob container with STL-like semantics*/
159 auto blobsPtr = blob_.mutable_blobs();
160 auto blobIt =
161 std::find_if(blobsPtr->begin(), blobsPtr->end(),
162 [&](const auto& b) { return b.blob_id() == blobId; });
163
164 if (blobIt != blobsPtr->end())
165 {
166 currentBlob_ = &(*blobIt);
167 return true;
168 }
169
170 /* Otherwise, create the blob and append it */
171 currentBlob_ = blob_.add_blobs();
172 currentBlob_->set_blob_id(blobId);
173
Kun Yi1a25e0d2020-05-11 12:28:53 -0700174 commitState_ = CommitState::Dirty;
Kun Yi8bcf79d2019-01-16 15:17:57 -0800175 log<level::NOTICE>("Created new blob", entry("BLOB_ID=%s", blobId.c_str()));
Kun Yi6baa7132019-01-08 21:21:02 -0800176 return true;
Kun Yi64dc05c2018-12-19 13:19:03 -0800177}
178
179bool BinaryStore::deleteBlob(const std::string& blobId)
180{
181 return false;
182}
183
184std::vector<uint8_t> BinaryStore::read(uint32_t offset, uint32_t requestedSize)
185{
Kun Yi6967b772019-02-22 13:48:12 -0800186 std::vector<uint8_t> result;
187
188 if (!currentBlob_)
189 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800190 log<level::ERR>("No open blob to read");
Kun Yi6967b772019-02-22 13:48:12 -0800191 return result;
192 }
193
194 auto dataPtr = currentBlob_->mutable_data();
195
196 /* If it is out of bound, return empty vector */
197 if (offset >= dataPtr->size())
198 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800199 log<level::ERR>("Read offset is beyond data size",
200 entry("MAX_SIZE=0x%x", dataPtr->size()),
201 entry("RECEIVED_OFFSET=0x%x", offset));
Kun Yi6967b772019-02-22 13:48:12 -0800202 return result;
203 }
204
205 uint32_t requestedEndPos = offset + requestedSize;
206
207 result = std::vector<uint8_t>(
208 dataPtr->begin() + offset,
209 std::min(dataPtr->begin() + requestedEndPos, dataPtr->end()));
Kun Yi64dc05c2018-12-19 13:19:03 -0800210 return result;
211}
212
213bool BinaryStore::write(uint32_t offset, const std::vector<uint8_t>& data)
214{
Kun Yi6967b772019-02-22 13:48:12 -0800215 if (!currentBlob_)
216 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800217 log<level::ERR>("No open blob to write");
Kun Yi6967b772019-02-22 13:48:12 -0800218 return false;
219 }
220
221 if (!writable_)
222 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800223 log<level::ERR>("Open blob is not writable");
Kun Yi6967b772019-02-22 13:48:12 -0800224 return false;
225 }
226
227 auto dataPtr = currentBlob_->mutable_data();
228
229 if (offset > dataPtr->size())
230 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800231 log<level::ERR>("Write would leave a gap with undefined data. Return.");
Kun Yi6967b772019-02-22 13:48:12 -0800232 return false;
233 }
234
Kun Yi1a25e0d2020-05-11 12:28:53 -0700235 commitState_ = CommitState::Dirty;
Kun Yi6967b772019-02-22 13:48:12 -0800236 /* Copy (overwrite) the data */
237 if (offset + data.size() > dataPtr->size())
238 {
239 dataPtr->resize(offset + data.size()); // not enough space, extend
240 }
241 std::copy(data.begin(), data.end(), dataPtr->begin() + offset);
242 return true;
Kun Yi64dc05c2018-12-19 13:19:03 -0800243}
244
245bool BinaryStore::commit()
246{
Kun Yi70059172019-02-22 16:31:42 -0800247 /* Store as little endian to be platform agnostic. Consistent with read. */
Kun Yid297c9f2019-01-09 13:52:30 -0800248 auto blobData = blob_.SerializeAsString();
Kun Yi70059172019-02-22 16:31:42 -0800249 boost::endian::little_uint64_t sizeLE = blobData.size();
William A. Kennington III805ade32020-05-06 20:39:05 -0700250 std::string commitData(reinterpret_cast<const char*>(sizeLE.data()),
251 sizeof(sizeLE));
Kun Yid297c9f2019-01-09 13:52:30 -0800252 commitData += blobData;
253
254 try
255 {
256 file_->writeStr(commitData, 0);
257 }
258 catch (const std::exception& e)
259 {
Kun Yid297c9f2019-01-09 13:52:30 -0800260 commitState_ = CommitState::CommitError;
Kun Yi8bcf79d2019-01-16 15:17:57 -0800261 log<level::ERR>("Writing to sysfile failed",
262 entry("ERROR=%s", e.what()));
Kun Yid297c9f2019-01-09 13:52:30 -0800263 return false;
264 };
265
266 commitState_ = CommitState::Clean;
267 return true;
Kun Yi64dc05c2018-12-19 13:19:03 -0800268}
269
270bool BinaryStore::close()
271{
Kun Yi6baa7132019-01-08 21:21:02 -0800272 currentBlob_ = nullptr;
273 writable_ = false;
Kun Yid297c9f2019-01-09 13:52:30 -0800274 commitState_ = CommitState::Dirty;
Kun Yi6baa7132019-01-08 21:21:02 -0800275 return true;
Kun Yi64dc05c2018-12-19 13:19:03 -0800276}
277
Kun Yi1a25e0d2020-05-11 12:28:53 -0700278/*
279 * Sets |meta| with size and state of the blob. Returns |blobState| with
280 * standard definition from phosphor-ipmi-blobs header blob.hpp, plus OEM
281 * flag bits BinaryStore::CommitState.
282
283enum StateFlags
Kun Yi64dc05c2018-12-19 13:19:03 -0800284{
Kun Yi1a25e0d2020-05-11 12:28:53 -0700285 open_read = (1 << 0),
286 open_write = (1 << 1),
287 committing = (1 << 2),
288 committed = (1 << 3),
289 commit_error = (1 << 4),
290};
291
292enum CommitState
293{
294 Dirty = (1 << 8), // In-memory data might not match persisted data
295 Clean = (1 << 9), // In-memory data matches persisted data
296 Uninitialized = (1 << 10), // Cannot find persisted data
297 CommitError = (1 << 11) // Error happened during committing
298};
299
300*/
301bool BinaryStore::stat(blobs::BlobMeta* meta)
302{
303 uint16_t blobState = blobs::StateFlags::open_read;
304 if (writable_)
305 {
306 blobState |= blobs::StateFlags::open_write;
307 }
308
309 if (commitState_ == CommitState::Clean)
310 {
311 blobState |= blobs::StateFlags::committed;
312 }
313 else if (commitState_ == CommitState::CommitError)
314 {
315 blobState |= blobs::StateFlags::commit_error;
316 }
317 blobState |= commitState_;
318
319 if (currentBlob_)
320 {
321 meta->size = currentBlob_->data().size();
322 }
323 else
324 {
325 meta->size = 0;
326 }
327 meta->blobState = blobState;
328
329 return true;
Kun Yi64dc05c2018-12-19 13:19:03 -0800330}
331
332} // namespace binstore