From b3e27d9a880c8b9a321ee372809b441efb0f9c92 Mon Sep 17 00:00:00 2001 From: pasta Date: Wed, 7 Oct 2026 01:35:08 -0500 Subject: [PATCH] backport: partial bitcoin#34156 restore only owned directories Adapt 4ed0693a3f2a427ef9e7ad016930ec29fa244995. Keep recursive cleanup for newly created Dash BDB directories, which can contain verification auxiliaries. Existing-directory restore and the remaining migration changes/tests in Bitcoin #34156 are outside this partial backport. --- src/wallet/wallet.cpp | 5 ++++- test/functional/wallet_backup.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 2a42bcfe7fa5..dcc967cd758b 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -502,6 +502,7 @@ std::shared_ptr RestoreWallet(WalletContext& context, const fs::path& b const fs::path wallet_path = fsbridge::AbsPathJoin(GetWalletDir(), fs::u8path(wallet_name)); auto wallet_file = wallet_path / "wallet.dat"; std::shared_ptr wallet; + bool created_parent_dir = false; try { if (!fs::exists(backup_file)) { @@ -516,6 +517,7 @@ std::shared_ptr RestoreWallet(WalletContext& context, const fs::path& b return nullptr; } + created_parent_dir = true; fs::copy_file(backup_file, wallet_file, fs::copy_options::none); wallet = LoadWallet(context, wallet_name, load_on_start, options, status, error, warnings); @@ -523,8 +525,9 @@ std::shared_ptr RestoreWallet(WalletContext& context, const fs::path& b assert(!wallet); if (!error.empty()) error += Untranslated("\n"); error += strprintf(Untranslated("Unexpected exception: %s"), e.what()); + status = DatabaseStatus::FAILED_LOAD; } - if (!wallet) { + if (!wallet && created_parent_dir) { fs::remove_all(wallet_path); } diff --git a/test/functional/wallet_backup.py b/test/functional/wallet_backup.py index 13d91a6b1802..b137f34f460c 100755 --- a/test/functional/wallet_backup.py +++ b/test/functional/wallet_backup.py @@ -119,6 +119,18 @@ def restore_invalid_wallet(self): assert_raises_rpc_error(-18, error_message, node.restorewallet, wallet_name, invalid_wallet_file) assert not os.path.exists(not_created_wallet_file) + def restore_corrupt_bdb_wallet(self): + if self.options.descriptors: + return + node = self.nodes[3] + backup_file = os.path.join(self.nodes[0].datadir, 'wallet.bak') + corrupt_file = backup_file + '.corrupt' + with open(backup_file, 'rb') as source, open(corrupt_file, 'wb') as dest: + dest.write(source.read(4096)) + wallet_name = 'restore_corrupt_bdb' + assert_raises_rpc_error(-4, 'corrupt', node.restorewallet, wallet_name, corrupt_file) + assert not os.path.exists(os.path.join(node.datadir, self.chain, 'wallets', wallet_name)) + def restore_nonexistent_wallet(self): node = self.nodes[3] nonexistent_wallet_file = os.path.join(self.nodes[0].datadir, 'nonexistent_wallet.bak') @@ -127,6 +139,24 @@ def restore_nonexistent_wallet(self): not_created_wallet_file = os.path.join(node.datadir, self.chain, 'wallets', wallet_name) assert not os.path.exists(not_created_wallet_file) + def restore_preserves_existing_directory(self): + node = self.nodes[3] + wallet_dir = os.path.join(node.datadir, self.chain, 'wallets') + backup_file = os.path.join(self.nodes[0].datadir, 'wallet.bak') + invalid_path = os.path.join(node.datadir, 'x' * 256) + for wallet_name in ('restore_existing', os.path.join(node.datadir, 'restore_absolute')): + destination = os.path.join(wallet_dir, wallet_name) + sentinel = os.path.join(destination, 'nested', 'sentinel') + os.makedirs(os.path.dirname(sentinel)) + with open(sentinel, 'w', encoding='utf8') as f: + f.write('preserve existing data') + assert_raises_rpc_error(-4, 'Unexpected exception:', node.restorewallet, wallet_name, invalid_path) + with open(sentinel, encoding='utf8') as f: + assert_equal(f.read(), 'preserve existing data') + assert_raises_rpc_error(-8, 'Backup file does not exist', node.restorewallet, wallet_name, backup_file + '.missing') + assert_raises_rpc_error(-36, 'Database already exists', node.restorewallet, wallet_name, backup_file) + assert os.path.isfile(sentinel) + def restore_wallet_existent_name(self): node = self.nodes[3] backup_file = os.path.join(self.nodes[0].datadir, 'wallet.bak') @@ -188,6 +218,8 @@ def run_test(self): self.restore_invalid_wallet() self.restore_nonexistent_wallet() + self.restore_preserves_existing_directory() + self.restore_corrupt_bdb_wallet() backup_file_0 = os.path.join(self.nodes[0].datadir, 'wallet.bak') backup_file_1 = os.path.join(self.nodes[1].datadir, 'wallet.bak')