blob: 22e2e484d9bfc2382656d49251db6eb3b9504bba [file] [log] [blame]
Kun Yi64dc05c2018-12-19 13:19:03 -08001#include "binarystore.hpp"
2
Kun Yid2d8cb62019-01-10 15:06:12 -08003#include <unistd.h>
4
Kun Yi6baa7132019-01-08 21:21:02 -08005#include <algorithm>
6#include <blobs-ipmid/blobs.hpp>
Kun Yid2d8cb62019-01-10 15:06:12 -08007#include <cstdint>
8#include <memory>
9#include <string>
10#include <vector>
11
12#include "binaryblob.pb.h"
13
14using std::size_t;
15using std::uint16_t;
16using std::uint32_t;
17using std::uint64_t;
18using std::uint8_t;
Kun Yi6baa7132019-01-08 21:21:02 -080019
Kun Yi64dc05c2018-12-19 13:19:03 -080020namespace binstore
21{
22
Kun Yid297c9f2019-01-09 13:52:30 -080023namespace internal
24{
25
26/* Helper methods to interconvert an uint64_t and bytes, LSB first.
27 Convert bytes to uint64. Input should be exactly 8 bytes. */
28uint64_t bytesToUint64(const std::string& bytes)
29{
30 if (bytes.size() != sizeof(uint64_t))
31 {
32 return 0;
33 }
34
35 return static_cast<uint64_t>(bytes[7]) << 56 |
36 static_cast<uint64_t>(bytes[6]) << 48 |
37 static_cast<uint64_t>(bytes[5]) << 40 |
38 static_cast<uint64_t>(bytes[4]) << 32 |
39 static_cast<uint64_t>(bytes[3]) << 24 |
40 static_cast<uint64_t>(bytes[2]) << 16 |
41 static_cast<uint64_t>(bytes[1]) << 8 |
42 static_cast<uint64_t>(bytes[0]);
43}
44
45/* Convert uint64 to bytes, LSB first. */
46std::string uint64ToBytes(uint64_t num)
47{
48 std::string result;
49 for (size_t i = 0; i < sizeof(uint64_t); ++i)
50 {
51 result += static_cast<char>(num & 0xff);
52 num >>= 8;
53 }
54 return result;
55}
56
57} // namespace internal
58
Kun Yi64dc05c2018-12-19 13:19:03 -080059std::unique_ptr<BinaryStoreInterface>
60 BinaryStore::createFromConfig(const std::string& baseBlobId,
Kun Yi2765b642019-01-16 11:11:24 -080061 std::unique_ptr<SysFile> file,
62 uint32_t maxSize)
Kun Yi64dc05c2018-12-19 13:19:03 -080063{
Kun Yi2765b642019-01-16 11:11:24 -080064 if (baseBlobId.empty() || !file)
65 {
66 return nullptr;
67 }
68
69 auto store =
70 std::make_unique<BinaryStore>(baseBlobId, std::move(file), maxSize);
71
72 store->blob_.set_blob_base_id(store->baseBlobId_);
73
74 return std::move(store);
Kun Yi64dc05c2018-12-19 13:19:03 -080075}
76
77std::string BinaryStore::getBaseBlobId() const
78{
79 return baseBlobId_;
80}
81
82std::vector<std::string> BinaryStore::getBlobIds() const
83{
84 std::vector<std::string> result;
85 result.push_back(baseBlobId_);
86
Kun Yi0a940b92019-01-07 16:33:11 -080087 for (const auto& blob : blob_.blobs())
Kun Yi64dc05c2018-12-19 13:19:03 -080088 {
Kun Yi0a940b92019-01-07 16:33:11 -080089 result.push_back(blob.blob_id());
Kun Yi64dc05c2018-12-19 13:19:03 -080090 }
91
92 return result;
93}
94
95bool BinaryStore::openOrCreateBlob(const std::string& blobId, uint16_t flags)
96{
Kun Yi6baa7132019-01-08 21:21:02 -080097 if (!(flags & blobs::OpenFlags::read))
98 {
99 return false;
100 }
101
102 if (currentBlob_ && (currentBlob_->blob_id() != blobId))
103 {
104 /* Already handling a different blob */
105 return false;
106 }
107
108 writable_ = flags & blobs::OpenFlags::write;
109
Kun Yid297c9f2019-01-09 13:52:30 -0800110 /* Load blob from sysfile if we know it might not match what we have.
111 * Note it will overwrite existing unsaved data per design. */
112 if (commitState_ != CommitState::Clean)
113 {
114 try
115 {
116 /* Parse length-prefixed format to protobuf */
117 auto size =
118 internal::bytesToUint64(file_->readAsStr(0, sizeof(uint64_t)));
119 if (!blob_.ParseFromString(
120 file_->readAsStr(sizeof(uint64_t), size)))
121 {
122 /* Fail to parse the data, which might mean no preexsiting data
123 * and is a valid case to handle */
124 // TODO: logging
125 }
126 }
127 catch (const std::exception& e)
128 {
129 /* Read causes unexpected system-level failure */
130 // TODO: logging
131 return false;
132 }
133 }
134
Kun Yi6baa7132019-01-08 21:21:02 -0800135 /* Iterate and find if there is an existing blob with this id.
136 * blobsPtr points to a BinaryBlob container with STL-like semantics*/
137 auto blobsPtr = blob_.mutable_blobs();
138 auto blobIt =
139 std::find_if(blobsPtr->begin(), blobsPtr->end(),
140 [&](const auto& b) { return b.blob_id() == blobId; });
141
142 if (blobIt != blobsPtr->end())
143 {
144 currentBlob_ = &(*blobIt);
145 return true;
146 }
147
148 /* Otherwise, create the blob and append it */
149 currentBlob_ = blob_.add_blobs();
150 currentBlob_->set_blob_id(blobId);
151
152 return true;
Kun Yi64dc05c2018-12-19 13:19:03 -0800153}
154
155bool BinaryStore::deleteBlob(const std::string& blobId)
156{
157 return false;
158}
159
160std::vector<uint8_t> BinaryStore::read(uint32_t offset, uint32_t requestedSize)
161{
Kun Yi6967b772019-02-22 13:48:12 -0800162 std::vector<uint8_t> result;
163
164 if (!currentBlob_)
165 {
166 return result;
167 }
168
169 auto dataPtr = currentBlob_->mutable_data();
170
171 /* If it is out of bound, return empty vector */
172 if (offset >= dataPtr->size())
173 {
174 return result;
175 }
176
177 uint32_t requestedEndPos = offset + requestedSize;
178
179 result = std::vector<uint8_t>(
180 dataPtr->begin() + offset,
181 std::min(dataPtr->begin() + requestedEndPos, dataPtr->end()));
Kun Yi64dc05c2018-12-19 13:19:03 -0800182 return result;
183}
184
185bool BinaryStore::write(uint32_t offset, const std::vector<uint8_t>& data)
186{
Kun Yi6967b772019-02-22 13:48:12 -0800187 if (!currentBlob_)
188 {
189 return false;
190 }
191
192 if (!writable_)
193 {
194 return false;
195 }
196
197 auto dataPtr = currentBlob_->mutable_data();
198
199 if (offset > dataPtr->size())
200 {
201 /* Will leave a gap with undefined data */
202 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 Yid297c9f2019-01-09 13:52:30 -0800216
217 auto blobData = blob_.SerializeAsString();
218 std::string commitData(internal::uint64ToBytes((uint64_t)blobData.size()));
219 commitData += blobData;
220
221 try
222 {
223 file_->writeStr(commitData, 0);
224 }
225 catch (const std::exception& e)
226 {
227 // TODO: logging
228 commitState_ = CommitState::CommitError;
229 return false;
230 };
231
232 commitState_ = CommitState::Clean;
233 return true;
Kun Yi64dc05c2018-12-19 13:19:03 -0800234}
235
236bool BinaryStore::close()
237{
Kun Yi6baa7132019-01-08 21:21:02 -0800238 currentBlob_ = nullptr;
239 writable_ = false;
Kun Yid297c9f2019-01-09 13:52:30 -0800240 commitState_ = CommitState::Dirty;
Kun Yi6baa7132019-01-08 21:21:02 -0800241 return true;
Kun Yi64dc05c2018-12-19 13:19:03 -0800242}
243
244bool BinaryStore::stat()
245{
246 return false;
247}
248
249} // namespace binstore