20static 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");
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;
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");
587 throw std::runtime_error(
"Unexpected outer database root page type");
590 throw std::runtime_error(
"Unexpected number of entries in outer database root page");
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");
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");
623 throw std::runtime_error(
"Subdatabase last page is greater than database last page");
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();
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) {
675 db_file >> opage_header;
677 throw std::runtime_error(
"Bad overflow record page type");
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) {
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(Span< const value_type > src)
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
bool ReadKey(DataStream &&key, DataStream &value) override
std::unique_ptr< DatabaseCursor > GetNewPrefixCursor(Span< const std::byte > prefix) override
const BerkeleyRODatabase & m_database
bool HasKey(DataStream &&key) override
BerkeleyROData::const_iterator m_cursor_end
BerkeleyROCursor(const BerkeleyRODatabase &database, Span< const std::byte > prefix={})
BerkeleyROData::const_iterator m_cursor
const BerkeleyRODatabase & m_database
Status Next(DataStream &key, DataStream &value) override
A class representing a BerkeleyDB file from which we can only read records.
void Open() override
Open the database if it is not already opened.
const fs::path m_filepath
std::unique_ptr< DatabaseBatch > MakeBatch(bool flush_on_close=true) override
Make a DatabaseBatch connected to this database.
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
static uint32_t ReadBE32(const unsigned char *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)
std::string get_filesystem_error_message(const fs::filesystem_error &e)
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.
Span< std::byte > AsWritableBytes(Span< T > s) noexcept
Span(T *, EndOrSize) -> Span< T >
unsigned char * UCharCast(char *c)
std::vector< std::byte, zero_after_free_allocator< std::byte > > SerializeData
Byte-vector that clears its contents before deletion.