20 static const std::vector<std::byte>
SUBDATABASE_NAME = {std::byte{
'm'}, std::byte{
'a'}, std::byte{
'i'}, std::byte{
'n'}};
87 std::array<std::byte, 20>
uid;
105 template <
typename Stream>
130 uint32_t uint32_flags;
171 throw std::runtime_error(
"Meta page number mismatch");
176 throw std::runtime_error(
"Not a BDB file");
181 throw std::runtime_error(
"Unsupported BDB data file version number");
185 if (pagesize < 512 || pagesize > 65536 || (
pagesize & (
pagesize - 1)) != 0) {
186 throw std::runtime_error(
"Bad page size");
191 throw std::runtime_error(
"Unexpected page type, should be 9 (BTree Metadata)");
196 throw std::runtime_error(
"Unexpected database flags, should only be 0x20 (subdatabases)");
209 static constexpr
size_t SIZE = 3;
216 template <
typename Stream>
243 template <
typename Stream>
267 template <
typename Stream>
300 static constexpr
size_t SIZE = 9;
302 template <
typename Stream>
330 static constexpr int64_t
SIZE = 26;
338 template <
typename Stream>
365 throw std::runtime_error(
"Page number mismatch");
368 throw std::runtime_error(
"Bad btree level");
383 std::vector<std::variant<DataRecord, OverflowRecord>>
records;
385 template <
typename Stream>
400 pos +=
sizeof(uint16_t);
403 int64_t to_jump = index - pos;
405 throw std::runtime_error(
"Data record position not in page");
414 switch (rec_hdr.type) {
419 to_jump += rec_hdr.len;
430 throw std::runtime_error(
"Unknown record type in records page");
434 s.seek(-to_jump, SEEK_CUR);
455 template <
typename Stream>
475 template <
typename Stream>
490 pos +=
sizeof(uint16_t);
493 int64_t to_jump = index - pos;
495 throw std::runtime_error(
"Internal record position not in page");
505 throw std::runtime_error(
"Unknown record type in internal page");
513 s.seek(-to_jump, SEEK_CUR);
520 int64_t pos = int64_t{page_num} * page_size;
521 s.seek(pos, SEEK_SET);
530 throw std::runtime_error(
"BerkeleyRODatabase: Failed to open database file");
533 uint32_t page_size = 4096;
538 db_file >> outer_meta;
542 db_file.
seek(0, SEEK_END);
543 int64_t size = db_file.
tell();
554 uint32_t expected_last_page{uint32_t((size / page_size) - 1)};
555 if (outer_meta.last_page != expected_last_page) {
556 throw std::runtime_error(
"Last page number could not fit in file");
560 if (outer_meta.encrypt_algo != 0) {
561 throw std::runtime_error(
"BDB builtin encryption is not supported");
566 for (uint32_t i = 0; i < outer_meta.last_page; ++i) {
572 db_file >> file >> offset;
573 if (outer_meta.other_endian) {
577 if (file != 0 || offset != 1) {
578 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");
583 SeekToPage(db_file, outer_meta.root, page_size);
584 PageHeader header(outer_meta.root, outer_meta.other_endian);
587 throw std::runtime_error(
"Unexpected outer database root page type");
589 if (header.entries != 2) {
590 throw std::runtime_error(
"Unexpected number of entries in outer database root page");
596 if (!std::holds_alternative<DataRecord>(page.records.at(0)) || std::get<DataRecord>(page.records.at(0)).
data !=
SUBDATABASE_NAME) {
597 throw std::runtime_error(
"Subdatabase has an unexpected name");
600 if (!std::holds_alternative<DataRecord>(page.records.at(1)) || std::get<DataRecord>(page.records.at(1)).m_header.len != 4) {
601 throw std::runtime_error(
"Subdatabase page number has unexpected length");
606 uint32_t main_db_page =
ReadBE32(std::get<DataRecord>(page.records.at(1)).
data.data());
609 if (main_db_page > outer_meta.last_page) {
610 throw std::runtime_error(
"Page number is greater than database last page");
616 db_file >> inner_meta;
618 if (inner_meta.pagesize != page_size) {
619 throw std::runtime_error(
"Unexpected page size");
622 if (inner_meta.last_page > outer_meta.last_page) {
623 throw std::runtime_error(
"Subdatabase last page is greater than database last page");
627 if (inner_meta.encrypt_algo != 0) {
628 throw std::runtime_error(
"BDB builtin encryption is not supported");
632 std::vector<uint32_t> pages{inner_meta.root};
633 while (pages.size() > 0) {
634 uint32_t curr_page = pages.back();
643 PageHeader header(curr_page, inner_meta.other_endian);
645 switch (header.type) {
658 if (rec_page.records.size() % 2 != 0) {
660 throw std::runtime_error(
"Records page has odd number of records");
663 std::vector<std::byte> key;
664 for (
const std::variant<DataRecord, OverflowRecord>& rec : rec_page.records) {
665 std::vector<std::byte>
data;
666 if (
const DataRecord* drec = std::get_if<DataRecord>(&rec)) {
667 if (drec->m_header.deleted)
continue;
669 }
else if (
const OverflowRecord* orec = std::get_if<OverflowRecord>(&rec)) {
670 if (orec->m_header.deleted)
continue;
671 uint32_t next_page = orec->page_number;
672 while (next_page != 0) {
674 PageHeader opage_header(next_page, inner_meta.other_endian);
675 db_file >> opage_header;
677 throw std::runtime_error(
"Bad overflow record page type");
681 data.insert(
data.end(), opage.data.begin(), opage.data.end());
682 next_page = opage_header.next_page;
697 throw std::runtime_error(
"Unexpected page type");
704 return std::make_unique<BerkeleyROBatch>(*this);
712 if (fs::is_directory(dst)) {
716 if (
fs::exists(dst) && fs::equivalent(src, dst)) {
721 fs::copy_file(src, dst, fs::copy_options::overwrite_existing);
724 }
catch (
const fs::filesystem_error& e) {
737 auto val = it->second;
750 : m_database(database)
775 std::unique_ptr<BerkeleyRODatabase> db = std::make_unique<BerkeleyRODatabase>(data_file);
778 }
catch (
const std::runtime_error& e) {
std::vector< std::variant< DataRecord, OverflowRecord > > records
BSWAP_CONSTEXPR uint32_t internal_bswap_32(uint32_t x)
static path PathFromString(const std::string &string)
Convert byte string to path object.
bool Backup(const std::string &strDest) const override
Back up the entire database to a file.
BerkeleyROData::const_iterator m_cursor_end
BerkeleyROData::const_iterator m_cursor
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.
FILE * fopen(const fs::path &p, const char *mode)
static const std::vector< std::byte > SUBDATABASE_NAME
static constexpr size_t SIZE
uint32_t ReadBE32(const B *ptr)
std::vector< std::byte > data
void Unserialize(Stream &s)
Span< std::byte > AsWritableBytes(Span< T > s) noexcept
std::vector< uint16_t > indexes
std::vector< InternalRecord > records
std::vector< std::byte > data
void Open() override
Open the database if it is not already opened.
InternalPage(const PageHeader &header)
A page of records in the database.
std::array< std::byte, 20 > uid
OverflowPage(const PageHeader &header)
uint32_t expected_page_num
void write(Span< const value_type > src)
void Unserialize(Stream &s)
MetaPage(uint32_t expected_page_num)
A class representing a BerkeleyDB file from which we can only read records.
Non-refcounted RAII wrapper for FILE*.
std::vector< std::byte > data
void Unserialize(Stream &s)
void Unserialize(Stream &s)
int64_t tell()
Find position within the file.
constexpr uint32_t BTREE_MAGIC
static std::string PathToString(const path &path)
Convert path object to a byte string.
DataRecord(const RecordHeader &header)
Double ended buffer combining vector and stream-like interfaces.
std::vector< std::byte, zero_after_free_allocator< std::byte > > SerializeData
Byte-vector that clears its contents before deletion.
Class for records representing internal nodes of the BTree.
Status Next(DataStream &key, DataStream &value) override
std::unique_ptr< DatabaseCursor > GetNewPrefixCursor(Span< const std::byte > prefix) override
A page containing overflow data.
BerkeleyROCursor(const BerkeleyRODatabase &database, Span< const std::byte > prefix={})
std::unique_ptr< DatabaseBatch > MakeBatch(bool flush_on_close=true) override
Make a DatabaseBatch connected to this database.
std::vector< uint16_t > indexes
Class for records representing overflow records of the BTree.
bool HasKey(DataStream &&key) override
std::string get_filesystem_error_message(const fs::filesystem_error &e)
bool ReadKey(DataStream &&key, DataStream &value) override
constexpr uint32_t BTREE_MAGIC_OE
Berkeley DB BTree metadata page layout.
void Unserialize(Stream &s)
static void SeekToPage(AutoFile &s, uint32_t page_num, uint32_t page_size)
fs::path BDBDataFile(const fs::path &wallet_path)
RecordsPage(const PageHeader &header)
A page of records in the database.
void seek(int64_t offset, int origin)
Wrapper around fseek().
OverflowRecord(const RecordHeader &header)
const BerkeleyRODatabase & m_database
static bool exists(const path &p)
void Unserialize(Stream &s)
Class for data in the record directly.
Span(T *, EndOrSize) -> Span< T >
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
const fs::path m_filepath
const BerkeleyRODatabase & m_database
InternalRecord(const RecordHeader &header)
static constexpr size_t FIXED_SIZE
static bool copy_file(const path &from, const path &to, copy_options options)
void Unserialize(Stream &s)
bool IsNull() const
Return true if the wrapped FILE* is nullptr, false otherwise.
BSWAP_CONSTEXPR uint16_t internal_bswap_16(uint16_t x)