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