diff options
Diffstat (limited to 'test/syscalls/linux/flock.cc')
-rw-r--r-- | test/syscalls/linux/flock.cc | 716 |
1 files changed, 0 insertions, 716 deletions
diff --git a/test/syscalls/linux/flock.cc b/test/syscalls/linux/flock.cc deleted file mode 100644 index fd387aa45..000000000 --- a/test/syscalls/linux/flock.cc +++ /dev/null @@ -1,716 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <sys/file.h> - -#include <string> - -#include "gtest/gtest.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/syscalls/linux/file_base.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" -#include "test/util/timer_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -class FlockTest : public FileTest {}; - -TEST_F(FlockTest, InvalidOpCombinations) { - // The operation cannot be both exclusive and shared. - EXPECT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_SH | LOCK_NB), - SyscallFailsWithErrno(EINVAL)); - - // Locking and Unlocking doesn't make sense. - EXPECT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_UN | LOCK_NB), - SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_UN | LOCK_NB), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_F(FlockTest, NoOperationSpecified) { - // Not specifying an operation is invalid. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_F(FlockTest, TestSimpleExLock) { - // Test that we can obtain an exclusive lock (no other holders) - // and that we can unlock it. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestSimpleShLock) { - // Test that we can obtain a shared lock (no other holders) - // and that we can unlock it. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_NB), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestLockableAnyMode) { - // flock(2): A shared or exclusive lock can be placed on a file - // regardless of the mode in which the file was opened. - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE( - Open(test_file_name_, O_RDONLY)); // open read only to test - - // Mode shouldn't prevent us from taking an exclusive lock. - ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), SyscallSucceedsWithValue(0)); - - // Unlock - ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestUnlockWithNoHolders) { - // Test that unlocking when no one holds a lock succeeeds. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestRepeatedExLockingBySameHolder) { - // Test that repeated locking by the same holder for the - // same type of lock works correctly. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_EX), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_EX), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestRepeatedExLockingSingleUnlock) { - // Test that repeated locking by the same holder for the - // same type of lock works correctly and that a single unlock is required. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_EX), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_EX), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDONLY)); - - // Should be unlocked at this point - ASSERT_THAT(flock(fd.get(), LOCK_NB | LOCK_EX), SyscallSucceedsWithValue(0)); - - ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestRepeatedShLockingBySameHolder) { - // Test that repeated locking by the same holder for the - // same type of lock works correctly. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_SH), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_SH), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestSingleHolderUpgrade) { - // Test that a shared lock is upgradable when no one else holds a lock. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_SH), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_EX), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestSingleHolderDowngrade) { - // Test single holder lock downgrade case. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_NB), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestMultipleShared) { - // This is a simple test to verify that multiple independent shared - // locks will be granted. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_NB), - SyscallSucceedsWithValue(0)); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - // A shared lock should be granted as there only exists other shared locks. - ASSERT_THAT(flock(fd.get(), LOCK_SH | LOCK_NB), SyscallSucceedsWithValue(0)); - - // Unlock both. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); - ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -/* - * flock(2): If a process uses open(2) (or similar) to obtain more than one - * descriptor for the same file, these descriptors are treated - * independently by flock(). An attempt to lock the file using one of - * these file descriptors may be denied by a lock that the calling process - * has already placed via another descriptor. - */ -TEST_F(FlockTest, TestMultipleHolderSharedExclusive) { - // This test will verify that an exclusive lock will not be granted - // while a shared is held. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_NB), - SyscallSucceedsWithValue(0)); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - // Verify We're unable to get an exlcusive lock via the second FD. - // because someone is holding a shared lock. - ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), - SyscallFailsWithErrno(EWOULDBLOCK)); - - // Unlock - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestSharedLockFailExclusiveHolderNonblocking) { - // This test will verify that a shared lock is denied while - // someone holds an exclusive lock. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB), - SyscallSucceedsWithValue(0)); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - // Verify we're unable to get an shared lock via the second FD. - // because someone is holding an exclusive lock. - ASSERT_THAT(flock(fd.get(), LOCK_SH | LOCK_NB), - SyscallFailsWithErrno(EWOULDBLOCK)); - - // Unlock - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -void trivial_handler(int signum) {} - -TEST_F(FlockTest, TestSharedLockFailExclusiveHolderBlocking) { - const DisableSave ds; // Timing-related. - - // This test will verify that a shared lock is denied while - // someone holds an exclusive lock. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB), - SyscallSucceedsWithValue(0)); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - // Make sure that a blocking flock() call will return EINTR when interrupted - // by a signal. Create a timer that will go off while blocking on flock(), and - // register the corresponding signal handler. - auto timer = ASSERT_NO_ERRNO_AND_VALUE( - TimerCreate(CLOCK_MONOTONIC, sigevent_t{ - .sigev_signo = SIGALRM, - .sigev_notify = SIGEV_SIGNAL, - })); - - struct sigaction act = {}; - act.sa_handler = trivial_handler; - ASSERT_THAT(sigaction(SIGALRM, &act, NULL), SyscallSucceeds()); - - // Now that the signal handler is registered, set the timer. Set an interval - // so that it's ok if the timer goes off before we call flock. - ASSERT_NO_ERRNO( - timer.Set(0, itimerspec{ - .it_interval = absl::ToTimespec(absl::Milliseconds(10)), - .it_value = absl::ToTimespec(absl::Milliseconds(10)), - })); - - ASSERT_THAT(flock(fd.get(), LOCK_SH), SyscallFailsWithErrno(EINTR)); - timer.reset(); - - // Unlock - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestExclusiveLockFailExclusiveHolderNonblocking) { - // This test will verify that an exclusive lock is denied while - // someone already holds an exclsuive lock. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB), - SyscallSucceedsWithValue(0)); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - // Verify we're unable to get an exclusive lock via the second FD - // because someone is already holding an exclusive lock. - ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), - SyscallFailsWithErrno(EWOULDBLOCK)); - - // Unlock - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestExclusiveLockFailExclusiveHolderBlocking) { - const DisableSave ds; // Timing-related. - - // This test will verify that an exclusive lock is denied while - // someone already holds an exclsuive lock. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB), - SyscallSucceedsWithValue(0)); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - // Make sure that a blocking flock() call will return EINTR when interrupted - // by a signal. Create a timer that will go off while blocking on flock(), and - // register the corresponding signal handler. - auto timer = ASSERT_NO_ERRNO_AND_VALUE( - TimerCreate(CLOCK_MONOTONIC, sigevent_t{ - .sigev_signo = SIGALRM, - .sigev_notify = SIGEV_SIGNAL, - })); - - struct sigaction act = {}; - act.sa_handler = trivial_handler; - ASSERT_THAT(sigaction(SIGALRM, &act, NULL), SyscallSucceeds()); - - // Now that the signal handler is registered, set the timer. Set an interval - // so that it's ok if the timer goes off before we call flock. - ASSERT_NO_ERRNO( - timer.Set(0, itimerspec{ - .it_interval = absl::ToTimespec(absl::Milliseconds(10)), - .it_value = absl::ToTimespec(absl::Milliseconds(10)), - })); - - ASSERT_THAT(flock(fd.get(), LOCK_EX), SyscallFailsWithErrno(EINTR)); - timer.reset(); - - // Unlock - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestMultipleHolderSharedExclusiveUpgrade) { - // This test will verify that we cannot obtain an exclusive lock while - // a shared lock is held by another descriptor, then verify that an upgrade - // is possible on a shared lock once all other shared locks have closed. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_NB), - SyscallSucceedsWithValue(0)); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - // Verify we're unable to get an exclusive lock via the second FD because - // a shared lock is held. - ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), - SyscallFailsWithErrno(EWOULDBLOCK)); - - // Verify that we can get a shared lock via the second descriptor instead - ASSERT_THAT(flock(fd.get(), LOCK_SH | LOCK_NB), SyscallSucceedsWithValue(0)); - - // Unlock the first and there will only be one shared lock remaining. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); - - // Upgrade 2nd fd. - ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), SyscallSucceedsWithValue(0)); - - // Finally unlock the second - ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestMultipleHolderSharedExclusiveDowngrade) { - // This test will verify that a shared lock is not obtainable while an - // exclusive lock is held but that once the first is downgraded that - // the second independent file descriptor can also get a shared lock. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB), - SyscallSucceedsWithValue(0)); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - // Verify We're unable to get a shared lock via the second FD because - // an exclusive lock is held. - ASSERT_THAT(flock(fd.get(), LOCK_SH | LOCK_NB), - SyscallFailsWithErrno(EWOULDBLOCK)); - - // Verify that we can downgrade the first. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_NB), - SyscallSucceedsWithValue(0)); - - // Now verify that we can obtain a shared lock since the first was downgraded. - ASSERT_THAT(flock(fd.get(), LOCK_SH | LOCK_NB), SyscallSucceedsWithValue(0)); - - // Finally unlock both. - ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -/* - * flock(2): Locks created by flock() are associated with an open file table - * entry. This means that duplicate file descriptors (created by, for example, - * fork(2) or dup(2)) refer to the same lock, and this lock may be modified or - * released using any of these descriptors. Furthermore, the lock is released - * either by an explicit LOCK_UN operation on any of these duplicate descriptors - * or when all such descriptors have been closed. - */ -TEST_F(FlockTest, TestDupFdUpgrade) { - // This test will verify that a shared lock is upgradeable via a dupped - // file descriptor, if the FD wasn't dupped this would fail. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_NB), - SyscallSucceedsWithValue(0)); - - const FileDescriptor dup_fd = ASSERT_NO_ERRNO_AND_VALUE(test_file_fd_.Dup()); - - // Now we should be able to upgrade via the dupped fd. - ASSERT_THAT(flock(dup_fd.get(), LOCK_EX | LOCK_NB), - SyscallSucceedsWithValue(0)); - - // Validate unlock via dupped fd. - ASSERT_THAT(flock(dup_fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestDupFdDowngrade) { - // This test will verify that a exclusive lock is downgradable via a dupped - // file descriptor, if the FD wasn't dupped this would fail. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB), - SyscallSucceedsWithValue(0)); - - const FileDescriptor dup_fd = ASSERT_NO_ERRNO_AND_VALUE(test_file_fd_.Dup()); - - // Now we should be able to downgrade via the dupped fd. - ASSERT_THAT(flock(dup_fd.get(), LOCK_SH | LOCK_NB), - SyscallSucceedsWithValue(0)); - - // Validate unlock via dupped fd - ASSERT_THAT(flock(dup_fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestDupFdCloseRelease) { - // flock(2): Furthermore, the lock is released either by an explicit LOCK_UN - // operation on any of these duplicate descriptors, or when all such - // descriptors have been closed. - // - // This test will verify that a dupped fd closing will not release the - // underlying lock until all such dupped fds have closed. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB), - SyscallSucceedsWithValue(0)); - - FileDescriptor dup_fd = ASSERT_NO_ERRNO_AND_VALUE(test_file_fd_.Dup()); - - // At this point we have ONE exclusive locked referenced by two different fds. - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - // Validate that we cannot get a lock on a new unrelated FD. - ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), - SyscallFailsWithErrno(EWOULDBLOCK)); - - // Closing the dupped fd shouldn't affect the lock until all are closed. - dup_fd.reset(); // Closed the duped fd. - - // Validate that we still cannot get a lock on a new unrelated FD. - ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), - SyscallFailsWithErrno(EWOULDBLOCK)); - - // Closing the first fd - CloseFile(); // Will validate the syscall succeeds. - - // Now we should actually be able to get a lock since all fds related to - // the first lock are closed. - ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), SyscallSucceedsWithValue(0)); - - // Unlock. - ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestDupFdUnlockRelease) { - /* flock(2): Furthermore, the lock is released either by an explicit LOCK_UN - * operation on any of these duplicate descriptors, or when all such - * descriptors have been closed. - */ - // This test will verify that an explict unlock on a dupped FD will release - // the underlying lock unlike the previous case where close on a dup was - // not enough to release the lock. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB), - SyscallSucceedsWithValue(0)); - - const FileDescriptor dup_fd = ASSERT_NO_ERRNO_AND_VALUE(test_file_fd_.Dup()); - - // At this point we have ONE exclusive locked referenced by two different fds. - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - // Validate that we cannot get a lock on a new unrelated FD. - ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), - SyscallFailsWithErrno(EWOULDBLOCK)); - - // Explicitly unlock via the dupped descriptor. - ASSERT_THAT(flock(dup_fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); - - // Validate that we can now get the lock since we explicitly unlocked. - ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), SyscallSucceedsWithValue(0)); - - // Unlock - ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestDupFdFollowedByLock) { - // This test will verify that taking a lock on a file descriptor that has - // already been dupped means that the lock is shared between both. This is - // slightly different than than duping on an already locked FD. - FileDescriptor dup_fd = ASSERT_NO_ERRNO_AND_VALUE(test_file_fd_.Dup()); - - // Take a lock. - ASSERT_THAT(flock(dup_fd.get(), LOCK_EX | LOCK_NB), SyscallSucceeds()); - - // Now dup_fd and test_file_ should both reference the same lock. - // We shouldn't be able to obtain a lock until both are closed. - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - // Closing the first fd - dup_fd.reset(); // Close the duped fd. - - // Validate that we cannot get a lock yet because the dupped descriptor. - ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), - SyscallFailsWithErrno(EWOULDBLOCK)); - - // Closing the second fd. - CloseFile(); // CloseFile() will validate the syscall succeeds. - - // Now we should be able to get the lock. - ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), SyscallSucceeds()); - - // Unlock. - ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -// NOTE: These blocking tests are not perfect. Unfortunately it's very hard to -// determine if a thread was actually blocked in the kernel so we're forced -// to use timing. -TEST_F(FlockTest, BlockingLockNoBlockingForSharedLocks) { - // This test will verify that although LOCK_NB isn't specified - // two different fds can obtain shared locks without blocking. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH), SyscallSucceeds()); - - // kHoldLockTime is the amount of time we will hold the lock before releasing. - constexpr absl::Duration kHoldLockTime = absl::Seconds(30); - - const DisableSave ds; // Timing-related. - - // We do this in another thread so we can determine if it was actually - // blocked by timing the amount of time it took for the syscall to complete. - ScopedThread t([&] { - MonotonicTimer timer; - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - // Only a single shared lock is held, the lock will be granted immediately. - // This should be granted without any blocking. Don't save here to avoid - // wild discrepencies on timing. - timer.Start(); - ASSERT_THAT(flock(fd.get(), LOCK_SH), SyscallSucceeds()); - - // We held the lock for 30 seconds but this thread should not have - // blocked at all so we expect a very small duration on syscall completion. - ASSERT_LT(timer.Duration(), - absl::Seconds(1)); // 1000ms is much less than 30s. - - // We can release our second shared lock - ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceeds()); - }); - - // Sleep before unlocking. - absl::SleepFor(kHoldLockTime); - - // Release the first shared lock. Don't save in this situation to avoid - // discrepencies in timing. - EXPECT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceeds()); -} - -TEST_F(FlockTest, BlockingLockFirstSharedSecondExclusive) { - // This test will verify that if someone holds a shared lock any attempt to - // obtain an exclusive lock will result in blocking. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH), SyscallSucceeds()); - - // kHoldLockTime is the amount of time we will hold the lock before releasing. - constexpr absl::Duration kHoldLockTime = absl::Seconds(2); - - const DisableSave ds; // Timing-related. - - // We do this in another thread so we can determine if it was actually - // blocked by timing the amount of time it took for the syscall to complete. - ScopedThread t([&] { - MonotonicTimer timer; - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - // This exclusive lock should block because someone is already holding a - // shared lock. We don't save here to avoid wild discrepencies on timing. - timer.Start(); - ASSERT_THAT(RetryEINTR(flock)(fd.get(), LOCK_EX), SyscallSucceeds()); - - // We should be blocked, we will expect to be blocked for more than 1.0s. - ASSERT_GT(timer.Duration(), absl::Seconds(1)); - - // We can release our exclusive lock. - ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceeds()); - }); - - // Sleep before unlocking. - absl::SleepFor(kHoldLockTime); - - // Release the shared lock allowing the thread to proceed. - // We don't save here to avoid wild discrepencies in timing. - EXPECT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceeds()); -} - -TEST_F(FlockTest, BlockingLockFirstExclusiveSecondShared) { - // This test will verify that if someone holds an exclusive lock any attempt - // to obtain a shared lock will result in blocking. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX), SyscallSucceeds()); - - // kHoldLockTime is the amount of time we will hold the lock before releasing. - constexpr absl::Duration kHoldLockTime = absl::Seconds(2); - - const DisableSave ds; // Timing-related. - - // We do this in another thread so we can determine if it was actually - // blocked by timing the amount of time it took for the syscall to complete. - ScopedThread t([&] { - MonotonicTimer timer; - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - // This shared lock should block because someone is already holding an - // exclusive lock. We don't save here to avoid wild discrepencies on timing. - timer.Start(); - ASSERT_THAT(RetryEINTR(flock)(fd.get(), LOCK_SH), SyscallSucceeds()); - - // We should be blocked, we will expect to be blocked for more than 1.0s. - ASSERT_GT(timer.Duration(), absl::Seconds(1)); - - // We can release our shared lock. - ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceeds()); - }); - - // Sleep before unlocking. - absl::SleepFor(kHoldLockTime); - - // Release the exclusive lock allowing the blocked thread to proceed. - // We don't save here to avoid wild discrepencies in timing. - EXPECT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceeds()); -} - -TEST_F(FlockTest, BlockingLockFirstExclusiveSecondExclusive) { - // This test will verify that if someone holds an exclusive lock any attempt - // to obtain another exclusive lock will result in blocking. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX), SyscallSucceeds()); - - // kHoldLockTime is the amount of time we will hold the lock before releasing. - constexpr absl::Duration kHoldLockTime = absl::Seconds(2); - - const DisableSave ds; // Timing-related. - - // We do this in another thread so we can determine if it was actually - // blocked by timing the amount of time it took for the syscall to complete. - ScopedThread t([&] { - MonotonicTimer timer; - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - // This exclusive lock should block because someone is already holding an - // exclusive lock. - timer.Start(); - ASSERT_THAT(RetryEINTR(flock)(fd.get(), LOCK_EX), SyscallSucceeds()); - - // We should be blocked, we will expect to be blocked for more than 1.0s. - ASSERT_GT(timer.Duration(), absl::Seconds(1)); - - // We can release our exclusive lock. - ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceeds()); - }); - - // Sleep before unlocking. - absl::SleepFor(kHoldLockTime); - - // Release the exclusive lock allowing the blocked thread to proceed. - // We don't save to avoid wild discrepencies in timing. - EXPECT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceeds()); -} - -TEST(FlockTestNoFixture, BadFD) { - // EBADF: fd is not an open file descriptor. - ASSERT_THAT(flock(-1, 0), SyscallFailsWithErrno(EBADF)); -} - -TEST(FlockTestNoFixture, FlockDir) { - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_RDONLY, 0000)); - EXPECT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), SyscallSucceeds()); -} - -TEST(FlockTestNoFixture, FlockSymlink) { - // TODO(gvisor.dev/issue/2782): Replace with IsRunningWithVFS1() when O_PATH - // is supported. - SKIP_IF(IsRunningOnGvisor()); - - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - auto symlink = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateSymlinkTo(GetAbsoluteTestTmpdir(), file.path())); - - auto fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(symlink.path(), O_RDONLY | O_PATH, 0000)); - EXPECT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), SyscallFailsWithErrno(EBADF)); -} - -TEST(FlockTestNoFixture, FlockProc) { - auto fd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/self/status", O_RDONLY, 0000)); - EXPECT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), SyscallSucceeds()); -} - -TEST(FlockTestNoFixture, FlockPipe) { - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - - EXPECT_THAT(flock(fds[0], LOCK_EX | LOCK_NB), SyscallSucceeds()); - // Check that the pipe was locked above. - EXPECT_THAT(flock(fds[1], LOCK_EX | LOCK_NB), SyscallFailsWithErrno(EAGAIN)); - - EXPECT_THAT(flock(fds[0], LOCK_UN), SyscallSucceeds()); - EXPECT_THAT(flock(fds[1], LOCK_EX | LOCK_NB), SyscallSucceeds()); - - EXPECT_THAT(close(fds[0]), SyscallSucceeds()); - EXPECT_THAT(close(fds[1]), SyscallSucceeds()); -} - -TEST(FlockTestNoFixture, FlockSocket) { - int sock = socket(AF_UNIX, SOCK_STREAM, 0); - ASSERT_THAT(sock, SyscallSucceeds()); - - struct sockaddr_un addr = - ASSERT_NO_ERRNO_AND_VALUE(UniqueUnixAddr(true /* abstract */, AF_UNIX)); - ASSERT_THAT( - bind(sock, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)), - SyscallSucceeds()); - - EXPECT_THAT(flock(sock, LOCK_EX | LOCK_NB), SyscallSucceeds()); - EXPECT_THAT(close(sock), SyscallSucceeds()); -} - -} // namespace - -} // namespace testing -} // namespace gvisor |