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