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