blob: adabe8458fb144d06468560268ffefd3e0a3faad [file] [log] [blame]
/*
* Copyright 2018 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <algorithm>
#include <blobs-ipmid/manager.hpp>
#include <memory>
#include <string>
#include <vector>
namespace blobs
{
void BlobManager::incrementOpen(const std::string& path)
{
if (path.empty())
{
return;
}
openFiles[path] += 1;
}
void BlobManager::decrementOpen(const std::string& path)
{
if (path.empty())
{
return;
}
/* TODO(venture): Check into the iterator from find, does it makes sense
* to just update it directly? */
auto entry = openFiles.find(path);
if (entry != openFiles.end())
{
/* found it, decrement it and remove it if 0. */
openFiles[path] -= 1;
if (openFiles[path] == 0)
{
openFiles.erase(path);
}
}
}
int BlobManager::getOpen(const std::string& path) const
{
/* No need to input check on the read-only call. */
auto entry = openFiles.find(path);
if (entry != openFiles.end())
{
return entry->second;
}
return 0;
}
bool BlobManager::registerHandler(std::unique_ptr<GenericBlobInterface> handler)
{
if (!handler)
{
return false;
}
handlers.push_back(std::move(handler));
return true;
}
uint32_t BlobManager::buildBlobList()
{
/* Clear out the current list (IPMI handler is presently single-threaded).
*/
ids.clear();
/* Grab the list of blobs and extend the local list */
for (const auto& h : handlers)
{
std::vector<std::string> blobs = h->getBlobIds();
ids.insert(ids.end(), blobs.begin(), blobs.end());
}
return ids.size();
}
std::string BlobManager::getBlobId(uint32_t index)
{
/* Range check. */
if (index >= ids.size())
{
return "";
}
return ids[index];
}
bool BlobManager::open(uint16_t flags, const std::string& path,
uint16_t* session)
{
GenericBlobInterface* handler = getHandler(path);
/* No handler found. */
if (!handler)
{
return false;
}
/* No sessions availabe... */
if (!getSession(session))
{
return false;
}
/* Verify flags - must be at least read or write */
if (!(flags & (OpenFlags::read | OpenFlags::write)))
{
/* Neither read not write set, which means calls to Read/Write will
* reject. */
return false;
}
if (!handler->open(*session, flags, path))
{
return false;
}
/* Associate session with handler */
sessions[*session] = SessionInfo(path, handler, flags);
incrementOpen(path);
return true;
}
GenericBlobInterface* BlobManager::getHandler(const std::string& path)
{
/* Find a handler. */
auto h = std::find_if(
handlers.begin(), handlers.end(),
[&path](const auto& iter) { return (iter->canHandleBlob(path)); });
if (h != handlers.end())
{
return h->get();
}
return nullptr;
}
GenericBlobInterface* BlobManager::getHandler(uint16_t session)
{
auto item = sessions.find(session);
if (item == sessions.end())
{
return nullptr;
}
return item->second.handler;
}
SessionInfo* BlobManager::getSessionInfo(uint16_t session)
{
auto item = sessions.find(session);
if (item == sessions.end())
{
return nullptr;
}
/* If we go to multi-threaded, this pointer can be invalidated and this
* method will need to change.
*/
return &item->second;
}
std::string BlobManager::getPath(uint16_t session) const
{
auto item = sessions.find(session);
if (item == sessions.end())
{
return "";
}
return item->second.blobId;
}
bool BlobManager::stat(const std::string& path, struct BlobMeta* meta)
{
/* meta should never be NULL. */
GenericBlobInterface* handler = getHandler(path);
/* No handler found. */
if (!handler)
{
return false;
}
return handler->stat(path, meta);
}
bool BlobManager::stat(uint16_t session, struct BlobMeta* meta)
{
/* meta should never be NULL. */
GenericBlobInterface* handler = getHandler(session);
/* No handler found. */
if (!handler)
{
return false;
}
return handler->stat(session, meta);
}
bool BlobManager::commit(uint16_t session, const std::vector<uint8_t>& data)
{
GenericBlobInterface* handler = getHandler(session);
/* No handler found. */
if (!handler)
{
return false;
}
return handler->commit(session, data);
}
bool BlobManager::close(uint16_t session)
{
GenericBlobInterface* handler = getHandler(session);
/* No handler found. */
if (!handler)
{
return false;
}
/* Handler returns failure */
if (!handler->close(session))
{
return false;
}
sessions.erase(session);
decrementOpen(getPath(session));
return true;
}
std::vector<uint8_t> BlobManager::read(uint16_t session, uint32_t offset,
uint32_t requestedSize)
{
SessionInfo* info = getSessionInfo(session);
/* No session found. */
if (!info)
{
return std::vector<uint8_t>();
}
/* Check flags. */
if (!(info->flags & OpenFlags::read))
{
return std::vector<uint8_t>();
}
/* Try reading from it. */
return info->handler->read(session, offset, requestedSize);
}
bool BlobManager::write(uint16_t session, uint32_t offset,
const std::vector<uint8_t>& data)
{
SessionInfo* info = getSessionInfo(session);
/* No session found. */
if (!info)
{
return false;
}
/* Check flags. */
if (!(info->flags & OpenFlags::write))
{
return false;
}
/* Try writing to it. */
return info->handler->write(session, offset, data);
}
bool BlobManager::deleteBlob(const std::string& path)
{
GenericBlobInterface* handler = getHandler(path);
/* No handler found. */
if (!handler)
{
return false;
}
/* Check if the file has any open handles. */
if (getOpen(path) > 0)
{
return false;
}
/* Try deleting it. */
return handler->deleteBlob(path);
}
bool BlobManager::writeMeta(uint16_t session, uint32_t offset,
const std::vector<uint8_t>& data)
{
SessionInfo* info = getSessionInfo(session);
/* No session found. */
if (!info)
{
return false;
}
/* Try writing metadata to it. */
return info->handler->writeMeta(session, offset, data);
}
bool BlobManager::getSession(uint16_t* sess)
{
uint16_t tries = 0;
if (!sess)
{
return false;
}
/* This is not meant to fail as you have 64KiB values available. */
/* TODO(venture): We could just count the keys in the session map to know
* if it's full.
*/
do
{
uint16_t lsess = next++;
if (!sessions.count(lsess))
{
/* value not in use, return it. */
(*sess) = lsess;
return true;
}
} while (++tries < 0xffff);
return false;
}
static std::unique_ptr<BlobManager> manager;
ManagerInterface* getBlobManager()
{
if (manager == nullptr)
{
manager = std::make_unique<BlobManager>();
}
return manager.get();
}
} // namespace blobs