blob: 29e6b9f5481e231a0cd9964a38f99a93ec947d9e [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 Yi2765b642019-01-16 11:11:24 -080045 return std::move(store);
Kun Yi64dc05c2018-12-19 13:19:03 -080046}
47
Kun Yi97be3af2019-03-05 22:43:41 -080048bool BinaryStore::loadSerializedData()
49{
50 /* Load blob from sysfile if we know it might not match what we have.
51 * Note it will overwrite existing unsaved data per design. */
52 if (commitState_ == CommitState::Clean)
53 {
54 return true;
55 }
56
57 log<level::NOTICE>("Try loading blob from persistent data",
58 entry("BASE_ID=%s", baseBlobId_.c_str()));
59 try
60 {
61 /* Parse length-prefixed format to protobuf */
62 boost::endian::little_uint64_t size = 0;
63 file_->readToBuf(0, sizeof(size), reinterpret_cast<char*>(&size));
64
65 if (!blob_.ParseFromString(file_->readAsStr(sizeof(uint64_t), size)))
66 {
67 /* Fail to parse the data, which might mean no preexsiting blobs
68 * and is a valid case to handle. Simply init an empty binstore. */
69 log<level::WARNING>(
70 "Fail to parse. There might be no persisted blobs",
71 entry("BASE_ID=%s", baseBlobId_.c_str()));
72 }
73 }
74 catch (const std::exception& e)
75 {
76 /* Read causes unexpected system-level failure */
77 log<level::ERR>("Reading from sysfile failed",
78 entry("ERROR=%s", e.what()));
79 return false;
80 }
81
82 return true;
83}
84
Kun Yi64dc05c2018-12-19 13:19:03 -080085std::string BinaryStore::getBaseBlobId() const
86{
87 return baseBlobId_;
88}
89
90std::vector<std::string> BinaryStore::getBlobIds() const
91{
92 std::vector<std::string> result;
93 result.push_back(baseBlobId_);
94
Kun Yi0a940b92019-01-07 16:33:11 -080095 for (const auto& blob : blob_.blobs())
Kun Yi64dc05c2018-12-19 13:19:03 -080096 {
Kun Yi0a940b92019-01-07 16:33:11 -080097 result.push_back(blob.blob_id());
Kun Yi64dc05c2018-12-19 13:19:03 -080098 }
99
100 return result;
101}
102
103bool BinaryStore::openOrCreateBlob(const std::string& blobId, uint16_t flags)
104{
Kun Yi6baa7132019-01-08 21:21:02 -0800105 if (!(flags & blobs::OpenFlags::read))
106 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800107 log<level::ERR>("OpenFlags::read not specified when opening",
108 entry("BLOB_ID=%s", blobId.c_str()));
Kun Yi6baa7132019-01-08 21:21:02 -0800109 return false;
110 }
111
112 if (currentBlob_ && (currentBlob_->blob_id() != blobId))
113 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800114 log<level::ERR>("Already handling a different blob",
115 entry("EXPECTED=%s", currentBlob_->blob_id().c_str()),
116 entry("RECEIVED=%s", blobId.c_str()));
Kun Yi6baa7132019-01-08 21:21:02 -0800117 return false;
118 }
119
120 writable_ = flags & blobs::OpenFlags::write;
121
Kun Yi97be3af2019-03-05 22:43:41 -0800122 /* If there are uncommitted data, discard them. */
123 if (!this->loadSerializedData())
Kun Yid297c9f2019-01-09 13:52:30 -0800124 {
Kun Yi97be3af2019-03-05 22:43:41 -0800125 return false;
Kun Yid297c9f2019-01-09 13:52:30 -0800126 }
127
Kun Yi6baa7132019-01-08 21:21:02 -0800128 /* Iterate and find if there is an existing blob with this id.
129 * blobsPtr points to a BinaryBlob container with STL-like semantics*/
130 auto blobsPtr = blob_.mutable_blobs();
131 auto blobIt =
132 std::find_if(blobsPtr->begin(), blobsPtr->end(),
133 [&](const auto& b) { return b.blob_id() == blobId; });
134
135 if (blobIt != blobsPtr->end())
136 {
137 currentBlob_ = &(*blobIt);
138 return true;
139 }
140
141 /* Otherwise, create the blob and append it */
142 currentBlob_ = blob_.add_blobs();
143 currentBlob_->set_blob_id(blobId);
144
Kun Yi8bcf79d2019-01-16 15:17:57 -0800145 log<level::NOTICE>("Created new blob", entry("BLOB_ID=%s", blobId.c_str()));
Kun Yi6baa7132019-01-08 21:21:02 -0800146 return true;
Kun Yi64dc05c2018-12-19 13:19:03 -0800147}
148
149bool BinaryStore::deleteBlob(const std::string& blobId)
150{
151 return false;
152}
153
154std::vector<uint8_t> BinaryStore::read(uint32_t offset, uint32_t requestedSize)
155{
Kun Yi6967b772019-02-22 13:48:12 -0800156 std::vector<uint8_t> result;
157
158 if (!currentBlob_)
159 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800160 log<level::ERR>("No open blob to read");
Kun Yi6967b772019-02-22 13:48:12 -0800161 return result;
162 }
163
164 auto dataPtr = currentBlob_->mutable_data();
165
166 /* If it is out of bound, return empty vector */
167 if (offset >= dataPtr->size())
168 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800169 log<level::ERR>("Read offset is beyond data size",
170 entry("MAX_SIZE=0x%x", dataPtr->size()),
171 entry("RECEIVED_OFFSET=0x%x", offset));
Kun Yi6967b772019-02-22 13:48:12 -0800172 return result;
173 }
174
175 uint32_t requestedEndPos = offset + requestedSize;
176
177 result = std::vector<uint8_t>(
178 dataPtr->begin() + offset,
179 std::min(dataPtr->begin() + requestedEndPos, dataPtr->end()));
Kun Yi64dc05c2018-12-19 13:19:03 -0800180 return result;
181}
182
183bool BinaryStore::write(uint32_t offset, const std::vector<uint8_t>& data)
184{
Kun Yi6967b772019-02-22 13:48:12 -0800185 if (!currentBlob_)
186 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800187 log<level::ERR>("No open blob to write");
Kun Yi6967b772019-02-22 13:48:12 -0800188 return false;
189 }
190
191 if (!writable_)
192 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800193 log<level::ERR>("Open blob is not writable");
Kun Yi6967b772019-02-22 13:48:12 -0800194 return false;
195 }
196
197 auto dataPtr = currentBlob_->mutable_data();
198
199 if (offset > dataPtr->size())
200 {
Kun Yi8bcf79d2019-01-16 15:17:57 -0800201 log<level::ERR>("Write would leave a gap with undefined data. Return.");
Kun Yi6967b772019-02-22 13:48:12 -0800202 return false;
203 }
204
205 /* Copy (overwrite) the data */
206 if (offset + data.size() > dataPtr->size())
207 {
208 dataPtr->resize(offset + data.size()); // not enough space, extend
209 }
210 std::copy(data.begin(), data.end(), dataPtr->begin() + offset);
211 return true;
Kun Yi64dc05c2018-12-19 13:19:03 -0800212}
213
214bool BinaryStore::commit()
215{
Kun Yi70059172019-02-22 16:31:42 -0800216 /* Store as little endian to be platform agnostic. Consistent with read. */
Kun Yid297c9f2019-01-09 13:52:30 -0800217 auto blobData = blob_.SerializeAsString();
Kun Yi70059172019-02-22 16:31:42 -0800218 boost::endian::little_uint64_t sizeLE = blobData.size();
219 std::string commitData(sizeLE.data(), sizeof(sizeLE));
Kun Yid297c9f2019-01-09 13:52:30 -0800220 commitData += blobData;
221
222 try
223 {
224 file_->writeStr(commitData, 0);
225 }
226 catch (const std::exception& e)
227 {
Kun Yid297c9f2019-01-09 13:52:30 -0800228 commitState_ = CommitState::CommitError;
Kun Yi8bcf79d2019-01-16 15:17:57 -0800229 log<level::ERR>("Writing to sysfile failed",
230 entry("ERROR=%s", e.what()));
Kun Yid297c9f2019-01-09 13:52:30 -0800231 return false;
232 };
233
234 commitState_ = CommitState::Clean;
235 return true;
Kun Yi64dc05c2018-12-19 13:19:03 -0800236}
237
238bool BinaryStore::close()
239{
Kun Yi6baa7132019-01-08 21:21:02 -0800240 currentBlob_ = nullptr;
241 writable_ = false;
Kun Yid297c9f2019-01-09 13:52:30 -0800242 commitState_ = CommitState::Dirty;
Kun Yi6baa7132019-01-08 21:21:02 -0800243 return true;
Kun Yi64dc05c2018-12-19 13:19:03 -0800244}
245
246bool BinaryStore::stat()
247{
248 return false;
249}
250
251} // namespace binstore