Kun Yi | 64dc05c | 2018-12-19 13:19:03 -0800 | [diff] [blame] | 1 | #include "binarystore.hpp" |
| 2 | |
Kun Yi | 97be3af | 2019-03-05 22:43:41 -0800 | [diff] [blame] | 3 | #include "sys_file.hpp" |
| 4 | |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 5 | #include <pb_decode.h> |
| 6 | #include <pb_encode.h> |
Kun Yi | 97be3af | 2019-03-05 22:43:41 -0800 | [diff] [blame] | 7 | #include <sys/types.h> |
Kun Yi | d2d8cb6 | 2019-01-10 15:06:12 -0800 | [diff] [blame] | 8 | #include <unistd.h> |
| 9 | |
Kun Yi | 6baa713 | 2019-01-08 21:21:02 -0800 | [diff] [blame] | 10 | #include <algorithm> |
| 11 | #include <blobs-ipmid/blobs.hpp> |
Kun Yi | 7005917 | 2019-02-22 16:31:42 -0800 | [diff] [blame] | 12 | #include <boost/endian/arithmetic.hpp> |
Kun Yi | d2d8cb6 | 2019-01-10 15:06:12 -0800 | [diff] [blame] | 13 | #include <cstdint> |
Maksym Sloyko | eb27411 | 2021-10-27 23:48:32 +0000 | [diff] [blame] | 14 | #include <ipmid/handler.hpp> |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 15 | #include <map> |
Kun Yi | d2d8cb6 | 2019-01-10 15:06:12 -0800 | [diff] [blame] | 16 | #include <memory> |
Willy Tu | 67391d5 | 2021-12-07 19:53:49 -0800 | [diff] [blame] | 17 | #include <optional> |
Kun Yi | 8bcf79d | 2019-01-16 15:17:57 -0800 | [diff] [blame] | 18 | #include <phosphor-logging/elog.hpp> |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 19 | #include <stdplus/str/cat.hpp> |
Kun Yi | d2d8cb6 | 2019-01-10 15:06:12 -0800 | [diff] [blame] | 20 | #include <string> |
| 21 | #include <vector> |
| 22 | |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 23 | #include "binaryblob.pb.n.h" |
Kun Yi | d2d8cb6 | 2019-01-10 15:06:12 -0800 | [diff] [blame] | 24 | |
| 25 | using std::size_t; |
| 26 | using std::uint16_t; |
| 27 | using std::uint32_t; |
| 28 | using std::uint64_t; |
| 29 | using std::uint8_t; |
Kun Yi | 6baa713 | 2019-01-08 21:21:02 -0800 | [diff] [blame] | 30 | |
Kun Yi | 64dc05c | 2018-12-19 13:19:03 -0800 | [diff] [blame] | 31 | namespace binstore |
| 32 | { |
| 33 | |
Kun Yi | 8bcf79d | 2019-01-16 15:17:57 -0800 | [diff] [blame] | 34 | using namespace phosphor::logging; |
| 35 | |
Willy Tu | 7f10780 | 2023-11-06 23:05:25 -0800 | [diff] [blame] | 36 | std::unique_ptr<BinaryStoreInterface> BinaryStore::createFromConfig( |
| 37 | const std::string& baseBlobId, std::unique_ptr<SysFile> file, |
| 38 | std::optional<uint32_t> maxSize, std::optional<std::string> aliasBlobBaseId) |
Kun Yi | 64dc05c | 2018-12-19 13:19:03 -0800 | [diff] [blame] | 39 | { |
Kun Yi | 2765b64 | 2019-01-16 11:11:24 -0800 | [diff] [blame] | 40 | if (baseBlobId.empty() || !file) |
| 41 | { |
Kun Yi | 8bcf79d | 2019-01-16 15:17:57 -0800 | [diff] [blame] | 42 | log<level::ERR>("Unable to create binarystore from invalid config", |
| 43 | entry("BASE_ID=%s", baseBlobId.c_str())); |
Kun Yi | 2765b64 | 2019-01-16 11:11:24 -0800 | [diff] [blame] | 44 | return nullptr; |
| 45 | } |
| 46 | |
Willy Tu | 67391d5 | 2021-12-07 19:53:49 -0800 | [diff] [blame] | 47 | auto store = |
| 48 | std::make_unique<BinaryStore>(baseBlobId, std::move(file), maxSize); |
Kun Yi | 2765b64 | 2019-01-16 11:11:24 -0800 | [diff] [blame] | 49 | |
Willy Tu | 7f10780 | 2023-11-06 23:05:25 -0800 | [diff] [blame] | 50 | if (!store->loadSerializedData(aliasBlobBaseId)) |
Kun Yi | 8ca234e | 2019-03-04 10:14:45 -0800 | [diff] [blame] | 51 | { |
| 52 | return nullptr; |
| 53 | } |
| 54 | |
Willy Tu | 351a5d0 | 2021-12-07 19:48:40 -0800 | [diff] [blame] | 55 | return store; |
Kun Yi | 64dc05c | 2018-12-19 13:19:03 -0800 | [diff] [blame] | 56 | } |
| 57 | |
Maksym Sloyko | eb27411 | 2021-10-27 23:48:32 +0000 | [diff] [blame] | 58 | std::unique_ptr<BinaryStoreInterface> |
Willy Tu | 67391d5 | 2021-12-07 19:53:49 -0800 | [diff] [blame] | 59 | BinaryStore::createFromFile(std::unique_ptr<SysFile> file, bool readOnly, |
| 60 | std::optional<uint32_t> maxSize) |
Maksym Sloyko | eb27411 | 2021-10-27 23:48:32 +0000 | [diff] [blame] | 61 | { |
| 62 | if (!file) |
| 63 | { |
| 64 | log<level::ERR>("Unable to create binarystore from invalid file"); |
| 65 | return nullptr; |
| 66 | } |
| 67 | |
Willy Tu | 67391d5 | 2021-12-07 19:53:49 -0800 | [diff] [blame] | 68 | auto store = |
| 69 | std::make_unique<BinaryStore>(std::move(file), readOnly, maxSize); |
Maksym Sloyko | eb27411 | 2021-10-27 23:48:32 +0000 | [diff] [blame] | 70 | |
| 71 | if (!store->loadSerializedData()) |
| 72 | { |
| 73 | return nullptr; |
| 74 | } |
| 75 | |
Willy Tu | 351a5d0 | 2021-12-07 19:48:40 -0800 | [diff] [blame] | 76 | return store; |
Maksym Sloyko | eb27411 | 2021-10-27 23:48:32 +0000 | [diff] [blame] | 77 | } |
| 78 | |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 79 | template <typename S> |
| 80 | static constexpr auto pbDecodeStr = [](pb_istream_t* stream, |
| 81 | const pb_field_iter_t*, |
| 82 | void** arg) noexcept { |
| 83 | static_assert(sizeof(*std::declval<S>().data()) == sizeof(pb_byte_t)); |
| 84 | auto& s = *reinterpret_cast<S*>(*arg); |
| 85 | s.resize(stream->bytes_left); |
| 86 | return pb_read(stream, reinterpret_cast<pb_byte_t*>(s.data()), s.size()); |
| 87 | }; |
| 88 | |
| 89 | template <typename T> |
| 90 | static pb_callback_t pbStrDecoder(T& t) noexcept |
| 91 | { |
| 92 | return {{.decode = pbDecodeStr<T>}, &t}; |
| 93 | } |
| 94 | |
| 95 | template <typename S> |
| 96 | static constexpr auto pbEncodeStr = [](pb_ostream_t* stream, |
| 97 | const pb_field_iter_t* field, |
| 98 | void* const* arg) noexcept { |
| 99 | static_assert(sizeof(*std::declval<S>().data()) == sizeof(pb_byte_t)); |
| 100 | const auto& s = *reinterpret_cast<const S*>(*arg); |
| 101 | return pb_encode_tag_for_field(stream, field) && |
| 102 | pb_encode_string( |
| 103 | stream, reinterpret_cast<const pb_byte_t*>(s.data()), s.size()); |
| 104 | }; |
| 105 | |
| 106 | template <typename T> |
| 107 | static pb_callback_t pbStrEncoder(const T& t) noexcept |
| 108 | { |
| 109 | return {{.encode = pbEncodeStr<T>}, const_cast<T*>(&t)}; |
| 110 | } |
| 111 | |
Willy Tu | 7f10780 | 2023-11-06 23:05:25 -0800 | [diff] [blame] | 112 | bool BinaryStore::loadSerializedData(std::optional<std::string> aliasBlobBaseId) |
Kun Yi | 97be3af | 2019-03-05 22:43:41 -0800 | [diff] [blame] | 113 | { |
| 114 | /* Load blob from sysfile if we know it might not match what we have. |
| 115 | * Note it will overwrite existing unsaved data per design. */ |
Kun Yi | e535a73 | 2019-04-03 19:03:25 -0700 | [diff] [blame] | 116 | if (commitState_ == CommitState::Clean || |
| 117 | commitState_ == CommitState::Uninitialized) |
Kun Yi | 97be3af | 2019-03-05 22:43:41 -0800 | [diff] [blame] | 118 | { |
| 119 | return true; |
| 120 | } |
| 121 | |
| 122 | log<level::NOTICE>("Try loading blob from persistent data", |
| 123 | entry("BASE_ID=%s", baseBlobId_.c_str())); |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 124 | std::string protoBlobId; |
| 125 | static constexpr auto blobcb = [](pb_istream_t* stream, |
| 126 | const pb_field_iter_t*, |
| 127 | void** arg) noexcept { |
| 128 | std::string id; |
| 129 | std::vector<std::uint8_t> data; |
| 130 | binstore_binaryblobproto_BinaryBlob msg = { |
| 131 | .blob_id = pbStrDecoder(id), |
| 132 | .data = pbStrDecoder(data), |
| 133 | }; |
| 134 | if (!pb_decode(stream, binstore_binaryblobproto_BinaryBlob_fields, |
| 135 | &msg)) |
| 136 | { |
| 137 | return false; |
| 138 | } |
| 139 | reinterpret_cast<decltype(std::declval<BinaryStore>().blobs_)*>(*arg) |
| 140 | ->emplace(id, data); |
| 141 | return true; |
| 142 | }; |
| 143 | |
Kun Yi | 97be3af | 2019-03-05 22:43:41 -0800 | [diff] [blame] | 144 | try |
| 145 | { |
| 146 | /* Parse length-prefixed format to protobuf */ |
| 147 | boost::endian::little_uint64_t size = 0; |
| 148 | file_->readToBuf(0, sizeof(size), reinterpret_cast<char*>(&size)); |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 149 | auto proto = file_->readAsStr(sizeof(size), size); |
Kun Yi | 97be3af | 2019-03-05 22:43:41 -0800 | [diff] [blame] | 150 | |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 151 | auto ist = pb_istream_from_buffer( |
| 152 | reinterpret_cast<const pb_byte_t*>(proto.data()), proto.size()); |
| 153 | binstore_binaryblobproto_BinaryBlobBase msg = { |
| 154 | .blob_base_id = pbStrDecoder(protoBlobId), |
| 155 | .blobs = {{.decode = blobcb}, &blobs_}, |
| 156 | }; |
| 157 | blobs_.clear(); // Purge old contents before new append during decode |
| 158 | if (!pb_decode(&ist, binstore_binaryblobproto_BinaryBlobBase_fields, |
| 159 | &msg)) |
Kun Yi | 97be3af | 2019-03-05 22:43:41 -0800 | [diff] [blame] | 160 | { |
| 161 | /* Fail to parse the data, which might mean no preexsiting blobs |
| 162 | * and is a valid case to handle. Simply init an empty binstore. */ |
Kun Yi | e535a73 | 2019-04-03 19:03:25 -0700 | [diff] [blame] | 163 | commitState_ = CommitState::Uninitialized; |
Kun Yi | 97be3af | 2019-03-05 22:43:41 -0800 | [diff] [blame] | 164 | } |
| 165 | } |
Kun Yi | c83d2fa | 2019-04-03 18:40:19 -0700 | [diff] [blame] | 166 | catch (const std::system_error& e) |
Kun Yi | 97be3af | 2019-03-05 22:43:41 -0800 | [diff] [blame] | 167 | { |
| 168 | /* Read causes unexpected system-level failure */ |
| 169 | log<level::ERR>("Reading from sysfile failed", |
| 170 | entry("ERROR=%s", e.what())); |
| 171 | return false; |
| 172 | } |
Kun Yi | c83d2fa | 2019-04-03 18:40:19 -0700 | [diff] [blame] | 173 | catch (const std::exception& e) |
| 174 | { |
Kun Yi | e535a73 | 2019-04-03 19:03:25 -0700 | [diff] [blame] | 175 | /* Non system error originates from junk value in 'size' */ |
| 176 | commitState_ = CommitState::Uninitialized; |
| 177 | } |
| 178 | |
| 179 | if (commitState_ == CommitState::Uninitialized) |
| 180 | { |
| 181 | log<level::WARNING>("Fail to parse. There might be no persisted blobs", |
| 182 | entry("BASE_ID=%s", baseBlobId_.c_str())); |
Kun Yi | c83d2fa | 2019-04-03 18:40:19 -0700 | [diff] [blame] | 183 | return true; |
| 184 | } |
Kun Yi | 97be3af | 2019-03-05 22:43:41 -0800 | [diff] [blame] | 185 | |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 186 | if (baseBlobId_.empty() && !protoBlobId.empty()) |
| 187 | { |
| 188 | baseBlobId_ = std::move(protoBlobId); |
| 189 | } |
| 190 | else if (protoBlobId == aliasBlobBaseId) |
Willy Tu | 7f10780 | 2023-11-06 23:05:25 -0800 | [diff] [blame] | 191 | { |
| 192 | log<level::WARNING>("Alias blob id, rename blob id...", |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 193 | entry("LOADED=%s", protoBlobId.c_str()), |
Willy Tu | 7f10780 | 2023-11-06 23:05:25 -0800 | [diff] [blame] | 194 | entry("RENAMED=%s", baseBlobId_.c_str())); |
| 195 | std::string tmpBlobId = baseBlobId_; |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 196 | baseBlobId_ = *aliasBlobBaseId; |
Willy Tu | 7f10780 | 2023-11-06 23:05:25 -0800 | [diff] [blame] | 197 | return setBaseBlobId(tmpBlobId); |
| 198 | } |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 199 | else if (protoBlobId != baseBlobId_ && !readOnly_) |
Kun Yi | 8ca234e | 2019-03-04 10:14:45 -0800 | [diff] [blame] | 200 | { |
| 201 | /* Uh oh, stale data loaded. Clean it and commit. */ |
| 202 | // TODO: it might be safer to add an option in config to error out |
| 203 | // instead of to overwrite. |
| 204 | log<level::ERR>("Stale blob data, resetting internals...", |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 205 | entry("LOADED=%s", protoBlobId.c_str()), |
Kun Yi | 8ca234e | 2019-03-04 10:14:45 -0800 | [diff] [blame] | 206 | entry("EXPECTED=%s", baseBlobId_.c_str())); |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 207 | blobs_.clear(); |
Kun Yi | 8ca234e | 2019-03-04 10:14:45 -0800 | [diff] [blame] | 208 | return this->commit(); |
| 209 | } |
| 210 | |
Kun Yi | 97be3af | 2019-03-05 22:43:41 -0800 | [diff] [blame] | 211 | return true; |
| 212 | } |
| 213 | |
Kun Yi | 64dc05c | 2018-12-19 13:19:03 -0800 | [diff] [blame] | 214 | std::string BinaryStore::getBaseBlobId() const |
| 215 | { |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 216 | return baseBlobId_; |
Kun Yi | 64dc05c | 2018-12-19 13:19:03 -0800 | [diff] [blame] | 217 | } |
| 218 | |
Willy Tu | 7f10780 | 2023-11-06 23:05:25 -0800 | [diff] [blame] | 219 | bool BinaryStore::setBaseBlobId(const std::string& baseBlobId) |
| 220 | { |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 221 | for (auto it = blobs_.begin(); it != blobs_.end();) |
Willy Tu | 7f10780 | 2023-11-06 23:05:25 -0800 | [diff] [blame] | 222 | { |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 223 | auto curr = it++; |
| 224 | if (curr->first.starts_with(baseBlobId_)) |
Willy Tu | 7f10780 | 2023-11-06 23:05:25 -0800 | [diff] [blame] | 225 | { |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 226 | auto nh = blobs_.extract(curr); |
| 227 | nh.key() = stdplus::strCat( |
| 228 | baseBlobId, |
| 229 | std::string_view(curr->first).substr(baseBlobId_.size())); |
| 230 | blobs_.insert(std::move(nh)); |
Willy Tu | 7f10780 | 2023-11-06 23:05:25 -0800 | [diff] [blame] | 231 | } |
| 232 | } |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 233 | baseBlobId_ = baseBlobId; |
Willy Tu | 7f10780 | 2023-11-06 23:05:25 -0800 | [diff] [blame] | 234 | return this->commit(); |
| 235 | } |
| 236 | |
Kun Yi | 64dc05c | 2018-12-19 13:19:03 -0800 | [diff] [blame] | 237 | std::vector<std::string> BinaryStore::getBlobIds() const |
| 238 | { |
| 239 | std::vector<std::string> result; |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 240 | result.reserve(blobs_.size() + 1); |
Willy Tu | fdebaa3 | 2022-02-08 16:34:20 -0800 | [diff] [blame] | 241 | result.emplace_back(getBaseBlobId()); |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 242 | for (const auto& kv : blobs_) |
| 243 | { |
| 244 | result.emplace_back(kv.first); |
| 245 | } |
Kun Yi | 64dc05c | 2018-12-19 13:19:03 -0800 | [diff] [blame] | 246 | return result; |
| 247 | } |
| 248 | |
| 249 | bool BinaryStore::openOrCreateBlob(const std::string& blobId, uint16_t flags) |
| 250 | { |
Kun Yi | 6baa713 | 2019-01-08 21:21:02 -0800 | [diff] [blame] | 251 | if (!(flags & blobs::OpenFlags::read)) |
| 252 | { |
Kun Yi | 8bcf79d | 2019-01-16 15:17:57 -0800 | [diff] [blame] | 253 | log<level::ERR>("OpenFlags::read not specified when opening", |
| 254 | entry("BLOB_ID=%s", blobId.c_str())); |
Kun Yi | 6baa713 | 2019-01-08 21:21:02 -0800 | [diff] [blame] | 255 | return false; |
| 256 | } |
| 257 | |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 258 | if (!currentBlob_.empty()) |
Kun Yi | 6baa713 | 2019-01-08 21:21:02 -0800 | [diff] [blame] | 259 | { |
Kun Yi | 8bcf79d | 2019-01-16 15:17:57 -0800 | [diff] [blame] | 260 | log<level::ERR>("Already handling a different blob", |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 261 | entry("EXPECTED=%s", currentBlob_.c_str()), |
Kun Yi | 8bcf79d | 2019-01-16 15:17:57 -0800 | [diff] [blame] | 262 | entry("RECEIVED=%s", blobId.c_str())); |
Kun Yi | 6baa713 | 2019-01-08 21:21:02 -0800 | [diff] [blame] | 263 | return false; |
| 264 | } |
| 265 | |
Maksym Sloyko | eb27411 | 2021-10-27 23:48:32 +0000 | [diff] [blame] | 266 | if (readOnly_ && (flags & blobs::OpenFlags::write)) |
| 267 | { |
| 268 | log<level::ERR>("Can't open the blob for writing: read-only store", |
| 269 | entry("BLOB_ID=%s", blobId.c_str())); |
| 270 | return false; |
| 271 | } |
| 272 | |
Kun Yi | 6baa713 | 2019-01-08 21:21:02 -0800 | [diff] [blame] | 273 | writable_ = flags & blobs::OpenFlags::write; |
| 274 | |
Kun Yi | 97be3af | 2019-03-05 22:43:41 -0800 | [diff] [blame] | 275 | /* If there are uncommitted data, discard them. */ |
| 276 | if (!this->loadSerializedData()) |
Kun Yi | d297c9f | 2019-01-09 13:52:30 -0800 | [diff] [blame] | 277 | { |
Kun Yi | 97be3af | 2019-03-05 22:43:41 -0800 | [diff] [blame] | 278 | return false; |
Kun Yi | d297c9f | 2019-01-09 13:52:30 -0800 | [diff] [blame] | 279 | } |
| 280 | |
Kun Yi | 6baa713 | 2019-01-08 21:21:02 -0800 | [diff] [blame] | 281 | /* Iterate and find if there is an existing blob with this id. |
| 282 | * blobsPtr points to a BinaryBlob container with STL-like semantics*/ |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 283 | if (blobs_.find(blobId) != blobs_.end()) |
Kun Yi | 6baa713 | 2019-01-08 21:21:02 -0800 | [diff] [blame] | 284 | { |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 285 | currentBlob_ = blobId; |
Kun Yi | 6baa713 | 2019-01-08 21:21:02 -0800 | [diff] [blame] | 286 | return true; |
| 287 | } |
| 288 | |
| 289 | /* Otherwise, create the blob and append it */ |
Maksym Sloyko | eb27411 | 2021-10-27 23:48:32 +0000 | [diff] [blame] | 290 | if (readOnly_) |
| 291 | { |
| 292 | return false; |
| 293 | } |
Kun Yi | 6baa713 | 2019-01-08 21:21:02 -0800 | [diff] [blame] | 294 | |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 295 | blobs_.emplace(blobId, std::vector<std::uint8_t>{}); |
| 296 | currentBlob_ = blobId; |
| 297 | commitState_ = CommitState::Dirty; |
| 298 | 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] | 299 | return true; |
Kun Yi | 64dc05c | 2018-12-19 13:19:03 -0800 | [diff] [blame] | 300 | } |
| 301 | |
Willy Tu | 351a5d0 | 2021-12-07 19:48:40 -0800 | [diff] [blame] | 302 | bool BinaryStore::deleteBlob(const std::string&) |
Kun Yi | 64dc05c | 2018-12-19 13:19:03 -0800 | [diff] [blame] | 303 | { |
| 304 | return false; |
| 305 | } |
| 306 | |
| 307 | std::vector<uint8_t> BinaryStore::read(uint32_t offset, uint32_t requestedSize) |
| 308 | { |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 309 | if (currentBlob_.empty()) |
Kun Yi | 6967b77 | 2019-02-22 13:48:12 -0800 | [diff] [blame] | 310 | { |
Kun Yi | 8bcf79d | 2019-01-16 15:17:57 -0800 | [diff] [blame] | 311 | log<level::ERR>("No open blob to read"); |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 312 | return {}; |
Kun Yi | 6967b77 | 2019-02-22 13:48:12 -0800 | [diff] [blame] | 313 | } |
| 314 | |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 315 | const auto& data = blobs_.find(currentBlob_)->second; |
Kun Yi | 6967b77 | 2019-02-22 13:48:12 -0800 | [diff] [blame] | 316 | |
| 317 | /* If it is out of bound, return empty vector */ |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 318 | if (offset >= data.size()) |
Kun Yi | 6967b77 | 2019-02-22 13:48:12 -0800 | [diff] [blame] | 319 | { |
Kun Yi | 8bcf79d | 2019-01-16 15:17:57 -0800 | [diff] [blame] | 320 | log<level::ERR>("Read offset is beyond data size", |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 321 | entry("MAX_SIZE=0x%x", data.size()), |
Kun Yi | 8bcf79d | 2019-01-16 15:17:57 -0800 | [diff] [blame] | 322 | entry("RECEIVED_OFFSET=0x%x", offset)); |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 323 | return {}; |
Kun Yi | 6967b77 | 2019-02-22 13:48:12 -0800 | [diff] [blame] | 324 | } |
| 325 | |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 326 | auto s = data.begin() + offset; |
| 327 | return {s, s + std::min<size_t>(requestedSize, data.size() - offset)}; |
Kun Yi | 64dc05c | 2018-12-19 13:19:03 -0800 | [diff] [blame] | 328 | } |
| 329 | |
Maksym Sloyko | eb27411 | 2021-10-27 23:48:32 +0000 | [diff] [blame] | 330 | std::vector<uint8_t> BinaryStore::readBlob(const std::string& blobId) const |
| 331 | { |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 332 | const auto blobIt = blobs_.find(blobId); |
| 333 | if (blobIt == blobs_.end()) |
Maksym Sloyko | eb27411 | 2021-10-27 23:48:32 +0000 | [diff] [blame] | 334 | { |
| 335 | throw ipmi::HandlerCompletion(ipmi::ccUnspecifiedError); |
| 336 | } |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 337 | return blobIt->second; |
| 338 | } |
Maksym Sloyko | eb27411 | 2021-10-27 23:48:32 +0000 | [diff] [blame] | 339 | |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 340 | static binstore_binaryblobproto_BinaryBlobBase makeEncoder( |
| 341 | const std::string& base, |
| 342 | const std::map<std::string, std::vector<std::uint8_t>>& blobs) noexcept |
| 343 | { |
| 344 | using Blobs = std::decay_t<decltype(blobs)>; |
| 345 | static constexpr auto blobcb = [](pb_ostream_t* stream, |
| 346 | const pb_field_iter_t* field, |
| 347 | void* const* arg) noexcept { |
| 348 | const auto& blobs = *reinterpret_cast<const Blobs*>(*arg); |
| 349 | for (const auto& [id, data] : blobs) |
| 350 | { |
| 351 | binstore_binaryblobproto_BinaryBlob msg = { |
| 352 | .blob_id = pbStrEncoder(id), |
| 353 | .data = pbStrEncoder(data), |
| 354 | }; |
| 355 | if (!pb_encode_tag_for_field(stream, field) || |
| 356 | !pb_encode_submessage( |
| 357 | stream, binstore_binaryblobproto_BinaryBlob_fields, &msg)) |
| 358 | { |
| 359 | return false; |
| 360 | } |
| 361 | } |
| 362 | return true; |
| 363 | }; |
| 364 | return { |
| 365 | .blob_base_id = pbStrEncoder(base), |
| 366 | .blobs = {{.encode = blobcb}, |
| 367 | const_cast<void*>(reinterpret_cast<const void*>(&blobs))}, |
| 368 | }; |
| 369 | } |
Maksym Sloyko | eb27411 | 2021-10-27 23:48:32 +0000 | [diff] [blame] | 370 | |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 371 | static std::size_t |
| 372 | payloadCalcSize(const binstore_binaryblobproto_BinaryBlobBase& msg) |
| 373 | { |
| 374 | pb_ostream_t nost = {}; |
| 375 | if (!pb_encode(&nost, binstore_binaryblobproto_BinaryBlobBase_fields, &msg)) |
| 376 | { |
| 377 | throw std::runtime_error( |
| 378 | std::format("Calculating msg size: {}", PB_GET_ERROR(&nost))); |
| 379 | } |
| 380 | // Proto is prepended with the size of the proto |
| 381 | return nost.bytes_written + sizeof(boost::endian::little_uint64_t); |
Maksym Sloyko | eb27411 | 2021-10-27 23:48:32 +0000 | [diff] [blame] | 382 | } |
| 383 | |
Kun Yi | 64dc05c | 2018-12-19 13:19:03 -0800 | [diff] [blame] | 384 | bool BinaryStore::write(uint32_t offset, const std::vector<uint8_t>& data) |
| 385 | { |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 386 | if (currentBlob_.empty()) |
Kun Yi | 6967b77 | 2019-02-22 13:48:12 -0800 | [diff] [blame] | 387 | { |
Kun Yi | 8bcf79d | 2019-01-16 15:17:57 -0800 | [diff] [blame] | 388 | log<level::ERR>("No open blob to write"); |
Kun Yi | 6967b77 | 2019-02-22 13:48:12 -0800 | [diff] [blame] | 389 | return false; |
| 390 | } |
| 391 | |
| 392 | if (!writable_) |
| 393 | { |
Kun Yi | 8bcf79d | 2019-01-16 15:17:57 -0800 | [diff] [blame] | 394 | log<level::ERR>("Open blob is not writable"); |
Kun Yi | 6967b77 | 2019-02-22 13:48:12 -0800 | [diff] [blame] | 395 | return false; |
| 396 | } |
| 397 | |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 398 | auto& bdata = blobs_.find(currentBlob_)->second; |
| 399 | if (offset > bdata.size()) |
Kun Yi | 6967b77 | 2019-02-22 13:48:12 -0800 | [diff] [blame] | 400 | { |
Kun Yi | 8bcf79d | 2019-01-16 15:17:57 -0800 | [diff] [blame] | 401 | log<level::ERR>("Write would leave a gap with undefined data. Return."); |
Kun Yi | 6967b77 | 2019-02-22 13:48:12 -0800 | [diff] [blame] | 402 | return false; |
| 403 | } |
| 404 | |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 405 | std::size_t oldsize = bdata.size(), reqSize = offset + data.size(); |
| 406 | if (reqSize > bdata.size()) |
| 407 | { |
| 408 | bdata.resize(reqSize); |
| 409 | } |
Willy Tu | 67391d5 | 2021-12-07 19:53:49 -0800 | [diff] [blame] | 410 | |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 411 | if (payloadCalcSize(makeEncoder(baseBlobId_, blobs_)) > |
| 412 | maxSize.value_or( |
| 413 | std::numeric_limits<std::decay_t<decltype(*maxSize)>>::max())) |
Willy Tu | 67391d5 | 2021-12-07 19:53:49 -0800 | [diff] [blame] | 414 | { |
| 415 | log<level::ERR>("Write data would make the total size exceed the max " |
| 416 | "size allowed. Return."); |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 417 | bdata.resize(oldsize); |
Willy Tu | 67391d5 | 2021-12-07 19:53:49 -0800 | [diff] [blame] | 418 | return false; |
| 419 | } |
| 420 | |
Kun Yi | 1a25e0d | 2020-05-11 12:28:53 -0700 | [diff] [blame] | 421 | commitState_ = CommitState::Dirty; |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 422 | std::copy(data.begin(), data.end(), bdata.data() + offset); |
Kun Yi | 6967b77 | 2019-02-22 13:48:12 -0800 | [diff] [blame] | 423 | return true; |
Kun Yi | 64dc05c | 2018-12-19 13:19:03 -0800 | [diff] [blame] | 424 | } |
| 425 | |
| 426 | bool BinaryStore::commit() |
| 427 | { |
Maksym Sloyko | eb27411 | 2021-10-27 23:48:32 +0000 | [diff] [blame] | 428 | if (readOnly_) |
| 429 | { |
| 430 | log<level::ERR>("ReadOnly blob, not committing"); |
| 431 | return false; |
| 432 | } |
| 433 | |
Kun Yi | 7005917 | 2019-02-22 16:31:42 -0800 | [diff] [blame] | 434 | /* Store as little endian to be platform agnostic. Consistent with read. */ |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 435 | auto msg = makeEncoder(baseBlobId_, blobs_); |
| 436 | auto outSize = payloadCalcSize(msg); |
| 437 | if (outSize > |
| 438 | maxSize.value_or( |
| 439 | std::numeric_limits<std::decay_t<decltype(*maxSize)>>::max())) |
Willy Tu | 67391d5 | 2021-12-07 19:53:49 -0800 | [diff] [blame] | 440 | { |
Willy Tu | 9163385 | 2022-02-18 08:55:43 -0800 | [diff] [blame] | 441 | log<level::ERR>("Commit Data exceeded maximum allowed size"); |
Willy Tu | 67391d5 | 2021-12-07 19:53:49 -0800 | [diff] [blame] | 442 | return false; |
| 443 | } |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 444 | std::string buf(outSize, '\0'); |
| 445 | auto& size = *reinterpret_cast<boost::endian::little_uint64_t*>(buf.data()); |
| 446 | auto ost = pb_ostream_from_buffer(reinterpret_cast<pb_byte_t*>(buf.data()) + |
| 447 | sizeof(size), |
| 448 | buf.size() - sizeof(size)); |
| 449 | pb_encode(&ost, binstore_binaryblobproto_BinaryBlobBase_fields, &msg); |
| 450 | size = ost.bytes_written; |
Kun Yi | d297c9f | 2019-01-09 13:52:30 -0800 | [diff] [blame] | 451 | try |
| 452 | { |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 453 | file_->writeStr(buf, 0); |
Kun Yi | d297c9f | 2019-01-09 13:52:30 -0800 | [diff] [blame] | 454 | } |
| 455 | catch (const std::exception& e) |
| 456 | { |
Kun Yi | d297c9f | 2019-01-09 13:52:30 -0800 | [diff] [blame] | 457 | commitState_ = CommitState::CommitError; |
Kun Yi | 8bcf79d | 2019-01-16 15:17:57 -0800 | [diff] [blame] | 458 | log<level::ERR>("Writing to sysfile failed", |
| 459 | entry("ERROR=%s", e.what())); |
Kun Yi | d297c9f | 2019-01-09 13:52:30 -0800 | [diff] [blame] | 460 | return false; |
| 461 | }; |
| 462 | |
| 463 | commitState_ = CommitState::Clean; |
| 464 | return true; |
Kun Yi | 64dc05c | 2018-12-19 13:19:03 -0800 | [diff] [blame] | 465 | } |
| 466 | |
| 467 | bool BinaryStore::close() |
| 468 | { |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 469 | currentBlob_.clear(); |
Kun Yi | 6baa713 | 2019-01-08 21:21:02 -0800 | [diff] [blame] | 470 | writable_ = false; |
Kun Yi | d297c9f | 2019-01-09 13:52:30 -0800 | [diff] [blame] | 471 | commitState_ = CommitState::Dirty; |
Kun Yi | 6baa713 | 2019-01-08 21:21:02 -0800 | [diff] [blame] | 472 | return true; |
Kun Yi | 64dc05c | 2018-12-19 13:19:03 -0800 | [diff] [blame] | 473 | } |
| 474 | |
Kun Yi | 1a25e0d | 2020-05-11 12:28:53 -0700 | [diff] [blame] | 475 | /* |
| 476 | * Sets |meta| with size and state of the blob. Returns |blobState| with |
| 477 | * standard definition from phosphor-ipmi-blobs header blob.hpp, plus OEM |
| 478 | * flag bits BinaryStore::CommitState. |
| 479 | |
| 480 | enum StateFlags |
Kun Yi | 64dc05c | 2018-12-19 13:19:03 -0800 | [diff] [blame] | 481 | { |
Kun Yi | 1a25e0d | 2020-05-11 12:28:53 -0700 | [diff] [blame] | 482 | open_read = (1 << 0), |
| 483 | open_write = (1 << 1), |
| 484 | committing = (1 << 2), |
| 485 | committed = (1 << 3), |
| 486 | commit_error = (1 << 4), |
| 487 | }; |
| 488 | |
| 489 | enum CommitState |
| 490 | { |
| 491 | Dirty = (1 << 8), // In-memory data might not match persisted data |
| 492 | Clean = (1 << 9), // In-memory data matches persisted data |
| 493 | Uninitialized = (1 << 10), // Cannot find persisted data |
| 494 | CommitError = (1 << 11) // Error happened during committing |
| 495 | }; |
| 496 | |
| 497 | */ |
| 498 | bool BinaryStore::stat(blobs::BlobMeta* meta) |
| 499 | { |
| 500 | uint16_t blobState = blobs::StateFlags::open_read; |
| 501 | if (writable_) |
| 502 | { |
| 503 | blobState |= blobs::StateFlags::open_write; |
| 504 | } |
| 505 | |
| 506 | if (commitState_ == CommitState::Clean) |
| 507 | { |
| 508 | blobState |= blobs::StateFlags::committed; |
| 509 | } |
| 510 | else if (commitState_ == CommitState::CommitError) |
| 511 | { |
| 512 | blobState |= blobs::StateFlags::commit_error; |
| 513 | } |
| 514 | blobState |= commitState_; |
| 515 | |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 516 | if (!currentBlob_.empty()) |
Kun Yi | 1a25e0d | 2020-05-11 12:28:53 -0700 | [diff] [blame] | 517 | { |
William A. Kennington III | 7e14586 | 2024-02-01 15:56:33 -0800 | [diff] [blame^] | 518 | meta->size = blobs_.find(currentBlob_)->second.size(); |
Kun Yi | 1a25e0d | 2020-05-11 12:28:53 -0700 | [diff] [blame] | 519 | } |
| 520 | else |
| 521 | { |
| 522 | meta->size = 0; |
| 523 | } |
| 524 | meta->blobState = blobState; |
| 525 | |
| 526 | return true; |
Kun Yi | 64dc05c | 2018-12-19 13:19:03 -0800 | [diff] [blame] | 527 | } |
| 528 | |
| 529 | } // namespace binstore |