Bitcoin Core 22.99.0
P2P Digital Currency
db.cpp
Go to the documentation of this file.
1// Copyright (c) 2009-2010 Satoshi Nakamoto
2// Copyright (c) 2009-2020 The Bitcoin Core developers
3// Distributed under the MIT software license, see the accompanying
4// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6#include <chainparams.h>
7#include <fs.h>
8#include <logging.h>
9#include <wallet/db.h>
10
11#include <string>
12
13std::vector<fs::path> ListDatabases(const fs::path& wallet_dir)
14{
15 std::vector<fs::path> paths;
16 boost::system::error_code ec;
17
18 for (auto it = fs::recursive_directory_iterator(wallet_dir, ec); it != fs::recursive_directory_iterator(); it.increment(ec)) {
19 if (ec) {
20 if (fs::is_directory(*it)) {
21 it.no_push();
22 LogPrintf("%s: %s %s -- skipping.\n", __func__, ec.message(), fs::PathToString(it->path()));
23 } else {
24 LogPrintf("%s: %s %s\n", __func__, ec.message(), fs::PathToString(it->path()));
25 }
26 continue;
27 }
28
29 try {
30 const fs::path path{it->path().lexically_relative(wallet_dir)};
31
32 if (it->status().type() == fs::directory_file &&
33 (IsBDBFile(BDBDataFile(it->path())) || IsSQLiteFile(SQLiteDataFile(it->path())))) {
34 // Found a directory which contains wallet.dat btree file, add it as a wallet.
35 paths.emplace_back(path);
36 } else if (it.level() == 0 && it->symlink_status().type() == fs::regular_file && IsBDBFile(it->path())) {
37 if (it->path().filename() == "wallet.dat") {
38 // Found top-level wallet.dat btree file, add top level directory ""
39 // as a wallet.
40 paths.emplace_back();
41 } else {
42 // Found top-level btree file not called wallet.dat. Current bitcoin
43 // software will never create these files but will allow them to be
44 // opened in a shared database environment for backwards compatibility.
45 // Add it to the list of available wallets.
46 paths.emplace_back(path);
47 }
48 }
49 } catch (const std::exception& e) {
50 LogPrintf("%s: Error scanning %s: %s\n", __func__, fs::PathToString(it->path()), e.what());
51 it.no_push();
52 }
53 }
54
55 return paths;
56}
57
58fs::path BDBDataFile(const fs::path& wallet_path)
59{
60 if (fs::is_regular_file(wallet_path)) {
61 // Special case for backwards compatibility: if wallet path points to an
62 // existing file, treat it as the path to a BDB data file in a parent
63 // directory that also contains BDB log files.
64 return wallet_path;
65 } else {
66 // Normal case: Interpret wallet path as a directory path containing
67 // data and log files.
68 return wallet_path / "wallet.dat";
69 }
70}
71
73{
74 return path / "wallet.dat";
75}
76
77bool IsBDBFile(const fs::path& path)
78{
79 if (!fs::exists(path)) return false;
80
81 // A Berkeley DB Btree file has at least 4K.
82 // This check also prevents opening lock files.
83 boost::system::error_code ec;
84 auto size = fs::file_size(path, ec);
85 if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), fs::PathToString(path));
86 if (size < 4096) return false;
87
88 fsbridge::ifstream file(path, std::ios::binary);
89 if (!file.is_open()) return false;
90
91 file.seekg(12, std::ios::beg); // Magic bytes start at offset 12
92 uint32_t data = 0;
93 file.read((char*) &data, sizeof(data)); // Read 4 bytes of file to compare against magic
94
95 // Berkeley DB Btree magic bytes, from:
96 // https://github.com/file/file/blob/5824af38469ec1ca9ac3ffd251e7afe9dc11e227/magic/Magdir/database#L74-L75
97 // - big endian systems - 00 05 31 62
98 // - little endian systems - 62 31 05 00
99 return data == 0x00053162 || data == 0x62310500;
100}
101
102bool IsSQLiteFile(const fs::path& path)
103{
104 if (!fs::exists(path)) return false;
105
106 // A SQLite Database file is at least 512 bytes.
107 boost::system::error_code ec;
108 auto size = fs::file_size(path, ec);
109 if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), fs::PathToString(path));
110 if (size < 512) return false;
111
112 fsbridge::ifstream file(path, std::ios::binary);
113 if (!file.is_open()) return false;
114
115 // Magic is at beginning and is 16 bytes long
116 char magic[16];
117 file.read(magic, 16);
118
119 // Application id is at offset 68 and 4 bytes long
120 file.seekg(68, std::ios::beg);
121 char app_id[4];
122 file.read(app_id, 4);
123
124 file.close();
125
126 // Check the magic, see https://sqlite.org/fileformat2.html
127 std::string magic_str(magic, 16);
128 if (magic_str != std::string("SQLite format 3", 16)) {
129 return false;
130 }
131
132 // Check the application id matches our network magic
133 return memcmp(Params().MessageStart(), app_id, 4) == 0;
134}
const CChainParams & Params()
Return the currently selected parameters.
Path class wrapper to prepare application code for transition from boost::filesystem library to std::...
Definition: fs.h:34
path(boost::filesystem::path path)
Definition: fs.h:39
fs::path SQLiteDataFile(const fs::path &path)
Definition: db.cpp:72
fs::path BDBDataFile(const fs::path &wallet_path)
Definition: db.cpp:58
bool IsBDBFile(const fs::path &path)
Definition: db.cpp:77
bool IsSQLiteFile(const fs::path &path)
Definition: db.cpp:102
std::vector< fs::path > ListDatabases(const fs::path &wallet_dir)
Recursively list database paths in directory.
Definition: db.cpp:13
#define LogPrintf(...)
Definition: logging.h:187
static bool exists(const path &p)
Definition: fs.h:77
static std::string PathToString(const path &path)
Convert path object to byte string.
Definition: fs.h:120
fs::ifstream ifstream
Definition: fs.h:224