// Copyright (c) 2011 The LevelDB Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. #include #include #include "leveldb/env.h" #include "port/port.h" #include "util/env_posix_test_helper.h" #include "util/testharness.h" namespace leveldb { static const int kReadOnlyFileLimit = 4; static const int kMMapLimit = 4; class EnvPosixTest { public: static void SetFileLimits(int read_only_file_limit, int mmap_limit) { EnvPosixTestHelper::SetReadOnlyFDLimit(read_only_file_limit); EnvPosixTestHelper::SetReadOnlyMMapLimit(mmap_limit); } EnvPosixTest() : env_(Env::Default()) {} Env* env_; }; TEST(EnvPosixTest, TestOpenOnRead) { // Write some test data to a single file that will be opened |n| times. std::string test_dir; ASSERT_OK(env_->GetTestDirectory(&test_dir)); std::string test_file = test_dir + "/open_on_read.txt"; FILE* f = fopen(test_file.c_str(), "we"); ASSERT_TRUE(f != nullptr); const char kFileData[] = "abcdefghijklmnopqrstuvwxyz"; fputs(kFileData, f); fclose(f); // Open test file some number above the sum of the two limits to force // open-on-read behavior of POSIX Env leveldb::RandomAccessFile. const int kNumFiles = kReadOnlyFileLimit + kMMapLimit + 5; leveldb::RandomAccessFile* files[kNumFiles] = {0}; for (int i = 0; i < kNumFiles; i++) { ASSERT_OK(env_->NewRandomAccessFile(test_file, &files[i])); } char scratch; Slice read_result; for (int i = 0; i < kNumFiles; i++) { ASSERT_OK(files[i]->Read(i, 1, &read_result, &scratch)); ASSERT_EQ(kFileData[i], read_result[0]); } for (int i = 0; i < kNumFiles; i++) { delete files[i]; } ASSERT_OK(env_->DeleteFile(test_file)); } #if defined(HAVE_O_CLOEXEC) TEST(EnvPosixTest, TestCloseOnExec) { // Test that file handles are not inherited by child processes. // Open file handles with each of the open methods. std::string test_dir; ASSERT_OK(env_->GetTestDirectory(&test_dir)); std::vector test_files = { test_dir + "/close_on_exec_seq.txt", test_dir + "/close_on_exec_rand.txt", test_dir + "/close_on_exec_write.txt", test_dir + "/close_on_exec_append.txt", test_dir + "/close_on_exec_lock.txt", test_dir + "/close_on_exec_log.txt", }; for (const std::string& test_file : test_files) { const char kFileData[] = "0123456789"; ASSERT_OK(WriteStringToFile(env_, kFileData, test_file)); } leveldb::SequentialFile* seqFile = nullptr; leveldb::RandomAccessFile* randFile = nullptr; leveldb::WritableFile* writeFile = nullptr; leveldb::WritableFile* appendFile = nullptr; leveldb::FileLock* lockFile = nullptr; leveldb::Logger* logFile = nullptr; ASSERT_OK(env_->NewSequentialFile(test_files[0], &seqFile)); ASSERT_OK(env_->NewRandomAccessFile(test_files[1], &randFile)); ASSERT_OK(env_->NewWritableFile(test_files[2], &writeFile)); ASSERT_OK(env_->NewAppendableFile(test_files[3], &appendFile)); ASSERT_OK(env_->LockFile(test_files[4], &lockFile)); ASSERT_OK(env_->NewLogger(test_files[5], &logFile)); // Fork a child process and wait for it to complete. int pid = fork(); if (pid == 0) { const char* const child[] = {"/proc/self/exe", "-cloexec-child", nullptr}; execv(child[0], const_cast(child)); printf("Error spawning child process: %s\n", strerror(errno)); exit(6); } int status; waitpid(pid, &status, 0); ASSERT_EQ(0, WEXITSTATUS(status)); // cleanup ASSERT_OK(env_->UnlockFile(lockFile)); delete seqFile; delete randFile; delete writeFile; delete appendFile; delete logFile; for (const std::string& test_file : test_files) { ASSERT_OK(env_->DeleteFile(test_file)); } } #endif // defined(HAVE_O_CLOEXEC) int cloexecChild() { // Checks for open file descriptors in the range 3..FD_SETSIZE. for (int i = 3; i < FD_SETSIZE; i++) { int dup_result = dup2(i, i); if (dup_result != -1) { printf("Unexpected open file %d\n", i); char nbuf[28]; snprintf(nbuf, 28, "/proc/self/fd/%d", i); char dbuf[1024]; int result = readlink(nbuf, dbuf, 1024); if (0 < result && result < 1024) { dbuf[result] = 0; printf("File descriptor %d is %s\n", i, dbuf); if (strstr(dbuf, "close_on_exec_") == nullptr) { continue; } } else if (result >= 1024) { printf("(file name length is too long)\n"); } else { printf("Couldn't get file name: %s\n", strerror(errno)); } return 3; } else { int e = errno; if (e != EBADF) { printf("Unexpected result reading file handle %d: %s\n", i, strerror(errno)); return 4; } } } return 0; } } // namespace leveldb int main(int argc, char** argv) { // Check if this is the child process for TestCloseOnExec if (argc > 1 && strcmp(argv[1], "-cloexec-child") == 0) { return leveldb::cloexecChild(); } // All tests currently run with the same read-only file limits. leveldb::EnvPosixTest::SetFileLimits(leveldb::kReadOnlyFileLimit, leveldb::kMMapLimit); return leveldb::test::RunAllTests(); }