From 8fb5abcc550d28b562d5817251ceaca24ba7b08c Mon Sep 17 00:00:00 2001 From: Garrett Brown Date: Thu, 12 Jul 2018 14:31:05 -0700 Subject: [PATCH] Filesystem: Add IPFS implementation This adds an abstraction for a data store. LMDB is included as an implementation. A block store is built on the data store abstraction. Blocks are chunks data addressed by a CID (content identifier). --- cmake/treedata/common/subdirs.txt | 1 + xbmc/datastore/Block.cpp | 30 +++ xbmc/datastore/Block.h | 81 +++++++ xbmc/datastore/BlockStore.cpp | 63 +++++ xbmc/datastore/BlockStore.h | 76 ++++++ xbmc/datastore/CID.cpp | 31 +++ xbmc/datastore/CID.h | 142 +++++++++++ xbmc/datastore/CMakeLists.txt | 17 ++ xbmc/datastore/DataStore.cpp | 101 ++++++++ xbmc/datastore/DataStore.h | 46 ++++ xbmc/datastore/DataStoreLMDB.cpp | 388 ++++++++++++++++++++++++++++++ xbmc/datastore/DataStoreLMDB.h | 51 ++++ xbmc/datastore/IDataStore.cpp | 14 ++ xbmc/datastore/IDataStore.h | 110 +++++++++ xbmc/filesystem/CMakeLists.txt | 8 + xbmc/filesystem/File.cpp | 24 ++ xbmc/filesystem/File.h | 10 + xbmc/filesystem/IFile.h | 13 + xbmc/filesystem/IIPFS.cpp | 13 + xbmc/filesystem/IIPFS.h | 55 +++++ xbmc/filesystem/IPFS.cpp | 154 ++++++++++++ xbmc/filesystem/IPFS.h | 55 +++++ xbmc/filesystem/IPFSDirectory.cpp | 31 +++ xbmc/filesystem/IPFSDirectory.h | 30 +++ xbmc/filesystem/IPFSFile.cpp | 77 ++++++ xbmc/filesystem/IPFSFile.h | 37 +++ xbmc/profiles/ProfileManager.cpp | 8 + xbmc/profiles/ProfileManager.h | 1 + 28 files changed, 1667 insertions(+) create mode 100644 xbmc/datastore/Block.cpp create mode 100644 xbmc/datastore/Block.h create mode 100644 xbmc/datastore/BlockStore.cpp create mode 100644 xbmc/datastore/BlockStore.h create mode 100644 xbmc/datastore/CID.cpp create mode 100644 xbmc/datastore/CID.h create mode 100644 xbmc/datastore/CMakeLists.txt create mode 100644 xbmc/datastore/DataStore.cpp create mode 100644 xbmc/datastore/DataStore.h create mode 100644 xbmc/datastore/DataStoreLMDB.cpp create mode 100644 xbmc/datastore/DataStoreLMDB.h create mode 100644 xbmc/datastore/IDataStore.cpp create mode 100644 xbmc/datastore/IDataStore.h create mode 100644 xbmc/filesystem/IIPFS.cpp create mode 100644 xbmc/filesystem/IIPFS.h create mode 100644 xbmc/filesystem/IPFS.cpp create mode 100644 xbmc/filesystem/IPFS.h create mode 100644 xbmc/filesystem/IPFSDirectory.cpp create mode 100644 xbmc/filesystem/IPFSDirectory.h create mode 100644 xbmc/filesystem/IPFSFile.cpp create mode 100644 xbmc/filesystem/IPFSFile.h diff --git a/cmake/treedata/common/subdirs.txt b/cmake/treedata/common/subdirs.txt index 36953f93b5ba7..a670656672cd5 100644 --- a/cmake/treedata/common/subdirs.txt +++ b/cmake/treedata/common/subdirs.txt @@ -18,6 +18,7 @@ xbmc/crypto/did crypto/did xbmc/crypto/ed25519 crypto/ed25519 xbmc/crypto/multiformats crypto/multiformats xbmc/crypto/random crypto/random +xbmc/datastore datastore xbmc/dbwrappers dbwrappers xbmc/dialogs dialogs xbmc/favourites favourites diff --git a/xbmc/datastore/Block.cpp b/xbmc/datastore/Block.cpp new file mode 100644 index 0000000000000..f5dd320d54e4c --- /dev/null +++ b/xbmc/datastore/Block.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2018-2020 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "Block.h" + +using namespace KODI; +using namespace DATASTORE; + +CBlock::CBlock(CCID cid, std::vector data) : m_cid(std::move(cid)), m_data(std::move(data)) +{ +} + +std::vector CBlock::Serialize() const +{ + std::vector data; + + //! @todo + + return data; +} + +void CBlock::Deserialize(const std::vector& data) +{ + //! @todo +} diff --git a/xbmc/datastore/Block.h b/xbmc/datastore/Block.h new file mode 100644 index 0000000000000..82cbe09bf73c1 --- /dev/null +++ b/xbmc/datastore/Block.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2018-2020 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "CID.h" + +#include +#include +#include + +namespace KODI +{ +namespace DATASTORE +{ +/*! + * \brief Container of raw data with a CID identifier + */ +class CBlock +{ +public: + /*! + * \brief Construct an empty block + */ + CBlock() = default; + + /*! + * \brief Construct a block from parameters + * + * \param cid The block CID + * \param data The block data + */ + CBlock(CCID cid, std::vector data); + + /*! + * \brief Get the CID + */ + const CCID& CID() const { return m_cid; } + + /*! + * \brief Set the CID + */ + void SetCID(CCID cid) { m_cid = std::move(cid); } + + /*! + * \brief Get the data (const) + */ + const std::vector& Data() const { return m_data; } + + /*! + * \brief Get the data (mutable) + */ + std::vector& Data() { return m_data; } + + /*! + * \brief Set the data + */ + void SetData(std::vector data) { m_data = std::move(data); } + + /*! + * \brief Serialize as an array of bytes + */ + std::vector Serialize() const; + + /*! + * \brief Deserialize an array of bytes + */ + void Deserialize(const std::vector& data); + +private: + // Block parameters + CCID m_cid; + std::vector m_data; +}; +} // namespace DATASTORE +} // namespace KODI diff --git a/xbmc/datastore/BlockStore.cpp b/xbmc/datastore/BlockStore.cpp new file mode 100644 index 0000000000000..f1c40b14013e4 --- /dev/null +++ b/xbmc/datastore/BlockStore.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2018-2020 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "BlockStore.h" + +#include "Block.h" +#include "CID.h" +#include "DataStore.h" + +using namespace KODI; +using namespace DATASTORE; + +CBlockStore::CBlockStore(CDataStore& dataStore) : m_dataStore(dataStore) +{ +} + +bool CBlockStore::Has(const CCID& cid) +{ + std::vector key = cid.Serialize(); + + if (m_dataStore.Has(key.data(), key.size())) + return true; + + return false; +} + +bool CBlockStore::Get(const CCID& cid, CBlock& block) +{ + std::vector key = cid.Serialize(); + + if (m_dataStore.Get(key.data(), key.size(), block.Data())) + { + block.SetCID(cid); + return true; + } + + return false; +} + +bool CBlockStore::Put(const CBlock& block) +{ + std::vector key = block.CID().Serialize(); + + if (m_dataStore.Put(key.data(), key.size(), block.Data().data(), block.Data().size())) + return true; + + return false; +} + +bool CBlockStore::Delete(const CCID& cid) +{ + std::vector key = cid.Serialize(); + + if (m_dataStore.Delete(key.data(), key.size())) + return true; + + return false; +} diff --git a/xbmc/datastore/BlockStore.h b/xbmc/datastore/BlockStore.h new file mode 100644 index 0000000000000..d0b33d753b529 --- /dev/null +++ b/xbmc/datastore/BlockStore.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2018-2020 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +namespace KODI +{ +namespace DATASTORE +{ +class CBlock; +class CCID; +class CDataStore; + +/*! + * \brief Thin wrapper over a data store for getting and putting block + * objects + */ +class CBlockStore +{ +public: + /*! + * \brief Construct a block store + * + * \param dataStore The underlying data store + */ + CBlockStore(CDataStore& dataStore); + ~CBlockStore() = default; + + /*! + * \brief Determine if the CID can be found + * + * \param CID The CID to look for + * + * \return True if a block with the given CID was found, false otherwise + */ + bool Has(const CCID& cid); + + /*! + * \brief Find a block based on its CID + * + * \param cid The CID to look for + * \param block The block, or unmodified if this returns false + * + * \return True if the block was retrieved, false for missing CID or error + */ + bool Get(const CCID& cid, CBlock& block); + + /*! + * \brief Add a block to the block store, replacing any existing block + * + * \param block The block to store + * + * \return True if the block was added, false on error + */ + bool Put(const CBlock& block); + + /*! + * \brief Delete a block based on its CID + * + * \param CID The CID to look for + * + * \return True on success, false on error + */ + bool Delete(const CCID& cid); + +private: + // Construction parameters + CDataStore& m_dataStore; +}; +} // namespace DATASTORE +} // namespace KODI diff --git a/xbmc/datastore/CID.cpp b/xbmc/datastore/CID.cpp new file mode 100644 index 0000000000000..90d91adfbdeae --- /dev/null +++ b/xbmc/datastore/CID.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2018-2020 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "CID.h" + +using namespace KODI; +using namespace DATASTORE; + +CCID::CCID(CIDCodec codec, std::vector multihash) + : m_codec(codec), m_multihash(std::move(multihash)) +{ +} + +std::vector CCID::Serialize() const +{ + std::vector data; + + //! @todo + + return data; +} + +void CCID::Deserialize(const std::vector& data) +{ + //! @todo +} diff --git a/xbmc/datastore/CID.h b/xbmc/datastore/CID.h new file mode 100644 index 0000000000000..4136542164357 --- /dev/null +++ b/xbmc/datastore/CID.h @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2018-2020 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include +#include +#include + +namespace KODI +{ +namespace DATASTORE +{ +/*! + * \brief Multicodec packed content type + * + * Matches the codes described in the spec: + * + * https://github.com/multiformats/multicodec/blob/master/table.csv + */ +enum class CIDCodec +{ + // Codecs in the Multicodec table + RAW = 0x55, + DAG_JSON = 0x0129, // MerkleDAG json + + // Codecs for this application (consider upstreaming) + DAG_FLATBUFFER = 0x73, + DAG_JSON_LZ4 = 0x012a, // MerkleDAG json + lz4 +}; + +/*! + * \brief Implementation of the Content ID v1 spec for content-addressed + * identifiers + */ +class CCID +{ +public: + /*! + * \brief Construct an empty CID + */ + CCID() = default; + + /*! + * \brief Copy-construct a CID + * + * \param cid The CID being copied + */ + CCID(const CCID& cid) = default; + + /*! + * \brief Move-construct a CID + * + * Makes a copy of the object representation as if by std::memmove(). + * + * \param cid The CID being moved + */ + CCID(CCID&& cid) = default; + + /*! + * \brief Construct a CID from parameters + * + * \param codec The CID codec + * \param multihash The multihash + */ + CCID(CIDCodec codec, std::vector multihash); + + /*! + * \brief Construct a CID from a string + * + * \param codec The CID codec + * \param multihash The multihash + */ + //CCID(const std::vector& cid); + + /*! + * \brief Copy-assign a CID + * + * \param cid The CID being copied + */ + CCID& operator=(const CCID& cid) = default; + + /*! + * \brief Move-assign a CID + * + * Makes a copy of the object representation as if by std::memmove(). + * + * \param cid The CID being moved + */ + CCID& operator=(CCID&& cid) = default; + + bool operator==(const CCID& other) const; + + /*! + * \brief Get the CID codec + */ + CIDCodec Codec() const { return m_codec; } + + /*! + * \brief Set the CID codec + */ + void SetCodec(CIDCodec codec) { m_codec = codec; } + + /*! + * \brief Get the multihash + */ + const std::vector& Multihash() const { return m_multihash; } + + /*! + * \brief Set the multihash + */ + void SetMultihash(std::vector multihash) { m_multihash = std::move(multihash); } + + /*! + * \brief Serialize as an array of bytes + */ + std::vector Serialize() const; + + /*! + * \brief Deserialize an array of bytes + */ + void Deserialize(const std::vector& data); + +private: + // CID parameters + CIDCodec m_codec = CIDCodec::RAW; + std::vector m_multihash; + + /* + // Buffer parameters + std::vector m_bytes; + unsigned int m_byteOffset = 0; + unsigned int m_byteLength = 0; + */ +}; +} // namespace DATASTORE +} // namespace KODI diff --git a/xbmc/datastore/CMakeLists.txt b/xbmc/datastore/CMakeLists.txt new file mode 100644 index 0000000000000..01ef698f807cd --- /dev/null +++ b/xbmc/datastore/CMakeLists.txt @@ -0,0 +1,17 @@ +set(SOURCES Block.cpp + BlockStore.cpp + CID.cpp + DataStore.cpp + DataStoreLMDB.cpp + IDataStore.cpp +) + +set(HEADERS Block.h + BlockStore.h + CID.h + DataStore.h + DataStoreLMDB.h + IDataStore.h +) + +core_add_library(datastore) diff --git a/xbmc/datastore/DataStore.cpp b/xbmc/datastore/DataStore.cpp new file mode 100644 index 0000000000000..cd117c5cfb98e --- /dev/null +++ b/xbmc/datastore/DataStore.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2018-2020 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "DataStore.h" + +#include "DataStoreLMDB.h" +#include "IDataStore.h" +#include "URL.h" +#include "utils/URIUtils.h" +#include "utils/log.h" + +using namespace KODI; +using namespace DATASTORE; + +CDataStore::~CDataStore() +{ + Close(); +} + +bool CDataStore::Open(const std::string& dataStorePath) +{ + // Validate parameters + if (dataStorePath.empty()) + return false; + + std::unique_ptr dataStore = CreateDataStore(); + if (!dataStore) + return false; + + CLog::Log(LOGINFO, "Opening data store at {}", CURL::GetRedacted(dataStorePath)); + if (!dataStore->Open(dataStorePath)) + return false; + + m_dataStore = std::move(dataStore); + return true; +} + +void CDataStore::Close() +{ + if (m_dataStore) + { + m_dataStore->Close(); + m_dataStore.reset(); + } +} + +bool CDataStore::Has(const uint8_t* key, size_t keySize) +{ + if (m_dataStore) + return m_dataStore->Has(key, keySize); + + return false; +} + +bool CDataStore::Get(const uint8_t* key, size_t keySize, std::vector& data) +{ + if (m_dataStore) + return m_dataStore->Get(key, keySize, data); + + return false; +} + +uint8_t* CDataStore::Reserve(const uint8_t* key, size_t keySize, size_t dataSize) +{ + if (m_dataStore) + return m_dataStore->Reserve(key, keySize, dataSize); + + return nullptr; +} + +void CDataStore::Commit(const uint8_t* data) +{ + if (m_dataStore) + m_dataStore->Commit(data); +} + +bool CDataStore::Put(const uint8_t* key, size_t keySize, const uint8_t* data, size_t dataSize) +{ + if (m_dataStore) + return m_dataStore->Put(key, keySize, data, dataSize); + + return false; +} + +bool CDataStore::Delete(const uint8_t* key, size_t keySize) +{ + if (m_dataStore) + return m_dataStore->Delete(key, keySize); + + return false; +} + +std::unique_ptr CDataStore::CreateDataStore() +{ + return std::make_unique(); +} diff --git a/xbmc/datastore/DataStore.h b/xbmc/datastore/DataStore.h new file mode 100644 index 0000000000000..e86b2acb7b650 --- /dev/null +++ b/xbmc/datastore/DataStore.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2018-2020 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include +#include +#include +#include + +namespace KODI +{ +namespace DATASTORE +{ +class IDataStore; + +class CDataStore +{ +public: + CDataStore() = default; + ~CDataStore(); + + // Wrappers for IDataStore functions + bool Open(const std::string& dataStoreName); + void Close(); + bool Has(const uint8_t* key, size_t keySize); + bool Get(const uint8_t* key, size_t keySize, std::vector& data); + uint8_t* Reserve(const uint8_t* key, size_t keySize, size_t dataSize); + void Commit(const uint8_t* data); + bool Put(const uint8_t* key, size_t keySize, const uint8_t* data, size_t dataSize); + bool Delete(const uint8_t* key, size_t keySize); + +private: + // Factory function + std::unique_ptr CreateDataStore(); + + // Data store parameters + std::unique_ptr m_dataStore; +}; +} // namespace DATASTORE +} // namespace KODI diff --git a/xbmc/datastore/DataStoreLMDB.cpp b/xbmc/datastore/DataStoreLMDB.cpp new file mode 100644 index 0000000000000..637460fc7072c --- /dev/null +++ b/xbmc/datastore/DataStoreLMDB.cpp @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2018-2020 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "DataStoreLMDB.h" + +#include "filesystem/Directory.h" +#include "utils/log.h" + +#include + +using namespace KODI; +using namespace DATASTORE; + +/*! + * \brief The size of the memory map to use for the LMDB environment + * + * \sa mdb_env_set_mapsize() + */ +#define CACHE_SIZE (1UL * 1024UL * 1024UL * 1024UL) + +bool CDataStoreLMDB::Open(const std::string& dataStorePath) +{ + // Create path if it doesn't exist + if (!XFILE::CDirectory::Exists(dataStorePath, true)) + XFILE::CDirectory::Create(dataStorePath); + + if (mdb_env_create(&m_environment) != 0) + { + CLog::Log(LOGERROR, "Failed to create LMDB environment"); + return false; + } + + // Set the cache size + if (mdb_env_set_mapsize(m_environment, CACHE_SIZE) != 0) + { + CLog::Log(LOGERROR, "Failed to set the LMDB cache size to %u bytes", CACHE_SIZE); + return false; + } + + int result = mdb_env_open(m_environment, dataStorePath.c_str(), 0, 0664); + if (result != 0) + { + switch (result) + { + case MDB_VERSION_MISMATCH: + CLog::Log(LOGERROR, "The version of the LMDB library doesn't match the version that " + "created the database environment"); + break; + case MDB_INVALID: + CLog::Log(LOGERROR, "The LMDB environment file headers are corrupted"); + break; + case ENOENT: + CLog::Log(LOGERROR, "The LMDB directory doesn't exist: %s", dataStorePath.c_str()); + break; + case EACCES: + CLog::Log(LOGERROR, + "The user doesn't have permission to access the LMDB environment files"); + break; + case EAGAIN: + CLog::Log(LOGERROR, "The LMDB environment is locked by another process"); + break; + default: + break; + } + + Close(); + return false; + } + + return true; +} + +void CDataStoreLMDB::Close() +{ + if (m_environment != nullptr) + { + mdb_env_close(m_environment); + m_environment = nullptr; + } +} + +bool CDataStoreLMDB::Has(const uint8_t* key, size_t keySize) +{ + //! @todo + std::vector dummy; + return Get(key, keySize, dummy); +} + +bool CDataStoreLMDB::Get(const uint8_t* key, size_t keySize, std::vector& data) +{ + MDB_txn* transaction = CreateTransaction(); + if (transaction == nullptr) + return false; + + MDB_dbi databaseHandle; + if (!OpenDataStore(transaction, static_cast(databaseHandle))) + return false; + + // Get the data + MDB_val databaseKey{keySize, const_cast(key)}; + MDB_val databaseData{}; + int result = mdb_get(transaction, databaseHandle, &databaseKey, &databaseData); + if (result != 0) + { + switch (result) + { + case MDB_NOTFOUND: + { + //! The key was not in the database + break; + } + case EINVAL: + { + CLog::Log(LOGERROR, "LMDB: An invalid parameter was specified"); + break; + } + default: + break; + } + + AbortTransaction(transaction); + return false; + } + + // Success + const uint8_t* outData = static_cast(databaseData.mv_data); + data.assign(outData, outData + databaseData.mv_size); + + CommitTransaction(transaction); + CloseDataStore(databaseHandle); + + return true; +} + +uint8_t* CDataStoreLMDB::Reserve(const uint8_t* key, size_t keySize, size_t dataSize) +{ + MDB_txn* transaction = CreateTransaction(); + if (transaction == nullptr) + return nullptr; + + MDB_dbi databaseHandle; + if (!OpenDataStore(transaction, static_cast(databaseHandle))) + return nullptr; + + // Reserve the data + MDB_val databaseKey{keySize, const_cast(key)}; + MDB_val databaseData{dataSize, nullptr}; + int result = mdb_put(transaction, databaseHandle, &databaseKey, &databaseData, MDB_RESERVE); + if (result != 0) + { + switch (result) + { + case MDB_MAP_FULL: + { + CLog::Log(LOGERROR, "LMDB: The database is full"); + //! @todo Adjust size. See mdb_env_set_mapsize() + break; + } + case MDB_TXN_FULL: + { + CLog::Log(LOGERROR, "LMDB: The transaction has too many dirty pages"); + break; + } + case EACCES: + { + CLog::Log(LOGERROR, "LMDB: An attempt was made to write in a read-only transaction"); + break; + } + case EINVAL: + { + CLog::Log(LOGERROR, + "LMDB: An invalid parameter was specified while writing to the database"); + break; + } + default: + break; + } + + AbortTransaction(transaction); + return nullptr; + } + + // Success + uint8_t* data = static_cast(databaseData.mv_data); + m_reservedData[data] = std::make_pair(transaction, static_cast(databaseHandle)); + + return data; +} + +void CDataStoreLMDB::Commit(const uint8_t* data) +{ + auto it = m_reservedData.find(data); + if (it != m_reservedData.end()) + { + const DataStoreHandle& handle = it->second; + + CommitTransaction(handle.first); + CloseDataStore(static_cast(handle.second)); + + m_reservedData.erase(it); + } +} + +bool CDataStoreLMDB::Put(const uint8_t* key, size_t keySize, const uint8_t* data, size_t dataSize) +{ + MDB_txn* transaction = CreateTransaction(); + if (transaction == nullptr) + return false; + + MDB_dbi databaseHandle; + if (!OpenDataStore(transaction, static_cast(databaseHandle))) + return false; + + // Add the data + MDB_val databaseKey{keySize, const_cast(key)}; + MDB_val databaseData; + int result = mdb_put(transaction, databaseHandle, &databaseKey, &databaseData, 0); + if (result != 0) + { + switch (result) + { + case MDB_MAP_FULL: + { + CLog::Log(LOGERROR, "LMDB: The database is full"); + //! @todo Adjust size. See mdb_env_set_mapsize() + break; + } + case MDB_TXN_FULL: + { + CLog::Log(LOGERROR, "LMDB: The transaction has too many dirty pages"); + break; + } + case EACCES: + { + CLog::Log(LOGERROR, "LMDB: An attempt was made to write in a read-only transaction"); + break; + } + case EINVAL: + { + CLog::Log(LOGERROR, + "LMDB: An invalid parameter was specified while writing to the database"); + break; + } + default: + break; + } + + AbortTransaction(transaction); + return false; + } + + // Success + CommitTransaction(transaction); + CloseDataStore(databaseHandle); + + return true; +} + +bool CDataStoreLMDB::Delete(const uint8_t* key, size_t keySize) +{ + //! @todo + return false; +} + +MDB_txn* CDataStoreLMDB::CreateTransaction() +{ + MDB_txn* transaction = nullptr; + + if (m_environment != nullptr) + { + int result = mdb_txn_begin(m_environment, NULL, 0, &transaction); + if (result != 0) + { + switch (result) + { + case MDB_PANIC: + { + CLog::Log(LOGERROR, + "A fatal error occurred earlier in the LMDB environment. Closing it now."); + // Shutdown LMDB environment + Close(); + break; + } + case MDB_MAP_RESIZED: + { + CLog::Log(LOGERROR, "Another process wrote data beyond the LMDB mapsize"); + //! @todo This environment's map must be resized. See mdb_env_set_mapsize() + break; + } + case MDB_READERS_FULL: + { + CLog::Log(LOGERROR, + "A read-only transaction was requested and the reader lock table is full"); + //! @todo Adjust reader lock table. See mdb_env_set_maxreaders() + break; + } + case ENOMEM: + { + CLog::Log(LOGERROR, "LMDB: Out of memory"); + break; + } + default: + break; + } + } + } + + return transaction; +} + +void CDataStoreLMDB::CommitTransaction(MDB_txn* transaction) +{ + int result = mdb_txn_commit(transaction); + if (result != 0) + { + switch (result) + { + case EINVAL: + { + CLog::Log(LOGERROR, "LMDB: Commit transaction: An invalid parameter was specified"); + break; + } + case ENOSPC: + { + CLog::Log(LOGERROR, "LMDB: Commit transaction: No more disk space"); + break; + } + case EIO: + { + CLog::Log(LOGERROR, + "LMDB: Commit transaction: A low-level I/O error occurred while writing"); + break; + } + case ENOMEM: + { + CLog::Log(LOGERROR, "LMDB: Commit transaction: Out of memory"); + break; + } + default: + break; + } + } +} + +void CDataStoreLMDB::AbortTransaction(MDB_txn* transaction) +{ + mdb_txn_abort(transaction); +} + +bool CDataStoreLMDB::OpenDataStore(MDB_txn* transaction, unsigned int& databaseHandle) +{ + if (transaction != nullptr) + { + int result = mdb_dbi_open(transaction, NULL, 0, static_cast(&databaseHandle)); + if (result != 0) + { + switch (result) + { + case MDB_NOTFOUND: + { + CLog::Log(LOGERROR, "The specified LMDB database doesn't exist in the environment"); + break; + } + case MDB_DBS_FULL: + { + CLog::Log(LOGERROR, "Too many LMDB databases have been opened"); + //! @todo Set max number of DBs for environment. See mdb_env_set_maxdbs() + break; + } + default: + break; + } + } + } + + return transaction; +} + +void CDataStoreLMDB::CloseDataStore(unsigned int databaseHandle) +{ + if (m_environment != nullptr) + mdb_dbi_close(m_environment, static_cast(databaseHandle)); +} diff --git a/xbmc/datastore/DataStoreLMDB.h b/xbmc/datastore/DataStoreLMDB.h new file mode 100644 index 0000000000000..97d97372fdd5a --- /dev/null +++ b/xbmc/datastore/DataStoreLMDB.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2018-2020 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "IDataStore.h" + +#include +#include + +struct MDB_env; +struct MDB_txn; + +namespace KODI +{ +namespace DATASTORE +{ +class CDataStoreLMDB : public IDataStore +{ +public: + ~CDataStoreLMDB() override = default; + + // Implementation of IDataStore + bool Open(const std::string& dataStorePath) override; + void Close() override; + bool Has(const uint8_t* key, size_t keySize) override; + bool Get(const uint8_t* key, size_t keySize, std::vector& data) override; + uint8_t* Reserve(const uint8_t* key, size_t keySize, size_t dataSize) override; + void Commit(const uint8_t* data) override; + bool Put(const uint8_t* key, size_t keySize, const uint8_t* data, size_t dataSize) override; + bool Delete(const uint8_t* key, size_t keySize) override; + +private: + MDB_txn* CreateTransaction(); + void CommitTransaction(MDB_txn* transaction); + void AbortTransaction(MDB_txn* transaction); + bool OpenDataStore(MDB_txn* transaction, unsigned int& databaseHandle); + void CloseDataStore(unsigned int databaseHandle); + + // LMDB parameters + MDB_env* m_environment = nullptr; + using DataStoreHandle = std::pair; + std::map m_reservedData; +}; +} // namespace DATASTORE +} // namespace KODI diff --git a/xbmc/datastore/IDataStore.cpp b/xbmc/datastore/IDataStore.cpp new file mode 100644 index 0000000000000..991c365028206 --- /dev/null +++ b/xbmc/datastore/IDataStore.cpp @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2022 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "IDataStore.h" + +using namespace KODI; +using namespace DATASTORE; + +IDataStore::~IDataStore() = default; diff --git a/xbmc/datastore/IDataStore.h b/xbmc/datastore/IDataStore.h new file mode 100644 index 0000000000000..7af29afea3ff8 --- /dev/null +++ b/xbmc/datastore/IDataStore.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2018-2020 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include +#include +#include + +namespace KODI +{ +namespace DATASTORE +{ +class IDataStore +{ +public: + virtual ~IDataStore(); + + /*! + * \brief Open and connect to the data store + * + * \param dataStorePath The directory dedicated to this data store. Formed + * by adding the name to the data store profile path. + * + * \return True on success, false on failure + */ + virtual bool Open(const std::string& dataStorePath) = 0; + + /*! + * \brief Close the data store and release all resources + */ + virtual void Close() = 0; + + /*! + * \brief Determine if the key can be found + * + * \param key The key to look for + * \param keySize The size of the key, in bytes + * + * \return True if data with the given key was found, false otherwise + */ + virtual bool Has(const uint8_t* key, size_t keySize) = 0; + + /*! + * \brief Get data from the data store + * + * \param key The key used to look up the data + * \param keySize The key size, in bytes + * \param[out] data The data, or unmodified if this function returns false + * + * \return True if the data was retrieved, false for missing key or error + */ + virtual bool Get(const uint8_t* key, size_t keySize, std::vector& data) = 0; + + /*! + * \brief Reserve a buffer in the data store for the given key, saving an + * extra memcpy + * + * Duplicates are not allowed, so this replaces any previously existing + * key. + * + * \param key The key used to add the data + * \param keySize The key size, in bytes + * \param dataSize The size of the buffer to reserve + * + * \return A pointer to the reserved space which the caller can write to, + * or nullptr if a buffer of the requested size could not be + * reserved + */ + virtual uint8_t* Reserve(const uint8_t* key, size_t keySize, size_t dataSize) = 0; + + /*! + * \brief Commit reserved memory to the data store + * + * \param data The data returned by Reserve() + */ + virtual void Commit(const uint8_t* data) = 0; + + /*! + * \brief Add a key/value pair to the data store + * + * Duplicates are not allowed, so this replaces any previously existing + * key. + * + * \param key The key used to add the data + * \param keySize The key size, in bytes + * \param data The data to store + * \param dataSize The size of the data to store + * + * \return True if the data was added, false on error + */ + virtual bool Put(const uint8_t* key, size_t keySize, const uint8_t* data, size_t dataSize) = 0; + + /*! + * \brief Delete data based on its key + * + * \param key The key to look for + * \param keySize The size of the key, in bytes + * + * \return True on success, false on error + */ + virtual bool Delete(const uint8_t* key, size_t keySize) = 0; +}; +} // namespace DATASTORE +} // namespace KODI diff --git a/xbmc/filesystem/CMakeLists.txt b/xbmc/filesystem/CMakeLists.txt index c163f227f8c9d..d4f0d22e6c953 100644 --- a/xbmc/filesystem/CMakeLists.txt +++ b/xbmc/filesystem/CMakeLists.txt @@ -23,6 +23,10 @@ set(SOURCES AddonsDirectory.cpp IDirectory.cpp IFile.cpp ImageFile.cpp + IIPFS.cpp + IPFS.cpp + IPFSFile.cpp + IPFSDirectory.cpp LibraryDirectory.cpp MultiPathDirectory.cpp MultiPathFile.cpp @@ -87,6 +91,10 @@ set(HEADERS AddonsDirectory.h IFileDirectory.h IFileTypes.h ImageFile.h + IIPFS.h + IPFS.h + IPFSFile.h + IPFSDirectory.h LibraryDirectory.h MultiPathDirectory.h MultiPathFile.h diff --git a/xbmc/filesystem/File.cpp b/xbmc/filesystem/File.cpp index 3786611a019e4..76fb081509f71 100644 --- a/xbmc/filesystem/File.cpp +++ b/xbmc/filesystem/File.cpp @@ -834,6 +834,30 @@ ssize_t CFile::Write(const void* lpBuf, size_t uiBufSize) return -1; } +ssize_t CFile::AddContent(const void* lpBuf, size_t uiBufSize, std::string& contentId) +{ + if (!m_pFile) + return -1; + if (lpBuf == NULL && uiBufSize != 0) + return -1; + + try + { + if (uiBufSize == 0 && lpBuf == NULL) + { // "test" write with zero size + // some VFSs don't handle correctly null buffer pointer + // provide valid buffer pointer for them + const char dummyBuf = 0; + return m_pFile->AddContent(&dummyBuf, 0, contentId); + } + + return m_pFile->AddContent(lpBuf, uiBufSize, contentId); + } + XBMCCOMMONS_HANDLE_UNCHECKED + catch (...) { CLog::Log(LOGERROR, "{} - Unhandled exception", __FUNCTION__); } + return -1; +} + bool CFile::Delete(const std::string& strFileName) { const CURL pathToUrl(strFileName); diff --git a/xbmc/filesystem/File.h b/xbmc/filesystem/File.h index a5f991af301fc..2df555d97b118 100644 --- a/xbmc/filesystem/File.h +++ b/xbmc/filesystem/File.h @@ -82,6 +82,16 @@ class CFile * -1 in case of any explicit error */ ssize_t Write(const void* bufPtr, size_t bufSize); + /** + * Attempt to add bufSize bytes from buffer bufPtr to currently opened file. + * @param bufPtr pointer to buffer + * @param bufSize size of the buffer + * @param[out] contentId Set to the content ID of the accumulated data + * @return number of successfully written bytes if any bytes were written, + * zero if no bytes were written and no detectable error occur, + * -1 in case of any explicit error + */ + ssize_t AddContent(const void* bufPtr, size_t bufSize, std::string& contentId); void Flush(); int64_t Seek(int64_t iFilePosition, int iWhence = SEEK_SET); int Truncate(int64_t iSize); diff --git a/xbmc/filesystem/IFile.h b/xbmc/filesystem/IFile.h index d76d6ed1adf83..1696f2187610f 100644 --- a/xbmc/filesystem/IFile.h +++ b/xbmc/filesystem/IFile.h @@ -92,6 +92,19 @@ class IFile * -1 in case of any explicit error */ virtual ssize_t Write(const void* bufPtr, size_t bufSize) { return -1;} + /*! + * Attempt to add bufSize bytes from buffer bufPtr to currently opened file. + * @param bufPtr pointer to buffer + * @param bufSize size of the buffer + * @param[out] contentId Set to the content ID of the accumulated data + * @return number of successfully added bytes if any bytes were added, + * zero if no bytes were added and no detectable error occurred, + * -1 in case of any explicit error + */ + virtual ssize_t AddContent(const void* bufPtr, size_t bufSize, std::string& contentId) + { + return -1; + } virtual bool ReadString(char *szLine, int iLineLength); virtual int64_t Seek(int64_t iFilePosition, int iWhence = SEEK_SET) = 0; virtual void Close() = 0; diff --git a/xbmc/filesystem/IIPFS.cpp b/xbmc/filesystem/IIPFS.cpp new file mode 100644 index 0000000000000..9c313e3fc4e84 --- /dev/null +++ b/xbmc/filesystem/IIPFS.cpp @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2022 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "IIPFS.h" + +using namespace XFILE; + +IIPFS::~IIPFS() = default; diff --git a/xbmc/filesystem/IIPFS.h b/xbmc/filesystem/IIPFS.h new file mode 100644 index 0000000000000..9c10dc3341cff --- /dev/null +++ b/xbmc/filesystem/IIPFS.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2022 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include +#include + +class CVariant; + +namespace KODI +{ +namespace CRYPTO +{ +struct PrivateKey; +} // namespace CRYPTO +} // namespace KODI + +namespace XFILE +{ +class IIPFS +{ +public: + virtual ~IIPFS(); + + // Lifecycle functions + virtual bool Initialize(const std::string& dataStoreRoot) = 0; + virtual void Deinitialize() = 0; + virtual bool IsOnline() = 0; + + // Name subsystem + virtual std::string ResolveName(const std::string& identifier) = 0; + virtual void PublishName(const std::string& ipfsPath, + unsigned int lifetimeSecs, + unsigned int ttlSecs, + const std::string& keyName) = 0; + + // Directed Acyclic Graph (DAG) subsystem + virtual CVariant GetDAG(const std::string& cid) = 0; + virtual std::string PutDAG(const CVariant& content) = 0; + + // Key subsystem + virtual std::vector ListKeys() = 0; + virtual void RemoveKey(const std::string& keyName) = 0; + virtual void ImportKey(const std::string& keyName, + const KODI::CRYPTO::PrivateKey& privateKey, + const std::string& password) = 0; +}; + +} // namespace XFILE diff --git a/xbmc/filesystem/IPFS.cpp b/xbmc/filesystem/IPFS.cpp new file mode 100644 index 0000000000000..96f4737194349 --- /dev/null +++ b/xbmc/filesystem/IPFS.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2022 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "IPFS.h" + +#include "ServiceBroker.h" +#include "datastore/Block.h" +#include "datastore/BlockStore.h" +#include "datastore/CID.h" +#include "datastore/DataStore.h" +#include "datastore/IDataStore.h" +#include "profiles/ProfileManager.h" +#include "settings/SettingsComponent.h" +#include "utils/JSONVariantParser.h" +#include "utils/JSONVariantWriter.h" +#include "utils/URIUtils.h" +#include "utils/Variant.h" + +using namespace KODI; +using namespace XFILE; + +namespace +{ +// Name of the data store +constexpr const char* DATA_STORE_NAME = "ipfs"; +} // namespace + +CIPFS::CIPFS() = default; + +CIPFS::~CIPFS() +{ + Deinitialize(); +} + +bool CIPFS::Initialize(const std::string& dataStoreRoot) +{ + // Validate parameters + if (dataStoreRoot.empty()) + return false; + + const std::string dataStorePath = URIUtils::AddFileToFolder(dataStoreRoot, DATA_STORE_NAME); + + m_dataStore = std::make_unique(); + if (m_dataStore && m_dataStore->Open(dataStorePath)) + { + m_blockStore = std::make_unique(*m_dataStore); + return true; + } + + return false; +} + +void CIPFS::Deinitialize() +{ + m_blockStore.reset(); + m_dataStore.reset(); +} + +bool CIPFS::IsOnline() +{ + //! @todo + return static_cast(m_blockStore); +} + +std::string CIPFS::ResolveName(const std::string& identifier) +{ + //! @todo + return ""; +} + +void CIPFS::PublishName(const std::string& ipfsPath, + unsigned int lifetimeSecs, + unsigned int ttlSecs, + const std::string& keyName) +{ + //! @todo +} + +CVariant CIPFS::GetDAG(const std::string& cid) +{ + if (!m_blockStore) + return CVariant{}; + + //! @todo + std::vector multihash{cid.begin(), cid.end()}; + + // Get classed CID + const DATASTORE::CCID ccid{DATASTORE::CIDCodec::DAG_JSON, std::move(multihash)}; + + DATASTORE::CBlock block; + if (!m_blockStore->Get(ccid, block)) + return CVariant{}; + + //! @todo lz4-decompress block + std::string json{block.Data().begin(), block.Data().end()}; + + CVariant data; + if (!CJSONVariantParser::Parse(json, data)) + return CVariant{}; + + return data; +} + +std::string CIPFS::PutDAG(const CVariant& content) +{ + if (!m_blockStore) + return ""; + + std::string json; + if (!CJSONVariantWriter::Write(content, json, true)) + return ""; + + //! @todo lz4-compress data + std::vector data{json.begin(), json.end()}; + if (data.empty()) + return ""; + + std::vector multihash = {}; //! @todo MakeMultihash(data); + + //! Get classed CID + DATASTORE::CCID ccid{DATASTORE::CIDCodec::DAG_JSON, multihash}; + + DATASTORE::CBlock block{std::move(ccid), std::move(data)}; + + if (!m_blockStore->Put(block)) + return ""; + + //! @todo + std::string cid{multihash.begin(), multihash.end()}; + return cid; +} + +std::vector CIPFS::ListKeys() +{ + //! @todo + return {}; +} + +void CIPFS::RemoveKey(const std::string& keyName) +{ + //! @todo +} + +void CIPFS::ImportKey(const std::string& keyName, + const KODI::CRYPTO::PrivateKey& privateKey, + const std::string& password) +{ + //! @todo +} diff --git a/xbmc/filesystem/IPFS.h b/xbmc/filesystem/IPFS.h new file mode 100644 index 0000000000000..73a1f58d2ccc7 --- /dev/null +++ b/xbmc/filesystem/IPFS.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2022 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "IIPFS.h" + +#include + +namespace KODI +{ +namespace DATASTORE +{ +class CBlockStore; +class CDataStore; +} // namespace DATASTORE +} // namespace KODI + +namespace XFILE +{ + +class CIPFS : public IIPFS +{ +public: + CIPFS(); + ~CIPFS() override; + + // Implementation of IIPFS + bool Initialize(const std::string& dataStoreRoot) override; + void Deinitialize() override; + bool IsOnline() override; + std::string ResolveName(const std::string& identifier) override; + void PublishName(const std::string& ipfsPath, + unsigned int lifetimeSecs, + unsigned int ttlSecs, + const std::string& keyName) override; + CVariant GetDAG(const std::string& cid) override; + std::string PutDAG(const CVariant& content) override; + std::vector ListKeys() override; + void RemoveKey(const std::string& keyName) override; + void ImportKey(const std::string& keyName, + const KODI::CRYPTO::PrivateKey& privateKey, + const std::string& password) override; + +private: + std::unique_ptr m_dataStore; + std::unique_ptr m_blockStore; +}; + +} // namespace XFILE diff --git a/xbmc/filesystem/IPFSDirectory.cpp b/xbmc/filesystem/IPFSDirectory.cpp new file mode 100644 index 0000000000000..413d9e3395800 --- /dev/null +++ b/xbmc/filesystem/IPFSDirectory.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2022 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "IPFSDirectory.h" + +#include "FileItem.h" +#include "IPFS.h" +#include "URL.h" + +using namespace XFILE; + +CIPFSDirectory::CIPFSDirectory() = default; + +CIPFSDirectory::~CIPFSDirectory() = default; + +bool CIPFSDirectory::GetDirectory(const CURL& urlOrig, CFileItemList& items) +{ + //! @todo + return false; +} + +bool CIPFSDirectory::ContainsFiles(const CURL& url) +{ + //! @todo + return false; +} diff --git a/xbmc/filesystem/IPFSDirectory.h b/xbmc/filesystem/IPFSDirectory.h new file mode 100644 index 0000000000000..41a05a91b93af --- /dev/null +++ b/xbmc/filesystem/IPFSDirectory.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2022 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "IFileDirectory.h" + +namespace XFILE +{ + +class CIPFSDirectory : public IFileDirectory +{ +public: + CIPFSDirectory(); + ~CIPFSDirectory() override; + + // Implementation of IDirectory via IFileDirectory + bool GetDirectory(const CURL& url, CFileItemList& items) override; + DIR_CACHE_TYPE GetCacheType(const CURL& url) const override { return DIR_CACHE_ALWAYS; } + + // Implementation of IFileDirectory + bool ContainsFiles(const CURL& url) override; +}; + +} // namespace XFILE diff --git a/xbmc/filesystem/IPFSFile.cpp b/xbmc/filesystem/IPFSFile.cpp new file mode 100644 index 0000000000000..f7ec7f0b6acf5 --- /dev/null +++ b/xbmc/filesystem/IPFSFile.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2022 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "IPFSFile.h" + +using namespace XFILE; + +CIPFSFile::CIPFSFile() = default; + +CIPFSFile::~CIPFSFile() +{ + Close(); +} + +bool CIPFSFile::Open(const CURL& url) +{ + //! @todo + return false; +} + +bool CIPFSFile::Exists(const CURL& url) +{ + //! @todo + return false; +} + +int CIPFSFile::Stat(const CURL& url, struct __stat64* buffer) +{ + //! @todo + return -1; +} + +int CIPFSFile::Stat(struct __stat64* buffer) +{ + //! @todo + return -1; +} + +ssize_t CIPFSFile::Read(void* lpBuf, size_t uiBufSize) +{ + //! @todo + return -1; +} + +ssize_t CIPFSFile::AddContent(const void* bufPtr, size_t bufSize, std::string& contentId) +{ + //! @todo + return -1; +} + +int64_t CIPFSFile::GetPosition() +{ + //! @todo + return -1; +} + +int64_t CIPFSFile::GetLength() +{ + //! @todo + return -1; +} + +int64_t CIPFSFile::Seek(int64_t iFilePosition, int iWhence) +{ + //! @todo + return -1; +} + +void CIPFSFile::Close() +{ + //! @todo +} diff --git a/xbmc/filesystem/IPFSFile.h b/xbmc/filesystem/IPFSFile.h new file mode 100644 index 0000000000000..117e9780b8565 --- /dev/null +++ b/xbmc/filesystem/IPFSFile.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2022 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "File.h" +#include "IFile.h" +#include "IPFS.h" + +namespace XFILE +{ + +class CIPFSFile : public IFile +{ +public: + CIPFSFile(); + ~CIPFSFile() override; + + // Implementation of IFile + bool Open(const CURL& url) override; + bool Exists(const CURL& url) override; + int Stat(const CURL& url, struct __stat64* buffer) override; + int Stat(struct __stat64* buffer) override; + ssize_t Read(void* lpBuf, size_t uiBufSize) override; + ssize_t AddContent(const void* bufPtr, size_t bufSize, std::string& contentId) override; + int64_t GetPosition() override; + int64_t GetLength() override; + int64_t Seek(int64_t iFilePosition, int iWhence = SEEK_SET) override; + void Close() override; +}; + +} // namespace XFILE diff --git a/xbmc/profiles/ProfileManager.cpp b/xbmc/profiles/ProfileManager.cpp index 600c812cf1090..e91062e084643 100644 --- a/xbmc/profiles/ProfileManager.cpp +++ b/xbmc/profiles/ProfileManager.cpp @@ -660,6 +660,14 @@ std::string CProfileManager::GetDatabaseFolder() const return URIUtils::AddFileToFolder(GetUserDataFolder(), "Database"); } +std::string CProfileManager::GetDataStoreFolder() const +{ + if (GetCurrentProfile().hasDatabases()) + return URIUtils::AddFileToFolder(GetProfileUserDataFolder(), "Datastore"); + + return URIUtils::AddFileToFolder(GetUserDataFolder(), "Datastore"); +} + std::string CProfileManager::GetCDDBFolder() const { return URIUtils::AddFileToFolder(GetDatabaseFolder(), "CDDB"); diff --git a/xbmc/profiles/ProfileManager.h b/xbmc/profiles/ProfileManager.h index e39451893fc80..3ae71d8eb21f4 100644 --- a/xbmc/profiles/ProfileManager.h +++ b/xbmc/profiles/ProfileManager.h @@ -175,6 +175,7 @@ class CProfileManager : protected ISettingsHandler, std::string GetUserDataFolder() const; std::string GetProfileUserDataFolder() const; std::string GetDatabaseFolder() const; + std::string GetDataStoreFolder() const; std::string GetCDDBFolder() const; std::string GetThumbnailsFolder() const; std::string GetVideoThumbFolder() const;