Skip to content

Commit 8bb34f0

Browse files
committed
Explicitly close all AutoFiles that have been written
There is no way to report a close error from `AutoFile` destructor. Such an error could be serious if the file has been written to because it may mean the file is now corrupted (same as if write fails). So, change all users of `AutoFile` that use it to write data to explicitly close the file and handle a possible error.
1 parent a69c409 commit 8bb34f0

15 files changed

+121
-38
lines changed

src/addrdb.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <univalue.h>
2525
#include <util/fs.h>
2626
#include <util/fs_helpers.h>
27+
#include <util/syserror.h>
2728
#include <util/translation.h>
2829

2930
namespace {
@@ -61,25 +62,29 @@ bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data
6162
FILE *file = fsbridge::fopen(pathTmp, "wb");
6263
AutoFile fileout{file};
6364
if (fileout.IsNull()) {
64-
fileout.fclose();
6565
remove(pathTmp);
6666
LogError("%s: Failed to open file %s\n", __func__, fs::PathToString(pathTmp));
6767
return false;
6868
}
6969

7070
// Serialize
7171
if (!SerializeDB(fileout, data)) {
72-
fileout.fclose();
72+
(void)fileout.fclose();
7373
remove(pathTmp);
7474
return false;
7575
}
7676
if (!fileout.Commit()) {
77-
fileout.fclose();
77+
(void)fileout.fclose();
7878
remove(pathTmp);
7979
LogError("%s: Failed to flush file %s\n", __func__, fs::PathToString(pathTmp));
8080
return false;
8181
}
82-
fileout.fclose();
82+
if (fileout.fclose() != 0) {
83+
const int errno_save{errno};
84+
remove(pathTmp);
85+
LogError("Failed to close file %s after commit: %s", fs::PathToString(pathTmp), SysErrorString(errno_save));
86+
return false;
87+
}
8388

8489
// replace existing file, if any, with new file
8590
if (!RenameOver(pathTmp, path)) {

src/bench/streams_findbyte.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ static void FindByte(benchmark::Bench& bench)
2828
});
2929

3030
// Cleanup
31-
file.fclose();
31+
assert(file.fclose() == 0);
3232
fs::remove("streams_tmp");
3333
}
3434

src/index/blockfilterindex.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <node/blockstorage.h>
1414
#include <undo.h>
1515
#include <util/fs_helpers.h>
16+
#include <util/syserror.h>
1617

