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