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