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.
251Limiter* const mmap_limiter_;252const std::string filename_;253};255class PosixWritableFile final : public WritableFile {256public:257PosixWritableFile(std::string filename, int fd)258: pos_(0),259fd_(fd),260is_manifest_(IsManifest(filename)),261filename_(std::move(filename)),262dirname_(Dirname(filename_)) {}264~PosixWritableFile() override {265if (fd_ >= 0) {266// Ignoring any potential errors267Close();268}269}271Status Append(const Slice& data) override {272size_t write_size = data.size();273const char* write_data = data.data();275// Fit as much as possible into buffer.276size_t copy_size = std::min(write_size, kWritableFileBufferSize - pos_);277std::memcpy(buf_ + pos_, write_data, copy_size);278write_data += copy_size;279write_size -= copy_size;280pos_ += copy_size;281if (write_size == 0) {282return Status::OK();283}285// Can't fit in buffer, so need to do at least one write.286Status status = FlushBuffer();287if (!status.ok()) {288return status;289}291// Small writes go to buffer, large writes are written directly.292if (write_size < kWritableFileBufferSize) {293std::memcpy(buf_, write_data, write_size);294pos_ = write_size;295return Status::OK();296}297return WriteUnbuffered(write_data, write_size);298}300Status Close() override {301Status status = FlushBuffer();302const int close_result = ::close(fd_);303if (close_result < 0 && status.ok()) {304status = PosixError(filename_, errno);305}306fd_ = -1;307return status;308}310Status Flush() override { return FlushBuffer(); }312Status Sync() override {313// Ensure new files referred to by the manifest are in the filesystem.314//315// This needs to happen before the manifest file is flushed to disk, to316// avoid crashing in a state where the manifest refers to files that are not317// yet on disk.318Status status = SyncDirIfManifest();319if (!status.ok()) {320return status;321}323status = FlushBuffer();324if (!status.ok()) {325return status;326}328return SyncFd(fd_, filename_, false);329}331private:332Status FlushBuffer() {333Status status = WriteUnbuffered(buf_, pos_);334pos_ = 0;335return status;336}338Status WriteUnbuffered(const char* data, size_t size) {339while (size > 0) {340ssize_t write_result = ::write(fd_, data, size);341if (write_result < 0) {342if (errno == EINTR) {343continue; // Retry344}345return PosixError(filename_, errno);346}347data += write_result;348size -= write_result;349}350return Status::OK();351}353Status SyncDirIfManifest() {354Status status;355if (!is_manifest_) {356return status;357}359int fd = ::open(dirname_.c_str(), O_RDONLY | kOpenBaseFlags);360if (fd < 0) {361status = PosixError(dirname_, errno);362} else {363status = SyncFd(fd, dirname_, true);364::close(fd);365}366return status;367}369// Ensures that all the caches associated with the given file descriptor's370// data are flushed all the way to durable media, and can withstand power371// failures.372//373// The path argument is only used to populate the description string in the374// returned Status if an error occurs.375static Status SyncFd(int fd, const std::string& fd_path, bool syncing_dir) {376#if HAVE_FULLFSYNC377// On macOS and iOS, fsync() doesn't guarantee durability past power378// failures. fcntl(F_FULLFSYNC) is required for that purpose. Some379// filesystems don't support fcntl(F_FULLFSYNC), and require a fallback to380// fsync().381if (::fcntl(fd, F_FULLFSYNC) == 0) {382return Status::OK();383}384#endif // HAVE_FULLFSYNC386#if HAVE_FDATASYNC387bool sync_success = ::fdatasync(fd) == 0;388#else389bool sync_success = ::fsync(fd) == 0;390#endif // HAVE_FDATASYNC392if (sync_success) {393return Status::OK();394}395// Do not crash if filesystem can't fsync directories396// (see https://github.com/bitcoin/bitcoin/pull/10000)397if (syncing_dir && errno == EINVAL) {398return Status::OK();399}400return PosixError(fd_path, errno);401}403// Returns the directory name in a path pointing to a file.404//405// Returns "." if the path does not contain any directory separator.406static std::string Dirname(const std::string& filename) {407std::string::size_type separator_pos = filename.rfind('/');408if (separator_pos == std::string::npos) {409return std::string(".");410}411// The filename component should not contain a path separator. If it does,412// the splitting was done incorrectly.413assert(filename.find('/', separator_pos + 1) == std::string::npos);415return filename.substr(0, separator_pos);416}418// Extracts the file name from a path pointing to a file.419//420// The returned Slice points to |filename|'s data buffer, so it is only valid421// while |filename| is alive and unchanged.422static Slice Basename(const std::string& filename) {423std::string::size_type separator_pos = filename.rfind('/');424if (separator_pos == std::string::npos) {425return Slice(filename);426}427// The filename component should not contain a path separator. If it does,428// the splitting was done incorrectly.429assert(filename.find('/', separator_pos + 1) == std::string::npos);431return Slice(filename.data() + separator_pos + 1,432filename.length() - separator_pos - 1);433}435// True if the given file is a manifest file.436static bool IsManifest(const std::string& filename) {437return Basename(filename).starts_with("MANIFEST");438}440virtual std::string GetName() const override { return filename_; }442// buf_[0, pos_ - 1] contains data to be written to fd_.443char buf_[kWritableFileBufferSize];444size_t pos_;445int fd_;447const bool is_manifest_; // True if the file's name starts with MANIFEST.448const std::string filename_;449const std::string dirname_; // The directory of filename_.450};452int LockOrUnlock(int fd, bool lock) {453errno = 0;454struct ::flock file_lock_info;455std::memset(&file_lock_info, 0, sizeof(file_lock_info));456file_lock_info.l_type = (lock ? F_WRLCK : F_UNLCK);457file_lock_info.l_whence = SEEK_SET;458file_lock_info.l_start = 0;459file_lock_info.l_len = 0; // Lock/unlock entire file.460return ::fcntl(fd, F_SETLK, &file_lock_info);461}463// Instances are thread-safe because they are immutable.464class PosixFileLock : public FileLock {465public:466PosixFileLock(int fd, std::string filename)467: fd_(fd), filename_(std::move(filename)) {}469int fd() const { return fd_; }470const std::string& filename() const { return filename_; }472private:473const int fd_;474const std::string filename_;475};477// Tracks the files locked by PosixEnv::LockFile().478//479// We maintain a separate set instead of relying on fcntl(F_SETLK) because480// fcntl(F_SETLK) does not provide any protection against multiple uses from the481// same process.482//483// Instances are thread-safe because all member data is guarded by a mutex.484class PosixLockTable {485public:486bool Insert(const std::string& fname) LOCKS_EXCLUDED(mu_) {487mu_.Lock();488bool succeeded = locked_files_.insert(fname).second;489mu_.Unlock();490return succeeded;491}492void Remove(const std::string& fname) LOCKS_EXCLUDED(mu_) {493mu_.Lock();494locked_files_.erase(fname);495mu_.Unlock();496}498private:499port::Mutex mu_;500std::set<std::string> locked_files_ GUARDED_BY(mu_);
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.