1718
/* The index database stores three items for each block: the disk location of the encoded filter,
1819
* its dSHA256 hash, and the header. Those belonging to blocks on the active chain are indexed by
@@ -159,6 +160,11 @@ bool BlockFilterIndex::CustomCommit(CDBBatch& batch)
159160
}
160161
if (!file.Commit()) {
161162
LogError("%s: Failed to commit filter file %d\n", __func__, pos.nFile);
163+
(void)file.fclose();
164+
return false;
165+
}
166+
if (file.fclose() != 0) {
167+
LogError("Failed to close filter file %d after commit: %s", pos.nFile, SysErrorString(errno));
162168
return false;
163169
}
164170

@@ -213,6 +219,11 @@ size_t BlockFilterIndex::WriteFilterToDisk(FlatFilePos& pos, const BlockFilter&
213219
}
214220
if (!last_file.Commit()) {
215221
LogPrintf("%s: Failed to commit filter file %d\n", __func__, pos.nFile);
222+
(void)last_file.fclose();
223+
return 0;
224+
}
225+
if (last_file.fclose() != 0) {
226+
LogError("Failed to close filter file %d after commit: %s", pos.nFile, SysErrorString(errno));
216227
return 0;
217228
}
218229

@@ -235,6 +246,12 @@ size_t BlockFilterIndex::WriteFilterToDisk(FlatFilePos& pos, const BlockFilter&
235246
}
236247

237248
fileout << filter.GetBlockHash() << filter.GetEncodedFilter();
249+
250+
if (fileout.fclose() != 0) {
251+
LogError("Failed to close filter file %d: %s", pos.nFile, SysErrorString(errno));
252+
return 0;
253+
}
254+
238255
return data_size;
239256
}
240257

src/net.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4005,6 +4005,11 @@ static void CaptureMessageToFile(const CAddress& addr,
40054005
uint32_t size = data.size();
40064006
ser_writedata32(f, size);
40074007
f << data;
4008+
4009+
if (f.fclose() != 0) {
4010+
throw std::ios_base::failure(
4011+
strprintf("Error closing %s after write, file contents are likely incomplete", fs::PathToString(path)));
4012+
}
40084013
}
40094014

40104015
std::function<void(const CAddress& addr,

src/node/blockstorage.cpp

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include <util/fs.h>
3434
#include <util/signalinterrupt.h>
3535
#include <util/strencodings.h>
36+
#include <util/syserror.h>
3637
#include <util/translation.h>
3738
#include <validation.h>
3839

@@ -941,13 +942,13 @@ bool BlockManager::WriteBlockUndo(const CBlockUndo& blockundo, BlockValidationSt
941942
return false;
942943
}
943944

945+
// Open history file to append
946+
AutoFile file{OpenUndoFile(pos)};
947+
if (file.IsNull()) {
948+
LogError("OpenUndoFile failed for %s while writing block undo", pos.ToString());
949+
return FatalError(m_opts.notifications, state, _("Failed to write undo data."));
950+
}
944951
{
945-
// Open history file to append
946-
AutoFile file{OpenUndoFile(pos)};
947-
if (file.IsNull()) {
948-
LogError("OpenUndoFile failed for %s while writing block undo", pos.ToString());
949-
return FatalError(m_opts.notifications, state, _("Failed to write undo data."));
950-
}
951952
BufferedWriter fileout{file};
952953

953954
// Write index header
@@ -960,8 +961,13 @@ bool BlockManager::WriteBlockUndo(const CBlockUndo& blockundo, BlockValidationSt
960961
// Write undo data & checksum
961962
fileout << blockundo << hasher.GetHash();
962963
}
964+
// BufferedWriter will flush pending data to file when fileout goes out of scope.
965+
}
963966

964-
fileout.flush(); // Make sure `AutoFile`/`BufferedWriter` go out of scope before we call `FlushUndoFile`
967+
// Make sure that the file is closed before we call `FlushUndoFile`.
968+
if (file.fclose() != 0) {
969+
LogError("Failed to close block undo file %s: %s", pos.ToString(), SysErrorString(errno));
970+
return FatalError(m_opts.notifications, state, _("Failed to close block undo file."));
965971
}
966972

967973
// rev files are written in block height order, whereas blk files are written as blocks come in (often out of order)
@@ -1094,13 +1100,22 @@ FlatFilePos BlockManager::WriteBlock(const CBlock& block, int nHeight)
10941100
m_opts.notifications.fatalError(_("Failed to write block."));
10951101
return FlatFilePos();
10961102
}
1097-
BufferedWriter fileout{file};
1103+
{
1104+
BufferedWriter fileout{file};
1105+
1106+
// Write index header
1107+
fileout << GetParams().MessageStart() << block_size;
1108+
pos.nPos += STORAGE_HEADER_BYTES;
1109+
// Write block
1110+
fileout << TX_WITH_WITNESS(block);
1111+
}
1112+
1113+
if (file.fclose() != 0) {
1114+
LogError("Failed to close block file %s: %s", pos.ToString(), SysErrorString(errno));
1115+
m_opts.notifications.fatalError(_("Failed to close file when writing block."));
1116+
return FlatFilePos();
1117+
}
10981118

1099-
// Write index header
1100-
fileout << GetParams().MessageStart() << block_size;
1101-
pos.nPos += STORAGE_HEADER_BYTES;
1102-
// Write block
1103-
fileout << TX_WITH_WITNESS(block);
11041119
return pos;
11051120
}
11061121

@@ -1143,6 +1158,11 @@ static auto InitBlocksdirXorKey(const BlockManager::Options& opts)
11431158
#endif
11441159
)};
11451160
xor_key_file << xor_key;
1161+
if (xor_key_file.fclose() != 0) {
1162+
throw std::runtime_error{strprintf("Error closing XOR key file %s: %s",
1163+
fs::PathToString(xor_key_path),
1164+
SysErrorString(errno))};
1165+
}
11461166
}
11471167
// If the user disabled the key, it must be zero.
11481168
if (!opts.use_xor && xor_key != decltype(xor_key){}) {

src/node/mempool_persist.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <util/fs.h>
1818
#include <util/fs_helpers.h>
1919
#include <util/signalinterrupt.h>
20+
#include <util/syserror.h>
2021
#include <util/time.h>
2122
#include <validation.h>
2223

@@ -168,7 +169,8 @@ bool DumpMempool(const CTxMemPool& pool, const fs::path& dump_path, FopenFn mock
168169

169170
auto mid = SteadyClock::now();
170171

171-
AutoFile file{mockable_fopen_function(dump_path + ".new", "wb")};
172+
const fs::path file_fspath{dump_path + ".new"};
173+
AutoFile file{mockable_fopen_function(file_fspath, "wb")};
172174
if (file.IsNull()) {
173175
return false;
174176
}
@@ -199,9 +201,14 @@ bool DumpMempool(const CTxMemPool& pool, const fs::path& dump_path, FopenFn mock
199201
LogInfo("Writing %d unbroadcast transactions to file.\n", unbroadcast_txids.size());
200202
file << unbroadcast_txids;
201203

202-
if (!skip_file_commit && !file.Commit())
204+
if (!skip_file_commit && !file.Commit()) {
205+
(void)file.fclose();
203206
throw std::runtime_error("Commit failed");
204-
file.fclose();
207+
}
208+
if (file.fclose() != 0) {
209+
throw std::runtime_error(
210+
strprintf("Error closing %s: %s", fs::PathToString(file_fspath), SysErrorString(errno)));
211+
}
205212
if (!RenameOver(dump_path + ".new", dump_path)) {
206213
throw std::runtime_error("Rename failed");
207214
}
@@ -213,6 +220,7 @@ bool DumpMempool(const CTxMemPool& pool, const fs::path& dump_path, FopenFn mock
213220
fs::file_size(dump_path));
214221
} catch (const std::exception& e) {
215222
LogInfo("Failed to dump mempool: %s. Continuing anyway.\n", e.what());
223+
(void)file.fclose();
216224
return false;
217225
}
218226
return true;

src/policy/fees.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <uint256.h>
2020
#include <util/fs.h>
2121
#include <util/serfloat.h>
22+
#include <util/syserror.h>
2223
#include <util/time.h>
2324

2425
#include <algorithm>
@@ -963,9 +964,14 @@ void CBlockPolicyEstimator::FlushFeeEstimates()
963964
AutoFile est_file{fsbridge::fopen(m_estimation_filepath, "wb")};
964965
if (est_file.IsNull() || !Write(est_file)) {
965966
LogPrintf("Failed to write fee estimates to %s. Continue anyway.\n", fs::PathToString(m_estimation_filepath));
966-
} else {
967-
LogPrintf("Flushed fee estimates to %s.\n", fs::PathToString(m_estimation_filepath.filename()));
967+
(void)est_file.fclose();
968+
return;
969+
}
970+
if (est_file.fclose() != 0) {
971+
LogError("Failed to close fee estimates file %s: %s. Continuing anyway.", fs::PathToString(m_estimation_filepath), SysErrorString(errno));
972+
return;
968973
}
974+
LogPrintf("Flushed fee estimates to %s.\n", fs::PathToString(m_estimation_filepath.filename()));
969975
}
970976

971977
bool CBlockPolicyEstimator::Write(AutoFile& fileout) const

src/rpc/blockchain.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
#include <util/check.h>
4848
#include <util/fs.h>
4949
#include <util/strencodings.h>
50+
#include <util/syserror.h>
5051
#include <util/translation.h>
5152
#include <validation.h>
5253
#include <validationinterface.h>
@@ -3232,7 +3233,10 @@ UniValue WriteUTXOSnapshot(
32323233

32333234
CHECK_NONFATAL(written_coins_count == maybe_stats->coins_count);
32343235

3235-
afile.fclose();
3236+
if (afile.fclose() != 0) {
3237+
throw std::ios_base::failure(
3238+
strprintf("Error closing %s: %s", fs::PathToString(temppath), SysErrorString(errno)));
3239+
}
32363240

32373241
UniValue result(UniValue::VOBJ);
32383242
result.pushKV("coins_written", written_coins_count);

src/test/flatfile_tests.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ BOOST_AUTO_TEST_CASE(flatfile_open)
4646
{
4747
AutoFile file{seq.Open(FlatFilePos(0, pos1))};
4848
file << LIMITED_STRING(line1, 256);
49+
BOOST_REQUIRE_EQUAL(file.fclose(), 0);
4950
}
5051

5152
// Attempt to append to file opened in read-only mode.
@@ -58,6 +59,7 @@ BOOST_AUTO_TEST_CASE(flatfile_open)
5859
{
5960
AutoFile file{seq.Open(FlatFilePos(0, pos2))};
6061
file << LIMITED_STRING(line2, 256);
62+
BOOST_REQUIRE_EQUAL(file.fclose(), 0);
6163
}
6264

6365
// Read text from file in read-only mode.
@@ -79,13 +81,15 @@ BOOST_AUTO_TEST_CASE(flatfile_open)
7981

8082
file >> LIMITED_STRING(text, 256);
8183
BOOST_CHECK_EQUAL(text, line2);
84+
BOOST_REQUIRE_EQUAL(file.fclose(), 0);
8285
}
8386

8487
// Ensure another file in the sequence has no data.
8588
{
8689
std::string text;
8790
AutoFile file{seq.Open(FlatFilePos(1, pos2))};
8891
BOOST_CHECK_THROW(file >> LIMITED_STRING(text, 256), std::ios_base::failure);
92+
BOOST_REQUIRE_EQUAL(file.fclose(), 0);
8993
}
9094
}
9195

src/test/fuzz/autofile.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ FUZZ_TARGET(autofile)
4747
}
4848
},
4949
[&] {
50-
auto_file.fclose();
50+
(void)auto_file.fclose();
5151
},
5252
[&] {
5353
ReadFromStream(fuzzed_data_provider, auto_file);
@@ -62,5 +62,7 @@ FUZZ_TARGET(autofile)
6262
if (f != nullptr) {
6363
fclose(f);
6464
}
65+
} else {
66+
(void)auto_file.fclose();
6567
}
6668
}

0 commit comments

Comments
 (0)