25static const std::vector<std::byte>
SUBDATABASE_NAME = {std::byte{
'm'}, std::byte{
'a'}, std::byte{
'i'}, std::byte{
'n'}};
92 std::array<std::byte, 20>
uid;
101 unsigned char iv[20];
110 template <
typename Stream>
135 uint32_t uint32_flags;
176 throw std::runtime_error(
"Meta page number mismatch");
181 throw std::runtime_error(
"Not a BDB file");
186 throw std::runtime_error(
"Unsupported BDB data file version number");
190 if (pagesize < 512 || pagesize > 65536 || (
pagesize & (
pagesize - 1)) != 0) {
191 throw std::runtime_error(
"Bad page size");
196 throw std::runtime_error(
"Unexpected page type, should be 9 (BTree Metadata)");
201 throw std::runtime_error(
"Unexpected database flags, should only be 0x20 (subdatabases)");
214 static constexpr size_t SIZE = 3;
221 template <
typename Stream>
248 template <
typename Stream>
252 s.read(std::as_writable_bytes(std::span(
data.data(),
data.size())));
272 template <
typename Stream>
280 s.read(std::as_writable_bytes(std::span(
data.data(),
data.size())));
305 static constexpr size_t SIZE = 9;
307 template <
typename Stream>
335 static constexpr int64_t
SIZE = 26;
343 template <
typename Stream>
370 throw std::runtime_error(
"Page number mismatch");
373 throw std::runtime_error(
"Bad btree level");
388 std::vector<std::variant<DataRecord, OverflowRecord>>
records;
390 template <
typename Stream>
405 pos +=
sizeof(uint16_t);
408 int64_t to_jump = index - pos;
410 throw std::runtime_error(
"Data record position not in page");
419 switch (rec_hdr.
type) {
424 to_jump += rec_hdr.
len;
435 throw std::runtime_error(
"Unknown record type in records page");
439 s.seek(-to_jump, SEEK_CUR);
460 template <
typename Stream>
464 s.read(std::as_writable_bytes(std::span(
data.data(),
data.size())));
480 template <
typename Stream>
495 pos +=
sizeof(uint16_t);
498 int64_t to_jump = index - pos;
500 throw std::runtime_error(
"Internal record position not in page");
510 throw std::runtime_error(
"Unknown record type in internal page");
518 s.seek(-to_jump, SEEK_CUR);
525 int64_t pos = int64_t{page_num} * page_size;
526 s.seek(pos, SEEK_SET);
535 throw std::runtime_error(
"BerkeleyRODatabase: Failed to open database file");
538 uint32_t page_size = 4096;
543 db_file >> outer_meta;
547 db_file.
seek(0, SEEK_END);
548 int64_t size = db_file.
tell();
559 uint32_t expected_last_page{uint32_t((size / page_size) - 1)};
560 if (outer_meta.
last_page != expected_last_page) {
561 throw std::runtime_error(
"Last page number could not fit in file");
566 throw std::runtime_error(
"BDB builtin encryption is not supported");
571 for (uint32_t i = 0; i < outer_meta.
last_page; ++i) {
577 db_file >> file >> offset;
582 if (file != 0 || offset != 1) {
583 throw std::runtime_error(
"LSNs are not reset, this database is not completely flushed. Please reopen then close the database with a version that has BDB support");
592 throw std::runtime_error(
"Unexpected outer database root page type");
595 throw std::runtime_error(
"Unexpected number of entries in outer database root page");
602 throw std::runtime_error(
"Subdatabase has an unexpected name");
605 if (!std::holds_alternative<DataRecord>(page.
records.at(1)) || std::get<DataRecord>(page.
records.at(1)).m_header.len != 4) {
606 throw std::runtime_error(
"Subdatabase page number has unexpected length");
611 uint32_t main_db_page =
ReadBE32(std::get<DataRecord>(page.
records.at(1)).data.data());
614 if (main_db_page > outer_meta.
last_page) {
615 throw std::runtime_error(
"Page number is greater than database last page");
621 db_file >> inner_meta;
623 if (inner_meta.
pagesize != page_size) {
624 throw std::runtime_error(
"Unexpected page size");
628 throw std::runtime_error(
"Subdatabase last page is greater than database last page");
633 throw std::runtime_error(
"BDB builtin encryption is not supported");
637 std::vector<uint32_t> pages{inner_meta.
root};
638 while (pages.size() > 0) {
639 uint32_t curr_page = pages.back();
650 switch (header.
type) {
663 if (rec_page.
records.size() % 2 != 0) {
665 throw std::runtime_error(
"Records page has odd number of records");
668 std::vector<std::byte> key;
669 for (
const std::variant<DataRecord, OverflowRecord>& rec : rec_page.
records) {
670 std::vector<std::byte>
data;
671 if (
const DataRecord* drec = std::get_if<DataRecord>(&rec)) {
672 if (drec->m_header.deleted)
continue;
674 }
else if (
const OverflowRecord* orec = std::get_if<OverflowRecord>(&rec)) {
675 if (orec->m_header.deleted)
continue;
676 uint32_t next_page = orec->page_number;
677 while (next_page != 0) {
680 db_file >> opage_header;
682 throw std::runtime_error(
"Bad overflow record page type");
702 throw std::runtime_error(
"Unexpected page type");
709 return std::make_unique<BerkeleyROBatch>(*
this);
717 if (fs::is_directory(dst)) {
721 if (
fs::exists(dst) && fs::equivalent(src, dst)) {
726 fs::copy_file(src, dst, fs::copy_options::overwrite_existing);
729 }
catch (
const fs::filesystem_error& e) {
742 auto val = it->second;
744 value.
write(std::span(val));
755 : m_database(database)
780 std::unique_ptr<BerkeleyRODatabase> db = std::make_unique<BerkeleyRODatabase>(data_file);
783 }
catch (
const std::runtime_error& e) {
BSWAP_CONSTEXPR uint32_t internal_bswap_32(uint32_t x)
BSWAP_CONSTEXPR uint16_t internal_bswap_16(uint16_t x)
Non-refcounted RAII wrapper for FILE*.
void seek(int64_t offset, int origin)
Wrapper around fseek().
bool IsNull() const
Return true if the wrapped FILE* is nullptr, false otherwise.
int64_t tell()
Find position within the file.
Double ended buffer combining vector and stream-like interfaces.
void write(std::span< const value_type > src)
bool ReadKey(DataStream &&key, DataStream &value) override
std::unique_ptr< DatabaseCursor > GetNewPrefixCursor(std::span< const std::byte > prefix) override
const BerkeleyRODatabase & m_database
bool HasKey(DataStream &&key) override
BerkeleyROData::const_iterator m_cursor_end
BerkeleyROData::const_iterator m_cursor
const BerkeleyRODatabase & m_database
BerkeleyROCursor(const BerkeleyRODatabase &database, std::span< const std::byte > prefix={})
Status Next(DataStream &key, DataStream &value) override
A class representing a BerkeleyDB file from which we can only read records.
std::unique_ptr< DatabaseBatch > MakeBatch() override
Make a DatabaseBatch connected to this database.
void Open() override
Open the database if it is not already opened.
const fs::path m_filepath
bool Backup(const std::string &strDest) const override
Back up the entire database to a file.
Class for data in the record directly.
void Unserialize(Stream &s)
std::vector< std::byte > data
DataRecord(const RecordHeader &header)
A page of records in the database.
InternalPage(const PageHeader &header)
void Unserialize(Stream &s)
std::vector< uint16_t > indexes
std::vector< InternalRecord > records
Class for records representing internal nodes of the BTree.
std::vector< std::byte > data
static constexpr size_t FIXED_SIZE
InternalRecord(const RecordHeader &header)
void Unserialize(Stream &s)
Berkeley DB BTree metadata page layout.
void Unserialize(Stream &s)
MetaPage(uint32_t expected_page_num)
uint32_t expected_page_num
std::array< std::byte, 20 > uid
A page containing overflow data.
std::vector< std::byte > data
void Unserialize(Stream &s)
OverflowPage(const PageHeader &header)
Class for records representing overflow records of the BTree.
OverflowRecord(const RecordHeader &header)
void Unserialize(Stream &s)
static constexpr size_t SIZE
A page of records in the database.
std::vector< std::variant< DataRecord, OverflowRecord > > records
void Unserialize(Stream &s)
RecordsPage(const PageHeader &header)
std::vector< uint16_t > indexes
uint32_t ReadBE32(const B *ptr)
static bool exists(const path &p)
static bool copy_file(const path &from, const path &to, copy_options options)
static std::string PathToString(const path &path)
Convert path object to a byte string.
static path PathFromString(const std::string &string)
Convert byte string to path object.
FILE * fopen(const fs::path &p, const char *mode)
static const std::vector< std::byte > SUBDATABASE_NAME
constexpr uint32_t BTREE_MAGIC
fs::path BDBDataFile(const fs::path &wallet_path)
constexpr uint32_t BTREE_MAGIC_OE
static void SeekToPage(AutoFile &s, uint32_t page_num, uint32_t page_size)
std::unique_ptr< BerkeleyRODatabase > MakeBerkeleyRODatabase(const fs::path &path, const DatabaseOptions &options, DatabaseStatus &status, bilingual_str &error)
Return object giving access to Berkeley Read Only database at specified path.
std::vector< std::byte, zero_after_free_allocator< std::byte > > SerializeData
Byte-vector that clears its contents before deletion.