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.5#include <sys/resource.h>6#include <sys/wait.h>7#include <unistd.h>9#include <cstdio>10#include <cstdlib>11#include <cstring>12#include <string>13#include <unordered_set>14#include <vector>16#include "leveldb/env.h"17#include "port/port.h"18#include "util/env_posix_test_helper.h"19#include "util/testharness.h"21#if HAVE_O_CLOEXEC23namespace {25// Exit codes for the helper process spawned by TestCloseOnExec* tests.26// Useful for debugging test failures.27constexpr int kTextCloseOnExecHelperExecFailedCode = 61;28constexpr int kTextCloseOnExecHelperDup2FailedCode = 62;29constexpr int kTextCloseOnExecHelperFoundOpenFdCode = 63;31// Global set by main() and read in TestCloseOnExec.32//33// The argv[0] value is stored in a std::vector instead of a std::string because34// std::string does not return a mutable pointer to its buffer until C++17.35//36// The vector stores the string pointed to by argv[0], plus the trailing null.37std::vector<char>* GetArgvZero() {38static std::vector<char> program_name;39return &program_name;40}42// Command-line switch used to run this test as the CloseOnExecSwitch helper.43static const char kTestCloseOnExecSwitch[] = "--test-close-on-exec-helper";45// Executed in a separate process by TestCloseOnExec* tests.46//47// main() delegates to this function when the test executable is launched with48// a special command-line switch. TestCloseOnExec* tests fork()+exec() the test49// executable and pass the special command-line switch.50//52// main() delegates to this function when the test executable is launched with53// a special command-line switch. TestCloseOnExec* tests fork()+exec() the test54// executable and pass the special command-line switch.55//56// When main() delegates to this function, the process probes whether a given57// file descriptor is open, and communicates the result via its exit code.58int TestCloseOnExecHelperMain(char* pid_arg) {59int fd = std::atoi(pid_arg);60// When given the same file descriptor twice, dup2() returns -1 if the61// file descriptor is closed, or the given file descriptor if it is open.62if (::dup2(fd, fd) == fd) {63std::fprintf(stderr, "Unexpected open fd %d\n", fd);64return kTextCloseOnExecHelperFoundOpenFdCode;65}66// Double-check that dup2() is saying the file descriptor is closed.67if (errno != EBADF) {68std::fprintf(stderr, "Unexpected errno after calling dup2 on fd %d: %s\n",69fd, std::strerror(errno));70return kTextCloseOnExecHelperDup2FailedCode;71}72return 0;73}75// File descriptors are small non-negative integers.76//77// Returns void so the implementation can use ASSERT_EQ.78void GetMaxFileDescriptor(int* result_fd) {79// Get the maximum file descriptor number.80::rlimit fd_rlimit;81ASSERT_EQ(0, ::getrlimit(RLIMIT_NOFILE, &fd_rlimit));82*result_fd = fd_rlimit.rlim_cur;83}85// Iterates through all possible FDs and returns the currently open ones.86//87// Returns void so the implementation can use ASSERT_EQ.88void GetOpenFileDescriptors(std::unordered_set<int>* open_fds) {89int max_fd = 0;90GetMaxFileDescriptor(&max_fd);92for (int fd = 0; fd < max_fd; ++fd) {93if (::dup2(fd, fd) != fd) {94// When given the same file descriptor twice, dup2() returns -1 if the95// file descriptor is closed, or the given file descriptor if it is open.96//97// Double-check that dup2() is saying the fd is closed.98ASSERT_EQ(EBADF, errno)99<< "dup2() should set errno to EBADF on closed file descriptors";100continue;101}102open_fds->insert(fd);103}104}106// Finds an FD open since a previous call to GetOpenFileDescriptors().107//108// |baseline_open_fds| is the result of a previous GetOpenFileDescriptors()109// call. Assumes that exactly one FD was opened since that call.110//111// Returns void so the implementation can use ASSERT_EQ.112void GetNewlyOpenedFileDescriptor(113const std::unordered_set<int>& baseline_open_fds, int* result_fd) {114std::unordered_set<int> open_fds;115GetOpenFileDescriptors(&open_fds);116for (int fd : baseline_open_fds) {117ASSERT_EQ(1, open_fds.count(fd))118<< "Previously opened file descriptor was closed during test setup";119open_fds.erase(fd);120}121ASSERT_EQ(1, open_fds.size())122<< "Expected exactly one newly opened file descriptor during test setup";123*result_fd = *open_fds.begin();124}126// Check that a fork()+exec()-ed child process does not have an extra open FD.127void CheckCloseOnExecDoesNotLeakFDs(128const std::unordered_set<int>& baseline_open_fds) {129// Prepare the argument list for the child process.130// execv() wants mutable buffers.131char switch_buffer[sizeof(kTestCloseOnExecSwitch)];132std::memcpy(switch_buffer, kTestCloseOnExecSwitch,133sizeof(kTestCloseOnExecSwitch));135int probed_fd;136GetNewlyOpenedFileDescriptor(baseline_open_fds, &probed_fd);137std::string fd_string = std::to_string(probed_fd);138std::vector<char> fd_buffer(fd_string.begin(), fd_string.end());139fd_buffer.emplace_back('\0');141// The helper process is launched with the command below.142// env_posix_tests --test-close-on-exec-helper 3143char* child_argv[] = {GetArgvZero()->data(), switch_buffer, fd_buffer.data(),144nullptr};146constexpr int kForkInChildProcessReturnValue = 0;147int child_pid = fork();148if (child_pid == kForkInChildProcessReturnValue) {149::execv(child_argv[0], child_argv);150std::fprintf(stderr, "Error spawning child process: %s\n", strerror(errno));151std::exit(kTextCloseOnExecHelperExecFailedCode);152}154int child_status = 0;155ASSERT_EQ(child_pid, ::waitpid(child_pid, &child_status, 0));156ASSERT_TRUE(WIFEXITED(child_status))157<< "The helper process did not exit with an exit code";158ASSERT_EQ(0, WEXITSTATUS(child_status))159<< "The helper process encountered an error";160}162} // namespace164#endif // HAVE_O_CLOEXEC166namespace leveldb {168static const int kReadOnlyFileLimit = 4;169static const int kMMapLimit = 4;171class EnvPosixTest {172public:173static void SetFileLimits(int read_only_file_limit, int mmap_limit) {174EnvPosixTestHelper::SetReadOnlyFDLimit(read_only_file_limit);175EnvPosixTestHelper::SetReadOnlyMMapLimit(mmap_limit);176}178EnvPosixTest() : env_(Env::Default()) {}180Env* env_;181};183TEST(EnvPosixTest, TestOpenOnRead) {184// Write some test data to a single file that will be opened |n| times.185std::string test_dir;186ASSERT_OK(env_->GetTestDirectory(&test_dir));187std::string test_file = test_dir + "/open_on_read.txt";189FILE* f = fopen(test_file.c_str(), "we");190ASSERT_TRUE(f != nullptr);191const char kFileData[] = "abcdefghijklmnopqrstuvwxyz";192fputs(kFileData, f);193fclose(f);195// Open test file some number above the sum of the two limits to force196// open-on-read behavior of POSIX Env leveldb::RandomAccessFile.197const int kNumFiles = kReadOnlyFileLimit + kMMapLimit + 5;198leveldb::RandomAccessFile* files[kNumFiles] = {0};199for (int i = 0; i < kNumFiles; i++) {200ASSERT_OK(env_->NewRandomAccessFile(test_file, &files[i]));201}202char scratch;203Slice read_result;204for (int i = 0; i < kNumFiles; i++) {205ASSERT_OK(files[i]->Read(i, 1, &read_result, &scratch));206ASSERT_EQ(kFileData[i], read_result[0]);207}208for (int i = 0; i < kNumFiles; i++) {209delete files[i];210}211ASSERT_OK(env_->DeleteFile(test_file));212}214#if HAVE_O_CLOEXEC216TEST(EnvPosixTest, TestCloseOnExecSequentialFile) {217std::unordered_set<int> open_fds;218GetOpenFileDescriptors(&open_fds);220std::string test_dir;221ASSERT_OK(env_->GetTestDirectory(&test_dir));222std::string file_path = test_dir + "/close_on_exec_sequential.txt";223ASSERT_OK(WriteStringToFile(env_, "0123456789", file_path));225leveldb::SequentialFile* file = nullptr;226ASSERT_OK(env_->NewSequentialFile(file_path, &file));227CheckCloseOnExecDoesNotLeakFDs(open_fds);228delete file;230ASSERT_OK(env_->DeleteFile(file_path));231}233TEST(EnvPosixTest, TestCloseOnExecRandomAccessFile) {234std::unordered_set<int> open_fds;235GetOpenFileDescriptors(&open_fds);237std::string test_dir;238ASSERT_OK(env_->GetTestDirectory(&test_dir));239std::string file_path = test_dir + "/close_on_exec_random_access.txt";240ASSERT_OK(WriteStringToFile(env_, "0123456789", file_path));242// Exhaust the RandomAccessFile mmap limit. This way, the test243// RandomAccessFile instance below is backed by a file descriptor, not by an244// mmap region.245leveldb::RandomAccessFile* mmapped_files[kReadOnlyFileLimit] = {nullptr};246for (int i = 0; i < kReadOnlyFileLimit; i++) {247ASSERT_OK(env_->NewRandomAccessFile(file_path, &mmapped_files[i]));248}250leveldb::RandomAccessFile* file = nullptr;
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.