util: Check write failures before renaming settings.json

In WriteSettings(), verify that writing to the stream and closing it
succeeded before returning true. This prevents RenameOver() from replacing
a valid settings.json with a corrupted or zero-byte file when write limits
or a full disk are encountered.

Additionally, update the ReadSettings() parse failure message to mention
power loss, full disk, or storage error as possible causes.

Fixes #35373

Github-Pull: #35384
Rebased-From: 0654511e1b
This commit is contained in:
Shrey
2026-05-28 21:05:09 +05:30
committed by fanquake
parent 1ee11d8ba6
commit ca00827fab
3 changed files with 11 additions and 3 deletions

View File

@@ -86,7 +86,7 @@ bool ReadSettings(const fs::path& path, std::map<std::string, SettingsValue>& va
SettingsValue in;
if (!in.read(std::string{std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()})) {
errors.emplace_back(strprintf("Settings file %s does not contain valid JSON. This is probably caused by disk corruption or a crash, "
errors.emplace_back(strprintf("Settings file %s does not contain valid JSON. This may be caused by a crash, power loss, full disk, or storage error, "
"and can be fixed by removing the file, which will reset settings to default values.",
fs::PathToString(path)));
return false;
@@ -139,7 +139,15 @@ bool WriteSettings(const fs::path& path,
return false;
}
file << out.write(/* prettyIndent= */ 4, /* indentLevel= */ 1) << std::endl;
if (file.fail()) {
errors.emplace_back(strprintf("Error: Unable to write settings file %s", fs::PathToString(path)));
return false;
}
file.close();
if (file.fail()) {
errors.emplace_back(strprintf("Error: Unable to close settings file %s", fs::PathToString(path)));
return false;
}
return true;
}

View File

@@ -101,7 +101,7 @@ BOOST_AUTO_TEST_CASE(ReadWrite)
// Check invalid json not allowed
WriteText(path, R"(invalid json)");
BOOST_CHECK(!common::ReadSettings(path, values, errors));
std::vector<std::string> fail_parse = {strprintf("Settings file %s does not contain valid JSON. This is probably caused by disk corruption or a crash, "
std::vector<std::string> fail_parse = {strprintf("Settings file %s does not contain valid JSON. This may be caused by a crash, power loss, full disk, or storage error, "
"and can be fixed by removing the file, which will reset settings to default values.",
fs::PathToString(path))};
BOOST_CHECK_EQUAL_COLLECTIONS(errors.begin(), errors.end(), fail_parse.begin(), fail_parse.end());

View File

@@ -73,7 +73,7 @@ class SettingsTest(BitcoinTestFramework):
# Test invalid json
with settings.open("w") as fp:
fp.write("invalid json")
node.assert_start_raises_init_error(expected_msg='does not contain valid JSON. This is probably caused by disk corruption or a crash', match=ErrorMatch.PARTIAL_REGEX)
node.assert_start_raises_init_error(expected_msg='does not contain valid JSON. This may be caused by a crash, power loss, full disk, or storage error', match=ErrorMatch.PARTIAL_REGEX)
# Test invalid json object
with settings.open("w") as fp: