1 #include "version_handler.hpp"
2
3 #include <algorithm>
4 #include <cstring>
5 #include <ios>
6 #include <limits>
7 #include <memory>
8 #include <optional>
9 #include <utility>
10 #include <vector>
11
12 namespace ipmi_flash
13 {
14
VersionBlobHandler(std::vector<HandlerConfig<ActionPack>> && configs)15 VersionBlobHandler::VersionBlobHandler(
16 std::vector<HandlerConfig<ActionPack>>&& configs)
17 {
18 for (auto& config : configs)
19 {
20 auto info = std::make_unique<BlobInfo>();
21 info->blobId = std::move(config.blobId);
22 info->actions = std::move(config.actions);
23 info->handler = std::move(config.handler);
24 info->actions->onOpen->setCallback(
25 [infoP = info.get()](TriggerableActionInterface& tai) {
26 auto data =
27 std::make_shared<std::optional<std::vector<uint8_t>>>();
28 do
29 {
30 if (tai.status() != ActionStatus::success)
31 {
32 fprintf(stderr, "Version file unit failed for %s\n",
33 infoP->blobId.c_str());
34 continue;
35 }
36 if (!infoP->handler->open("", std::ios::in))
37 {
38 fprintf(stderr, "Opening version file failed for %s\n",
39 infoP->blobId.c_str());
40 continue;
41 }
42 auto d = infoP->handler->read(
43 0, std::numeric_limits<uint32_t>::max());
44 infoP->handler->close();
45 if (!d)
46 {
47 fprintf(stderr, "Reading version file failed for %s\n",
48 infoP->blobId.c_str());
49 continue;
50 }
51 *data = std::move(d);
52 } while (false);
53 for (auto sessionP : infoP->sessionsToUpdate)
54 {
55 sessionP->data = data;
56 }
57 infoP->sessionsToUpdate.clear();
58 });
59 if (!blobInfoMap.try_emplace(info->blobId, std::move(info)).second)
60 {
61 fprintf(stderr, "Ignoring duplicate config for %s\n",
62 info->blobId.c_str());
63 }
64 }
65 }
66
canHandleBlob(const std::string & path)67 bool VersionBlobHandler::canHandleBlob(const std::string& path)
68 {
69 return blobInfoMap.find(path) != blobInfoMap.end();
70 }
71
getBlobIds()72 std::vector<std::string> VersionBlobHandler::getBlobIds()
73 {
74 std::vector<std::string> ret;
75 for (const auto& [key, _] : blobInfoMap)
76 {
77 ret.emplace_back(key);
78 }
79 return ret;
80 }
81
82 /**
83 * deleteBlob - does nothing, always fails
84 */
deleteBlob(const std::string &)85 bool VersionBlobHandler::deleteBlob(const std::string&)
86 {
87 return false;
88 }
89
stat(const std::string &,blobs::BlobMeta *)90 bool VersionBlobHandler::stat(const std::string&, blobs::BlobMeta*)
91 {
92 return false;
93 }
94
open(uint16_t session,uint16_t flags,const std::string & path)95 bool VersionBlobHandler::open(uint16_t session, uint16_t flags,
96 const std::string& path)
97 {
98 /* only reads are supported, check if blob is handled and make sure
99 * the blob isn't already opened
100 */
101 if (flags != blobs::read)
102 {
103 fprintf(stderr, "open %s fail: unsupported flags(0x%04X.)\n",
104 path.c_str(), flags);
105 return false;
106 }
107
108 auto info = std::make_unique<SessionInfo>();
109 info->blob = blobInfoMap.at(path).get();
110 info->blob->sessionsToUpdate.emplace(info.get());
111 if (info->blob->sessionsToUpdate.size() == 1 &&
112 !info->blob->actions->onOpen->trigger())
113 {
114 fprintf(stderr, "open %s fail: onOpen trigger failed\n", path.c_str());
115 info->blob->sessionsToUpdate.erase(info.get());
116 return false;
117 }
118
119 sessionInfoMap[session] = std::move(info);
120 return true;
121 }
122
read(uint16_t session,uint32_t offset,uint32_t requestedSize)123 std::vector<uint8_t> VersionBlobHandler::read(uint16_t session, uint32_t offset,
124 uint32_t requestedSize)
125 {
126 auto& data = sessionInfoMap.at(session)->data;
127 if (data == nullptr || !*data)
128 {
129 throw std::runtime_error("Version data not ready for read");
130 }
131 if ((*data)->size() < offset)
132 {
133 return {};
134 }
135 std::vector<uint8_t> ret(
136 std::min<size_t>(requestedSize, (*data)->size() - offset));
137 std::memcpy(&ret[0], &(**data)[offset], ret.size());
138 return ret;
139 }
140
close(uint16_t session)141 bool VersionBlobHandler::close(uint16_t session)
142 {
143 auto it = sessionInfoMap.find(session);
144 if (it == sessionInfoMap.end())
145 {
146 return false;
147 }
148 auto& info = *it->second;
149 info.blob->sessionsToUpdate.erase(&info);
150 if (info.blob->sessionsToUpdate.empty())
151 {
152 info.blob->actions->onOpen->abort();
153 }
154 sessionInfoMap.erase(it);
155 return true;
156 }
157
stat(uint16_t session,blobs::BlobMeta * meta)158 bool VersionBlobHandler::stat(uint16_t session, blobs::BlobMeta* meta)
159 {
160 const auto& data = sessionInfoMap.at(session)->data;
161 if (data == nullptr)
162 {
163 meta->blobState = blobs::StateFlags::committing;
164 meta->size = 0;
165 }
166 else if (!*data)
167 {
168 meta->blobState = blobs::StateFlags::commit_error;
169 meta->size = 0;
170 }
171 else
172 {
173 meta->blobState = blobs::StateFlags::committed |
174 blobs::StateFlags::open_read;
175 meta->size = (*data)->size();
176 }
177 return true;
178 }
179
expire(uint16_t session)180 bool VersionBlobHandler::expire(uint16_t session)
181 {
182 close(session);
183 return true;
184 }
185
186 } // namespace ipmi_flash
187