| Kun Yi | 64dc05c | 2018-12-19 13:19:03 -0800 | [diff] [blame] | 1 | #include "binarystore.hpp" | 
|  | 2 |  | 
| Kun Yi | d2d8cb6 | 2019-01-10 15:06:12 -0800 | [diff] [blame] | 3 | #include <unistd.h> | 
|  | 4 |  | 
| Kun Yi | 6baa713 | 2019-01-08 21:21:02 -0800 | [diff] [blame] | 5 | #include <algorithm> | 
|  | 6 | #include <blobs-ipmid/blobs.hpp> | 
| Kun Yi | 7005917 | 2019-02-22 16:31:42 -0800 | [diff] [blame] | 7 | #include <boost/endian/arithmetic.hpp> | 
| Kun Yi | d2d8cb6 | 2019-01-10 15:06:12 -0800 | [diff] [blame] | 8 | #include <cstdint> | 
|  | 9 | #include <memory> | 
| Kun Yi | 8bcf79d | 2019-01-16 15:17:57 -0800 | [diff] [blame] | 10 | #include <phosphor-logging/elog.hpp> | 
| Kun Yi | d2d8cb6 | 2019-01-10 15:06:12 -0800 | [diff] [blame] | 11 | #include <string> | 
|  | 12 | #include <vector> | 
|  | 13 |  | 
|  | 14 | #include "binaryblob.pb.h" | 
|  | 15 |  | 
|  | 16 | using std::size_t; | 
|  | 17 | using std::uint16_t; | 
|  | 18 | using std::uint32_t; | 
|  | 19 | using std::uint64_t; | 
|  | 20 | using std::uint8_t; | 
| Kun Yi | 6baa713 | 2019-01-08 21:21:02 -0800 | [diff] [blame] | 21 |  | 
| Kun Yi | 64dc05c | 2018-12-19 13:19:03 -0800 | [diff] [blame] | 22 | namespace binstore | 
|  | 23 | { | 
|  | 24 |  | 
| Kun Yi | 8bcf79d | 2019-01-16 15:17:57 -0800 | [diff] [blame] | 25 | using namespace phosphor::logging; | 
|  | 26 |  | 
| Kun Yi | 64dc05c | 2018-12-19 13:19:03 -0800 | [diff] [blame] | 27 | std::unique_ptr<BinaryStoreInterface> | 
|  | 28 | BinaryStore::createFromConfig(const std::string& baseBlobId, | 
| Kun Yi | 2765b64 | 2019-01-16 11:11:24 -0800 | [diff] [blame] | 29 | std::unique_ptr<SysFile> file, | 
|  | 30 | uint32_t maxSize) | 
| Kun Yi | 64dc05c | 2018-12-19 13:19:03 -0800 | [diff] [blame] | 31 | { | 
| Kun Yi | 2765b64 | 2019-01-16 11:11:24 -0800 | [diff] [blame] | 32 | if (baseBlobId.empty() || !file) | 
|  | 33 | { | 
| Kun Yi | 8bcf79d | 2019-01-16 15:17:57 -0800 | [diff] [blame] | 34 | log<level::ERR>("Unable to create binarystore from invalid config", | 
|  | 35 | entry("BASE_ID=%s", baseBlobId.c_str())); | 
| Kun Yi | 2765b64 | 2019-01-16 11:11:24 -0800 | [diff] [blame] | 36 | return nullptr; | 
|  | 37 | } | 
|  | 38 |  | 
|  | 39 | auto store = | 
|  | 40 | std::make_unique<BinaryStore>(baseBlobId, std::move(file), maxSize); | 
|  | 41 |  | 
|  | 42 | store->blob_.set_blob_base_id(store->baseBlobId_); | 
|  | 43 |  | 
|  | 44 | return std::move(store); | 
| Kun Yi | 64dc05c | 2018-12-19 13:19:03 -0800 | [diff] [blame] | 45 | } | 
|  | 46 |  | 
|  | 47 | std::string BinaryStore::getBaseBlobId() const | 
|  | 48 | { | 
|  | 49 | return baseBlobId_; | 
|  | 50 | } | 
|  | 51 |  | 
|  | 52 | std::vector<std::string> BinaryStore::getBlobIds() const | 
|  | 53 | { | 
|  | 54 | std::vector<std::string> result; | 
|  | 55 | result.push_back(baseBlobId_); | 
|  | 56 |  | 
| Kun Yi | 0a940b9 | 2019-01-07 16:33:11 -0800 | [diff] [blame] | 57 | for (const auto& blob : blob_.blobs()) | 
| Kun Yi | 64dc05c | 2018-12-19 13:19:03 -0800 | [diff] [blame] | 58 | { | 
| Kun Yi | 0a940b9 | 2019-01-07 16:33:11 -0800 | [diff] [blame] | 59 | result.push_back(blob.blob_id()); | 
| Kun Yi | 64dc05c | 2018-12-19 13:19:03 -0800 | [diff] [blame] | 60 | } | 
|  | 61 |  | 
|  | 62 | return result; | 
|  | 63 | } | 
|  | 64 |  | 
|  | 65 | bool BinaryStore::openOrCreateBlob(const std::string& blobId, uint16_t flags) | 
|  | 66 | { | 
| Kun Yi | 6baa713 | 2019-01-08 21:21:02 -0800 | [diff] [blame] | 67 | if (!(flags & blobs::OpenFlags::read)) | 
|  | 68 | { | 
| Kun Yi | 8bcf79d | 2019-01-16 15:17:57 -0800 | [diff] [blame] | 69 | log<level::ERR>("OpenFlags::read not specified when opening", | 
|  | 70 | entry("BLOB_ID=%s", blobId.c_str())); | 
| Kun Yi | 6baa713 | 2019-01-08 21:21:02 -0800 | [diff] [blame] | 71 | return false; | 
|  | 72 | } | 
|  | 73 |  | 
|  | 74 | if (currentBlob_ && (currentBlob_->blob_id() != blobId)) | 
|  | 75 | { | 
| Kun Yi | 8bcf79d | 2019-01-16 15:17:57 -0800 | [diff] [blame] | 76 | log<level::ERR>("Already handling a different blob", | 
|  | 77 | entry("EXPECTED=%s", currentBlob_->blob_id().c_str()), | 
|  | 78 | entry("RECEIVED=%s", blobId.c_str())); | 
| Kun Yi | 6baa713 | 2019-01-08 21:21:02 -0800 | [diff] [blame] | 79 | return false; | 
|  | 80 | } | 
|  | 81 |  | 
|  | 82 | writable_ = flags & blobs::OpenFlags::write; | 
|  | 83 |  | 
| Kun Yi | d297c9f | 2019-01-09 13:52:30 -0800 | [diff] [blame] | 84 | /* Load blob from sysfile if we know it might not match what we have. | 
|  | 85 | * Note it will overwrite existing unsaved data per design. */ | 
|  | 86 | if (commitState_ != CommitState::Clean) | 
|  | 87 | { | 
| Kun Yi | 8bcf79d | 2019-01-16 15:17:57 -0800 | [diff] [blame] | 88 | log<level::NOTICE>("Try loading blob from persistent data", | 
|  | 89 | entry("BLOB_ID=%s", blobId.c_str())); | 
| Kun Yi | d297c9f | 2019-01-09 13:52:30 -0800 | [diff] [blame] | 90 | try | 
|  | 91 | { | 
|  | 92 | /* Parse length-prefixed format to protobuf */ | 
| Kun Yi | 7005917 | 2019-02-22 16:31:42 -0800 | [diff] [blame] | 93 | boost::endian::little_uint64_t size = 0; | 
|  | 94 | file_->readToBuf(0, sizeof(size), reinterpret_cast<char*>(&size)); | 
|  | 95 |  | 
| Kun Yi | d297c9f | 2019-01-09 13:52:30 -0800 | [diff] [blame] | 96 | if (!blob_.ParseFromString( | 
|  | 97 | file_->readAsStr(sizeof(uint64_t), size))) | 
|  | 98 | { | 
|  | 99 | /* Fail to parse the data, which might mean no preexsiting data | 
|  | 100 | * and is a valid case to handle */ | 
| Kun Yi | 8bcf79d | 2019-01-16 15:17:57 -0800 | [diff] [blame] | 101 | log<level::WARNING>( | 
|  | 102 | "Fail to parse. There might be no persisted blobs", | 
|  | 103 | entry("BLOB_ID=%s", blobId.c_str())); | 
| Kun Yi | d297c9f | 2019-01-09 13:52:30 -0800 | [diff] [blame] | 104 | } | 
|  | 105 | } | 
|  | 106 | catch (const std::exception& e) | 
|  | 107 | { | 
|  | 108 | /* Read causes unexpected system-level failure */ | 
| Kun Yi | 8bcf79d | 2019-01-16 15:17:57 -0800 | [diff] [blame] | 109 | log<level::ERR>("Reading from sysfile failed", | 
|  | 110 | entry("ERROR=%s", e.what())); | 
| Kun Yi | d297c9f | 2019-01-09 13:52:30 -0800 | [diff] [blame] | 111 | return false; | 
|  | 112 | } | 
|  | 113 | } | 
|  | 114 |  | 
| Kun Yi | 6baa713 | 2019-01-08 21:21:02 -0800 | [diff] [blame] | 115 | /* Iterate and find if there is an existing blob with this id. | 
|  | 116 | * blobsPtr points to a BinaryBlob container with STL-like semantics*/ | 
|  | 117 | auto blobsPtr = blob_.mutable_blobs(); | 
|  | 118 | auto blobIt = | 
|  | 119 | std::find_if(blobsPtr->begin(), blobsPtr->end(), | 
|  | 120 | [&](const auto& b) { return b.blob_id() == blobId; }); | 
|  | 121 |  | 
|  | 122 | if (blobIt != blobsPtr->end()) | 
|  | 123 | { | 
|  | 124 | currentBlob_ = &(*blobIt); | 
|  | 125 | return true; | 
|  | 126 | } | 
|  | 127 |  | 
|  | 128 | /* Otherwise, create the blob and append it */ | 
|  | 129 | currentBlob_ = blob_.add_blobs(); | 
|  | 130 | currentBlob_->set_blob_id(blobId); | 
|  | 131 |  | 
| Kun Yi | 8bcf79d | 2019-01-16 15:17:57 -0800 | [diff] [blame] | 132 | log<level::NOTICE>("Created new blob", entry("BLOB_ID=%s", blobId.c_str())); | 
| Kun Yi | 6baa713 | 2019-01-08 21:21:02 -0800 | [diff] [blame] | 133 | return true; | 
| Kun Yi | 64dc05c | 2018-12-19 13:19:03 -0800 | [diff] [blame] | 134 | } | 
|  | 135 |  | 
|  | 136 | bool BinaryStore::deleteBlob(const std::string& blobId) | 
|  | 137 | { | 
|  | 138 | return false; | 
|  | 139 | } | 
|  | 140 |  | 
|  | 141 | std::vector<uint8_t> BinaryStore::read(uint32_t offset, uint32_t requestedSize) | 
|  | 142 | { | 
| Kun Yi | 6967b77 | 2019-02-22 13:48:12 -0800 | [diff] [blame] | 143 | std::vector<uint8_t> result; | 
|  | 144 |  | 
|  | 145 | if (!currentBlob_) | 
|  | 146 | { | 
| Kun Yi | 8bcf79d | 2019-01-16 15:17:57 -0800 | [diff] [blame] | 147 | log<level::ERR>("No open blob to read"); | 
| Kun Yi | 6967b77 | 2019-02-22 13:48:12 -0800 | [diff] [blame] | 148 | return result; | 
|  | 149 | } | 
|  | 150 |  | 
|  | 151 | auto dataPtr = currentBlob_->mutable_data(); | 
|  | 152 |  | 
|  | 153 | /* If it is out of bound, return empty vector */ | 
|  | 154 | if (offset >= dataPtr->size()) | 
|  | 155 | { | 
| Kun Yi | 8bcf79d | 2019-01-16 15:17:57 -0800 | [diff] [blame] | 156 | log<level::ERR>("Read offset is beyond data size", | 
|  | 157 | entry("MAX_SIZE=0x%x", dataPtr->size()), | 
|  | 158 | entry("RECEIVED_OFFSET=0x%x", offset)); | 
| Kun Yi | 6967b77 | 2019-02-22 13:48:12 -0800 | [diff] [blame] | 159 | return result; | 
|  | 160 | } | 
|  | 161 |  | 
|  | 162 | uint32_t requestedEndPos = offset + requestedSize; | 
|  | 163 |  | 
|  | 164 | result = std::vector<uint8_t>( | 
|  | 165 | dataPtr->begin() + offset, | 
|  | 166 | std::min(dataPtr->begin() + requestedEndPos, dataPtr->end())); | 
| Kun Yi | 64dc05c | 2018-12-19 13:19:03 -0800 | [diff] [blame] | 167 | return result; | 
|  | 168 | } | 
|  | 169 |  | 
|  | 170 | bool BinaryStore::write(uint32_t offset, const std::vector<uint8_t>& data) | 
|  | 171 | { | 
| Kun Yi | 6967b77 | 2019-02-22 13:48:12 -0800 | [diff] [blame] | 172 | if (!currentBlob_) | 
|  | 173 | { | 
| Kun Yi | 8bcf79d | 2019-01-16 15:17:57 -0800 | [diff] [blame] | 174 | log<level::ERR>("No open blob to write"); | 
| Kun Yi | 6967b77 | 2019-02-22 13:48:12 -0800 | [diff] [blame] | 175 | return false; | 
|  | 176 | } | 
|  | 177 |  | 
|  | 178 | if (!writable_) | 
|  | 179 | { | 
| Kun Yi | 8bcf79d | 2019-01-16 15:17:57 -0800 | [diff] [blame] | 180 | log<level::ERR>("Open blob is not writable"); | 
| Kun Yi | 6967b77 | 2019-02-22 13:48:12 -0800 | [diff] [blame] | 181 | return false; | 
|  | 182 | } | 
|  | 183 |  | 
|  | 184 | auto dataPtr = currentBlob_->mutable_data(); | 
|  | 185 |  | 
|  | 186 | if (offset > dataPtr->size()) | 
|  | 187 | { | 
| Kun Yi | 8bcf79d | 2019-01-16 15:17:57 -0800 | [diff] [blame] | 188 | log<level::ERR>("Write would leave a gap with undefined data. Return."); | 
| Kun Yi | 6967b77 | 2019-02-22 13:48:12 -0800 | [diff] [blame] | 189 | return false; | 
|  | 190 | } | 
|  | 191 |  | 
|  | 192 | /* Copy (overwrite) the data */ | 
|  | 193 | if (offset + data.size() > dataPtr->size()) | 
|  | 194 | { | 
|  | 195 | dataPtr->resize(offset + data.size()); // not enough space, extend | 
|  | 196 | } | 
|  | 197 | std::copy(data.begin(), data.end(), dataPtr->begin() + offset); | 
|  | 198 | return true; | 
| Kun Yi | 64dc05c | 2018-12-19 13:19:03 -0800 | [diff] [blame] | 199 | } | 
|  | 200 |  | 
|  | 201 | bool BinaryStore::commit() | 
|  | 202 | { | 
| Kun Yi | 7005917 | 2019-02-22 16:31:42 -0800 | [diff] [blame] | 203 | /* Store as little endian to be platform agnostic. Consistent with read. */ | 
| Kun Yi | d297c9f | 2019-01-09 13:52:30 -0800 | [diff] [blame] | 204 | auto blobData = blob_.SerializeAsString(); | 
| Kun Yi | 7005917 | 2019-02-22 16:31:42 -0800 | [diff] [blame] | 205 | boost::endian::little_uint64_t sizeLE = blobData.size(); | 
|  | 206 | std::string commitData(sizeLE.data(), sizeof(sizeLE)); | 
| Kun Yi | d297c9f | 2019-01-09 13:52:30 -0800 | [diff] [blame] | 207 | commitData += blobData; | 
|  | 208 |  | 
|  | 209 | try | 
|  | 210 | { | 
|  | 211 | file_->writeStr(commitData, 0); | 
|  | 212 | } | 
|  | 213 | catch (const std::exception& e) | 
|  | 214 | { | 
| Kun Yi | d297c9f | 2019-01-09 13:52:30 -0800 | [diff] [blame] | 215 | commitState_ = CommitState::CommitError; | 
| Kun Yi | 8bcf79d | 2019-01-16 15:17:57 -0800 | [diff] [blame] | 216 | log<level::ERR>("Writing to sysfile failed", | 
|  | 217 | entry("ERROR=%s", e.what())); | 
| Kun Yi | d297c9f | 2019-01-09 13:52:30 -0800 | [diff] [blame] | 218 | return false; | 
|  | 219 | }; | 
|  | 220 |  | 
|  | 221 | commitState_ = CommitState::Clean; | 
|  | 222 | return true; | 
| Kun Yi | 64dc05c | 2018-12-19 13:19:03 -0800 | [diff] [blame] | 223 | } | 
|  | 224 |  | 
|  | 225 | bool BinaryStore::close() | 
|  | 226 | { | 
| Kun Yi | 6baa713 | 2019-01-08 21:21:02 -0800 | [diff] [blame] | 227 | currentBlob_ = nullptr; | 
|  | 228 | writable_ = false; | 
| Kun Yi | d297c9f | 2019-01-09 13:52:30 -0800 | [diff] [blame] | 229 | commitState_ = CommitState::Dirty; | 
| Kun Yi | 6baa713 | 2019-01-08 21:21:02 -0800 | [diff] [blame] | 230 | return true; | 
| Kun Yi | 64dc05c | 2018-12-19 13:19:03 -0800 | [diff] [blame] | 231 | } | 
|  | 232 |  | 
|  | 233 | bool BinaryStore::stat() | 
|  | 234 | { | 
|  | 235 | return false; | 
|  | 236 | } | 
|  | 237 |  | 
|  | 238 | } // namespace binstore |