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 
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 
67 bool VersionBlobHandler::canHandleBlob(const std::string& path)
68 {
69     return blobInfoMap.find(path) != blobInfoMap.end();
70 }
71 
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  */
85 bool VersionBlobHandler::deleteBlob(const std::string& path)
86 {
87     return false;
88 }
89 
90 bool VersionBlobHandler::stat(const std::string& path, blobs::BlobMeta* meta)
91 {
92     return false;
93 }
94 
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 
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 
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 
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 =
174             blobs::StateFlags::committed | blobs::StateFlags::open_read;
175         meta->size = (*data)->size();
176     }
177     return true;
178 }
179 
180 bool VersionBlobHandler::expire(uint16_t session)
181 {
182     close(session);
183     return true;
184 }
185 
186 } // namespace ipmi_flash
187