blob: 1c94dd389e3e144f93a57e90e630334e0fc0e089 [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. */
57 if (commitState_ == CommitState::Clean)
58 {
59 return true;
60 }
61
62 log<level::NOTICE>("Try loading blob from persistent data",
63 entry("BASE_ID=%s", baseBlobId_.c_str()));
64 try
65 {
66 /* Parse length-prefixed format to protobuf */
67 boost::endian::little_uint64_t size = 0;
68 file_->readToBuf(0, sizeof(size), reinterpret_cast<char*>(&size));
69
70 if (!blob_.ParseFromString(file_->readAsStr(sizeof(uint64_t), size)))
71 {
72 /* Fail to parse the data, which might mean no preexsiting blobs
73 * and is a valid case to handle. Simply init an empty binstore. */
74 log<level::WARNING>(
75 "Fail to parse. There might be no persisted blobs",
76 entry("BASE_ID=%s", baseBlobId_.c_str()));
Kun Yic83d2fa2019-04-03 18:40:19 -070077
78 return true;
Kun Yi97be3af2019-03-05 22:43:41 -080079 }
80 }
Kun Yic83d2fa2019-04-03 18:40:19 -070081 catch (const std::system_error& e)
Kun Yi97be3af2019-03-05 22:43:41 -080082 {
83 /* Read causes unexpected system-level failure */
84 log<level::ERR>("Reading from sysfile failed",
85 entry("ERROR=%s", e.what()));
86 return false;
87 }
Kun Yic83d2fa2019-04-03 18:40:19 -070088 catch (const std::exception& e)
89 {
90 log<level::WARNING>("Invalid size. There might be no persisted blobs.");
91 return true;
92 }
Kun Yi97be3af2019-03-05 22:43:41 -080093
Kun Yi8ca234e2019-03-04 10:14:45 -080094 if (blob_.blob_base_id() != baseBlobId_)
95 {
96 /* Uh oh, stale data loaded. Clean it and commit. */
97 // TODO: it might be safer to add an option in config to error out
98 // instead of to overwrite.
99 log<level::ERR>("Stale blob data, resetting internals...",
100 entry("LOADED=%s", blob_.blob_base_id().c_str()),
101 entry("EXPECTED=%s", baseBlobId_.c_str()));
102 blob_.Clear();
103 blob_.set_blob_base_id(baseBlobId_);
104 return this->commit();
105 }
106
Kun Yi97be3af2019-03-05 22:43:41 -0800107 return true;
108}
109
Kun Yi64dc05c2018-12-19 13:19:03 -0800110std::string BinaryStore::getBaseBlobId() const
111{
112 return baseBlobId_;
113}
114
115std::vector<std::string> BinaryStore::getBlobIds() const
116{
117 std::vector<std::string> result;
118 result.push_back(baseBlobId_);
119
Kun Yi0a940b92019-01-07 16:33:11 -0800120 for (const auto& blob : blob_.blobs())
Kun Yi64dc05c2018-12-19 13:19:03 -0800121 {
Kun Yi0a940b92019-01-07 16:33:11 -0800122 result.push_back(blob.blob_id());
Kun Yi64dc05c2018-12-19 13:19:03 -0800123 }
124
125 return result;
126}
127
128bool BinaryStore::openOrCreateBlob(const std::string& blobId, uint16_t flags)
129{
Kun Yi6baa7132019-01-08 21:21:02 -0800130 if (!(flags & blobs::OpenFlags::read))
131 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800132 log<level::ERR>("OpenFlags::read not specified when opening",
133 entry("BLOB_ID=%s", blobId.c_str()));
Kun Yi6baa7132019-01-08 21:21:02 -0800134 return false;
135 }
136
137 if (currentBlob_ && (currentBlob_->blob_id() != blobId))
138 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800139 log<level::ERR>("Already handling a different blob",
140 entry("EXPECTED=%s", currentBlob_->blob_id().c_str()),
141 entry("RECEIVED=%s", blobId.c_str()));
Kun Yi6baa7132019-01-08 21:21:02 -0800142 return false;
143 }
144
145 writable_ = flags & blobs::OpenFlags::write;
146
Kun Yi97be3af2019-03-05 22:43:41 -0800147 /* If there are uncommitted data, discard them. */
148 if (!this->loadSerializedData())
Kun Yid297c9f2019-01-09 13:52:30 -0800149 {
Kun Yi97be3af2019-03-05 22:43:41 -0800150 return false;
Kun Yid297c9f2019-01-09 13:52:30 -0800151 }
152
Kun Yi6baa7132019-01-08 21:21:02 -0800153 /* Iterate and find if there is an existing blob with this id.
154 * blobsPtr points to a BinaryBlob container with STL-like semantics*/
155 auto blobsPtr = blob_.mutable_blobs();
156 auto blobIt =
157 std::find_if(blobsPtr->begin(), blobsPtr->end(),
158 [&](const auto& b) { return b.blob_id() == blobId; });
159
160 if (blobIt != blobsPtr->end())
161 {
162 currentBlob_ = &(*blobIt);
163 return true;
164 }
165
166 /* Otherwise, create the blob and append it */
167 currentBlob_ = blob_.add_blobs();
168 currentBlob_->set_blob_id(blobId);
169
Kun Yi8bcf79d2019-01-16 15:17:57 -0800170 log<level::NOTICE>("Created new blob", entry("BLOB_ID=%s", blobId.c_str()));
Kun Yi6baa7132019-01-08 21:21:02 -0800171 return true;
Kun Yi64dc05c2018-12-19 13:19:03 -0800172}
173
174bool BinaryStore::deleteBlob(const std::string& blobId)
175{
176 return false;
177}
178
179std::vector<uint8_t> BinaryStore::read(uint32_t offset, uint32_t requestedSize)
180{
Kun Yi6967b772019-02-22 13:48:12 -0800181 std::vector<uint8_t> result;
182
183 if (!currentBlob_)
184 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800185 log<level::ERR>("No open blob to read");
Kun Yi6967b772019-02-22 13:48:12 -0800186 return result;
187 }
188
189 auto dataPtr = currentBlob_->mutable_data();
190
191 /* If it is out of bound, return empty vector */
192 if (offset >= dataPtr->size())
193 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800194 log<level::ERR>("Read offset is beyond data size",
195 entry("MAX_SIZE=0x%x", dataPtr->size()),
196 entry("RECEIVED_OFFSET=0x%x", offset));
Kun Yi6967b772019-02-22 13:48:12 -0800197 return result;
198 }
199
200 uint32_t requestedEndPos = offset + requestedSize;
201
202 result = std::vector<uint8_t>(
203 dataPtr->begin() + offset,
204 std::min(dataPtr->begin() + requestedEndPos, dataPtr->end()));
Kun Yi64dc05c2018-12-19 13:19:03 -0800205 return result;
206}
207
208bool BinaryStore::write(uint32_t offset, const std::vector<uint8_t>& data)
209{
Kun Yi6967b772019-02-22 13:48:12 -0800210 if (!currentBlob_)
211 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800212 log<level::ERR>("No open blob to write");
Kun Yi6967b772019-02-22 13:48:12 -0800213 return false;
214 }
215
216 if (!writable_)
217 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800218 log<level::ERR>("Open blob is not writable");
Kun Yi6967b772019-02-22 13:48:12 -0800219 return false;
220 }
221
222 auto dataPtr = currentBlob_->mutable_data();
223
224 if (offset > dataPtr->size())
225 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800226 log<level::ERR>("Write would leave a gap with undefined data. Return.");
Kun Yi6967b772019-02-22 13:48:12 -0800227 return false;
228 }
229
230 /* Copy (overwrite) the data */
231 if (offset + data.size() > dataPtr->size())
232 {
233 dataPtr->resize(offset + data.size()); // not enough space, extend
234 }
235 std::copy(data.begin(), data.end(), dataPtr->begin() + offset);
236 return true;
Kun Yi64dc05c2018-12-19 13:19:03 -0800237}
238
239bool BinaryStore::commit()
240{
Kun Yi70059172019-02-22 16:31:42 -0800241 /* Store as little endian to be platform agnostic. Consistent with read. */
Kun Yid297c9f2019-01-09 13:52:30 -0800242 auto blobData = blob_.SerializeAsString();
Kun Yi70059172019-02-22 16:31:42 -0800243 boost::endian::little_uint64_t sizeLE = blobData.size();
244 std::string commitData(sizeLE.data(), sizeof(sizeLE));
Kun Yid297c9f2019-01-09 13:52:30 -0800245 commitData += blobData;
246
247 try
248 {
249 file_->writeStr(commitData, 0);
250 }
251 catch (const std::exception& e)
252 {
Kun Yid297c9f2019-01-09 13:52:30 -0800253 commitState_ = CommitState::CommitError;
Kun Yi8bcf79d2019-01-16 15:17:57 -0800254 log<level::ERR>("Writing to sysfile failed",
255 entry("ERROR=%s", e.what()));
Kun Yid297c9f2019-01-09 13:52:30 -0800256 return false;
257 };
258
259 commitState_ = CommitState::Clean;
260 return true;
Kun Yi64dc05c2018-12-19 13:19:03 -0800261}
262
263bool BinaryStore::close()
264{
Kun Yi6baa7132019-01-08 21:21:02 -0800265 currentBlob_ = nullptr;
266 writable_ = false;
Kun Yid297c9f2019-01-09 13:52:30 -0800267 commitState_ = CommitState::Dirty;
Kun Yi6baa7132019-01-08 21:21:02 -0800268 return true;
Kun Yi64dc05c2018-12-19 13:19:03 -0800269}
270
271bool BinaryStore::stat()
272{
273 return false;
274}
275
276} // namespace binstore