Skip to content

Commit ac38a87

Browse files
committed
Determine wallet file type based on file magic
1 parent 6045f77 commit ac38a87

File tree

7 files changed

+83
-30
lines changed

7 files changed

+83
-30
lines changed

src/wallet/bdb.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -813,7 +813,7 @@ bool ExistsBerkeleyDatabase(const fs::path& path)
813813
fs::path env_directory;
814814
std::string data_filename;
815815
SplitWalletPath(path, env_directory, data_filename);
816-
return IsBerkeleyBtree(env_directory / data_filename);
816+
return IsBDBFile(env_directory / data_filename);
817817
}
818818

819819
std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error)
@@ -839,3 +839,28 @@ std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, con
839839
status = DatabaseStatus::SUCCESS;
840840
return db;
841841
}
842+
843+
bool IsBDBFile(const fs::path& path)
844+
{
845+
if (!fs::exists(path)) return false;
846+
847+
// A Berkeley DB Btree file has at least 4K.
848+
// This check also prevents opening lock files.
849+
boost::system::error_code ec;
850+
auto size = fs::file_size(path, ec);
851+
if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), path.string());
852+
if (size < 4096) return false;
853+
854+
fsbridge::ifstream file(path, std::ios::binary);
855+
if (!file.is_open()) return false;
856+
857+
file.seekg(12, std::ios::beg); // Magic bytes start at offset 12
858+
uint32_t data = 0;
859+
file.read((char*) &data, sizeof(data)); // Read 4 bytes of file to compare against magic
860+
861+
// Berkeley DB Btree magic bytes, from:
862+
// https://github.com/file/file/blob/5824af38469ec1ca9ac3ffd251e7afe9dc11e227/magic/Magdir/database#L74-L75
863+
// - big endian systems - 00 05 31 62
864+
// - little endian systems - 62 31 05 00
865+
return data == 0x00053162 || data == 0x62310500;
866+
}

src/wallet/bdb.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ class BerkeleyEnvironment
8787
std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& wallet_path, std::string& database_filename);
8888

8989
/** Check format of database file */
90-
bool IsBerkeleyBtree(const fs::path& path);
90+
bool IsBDBFile(const fs::path& path);
9191

9292
class BerkeleyBatch;
9393

src/wallet/db.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include <clientversion.h>
1010
#include <fs.h>
11+
#include <optional.h>
1112
#include <streams.h>
1213
#include <support/allocators/secure.h>
1314
#include <util/memory.h>
@@ -194,11 +195,13 @@ class DummyDatabase : public WalletDatabase
194195

195196
enum class DatabaseFormat {
196197
BERKELEY,
198+
SQLITE,
197199
};
198200

199201
struct DatabaseOptions {
200202
bool require_existing = false;
201203
bool require_create = false;
204+
Optional<DatabaseFormat> require_format;
202205
uint64_t create_flags = 0;
203206
SecureString create_passphrase;
204207
bool verify = true;

src/wallet/sqlite.cpp

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,8 @@ bool SQLiteBatch::TxnAbort()
502502

503503
bool ExistsSQLiteDatabase(const fs::path& path)
504504
{
505-
return false;
505+
const fs::path file = path / DATABASE_FILENAME;
506+
return fs::symlink_status(file).type() == fs::regular_file && IsSQLiteFile(file);
506507
}
507508

508509
std::unique_ptr<SQLiteDatabase> MakeSQLiteDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error)
@@ -526,3 +527,26 @@ std::string SQLiteDatabaseVersion()
526527
{
527528
return std::string(sqlite3_libversion());
528529
}
530+
531+
bool IsSQLiteFile(const fs::path& path)
532+
{
533+
if (!fs::exists(path)) return false;
534+
535+
// A SQLite Database file is at least 512 bytes.
536+
boost::system::error_code ec;
537+
auto size = fs::file_size(path, ec);
538+
if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), path.string());
539+
if (size < 512) return false;
540+
541+
fsbridge::ifstream file(path, std::ios::binary);
542+
if (!file.is_open()) return false;
543+
544+
// Magic is at beginning and is 16 bytes long
545+
char magic[16];
546+
file.read(magic, 16);
547+
file.close();
548+
549+
// Check the magic, see https://sqlite.org/fileformat2.html
550+
std::string magic_str(magic);
551+
return magic_str == std::string("SQLite format 3");
552+
}

src/wallet/sqlite.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,5 +116,6 @@ bool ExistsSQLiteDatabase(const fs::path& path);
116116
std::unique_ptr<SQLiteDatabase> MakeSQLiteDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);
117117

118118
std::string SQLiteDatabaseVersion();
119+
bool IsSQLiteFile(const fs::path& path);
119120

