|
6 | 6 | #ifndef BITCOIN_STREAMS_H
|
7 | 7 | #define BITCOIN_STREAMS_H
|
8 | 8 |
|
| 9 | +#include <logging.h> |
9 | 10 | #include <serialize.h>
|
10 | 11 | #include <span.h>
|
11 | 12 | #include <support/allocators/zeroafterfree.h>
|
| 13 | +#include <util/check.h> |
12 | 14 | #include <util/overflow.h>
|
| 15 | +#include <util/syserror.h> |
13 | 16 |
|
14 | 17 | #include <algorithm>
|
15 | 18 | #include <cassert>
|
@@ -386,27 +389,50 @@ class BitStreamWriter
|
386 | 389 | *
|
387 | 390 | * Will automatically close the file when it goes out of scope if not null.
|
388 | 391 | * If you're returning the file pointer, return file.release().
|
389 |
| - * If you need to close the file early, use file.fclose() instead of fclose(file). |
| 392 | + * If you need to close the file early, use autofile.fclose() instead of fclose(underlying_FILE). |
| 393 | + * |
| 394 | + * @note If the file has been written to, then the caller must close it |
| 395 | + * explicitly with the `fclose()` method, check if it returns an error and treat |
| 396 | + * such an error as if the `write()` method failed. The OS's `fclose(3)` may |
| 397 | + * fail to flush to disk data that has been previously written, rendering the |
| 398 | + * file corrupt. |
390 | 399 | */
|
391 | 400 | class AutoFile
|
392 | 401 | {
|
393 | 402 | protected:
|
394 | 403 | std::FILE* m_file;
|
395 | 404 | std::vector<std::byte> m_xor;
|
396 | 405 | std::optional<int64_t> m_position;
|
| 406 | + bool m_was_written{false}; |
397 | 407 |
|
398 | 408 | public:
|
399 | 409 | explicit AutoFile(std::FILE* file, std::vector<std::byte> data_xor={});
|
400 | 410 |
|
401 |
| - ~AutoFile() { fclose(); } |
| 411 | + ~AutoFile() |
| 412 | + { |
| 413 | + if (m_was_written) { |
| 414 | + // Callers that wrote to the file must have closed it explicitly |
| 415 | + // with the fclose() method and checked that the close succeeded. |
| 416 | + // This is because here in the destructor we have no way to signal |
| 417 | + // errors from fclose() which, after write, could mean the file is |
| 418 | + // corrupted and must be handled properly at the call site. |
| 419 | + // Destructors in C++ cannot signal an error to the callers because |
| 420 | + // they do not return a value and are not allowed to throw exceptions. |
| 421 | + Assume(IsNull()); |
| 422 | + } |
| 423 | + |
| 424 | + if (fclose() != 0) { |
| 425 | + LogError("Failed to close file: %s", SysErrorString(errno)); |
| 426 | + } |
| 427 | + } |
402 | 428 |
|
403 | 429 | // Disallow copies
|
404 | 430 | AutoFile(const AutoFile&) = delete;
|
405 | 431 | AutoFile& operator=(const AutoFile&) = delete;
|
406 | 432 |
|
407 | 433 | bool feof() const { return std::feof(m_file); }
|
408 | 434 |
|
409 |
| - int fclose() |
| 435 | + [[nodiscard]] int fclose() |
410 | 436 | {
|
411 | 437 | if (auto rel{release()}) return std::fclose(rel);
|
412 | 438 | return 0;
|
|
0 commit comments