1 //  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
2 //  This source code is licensed under both the GPLv2 (found in the
3 //  COPYING file in the root directory) and Apache 2.0 License
4 //  (found in the LICENSE.Apache file in the root directory).
5 //
6 #include "rocksdb/status.h"
7 #include "rocksdb/env.h"
8 
9 #include <fcntl.h>
10 #include <vector>
11 #include "test_util/testharness.h"
12 #include "util/coding.h"
13 
14 namespace ROCKSDB_NAMESPACE {
15 
16 class LockTest : public testing::Test {
17  public:
18   static LockTest* current_;
19   std::string file_;
20   ROCKSDB_NAMESPACE::Env* env_;
21 
LockTest()22   LockTest()
23       : file_(test::PerThreadDBPath("db_testlock_file")),
24         env_(ROCKSDB_NAMESPACE::Env::Default()) {
25     current_ = this;
26   }
27 
~LockTest()28   ~LockTest() override {}
29 
LockFile(FileLock ** db_lock)30   Status LockFile(FileLock** db_lock) {
31     return env_->LockFile(file_, db_lock);
32   }
33 
UnlockFile(FileLock * db_lock)34   Status UnlockFile(FileLock* db_lock) {
35     return env_->UnlockFile(db_lock);
36   }
37 
AssertFileIsLocked()38   bool AssertFileIsLocked(){
39     return CheckFileLock( /* lock_expected = */ true);
40   }
41 
AssertFileIsNotLocked()42   bool AssertFileIsNotLocked(){
43     return CheckFileLock( /* lock_expected = */ false);
44   }
45 
CheckFileLock(bool lock_expected)46   bool CheckFileLock(bool lock_expected){
47     // We need to fork to check the fcntl lock as we need
48     // to open and close the file from a different process
49     // to avoid either releasing the lock on close, or not
50     // contending for it when requesting a lock.
51 
52 #ifdef OS_WIN
53 
54     // WaitForSingleObject and GetExitCodeProcess can do what waitpid does.
55     // TODO - implement on Windows
56     return true;
57 
58 #else
59 
60     pid_t pid = fork();
61     if ( 0 == pid ) {
62       // child process
63       int exit_val = EXIT_FAILURE;
64       int fd = open(file_.c_str(), O_RDWR | O_CREAT, 0644);
65       if (fd < 0) {
66         // could not open file, could not check if it was locked
67         fprintf( stderr, "Open on on file %s failed.\n",file_.c_str());
68         exit(exit_val);
69       }
70 
71       struct flock f;
72       memset(&f, 0, sizeof(f));
73       f.l_type = (F_WRLCK);
74       f.l_whence = SEEK_SET;
75       f.l_start = 0;
76       f.l_len = 0; // Lock/unlock entire file
77       int value = fcntl(fd, F_SETLK, &f);
78       if( value == -1 ){
79         if( lock_expected ){
80           exit_val = EXIT_SUCCESS;
81         }
82       } else {
83         if( ! lock_expected ){
84           exit_val = EXIT_SUCCESS;
85         }
86       }
87       close(fd); // lock is released for child process
88       exit(exit_val);
89     } else if (pid > 0) {
90       // parent process
91       int status;
92       while (-1 == waitpid(pid, &status, 0));
93       if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
94         // child process exited with non success status
95         return false;
96       } else {
97         return true;
98       }
99     } else {
100       fprintf( stderr, "Fork failed\n" );
101       return false;
102     }
103     return false;
104 
105 #endif
106 
107   }
108 
109 };
110 LockTest* LockTest::current_;
111 
TEST_F(LockTest,LockBySameThread)112 TEST_F(LockTest, LockBySameThread) {
113   FileLock* lock1;
114   FileLock* lock2;
115 
116   // acquire a lock on a file
117   ASSERT_OK(LockFile(&lock1));
118 
119   // check the file is locked
120   ASSERT_TRUE( AssertFileIsLocked() );
121 
122   // re-acquire the lock on the same file. This should fail.
123   ASSERT_TRUE(LockFile(&lock2).IsIOError());
124 
125   // check the file is locked
126   ASSERT_TRUE( AssertFileIsLocked() );
127 
128   // release the lock
129   ASSERT_OK(UnlockFile(lock1));
130 
131   // check the file is not locked
132   ASSERT_TRUE( AssertFileIsNotLocked() );
133 
134 }
135 
136 }  // namespace ROCKSDB_NAMESPACE
137 
main(int argc,char ** argv)138 int main(int argc, char** argv) {
139   ::testing::InitGoogleTest(&argc, argv);
140   return RUN_ALL_TESTS();
141 }
142