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 <blobs-ipmid/manager.hpp>
18 #include <memory>
19 #include <string>
20 #include <vector>
21 
22 namespace blobs
23 {
24 
25 void BlobManager::incrementOpen(const std::string& path)
26 {
27     if (path.empty())
28     {
29         return;
30     }
31 
32     openFiles[path] += 1;
33 }
34 
35 void BlobManager::decrementOpen(const std::string& path)
36 {
37     if (path.empty())
38     {
39         return;
40     }
41 
42     /* TODO(venture): Check into the iterator from find, does it makes sense
43      * to just update it directly? */
44     auto entry = openFiles.find(path);
45     if (entry != openFiles.end())
46     {
47         /* found it, decrement it and remove it if 0. */
48         openFiles[path] -= 1;
49         if (openFiles[path] == 0)
50         {
51             openFiles.erase(path);
52         }
53     }
54 }
55 
56 int BlobManager::getOpen(const std::string& path) const
57 {
58     /* No need to input check on the read-only call. */
59     auto entry = openFiles.find(path);
60     if (entry != openFiles.end())
61     {
62         return entry->second;
63     }
64 
65     return 0;
66 }
67 
68 bool BlobManager::registerHandler(std::unique_ptr<GenericBlobInterface> handler)
69 {
70     if (!handler)
71     {
72         return false;
73     }
74 
75     handlers.push_back(std::move(handler));
76     return true;
77 }
78 
79 uint32_t BlobManager::buildBlobList()
80 {
81     /* Clear out the current list (IPMI handler is presently single-threaded).
82      */
83     ids.clear();
84 
85     /* Grab the list of blobs and extend the local list */
86     for (auto& h : handlers)
87     {
88         std::vector<std::string> blobs = h->getBlobIds();
89         ids.insert(ids.end(), blobs.begin(), blobs.end());
90     }
91 
92     return ids.size();
93 }
94 
95 std::string BlobManager::getBlobId(uint32_t index)
96 {
97     /* Range check. */
98     if (index >= ids.size())
99     {
100         return "";
101     }
102 
103     return ids[index];
104 }
105 
106 bool BlobManager::open(uint16_t flags, const std::string& path,
107                        uint16_t* session)
108 {
109     GenericBlobInterface* handler = getHandler(path);
110 
111     /* No handler found. */
112     if (!handler)
113     {
114         return false;
115     }
116 
117     /* No sessions availabe... */
118     if (!getSession(session))
119     {
120         return false;
121     }
122 
123     /* Verify flags - must be at least read or write */
124     if (!(flags & (OpenFlags::read | OpenFlags::write)))
125     {
126         /* Neither read not write set, which means calls to Read/Write will
127          * reject. */
128         return false;
129     }
130 
131     if (!handler->open(*session, flags, path))
132     {
133         return false;
134     }
135 
136     /* Associate session with handler */
137     sessions[*session] = SessionInfo(path, handler, flags);
138     incrementOpen(path);
139     return true;
140 }
141 
142 GenericBlobInterface* BlobManager::getHandler(const std::string& path)
143 {
144     /* Find a handler. */
145     GenericBlobInterface* handler = nullptr;
146 
147     for (auto& h : handlers)
148     {
149         if (h->canHandleBlob(path))
150         {
151             handler = h.get();
152             break;
153         }
154     }
155 
156     return handler;
157 }
158 
159 GenericBlobInterface* BlobManager::getHandler(uint16_t session)
160 {
161     auto item = sessions.find(session);
162     if (item == sessions.end())
163     {
164         return nullptr;
165     }
166 
167     return item->second.handler;
168 }
169 
170 SessionInfo* BlobManager::getSessionInfo(uint16_t session)
171 {
172     auto item = sessions.find(session);
173     if (item == sessions.end())
174     {
175         return nullptr;
176     }
177 
178     /* If we go to multi-threaded, this pointer can be invalidated and this
179      * method will need to change.
180      */
181     return &item->second;
182 }
183 
184 std::string BlobManager::getPath(uint16_t session) const
185 {
186     auto item = sessions.find(session);
187     if (item == sessions.end())
188     {
189         return "";
190     }
191 
192     return item->second.blobId;
193 }
194 
195 bool BlobManager::stat(const std::string& path, struct BlobMeta* meta)
196 {
197     /* meta should never be NULL. */
198     GenericBlobInterface* handler = getHandler(path);
199 
200     /* No handler found. */
201     if (!handler)
202     {
203         return false;
204     }
205 
206     return handler->stat(path, meta);
207 }
208 
209 bool BlobManager::stat(uint16_t session, struct BlobMeta* meta)
210 {
211     /* meta should never be NULL. */
212     GenericBlobInterface* handler = getHandler(session);
213 
214     /* No handler found. */
215     if (!handler)
216     {
217         return false;
218     }
219 
220     return handler->stat(session, meta);
221 }
222 
223 bool BlobManager::commit(uint16_t session, const std::vector<uint8_t>& data)
224 {
225     GenericBlobInterface* handler = getHandler(session);
226 
227     /* No handler found. */
228     if (!handler)
229     {
230         return false;
231     }
232 
233     return handler->commit(session, data);
234 }
235 
236 bool BlobManager::close(uint16_t session)
237 {
238     GenericBlobInterface* handler = getHandler(session);
239 
240     /* No handler found. */
241     if (!handler)
242     {
243         return false;
244     }
245 
246     /* Handler returns failure */
247     if (!handler->close(session))
248     {
249         return false;
250     }
251 
252     sessions.erase(session);
253     decrementOpen(getPath(session));
254     return true;
255 }
256 
257 std::vector<uint8_t> BlobManager::read(uint16_t session, uint32_t offset,
258                                        uint32_t requestedSize)
259 {
260     SessionInfo* info = getSessionInfo(session);
261 
262     /* No session found. */
263     if (!info)
264     {
265         return std::vector<uint8_t>();
266     }
267 
268     /* Check flags. */
269     if (!(info->flags & OpenFlags::read))
270     {
271         return std::vector<uint8_t>();
272     }
273 
274     /* Try reading from it. */
275     return info->handler->read(session, offset, requestedSize);
276 }
277 
278 bool BlobManager::write(uint16_t session, uint32_t offset,
279                         const std::vector<uint8_t>& data)
280 {
281     SessionInfo* info = getSessionInfo(session);
282 
283     /* No session found. */
284     if (!info)
285     {
286         return false;
287     }
288 
289     /* Check flags. */
290     if (!(info->flags & OpenFlags::write))
291     {
292         return false;
293     }
294 
295     /* Try writing to it. */
296     return info->handler->write(session, offset, data);
297 }
298 
299 bool BlobManager::deleteBlob(const std::string& path)
300 {
301     GenericBlobInterface* handler = getHandler(path);
302 
303     /* No handler found. */
304     if (!handler)
305     {
306         return false;
307     }
308 
309     /* Check if the file has any open handles. */
310     if (getOpen(path) > 0)
311     {
312         return false;
313     }
314 
315     /* Try deleting it. */
316     return handler->deleteBlob(path);
317 }
318 
319 bool BlobManager::getSession(uint16_t* sess)
320 {
321     uint16_t tries = 0;
322     uint16_t lsess;
323 
324     if (!sess)
325     {
326         return false;
327     }
328 
329     /* This is not meant to fail as you have 64KiB values available. */
330 
331     /* TODO(venture): We could just count the keys in the session map to know
332      * if it's full.
333      */
334     do
335     {
336         lsess = next++;
337         if (!sessions.count(lsess))
338         {
339             /* value not in use, return it. */
340             (*sess) = lsess;
341             return true;
342         }
343     } while (++tries < 0xffff);
344 
345     return false;
346 }
347 
348 static std::unique_ptr<BlobManager> manager;
349 
350 ManagerInterface* getBlobManager()
351 {
352     if (manager == nullptr)
353     {
354         manager = std::make_unique<BlobManager>();
355     }
356 
357     return manager.get();
358 }
359 
360 } // namespace blobs
361