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 2014 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.5// This test uses a custom Env to keep track of the state of a filesystem as of6// the last "sync". It then checks for data loss errors by purposely dropping7// file data (or entire files) not protected by a "sync".9#include <map>10#include <set>12#include "db/db_impl.h"13#include "db/filename.h"14#include "db/log_format.h"15#include "db/version_set.h"16#include "leveldb/cache.h"17#include "leveldb/db.h"18#include "leveldb/env.h"19#include "leveldb/table.h"20#include "leveldb/write_batch.h"21#include "port/port.h"22#include "port/thread_annotations.h"23#include "util/logging.h"24#include "util/mutexlock.h"25#include "util/testharness.h"26#include "util/testutil.h"28namespace leveldb {30static const int kValueSize = 1000;31static const int kMaxNumValues = 2000;32static const size_t kNumIterations = 3;34class FaultInjectionTestEnv;36namespace {38// Assume a filename, and not a directory name like "/foo/bar/"39static std::string GetDirName(const std::string& filename) {40size_t found = filename.find_last_of("/\\");41if (found == std::string::npos) {42return "";43} else {44return filename.substr(0, found);45}46}48Status SyncDir(const std::string& dir) {49// As this is a test it isn't required to *actually* sync this directory.50return Status::OK();51}53// A basic file truncation function suitable for this test.54Status Truncate(const std::string& filename, uint64_t length) {55leveldb::Env* env = leveldb::Env::Default();57SequentialFile* orig_file;58Status s = env->NewSequentialFile(filename, &orig_file);59if (!s.ok()) return s;61char* scratch = new char[length];62leveldb::Slice result;63s = orig_file->Read(length, &result, scratch);64delete orig_file;65if (s.ok()) {66std::string tmp_name = GetDirName(filename) + "/truncate.tmp";67WritableFile* tmp_file;68s = env->NewWritableFile(tmp_name, &tmp_file);69if (s.ok()) {70s = tmp_file->Append(result);71delete tmp_file;72if (s.ok()) {73s = env->RenameFile(tmp_name, filename);74} else {75env->DeleteFile(tmp_name);76}77}78}80delete[] scratch;82return s;83}85struct FileState {86std::string filename_;87int64_t pos_;88int64_t pos_at_last_sync_;89int64_t pos_at_last_flush_;91FileState(const std::string& filename)92: filename_(filename),93pos_(-1),94pos_at_last_sync_(-1),95pos_at_last_flush_(-1) {}97FileState() : pos_(-1), pos_at_last_sync_(-1), pos_at_last_flush_(-1) {}99bool IsFullySynced() const { return pos_ <= 0 || pos_ == pos_at_last_sync_; }101Status DropUnsyncedData() const;102};104} // anonymous namespace106// A wrapper around WritableFile which informs another Env whenever this file107// is written to or sync'ed.108class TestWritableFile : public WritableFile {109public:110TestWritableFile(const FileState& state, WritableFile* f,111FaultInjectionTestEnv* env);112~TestWritableFile() override;113Status Append(const Slice& data) override;114Status Close() override;115Status Flush() override;116Status Sync() override;117std::string GetName() const override { return ""; }119private:120FileState state_;121WritableFile* target_;122bool writable_file_opened_;123FaultInjectionTestEnv* env_;125Status SyncParent();126};128class FaultInjectionTestEnv : public EnvWrapper {129public:130FaultInjectionTestEnv()131: EnvWrapper(Env::Default()), filesystem_active_(true) {}132~FaultInjectionTestEnv() override = default;133Status NewWritableFile(const std::string& fname,134WritableFile** result) override;135Status NewAppendableFile(const std::string& fname,136WritableFile** result) override;137Status DeleteFile(const std::string& f) override;138Status RenameFile(const std::string& s, const std::string& t) override;140void WritableFileClosed(const FileState& state);141Status DropUnsyncedFileData();142Status DeleteFilesCreatedAfterLastDirSync();143void DirWasSynced();144bool IsFileCreatedSinceLastDirSync(const std::string& filename);145void ResetState();146void UntrackFile(const std::string& f);147// Setting the filesystem to inactive is the test equivalent to simulating a148// system reset. Setting to inactive will freeze our saved filesystem state so149// that it will stop being recorded. It can then be reset back to the state at150// the time of the reset.151bool IsFilesystemActive() LOCKS_EXCLUDED(mutex_) {152MutexLock l(&mutex_);153return filesystem_active_;154}155void SetFilesystemActive(bool active) LOCKS_EXCLUDED(mutex_) {156MutexLock l(&mutex_);157filesystem_active_ = active;158}160private:161port::Mutex mutex_;162std::map<std::string, FileState> db_file_state_ GUARDED_BY(mutex_);163std::set<std::string> new_files_since_last_dir_sync_ GUARDED_BY(mutex_);164bool filesystem_active_ GUARDED_BY(mutex_); // Record flushes, syncs, writes165};167TestWritableFile::TestWritableFile(const FileState& state, WritableFile* f,168FaultInjectionTestEnv* env)169: state_(state), target_(f), writable_file_opened_(true), env_(env) {170assert(f != nullptr);171}173TestWritableFile::~TestWritableFile() {174if (writable_file_opened_) {175Close();176}177delete target_;178}180Status TestWritableFile::Append(const Slice& data) {181Status s = target_->Append(data);182if (s.ok() && env_->IsFilesystemActive()) {183state_.pos_ += data.size();184}185return s;186}188Status TestWritableFile::Close() {189writable_file_opened_ = false;190Status s = target_->Close();191if (s.ok()) {192env_->WritableFileClosed(state_);193}194return s;195}197Status TestWritableFile::Flush() {198Status s = target_->Flush();199if (s.ok() && env_->IsFilesystemActive()) {200state_.pos_at_last_flush_ = state_.pos_;201}202return s;203}205Status TestWritableFile::SyncParent() {206Status s = SyncDir(GetDirName(state_.filename_));207if (s.ok()) {208env_->DirWasSynced();209}210return s;211}213Status TestWritableFile::Sync() {214if (!env_->IsFilesystemActive()) {215return Status::OK();216}217// Ensure new files referred to by the manifest are in the filesystem.218Status s = target_->Sync();219if (s.ok()) {220state_.pos_at_last_sync_ = state_.pos_;221}222if (env_->IsFileCreatedSinceLastDirSync(state_.filename_)) {223Status ps = SyncParent();224if (s.ok() && !ps.ok()) {225s = ps;226}227}228return s;229}231Status FaultInjectionTestEnv::NewWritableFile(const std::string& fname,232WritableFile** result) {233WritableFile* actual_writable_file;234Status s = target()->NewWritableFile(fname, &actual_writable_file);235if (s.ok()) {236FileState state(fname);237state.pos_ = 0;238*result = new TestWritableFile(state, actual_writable_file, this);239// NewWritableFile doesn't append to files, so if the same file is240// opened again then it will be truncated - so forget our saved241// state.242UntrackFile(fname);243MutexLock l(&mutex_);244new_files_since_last_dir_sync_.insert(fname);245}246return s;247}249Status FaultInjectionTestEnv::NewAppendableFile(const std::string& fname,250WritableFile** result) {
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.