#include "version_handler.hpp" #include #include #include #include #include #include #include #include namespace ipmi_flash { VersionBlobHandler::VersionBlobHandler( std::vector>&& configs) { for (auto& config : configs) { auto info = std::make_unique(); info->blobId = std::move(config.blobId); info->actions = std::move(config.actions); info->handler = std::move(config.handler); info->actions->onOpen->setCallback( [infoP = info.get()](TriggerableActionInterface& tai) { auto data = std::make_shared>>(); do { if (tai.status() != ActionStatus::success) { fprintf(stderr, "Version file unit failed for %s\n", infoP->blobId.c_str()); continue; } if (!infoP->handler->open("", std::ios::in)) { fprintf(stderr, "Opening version file failed for %s\n", infoP->blobId.c_str()); continue; } auto d = infoP->handler->read( 0, std::numeric_limits::max()); infoP->handler->close(); if (!d) { fprintf(stderr, "Reading version file failed for %s\n", infoP->blobId.c_str()); continue; } *data = std::move(d); } while (false); for (auto sessionP : infoP->sessionsToUpdate) { sessionP->data = data; } infoP->sessionsToUpdate.clear(); }); if (!blobInfoMap.try_emplace(info->blobId, std::move(info)).second) { fprintf(stderr, "Ignoring duplicate config for %s\n", info->blobId.c_str()); } } } bool VersionBlobHandler::canHandleBlob(const std::string& path) { return blobInfoMap.find(path) != blobInfoMap.end(); } std::vector VersionBlobHandler::getBlobIds() { std::vector ret; for (const auto& [key, _] : blobInfoMap) { ret.emplace_back(key); } return ret; } /** * deleteBlob - does nothing, always fails */ bool VersionBlobHandler::deleteBlob(const std::string&) { return false; } bool VersionBlobHandler::stat(const std::string&, blobs::BlobMeta*) { return false; } bool VersionBlobHandler::open(uint16_t session, uint16_t flags, const std::string& path) { /* only reads are supported, check if blob is handled and make sure * the blob isn't already opened */ if (flags != blobs::read) { fprintf(stderr, "open %s fail: unsupported flags(0x%04X.)\n", path.c_str(), flags); return false; } auto info = std::make_unique(); info->blob = blobInfoMap.at(path).get(); info->blob->sessionsToUpdate.emplace(info.get()); if (info->blob->sessionsToUpdate.size() == 1 && !info->blob->actions->onOpen->trigger()) { fprintf(stderr, "open %s fail: onOpen trigger failed\n", path.c_str()); info->blob->sessionsToUpdate.erase(info.get()); return false; } sessionInfoMap[session] = std::move(info); return true; } std::vector VersionBlobHandler::read(uint16_t session, uint32_t offset, uint32_t requestedSize) { auto& data = sessionInfoMap.at(session)->data; if (data == nullptr || !*data) { throw std::runtime_error("Version data not ready for read"); } if ((*data)->size() < offset) { return {}; } std::vector ret( std::min(requestedSize, (*data)->size() - offset)); std::memcpy(&ret[0], &(**data)[offset], ret.size()); return ret; } bool VersionBlobHandler::close(uint16_t session) { auto it = sessionInfoMap.find(session); if (it == sessionInfoMap.end()) { return false; } auto& info = *it->second; info.blob->sessionsToUpdate.erase(&info); if (info.blob->sessionsToUpdate.empty()) { info.blob->actions->onOpen->abort(); } sessionInfoMap.erase(it); return true; } bool VersionBlobHandler::stat(uint16_t session, blobs::BlobMeta* meta) { const auto& data = sessionInfoMap.at(session)->data; if (data == nullptr) { meta->blobState = blobs::StateFlags::committing; meta->size = 0; } else if (!*data) { meta->blobState = blobs::StateFlags::commit_error; meta->size = 0; } else { meta->blobState = blobs::StateFlags::committed | blobs::StateFlags::open_read; meta->size = (*data)->size(); } return true; } bool VersionBlobHandler::expire(uint16_t session) { close(session); return true; } } // namespace ipmi_flash