Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

350 рядки
12 KiB

  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 be
  3. // found in the LICENSE file. See the AUTHORS file for names of contributors.
  4. #include <sys/resource.h>
  5. #include <sys/wait.h>
  6. #include <unistd.h>
  7. #include <cstdio>
  8. #include <cstdlib>
  9. #include <cstring>
  10. #include <string>
  11. #include <unordered_set>
  12. #include <vector>
  13. #include "leveldb/env.h"
  14. #include "port/port.h"
  15. #include "util/env_posix_test_helper.h"
  16. #include "util/testharness.h"
  17. #if HAVE_O_CLOEXEC
  18. namespace {
  19. // Exit codes for the helper process spawned by TestCloseOnExec* tests.
  20. // Useful for debugging test failures.
  21. constexpr int kTextCloseOnExecHelperExecFailedCode = 61;
  22. constexpr int kTextCloseOnExecHelperDup2FailedCode = 62;
  23. constexpr int kTextCloseOnExecHelperFoundOpenFdCode = 63;
  24. // Global set by main() and read in TestCloseOnExec.
  25. //
  26. // The argv[0] value is stored in a std::vector instead of a std::string because
  27. // std::string does not return a mutable pointer to its buffer until C++17.
  28. //
  29. // The vector stores the string pointed to by argv[0], plus the trailing null.
  30. std::vector<char>* GetArgvZero() {
  31. static std::vector<char> program_name;
  32. return &program_name;
  33. }
  34. // Command-line switch used to run this test as the CloseOnExecSwitch helper.
  35. static const char kTestCloseOnExecSwitch[] = "--test-close-on-exec-helper";
  36. // Executed in a separate process by TestCloseOnExec* tests.
  37. //
  38. // main() delegates to this function when the test executable is launched with
  39. // a special command-line switch. TestCloseOnExec* tests fork()+exec() the test
  40. // executable and pass the special command-line switch.
  41. //
  42. // main() delegates to this function when the test executable is launched with
  43. // a special command-line switch. TestCloseOnExec* tests fork()+exec() the test
  44. // executable and pass the special command-line switch.
  45. //
  46. // When main() delegates to this function, the process probes whether a given
  47. // file descriptor is open, and communicates the result via its exit code.
  48. int TestCloseOnExecHelperMain(char* pid_arg) {
  49. int fd = std::atoi(pid_arg);
  50. // When given the same file descriptor twice, dup2() returns -1 if the
  51. // file descriptor is closed, or the given file descriptor if it is open.
  52. if (::dup2(fd, fd) == fd) {
  53. std::fprintf(stderr, "Unexpected open fd %d\n", fd);
  54. return kTextCloseOnExecHelperFoundOpenFdCode;
  55. }
  56. // Double-check that dup2() is saying the file descriptor is closed.
  57. if (errno != EBADF) {
  58. std::fprintf(stderr, "Unexpected errno after calling dup2 on fd %d: %s\n",
  59. fd, std::strerror(errno));
  60. return kTextCloseOnExecHelperDup2FailedCode;
  61. }
  62. return 0;
  63. }
  64. // File descriptors are small non-negative integers.
  65. //
  66. // Returns void so the implementation can use ASSERT_EQ.
  67. void GetMaxFileDescriptor(int* result_fd) {
  68. // Get the maximum file descriptor number.
  69. ::rlimit fd_rlimit;
  70. ASSERT_EQ(0, ::getrlimit(RLIMIT_NOFILE, &fd_rlimit));
  71. *result_fd = fd_rlimit.rlim_cur;
  72. }
  73. // Iterates through all possible FDs and returns the currently open ones.
  74. //
  75. // Returns void so the implementation can use ASSERT_EQ.
  76. void GetOpenFileDescriptors(std::unordered_set<int>* open_fds) {
  77. int max_fd = 0;
  78. GetMaxFileDescriptor(&max_fd);
  79. for (int fd = 0; fd < max_fd; ++fd) {
  80. if (::dup2(fd, fd) != fd) {
  81. // When given the same file descriptor twice, dup2() returns -1 if the
  82. // file descriptor is closed, or the given file descriptor if it is open.
  83. //
  84. // Double-check that dup2() is saying the fd is closed.
  85. ASSERT_EQ(EBADF, errno)
  86. << "dup2() should set errno to EBADF on closed file descriptors";
  87. continue;
  88. }
  89. open_fds->insert(fd);
  90. }
  91. }
  92. // Finds an FD open since a previous call to GetOpenFileDescriptors().
  93. //
  94. // |baseline_open_fds| is the result of a previous GetOpenFileDescriptors()
  95. // call. Assumes that exactly one FD was opened since that call.
  96. //
  97. // Returns void so the implementation can use ASSERT_EQ.
  98. void GetNewlyOpenedFileDescriptor(
  99. const std::unordered_set<int>& baseline_open_fds, int* result_fd) {
  100. std::unordered_set<int> open_fds;
  101. GetOpenFileDescriptors(&open_fds);
  102. for (int fd : baseline_open_fds) {
  103. ASSERT_EQ(1, open_fds.count(fd))
  104. << "Previously opened file descriptor was closed during test setup";
  105. open_fds.erase(fd);
  106. }
  107. ASSERT_EQ(1, open_fds.size())
  108. << "Expected exactly one newly opened file descriptor during test setup";
  109. *result_fd = *open_fds.begin();
  110. }
  111. // Check that a fork()+exec()-ed child process does not have an extra open FD.
  112. void CheckCloseOnExecDoesNotLeakFDs(
  113. const std::unordered_set<int>& baseline_open_fds) {
  114. // Prepare the argument list for the child process.
  115. // execv() wants mutable buffers.
  116. char switch_buffer[sizeof(kTestCloseOnExecSwitch)];
  117. std::memcpy(switch_buffer, kTestCloseOnExecSwitch,
  118. sizeof(kTestCloseOnExecSwitch));
  119. int probed_fd;
  120. GetNewlyOpenedFileDescriptor(baseline_open_fds, &probed_fd);
  121. std::string fd_string = std::to_string(probed_fd);
  122. std::vector<char> fd_buffer(fd_string.begin(), fd_string.end());
  123. fd_buffer.emplace_back('\0');
  124. // The helper process is launched with the command below.
  125. // env_posix_tests --test-close-on-exec-helper 3
  126. char* child_argv[] = {GetArgvZero()->data(), switch_buffer, fd_buffer.data(),
  127. nullptr};
  128. constexpr int kForkInChildProcessReturnValue = 0;
  129. int child_pid = fork();
  130. if (child_pid == kForkInChildProcessReturnValue) {
  131. ::execv(child_argv[0], child_argv);
  132. std::fprintf(stderr, "Error spawning child process: %s\n", strerror(errno));
  133. std::exit(kTextCloseOnExecHelperExecFailedCode);
  134. }
  135. int child_status = 0;
  136. ASSERT_EQ(child_pid, ::waitpid(child_pid, &child_status, 0));
  137. ASSERT_TRUE(WIFEXITED(child_status))
  138. << "The helper process did not exit with an exit code";
  139. ASSERT_EQ(0, WEXITSTATUS(child_status))
  140. << "The helper process encountered an error";
  141. }
  142. } // namespace
  143. #endif // HAVE_O_CLOEXEC
  144. namespace leveldb {
  145. static const int kReadOnlyFileLimit = 4;
  146. static const int kMMapLimit = 4;
  147. class EnvPosixTest {
  148. public:
  149. static void SetFileLimits(int read_only_file_limit, int mmap_limit) {
  150. EnvPosixTestHelper::SetReadOnlyFDLimit(read_only_file_limit);
  151. EnvPosixTestHelper::SetReadOnlyMMapLimit(mmap_limit);
  152. }
  153. EnvPosixTest() : env_(Env::Default()) {}
  154. Env* env_;
  155. };
  156. TEST(EnvPosixTest, TestOpenOnRead) {
  157. // Write some test data to a single file that will be opened |n| times.
  158. std::string test_dir;
  159. ASSERT_OK(env_->GetTestDirectory(&test_dir));
  160. std::string test_file = test_dir + "/open_on_read.txt";
  161. FILE* f = fopen(test_file.c_str(), "we");
  162. ASSERT_TRUE(f != nullptr);
  163. const char kFileData[] = "abcdefghijklmnopqrstuvwxyz";
  164. fputs(kFileData, f);
  165. fclose(f);
  166. // Open test file some number above the sum of the two limits to force
  167. // open-on-read behavior of POSIX Env leveldb::RandomAccessFile.
  168. const int kNumFiles = kReadOnlyFileLimit + kMMapLimit + 5;
  169. leveldb::RandomAccessFile* files[kNumFiles] = {0};
  170. for (int i = 0; i < kNumFiles; i++) {
  171. ASSERT_OK(env_->NewRandomAccessFile(test_file, &files[i]));
  172. }
  173. char scratch;
  174. Slice read_result;
  175. for (int i = 0; i < kNumFiles; i++) {
  176. ASSERT_OK(files[i]->Read(i, 1, &read_result, &scratch));
  177. ASSERT_EQ(kFileData[i], read_result[0]);
  178. }
  179. for (int i = 0; i < kNumFiles; i++) {
  180. delete files[i];
  181. }
  182. ASSERT_OK(env_->DeleteFile(test_file));
  183. }
  184. #if HAVE_O_CLOEXEC
  185. TEST(EnvPosixTest, TestCloseOnExecSequentialFile) {
  186. std::unordered_set<int> open_fds;
  187. GetOpenFileDescriptors(&open_fds);
  188. std::string test_dir;
  189. ASSERT_OK(env_->GetTestDirectory(&test_dir));
  190. std::string file_path = test_dir + "/close_on_exec_sequential.txt";
  191. ASSERT_OK(WriteStringToFile(env_, "0123456789", file_path));
  192. leveldb::SequentialFile* file = nullptr;
  193. ASSERT_OK(env_->NewSequentialFile(file_path, &file));
  194. CheckCloseOnExecDoesNotLeakFDs(open_fds);
  195. delete file;
  196. ASSERT_OK(env_->DeleteFile(file_path));
  197. }
  198. TEST(EnvPosixTest, TestCloseOnExecRandomAccessFile) {
  199. std::unordered_set<int> open_fds;
  200. GetOpenFileDescriptors(&open_fds);
  201. std::string test_dir;
  202. ASSERT_OK(env_->GetTestDirectory(&test_dir));
  203. std::string file_path = test_dir + "/close_on_exec_random_access.txt";
  204. ASSERT_OK(WriteStringToFile(env_, "0123456789", file_path));
  205. // Exhaust the RandomAccessFile mmap limit. This way, the test
  206. // RandomAccessFile instance below is backed by a file descriptor, not by an
  207. // mmap region.
  208. leveldb::RandomAccessFile* mmapped_files[kReadOnlyFileLimit] = {nullptr};
  209. for (int i = 0; i < kReadOnlyFileLimit; i++) {
  210. ASSERT_OK(env_->NewRandomAccessFile(file_path, &mmapped_files[i]));
  211. }
  212. leveldb::RandomAccessFile* file = nullptr;
  213. ASSERT_OK(env_->NewRandomAccessFile(file_path, &file));
  214. CheckCloseOnExecDoesNotLeakFDs(open_fds);
  215. delete file;
  216. for (int i = 0; i < kReadOnlyFileLimit; i++) {
  217. delete mmapped_files[i];
  218. }
  219. ASSERT_OK(env_->DeleteFile(file_path));
  220. }
  221. TEST(EnvPosixTest, TestCloseOnExecWritableFile) {
  222. std::unordered_set<int> open_fds;
  223. GetOpenFileDescriptors(&open_fds);
  224. std::string test_dir;
  225. ASSERT_OK(env_->GetTestDirectory(&test_dir));
  226. std::string file_path = test_dir + "/close_on_exec_writable.txt";
  227. ASSERT_OK(WriteStringToFile(env_, "0123456789", file_path));
  228. leveldb::WritableFile* file = nullptr;
  229. ASSERT_OK(env_->NewWritableFile(file_path, &file));
  230. CheckCloseOnExecDoesNotLeakFDs(open_fds);
  231. delete file;
  232. ASSERT_OK(env_->DeleteFile(file_path));
  233. }
  234. TEST(EnvPosixTest, TestCloseOnExecAppendableFile) {
  235. std::unordered_set<int> open_fds;
  236. GetOpenFileDescriptors(&open_fds);
  237. std::string test_dir;
  238. ASSERT_OK(env_->GetTestDirectory(&test_dir));
  239. std::string file_path = test_dir + "/close_on_exec_appendable.txt";
  240. ASSERT_OK(WriteStringToFile(env_, "0123456789", file_path));
  241. leveldb::WritableFile* file = nullptr;
  242. ASSERT_OK(env_->NewAppendableFile(file_path, &file));
  243. CheckCloseOnExecDoesNotLeakFDs(open_fds);
  244. delete file;
  245. ASSERT_OK(env_->DeleteFile(file_path));
  246. }
  247. TEST(EnvPosixTest, TestCloseOnExecLockFile) {
  248. std::unordered_set<int> open_fds;
  249. GetOpenFileDescriptors(&open_fds);
  250. std::string test_dir;
  251. ASSERT_OK(env_->GetTestDirectory(&test_dir));
  252. std::string file_path = test_dir + "/close_on_exec_lock.txt";
  253. ASSERT_OK(WriteStringToFile(env_, "0123456789", file_path));
  254. leveldb::FileLock* lock = nullptr;
  255. ASSERT_OK(env_->LockFile(file_path, &lock));
  256. CheckCloseOnExecDoesNotLeakFDs(open_fds);
  257. ASSERT_OK(env_->UnlockFile(lock));
  258. ASSERT_OK(env_->DeleteFile(file_path));
  259. }
  260. TEST(EnvPosixTest, TestCloseOnExecLogger) {
  261. std::unordered_set<int> open_fds;
  262. GetOpenFileDescriptors(&open_fds);
  263. std::string test_dir;
  264. ASSERT_OK(env_->GetTestDirectory(&test_dir));
  265. std::string file_path = test_dir + "/close_on_exec_logger.txt";
  266. ASSERT_OK(WriteStringToFile(env_, "0123456789", file_path));
  267. leveldb::Logger* file = nullptr;
  268. ASSERT_OK(env_->NewLogger(file_path, &file));
  269. CheckCloseOnExecDoesNotLeakFDs(open_fds);
  270. delete file;
  271. ASSERT_OK(env_->DeleteFile(file_path));
  272. }
  273. #endif // HAVE_O_CLOEXEC
  274. } // namespace leveldb
  275. int main(int argc, char** argv) {
  276. #if HAVE_O_CLOEXEC
  277. // Check if we're invoked as a helper program, or as the test suite.
  278. for (int i = 1; i < argc; ++i) {
  279. if (!std::strcmp(argv[i], kTestCloseOnExecSwitch)) {
  280. return TestCloseOnExecHelperMain(argv[i + 1]);
  281. }
  282. }
  283. // Save argv[0] early, because googletest may modify argv.
  284. GetArgvZero()->assign(argv[0], argv[0] + std::strlen(argv[0]) + 1);
  285. #endif // HAVE_O_CLOEXEC
  286. // All tests currently run with the same read-only file limits.
  287. leveldb::EnvPosixTest::SetFileLimits(leveldb::kReadOnlyFileLimit,
  288. leveldb::kMMapLimit);
  289. return leveldb::test::RunAllTests();
  290. }