Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2011 The LevelDB Authors. All rights reserved.2// Use of this source code is governed by a BSD-style license that can be3// found in the LICENSE file. See the AUTHORS file for names of contributors.4//5// We recover the contents of the descriptor from the other files we find.6// (1) Any log files are first converted to tables7// (2) We scan every table to compute8// (a) smallest/largest for the table9// (b) largest sequence number in the table10// (3) We generate descriptor contents:11// - log number is set to zero12// - next-file-number is set to 1 + largest file number we found13// - last-sequence-number is set to largest sequence# found across14// all tables (see 2c)15// - compaction pointers are cleared16// - every table file is added at level 017//18// Possible optimization 1:19// (a) Compute total size and use to pick appropriate max-level M20// (b) Sort tables by largest sequence# in the table21// (c) For each table: if it overlaps earlier table, place in level-0,22// else place in level-M.23// Possible optimization 2:24// Store per-table metadata (smallest, largest, largest-seq#, ...)25// in the table's meta section to speed up ScanTable.27#include "db/builder.h"28#include "db/db_impl.h"29#include "db/dbformat.h"30#include "db/filename.h"31#include "db/log_reader.h"32#include "db/log_writer.h"33#include "db/memtable.h"34#include "db/table_cache.h"35#include "db/version_edit.h"36#include "db/write_batch_internal.h"37#include "leveldb/comparator.h"38#include "leveldb/db.h"39#include "leveldb/env.h"41namespace leveldb {43namespace {45class Repairer {46public:47Repairer(const std::string& dbname, const Options& options)48: dbname_(dbname),49env_(options.env),50icmp_(options.comparator),51ipolicy_(options.filter_policy),52options_(SanitizeOptions(dbname, &icmp_, &ipolicy_, options)),53owns_info_log_(options_.info_log != options.info_log),54owns_cache_(options_.block_cache != options.block_cache),55next_file_number_(1) {56// TableCache can be small since we expect each table to be opened once.57table_cache_ = new TableCache(dbname_, options_, 10);58}60~Repairer() {61delete table_cache_;62if (owns_info_log_) {63delete options_.info_log;64}65if (owns_cache_) {66delete options_.block_cache;67}68}70Status Run() {71Status status = FindFiles();72if (status.ok()) {73ConvertLogFilesToTables();74ExtractMetaData();75status = WriteDescriptor();76}77if (status.ok()) {78unsigned long long bytes = 0;79for (size_t i = 0; i < tables_.size(); i++) {80bytes += tables_[i].meta.file_size;81}82Log(options_.info_log,83"**** Repaired leveldb %s; "84"recovered %d files; %llu bytes. "85"Some data may have been lost. "86"****",87dbname_.c_str(), static_cast<int>(tables_.size()), bytes);88}89return status;90}92private:93struct TableInfo {94FileMetaData meta;95SequenceNumber max_sequence;96};98Status FindFiles() {99std::vector<std::string> filenames;100Status status = env_->GetChildren(dbname_, &filenames);101if (!status.ok()) {102return status;103}104if (filenames.empty()) {105return Status::IOError(dbname_, "repair found no files");106}108uint64_t number;109FileType type;110for (size_t i = 0; i < filenames.size(); i++) {111if (ParseFileName(filenames[i], &number, &type)) {112if (type == kDescriptorFile) {113manifests_.push_back(filenames[i]);114} else {115if (number + 1 > next_file_number_) {116next_file_number_ = number + 1;117}118if (type == kLogFile) {119logs_.push_back(number);120} else if (type == kTableFile) {121table_numbers_.push_back(number);122} else {123// Ignore other files124}125}126}127}128return status;129}131void ConvertLogFilesToTables() {132for (size_t i = 0; i < logs_.size(); i++) {133std::string logname = LogFileName(dbname_, logs_[i]);134Status status = ConvertLogToTable(logs_[i]);135if (!status.ok()) {136Log(options_.info_log, "Log #%llu: ignoring conversion error: %s",137(unsigned long long)logs_[i], status.ToString().c_str());138}139ArchiveFile(logname);140}141}143Status ConvertLogToTable(uint64_t log) {144struct LogReporter : public log::Reader::Reporter {145Env* env;146Logger* info_log;147uint64_t lognum;148void Corruption(size_t bytes, const Status& s) override {149// We print error messages for corruption, but continue repairing.150Log(info_log, "Log #%llu: dropping %d bytes; %s",151(unsigned long long)lognum, static_cast<int>(bytes),152s.ToString().c_str());153}154};156// Open the log file157std::string logname = LogFileName(dbname_, log);158SequentialFile* lfile;159Status status = env_->NewSequentialFile(logname, &lfile);160if (!status.ok()) {161return status;162}164// Create the log reader.165LogReporter reporter;166reporter.env = env_;167reporter.info_log = options_.info_log;168reporter.lognum = log;169// We intentionally make log::Reader do checksumming so that170// corruptions cause entire commits to be skipped instead of171// propagating bad information (like overly large sequence172// numbers).173log::Reader reader(lfile, &reporter, false /*do not checksum*/,1740 /*initial_offset*/);176// Read all the records and add to a memtable177std::string scratch;178Slice record;179WriteBatch batch;180MemTable* mem = new MemTable(icmp_);181mem->Ref();182int counter = 0;183while (reader.ReadRecord(&record, &scratch)) {184if (record.size() < 12) {185reporter.Corruption(record.size(),186Status::Corruption("log record too small", logname));187continue;188}189WriteBatchInternal::SetContents(&batch, record);190status = WriteBatchInternal::InsertInto(&batch, mem);191if (status.ok()) {192counter += WriteBatchInternal::Count(&batch);193} else {194Log(options_.info_log, "Log #%llu: ignoring %s",195(unsigned long long)log, status.ToString().c_str());196status = Status::OK(); // Keep going with rest of file197}198}199delete lfile;201// Do not record a version edit for this conversion to a Table202// since ExtractMetaData() will also generate edits.203FileMetaData meta;204meta.number = next_file_number_++;205Iterator* iter = mem->NewIterator();206status = BuildTable(dbname_, env_, options_, table_cache_, iter, &meta);207delete iter;208mem->Unref();209mem = nullptr;210if (status.ok()) {211if (meta.file_size > 0) {212table_numbers_.push_back(meta.number);213}214}215Log(options_.info_log, "Log #%llu: %d ops saved to Table #%llu %s",216(unsigned long long)log, counter, (unsigned long long)meta.number,217status.ToString().c_str());218return status;219}221void ExtractMetaData() {222for (size_t i = 0; i < table_numbers_.size(); i++) {223ScanTable(table_numbers_[i]);224}225}227Iterator* NewTableIterator(const FileMetaData& meta) {228// Same as compaction iterators: if paranoid_checks are on, turn229// on checksum verification.230ReadOptions r;231r.verify_checksums = options_.paranoid_checks;232return table_cache_->NewIterator(r, meta.number, meta.file_size);233}235void ScanTable(uint64_t number) {236TableInfo t;237t.meta.number = number;238std::string fname = TableFileName(dbname_, number);239Status status = env_->GetFileSize(fname, &t.meta.file_size);240if (!status.ok()) {241// Try alternate file name.242fname = SSTTableFileName(dbname_, number);243Status s2 = env_->GetFileSize(fname, &t.meta.file_size);244if (s2.ok()) {245status = Status::OK();246}247}248if (!status.ok()) {249ArchiveFile(TableFileName(dbname_, number));250ArchiveFile(SSTTableFileName(dbname_, number));
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.