mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-04-29 14:59:39 -04:00
util: 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.
This commit is contained in:
parent
e66e30c9e5
commit
c52b673bf8
18 changed files with 145 additions and 46 deletions
|
@ -24,6 +24,7 @@
|
||||||
#include <univalue.h>
|
#include <univalue.h>
|
||||||
#include <util/fs.h>
|
#include <util/fs.h>
|
||||||
#include <util/fs_helpers.h>
|
#include <util/fs_helpers.h>
|
||||||
|
#include <util/syserror.h>
|
||||||
#include <util/translation.h>
|
#include <util/translation.h>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -61,7 +62,6 @@ bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data
|
||||||
FILE *file = fsbridge::fopen(pathTmp, "wb");
|
FILE *file = fsbridge::fopen(pathTmp, "wb");
|
||||||
AutoFile fileout{file};
|
AutoFile fileout{file};
|
||||||
if (fileout.IsNull()) {
|
if (fileout.IsNull()) {
|
||||||
fileout.fclose();
|
|
||||||
remove(pathTmp);
|
remove(pathTmp);
|
||||||
LogError("%s: Failed to open file %s\n", __func__, fs::PathToString(pathTmp));
|
LogError("%s: Failed to open file %s\n", __func__, fs::PathToString(pathTmp));
|
||||||
return false;
|
return false;
|
||||||
|
@ -69,17 +69,22 @@ bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data
|
||||||
|
|
||||||
// Serialize
|
// Serialize
|
||||||
if (!SerializeDB(fileout, data)) {
|
if (!SerializeDB(fileout, data)) {
|
||||||
fileout.fclose();
|
(void)fileout.fclose();
|
||||||
remove(pathTmp);
|
remove(pathTmp);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!fileout.Commit()) {
|
if (!fileout.Commit()) {
|
||||||
fileout.fclose();
|
(void)fileout.fclose();
|
||||||
remove(pathTmp);
|
remove(pathTmp);
|
||||||
LogError("%s: Failed to flush file %s\n", __func__, fs::PathToString(pathTmp));
|
LogError("%s: Failed to flush file %s\n", __func__, fs::PathToString(pathTmp));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
fileout.fclose();
|
if (fileout.fclose() != 0) {
|
||||||
|
const int errno_save{errno};
|
||||||
|
remove(pathTmp);
|
||||||
|
LogError("Failed to close file %s: %s", fs::PathToString(pathTmp), SysErrorString(errno_save));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// replace existing file, if any, with new file
|
// replace existing file, if any, with new file
|
||||||
if (!RenameOver(pathTmp, path)) {
|
if (!RenameOver(pathTmp, path)) {
|
||||||
|
|
|
@ -28,7 +28,7 @@ static void FindByte(benchmark::Bench& bench)
|
||||||
});
|
});
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
file.fclose();
|
assert(file.fclose() == 0);
|
||||||
fs::remove("streams_tmp");
|
fs::remove("streams_tmp");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -151,7 +151,7 @@ bool BlockFilterIndex::CustomCommit(CDBBatch& batch)
|
||||||
LogError("%s: Failed to open filter file %d\n", __func__, pos.nFile);
|
LogError("%s: Failed to open filter file %d\n", __func__, pos.nFile);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!file.Commit()) {
|
if (!file.Commit() || file.fclose() != 0) {
|
||||||
LogError("%s: Failed to commit filter file %d\n", __func__, pos.nFile);
|
LogError("%s: Failed to commit filter file %d\n", __func__, pos.nFile);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -205,7 +205,7 @@ size_t BlockFilterIndex::WriteFilterToDisk(FlatFilePos& pos, const BlockFilter&
|
||||||
LogPrintf("%s: Failed to truncate filter file %d\n", __func__, pos.nFile);
|
LogPrintf("%s: Failed to truncate filter file %d\n", __func__, pos.nFile);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (!last_file.Commit()) {
|
if (!last_file.Commit() || last_file.fclose() != 0) {
|
||||||
LogPrintf("%s: Failed to commit filter file %d\n", __func__, pos.nFile);
|
LogPrintf("%s: Failed to commit filter file %d\n", __func__, pos.nFile);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -229,6 +229,12 @@ size_t BlockFilterIndex::WriteFilterToDisk(FlatFilePos& pos, const BlockFilter&
|
||||||
}
|
}
|
||||||
|
|
||||||
fileout << filter.GetBlockHash() << filter.GetEncodedFilter();
|
fileout << filter.GetBlockHash() << filter.GetEncodedFilter();
|
||||||
|
|
||||||
|
if (fileout.fclose() != 0) {
|
||||||
|
LogPrintf("Failed to close filter file %d: %s", pos.nFile, SysErrorString(errno));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
return data_size;
|
return data_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4012,6 +4012,11 @@ static void CaptureMessageToFile(const CAddress& addr,
|
||||||
uint32_t size = data.size();
|
uint32_t size = data.size();
|
||||||
ser_writedata32(f, size);
|
ser_writedata32(f, size);
|
||||||
f << data;
|
f << data;
|
||||||
|
|
||||||
|
if (f.fclose() != 0) {
|
||||||
|
throw std::ios_base::failure(
|
||||||
|
strprintf("Error closing %s after write, file contents are likely incomplete", fs::PathToString(path)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::function<void(const CAddress& addr,
|
std::function<void(const CAddress& addr,
|
||||||
|
|
|
@ -947,20 +947,30 @@ bool BlockManager::WriteBlockUndo(const CBlockUndo& blockundo, BlockValidationSt
|
||||||
LogError("OpenUndoFile failed for %s while writing block undo", pos.ToString());
|
LogError("OpenUndoFile failed for %s while writing block undo", pos.ToString());
|
||||||
return FatalError(m_opts.notifications, state, _("Failed to write undo data."));
|
return FatalError(m_opts.notifications, state, _("Failed to write undo data."));
|
||||||
}
|
}
|
||||||
BufferedWriter fileout{file};
|
|
||||||
|
|
||||||
// Write index header
|
|
||||||
fileout << GetParams().MessageStart() << blockundo_size;
|
|
||||||
pos.nPos += STORAGE_HEADER_BYTES;
|
|
||||||
{
|
{
|
||||||
// Calculate checksum
|
BufferedWriter fileout{file};
|
||||||
HashWriter hasher{};
|
|
||||||
hasher << block.pprev->GetBlockHash() << blockundo;
|
// Write index header
|
||||||
// Write undo data & checksum
|
fileout << GetParams().MessageStart() << blockundo_size;
|
||||||
fileout << blockundo << hasher.GetHash();
|
pos.nPos += STORAGE_HEADER_BYTES;
|
||||||
|
{
|
||||||
|
// Calculate checksum
|
||||||
|
HashWriter hasher{};
|
||||||
|
hasher << block.pprev->GetBlockHash() << blockundo;
|
||||||
|
// Write undo data & checksum
|
||||||
|
fileout << blockundo << hasher.GetHash();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileout.flush(); // Make sure `AutoFile`/`BufferedWriter` go out of scope before we call `FlushUndoFile`
|
if (file.fclose() != 0) {
|
||||||
|
LogPrintLevel(BCLog::BLOCKSTORAGE,
|
||||||
|
BCLog::Level::Error,
|
||||||
|
"Failed to close block undo file %s: %s",
|
||||||
|
pos.ToString(),
|
||||||
|
SysErrorString(errno));
|
||||||
|
return FatalError(m_opts.notifications, state, _("Failed to close block undo file."));
|
||||||
|
}
|
||||||
|
// Make sure that the file is closed before we call `FlushUndoFile`.
|
||||||
}
|
}
|
||||||
|
|
||||||
// rev files are written in block height order, whereas blk files are written as blocks come in (often out of order)
|
// rev files are written in block height order, whereas blk files are written as blocks come in (often out of order)
|
||||||
|
@ -1093,13 +1103,26 @@ FlatFilePos BlockManager::WriteBlock(const CBlock& block, int nHeight)
|
||||||
m_opts.notifications.fatalError(_("Failed to write block."));
|
m_opts.notifications.fatalError(_("Failed to write block."));
|
||||||
return FlatFilePos();
|
return FlatFilePos();
|
||||||
}
|
}
|
||||||
BufferedWriter fileout{file};
|
{
|
||||||
|
BufferedWriter fileout{file};
|
||||||
|
|
||||||
|
// Write index header
|
||||||
|
fileout << GetParams().MessageStart() << block_size;
|
||||||
|
pos.nPos += STORAGE_HEADER_BYTES;
|
||||||
|
// Write block
|
||||||
|
fileout << TX_WITH_WITNESS(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file.fclose() != 0) {
|
||||||
|
LogPrintLevel(BCLog::BLOCKSTORAGE,
|
||||||
|
BCLog::Level::Error,
|
||||||
|
"Failed to close block file %s: %s",
|
||||||
|
pos.ToString(),
|
||||||
|
SysErrorString(errno));
|
||||||
|
m_opts.notifications.fatalError(_("Failed to close file when writing block."));
|
||||||
|
return FlatFilePos();
|
||||||
|
}
|
||||||
|
|
||||||
// Write index header
|
|
||||||
fileout << GetParams().MessageStart() << block_size;
|
|
||||||
pos.nPos += STORAGE_HEADER_BYTES;
|
|
||||||
// Write block
|
|
||||||
fileout << TX_WITH_WITNESS(block);
|
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1142,6 +1165,11 @@ static auto InitBlocksdirXorKey(const BlockManager::Options& opts)
|
||||||
#endif
|
#endif
|
||||||
)};
|
)};
|
||||||
xor_key_file << xor_key;
|
xor_key_file << xor_key;
|
||||||
|
if (xor_key_file.fclose() != 0) {
|
||||||
|
throw std::runtime_error{strprintf("Error closing XOR key file %s: %s\n",
|
||||||
|
fs::PathToString(xor_key_path),
|
||||||
|
SysErrorString(errno))};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// If the user disabled the key, it must be zero.
|
// If the user disabled the key, it must be zero.
|
||||||
if (!opts.use_xor && xor_key != decltype(xor_key){}) {
|
if (!opts.use_xor && xor_key != decltype(xor_key){}) {
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <util/fs.h>
|
#include <util/fs.h>
|
||||||
#include <util/fs_helpers.h>
|
#include <util/fs_helpers.h>
|
||||||
#include <util/signalinterrupt.h>
|
#include <util/signalinterrupt.h>
|
||||||
|
#include <util/syserror.h>
|
||||||
#include <util/time.h>
|
#include <util/time.h>
|
||||||
#include <validation.h>
|
#include <validation.h>
|
||||||
|
|
||||||
|
@ -168,7 +169,8 @@ bool DumpMempool(const CTxMemPool& pool, const fs::path& dump_path, FopenFn mock
|
||||||
|
|
||||||
auto mid = SteadyClock::now();
|
auto mid = SteadyClock::now();
|
||||||
|
|
||||||
AutoFile file{mockable_fopen_function(dump_path + ".new", "wb")};
|
const fs::path file_fspath{dump_path + ".new"};
|
||||||
|
AutoFile file{mockable_fopen_function(file_fspath, "wb")};
|
||||||
if (file.IsNull()) {
|
if (file.IsNull()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -201,7 +203,10 @@ bool DumpMempool(const CTxMemPool& pool, const fs::path& dump_path, FopenFn mock
|
||||||
|
|
||||||
if (!skip_file_commit && !file.Commit())
|
if (!skip_file_commit && !file.Commit())
|
||||||
throw std::runtime_error("Commit failed");
|
throw std::runtime_error("Commit failed");
|
||||||
file.fclose();
|
if (file.fclose() != 0) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
strprintf("Error closing %s: %s", fs::PathToString(file_fspath), SysErrorString(errno)));
|
||||||
|
}
|
||||||
if (!RenameOver(dump_path + ".new", dump_path)) {
|
if (!RenameOver(dump_path + ".new", dump_path)) {
|
||||||
throw std::runtime_error("Rename failed");
|
throw std::runtime_error("Rename failed");
|
||||||
}
|
}
|
||||||
|
@ -213,6 +218,7 @@ bool DumpMempool(const CTxMemPool& pool, const fs::path& dump_path, FopenFn mock
|
||||||
fs::file_size(dump_path));
|
fs::file_size(dump_path));
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
LogInfo("Failed to dump mempool: %s. Continuing anyway.\n", e.what());
|
LogInfo("Failed to dump mempool: %s. Continuing anyway.\n", e.what());
|
||||||
|
(void)file.fclose();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -953,8 +953,8 @@ void CBlockPolicyEstimator::Flush() {
|
||||||
void CBlockPolicyEstimator::FlushFeeEstimates()
|
void CBlockPolicyEstimator::FlushFeeEstimates()
|
||||||
{
|
{
|
||||||
AutoFile est_file{fsbridge::fopen(m_estimation_filepath, "wb")};
|
AutoFile est_file{fsbridge::fopen(m_estimation_filepath, "wb")};
|
||||||
if (est_file.IsNull() || !Write(est_file)) {
|
if (est_file.IsNull() || !Write(est_file) || est_file.fclose() != 0) {
|
||||||
LogPrintf("Failed to write fee estimates to %s. Continue anyway.\n", fs::PathToString(m_estimation_filepath));
|
LogWarning("Failed to write fee estimates to %s. Continuing anyway.", fs::PathToString(m_estimation_filepath));
|
||||||
} else {
|
} else {
|
||||||
LogPrintf("Flushed fee estimates to %s.\n", fs::PathToString(m_estimation_filepath.filename()));
|
LogPrintf("Flushed fee estimates to %s.\n", fs::PathToString(m_estimation_filepath.filename()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,7 @@
|
||||||
#include <util/check.h>
|
#include <util/check.h>
|
||||||
#include <util/fs.h>
|
#include <util/fs.h>
|
||||||
#include <util/strencodings.h>
|
#include <util/strencodings.h>
|
||||||
|
#include <util/syserror.h>
|
||||||
#include <util/translation.h>
|
#include <util/translation.h>
|
||||||
#include <validation.h>
|
#include <validation.h>
|
||||||
#include <validationinterface.h>
|
#include <validationinterface.h>
|
||||||
|
@ -3217,7 +3218,10 @@ UniValue WriteUTXOSnapshot(
|
||||||
|
|
||||||
CHECK_NONFATAL(written_coins_count == maybe_stats->coins_count);
|
CHECK_NONFATAL(written_coins_count == maybe_stats->coins_count);
|
||||||
|
|
||||||
afile.fclose();
|
if (afile.fclose() != 0) {
|
||||||
|
throw std::ios_base::failure(
|
||||||
|
strprintf("Error closing %s: %s", fs::PathToString(temppath), SysErrorString(errno)));
|
||||||
|
}
|
||||||
|
|
||||||
UniValue result(UniValue::VOBJ);
|
UniValue result(UniValue::VOBJ);
|
||||||
result.pushKV("coins_written", written_coins_count);
|
result.pushKV("coins_written", written_coins_count);
|
||||||
|
|
|
@ -95,6 +95,7 @@ void AutoFile::write(std::span<const std::byte> src)
|
||||||
src = src.subspan(buf_now.size());
|
src = src.subspan(buf_now.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
m_was_written = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoFile::write_buffer(std::span<std::byte> src)
|
void AutoFile::write_buffer(std::span<std::byte> src)
|
||||||
|
|
|
@ -6,10 +6,13 @@
|
||||||
#ifndef BITCOIN_STREAMS_H
|
#ifndef BITCOIN_STREAMS_H
|
||||||
#define BITCOIN_STREAMS_H
|
#define BITCOIN_STREAMS_H
|
||||||
|
|
||||||
|
#include <logging.h>
|
||||||
#include <serialize.h>
|
#include <serialize.h>
|
||||||
#include <span.h>
|
#include <span.h>
|
||||||
#include <support/allocators/zeroafterfree.h>
|
#include <support/allocators/zeroafterfree.h>
|
||||||
|
#include <util/check.h>
|
||||||
#include <util/overflow.h>
|
#include <util/overflow.h>
|
||||||
|
#include <util/syserror.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
@ -386,7 +389,13 @@ public:
|
||||||
*
|
*
|
||||||
* Will automatically close the file when it goes out of scope if not null.
|
* Will automatically close the file when it goes out of scope if not null.
|
||||||
* If you're returning the file pointer, return file.release().
|
* If you're returning the file pointer, return file.release().
|
||||||
* If you need to close the file early, use file.fclose() instead of fclose(file).
|
* If you need to close the file early, use autofile.fclose() instead of fclose(underlying_FILE).
|
||||||
|
*
|
||||||
|
* @note If the file has been written to, then the caller must close it
|
||||||
|
* explicitly with the `fclose()` method, check if it returns an error and treat
|
||||||
|
* such an error as if the `write()` method failed. The OS's `fclose(3)` may
|
||||||
|
* fail to flush to disk data that has been previously written, rendering the
|
||||||
|
* file corrupt.
|
||||||
*/
|
*/
|
||||||
class AutoFile
|
class AutoFile
|
||||||
{
|
{
|
||||||
|
@ -394,11 +403,26 @@ protected:
|
||||||
std::FILE* m_file;
|
std::FILE* m_file;
|
||||||
std::vector<std::byte> m_xor;
|
std::vector<std::byte> m_xor;
|
||||||
std::optional<int64_t> m_position;
|
std::optional<int64_t> m_position;
|
||||||
|
bool m_was_written{false};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit AutoFile(std::FILE* file, std::vector<std::byte> data_xor={});
|
explicit AutoFile(std::FILE* file, std::vector<std::byte> data_xor={});
|
||||||
|
|
||||||
~AutoFile() { fclose(); }
|
~AutoFile()
|
||||||
|
{
|
||||||
|
if (m_was_written) {
|
||||||
|
// Callers that wrote to the file must have closed it explicitly
|
||||||
|
// with the fclose() method and checked that the close succeeded.
|
||||||
|
// This is because here from the destructor we have no way to signal
|
||||||
|
// error due to close which, after write, could mean the file is
|
||||||
|
// corrupted and must be handled properly at the call site.
|
||||||
|
Assume(IsNull());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fclose() != 0) {
|
||||||
|
LogPrintLevel(BCLog::ALL, BCLog::Level::Error, "Failed to close file: %s", SysErrorString(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Disallow copies
|
// Disallow copies
|
||||||
AutoFile(const AutoFile&) = delete;
|
AutoFile(const AutoFile&) = delete;
|
||||||
|
@ -406,7 +430,7 @@ public:
|
||||||
|
|
||||||
bool feof() const { return std::feof(m_file); }
|
bool feof() const { return std::feof(m_file); }
|
||||||
|
|
||||||
int fclose()
|
[[nodiscard]] int fclose()
|
||||||
{
|
{
|
||||||
if (auto rel{release()}) return std::fclose(rel);
|
if (auto rel{release()}) return std::fclose(rel);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -46,6 +46,7 @@ BOOST_AUTO_TEST_CASE(flatfile_open)
|
||||||
{
|
{
|
||||||
AutoFile file{seq.Open(FlatFilePos(0, pos1))};
|
AutoFile file{seq.Open(FlatFilePos(0, pos1))};
|
||||||
file << LIMITED_STRING(line1, 256);
|
file << LIMITED_STRING(line1, 256);
|
||||||
|
BOOST_REQUIRE_EQUAL(file.fclose(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to append to file opened in read-only mode.
|
// Attempt to append to file opened in read-only mode.
|
||||||
|
@ -58,6 +59,7 @@ BOOST_AUTO_TEST_CASE(flatfile_open)
|
||||||
{
|
{
|
||||||
AutoFile file{seq.Open(FlatFilePos(0, pos2))};
|
AutoFile file{seq.Open(FlatFilePos(0, pos2))};
|
||||||
file << LIMITED_STRING(line2, 256);
|
file << LIMITED_STRING(line2, 256);
|
||||||
|
BOOST_REQUIRE_EQUAL(file.fclose(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read text from file in read-only mode.
|
// Read text from file in read-only mode.
|
||||||
|
@ -79,6 +81,7 @@ BOOST_AUTO_TEST_CASE(flatfile_open)
|
||||||
|
|
||||||
file >> LIMITED_STRING(text, 256);
|
file >> LIMITED_STRING(text, 256);
|
||||||
BOOST_CHECK_EQUAL(text, line2);
|
BOOST_CHECK_EQUAL(text, line2);
|
||||||
|
BOOST_REQUIRE_EQUAL(file.fclose(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure another file in the sequence has no data.
|
// Ensure another file in the sequence has no data.
|
||||||
|
@ -86,6 +89,7 @@ BOOST_AUTO_TEST_CASE(flatfile_open)
|
||||||
std::string text;
|
std::string text;
|
||||||
AutoFile file{seq.Open(FlatFilePos(1, pos2))};
|
AutoFile file{seq.Open(FlatFilePos(1, pos2))};
|
||||||
BOOST_CHECK_THROW(file >> LIMITED_STRING(text, 256), std::ios_base::failure);
|
BOOST_CHECK_THROW(file >> LIMITED_STRING(text, 256), std::ios_base::failure);
|
||||||
|
BOOST_REQUIRE_EQUAL(file.fclose(), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ FUZZ_TARGET(autofile)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[&] {
|
[&] {
|
||||||
auto_file.fclose();
|
(void)auto_file.fclose();
|
||||||
},
|
},
|
||||||
[&] {
|
[&] {
|
||||||
ReadFromStream(fuzzed_data_provider, auto_file);
|
ReadFromStream(fuzzed_data_provider, auto_file);
|
||||||
|
@ -62,5 +62,10 @@ FUZZ_TARGET(autofile)
|
||||||
if (f != nullptr) {
|
if (f != nullptr) {
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// FuzzedFileProvider::close() is expected to fail sometimes. Don't let
|
||||||
|
// the destructor of AutoFile be upset by a failing fclose(). Close it
|
||||||
|
// explicitly (and ignore any errors) so that the destructor is a noop.
|
||||||
|
(void)auto_file.fclose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,5 +104,6 @@ FUZZ_TARGET(policy_estimator, .init = initialize_policy_estimator)
|
||||||
AutoFile fuzzed_auto_file{fuzzed_file_provider.open()};
|
AutoFile fuzzed_auto_file{fuzzed_file_provider.open()};
|
||||||
block_policy_estimator.Write(fuzzed_auto_file);
|
block_policy_estimator.Write(fuzzed_auto_file);
|
||||||
block_policy_estimator.Read(fuzzed_auto_file);
|
block_policy_estimator.Read(fuzzed_auto_file);
|
||||||
|
(void)fuzzed_auto_file.fclose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,4 +32,5 @@ FUZZ_TARGET(policy_estimator_io, .init = initialize_policy_estimator_io)
|
||||||
if (block_policy_estimator.Read(fuzzed_auto_file)) {
|
if (block_policy_estimator.Read(fuzzed_auto_file)) {
|
||||||
block_policy_estimator.Write(fuzzed_auto_file);
|
block_policy_estimator.Write(fuzzed_auto_file);
|
||||||
}
|
}
|
||||||
|
(void)fuzzed_auto_file.fclose();
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,6 +150,7 @@ void utxo_snapshot_fuzz(FuzzBufferType buffer)
|
||||||
WriteCompactSize(outfile, 999); // index of coin
|
WriteCompactSize(outfile, 999); // index of coin
|
||||||
outfile << Coin{coinbase->vout[0], /*nHeightIn=*/999, /*fCoinBaseIn=*/0};
|
outfile << Coin{coinbase->vout[0], /*nHeightIn=*/999, /*fCoinBaseIn=*/0};
|
||||||
}
|
}
|
||||||
|
assert(outfile.fclose() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto ActivateFuzzedSnapshot{[&] {
|
const auto ActivateFuzzedSnapshot{[&] {
|
||||||
|
|
|
@ -39,6 +39,7 @@ BOOST_AUTO_TEST_CASE(xor_file)
|
||||||
#endif
|
#endif
|
||||||
AutoFile xor_file{raw_file(mode), xor_pat};
|
AutoFile xor_file{raw_file(mode), xor_pat};
|
||||||
xor_file << test1 << test2;
|
xor_file << test1 << test2;
|
||||||
|
BOOST_REQUIRE_EQUAL(xor_file.fclose(), 0);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
// Read raw from disk
|
// Read raw from disk
|
||||||
|
@ -379,8 +380,8 @@ BOOST_AUTO_TEST_CASE(streams_buffered_file)
|
||||||
// by the rewind window (relative to our farthest read position, 40).
|
// by the rewind window (relative to our farthest read position, 40).
|
||||||
BOOST_CHECK(bf.GetPos() <= 30U);
|
BOOST_CHECK(bf.GetPos() <= 30U);
|
||||||
|
|
||||||
// We can explicitly close the file, or the destructor will do it.
|
// Explicitly close the file and check that the close succeeds.
|
||||||
file.fclose();
|
BOOST_REQUIRE_EQUAL(file.fclose(), 0);
|
||||||
|
|
||||||
fs::remove(streams_test_filename);
|
fs::remove(streams_test_filename);
|
||||||
}
|
}
|
||||||
|
@ -430,7 +431,7 @@ BOOST_AUTO_TEST_CASE(streams_buffered_file_skip)
|
||||||
bf.SkipTo(13);
|
bf.SkipTo(13);
|
||||||
BOOST_CHECK_EQUAL(bf.GetPos(), 13U);
|
BOOST_CHECK_EQUAL(bf.GetPos(), 13U);
|
||||||
|
|
||||||
file.fclose();
|
BOOST_REQUIRE_EQUAL(file.fclose(), 0);
|
||||||
fs::remove(streams_test_filename);
|
fs::remove(streams_test_filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -551,6 +552,7 @@ BOOST_AUTO_TEST_CASE(streams_buffered_file_rand)
|
||||||
if (maxPos < currentPos)
|
if (maxPos < currentPos)
|
||||||
maxPos = currentPos;
|
maxPos = currentPos;
|
||||||
}
|
}
|
||||||
|
BOOST_REQUIRE_EQUAL(file.fclose(), 0);
|
||||||
}
|
}
|
||||||
fs::remove(streams_test_filename);
|
fs::remove(streams_test_filename);
|
||||||
}
|
}
|
||||||
|
@ -566,7 +568,9 @@ BOOST_AUTO_TEST_CASE(buffered_reader_matches_autofile_random_content)
|
||||||
|
|
||||||
// Write out the file with random content
|
// Write out the file with random content
|
||||||
{
|
{
|
||||||
AutoFile{test_file.Open(pos, /*read_only=*/false), obfuscation}.write(m_rng.randbytes<std::byte>(file_size));
|
AutoFile f{test_file.Open(pos, /*read_only=*/false), obfuscation};
|
||||||
|
f.write(m_rng.randbytes<std::byte>(file_size));
|
||||||
|
BOOST_REQUIRE_EQUAL(f.fclose(), 0);
|
||||||
}
|
}
|
||||||
BOOST_CHECK_EQUAL(fs::file_size(test_file.FileName(pos)), file_size);
|
BOOST_CHECK_EQUAL(fs::file_size(test_file.FileName(pos)), file_size);
|
||||||
|
|
||||||
|
@ -623,18 +627,21 @@ BOOST_AUTO_TEST_CASE(buffered_writer_matches_autofile_random_content)
|
||||||
AutoFile direct_file{test_direct.Open(pos, /*read_only=*/false), obfuscation};
|
AutoFile direct_file{test_direct.Open(pos, /*read_only=*/false), obfuscation};
|
||||||
|
|
||||||
AutoFile buffered_file{test_buffered.Open(pos, /*read_only=*/false), obfuscation};
|
AutoFile buffered_file{test_buffered.Open(pos, /*read_only=*/false), obfuscation};
|
||||||
BufferedWriter buffered{buffered_file, buf_size};
|
{
|
||||||
|
BufferedWriter buffered{buffered_file, buf_size};
|
||||||
|
|
||||||
for (size_t total_written{0}; total_written < file_size;) {
|
for (size_t total_written{0}; total_written < file_size;) {
|
||||||
const size_t write_size{Assert(std::min(1 + m_rng.randrange(m_rng.randbool() ? buf_size : 2 * buf_size), file_size - total_written))};
|
const size_t write_size{Assert(std::min(1 + m_rng.randrange(m_rng.randbool() ? buf_size : 2 * buf_size), file_size - total_written))};
|
||||||
|
|
||||||
auto current_span = std::span{test_data}.subspan(total_written, write_size);
|
auto current_span = std::span{test_data}.subspan(total_written, write_size);
|
||||||
direct_file.write(current_span);
|
direct_file.write(current_span);
|
||||||
buffered.write(current_span);
|
buffered.write(current_span);
|
||||||
|
|
||||||
total_written += write_size;
|
total_written += write_size;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Destructors of AutoFile and BufferedWriter will flush/close here
|
BOOST_REQUIRE_EQUAL(buffered_file.fclose(), 0);
|
||||||
|
BOOST_REQUIRE_EQUAL(direct_file.fclose(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare the resulting files
|
// Compare the resulting files
|
||||||
|
|
|
@ -48,7 +48,7 @@ CreateAndActivateUTXOSnapshot(
|
||||||
AutoFile auto_outfile{outfile};
|
AutoFile auto_outfile{outfile};
|
||||||
|
|
||||||
UniValue result = CreateUTXOSnapshot(
|
UniValue result = CreateUTXOSnapshot(
|
||||||
node, node.chainman->ActiveChainstate(), auto_outfile, snapshot_path, snapshot_path);
|
node, node.chainman->ActiveChainstate(), auto_outfile, snapshot_path, snapshot_path); // Will close auto_outfile.
|
||||||
LogPrintf(
|
LogPrintf(
|
||||||
"Wrote UTXO snapshot to %s: %s\n", fs::PathToString(snapshot_path.make_preferred()), result.write());
|
"Wrote UTXO snapshot to %s: %s\n", fs::PathToString(snapshot_path.make_preferred()), result.write());
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,7 @@ FUZZ_TARGET(wallet_bdb_parser, .init = initialize_wallet_bdb_parser)
|
||||||
{
|
{
|
||||||
AutoFile outfile{fsbridge::fopen(wallet_path, "wb")};
|
AutoFile outfile{fsbridge::fopen(wallet_path, "wb")};
|
||||||
outfile << std::span{buffer};
|
outfile << std::span{buffer};
|
||||||
|
assert(outfile.fclose() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
const DatabaseOptions options{};
|
const DatabaseOptions options{};
|
||||||
|
|
Loading…
Add table
Reference in a new issue