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