Line data Source code
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 <compat/compat.h>
7 : #include <fs.h>
8 : #include <wallet/bdb.h>
9 : #include <wallet/db.h>
10 :
11 : #include <util/strencodings.h>
12 : #include <util/translation.h>
13 :
14 : #include <stdint.h>
15 :
16 : #include <sys/stat.h>
17 :
18 : // Windows may not define S_IRUSR or S_IWUSR. We define both
19 : // here, with the same values as glibc (see stat.h).
20 : #ifdef WIN32
21 : #ifndef S_IRUSR
22 : #define S_IRUSR 0400
23 : #define S_IWUSR 0200
24 : #endif
25 : #endif
26 :
27 : namespace wallet {
28 : namespace {
29 290771 : Span<const std::byte> SpanFromDbt(const BerkeleyBatch::SafeDbt& dbt)
30 : {
31 290771 : return {reinterpret_cast<const std::byte*>(dbt.get_data()), dbt.get_size()};
32 : }
33 :
34 : //! Make sure database has a unique fileid within the environment. If it
35 : //! doesn't, throw an error. BDB caches do not work properly when more than one
36 : //! open database has the same fileid (values written to one database may show
37 : //! up in reads to other databases).
38 : //!
39 : //! BerkeleyDB generates unique fileids by default
40 : //! (https://docs.oracle.com/cd/E17275_01/html/programmer_reference/program_copy.html),
41 : //! so bitcoin should never create different databases with the same fileid, but
42 : //! this error can be triggered if users manually copy database files.
43 4126 : void CheckUniqueFileid(const BerkeleyEnvironment& env, const std::string& filename, Db& db, WalletDatabaseFileId& fileid)
44 : {
45 4126 : if (env.IsMock()) return;
46 :
47 4126 : int ret = db.get_mpf()->get_fileid(fileid.value);
48 4126 : if (ret != 0) {
49 12 : throw std::runtime_error(strprintf("BerkeleyDatabase: Can't open database %s (get_fileid failed with %d)", filename, ret));
50 : }
51 :
52 8312 : for (const auto& item : env.m_fileids) {
53 4198 : if (fileid == item.second && &fileid != &item.second) {
54 12 : throw std::runtime_error(strprintf("BerkeleyDatabase: Can't open database %s (duplicates fileid %s from %s)", filename,
55 12 : HexStr(item.second.value), item.first));
56 : }
57 : }
58 4126 : }
59 :
60 3444 : RecursiveMutex cs_db;
61 3444 : std::map<std::string, std::weak_ptr<BerkeleyEnvironment>> g_dbenvs GUARDED_BY(cs_db); //!< Map from directory name to db environment.
62 : } // namespace
63 :
64 4198 : bool WalletDatabaseFileId::operator==(const WalletDatabaseFileId& rhs) const
65 : {
66 4198 : return memcmp(value, &rhs.value, sizeof(value)) == 0;
67 : }
68 :
69 : /**
70 : * @param[in] env_directory Path to environment directory
71 : * @return A shared pointer to the BerkeleyEnvironment object for the wallet directory, never empty because ~BerkeleyEnvironment
72 : * erases the weak pointer from the g_dbenvs map.
73 : * @post A new BerkeleyEnvironment weak pointer is inserted into g_dbenvs if the directory path key was not already in the map.
74 : */
75 1940 : std::shared_ptr<BerkeleyEnvironment> GetBerkeleyEnv(const fs::path& env_directory, bool use_shared_memory)
76 : {
77 1940 : LOCK(cs_db);
78 1940 : auto inserted = g_dbenvs.emplace(fs::PathToString(env_directory), std::weak_ptr<BerkeleyEnvironment>());
79 1940 : if (inserted.second) {
80 1888 : auto env = std::make_shared<BerkeleyEnvironment>(env_directory, use_shared_memory);
81 1888 : inserted.first->second = env;
82 1888 : return env;
83 1888 : }
84 52 : return inserted.first->second.lock();
85 1940 : }
86 :
87 : //
88 : // BerkeleyBatch
89 : //
90 :
91 3118 : void BerkeleyEnvironment::Close()
92 : {
93 3118 : if (!fDbEnvInit)
94 1206 : return;
95 :
96 1912 : fDbEnvInit = false;
97 :
98 3154 : for (auto& db : m_databases) {
99 1242 : BerkeleyDatabase& database = db.second.get();
100 1242 : assert(database.m_refcount <= 0);
101 1242 : if (database.m_db) {
102 0 : database.m_db->close(0);
103 0 : database.m_db.reset();
104 0 : }
105 : }
106 :
107 1912 : FILE* error_file = nullptr;
108 1912 : dbenv->get_errfile(&error_file);
109 :
110 1912 : int ret = dbenv->close(0);
111 1912 : if (ret != 0)
112 0 : LogPrintf("BerkeleyEnvironment::Close: Error %d closing database environment: %s\n", ret, DbEnv::strerror(ret));
113 1912 : if (!fMockDb)
114 1912 : DbEnv(uint32_t{0}).remove(strPath.c_str(), 0);
115 :
116 1912 : if (error_file) fclose(error_file);
117 :
118 1912 : UnlockDirectory(fs::PathFromString(strPath), ".walletlock");
119 3118 : }
120 :
121 1937 : void BerkeleyEnvironment::Reset()
122 : {
123 1937 : dbenv.reset(new DbEnv(DB_CXX_NO_EXCEPTIONS));
124 1937 : fDbEnvInit = false;
125 1937 : fMockDb = false;
126 1937 : }
127 :
128 3776 : BerkeleyEnvironment::BerkeleyEnvironment(const fs::path& dir_path, bool use_shared_memory) : strPath(fs::PathToString(dir_path)), m_use_shared_memory(use_shared_memory)
129 1888 : {
130 1888 : Reset();
131 3776 : }
132 :
133 3776 : BerkeleyEnvironment::~BerkeleyEnvironment()
134 1888 : {
135 1888 : LOCK(cs_db);
136 1888 : g_dbenvs.erase(strPath);
137 1888 : Close();
138 3776 : }
139 :
140 243232 : bool BerkeleyEnvironment::Open(bilingual_str& err)
141 : {
142 243232 : if (fDbEnvInit) {
143 241304 : return true;
144 : }
145 :
146 1928 : fs::path pathIn = fs::PathFromString(strPath);
147 1928 : TryCreateDirectories(pathIn);
148 1924 : if (!LockDirectory(pathIn, ".walletlock")) {
149 12 : LogPrintf("Cannot obtain a lock on wallet directory %s. Another instance may be using it.\n", strPath);
150 12 : err = strprintf(_("Error initializing wallet database environment %s!"), fs::quoted(fs::PathToString(Directory())));
151 12 : return false;
152 : }
153 :
154 1912 : fs::path pathLogDir = pathIn / "database";
155 1912 : TryCreateDirectories(pathLogDir);
156 1912 : fs::path pathErrorFile = pathIn / "db.log";
157 1912 : LogPrintf("BerkeleyEnvironment::Open: LogDir=%s ErrorFile=%s\n", fs::PathToString(pathLogDir), fs::PathToString(pathErrorFile));
158 :
159 1912 : unsigned int nEnvFlags = 0;
160 1912 : if (!m_use_shared_memory) {
161 1912 : nEnvFlags |= DB_PRIVATE;
162 1912 : }
163 :
164 1912 : dbenv->set_lg_dir(fs::PathToString(pathLogDir).c_str());
165 1912 : dbenv->set_cachesize(0, 0x100000, 1); // 1 MiB should be enough for just the wallet
166 1912 : dbenv->set_lg_bsize(0x10000);
167 1912 : dbenv->set_lg_max(1048576);
168 1912 : dbenv->set_lk_max_locks(40000);
169 1912 : dbenv->set_lk_max_objects(40000);
170 1912 : dbenv->set_errfile(fsbridge::fopen(pathErrorFile, "a")); /// debug
171 1912 : dbenv->set_flags(DB_AUTO_COMMIT, 1);
172 1912 : dbenv->set_flags(DB_TXN_WRITE_NOSYNC, 1);
173 1912 : dbenv->log_set_config(DB_LOG_AUTO_REMOVE, 1);
174 3824 : int ret = dbenv->open(strPath.c_str(),
175 : DB_CREATE |
176 : DB_INIT_LOCK |
177 : DB_INIT_LOG |
178 : DB_INIT_MPOOL |
179 : DB_INIT_TXN |
180 : DB_THREAD |
181 1912 : DB_RECOVER |
182 1912 : nEnvFlags,
183 : S_IRUSR | S_IWUSR);
184 1912 : if (ret != 0) {
185 0 : LogPrintf("BerkeleyEnvironment::Open: Error %d opening database environment: %s\n", ret, DbEnv::strerror(ret));
186 0 : int ret2 = dbenv->close(0);
187 0 : if (ret2 != 0) {
188 0 : LogPrintf("BerkeleyEnvironment::Open: Error %d closing failed database environment: %s\n", ret2, DbEnv::strerror(ret2));
189 0 : }
190 0 : Reset();
191 0 : err = strprintf(_("Error initializing wallet database environment %s!"), fs::quoted(fs::PathToString(Directory())));
192 0 : if (ret == DB_RUNRECOVERY) {
193 0 : err += Untranslated(" ") + _("This error could occur if this wallet was not shutdown cleanly and was last loaded using a build with a newer version of Berkeley DB. If so, please use the software that last loaded this wallet");
194 0 : }
195 0 : return false;
196 : }
197 :
198 1912 : fDbEnvInit = true;
199 1912 : fMockDb = false;
200 1912 : return true;
201 243232 : }
202 :
203 : //! Construct an in-memory mock Berkeley environment for testing
204 0 : BerkeleyEnvironment::BerkeleyEnvironment() : m_use_shared_memory(false)
205 0 : {
206 0 : Reset();
207 :
208 0 : LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::MakeMock\n");
209 :
210 0 : dbenv->set_cachesize(1, 0, 1);
211 0 : dbenv->set_lg_bsize(10485760 * 4);
212 0 : dbenv->set_lg_max(10485760);
213 0 : dbenv->set_lk_max_locks(10000);
214 0 : dbenv->set_lk_max_objects(10000);
215 0 : dbenv->set_flags(DB_AUTO_COMMIT, 1);
216 0 : dbenv->log_set_config(DB_LOG_IN_MEMORY, 1);
217 0 : int ret = dbenv->open(nullptr,
218 : DB_CREATE |
219 : DB_INIT_LOCK |
220 : DB_INIT_LOG |
221 : DB_INIT_MPOOL |
222 : DB_INIT_TXN |
223 : DB_THREAD |
224 : DB_PRIVATE,
225 : S_IRUSR | S_IWUSR);
226 0 : if (ret > 0) {
227 0 : throw std::runtime_error(strprintf("BerkeleyEnvironment::MakeMock: Error %d opening database environment.", ret));
228 : }
229 :
230 0 : fDbEnvInit = true;
231 0 : fMockDb = true;
232 0 : }
233 :
234 595650 : BerkeleyBatch::SafeDbt::SafeDbt()
235 297825 : {
236 297825 : m_dbt.set_flags(DB_DBT_MALLOC);
237 595650 : }
238 :
239 1490120 : BerkeleyBatch::SafeDbt::SafeDbt(void* data, size_t size)
240 745060 : : m_dbt(data, size)
241 745060 : {
242 1490120 : }
243 :
244 2085770 : BerkeleyBatch::SafeDbt::~SafeDbt()
245 1042885 : {
246 1042885 : if (m_dbt.get_data() != nullptr) {
247 : // Clear memory, e.g. in case it was a private key
248 1035837 : memory_cleanse(m_dbt.get_data(), m_dbt.get_size());
249 : // under DB_DBT_MALLOC, data is malloced by the Dbt, but must be
250 : // freed by the caller.
251 : // https://docs.oracle.com/cd/E17275_01/html/api_reference/C/dbt.html
252 1035837 : if (m_dbt.get_flags() & DB_DBT_MALLOC) {
253 290777 : free(m_dbt.get_data());
254 290777 : }
255 1035837 : }
256 2085770 : }
257 :
258 581545 : const void* BerkeleyBatch::SafeDbt::get_data() const
259 : {
260 581545 : return m_dbt.get_data();
261 : }
262 :
263 290774 : uint32_t BerkeleyBatch::SafeDbt::get_size() const
264 : {
265 290774 : return m_dbt.get_size();
266 : }
267 :
268 1042885 : BerkeleyBatch::SafeDbt::operator Dbt*()
269 : {
270 1042885 : return &m_dbt;
271 : }
272 :
273 1536 : bool BerkeleyDatabase::Verify(bilingual_str& errorStr)
274 : {
275 1536 : fs::path walletDir = env->Directory();
276 1536 : fs::path file_path = walletDir / m_filename;
277 :
278 1536 : LogPrintf("Using BerkeleyDB version %s\n", BerkeleyDatabaseVersion());
279 1536 : LogPrintf("Using wallet %s\n", fs::PathToString(file_path));
280 :
281 1536 : if (!env->Open(errorStr)) {
282 12 : return false;
283 : }
284 :
285 1520 : if (fs::exists(file_path))
286 : {
287 748 : assert(m_refcount == 0);
288 :
289 748 : Db db(env->dbenv.get(), 0);
290 748 : const std::string strFile = fs::PathToString(m_filename);
291 748 : int result = db.verify(strFile.c_str(), nullptr, nullptr, 0);
292 748 : if (result != 0) {
293 0 : errorStr = strprintf(_("%s corrupt. Try using the wallet tool dash-wallet to salvage or restoring a backup."), fs::quoted(fs::PathToString(file_path)));
294 0 : return false;
295 : }
296 748 : }
297 : // also return true if files does not exists
298 1520 : return true;
299 1536 : }
300 :
301 1531 : void BerkeleyEnvironment::CheckpointLSN(const std::string& strFile)
302 : {
303 1531 : dbenv->txn_checkpoint(0, 0, 0);
304 1531 : if (fMockDb)
305 0 : return;
306 1531 : dbenv->lsn_reset(strFile.c_str(), 0);
307 1531 : }
308 :
309 5775 : BerkeleyDatabase::~BerkeleyDatabase()
310 5775 : {
311 1925 : if (env) {
312 1925 : LOCK(cs_db);
313 1925 : env->CloseDb(m_filename);
314 1925 : assert(!m_db);
315 1925 : size_t erased = env->m_databases.erase(m_filename);
316 1925 : assert(erased == 1);
317 1925 : env->m_fileids.erase(fs::PathToString(m_filename));
318 1925 : }
319 5775 : }
320 :
321 483290 : BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const bool read_only, bool fFlushOnCloseIn) : m_database(database)
322 483290 : {
323 : database.AddRef();
324 : database.Open();
325 : fReadOnly = read_only;
326 : fFlushOnClose = fFlushOnCloseIn;
327 : env = database.env.get();
328 : pdb = database.m_db.get();
329 : strFile = fs::PathToString(database.m_filename);
330 241645 : }
331 :
332 241645 : void BerkeleyDatabase::Open()
333 : {
334 241645 : unsigned int nFlags = DB_THREAD | DB_CREATE;
335 :
336 : {
337 241645 : LOCK(cs_db);
338 241645 : bilingual_str open_err;
339 241645 : if (!env->Open(open_err))
340 0 : throw std::runtime_error("BerkeleyDatabase: Failed to open database environment.");
341 :
342 241645 : if (m_db == nullptr) {
343 : int ret;
344 4126 : std::unique_ptr<Db> pdb_temp = std::make_unique<Db>(env->dbenv.get(), 0);
345 4126 : const std::string strFile = fs::PathToString(m_filename);
346 :
347 4126 : bool fMockDb = env->IsMock();
348 4126 : if (fMockDb) {
349 0 : DbMpoolFile* mpf = pdb_temp->get_mpf();
350 0 : ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
351 0 : if (ret != 0) {
352 0 : throw std::runtime_error(strprintf("BerkeleyDatabase: Failed to configure for no temp file backing for database %s", strFile));
353 : }
354 0 : }
355 :
356 8252 : ret = pdb_temp->open(nullptr, // Txn pointer
357 4126 : fMockDb ? nullptr : strFile.c_str(), // Filename
358 4126 : fMockDb ? strFile.c_str() : "main", // Logical db name
359 : DB_BTREE, // Database type
360 4126 : nFlags, // Flags
361 : 0);
362 :
363 4126 : if (ret != 0) {
364 0 : throw std::runtime_error(strprintf("BerkeleyDatabase: Error %d, can't open database %s", ret, strFile));
365 : }
366 :
367 : // Call CheckUniqueFileid on the containing BDB environment to
368 : // avoid BDB data consistency bugs that happen when different data
369 : // files in the same environment have the same fileid.
370 4126 : CheckUniqueFileid(*env, strFile, *pdb_temp, this->env->m_fileids[strFile]);
371 :
372 4114 : m_db.reset(pdb_temp.release());
373 :
374 4126 : }
375 241645 : }
376 241645 : }
377 :
378 163776 : void BerkeleyBatch::Flush()
379 : {
380 163776 : if (activeTxn)
381 0 : return;
382 :
383 : // Flush database activity from memory pool to disk log
384 163776 : unsigned int nMinutes = 0;
385 163776 : if (fReadOnly)
386 49 : nMinutes = 1;
387 :
388 163776 : if (env) { // env is nullptr for dummy databases (i.e. in tests). Don't actually flush if env is nullptr so we don't segfault
389 163776 : env->dbenv->txn_checkpoint(nMinutes ? m_database.m_max_log_mb * 1024 : 0, nMinutes, 0);
390 163776 : }
391 163776 : }
392 :
393 365573 : void BerkeleyDatabase::IncrementUpdateCounter()
394 : {
395 365573 : ++nUpdateCounter;
396 365573 : }
397 :
398 724850 : BerkeleyBatch::~BerkeleyBatch()
399 724850 : {
400 241633 : Close();
401 241633 : m_database.RemoveRef();
402 724850 : }
403 :
404 241682 : void BerkeleyBatch::Close()
405 : {
406 241682 : if (!pdb)
407 49 : return;
408 241633 : if (activeTxn)
409 0 : activeTxn->abort();
410 241633 : activeTxn = nullptr;
411 241633 : pdb = nullptr;
412 241633 : CloseCursor();
413 :
414 241633 : if (fFlushOnClose)
415 163597 : Flush();
416 241682 : }
417 :
418 6248 : void BerkeleyEnvironment::CloseDb(const fs::path& filename)
419 : {
420 : {
421 6248 : LOCK(cs_db);
422 6248 : auto it = m_databases.find(filename);
423 6248 : assert(it != m_databases.end());
424 6248 : BerkeleyDatabase& database = it->second.get();
425 6248 : if (database.m_db) {
426 : // Close the database handle
427 4114 : database.m_db->close(0);
428 4114 : database.m_db.reset();
429 4114 : }
430 6248 : }
431 6248 : }
432 :
433 49 : void BerkeleyEnvironment::ReloadDbEnv()
434 : {
435 : // Make sure that no Db's are in use
436 49 : AssertLockNotHeld(cs_db);
437 49 : std::unique_lock<RecursiveMutex> lock(cs_db);
438 98 : m_db_in_use.wait(lock, [this](){
439 98 : for (auto& db : m_databases) {
440 49 : if (db.second.get().m_refcount > 0) return false;
441 : }
442 49 : return true;
443 49 : });
444 :
445 49 : std::vector<fs::path> filenames;
446 98 : for (const auto& it : m_databases) {
447 49 : filenames.push_back(it.first);
448 : }
449 : // Close the individual Db's
450 98 : for (const fs::path& filename : filenames) {
451 49 : CloseDb(filename);
452 : }
453 : // Reset the environment
454 49 : Flush(true); // This will flush and close the environment
455 49 : Reset();
456 49 : bilingual_str open_err;
457 49 : Open(open_err);
458 49 : }
459 :
460 49 : bool BerkeleyDatabase::Rewrite(const char* pszSkip)
461 : {
462 49 : while (true) {
463 : {
464 49 : LOCK(cs_db);
465 49 : const std::string strFile = fs::PathToString(m_filename);
466 49 : if (m_refcount <= 0) {
467 : // Flush log data to the dat file
468 49 : env->CloseDb(m_filename);
469 49 : env->CheckpointLSN(strFile);
470 49 : m_refcount = -1;
471 :
472 49 : bool fSuccess = true;
473 49 : LogPrintf("BerkeleyBatch::Rewrite: Rewriting %s...\n", strFile);
474 49 : std::string strFileRes = strFile + ".rewrite";
475 : { // surround usage of db with extra {}
476 49 : BerkeleyBatch db(*this, true);
477 49 : std::unique_ptr<Db> pdbCopy = std::make_unique<Db>(env->dbenv.get(), 0);
478 :
479 98 : int ret = pdbCopy->open(nullptr, // Txn pointer
480 49 : strFileRes.c_str(), // Filename
481 : "main", // Logical db name
482 : DB_BTREE, // Database type
483 : DB_CREATE, // Flags
484 : 0);
485 49 : if (ret > 0) {
486 0 : LogPrintf("BerkeleyBatch::Rewrite: Can't create database file %s\n", strFileRes);
487 0 : fSuccess = false;
488 0 : }
489 :
490 49 : if (db.StartCursor()) {
491 3870 : while (fSuccess) {
492 3821 : CDataStream ssKey(SER_DISK, CLIENT_VERSION);
493 3821 : CDataStream ssValue(SER_DISK, CLIENT_VERSION);
494 : bool complete;
495 3821 : bool ret1 = db.ReadAtCursor(ssKey, ssValue, complete);
496 3821 : if (complete) {
497 49 : break;
498 3772 : } else if (!ret1) {
499 0 : fSuccess = false;
500 0 : break;
501 : }
502 3772 : if (pszSkip &&
503 0 : strncmp((const char*)ssKey.data(), pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0)
504 0 : continue;
505 3772 : if (strncmp((const char*)ssKey.data(), "\x07version", 8) == 0) {
506 : // Update version:
507 49 : ssValue.clear();
508 49 : ssValue << CLIENT_VERSION;
509 49 : }
510 3772 : Dbt datKey(ssKey.data(), ssKey.size());
511 3772 : Dbt datValue(ssValue.data(), ssValue.size());
512 3772 : int ret2 = pdbCopy->put(nullptr, &datKey, &datValue, DB_NOOVERWRITE);
513 3772 : if (ret2 > 0)
514 0 : fSuccess = false;
515 3821 : }
516 49 : db.CloseCursor();
517 49 : }
518 49 : if (fSuccess) {
519 49 : db.Close();
520 49 : env->CloseDb(m_filename);
521 49 : if (pdbCopy->close(0))
522 0 : fSuccess = false;
523 49 : } else {
524 0 : pdbCopy->close(0);
525 : }
526 49 : }
527 49 : if (fSuccess) {
528 49 : Db dbA(env->dbenv.get(), 0);
529 49 : if (dbA.remove(strFile.c_str(), nullptr, 0))
530 0 : fSuccess = false;
531 49 : Db dbB(env->dbenv.get(), 0);
532 49 : if (dbB.rename(strFileRes.c_str(), nullptr, strFile.c_str(), 0))
533 0 : fSuccess = false;
534 49 : }
535 49 : if (!fSuccess)
536 0 : LogPrintf("BerkeleyBatch::Rewrite: Failed to rewrite database file %s\n", strFileRes);
537 49 : return fSuccess;
538 49 : }
539 49 : }
540 0 : UninterruptibleSleep(std::chrono::milliseconds{100});
541 : }
542 49 : }
543 :
544 :
545 3844 : void BerkeleyEnvironment::Flush(bool fShutdown)
546 : {
547 3844 : const auto start{SteadyClock::now()};
548 : // Flush log data to the actual data file on all files that are not in use
549 3844 : LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: [%s] Flush(%s)%s\n", strPath, fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started");
550 3844 : if (!fDbEnvInit)
551 1165 : return;
552 : {
553 2679 : LOCK(cs_db);
554 2679 : bool no_dbs_accessed = true;
555 5434 : for (auto& db_it : m_databases) {
556 2755 : const fs::path& filename = db_it.first;
557 2755 : int nRefCount = db_it.second.get().m_refcount;
558 2755 : if (nRefCount < 0) continue;
559 2716 : const std::string strFile = fs::PathToString(filename);
560 2716 : LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: Flushing %s (refcount = %d)...\n", strFile, nRefCount);
561 2716 : if (nRefCount == 0) {
562 : // Move log data to the dat file
563 2694 : CloseDb(filename);
564 2694 : LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s checkpoint\n", strFile);
565 2694 : dbenv->txn_checkpoint(0, 0, 0);
566 2694 : LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s detach\n", strFile);
567 2694 : if (!fMockDb)
568 2694 : dbenv->lsn_reset(strFile.c_str(), 0);
569 2694 : LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s closed\n", strFile);
570 2694 : nRefCount = -1;
571 2694 : } else {
572 22 : no_dbs_accessed = false;
573 : }
574 2716 : }
575 2679 : LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: Flush(%s)%s took %15dms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started", Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
576 2679 : if (fShutdown) {
577 : char** listp;
578 1230 : if (no_dbs_accessed) {
579 1230 : dbenv->log_archive(&listp, DB_ARCH_REMOVE);
580 1230 : Close();
581 1230 : if (!fMockDb) {
582 1230 : fs::remove_all(fs::PathFromString(strPath) / "database");
583 1230 : }
584 1230 : }
585 1230 : }
586 2679 : }
587 3844 : }
588 :
589 1379 : bool BerkeleyDatabase::PeriodicFlush()
590 : {
591 : // Don't flush if we can't acquire the lock.
592 1379 : TRY_LOCK(cs_db, lockDb);
593 1379 : if (!lockDb) return false;
594 :
595 : // Don't flush if any databases are in use
596 2758 : for (auto& it : env->m_databases) {
597 1379 : if (it.second.get().m_refcount > 0) return false;
598 : }
599 :
600 : // Don't flush if there haven't been any batch writes for this database.
601 1379 : if (m_refcount < 0) return false;
602 :
603 1379 : const std::string strFile = fs::PathToString(m_filename);
604 1379 : LogPrint(BCLog::WALLETDB, "Flushing %s\n", strFile);
605 1379 : const auto start{SteadyClock::now()};
606 :
607 : // Flush wallet file so it's self contained
608 1379 : env->CloseDb(m_filename);
609 1379 : env->CheckpointLSN(strFile);
610 1379 : m_refcount = -1;
611 :
612 1379 : LogPrint(BCLog::WALLETDB, "Flushed %s %dms\n", strFile, Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
613 :
614 1379 : return true;
615 1379 : }
616 :
617 103 : bool BerkeleyDatabase::Backup(const std::string& strDest) const
618 : {
619 103 : const std::string strFile = fs::PathToString(m_filename);
620 103 : while (true)
621 : {
622 : {
623 103 : LOCK(cs_db);
624 103 : if (m_refcount <= 0)
625 : {
626 : // Flush log data to the dat file
627 103 : env->CloseDb(m_filename);
628 103 : env->CheckpointLSN(strFile);
629 :
630 : // Copy wallet file
631 103 : fs::path pathSrc = env->Directory() / m_filename;
632 103 : fs::path pathDest(fs::PathFromString(strDest));
633 103 : if (fs::is_directory(pathDest))
634 6 : pathDest /= m_filename;
635 :
636 : try {
637 103 : if (fs::exists(pathDest) && fs::equivalent(pathSrc, pathDest)) {
638 12 : LogPrintf("cannot backup to wallet source file %s\n", fs::PathToString(pathDest));
639 12 : return false;
640 : }
641 :
642 91 : fs::copy_file(pathSrc, pathDest, fs::copy_options::overwrite_existing);
643 91 : LogPrintf("copied %s to %s\n", strFile, fs::PathToString(pathDest));
644 91 : return true;
645 0 : } catch (const fs::filesystem_error& e) {
646 0 : LogPrintf("error copying %s to %s - %s\n", strFile, fs::PathToString(pathDest), fsbridge::get_filesystem_error_message(e));
647 0 : return false;
648 0 : }
649 103 : }
650 103 : }
651 0 : UninterruptibleSleep(std::chrono::milliseconds{100});
652 : }
653 103 : }
654 :
655 2590 : void BerkeleyDatabase::Flush()
656 : {
657 2590 : env->Flush(false);
658 2590 : }
659 :
660 1205 : void BerkeleyDatabase::Close()
661 : {
662 1205 : env->Flush(true);
663 1205 : }
664 :
665 49 : void BerkeleyDatabase::ReloadDbEnv()
666 : {
667 49 : env->ReloadDbEnv();
668 49 : }
669 :
670 1588 : bool BerkeleyBatch::StartCursor()
671 : {
672 1588 : assert(!m_cursor);
673 1588 : if (!pdb)
674 0 : return false;
675 1588 : int ret = pdb->cursor(nullptr, &m_cursor, 0);
676 1588 : return ret == 0;
677 1588 : }
678 :
679 134647 : bool BerkeleyBatch::ReadAtCursor(CDataStream& ssKey, CDataStream& ssValue, bool& complete)
680 : {
681 134647 : complete = false;
682 134647 : if (m_cursor == nullptr) return false;
683 : // Read at cursor
684 134647 : SafeDbt datKey;
685 134647 : SafeDbt datValue;
686 134647 : int ret = m_cursor->get(datKey, datValue, DB_NEXT);
687 134647 : if (ret == DB_NOTFOUND) {
688 1588 : complete = true;
689 1588 : }
690 134647 : if (ret != 0)
691 1588 : return false;
692 133059 : else if (datKey.get_data() == nullptr || datValue.get_data() == nullptr)
693 0 : return false;
694 :
695 : // Convert to streams
696 133059 : ssKey.SetType(SER_DISK);
697 133059 : ssKey.clear();
698 133059 : ssKey.write(SpanFromDbt(datKey));
699 133059 : ssValue.SetType(SER_DISK);
700 133059 : ssValue.clear();
701 133059 : ssValue.write(SpanFromDbt(datValue));
702 133059 : return true;
703 134647 : }
704 :
705 243221 : void BerkeleyBatch::CloseCursor()
706 : {
707 243221 : if (!m_cursor) return;
708 1588 : m_cursor->close();
709 1588 : m_cursor = nullptr;
710 243221 : }
711 :
712 64 : bool BerkeleyBatch::TxnBegin()
713 : {
714 64 : if (!pdb || activeTxn)
715 0 : return false;
716 64 : DbTxn* ptxn = env->TxnBegin();
717 64 : if (!ptxn)
718 0 : return false;
719 64 : activeTxn = ptxn;
720 64 : return true;
721 64 : }
722 :
723 56 : bool BerkeleyBatch::TxnCommit()
724 : {
725 56 : if (!pdb || !activeTxn)
726 0 : return false;
727 56 : int ret = activeTxn->commit(0);
728 56 : activeTxn = nullptr;
729 56 : return (ret == 0);
730 56 : }
731 :
732 8 : bool BerkeleyBatch::TxnAbort()
733 : {
734 8 : if (!pdb || !activeTxn)
735 0 : return false;
736 8 : int ret = activeTxn->abort();
737 8 : activeTxn = nullptr;
738 8 : return (ret == 0);
739 8 : }
740 :
741 3709 : bool BerkeleyDatabaseSanityCheck()
742 : {
743 : int major, minor;
744 3709 : DbEnv::version(&major, &minor, nullptr);
745 :
746 : /* If the major version differs, or the minor version of library is *older*
747 : * than the header that was compiled against, flag an error.
748 : */
749 3709 : if (major != DB_VERSION_MAJOR || minor < DB_VERSION_MINOR) {
750 0 : LogPrintf("BerkeleyDB database version conflict: header version is %d.%d, library version is %d.%d\n",
751 : DB_VERSION_MAJOR, DB_VERSION_MINOR, major, minor);
752 0 : return false;
753 : }
754 :
755 3709 : return true;
756 3709 : }
757 :
758 1536 : std::string BerkeleyDatabaseVersion()
759 : {
760 1536 : return DbEnv::version(nullptr, nullptr, nullptr);
761 : }
762 :
763 28525 : bool BerkeleyBatch::ReadKey(CDataStream&& key, CDataStream& value)
764 : {
765 28525 : if (!pdb)
766 0 : return false;
767 :
768 28525 : SafeDbt datKey(key.data(), key.size());
769 :
770 28525 : SafeDbt datValue;
771 28525 : int ret = pdb->get(activeTxn, datKey, datValue, 0);
772 28525 : if (ret == 0 && datValue.get_data() != nullptr) {
773 24653 : value.clear();
774 24653 : value.write(SpanFromDbt(datValue));
775 24653 : return true;
776 : }
777 3872 : return false;
778 28525 : }
779 :
780 350157 : bool BerkeleyBatch::WriteKey(CDataStream&& key, CDataStream&& value, bool overwrite)
781 : {
782 350157 : if (!pdb)
783 0 : return false;
784 350157 : if (fReadOnly)
785 0 : assert(!"Write called on database in read-only mode");
786 :
787 350157 : SafeDbt datKey(key.data(), key.size());
788 :
789 350157 : SafeDbt datValue(value.data(), value.size());
790 :
791 350157 : int ret = pdb->put(activeTxn, datKey, datValue, (overwrite ? 0 : DB_NOOVERWRITE));
792 350157 : return (ret == 0);
793 350157 : }
794 :
795 16221 : bool BerkeleyBatch::EraseKey(CDataStream&& key)
796 : {
797 16221 : if (!pdb)
798 0 : return false;
799 16221 : if (fReadOnly)
800 0 : assert(!"Erase called on database in read-only mode");
801 :
802 16221 : SafeDbt datKey(key.data(), key.size());
803 :
804 16221 : int ret = pdb->del(activeTxn, datKey, 0);
805 16221 : return (ret == 0 || ret == DB_NOTFOUND);
806 16221 : }
807 :
808 0 : bool BerkeleyBatch::HasKey(CDataStream&& key)
809 : {
810 0 : if (!pdb)
811 0 : return false;
812 :
813 0 : SafeDbt datKey(key.data(), key.size());
814 :
815 0 : int ret = pdb->exists(activeTxn, datKey, 0);
816 0 : return ret == 0;
817 0 : }
818 :
819 1 : bool BerkeleyBatch::ErasePrefix(Span<const std::byte> prefix)
820 : {
821 1 : if (!pdb)
822 0 : return false;
823 1 : if (!TxnBegin()) return false;
824 1 : Dbc* cursor = nullptr;
825 : // Transaction argument to cursor is needed when using the cursor to
826 : // write to the database (delete in this case).
827 1 : int ret = pdb->cursor(activeTxn, &cursor, 0);
828 1 : if (ret != 0) {
829 0 : TxnAbort();
830 0 : return false;
831 : }
832 : // const_cast is safe below even though prefix_key is an in/out parameter,
833 : // because we are not using the DB_DBT_USERMEM flag, so BDB will allocate
834 : // and return a different output data pointer
835 1 : Dbt prefix_key{const_cast<std::byte*>(prefix.data()), static_cast<uint32_t>(prefix.size())}, prefix_value{};
836 1 : ret = cursor->get(&prefix_key, &prefix_value, DB_SET_RANGE);
837 3 : for (int flag{DB_CURRENT}; ret == 0; flag = DB_NEXT) {
838 3 : SafeDbt key, value;
839 3 : ret = cursor->get(key, value, flag);
840 3 : if (ret != 0 || key.get_size() < prefix.size() || memcmp(key.get_data(), prefix.data(), prefix.size()) != 0) break;
841 2 : ret = cursor->del(0);
842 3 : }
843 1 : cursor->close();
844 2 : return TxnCommit() && (ret == 0 || ret == DB_NOTFOUND);
845 1 : }
846 :
847 241645 : void BerkeleyDatabase::AddRef()
848 : {
849 241645 : LOCK(cs_db);
850 241645 : if (m_refcount < 0) {
851 1428 : m_refcount = 1;
852 1428 : } else {
853 240217 : m_refcount++;
854 : }
855 241645 : }
856 :
857 241633 : void BerkeleyDatabase::RemoveRef()
858 : {
859 241633 : LOCK(cs_db);
860 241633 : m_refcount--;
861 241633 : if (env) env->m_db_in_use.notify_all();
862 241633 : }
863 :
864 241596 : std::unique_ptr<DatabaseBatch> BerkeleyDatabase::MakeBatch(bool flush_on_close)
865 : {
866 241596 : return std::make_unique<BerkeleyBatch>(*this, false, flush_on_close);
867 : }
868 :
869 1929 : std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error)
870 : {
871 1929 : fs::path data_file = BDBDataFile(path);
872 1929 : std::unique_ptr<BerkeleyDatabase> db;
873 : {
874 1929 : LOCK(cs_db); // Lock env.m_databases until insert in BerkeleyDatabase constructor
875 1929 : fs::path data_filename = data_file.filename();
876 1929 : std::shared_ptr<BerkeleyEnvironment> env = GetBerkeleyEnv(data_file.parent_path(), options.use_shared_memory);
877 1929 : if (env->m_databases.count(data_filename)) {
878 4 : error = Untranslated(strprintf("Refusing to load database. Data file '%s' is already loaded.", fs::PathToString(env->Directory() / data_filename)));
879 4 : status = DatabaseStatus::FAILED_ALREADY_LOADED;
880 4 : return nullptr;
881 : }
882 1925 : db = std::make_unique<BerkeleyDatabase>(std::move(env), std::move(data_filename), options);
883 1929 : }
884 :
885 1925 : if (options.verify && !db->Verify(error)) {
886 12 : status = DatabaseStatus::FAILED_VERIFY;
887 12 : return nullptr;
888 : }
889 :
890 1909 : status = DatabaseStatus::SUCCESS;
891 1909 : return db;
892 1929 : }
893 : } // namespace wallet
|