1 #include "version_handler.hpp"
2 
3 #include <stdexcept>
4 #include <utility>
5 #include <vector>
6 
7 namespace ipmi_flash
8 {
9 
10 VersionBlobHandler::VersionBlobHandler(
11     std::vector<HandlerConfig<ActionPack>>&& configs)
12 {
13     for (auto& config : configs)
14     {
15         BlobInfo info = {std::move(config.blobId), std::move(config.actions),
16                          std::move(config.handler)};
17         if (!blobInfoMap.try_emplace(info.blobId, std::move(info)).second)
18         {
19             fprintf(stderr, "Ignoring duplicate config for %s\n",
20                     info.blobId.c_str());
21         }
22     }
23 }
24 
25 bool VersionBlobHandler::canHandleBlob(const std::string& path)
26 {
27     return blobInfoMap.find(path) != blobInfoMap.end();
28 }
29 
30 std::vector<std::string> VersionBlobHandler::getBlobIds()
31 {
32     std::vector<std::string> ret;
33     for (const auto& [key, _] : blobInfoMap)
34     {
35         ret.push_back(key);
36     }
37     return ret;
38 }
39 
40 /**
41  * deleteBlob - does nothing, always fails
42  */
43 bool VersionBlobHandler::deleteBlob(const std::string& path)
44 {
45     return false;
46 }
47 
48 bool VersionBlobHandler::stat(const std::string& path, blobs::BlobMeta* meta)
49 {
50     // TODO: stat should return the blob state and in the meta data information
51     // on whether a read is successful should be contained
52     // do things like determine if systemd target is triggered
53     // then check if file can be opened for read
54     return false; /* not yet implemented */
55 }
56 
57 bool VersionBlobHandler::open(uint16_t session, uint16_t flags,
58                               const std::string& path)
59 {
60     if (sessionToBlob.insert({session, path}).second == false)
61     {
62         fprintf(stderr, "open %s fail: session number %d assigned to %s\n",
63                 path.c_str(), session, sessionToBlob.at(session).c_str());
64         return false;
65     }
66     /* only reads are supported, check if blob is handled and make sure
67      * the blob isn't already opened
68      */
69     if (flags != blobs::read)
70     {
71         fprintf(stderr, "open %s fail: unsupported flags(0x%04X.)\n",
72                 path.c_str(), flags);
73         cleanup(session);
74         return false;
75     }
76 
77     try
78     {
79         auto& v = blobInfoMap.at(path);
80         if (v.blobState == blobs::StateFlags::open_read)
81         {
82             cleanup(session);
83             fprintf(stderr, "open %s fail: blob already opened for read\n",
84                     path.c_str());
85             return false;
86         }
87         if (v.actions->onOpen->trigger() == false)
88         {
89             fprintf(stderr, "open %s fail: onOpen trigger failed\n",
90                     path.c_str());
91             cleanup(session);
92             return false;
93         }
94         v.blobState = blobs::StateFlags::open_read;
95         return true;
96     }
97     catch (const std::out_of_range& e)
98     {
99         fprintf(stderr, "open %s fail, exception:%s\n", path.c_str(), e.what());
100         cleanup(session);
101         return false;
102     }
103 }
104 
105 std::vector<uint8_t> VersionBlobHandler::read(uint16_t session, uint32_t offset,
106                                               uint32_t requestedSize)
107 {
108     std::string* blobName;
109     BlobInfo* pack;
110     try
111     {
112         blobName = &sessionToBlob.at(session);
113         pack = &blobInfoMap.at(*blobName);
114     }
115     catch (const std::out_of_range& e)
116     {
117         return {};
118     }
119     /* onOpen trigger must be successful, otherwise potential
120      * for stale data to be read
121      */
122     if (pack->actions->onOpen->status() != ActionStatus::success)
123     {
124         fprintf(stderr, "read failed: onOpen trigger not successful\n");
125         return {};
126     }
127     if (!pack->handler->open("don't care", std::ios::in))
128     {
129         fprintf(stderr, "read failed: file open unsuccessful blob=%s\n",
130                 blobName->c_str());
131         return {};
132     }
133     auto d = pack->handler->read(offset, requestedSize);
134     if (!d)
135     {
136         fprintf(stderr, "read failed: unable to read file for blob %s\n",
137                 blobName->c_str());
138         pack->handler->close();
139         return {};
140     }
141     pack->handler->close();
142     return *d;
143 }
144 
145 bool VersionBlobHandler::close(uint16_t session)
146 {
147     return cleanup(session);
148 }
149 
150 bool VersionBlobHandler::stat(uint16_t session, blobs::BlobMeta* meta)
151 {
152     return false;
153 }
154 
155 bool VersionBlobHandler::expire(uint16_t session)
156 {
157     return cleanup(session);
158 }
159 
160 bool VersionBlobHandler::cleanup(uint16_t session)
161 {
162     try
163     {
164         const auto& blobName = sessionToBlob.at(session);
165         auto& pack = blobInfoMap.at(blobName);
166         if (pack.actions->onOpen->status() == ActionStatus::running)
167         {
168             pack.actions->onOpen->abort();
169         }
170         pack.blobState = static_cast<blobs::StateFlags>(0);
171         sessionToBlob.erase(session);
172         return true;
173     }
174     catch (const std::out_of_range& e)
175     {
176         return false;
177     }
178 }
179 } // namespace ipmi_flash
180