xref: /openbmc/phosphor-ipmi-blobs/manager.cpp (revision 8bc117792fbf118dd74d015546c22612961ccc26)
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 GenericBlobInterface* BlobManager::getHandler(uint16_t session)
159 {
160     auto item = sessions.find(session);
161     if (item == sessions.end())
162     {
163         return nullptr;
164     }
165 
166     return item->second.handler;
167 }
168 
169 SessionInfo* BlobManager::getSessionInfo(uint16_t session)
170 {
171     auto item = sessions.find(session);
172     if (item == sessions.end())
173     {
174         return nullptr;
175     }
176 
177     /* If we go to multi-threaded, this pointer can be invalidated and this
178      * method will need to change.
179      */
180     return &item->second;
181 }
182 
183 std::string BlobManager::getPath(uint16_t session) const
184 {
185     auto item = sessions.find(session);
186     if (item == sessions.end())
187     {
188         return "";
189     }
190 
191     return item->second.blobId;
192 }
193 
194 bool BlobManager::stat(const std::string& path, BlobMeta* meta)
195 {
196     /* meta should never be NULL. */
197     GenericBlobInterface* handler = getHandler(path);
198 
199     /* No handler found. */
200     if (!handler)
201     {
202         return false;
203     }
204 
205     return handler->stat(path, meta);
206 }
207 
208 bool BlobManager::stat(uint16_t session, BlobMeta* meta)
209 {
210     /* meta should never be NULL. */
211     GenericBlobInterface* handler = getHandler(session);
212 
213     /* No handler found. */
214     if (!handler)
215     {
216         return false;
217     }
218 
219     return handler->stat(session, meta);
220 }
221 
222 bool BlobManager::commit(uint16_t session, const std::vector<uint8_t>& data)
223 {
224     GenericBlobInterface* handler = getHandler(session);
225 
226     /* No handler found. */
227     if (!handler)
228     {
229         return false;
230     }
231 
232     return handler->commit(session, data);
233 }
234 
235 bool BlobManager::close(uint16_t session)
236 {
237     GenericBlobInterface* handler = getHandler(session);
238 
239     /* No handler found. */
240     if (!handler)
241     {
242         return false;
243     }
244 
245     /* Handler returns failure */
246     if (!handler->close(session))
247     {
248         return false;
249     }
250 
251     sessions.erase(session);
252     decrementOpen(getPath(session));
253     return true;
254 }
255 
256 std::vector<uint8_t> BlobManager::read(uint16_t session, uint32_t offset,
257                                        uint32_t requestedSize)
258 {
259     SessionInfo* info = getSessionInfo(session);
260 
261     /* No session found. */
262     if (!info)
263     {
264         return std::vector<uint8_t>();
265     }
266 
267     /* Check flags. */
268     if (!(info->flags & OpenFlags::read))
269     {
270         return std::vector<uint8_t>();
271     }
272 
273     /* TODO: Currently, configure_ac isn't finding libuserlayer, w.r.t the
274      * symbols I need.
275      */
276 
277     /** The channel to use for now.
278      * TODO: We will receive this information through the IPMI message call.
279      */
280     // const int ipmiChannel = ipmi::currentChNum;
281     /** This information is transport specific.
282      * TODO: We need a way to know this dynamically.
283      * on BT, 4 bytes of header, and 1 reply code.
284      */
285     // uint32_t maxTransportSize = ipmi::getChannelMaxTransferSize(ipmiChannel);
286 
287     /* Try reading from it. */
288     return info->handler->read(session, offset,
289                                std::min(maximumReadSize, requestedSize));
290 }
291 
292 bool BlobManager::write(uint16_t session, uint32_t offset,
293                         const std::vector<uint8_t>& data)
294 {
295     SessionInfo* info = getSessionInfo(session);
296 
297     /* No session found. */
298     if (!info)
299     {
300         return false;
301     }
302 
303     /* Check flags. */
304     if (!(info->flags & OpenFlags::write))
305     {
306         return false;
307     }
308 
309     /* Try writing to it. */
310     return info->handler->write(session, offset, data);
311 }
312 
313 bool BlobManager::deleteBlob(const std::string& path)
314 {
315     GenericBlobInterface* handler = getHandler(path);
316 
317     /* No handler found. */
318     if (!handler)
319     {
320         return false;
321     }
322 
323     /* Check if the file has any open handles. */
324     if (getOpen(path) > 0)
325     {
326         return false;
327     }
328 
329     /* Try deleting it. */
330     return handler->deleteBlob(path);
331 }
332 
333 bool BlobManager::writeMeta(uint16_t session, uint32_t offset,
334                             const std::vector<uint8_t>& data)
335 {
336     SessionInfo* info = getSessionInfo(session);
337 
338     /* No session found. */
339     if (!info)
340     {
341         return false;
342     }
343 
344     /* Try writing metadata to it. */
345     return info->handler->writeMeta(session, offset, data);
346 }
347 
348 bool BlobManager::getSession(uint16_t* sess)
349 {
350     uint16_t tries = 0;
351 
352     if (!sess)
353     {
354         return false;
355     }
356 
357     /* This is not meant to fail as you have 64KiB values available. */
358 
359     /* TODO(venture): We could just count the keys in the session map to know
360      * if it's full.
361      */
362     do
363     {
364         uint16_t lsess = next++;
365         if (!sessions.count(lsess))
366         {
367             /* value not in use, return it. */
368             (*sess) = lsess;
369             return true;
370         }
371     } while (++tries < 0xffff);
372 
373     return false;
374 }
375 
376 static std::unique_ptr<BlobManager> manager;
377 
378 ManagerInterface* getBlobManager()
379 {
380     if (manager == nullptr)
381     {
382         manager = std::make_unique<BlobManager>();
383     }
384 
385     return manager.get();
386 }
387 
388 } // namespace blobs
389