120121
#endif // BITCOIN_WALLET_SQLITE_H

src/wallet/walletdb.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <util/time.h>
1616
#include <util/translation.h>
1717
#include <wallet/bdb.h>
18+
#include <wallet/sqlite.h>
1819
#include <wallet/wallet.h>
1920

2021
#include <atomic>
@@ -1011,6 +1012,14 @@ std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const Databas
10111012
if (ExistsBerkeleyDatabase(path)) {
10121013
format = DatabaseFormat::BERKELEY;
10131014
}
1015+
if (ExistsSQLiteDatabase(path)) {
1016+
if (format) {
1017+
error = Untranslated(strprintf("Failed to load database path '%s'. Data is in ambiguous format.", path.string()));
1018+
status = DatabaseStatus::FAILED_BAD_FORMAT;
1019+
return nullptr;
1020+
}
1021+
format = DatabaseFormat::SQLITE;
1022+
}
10141023
} else if (options.require_existing) {
10151024
error = Untranslated(strprintf("Failed to load database path '%s'. Path does not exist.", path.string()));
10161025
status = DatabaseStatus::FAILED_NOT_FOUND;
@@ -1029,6 +1038,20 @@ std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const Databas
10291038
return nullptr;
10301039
}
10311040

1041+
// A db already exists so format is set, but options also specifies the format, so make sure they agree
1042+
if (format && options.require_format && format != options.require_format) {
1043+
error = Untranslated(strprintf("Failed to load database path '%s'. Data is not in required format.", path.string()));
1044+
status = DatabaseStatus::FAILED_BAD_FORMAT;
1045+
return nullptr;
1046+
}
1047+
1048+
// Format is not set when a db doesn't already exist, so use the format specified by the options if it is set.
1049+
if (!format && options.require_format) format = options.require_format;
1050+
1051+
if (format && format == DatabaseFormat::SQLITE) {
1052+
return MakeSQLiteDatabase(path, options, status, error);
1053+
}
1054+
10321055
return MakeBerkeleyDatabase(path, options, status, error);
10331056
}
10341057

src/wallet/walletutil.cpp

Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#include <logging.h>
88
#include <util/system.h>
99

10+
bool ExistsBerkeleyDatabase(const fs::path& path);
11+
1012
fs::path GetWalletDir()
1113
{
1214
fs::path path;
@@ -29,31 +31,6 @@ fs::path GetWalletDir()
2931
return path;
3032
}
3133

32-
bool IsBerkeleyBtree(const fs::path& path)
33-
{
34-
if (!fs::exists(path)) return false;
35-
36-
// A Berkeley DB Btree file has at least 4K.
37-
// This check also prevents opening lock files.
38-
boost::system::error_code ec;
39-
auto size = fs::file_size(path, ec);
40-
if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), path.string());
41-
if (size < 4096) return false;
42-
43-
fsbridge::ifstream file(path, std::ios::binary);
44-
if (!file.is_open()) return false;
45-
46-
file.seekg(12, std::ios::beg); // Magic bytes start at offset 12
47-
uint32_t data = 0;
48-
file.read((char*) &data, sizeof(data)); // Read 4 bytes of file to compare against magic
49-
50-
// Berkeley DB Btree magic bytes, from:
51-
// https://github.com/file/file/blob/5824af38469ec1ca9ac3ffd251e7afe9dc11e227/magic/Magdir/database#L74-L75
52-
// - big endian systems - 00 05 31 62
53-
// - little endian systems - 62 31 05 00
54-
return data == 0x00053162 || data == 0x62310500;
55-
}
56-
5734
std::vector<fs::path> ListWalletDir()
5835
{
5936
const fs::path wallet_dir = GetWalletDir();
@@ -71,10 +48,10 @@ std::vector<fs::path> ListWalletDir()
7148
// This can be replaced by boost::filesystem::lexically_relative once boost is bumped to 1.60.
7249
const fs::path path = it->path().string().substr(offset);
7350

74-
if (it->status().type() == fs::directory_file && IsBerkeleyBtree(it->path() / "wallet.dat")) {
51+
if (it->status().type() == fs::directory_file && ExistsBerkeleyDatabase(it->path())) {
7552
// Found a directory which contains wallet.dat btree file, add it as a wallet.
7653
paths.emplace_back(path);
77-
} else if (it.level() == 0 && it->symlink_status().type() == fs::regular_file && IsBerkeleyBtree(it->path())) {
54+
} else if (it.level() == 0 && it->symlink_status().type() == fs::regular_file && ExistsBerkeleyDatabase(it->path())) {
7855
if (it->path().filename() == "wallet.dat") {
7956
// Found top-level wallet.dat btree file, add top level directory ""
8057
// as a wallet.

0 commit comments

Comments
 (0)