What changed, and why it matters
This commit adds a new helper method called size() to the AutoFile class, which reports how large a file is. It also updates two existing places in the code to use this new helper instead of manually seeking to the end of the file and asking for the current position. The change is purely a code cleanup (refactor) and does not fix any security bug.
No security action required. This is a benign refactor. Normal code-review approval is sufficient.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch introduces AutoFile::size(), implemented by saving the current file offset, seeking to SEEK_END, recording tell(), and restoring the original offset. It replaces the manual seek/tell/seek pattern in src/util/asmap.cpp (DecodeAsmap) and src/wallet/migrate.cpp (BerkeleyRODatabase::Open). Unit tests are added to verify behavior on null handles and normal files. There is no change to validation logic, no bounds-check relaxation, and no new attack surface.
Changed components
src/streams.cppsrc/streams.hsrc/test/streams_tests.cppsrc/util/asmap.cppsrc/wallet/migrate.cppInspect captured patch +24 / −6
diff --git a/src/streams.cpp b/src/streams.cpp
index 0364c213..e38b9592 100644
--- a/src/streams.cpp
+++ b/src/streams.cpp
@@ -57,6 +57,20 @@ int64_t AutoFile::tell()
return *m_position;
}
+int64_t AutoFile::size()
+{
+ if (IsNull()) {
+ throw std::ios_base::failure("AutoFile::size: file handle is nullptr");
+ }
+ // Temporarily save the current position
+ int64_t current_pos = tell();
+ seek(0, SEEK_END);
+ int64_t file_size = tell();
+ // Restore the original position
+ seek(current_pos, SEEK_SET);
+ return file_size;
+}
+
void AutoFile::read(std::span<std::byte> dst)
{
if (detail_fread(dst) != dst.size()) {
diff --git a/src/streams.h b/src/streams.h
index 36af8dd6..466084e9 100644
--- a/src/streams.h
+++ b/src/streams.h
@@ -435,6 +435,9 @@ public:
/** Find position within the file. Will throw if unknown. */
int64_t tell();
+ /** Return the size of the file. Will throw if unknown. */
+ int64_t size();
+
/** Wrapper around FileCommit(). */
bool Commit();
diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp
index ce496df5..6b9d2b35 100644
--- a/src/test/streams_tests.cpp
+++ b/src/test/streams_tests.cpp
@@ -122,6 +122,7 @@ BOOST_AUTO_TEST_CASE(xor_file)
BOOST_CHECK_EXCEPTION(xor_file << std::byte{}, std::ios_base::failure, HasReason{"AutoFile::write: file handle is nullptr"});
BOOST_CHECK_EXCEPTION(xor_file >> std::byte{}, std::ios_base::failure, HasReason{"AutoFile::read: file handle is nullptr"});
BOOST_CHECK_EXCEPTION(xor_file.ignore(1), std::ios_base::failure, HasReason{"AutoFile::ignore: file handle is nullptr"});
+ BOOST_CHECK_EXCEPTION(xor_file.size(), std::ios_base::failure, HasReason{"AutoFile::size: file handle is nullptr"});
}
{
#ifdef __MINGW64__
@@ -132,6 +133,7 @@ BOOST_AUTO_TEST_CASE(xor_file)
#endif
AutoFile xor_file{raw_file(mode), obfuscation};
xor_file << test1 << test2;
+ BOOST_CHECK_EQUAL(xor_file.size(), 7);
BOOST_REQUIRE_EQUAL(xor_file.fclose(), 0);
}
{
@@ -142,6 +144,7 @@ BOOST_AUTO_TEST_CASE(xor_file)
BOOST_CHECK_EQUAL(HexStr(raw), "fc01fd03fd04fa");
// Check that no padding exists
BOOST_CHECK_EXCEPTION(non_xor_file.ignore(1), std::ios_base::failure, HasReason{"AutoFile::ignore: end of file"});
+ BOOST_CHECK_EQUAL(non_xor_file.size(), 7);
}
{
AutoFile xor_file{raw_file("rb"), obfuscation};
@@ -151,6 +154,7 @@ BOOST_AUTO_TEST_CASE(xor_file)
BOOST_CHECK_EQUAL(HexStr(read2), HexStr(test2));
// Check that eof was reached
BOOST_CHECK_EXCEPTION(xor_file >> std::byte{}, std::ios_base::failure, HasReason{"AutoFile::read: end of file"});
+ BOOST_CHECK_EQUAL(xor_file.size(), 7);
}
{
AutoFile xor_file{raw_file("rb"), obfuscation};
@@ -162,6 +166,7 @@ BOOST_AUTO_TEST_CASE(xor_file)
// Check that ignore and read fail now
BOOST_CHECK_EXCEPTION(xor_file.ignore(1), std::ios_base::failure, HasReason{"AutoFile::ignore: end of file"});
BOOST_CHECK_EXCEPTION(xor_file >> std::byte{}, std::ios_base::failure, HasReason{"AutoFile::read: end of file"});
+ BOOST_CHECK_EQUAL(xor_file.size(), 7);
}
}
diff --git a/src/util/asmap.cpp b/src/util/asmap.cpp
index 7c5f6eb5..4c20bd81 100644
--- a/src/util/asmap.cpp
+++ b/src/util/asmap.cpp
@@ -203,10 +203,8 @@ std::vector<bool> DecodeAsmap(fs::path path)
LogWarning("Failed to open asmap file from disk");
return bits;
}
- file.seek(0, SEEK_END);
- int length = file.tell();
+ int64_t length{file.size()};
LogInfo("Opened asmap file %s (%d bytes) from disk", fs::quoted(fs::PathToString(path)), length);
- file.seek(0, SEEK_SET);
uint8_t cur_byte;
for (int i = 0; i < length; ++i) {
file >> cur_byte;
@@ -220,4 +218,3 @@ std::vector<bool> DecodeAsmap(fs::path path)
}
return bits;
}
-
diff --git a/src/wallet/migrate.cpp b/src/wallet/migrate.cpp
index 603a0d2b..5e202d8d 100644
--- a/src/wallet/migrate.cpp
+++ b/src/wallet/migrate.cpp
@@ -544,8 +544,7 @@ void BerkeleyRODatabase::Open()
page_size = outer_meta.pagesize;
// Verify the size of the file is a multiple of the page size
- db_file.seek(0, SEEK_END);
- int64_t size = db_file.tell();
+ const int64_t size{db_file.size()};
// Since BDB stores everything in a page, the file size should be a multiple of the page size;
// However, BDB doesn't actually check that this is the case, and enforcing this check results
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.