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 <string>
19 #include <vector>
20 
21 namespace blobs
22 {
23 
24 void BlobManager::incrementOpen(const std::string& path)
25 {
26     if (path.empty())
27     {
28         return;
29     }
30 
31     openFiles[path] += 1;
32 }
33 
34 void BlobManager::decrementOpen(const std::string& path)
35 {
36     if (path.empty())
37     {
38         return;
39     }
40 
41     /* TODO(venture): Check into the iterator from find, does it makes sense
42      * to just update it directly? */
43     auto entry = openFiles.find(path);
44     if (entry != openFiles.end())
45     {
46         /* found it, decrement it and remove it if 0. */
47         openFiles[path] -= 1;
48         if (openFiles[path] == 0)
49         {
50             openFiles.erase(path);
51         }
52     }
53 }
54 
55 int BlobManager::getOpen(const std::string& path) const
56 {
57     /* No need to input check on the read-only call. */
58     auto entry = openFiles.find(path);
59     if (entry != openFiles.end())
60     {
61         return entry->second;
62     }
63 
64     return 0;
65 }
66 
67 bool BlobManager::registerHandler(std::unique_ptr<GenericBlobInterface> handler)
68 {
69     if (!handler)
70     {
71         return false;
72     }
73 
74     handlers.push_back(std::move(handler));
75     return true;
76 }
77 
78 uint32_t BlobManager::buildBlobList()
79 {
80     /* Clear out the current list (IPMI handler is presently single-threaded).
81      */
82     ids.clear();
83 
84     /* Grab the list of blobs and extend the local list */
85     for (auto& h : handlers)
86     {
87         std::vector<std::string> blobs = h->getBlobIds();
88         ids.insert(ids.end(), blobs.begin(), blobs.end());
89     }
90 
91     return ids.size();
92 }
93 
94 std::string BlobManager::getBlobId(uint32_t index)
95 {
96     /* Range check. */
97     if (index >= ids.size())
98     {
99         return "";
100     }
101 
102     return ids[index];
103 }
104 
105 bool BlobManager::open(uint16_t flags, const std::string& path,
106                        uint16_t* session)
107 {
108     GenericBlobInterface* handler = getHandler(path);
109 
110     /* No handler found. */
111     if (!handler)
112     {
113         return false;
114     }
115 
116     /* No sessions availabe... */
117     if (!getSession(session))
118     {
119         return false;
120     }
121 
122     /* Verify flags - must be at least read or write */
123     if (!(flags & (OpenFlags::read | OpenFlags::write)))
124     {
125         /* Neither read not write set, which means calls to Read/Write will
126          * reject. */
127         return false;
128     }
129 
130     if (!handler->open(*session, flags, path))
131     {
132         return false;
133     }
134 
135     /* Associate session with handler */
136     sessions[*session] = SessionInfo(path, handler, flags);
137     incrementOpen(path);
138     return true;
139 }
140 
141 GenericBlobInterface* BlobManager::getHandler(const std::string& path)
142 {
143     /* Find a handler. */
144     GenericBlobInterface* handler = nullptr;
145 
146     for (auto& h : handlers)
147     {
148         if (h->canHandleBlob(path))
149         {
150             handler = h.get();
151             break;
152         }
153     }
154 
155     return handler;
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::getSession(uint16_t* sess)
319 {
320     uint16_t tries = 0;
321     uint16_t lsess;
322 
323     if (!sess)
324     {
325         return false;
326     }
327 
328     /* This is not meant to fail as you have 64KiB values available. */
329 
330     /* TODO(venture): We could just count the keys in the session map to know
331      * if it's full.
332      */
333     do
334     {
335         lsess = next++;
336         if (!sessions.count(lsess))
337         {
338             /* value not in use, return it. */
339             (*sess) = lsess;
340             return true;
341         }
342     } while (++tries < 0xffff);
343 
344     return false;
345 }
346 } // namespace blobs
347