xref: /openbmc/phosphor-ipmi-blobs/manager.cpp (revision c892f4a0a863ff982b108d1a73f65dacb2cfbdac)
1 /*
2  * Copyright 2018 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "manager.hpp"
18 
19 #include <algorithm>
20 #include <memory>
21 #include <string>
22 #include <vector>
23 
24 namespace blobs
25 {
26 
27 void BlobManager::incrementOpen(const std::string& path)
28 {
29     if (path.empty())
30     {
31         return;
32     }
33 
34     openFiles[path] += 1;
35 }
36 
37 void BlobManager::decrementOpen(const std::string& path)
38 {
39     if (path.empty())
40     {
41         return;
42     }
43 
44     /* TODO(venture): Check into the iterator from find, does it makes sense
45      * to just update it directly? */
46     auto entry = openFiles.find(path);
47     if (entry != openFiles.end())
48     {
49         /* found it, decrement it and remove it if 0. */
50         openFiles[path] -= 1;
51         if (openFiles[path] == 0)
52         {
53             openFiles.erase(path);
54         }
55     }
56 }
57 
58 int BlobManager::getOpen(const std::string& path) const
59 {
60     /* No need to input check on the read-only call. */
61     auto entry = openFiles.find(path);
62     if (entry != openFiles.end())
63     {
64         return entry->second;
65     }
66 
67     return 0;
68 }
69 
70 bool BlobManager::registerHandler(std::unique_ptr<GenericBlobInterface> handler)
71 {
72     if (!handler)
73     {
74         return false;
75     }
76 
77     handlers.push_back(std::move(handler));
78     return true;
79 }
80 
81 uint32_t BlobManager::buildBlobList()
82 {
83     /* Clear out the current list (IPMI handler is presently single-threaded).
84      */
85     ids.clear();
86 
87     /* Grab the list of blobs and extend the local list */
88     for (const auto& h : handlers)
89     {
90         std::vector<std::string> blobs = h->getBlobIds();
91         ids.insert(ids.end(), blobs.begin(), blobs.end());
92     }
93 
94     return ids.size();
95 }
96 
97 std::string BlobManager::getBlobId(uint32_t index)
98 {
99     /* Range check. */
100     if (index >= ids.size())
101     {
102         return "";
103     }
104 
105     return ids[index];
106 }
107 
108 bool BlobManager::open(uint16_t flags, const std::string& path,
109                        uint16_t* session)
110 {
111     GenericBlobInterface* handler = getHandler(path);
112 
113     /* No handler found. */
114     if (!handler)
115     {
116         return false;
117     }
118 
119     /* No sessions available... */
120     if (!getSession(session))
121     {
122         return false;
123     }
124 
125     /* Verify flags - must be at least read or write */
126     if (!(flags & (OpenFlags::read | OpenFlags::write)))
127     {
128         /* Neither read not write set, which means calls to Read/Write will
129          * reject. */
130         return false;
131     }
132 
133     if (!handler->open(*session, flags, path))
134     {
135         return false;
136     }
137 
138     /* Associate session with handler */
139     sessions[*session] = SessionInfo(path, handler, flags);
140     incrementOpen(path);
141     return true;
142 }
143 
144 GenericBlobInterface* BlobManager::getHandler(const std::string& path)
145 {
146     /* Find a handler. */
147     auto h = std::find_if(
148         handlers.begin(), handlers.end(),
149         [&path](const auto& iter) { return (iter->canHandleBlob(path)); });
150     if (h != handlers.end())
151     {
152         return h->get();
153     }
154 
155     return nullptr;
156 }
157 
158 std::string BlobManager::getPath(uint16_t session) const
159 {
160     auto item = sessions.find(session);
161     if (item == sessions.end())
162     {
163         return "";
164     }
165 
166     return item->second.blobId;
167 }
168 
169 bool BlobManager::stat(const std::string& path, BlobMeta* meta)
170 {
171     /* meta should never be NULL. */
172     GenericBlobInterface* handler = getHandler(path);
173 
174     /* No handler found. */
175     if (!handler)
176     {
177         return false;
178     }
179 
180     return handler->stat(path, meta);
181 }
182 
183 bool BlobManager::stat(uint16_t session, BlobMeta* meta)
184 {
185     if (auto handler = getActionHandle(session))
186     {
187         return handler->stat(session, meta);
188     }
189     return false;
190 }
191 
192 bool BlobManager::commit(uint16_t session, const std::vector<uint8_t>& data)
193 {
194     if (auto handler = getActionHandle(session))
195     {
196         return handler->commit(session, data);
197     }
198     return false;
199 }
200 
201 bool BlobManager::close(uint16_t session)
202 {
203     if (auto handler = getActionHandle(session))
204     {
205         if (!handler->close(session))
206         {
207             return false;
208         }
209         sessions.erase(session);
210         decrementOpen(getPath(session));
211         return true;
212     }
213     return false;
214 }
215 
216 std::vector<uint8_t> BlobManager::read(uint16_t session, uint32_t offset,
217                                        uint32_t requestedSize)
218 {
219     /* TODO: Currently, configure_ac isn't finding libuserlayer, w.r.t the
220      * symbols I need.
221      */
222 
223     /** The channel to use for now.
224      * TODO: We will receive this information through the IPMI message call.
225      */
226     // const int ipmiChannel = ipmi::currentChNum;
227     /** This information is transport specific.
228      * TODO: We need a way to know this dynamically.
229      * on BT, 4 bytes of header, and 1 reply code.
230      */
231     // uint32_t maxTransportSize = ipmi::getChannelMaxTransferSize(ipmiChannel);
232 
233     if (auto handler = getActionHandle(session, OpenFlags::read))
234     {
235         return handler->read(session, offset,
236                              std::min(maximumReadSize, requestedSize));
237     }
238     return {};
239 }
240 
241 bool BlobManager::write(uint16_t session, uint32_t offset,
242                         const std::vector<uint8_t>& data)
243 {
244     if (auto handler = getActionHandle(session, OpenFlags::write))
245     {
246         return handler->write(session, offset, data);
247     }
248     return {};
249 }
250 
251 bool BlobManager::deleteBlob(const std::string& path)
252 {
253     GenericBlobInterface* handler = getHandler(path);
254 
255     /* No handler found. */
256     if (!handler)
257     {
258         return false;
259     }
260 
261     /* Check if the file has any open handles. */
262     if (getOpen(path) > 0)
263     {
264         return false;
265     }
266 
267     /* Try deleting it. */
268     return handler->deleteBlob(path);
269 }
270 
271 bool BlobManager::writeMeta(uint16_t session, uint32_t offset,
272                             const std::vector<uint8_t>& data)
273 {
274     if (auto handler = getActionHandle(session))
275     {
276         return handler->writeMeta(session, offset, data);
277     }
278     return false;
279 }
280 
281 bool BlobManager::getSession(uint16_t* sess)
282 {
283     uint16_t tries = 0;
284 
285     if (!sess)
286     {
287         return false;
288     }
289 
290     /* This is not meant to fail as you have 64KiB values available. */
291 
292     /* TODO(venture): We could just count the keys in the session map to know
293      * if it's full.
294      */
295     do
296     {
297         uint16_t lsess = next++;
298         if (!sessions.count(lsess))
299         {
300             /* value not in use, return it. */
301             (*sess) = lsess;
302             return true;
303         }
304     } while (++tries < 0xffff);
305 
306     return false;
307 }
308 
309 static std::unique_ptr<BlobManager> manager;
310 
311 ManagerInterface* getBlobManager()
312 {
313     if (manager == nullptr)
314     {
315         manager = std::make_unique<BlobManager>();
316     }
317 
318     return manager.get();
319 }
320 
321 } // namespace blobs
322