xref: /openbmc/phosphor-ipmi-blobs/manager.cpp (revision cd8dab491d3f78124be800252186a32a90c884b8)
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, struct 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, struct 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     /* Try reading from it. */
274     return info->handler->read(session, offset, requestedSize);
275 }
276 
277 bool BlobManager::write(uint16_t session, uint32_t offset,
278                         const std::vector<uint8_t>& data)
279 {
280     SessionInfo* info = getSessionInfo(session);
281 
282     /* No session found. */
283     if (!info)
284     {
285         return false;
286     }
287 
288     /* Check flags. */
289     if (!(info->flags & OpenFlags::write))
290     {
291         return false;
292     }
293 
294     /* Try writing to it. */
295     return info->handler->write(session, offset, data);
296 }
297 
298 bool BlobManager::deleteBlob(const std::string& path)
299 {
300     GenericBlobInterface* handler = getHandler(path);
301 
302     /* No handler found. */
303     if (!handler)
304     {
305         return false;
306     }
307 
308     /* Check if the file has any open handles. */
309     if (getOpen(path) > 0)
310     {
311         return false;
312     }
313 
314     /* Try deleting it. */
315     return handler->deleteBlob(path);
316 }
317 
318 bool BlobManager::writeMeta(uint16_t session, uint32_t offset,
319                             const std::vector<uint8_t>& data)
320 {
321     SessionInfo* info = getSessionInfo(session);
322 
323     /* No session found. */
324     if (!info)
325     {
326         return false;
327     }
328 
329     /* Try writing metadata to it. */
330     return info->handler->writeMeta(session, offset, data);
331 }
332 
333 bool BlobManager::getSession(uint16_t* sess)
334 {
335     uint16_t tries = 0;
336 
337     if (!sess)
338     {
339         return false;
340     }
341 
342     /* This is not meant to fail as you have 64KiB values available. */
343 
344     /* TODO(venture): We could just count the keys in the session map to know
345      * if it's full.
346      */
347     do
348     {
349         uint16_t lsess = next++;
350         if (!sessions.count(lsess))
351         {
352             /* value not in use, return it. */
353             (*sess) = lsess;
354             return true;
355         }
356     } while (++tries < 0xffff);
357 
358     return false;
359 }
360 
361 static std::unique_ptr<BlobManager> manager;
362 
363 ManagerInterface* getBlobManager()
364 {
365     if (manager == nullptr)
366     {
367         manager = std::make_unique<BlobManager>();
368     }
369 
370     return manager.get();
371 }
372 
373 } // namespace blobs
374