Skip to content

Commit 482d255

Browse files
committed
Merge bitcoin/bitcoin#32736: wallet: Correct dir iteration error handling
272cd09 log: Use warning level while scanning wallet dir (MarcoFalke) 1777644 qa, wallet: Verify warning when failing to scan (Hodlinator) 893e51f wallet: Correct dir iteration error handling (Hodlinator) Pull request description: Make wallet DB properly detect and report failure to scan wallet directory. Seems to have been broken since moving from Boost to `std::filesystem`. Found while reviewing: bitcoin/bitcoin#31410 (review) ACKs for top commit: achow101: ACK 272cd09 maflcko: re-ACK 272cd09 🍽 rkrux: tACK 272cd09 Tree-SHA512: 969afde2e37f885ed0c823dc36d2dbeaa0378639849c6a26f8ac67b4f1997eea95bbcae6d58aef5b716807210f37eb166c0cda7ba1d6caffd34249970833af3a
2 parents 154b98a + 272cd09 commit 482d255

File tree

2 files changed

+31
-14
lines changed

2 files changed

+31
-14
lines changed

src/wallet/db.cpp

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,7 @@ std::vector<std::pair<fs::path, std::string>> ListDatabases(const fs::path& wall
2626
std::error_code ec;
2727

2828
for (auto it = fs::recursive_directory_iterator(wallet_dir, ec); it != fs::recursive_directory_iterator(); it.increment(ec)) {
29-
if (ec) {
30-
if (fs::is_directory(*it)) {
31-
it.disable_recursion_pending();
32-
LogPrintf("%s: %s %s -- skipping.\n", __func__, ec.message(), fs::PathToString(it->path()));
33-
} else {
34-
LogPrintf("%s: %s %s\n", __func__, ec.message(), fs::PathToString(it->path()));
35-
}
36-
continue;
37-
}
38-
29+
assert(!ec); // Loop should exit on error.
3930
try {
4031
const fs::path path{it->path().lexically_relative(wallet_dir)};
4132

@@ -65,10 +56,18 @@ std::vector<std::pair<fs::path, std::string>> ListDatabases(const fs::path& wall
6556
}
6657
}
6758
} catch (const std::exception& e) {
68-
LogPrintf("%s: Error scanning %s: %s\n", __func__, fs::PathToString(it->path()), e.what());
59+
LogWarning("Error while scanning wallet dir item: %s [%s].", e.what(), fs::PathToString(it->path()));
6960
it.disable_recursion_pending();
7061
}
7162
}
63+
if (ec) {
64+
// Loop could have exited with an error due to one of:
65+
// * wallet_dir itself not being scannable.
66+
// * increment() failure. (Observed on Windows native builds when
67+
// removing the ACL read permissions of a wallet directory after the
68+
// process started).
69+
LogWarning("Error scanning directory entries under %s: %s", fs::PathToString(wallet_dir), ec.message());
70+
}
7271

7372
return paths;
7473
}
@@ -100,7 +99,7 @@ bool IsBDBFile(const fs::path& path)
10099
// This check also prevents opening lock files.
101100
std::error_code ec;
102101
auto size = fs::file_size(path, ec);
103-
if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), fs::PathToString(path));
102+
if (ec) LogWarning("Error reading file_size: %s [%s]", ec.message(), fs::PathToString(path));
104103
if (size < 4096) return false;
105104

106105
std::ifstream file{path, std::ios::binary};
@@ -124,7 +123,7 @@ bool IsSQLiteFile(const fs::path& path)
124123
// A SQLite Database file is at least 512 bytes.
125124
std::error_code ec;
126125
auto size = fs::file_size(path, ec);
127-
if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), fs::PathToString(path));
126+
if (ec) LogWarning("Error reading file_size: %s [%s]", ec.message(), fs::PathToString(path));
128127
if (size < 512) return false;
129128

130129
std::ifstream file{path, std::ios::binary};

test/functional/wallet_multiwallet.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,24 @@ def wallet_file(name):
7878
self.stop_nodes()
7979
assert_equal(os.path.isfile(wallet_dir(self.default_wallet_name, self.wallet_data_filename)), True)
8080

81+
self.log.info("Verify warning is emitted when failing to scan the wallets directory")
82+
if platform.system() == 'Windows':
83+
self.log.warning('Skipping test involving chmod as Windows does not support it.')
84+
elif os.geteuid() == 0:
85+
self.log.warning('Skipping test involving chmod as it requires a non-root user.')
86+
else:
87+
self.start_node(0)
88+
with self.nodes[0].assert_debug_log(unexpected_msgs=['Error scanning directory entries under'], expected_msgs=[]):
89+
result = self.nodes[0].listwalletdir()
90+
assert_equal(result, {'wallets': [{'name': 'default_wallet', 'warnings': []}]})
91+
os.chmod(data_dir('wallets'), 0)
92+
with self.nodes[0].assert_debug_log(expected_msgs=['Error scanning directory entries under']):
93+
result = self.nodes[0].listwalletdir()
94+
assert_equal(result, {'wallets': []})
95+
self.stop_node(0)
96+
# Restore permissions
97+
os.chmod(data_dir('wallets'), stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
98+
8199
# create symlink to verify wallet directory path can be referenced
82100
# through symlink
83101
os.mkdir(wallet_dir('w7'))
@@ -129,7 +147,7 @@ def wallet_file(name):
129147
os.mkdir(wallet_dir('no_access'))
130148
os.chmod(wallet_dir('no_access'), 0)
131149
try:
132-
with self.nodes[0].assert_debug_log(expected_msgs=['Error scanning']):
150+
with self.nodes[0].assert_debug_log(expected_msgs=["Error while scanning wallet dir"]):
133151
walletlist = self.nodes[0].listwalletdir()['wallets']
134152
finally:
135153
# Need to ensure access is restored for cleanup

0 commit comments

Comments
 (0)