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