33// Distributed under the MIT software license, see the accompanying
44// file COPYING or http://www.opensource.org/licenses/mit-license.php.
55
6+ #include < chainparams.h>
67#include < fs.h>
8+ #include < logging.h>
79#include < wallet/db.h>
810
911#include < string>
1012
13+ fs::path GetWalletDir ();
14+
15+ #ifdef USE_BDB
16+ bool ExistsBerkeleyDatabase (const fs::path& path);
17+ #else
18+ # define ExistsBerkeleyDatabase (path ) (false )
19+ #endif
20+ #ifdef USE_SQLITE
21+ bool ExistsSQLiteDatabase (const fs::path& path);
22+ #else
23+ # define ExistsSQLiteDatabase (path ) (false )
24+ #endif
25+
26+ std::vector<fs::path> ListWalletDir ()
27+ {
28+ const fs::path wallet_dir = GetWalletDir ();
29+ const size_t offset = wallet_dir.string ().size () + 1 ;
30+ std::vector<fs::path> paths;
31+ boost::system::error_code ec;
32+
33+ for (auto it = fs::recursive_directory_iterator (wallet_dir, ec); it != fs::recursive_directory_iterator (); it.increment (ec)) {
34+ if (ec) {
35+ LogPrintf (" %s: %s %s\n " , __func__, ec.message (), it->path ().string ());
36+ continue ;
37+ }
38+
39+ try {
40+ // Get wallet path relative to walletdir by removing walletdir from the wallet path.
41+ // This can be replaced by boost::filesystem::lexically_relative once boost is bumped to 1.60.
42+ const fs::path path = it->path ().string ().substr (offset);
43+
44+ if (it->status ().type () == fs::directory_file &&
45+ (ExistsBerkeleyDatabase (it->path ()) || ExistsSQLiteDatabase (it->path ()))) {
46+ // Found a directory which contains wallet.dat btree file, add it as a wallet.
47+ paths.emplace_back (path);
48+ } else if (it.level () == 0 && it->symlink_status ().type () == fs::regular_file && ExistsBerkeleyDatabase (it->path ())) {
49+ if (it->path ().filename () == " wallet.dat" ) {
50+ // Found top-level wallet.dat btree file, add top level directory ""
51+ // as a wallet.
52+ paths.emplace_back ();
53+ } else {
54+ // Found top-level btree file not called wallet.dat. Current bitcoin
55+ // software will never create these files but will allow them to be
56+ // opened in a shared database environment for backwards compatibility.
57+ // Add it to the list of available wallets.
58+ paths.emplace_back (path);
59+ }
60+ }
61+ } catch (const std::exception& e) {
62+ LogPrintf (" %s: Error scanning %s: %s\n " , __func__, it->path ().string (), e.what ());
63+ it.no_push ();
64+ }
65+ }
66+
67+ return paths;
68+ }
69+
1170void SplitWalletPath (const fs::path& wallet_path, fs::path& env_directory, std::string& database_filename)
1271{
1372 if (fs::is_regular_file (wallet_path)) {
@@ -23,3 +82,62 @@ void SplitWalletPath(const fs::path& wallet_path, fs::path& env_directory, std::
2382 database_filename = " wallet.dat" ;
2483 }
2584}
85+
86+ bool IsBDBFile (const fs::path& path)
87+ {
88+ if (!fs::exists (path)) return false ;
89+
90+ // A Berkeley DB Btree file has at least 4K.
91+ // This check also prevents opening lock files.
92+ boost::system::error_code ec;
93+ auto size = fs::file_size (path, ec);
94+ if (ec) LogPrintf (" %s: %s %s\n " , __func__, ec.message (), path.string ());
95+ if (size < 4096 ) return false ;
96+
97+ fsbridge::ifstream file (path, std::ios::binary);
98+ if (!file.is_open ()) return false ;
99+
100+ file.seekg (12 , std::ios::beg); // Magic bytes start at offset 12
101+ uint32_t data = 0 ;
102+ file.read ((char *) &data, sizeof (data)); // Read 4 bytes of file to compare against magic
103+
104+ // Berkeley DB Btree magic bytes, from:
105+ // https://github.com/file/file/blob/5824af38469ec1ca9ac3ffd251e7afe9dc11e227/magic/Magdir/database#L74-L75
106+ // - big endian systems - 00 05 31 62
107+ // - little endian systems - 62 31 05 00
108+ return data == 0x00053162 || data == 0x62310500 ;
109+ }
110+
111+ bool IsSQLiteFile (const fs::path& path)
112+ {
113+ if (!fs::exists (path)) return false ;
114+
115+ // A SQLite Database file is at least 512 bytes.
116+ boost::system::error_code ec;
117+ auto size = fs::file_size (path, ec);
118+ if (ec) LogPrintf (" %s: %s %s\n " , __func__, ec.message (), path.string ());
119+ if (size < 512 ) return false ;
120+
121+ fsbridge::ifstream file (path, std::ios::binary);
122+ if (!file.is_open ()) return false ;
123+
124+ // Magic is at beginning and is 16 bytes long
125+ char magic[16 ];
126+ file.read (magic, 16 );
127+
128+ // Application id is at offset 68 and 4 bytes long
129+ file.seekg (68 , std::ios::beg);
130+ char app_id[4 ];
131+ file.read (app_id, 4 );
132+
133+ file.close ();
134+
135+ // Check the magic, see https://sqlite.org/fileformat2.html
136+ std::string magic_str (magic, 16 );
137+ if (magic_str != std::string (" SQLite format 3" , 16 )) {
138+ return false ;
139+ }
140+
141+ // Check the application id matches our network magic
142+ return memcmp (Params ().MessageStart (), app_id, 4 ) == 0 ;
143+ }
0 commit comments