diff --git a/src/wallet/bdb.cpp b/src/wallet/bdb.cpp index 24eb2ee34c9..61463aaf5e4 100644 --- a/src/wallet/bdb.cpp +++ b/src/wallet/bdb.cpp @@ -824,3 +824,35 @@ std::unique_ptr BerkeleyDatabase::MakeBatch(const char* mode, boo { return MakeUnique(*this, mode, flush_on_close); } + +bool ExistsBerkeleyDatabase(const fs::path& path) +{ + fs::path env_directory; + std::string data_filename; + SplitWalletPath(path, env_directory, data_filename); + return IsBerkeleyBtree(env_directory / data_filename); +} + +std::unique_ptr MakeBerkeleyDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error) +{ + std::unique_ptr db; + { + LOCK(cs_db); // Lock env.m_databases until insert in BerkeleyDatabase constructor + std::string data_filename; + std::shared_ptr env = GetWalletEnv(path, data_filename); + if (env->m_databases.count(data_filename)) { + error = Untranslated(strprintf("Refusing to load database. Data file '%s' is already loaded.", (env->Directory() / data_filename).string())); + status = DatabaseStatus::FAILED_ALREADY_LOADED; + return nullptr; + } + db = MakeUnique(std::move(env), std::move(data_filename)); + } + + if (options.verify && !db->Verify(error)) { + status = DatabaseStatus::FAILED_VERIFY; + return nullptr; + } + + status = DatabaseStatus::SUCCESS; + return db; +} diff --git a/src/wallet/bdb.h b/src/wallet/bdb.h index 75546924e83..82ad136649e 100644 --- a/src/wallet/bdb.h +++ b/src/wallet/bdb.h @@ -90,6 +90,9 @@ std::shared_ptr GetWalletEnv(const fs::path& wallet_path, s /** Return whether a BDB wallet database is currently loaded. */ bool IsBDBWalletLoaded(const fs::path& wallet_path); +/** Check format of database file */ +bool IsBerkeleyBtree(const fs::path& path); + class BerkeleyBatch; /** An instance of this class represents one database. @@ -224,4 +227,10 @@ public: std::string BerkeleyDatabaseVersion(); +//! Check if Berkeley database exists at specified path. +bool ExistsBerkeleyDatabase(const fs::path& path); + +//! Return object giving access to Berkeley database at specified path. +std::unique_ptr MakeBerkeleyDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error); + #endif // BITCOIN_WALLET_BDB_H diff --git a/src/wallet/db.h b/src/wallet/db.h index 0afaba5fd17..f0f6f03c437 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -195,4 +195,26 @@ public: std::unique_ptr MakeBatch(const char* mode = "r+", bool flush_on_close = true) override { return MakeUnique(); } }; +enum class DatabaseFormat { + BERKELEY, +}; + +struct DatabaseOptions { + bool require_existing = false; + bool require_create = false; + bool verify = true; +}; + +enum class DatabaseStatus { + SUCCESS, + FAILED_BAD_PATH, + FAILED_BAD_FORMAT, + FAILED_ALREADY_LOADED, + FAILED_ALREADY_EXISTS, + FAILED_NOT_FOUND, + FAILED_VERIFY, +}; + +std::unique_ptr MakeDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error); + #endif // BITCOIN_WALLET_DB_H diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 962ea66fa0f..23c4b697779 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include @@ -993,6 +995,43 @@ bool WalletBatch::TxnAbort() return m_batch->TxnAbort(); } +std::unique_ptr MakeDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error) +{ + bool exists; + try { + exists = fs::symlink_status(path).type() != fs::file_not_found; + } catch (const fs::filesystem_error& e) { + error = Untranslated(strprintf("Failed to access database path '%s': %s", path.string(), fsbridge::get_filesystem_error_message(e))); + status = DatabaseStatus::FAILED_BAD_PATH; + return nullptr; + } + + Optional format; + if (exists) { + if (ExistsBerkeleyDatabase(path)) { + format = DatabaseFormat::BERKELEY; + } + } else if (options.require_existing) { + error = Untranslated(strprintf("Failed to load database path '%s'. Path does not exist.", path.string())); + status = DatabaseStatus::FAILED_NOT_FOUND; + return nullptr; + } + + if (!format && options.require_existing) { + error = Untranslated(strprintf("Failed to load database path '%s'. Data is not in recognized format.", path.string())); + status = DatabaseStatus::FAILED_BAD_FORMAT; + return nullptr; + } + + if (format && options.require_create) { + error = Untranslated(strprintf("Failed to create database path '%s'. Database already exists.", path.string())); + status = DatabaseStatus::FAILED_ALREADY_EXISTS; + return nullptr; + } + + return MakeBerkeleyDatabase(path, options, status, error); +} + bool IsWalletLoaded(const fs::path& wallet_path) { return IsBDBWalletLoaded(wallet_path); diff --git a/src/wallet/walletutil.cpp b/src/wallet/walletutil.cpp index d6a8ee9e023..e4c72aed987 100644 --- a/src/wallet/walletutil.cpp +++ b/src/wallet/walletutil.cpp @@ -29,7 +29,7 @@ fs::path GetWalletDir() return path; } -static bool IsBerkeleyBtree(const fs::path& path) +bool IsBerkeleyBtree(const fs::path& path) { if (!fs::exists(path)) return false;