Bitcoin Core
31.1.0
P2P Digital Currency
Toggle main menu visibility
Loading...
Searching...
No Matches
src
wallet
db.cpp
Go to the documentation of this file.
1
// Copyright (c) 2009-2010 Satoshi Nakamoto
2
// Copyright (c) 2009-present 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 <
common/args.h
>
8
#include <logging.h>
9
#include <
util/fs.h
>
10
#include <
wallet/db.h
>
11
12
#include <algorithm>
13
#include <exception>
14
#include <fstream>
15
#include <string>
16
#include <system_error>
17
#include <vector>
18
19
namespace
wallet
{
20
bool
operator<
(
BytePrefix
a, std::span<const std::byte> b) {
return
std::ranges::lexicographical_compare(a.
prefix
, b.subspan(0, std::min(a.
prefix
.size(), b.size()))); }
21
bool
operator<
(std::span<const std::byte> a,
BytePrefix
b) {
return
std::ranges::lexicographical_compare(a.subspan(0, std::min(a.size(), b.
prefix
.size())), b.
prefix
); }
22
23
std::vector<std::pair<fs::path, std::string>>
ListDatabases
(
const
fs::path& wallet_dir)
24
{
25
std::vector<std::pair<fs::path, std::string>> paths;
26
std::error_code ec;
27
28
for
(
auto
it = fs::recursive_directory_iterator(wallet_dir, ec); it != fs::recursive_directory_iterator(); it.increment(ec)) {
29
assert
(!ec);
// Loop should exit on error.
30
try
{
31
const
fs::path path{it->path().lexically_relative(wallet_dir)};
32
33
if
(it->status().type() == fs::file_type::directory) {
34
if
(
IsBDBFile
(
BDBDataFile
(it->path()))) {
35
// Found a directory which contains wallet.dat btree file, add it as a wallet with BERKELEY format.
36
paths.emplace_back(path,
"bdb"
);
37
}
else
if
(
IsSQLiteFile
(
SQLiteDataFile
(it->path()))) {
38
// Found a directory which contains wallet.dat sqlite file, add it as a wallet with SQLITE format.
39
paths.emplace_back(path,
"sqlite"
);
40
}
41
}
else
if
(it.depth() == 0 && it->symlink_status().type() == fs::file_type::regular && it->path().extension() !=
".bak"
) {
42
if
(it->path().filename() ==
"wallet.dat"
) {
43
// Found top-level wallet.dat file, add top level directory ""
44
// as a wallet.
45
if
(
IsBDBFile
(it->path())) {
46
paths.emplace_back(fs::path(),
"bdb"
);
47
}
else
if
(
IsSQLiteFile
(it->path())) {
48
paths.emplace_back(fs::path(),
"sqlite"
);
49
}
50
}
else
if
(
IsBDBFile
(it->path())) {
51
// Found top-level btree file not called wallet.dat. Current bitcoin
52
// software will never create these files but will allow them to be
53
// opened in a shared database environment for backwards compatibility.
54
// Add it to the list of available wallets.
55
paths.emplace_back(path,
"bdb"
);
56
}
57
}
58
}
catch
(
const
std::exception& e) {
59
LogWarning
(
"Error while scanning wallet dir item: %s [%s]."
, e.what(), fs::PathToString(it->path()));
60
it.disable_recursion_pending();
61
}
62
}
63
if
(ec) {
64
// Loop could have exited with an error due to one of:
65
// * wallet_dir itself not being scannable.
66
// * increment() failure. (Observed on Windows native builds when
67
// removing the ACL read permissions of a wallet directory after the
68
// process started).
69
LogWarning
(
"Error scanning directory entries under %s: %s"
, fs::PathToString(wallet_dir), ec.message());
70
}
71
72
return
paths;
73
}
74
75
fs::path
BDBDataFile
(
const
fs::path& wallet_path)
76
{
77
if
(fs::is_regular_file(wallet_path)) {
78
// Special case for backwards compatibility: if wallet path points to an
79
// existing file, treat it as the path to a BDB data file in a parent
80
// directory that also contains BDB log files.
81
return
wallet_path;
82
}
else
{
83
// Normal case: Interpret wallet path as a directory path containing
84
// data and log files.
85
return
wallet_path /
"wallet.dat"
;
86
}
87
}
88
89
fs::path
SQLiteDataFile
(
const
fs::path& path)
90
{
91
return
path /
"wallet.dat"
;
92
}
93
94
bool
IsBDBFile
(
const
fs::path& path)
95
{
96
if
(!fs::exists(path))
return
false
;
97
98
// A Berkeley DB Btree file has at least 4K.
99
// This check also prevents opening lock files.
100
std::error_code ec;
101
auto
size = fs::file_size(path, ec);
102
if
(ec)
LogWarning
(
"Error reading file_size: %s [%s]"
, ec.message(), fs::PathToString(path));
103
if
(size < 4096)
return
false
;
104
105
std::ifstream file{path.std_path(), std::ios::binary};
106
if
(!file.is_open())
return
false
;
107
108
file.seekg(12, std::ios::beg);
// Magic bytes start at offset 12
109
uint32_t data = 0;
110
file.read((
char
*) &data,
sizeof
(data));
// Read 4 bytes of file to compare against magic
111
112
// Berkeley DB Btree magic bytes, from:
113
// https://github.com/file/file/blob/5824af38469ec1ca9ac3ffd251e7afe9dc11e227/magic/Magdir/database#L74-L75
114
// - big endian systems - 00 05 31 62
115
// - little endian systems - 62 31 05 00
116
return
data == 0x00053162 || data == 0x62310500;
117
}
118
119
bool
IsSQLiteFile
(
const
fs::path& path)
120
{
121
if
(!fs::exists(path))
return
false
;
122
123
// A SQLite Database file is at least 512 bytes.
124
std::error_code ec;
125
auto
size = fs::file_size(path, ec);
126
if
(ec)
LogWarning
(
"Error reading file_size: %s [%s]"
, ec.message(), fs::PathToString(path));
127
if
(size < 512)
return
false
;
128
129
std::ifstream file{path.std_path(), std::ios::binary};
130
if
(!file.is_open())
return
false
;
131
132
// Magic is at beginning and is 16 bytes long
133
char
magic[16];
134
file.read(magic, 16);
135
136
// Application id is at offset 68 and 4 bytes long
137
file.seekg(68, std::ios::beg);
138
char
app_id[4];
139
file.read(app_id, 4);
140
141
file.close();
142
143
// Check the magic, see https://sqlite.org/fileformat.html
144
std::string magic_str(magic, 16);
145
if
(magic_str != std::string{
"SQLite format 3\000"
, 16}) {
146
return
false
;
147
}
148
149
// Check the application id matches our network magic
150
return
memcmp(
Params
().MessageStart().data(), app_id, 4) == 0;
151
}
152
153
void
ReadDatabaseArgs
(
const
ArgsManager
&
args
,
DatabaseOptions
& options)
154
{
155
// Override current options with args values, if any were specified
156
options.
use_unsafe_sync
=
args
.GetBoolArg(
"-unsafesqlitesync"
, options.
use_unsafe_sync
);
157
}
158
159
}
// namespace wallet
args.h
args
ArgsManager & args
Definition
bitcoind.cpp:277
Params
const CChainParams & Params()
Return the currently selected parameters.
Definition
chainparams.cpp:112
ArgsManager
Definition
args.h:103
db.h
fs.h
LogWarning
#define LogWarning(...)
Definition
log.h:96
wallet
Definition
wallet_balance.cpp:26
wallet::ReadDatabaseArgs
void ReadDatabaseArgs(const ArgsManager &args, DatabaseOptions &options)
Definition
db.cpp:153
wallet::operator<
bool operator<(BytePrefix a, std::span< const std::byte > b)
Definition
db.cpp:20
wallet::SQLiteDataFile
fs::path SQLiteDataFile(const fs::path &path)
Definition
db.cpp:89
wallet::IsBDBFile
bool IsBDBFile(const fs::path &path)
Definition
db.cpp:94
wallet::BDBDataFile
fs::path BDBDataFile(const fs::path &wallet_path)
Definition
db.cpp:75
wallet::IsSQLiteFile
bool IsSQLiteFile(const fs::path &path)
Definition
db.cpp:119
wallet::ListDatabases
std::vector< std::pair< fs::path, std::string > > ListDatabases(const fs::path &wallet_dir)
Recursively list database paths in directory.
Definition
db.cpp:23
wallet::BytePrefix
Definition
db.h:24
wallet::BytePrefix::prefix
std::span< const std::byte > prefix
Definition
db.h:25
wallet::DatabaseOptions
Definition
db.h:172
wallet::DatabaseOptions::use_unsafe_sync
bool use_unsafe_sync
Disable file sync for faster performance.
Definition
db.h:181
assert
assert(!tx.IsCoinBase())
Generated on
for Bitcoin Core by
1.17.0