blob: 7cccf62379aa555f266900f4e3c13eb7ad9c5fa0 [file] [log] [blame]
#include "version_handler.hpp"
#include <utility>
#include <vector>
namespace ipmi_flash
{
VersionBlobHandler::VersionBlobHandler(
std::vector<HandlerConfig<ActionPack>>&& configs)
{
for (auto& config : configs)
{
auto info = std::make_unique<BlobInfo>();
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<std::optional<std::vector<uint8_t>>>();
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<uint32_t>::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<std::string> VersionBlobHandler::getBlobIds()
{
std::vector<std::string> ret;
for (const auto& [key, _] : blobInfoMap)
{
ret.emplace_back(key);
}
return ret;
}
/**
* deleteBlob - does nothing, always fails
*/
bool VersionBlobHandler::deleteBlob(const std::string& path)
{
return false;
}
bool VersionBlobHandler::stat(const std::string& path, blobs::BlobMeta* meta)
{
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<SessionInfo>();
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<uint8_t> 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<uint8_t> ret(
std::min<size_t>(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