diff options
Diffstat (limited to 'test/syscalls/linux/fcntl.cc')
-rw-r--r-- | test/syscalls/linux/fcntl.cc | 2018 |
1 files changed, 0 insertions, 2018 deletions
diff --git a/test/syscalls/linux/fcntl.cc b/test/syscalls/linux/fcntl.cc deleted file mode 100644 index 2d9feecb5..000000000 --- a/test/syscalls/linux/fcntl.cc +++ /dev/null @@ -1,2018 +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 <fcntl.h> -#include <signal.h> -#include <sys/epoll.h> -#include <sys/mman.h> -#include <sys/types.h> -#include <syscall.h> -#include <unistd.h> - -#include <atomic> -#include <deque> -#include <iostream> -#include <list> -#include <string> -#include <vector> - -#include "gtest/gtest.h" -#include "absl/base/macros.h" -#include "absl/base/port.h" -#include "absl/flags/flag.h" -#include "absl/memory/memory.h" -#include "absl/strings/str_cat.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/util/capability_util.h" -#include "test/util/cleanup.h" -#include "test/util/eventfd_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/memory_util.h" -#include "test/util/multiprocess_util.h" -#include "test/util/posix_error.h" -#include "test/util/save_util.h" -#include "test/util/signal_util.h" -#include "test/util/socket_util.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" - -ABSL_FLAG(std::string, child_set_lock_on, "", - "Contains the path to try to set a file lock on."); -ABSL_FLAG(bool, child_set_lock_write, false, - "Whether to set a writable lock (otherwise readable)"); -ABSL_FLAG(bool, blocking, false, - "Whether to set a blocking lock (otherwise non-blocking)."); -ABSL_FLAG(bool, retry_eintr, false, - "Whether to retry in the subprocess on EINTR."); -ABSL_FLAG(uint64_t, child_set_lock_start, 0, "The value of struct flock start"); -ABSL_FLAG(uint64_t, child_set_lock_len, 0, "The value of struct flock len"); -ABSL_FLAG(int32_t, socket_fd, -1, - "A socket to use for communicating more state back " - "to the parent."); - -namespace gvisor { -namespace testing { - -std::function<void(int, siginfo_t*, void*)> setsig_signal_handle; -void setsig_signal_handler(int signum, siginfo_t* siginfo, void* ucontext) { - setsig_signal_handle(signum, siginfo, ucontext); -} - -class FcntlLockTest : public ::testing::Test { - public: - void SetUp() override { - // Let's make a socket pair. - ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, fds_), SyscallSucceeds()); - } - - void TearDown() override { - EXPECT_THAT(close(fds_[0]), SyscallSucceeds()); - EXPECT_THAT(close(fds_[1]), SyscallSucceeds()); - } - - int64_t GetSubprocessFcntlTimeInUsec() { - int64_t ret = 0; - EXPECT_THAT(ReadFd(fds_[0], reinterpret_cast<void*>(&ret), sizeof(ret)), - SyscallSucceedsWithValue(sizeof(ret))); - return ret; - } - - // The first fd will remain with the process creating the subprocess - // and the second will go to the subprocess. - int fds_[2] = {}; -}; - -struct SignalDelivery { - int num; - siginfo_t info; -}; - -class FcntlSignalTest : public ::testing::Test { - public: - void SetUp() override { - int pipe_fds[2]; - ASSERT_THAT(pipe2(pipe_fds, O_NONBLOCK), SyscallSucceeds()); - pipe_read_fd_ = pipe_fds[0]; - pipe_write_fd_ = pipe_fds[1]; - } - - PosixErrorOr<Cleanup> RegisterSignalHandler(int signum) { - struct sigaction handler; - handler.sa_sigaction = setsig_signal_handler; - setsig_signal_handle = [&](int signum, siginfo_t* siginfo, - void* unused_ucontext) { - SignalDelivery sig; - sig.num = signum; - sig.info = *siginfo; - signals_received_.push_back(sig); - num_signals_received_++; - }; - sigemptyset(&handler.sa_mask); - handler.sa_flags = SA_SIGINFO; - return ScopedSigaction(signum, handler); - } - - void FlushAndCloseFD(int fd) { - char buf; - int read_bytes; - do { - read_bytes = read(fd, &buf, 1); - } while (read_bytes > 0); - // read() can also fail with EWOULDBLOCK since the pipe is open in - // non-blocking mode. This is not an error. - EXPECT_TRUE(read_bytes == 0 || (read_bytes == -1 && errno == EWOULDBLOCK)); - EXPECT_THAT(close(fd), SyscallSucceeds()); - } - - void DupReadFD() { - ASSERT_THAT(pipe_read_fd_dup_ = dup(pipe_read_fd_), SyscallSucceeds()); - max_expected_signals++; - } - - void RegisterFD(int fd, int signum) { - ASSERT_THAT(fcntl(fd, F_SETOWN, getpid()), SyscallSucceeds()); - ASSERT_THAT(fcntl(fd, F_SETSIG, signum), SyscallSucceeds()); - int old_flags; - ASSERT_THAT(old_flags = fcntl(fd, F_GETFL), SyscallSucceeds()); - ASSERT_THAT(fcntl(fd, F_SETFL, old_flags | O_ASYNC), SyscallSucceeds()); - } - - void GenerateIOEvent() { - ASSERT_THAT(write(pipe_write_fd_, "test", 4), SyscallSucceedsWithValue(4)); - } - - void WaitForSignalDelivery(absl::Duration timeout) { - absl::Time wait_start = absl::Now(); - while (num_signals_received_ < max_expected_signals && - absl::Now() - wait_start < timeout) { - absl::SleepFor(absl::Milliseconds(10)); - } - } - - int pipe_read_fd_ = -1; - int pipe_read_fd_dup_ = -1; - int pipe_write_fd_ = -1; - int max_expected_signals = 1; - std::deque<SignalDelivery> signals_received_; - std::atomic<int> num_signals_received_ = 0; -}; - -namespace { - -PosixErrorOr<Cleanup> SubprocessLock(std::string const& path, bool for_write, - bool blocking, bool retry_eintr, - int* socket_pair, off_t start, - off_t length, pid_t* child) { - std::vector<std::string> args = { - "/proc/self/exe", - "--child_set_lock_on", - path, - "--child_set_lock_start", - absl::StrCat(start), - "--child_set_lock_len", - absl::StrCat(length), - "--socket_fd", - absl::StrCat(socket_pair ? socket_pair[1] : -1)}; - - if (for_write) { - args.push_back("--child_set_lock_write"); - } - - if (blocking) { - args.push_back("--blocking"); - } - - if (retry_eintr) { - args.push_back("--retry_eintr"); - } - - int execve_errno = 0; - ASSIGN_OR_RETURN_ERRNO( - auto cleanup, - ForkAndExec("/proc/self/exe", ExecveArray(args.begin(), args.end()), {}, - nullptr, child, &execve_errno)); - - if (execve_errno != 0) { - return PosixError(execve_errno, "execve"); - } - - if (socket_pair) { - // Wait for when a chill will start. - char c; - EXPECT_THAT(ReadFd(socket_pair[0], reinterpret_cast<void*>(&c), sizeof(c)), - SyscallSucceedsWithValue(sizeof(c))); - } - return std::move(cleanup); -} - -TEST(FcntlTest, FcntlDupWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_PATH)); - - int new_fd; - // Dup the descriptor and make sure it's the same file. - EXPECT_THAT(new_fd = fcntl(fd.get(), F_DUPFD, 0), SyscallSucceeds()); - - FileDescriptor nfd = FileDescriptor(new_fd); - ASSERT_NE(fd.get(), nfd.get()); - ASSERT_NO_ERRNO(CheckSameFile(fd, nfd)); - EXPECT_THAT(fcntl(nfd.get(), F_GETFL), SyscallSucceedsWithValue(O_PATH)); -} - -TEST(FcntlTest, SetFileStatusFlagWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_PATH)); - - EXPECT_THAT(fcntl(fd.get(), F_SETFL, 0), SyscallFailsWithErrno(EBADF)); -} - -TEST(FcntlTest, BadFcntlsWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_PATH)); - - EXPECT_THAT(fcntl(fd.get(), F_SETOWN, 0), SyscallFailsWithErrno(EBADF)); - EXPECT_THAT(fcntl(fd.get(), F_GETOWN, 0), SyscallFailsWithErrno(EBADF)); - - EXPECT_THAT(fcntl(fd.get(), F_SETOWN_EX, 0), SyscallFailsWithErrno(EBADF)); - EXPECT_THAT(fcntl(fd.get(), F_GETOWN_EX, 0), SyscallFailsWithErrno(EBADF)); -} - -TEST(FcntlTest, SetCloExecBadFD) { - // Open an eventfd file descriptor with FD_CLOEXEC descriptor flag not set. - FileDescriptor f = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, 0)); - auto fd = f.get(); - f.reset(); - ASSERT_THAT(fcntl(fd, F_GETFD), SyscallFailsWithErrno(EBADF)); - ASSERT_THAT(fcntl(fd, F_SETFD, FD_CLOEXEC), SyscallFailsWithErrno(EBADF)); -} - -TEST(FcntlTest, SetCloExec) { - // Open an eventfd file descriptor with FD_CLOEXEC descriptor flag not set. - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, 0)); - ASSERT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(0)); - - // Set the FD_CLOEXEC flag. - ASSERT_THAT(fcntl(fd.get(), F_SETFD, FD_CLOEXEC), SyscallSucceeds()); - ASSERT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC)); -} - -TEST(FcntlTest, SetCloExecWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - // Open a file descriptor with FD_CLOEXEC descriptor flag not set. - TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_PATH)); - ASSERT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(0)); - - // Set the FD_CLOEXEC flag. - ASSERT_THAT(fcntl(fd.get(), F_SETFD, FD_CLOEXEC), SyscallSucceeds()); - ASSERT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC)); -} - -TEST(FcntlTest, DupFDCloExecWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - // Open a file descriptor with FD_CLOEXEC descriptor flag not set. - TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_PATH)); - int nfd; - ASSERT_THAT(nfd = fcntl(fd.get(), F_DUPFD_CLOEXEC, 0), SyscallSucceeds()); - FileDescriptor dup_fd(nfd); - - // Check for the FD_CLOEXEC flag. - ASSERT_THAT(fcntl(dup_fd.get(), F_GETFD), - SyscallSucceedsWithValue(FD_CLOEXEC)); -} - -TEST(FcntlTest, ClearCloExec) { - // Open an eventfd file descriptor with FD_CLOEXEC descriptor flag set. - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, EFD_CLOEXEC)); - ASSERT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC)); - - // Clear the FD_CLOEXEC flag. - ASSERT_THAT(fcntl(fd.get(), F_SETFD, 0), SyscallSucceeds()); - ASSERT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(0)); -} - -TEST(FcntlTest, IndependentDescriptorFlags) { - // Open an eventfd file descriptor with FD_CLOEXEC descriptor flag not set. - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, 0)); - ASSERT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(0)); - - // Duplicate the descriptor. Ensure that it also doesn't have FD_CLOEXEC. - FileDescriptor newfd = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup()); - ASSERT_THAT(fcntl(newfd.get(), F_GETFD), SyscallSucceedsWithValue(0)); - - // Set FD_CLOEXEC on the first FD. - ASSERT_THAT(fcntl(fd.get(), F_SETFD, FD_CLOEXEC), SyscallSucceeds()); - ASSERT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC)); - - // Ensure that the second FD is unaffected by the change on the first. - ASSERT_THAT(fcntl(newfd.get(), F_GETFD), SyscallSucceedsWithValue(0)); -} - -// All file description flags passed to open appear in F_GETFL. -TEST(FcntlTest, GetAllFlags) { - TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - int flags = O_RDWR | O_DIRECT | O_SYNC | O_NONBLOCK | O_APPEND; - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), flags)); - - // Linux forces O_LARGEFILE on all 64-bit kernels and gVisor's is 64-bit. - int expected = flags | kOLargeFile; - - int rflags; - EXPECT_THAT(rflags = fcntl(fd.get(), F_GETFL), SyscallSucceeds()); - EXPECT_EQ(rflags, expected); -} - -// When O_PATH is specified in flags, flag bits other than O_CLOEXEC, -// O_DIRECTORY, and O_NOFOLLOW are ignored. -TEST(FcntlTest, GetOpathFlag) { - SKIP_IF(IsRunningWithVFS1()); - TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - int flags = O_RDWR | O_DIRECT | O_SYNC | O_NONBLOCK | O_APPEND | O_PATH | - O_NOFOLLOW | O_DIRECTORY; - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), flags)); - - int expected = O_PATH | O_NOFOLLOW | O_DIRECTORY; - - int rflags; - EXPECT_THAT(rflags = fcntl(fd.get(), F_GETFL), SyscallSucceeds()); - EXPECT_EQ(rflags, expected); -} - -TEST(FcntlTest, SetFlags) { - TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), 0)); - - int const flags = O_RDWR | O_DIRECT | O_SYNC | O_NONBLOCK | O_APPEND; - EXPECT_THAT(fcntl(fd.get(), F_SETFL, flags), SyscallSucceeds()); - - // Can't set O_RDWR or O_SYNC. - // Linux forces O_LARGEFILE on all 64-bit kernels and gVisor's is 64-bit. - int expected = O_DIRECT | O_NONBLOCK | O_APPEND | kOLargeFile; - - int rflags; - EXPECT_THAT(rflags = fcntl(fd.get(), F_GETFL), SyscallSucceeds()); - EXPECT_EQ(rflags, expected); -} - -void TestLock(int fd, short lock_type = F_RDLCK) { // NOLINT, type in flock - struct flock fl; - fl.l_type = lock_type; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - // len 0 locks all bytes despite how large the file grows. - fl.l_len = 0; - EXPECT_THAT(fcntl(fd, F_SETLK, &fl), SyscallSucceeds()); -} - -void TestLockBadFD(int fd, - short lock_type = F_RDLCK) { // NOLINT, type in flock - struct flock fl; - fl.l_type = lock_type; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - // len 0 locks all bytes despite how large the file grows. - fl.l_len = 0; - EXPECT_THAT(fcntl(fd, F_SETLK, &fl), SyscallFailsWithErrno(EBADF)); -} - -TEST_F(FcntlLockTest, SetLockBadFd) { TestLockBadFD(-1); } - -TEST_F(FcntlLockTest, SetLockDir) { - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_RDONLY, 0000)); - TestLock(fd.get()); -} - -TEST_F(FcntlLockTest, SetLockSymlink) { - SKIP_IF(IsRunningWithVFS1()); - - 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)); - TestLockBadFD(fd.get()); -} - -TEST_F(FcntlLockTest, SetLockProc) { - auto fd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/self/status", O_RDONLY, 0000)); - TestLock(fd.get()); -} - -TEST_F(FcntlLockTest, SetLockPipe) { - SKIP_IF(IsRunningWithVFS1()); - - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - - TestLock(fds[0]); - TestLockBadFD(fds[0], F_WRLCK); - - TestLock(fds[1], F_WRLCK); - TestLockBadFD(fds[1]); - - EXPECT_THAT(close(fds[0]), SyscallSucceeds()); - EXPECT_THAT(close(fds[1]), SyscallSucceeds()); -} - -TEST_F(FcntlLockTest, SetLockSocket) { - SKIP_IF(IsRunningWithVFS1()); - - 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()); - - TestLock(sock); - EXPECT_THAT(close(sock), SyscallSucceeds()); -} - -TEST_F(FcntlLockTest, SetLockBadOpenFlagsWrite) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY, 0666)); - - struct flock fl0; - fl0.l_type = F_WRLCK; - fl0.l_whence = SEEK_SET; - fl0.l_start = 0; - fl0.l_len = 0; // Lock all file - - // Expect that setting a write lock using a read only file descriptor - // won't work. - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl0), SyscallFailsWithErrno(EBADF)); -} - -TEST_F(FcntlLockTest, SetLockBadOpenFlagsRead) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_WRONLY, 0666)); - - struct flock fl1; - fl1.l_type = F_RDLCK; - fl1.l_whence = SEEK_SET; - fl1.l_start = 0; - // Same as SetLockBadFd. - fl1.l_len = 0; - - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl1), SyscallFailsWithErrno(EBADF)); -} - -TEST_F(FcntlLockTest, SetLockWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_PATH)); - - struct flock fl0; - fl0.l_type = F_WRLCK; - fl0.l_whence = SEEK_SET; - fl0.l_start = 0; - fl0.l_len = 0; // Lock all file - - // Expect that setting a write lock using a Opath file descriptor - // won't work. - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl0), SyscallFailsWithErrno(EBADF)); -} - -TEST_F(FcntlLockTest, SetLockUnlockOnNothing) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_UNLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - // Same as SetLockBadFd. - fl.l_len = 0; - - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); -} - -TEST_F(FcntlLockTest, SetWriteLockSingleProc) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd0 = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - // Same as SetLockBadFd. - fl.l_len = 0; - - EXPECT_THAT(fcntl(fd0.get(), F_SETLK, &fl), SyscallSucceeds()); - // Expect to be able to take the same lock on the same fd no problem. - EXPECT_THAT(fcntl(fd0.get(), F_SETLK, &fl), SyscallSucceeds()); - - FileDescriptor fd1 = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - // Expect to be able to take the same lock from a different fd but for - // the same process. - EXPECT_THAT(fcntl(fd1.get(), F_SETLK, &fl), SyscallSucceeds()); -} - -TEST_F(FcntlLockTest, SetReadLockMultiProc) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - // Same as SetLockBadFd. - fl.l_len = 0; - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - - // spawn a child process to take a read lock on the same file. - pid_t child_pid = 0; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(SubprocessLock( - file.path(), false /* write lock */, false /* nonblocking */, - false /* no eintr retry */, nullptr /* no socket fd */, fl.l_start, - fl.l_len, &child_pid)); - - int status = 0; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "Exited with code: " << status; -} - -TEST_F(FcntlLockTest, SetReadThenWriteLockMultiProc) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - // Same as SetLockBadFd. - fl.l_len = 0; - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - - // Assert that another process trying to lock on the same file will fail - // with EAGAIN. It's important that we keep the fd above open so that - // that the other process will contend with the lock. - pid_t child_pid = 0; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(SubprocessLock( - file.path(), true /* write lock */, false /* nonblocking */, - false /* no eintr retry */, nullptr /* no socket fd */, fl.l_start, - fl.l_len, &child_pid)); - - int status = 0; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == EAGAIN) - << "Exited with code: " << status; - - // Close the fd: we want to test that another process can acquire the - // lock after this point. - fd.reset(); - // Assert that another process can now acquire the lock. - - child_pid = 0; - auto cleanup2 = ASSERT_NO_ERRNO_AND_VALUE(SubprocessLock( - file.path(), true /* write lock */, false /* nonblocking */, - false /* no eintr retry */, nullptr /* no socket fd */, fl.l_start, - fl.l_len, &child_pid)); - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "Exited with code: " << status; -} - -TEST_F(FcntlLockTest, SetWriteThenReadLockMultiProc) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - // Same as SetReadThenWriteLockMultiProc. - - struct flock fl; - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - // Same as SetLockBadFd. - fl.l_len = 0; - - // Same as SetReadThenWriteLockMultiProc. - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - - // Same as SetReadThenWriteLockMultiProc. - pid_t child_pid = 0; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(SubprocessLock( - file.path(), false /* write lock */, false /* nonblocking */, - false /* no eintr retry */, nullptr /* no socket fd */, fl.l_start, - fl.l_len, &child_pid)); - - int status = 0; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == EAGAIN) - << "Exited with code: " << status; - - // Same as SetReadThenWriteLockMultiProc. - fd.reset(); // Close the fd. - - // Same as SetReadThenWriteLockMultiProc. - child_pid = 0; - auto cleanup2 = ASSERT_NO_ERRNO_AND_VALUE(SubprocessLock( - file.path(), false /* write lock */, false /* nonblocking */, - false /* no eintr retry */, nullptr /* no socket fd */, fl.l_start, - fl.l_len, &child_pid)); - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "Exited with code: " << status; -} - -TEST_F(FcntlLockTest, SetWriteLockMultiProc) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - // Same as SetReadThenWriteLockMultiProc. - - struct flock fl; - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - // Same as SetLockBadFd. - fl.l_len = 0; - - // Same as SetReadWriteLockMultiProc. - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - - // Same as SetReadWriteLockMultiProc. - pid_t child_pid = 0; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(SubprocessLock( - file.path(), true /* write lock */, false /* nonblocking */, - false /* no eintr retry */, nullptr /* no socket fd */, fl.l_start, - fl.l_len, &child_pid)); - int status = 0; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == EAGAIN) - << "Exited with code: " << status; - - fd.reset(); // Close the FD. - // Same as SetReadWriteLockMultiProc. - child_pid = 0; - auto cleanup2 = ASSERT_NO_ERRNO_AND_VALUE(SubprocessLock( - file.path(), true /* write lock */, false /* nonblocking */, - false /* no eintr retry */, nullptr /* no socket fd */, fl.l_start, - fl.l_len, &child_pid)); - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "Exited with code: " << status; -} - -TEST_F(FcntlLockTest, SetLockIsRegional) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 4096; - - // Same as SetReadWriteLockMultiProc. - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - - // Same as SetReadWriteLockMultiProc. - pid_t child_pid = 0; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - SubprocessLock(file.path(), true /* write lock */, - false /* nonblocking */, false /* no eintr retry */, - nullptr /* no socket fd */, fl.l_len, 0, &child_pid)); - int status = 0; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "Exited with code: " << status; -} - -TEST_F(FcntlLockTest, SetLockUpgradeDowngrade) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - // Same as SetLockBadFd. - fl.l_len = 0; - - // Same as SetReadWriteLockMultiProc. - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - - // Upgrade to a write lock. This will prevent anyone else from taking - // the lock. - fl.l_type = F_WRLCK; - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - - // Same as SetReadWriteLockMultiProc., - pid_t child_pid = 0; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(SubprocessLock( - file.path(), false /* write lock */, false /* nonblocking */, - false /* no eintr retry */, nullptr /* no socket fd */, fl.l_start, - fl.l_len, &child_pid)); - - int status = 0; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == EAGAIN) - << "Exited with code: " << status; - - // Downgrade back to a read lock. - fl.l_type = F_RDLCK; - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - - // Do the same stint as before, but this time it should succeed. - child_pid = 0; - auto cleanup2 = ASSERT_NO_ERRNO_AND_VALUE(SubprocessLock( - file.path(), false /* write lock */, false /* nonblocking */, - false /* no eintr retry */, nullptr /* no socket fd */, fl.l_start, - fl.l_len, &child_pid)); - - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "Exited with code: " << status; -} - -TEST_F(FcntlLockTest, SetLockDroppedOnClose) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - // While somewhat surprising, obtaining another fd to the same file and - // then closing it in this process drops *all* locks. - FileDescriptor other_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - // Same as SetReadThenWriteLockMultiProc. - - struct flock fl; - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - // Same as SetLockBadFd. - fl.l_len = 0; - - // Same as SetReadWriteLockMultiProc. - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - - other_fd.reset(); // Close. - - // Expect to be able to get the lock, given that the close above dropped it. - pid_t child_pid = 0; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(SubprocessLock( - file.path(), true /* write lock */, false /* nonblocking */, - false /* no eintr retry */, nullptr /* no socket fd */, fl.l_start, - fl.l_len, &child_pid)); - - int status = 0; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "Exited with code: " << status; -} - -TEST_F(FcntlLockTest, SetLockUnlock) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - // Setup two regional locks with different permissions. - struct flock fl0; - fl0.l_type = F_WRLCK; - fl0.l_whence = SEEK_SET; - fl0.l_start = 0; - fl0.l_len = 4096; - - struct flock fl1; - fl1.l_type = F_RDLCK; - fl1.l_whence = SEEK_SET; - fl1.l_start = 4096; - // Same as SetLockBadFd. - fl1.l_len = 0; - - // Set both region locks. - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl0), SyscallSucceeds()); - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl1), SyscallSucceeds()); - - // Another process should fail to take a read lock on the entire file - // due to the regional write lock. - pid_t child_pid = 0; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - SubprocessLock(file.path(), false /* write lock */, - false /* nonblocking */, false /* no eintr retry */, - nullptr /* no socket fd */, 0, 0, &child_pid)); - - int status = 0; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == EAGAIN) - << "Exited with code: " << status; - - // Then only unlock the writable one. This should ensure that other - // processes can take any read lock that it wants. - fl0.l_type = F_UNLCK; - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl0), SyscallSucceeds()); - - // Another process should now succeed to get a read lock on the entire file. - child_pid = 0; - auto cleanup2 = ASSERT_NO_ERRNO_AND_VALUE( - SubprocessLock(file.path(), false /* write lock */, - false /* nonblocking */, false /* no eintr retry */, - nullptr /* no socket fd */, 0, 0, &child_pid)); - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "Exited with code: " << status; -} - -TEST_F(FcntlLockTest, SetLockAcrossRename) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - // Setup two regional locks with different permissions. - struct flock fl; - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - // Same as SetLockBadFd. - fl.l_len = 0; - - // Set the region lock. - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - - // Rename the file to someplace nearby. - std::string const newpath = NewTempAbsPath(); - EXPECT_THAT(rename(file.path().c_str(), newpath.c_str()), SyscallSucceeds()); - - // Another process should fail to take a read lock on the renamed file - // since we still have an open handle to the inode. - pid_t child_pid = 0; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - SubprocessLock(newpath, false /* write lock */, false /* nonblocking */, - false /* no eintr retry */, nullptr /* no socket fd */, - fl.l_start, fl.l_len, &child_pid)); - - int status = 0; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == EAGAIN) - << "Exited with code: " << status; -} - -// NOTE: The blocking tests below aren't perfect. It's hard to assert exactly -// what the kernel did while handling a syscall. These tests are timing based -// because there really isn't any other reasonable way to assert that correct -// blocking behavior happened. - -// This test will verify that blocking works as expected when another process -// holds a write lock when obtaining a write lock. This test will hold the lock -// for some amount of time and then wait for the second process to send over the -// socket_fd the amount of time it was blocked for before the lock succeeded. -TEST_F(FcntlLockTest, SetWriteLockThenBlockingWriteLock) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; - - // Take the write lock. - ASSERT_THAT(fcntl(fd.get(), F_SETLKW, &fl), SyscallSucceeds()); - - // Attempt to take the read lock in a sub process. This will immediately block - // so we will release our lock after some amount of time and then assert the - // amount of time the other process was blocked for. - pid_t child_pid = 0; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(SubprocessLock( - file.path(), true /* write lock */, true /* Blocking Lock */, - true /* Retry on EINTR */, fds_ /* Socket fd for timing information */, - fl.l_start, fl.l_len, &child_pid)); - - // We will wait kHoldLockForSec before we release our lock allowing the - // subprocess to obtain it. - constexpr absl::Duration kHoldLockFor = absl::Seconds(5); - const int64_t kMinBlockTimeUsec = absl::ToInt64Microseconds(absl::Seconds(1)); - - absl::SleepFor(kHoldLockFor); - - // Unlock our write lock. - fl.l_type = F_UNLCK; - ASSERT_THAT(fcntl(fd.get(), F_SETLKW, &fl), SyscallSucceeds()); - - // Read the blocked time from the subprocess socket. - int64_t subprocess_blocked_time_usec = GetSubprocessFcntlTimeInUsec(); - - // We must have been waiting at least kMinBlockTime. - EXPECT_GT(subprocess_blocked_time_usec, kMinBlockTimeUsec); - - // The FCNTL write lock must always succeed as it will simply block until it - // can obtain the lock. - int status = 0; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "Exited with code: " << status; -} - -// This test will verify that blocking works as expected when another process -// holds a read lock when obtaining a write lock. This test will hold the lock -// for some amount of time and then wait for the second process to send over the -// socket_fd the amount of time it was blocked for before the lock succeeded. -TEST_F(FcntlLockTest, SetReadLockThenBlockingWriteLock) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; - - // Take the write lock. - ASSERT_THAT(fcntl(fd.get(), F_SETLKW, &fl), SyscallSucceeds()); - - // Attempt to take the read lock in a sub process. This will immediately block - // so we will release our lock after some amount of time and then assert the - // amount of time the other process was blocked for. - pid_t child_pid = 0; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(SubprocessLock( - file.path(), true /* write lock */, true /* Blocking Lock */, - true /* Retry on EINTR */, fds_ /* Socket fd for timing information */, - fl.l_start, fl.l_len, &child_pid)); - - // We will wait kHoldLockForSec before we release our lock allowing the - // subprocess to obtain it. - constexpr absl::Duration kHoldLockFor = absl::Seconds(5); - - const int64_t kMinBlockTimeUsec = absl::ToInt64Microseconds(absl::Seconds(1)); - - absl::SleepFor(kHoldLockFor); - - // Unlock our READ lock. - fl.l_type = F_UNLCK; - ASSERT_THAT(fcntl(fd.get(), F_SETLKW, &fl), SyscallSucceeds()); - - // Read the blocked time from the subprocess socket. - int64_t subprocess_blocked_time_usec = GetSubprocessFcntlTimeInUsec(); - - // We must have been waiting at least kMinBlockTime. - EXPECT_GT(subprocess_blocked_time_usec, kMinBlockTimeUsec); - - // The FCNTL write lock must always succeed as it will simply block until it - // can obtain the lock. - int status = 0; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "Exited with code: " << status; -} - -// This test will veirfy that blocking works as expected when another process -// holds a write lock when obtaining a read lock. This test will hold the lock -// for some amount of time and then wait for the second process to send over the -// socket_fd the amount of time it was blocked for before the lock succeeded. -TEST_F(FcntlLockTest, SetWriteLockThenBlockingReadLock) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; - - // Take the write lock. - ASSERT_THAT(fcntl(fd.get(), F_SETLKW, &fl), SyscallSucceeds()); - - // Attempt to take the read lock in a sub process. This will immediately block - // so we will release our lock after some amount of time and then assert the - // amount of time the other process was blocked for. - pid_t child_pid = 0; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(SubprocessLock( - file.path(), false /* read lock */, true /* Blocking Lock */, - true /* Retry on EINTR */, fds_ /* Socket fd for timing information */, - fl.l_start, fl.l_len, &child_pid)); - - // We will wait kHoldLockForSec before we release our lock allowing the - // subprocess to obtain it. - constexpr absl::Duration kHoldLockFor = absl::Seconds(5); - - const int64_t kMinBlockTimeUsec = absl::ToInt64Microseconds(absl::Seconds(1)); - - absl::SleepFor(kHoldLockFor); - - // Unlock our write lock. - fl.l_type = F_UNLCK; - ASSERT_THAT(fcntl(fd.get(), F_SETLKW, &fl), SyscallSucceeds()); - - // Read the blocked time from the subprocess socket. - int64_t subprocess_blocked_time_usec = GetSubprocessFcntlTimeInUsec(); - - // We must have been waiting at least kMinBlockTime. - EXPECT_GT(subprocess_blocked_time_usec, kMinBlockTimeUsec); - - // The FCNTL read lock must always succeed as it will simply block until it - // can obtain the lock. - int status = 0; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "Exited with code: " << status; -} - -// This test will verify that when one process only holds a read lock that -// another will not block while obtaining a read lock when F_SETLKW is used. -TEST_F(FcntlLockTest, SetReadLockThenBlockingReadLock) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; - - // Take the READ lock. - ASSERT_THAT(fcntl(fd.get(), F_SETLKW, &fl), SyscallSucceeds()); - - // Attempt to take the read lock in a sub process. Since multiple processes - // can hold a read lock this should immediately return without blocking - // even though we used F_SETLKW in the subprocess. - pid_t child_pid = 0; - auto sp = ASSERT_NO_ERRNO_AND_VALUE(SubprocessLock( - file.path(), false /* read lock */, true /* Blocking Lock */, - true /* Retry on EINTR */, nullptr /* No fd, should not block */, - fl.l_start, fl.l_len, &child_pid)); - - // We never release the lock and the subprocess should still obtain it without - // blocking for any period of time. - int status = 0; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "Exited with code: " << status; -} - -TEST(FcntlTest, GetO_ASYNC) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - int flag_fl = -1; - ASSERT_THAT(flag_fl = fcntl(s.get(), F_GETFL), SyscallSucceeds()); - EXPECT_EQ(flag_fl & O_ASYNC, 0); - - int flag_fd = -1; - ASSERT_THAT(flag_fd = fcntl(s.get(), F_GETFD), SyscallSucceeds()); - EXPECT_EQ(flag_fd & O_ASYNC, 0); -} - -TEST(FcntlTest, SetFlO_ASYNC) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - int before_fl = -1; - ASSERT_THAT(before_fl = fcntl(s.get(), F_GETFL), SyscallSucceeds()); - - int before_fd = -1; - ASSERT_THAT(before_fd = fcntl(s.get(), F_GETFD), SyscallSucceeds()); - - ASSERT_THAT(fcntl(s.get(), F_SETFL, before_fl | O_ASYNC), SyscallSucceeds()); - - int after_fl = -1; - ASSERT_THAT(after_fl = fcntl(s.get(), F_GETFL), SyscallSucceeds()); - EXPECT_EQ(after_fl, before_fl | O_ASYNC); - - int after_fd = -1; - ASSERT_THAT(after_fd = fcntl(s.get(), F_GETFD), SyscallSucceeds()); - EXPECT_EQ(after_fd, before_fd); -} - -TEST(FcntlTest, SetFdO_ASYNC) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - int before_fl = -1; - ASSERT_THAT(before_fl = fcntl(s.get(), F_GETFL), SyscallSucceeds()); - - int before_fd = -1; - ASSERT_THAT(before_fd = fcntl(s.get(), F_GETFD), SyscallSucceeds()); - - ASSERT_THAT(fcntl(s.get(), F_SETFD, before_fd | O_ASYNC), SyscallSucceeds()); - - int after_fl = -1; - ASSERT_THAT(after_fl = fcntl(s.get(), F_GETFL), SyscallSucceeds()); - EXPECT_EQ(after_fl, before_fl); - - int after_fd = -1; - ASSERT_THAT(after_fd = fcntl(s.get(), F_GETFD), SyscallSucceeds()); - EXPECT_EQ(after_fd, before_fd); -} - -TEST(FcntlTest, DupAfterO_ASYNC) { - FileDescriptor s1 = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - int before = -1; - ASSERT_THAT(before = fcntl(s1.get(), F_GETFL), SyscallSucceeds()); - - ASSERT_THAT(fcntl(s1.get(), F_SETFL, before | O_ASYNC), SyscallSucceeds()); - - FileDescriptor fd2 = ASSERT_NO_ERRNO_AND_VALUE(s1.Dup()); - - int after = -1; - ASSERT_THAT(after = fcntl(fd2.get(), F_GETFL), SyscallSucceeds()); - EXPECT_EQ(after & O_ASYNC, O_ASYNC); -} - -TEST(FcntlTest, GetOwnNone) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - // Use the raw syscall because the glibc wrapper may convert F_{GET,SET}OWN - // into F_{GET,SET}OWN_EX. - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_GETOWN), - SyscallSucceedsWithValue(0)); -} - -TEST(FcntlTest, GetOwnExNone) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - f_owner_ex owner = {}; - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_GETOWN_EX, &owner), - SyscallSucceedsWithValue(0)); -} - -TEST(FcntlTest, SetOwnInvalidPid) { - SKIP_IF(IsRunningWithVFS1()); - - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN, 12345678), - SyscallFailsWithErrno(ESRCH)); -} - -TEST(FcntlTest, SetOwnInvalidPgrp) { - SKIP_IF(IsRunningWithVFS1()); - - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN, -12345678), - SyscallFailsWithErrno(ESRCH)); -} - -TEST(FcntlTest, SetOwnPid) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - pid_t pid; - EXPECT_THAT(pid = getpid(), SyscallSucceeds()); - - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN, pid), - SyscallSucceedsWithValue(0)); - - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_GETOWN), - SyscallSucceedsWithValue(pid)); -} - -TEST(FcntlTest, SetOwnPgrp) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - pid_t pgid; - EXPECT_THAT(pgid = getpgrp(), SyscallSucceeds()); - - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN, -pgid), - SyscallSucceedsWithValue(0)); - - // Verify with F_GETOWN_EX; using F_GETOWN on Linux may incorrectly treat the - // negative return value as an error, converting the return value to -1 and - // setting errno accordingly. - f_owner_ex got_owner = {}; - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_GETOWN_EX, &got_owner), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(got_owner.type, F_OWNER_PGRP); - EXPECT_EQ(got_owner.pid, pgid); -} - -TEST(FcntlTest, SetOwnUnset) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - // Set and unset pid. - pid_t pid; - EXPECT_THAT(pid = getpid(), SyscallSucceeds()); - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN, pid), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN, 0), - SyscallSucceedsWithValue(0)); - - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_GETOWN), - SyscallSucceedsWithValue(0)); - - // Set and unset pgid. - pid_t pgid; - EXPECT_THAT(pgid = getpgrp(), SyscallSucceeds()); - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN, -pgid), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN, 0), - SyscallSucceedsWithValue(0)); - - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_GETOWN), - SyscallSucceedsWithValue(0)); -} - -// F_SETOWN flips the sign of negative values, an operation that is guarded -// against overflow. -TEST(FcntlTest, SetOwnOverflow) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN, INT_MIN), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(FcntlTest, SetOwnExInvalidType) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - f_owner_ex owner = {}; - owner.type = __pid_type(-1); - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN_EX, &owner), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(FcntlTest, SetOwnExInvalidTid) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - f_owner_ex owner = {}; - owner.type = F_OWNER_TID; - owner.pid = -1; - - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN_EX, &owner), - SyscallFailsWithErrno(ESRCH)); -} - -TEST(FcntlTest, SetOwnExInvalidPid) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - f_owner_ex owner = {}; - owner.type = F_OWNER_PID; - owner.pid = -1; - - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN_EX, &owner), - SyscallFailsWithErrno(ESRCH)); -} - -TEST(FcntlTest, SetOwnExInvalidPgrp) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - f_owner_ex owner = {}; - owner.type = F_OWNER_PGRP; - owner.pid = -1; - - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN_EX, &owner), - SyscallFailsWithErrno(ESRCH)); -} - -TEST(FcntlTest, SetOwnExTid) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - f_owner_ex owner = {}; - owner.type = F_OWNER_TID; - EXPECT_THAT(owner.pid = syscall(__NR_gettid), SyscallSucceeds()); - - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN_EX, &owner), - SyscallSucceedsWithValue(0)); - - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_GETOWN), - SyscallSucceedsWithValue(owner.pid)); -} - -TEST(FcntlTest, SetOwnExPid) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - f_owner_ex owner = {}; - owner.type = F_OWNER_PID; - EXPECT_THAT(owner.pid = getpid(), SyscallSucceeds()); - - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN_EX, &owner), - SyscallSucceedsWithValue(0)); - - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_GETOWN), - SyscallSucceedsWithValue(owner.pid)); -} - -TEST(FcntlTest, SetOwnExPgrp) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - f_owner_ex set_owner = {}; - set_owner.type = F_OWNER_PGRP; - EXPECT_THAT(set_owner.pid = getpgrp(), SyscallSucceeds()); - - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN_EX, &set_owner), - SyscallSucceedsWithValue(0)); - - // Verify with F_GETOWN_EX; using F_GETOWN on Linux may incorrectly treat the - // negative return value as an error, converting the return value to -1 and - // setting errno accordingly. - f_owner_ex got_owner = {}; - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_GETOWN_EX, &got_owner), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(got_owner.type, set_owner.type); - EXPECT_EQ(got_owner.pid, set_owner.pid); -} - -TEST(FcntlTest, SetOwnExUnset) { - SKIP_IF(IsRunningWithVFS1()); - - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - // Set and unset pid. - f_owner_ex owner = {}; - owner.type = F_OWNER_PID; - EXPECT_THAT(owner.pid = getpid(), SyscallSucceeds()); - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN_EX, &owner), - SyscallSucceedsWithValue(0)); - owner.pid = 0; - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN_EX, &owner), - SyscallSucceedsWithValue(0)); - - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_GETOWN), - SyscallSucceedsWithValue(0)); - - // Set and unset pgid. - owner.type = F_OWNER_PGRP; - EXPECT_THAT(owner.pid = getpgrp(), SyscallSucceeds()); - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN_EX, &owner), - SyscallSucceedsWithValue(0)); - owner.pid = 0; - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN_EX, &owner), - SyscallSucceedsWithValue(0)); - - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_GETOWN), - SyscallSucceedsWithValue(0)); -} - -TEST(FcntlTest, GetOwnExTid) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - f_owner_ex set_owner = {}; - set_owner.type = F_OWNER_TID; - EXPECT_THAT(set_owner.pid = syscall(__NR_gettid), SyscallSucceeds()); - - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN_EX, &set_owner), - SyscallSucceedsWithValue(0)); - - f_owner_ex got_owner = {}; - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_GETOWN_EX, &got_owner), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(got_owner.type, set_owner.type); - EXPECT_EQ(got_owner.pid, set_owner.pid); -} - -TEST(FcntlTest, GetOwnExPid) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - f_owner_ex set_owner = {}; - set_owner.type = F_OWNER_PID; - EXPECT_THAT(set_owner.pid = getpid(), SyscallSucceeds()); - - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN_EX, &set_owner), - SyscallSucceedsWithValue(0)); - - f_owner_ex got_owner = {}; - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_GETOWN_EX, &got_owner), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(got_owner.type, set_owner.type); - EXPECT_EQ(got_owner.pid, set_owner.pid); -} - -TEST(FcntlTest, GetOwnExPgrp) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - f_owner_ex set_owner = {}; - set_owner.type = F_OWNER_PGRP; - EXPECT_THAT(set_owner.pid = getpgrp(), SyscallSucceeds()); - - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN_EX, &set_owner), - SyscallSucceedsWithValue(0)); - - f_owner_ex got_owner = {}; - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_GETOWN_EX, &got_owner), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(got_owner.type, set_owner.type); - EXPECT_EQ(got_owner.pid, set_owner.pid); -} - -TEST(FcntlTest, SetSig) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETSIG, SIGUSR1), - SyscallSucceedsWithValue(0)); - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_GETSIG), - SyscallSucceedsWithValue(SIGUSR1)); -} - -TEST(FcntlTest, SetSigDefaultsToZero) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - // Defaults to returning the zero value, indicating default behavior (SIGIO). - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_GETSIG), - SyscallSucceedsWithValue(0)); -} - -TEST(FcntlTest, SetSigToDefault) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETSIG, SIGIO), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_GETSIG), - SyscallSucceedsWithValue(SIGIO)); - - // Can be reset to the default behavior. - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETSIG, 0), - SyscallSucceedsWithValue(0)); - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_GETSIG), - SyscallSucceedsWithValue(0)); -} - -TEST(FcntlTest, SetSigInvalid) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETSIG, SIGRTMAX + 1), - SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_GETSIG), - SyscallSucceedsWithValue(0)); -} - -TEST(FcntlTest, SetSigInvalidDoesNotResetPreviousChoice) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETSIG, SIGUSR1), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETSIG, SIGRTMAX + 1), - SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_GETSIG), - SyscallSucceedsWithValue(SIGUSR1)); -} - -TEST_F(FcntlSignalTest, SetSigDefault) { - const auto signal_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGIO)); - RegisterFD(pipe_read_fd_, 0); // Zero = default behavior - GenerateIOEvent(); - WaitForSignalDelivery(absl::Seconds(1)); - ASSERT_EQ(num_signals_received_, 1); - SignalDelivery sig = signals_received_.front(); - signals_received_.pop_front(); - EXPECT_EQ(sig.num, SIGIO); - EXPECT_EQ(sig.info.si_signo, SIGIO); - // siginfo contents is undefined in this case. -} - -TEST_F(FcntlSignalTest, SetSigCustom) { - const auto signal_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGUSR1)); - RegisterFD(pipe_read_fd_, SIGUSR1); - GenerateIOEvent(); - WaitForSignalDelivery(absl::Seconds(1)); - ASSERT_EQ(num_signals_received_, 1); - SignalDelivery sig = signals_received_.front(); - signals_received_.pop_front(); - EXPECT_EQ(sig.num, SIGUSR1); - EXPECT_EQ(sig.info.si_signo, SIGUSR1); - EXPECT_EQ(sig.info.si_fd, pipe_read_fd_); - EXPECT_EQ(sig.info.si_band, EPOLLIN | EPOLLRDNORM); -} - -TEST_F(FcntlSignalTest, SetSigUnregisterStillGetsSigio) { - const auto sigio_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGIO)); - const auto sigusr1_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGUSR1)); - RegisterFD(pipe_read_fd_, SIGUSR1); - RegisterFD(pipe_read_fd_, 0); - GenerateIOEvent(); - WaitForSignalDelivery(absl::Seconds(1)); - ASSERT_EQ(num_signals_received_, 1); - SignalDelivery sig = signals_received_.front(); - signals_received_.pop_front(); - EXPECT_EQ(sig.num, SIGIO); - // siginfo contents is undefined in this case. -} - -TEST_F(FcntlSignalTest, SetSigWithSigioStillGetsSiginfo) { - const auto signal_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGIO)); - RegisterFD(pipe_read_fd_, SIGIO); - GenerateIOEvent(); - WaitForSignalDelivery(absl::Seconds(1)); - ASSERT_EQ(num_signals_received_, 1); - SignalDelivery sig = signals_received_.front(); - EXPECT_EQ(sig.num, SIGIO); - EXPECT_EQ(sig.info.si_signo, SIGIO); - EXPECT_EQ(sig.info.si_fd, pipe_read_fd_); - EXPECT_EQ(sig.info.si_band, EPOLLIN | EPOLLRDNORM); -} - -TEST_F(FcntlSignalTest, SetSigDupThenCloseOld) { - const auto sigusr1_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGUSR1)); - RegisterFD(pipe_read_fd_, SIGUSR1); - DupReadFD(); - FlushAndCloseFD(pipe_read_fd_); - GenerateIOEvent(); - WaitForSignalDelivery(absl::Seconds(1)); - ASSERT_EQ(num_signals_received_, 1); - SignalDelivery sig = signals_received_.front(); - // We get a signal with the **old** FD (even though it is closed). - EXPECT_EQ(sig.num, SIGUSR1); - EXPECT_EQ(sig.info.si_signo, SIGUSR1); - EXPECT_EQ(sig.info.si_fd, pipe_read_fd_); - EXPECT_EQ(sig.info.si_band, EPOLLIN | EPOLLRDNORM); -} - -TEST_F(FcntlSignalTest, SetSigDupThenCloseNew) { - const auto sigusr1_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGUSR1)); - RegisterFD(pipe_read_fd_, SIGUSR1); - DupReadFD(); - FlushAndCloseFD(pipe_read_fd_dup_); - GenerateIOEvent(); - WaitForSignalDelivery(absl::Seconds(1)); - ASSERT_EQ(num_signals_received_, 1); - SignalDelivery sig = signals_received_.front(); - // We get a signal with the old FD. - EXPECT_EQ(sig.num, SIGUSR1); - EXPECT_EQ(sig.info.si_signo, SIGUSR1); - EXPECT_EQ(sig.info.si_fd, pipe_read_fd_); - EXPECT_EQ(sig.info.si_band, EPOLLIN | EPOLLRDNORM); -} - -TEST_F(FcntlSignalTest, SetSigDupOldRegistered) { - const auto sigusr1_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGUSR1)); - RegisterFD(pipe_read_fd_, SIGUSR1); - DupReadFD(); - GenerateIOEvent(); - WaitForSignalDelivery(absl::Seconds(1)); - ASSERT_EQ(num_signals_received_, 1); - SignalDelivery sig = signals_received_.front(); - // We get a signal with the old FD. - EXPECT_EQ(sig.num, SIGUSR1); - EXPECT_EQ(sig.info.si_signo, SIGUSR1); - EXPECT_EQ(sig.info.si_fd, pipe_read_fd_); - EXPECT_EQ(sig.info.si_band, EPOLLIN | EPOLLRDNORM); -} - -TEST_F(FcntlSignalTest, SetSigDupNewRegistered) { - const auto sigusr2_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGUSR2)); - DupReadFD(); - RegisterFD(pipe_read_fd_dup_, SIGUSR2); - GenerateIOEvent(); - WaitForSignalDelivery(absl::Seconds(1)); - ASSERT_EQ(num_signals_received_, 1); - SignalDelivery sig = signals_received_.front(); - // We get a signal with the new FD. - EXPECT_EQ(sig.num, SIGUSR2); - EXPECT_EQ(sig.info.si_signo, SIGUSR2); - EXPECT_EQ(sig.info.si_fd, pipe_read_fd_dup_); - EXPECT_EQ(sig.info.si_band, EPOLLIN | EPOLLRDNORM); -} - -TEST_F(FcntlSignalTest, SetSigDupBothRegistered) { - const auto sigusr1_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGUSR1)); - const auto sigusr2_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGUSR2)); - RegisterFD(pipe_read_fd_, SIGUSR1); - DupReadFD(); - RegisterFD(pipe_read_fd_dup_, SIGUSR2); - GenerateIOEvent(); - WaitForSignalDelivery(absl::Seconds(1)); - ASSERT_EQ(num_signals_received_, 1); - SignalDelivery sig = signals_received_.front(); - // We get a signal with the **new** signal number, but the **old** FD. - EXPECT_EQ(sig.num, SIGUSR2); - EXPECT_EQ(sig.info.si_signo, SIGUSR2); - EXPECT_EQ(sig.info.si_fd, pipe_read_fd_); - EXPECT_EQ(sig.info.si_band, EPOLLIN | EPOLLRDNORM); -} - -TEST_F(FcntlSignalTest, SetSigDupBothRegisteredAfterDup) { - const auto sigusr1_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGUSR1)); - const auto sigusr2_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGUSR2)); - DupReadFD(); - RegisterFD(pipe_read_fd_, SIGUSR1); - RegisterFD(pipe_read_fd_dup_, SIGUSR2); - GenerateIOEvent(); - WaitForSignalDelivery(absl::Seconds(1)); - ASSERT_EQ(num_signals_received_, 1); - SignalDelivery sig = signals_received_.front(); - // We get a signal with the **new** signal number, but the **old** FD. - EXPECT_EQ(sig.num, SIGUSR2); - EXPECT_EQ(sig.info.si_signo, SIGUSR2); - EXPECT_EQ(sig.info.si_fd, pipe_read_fd_); - EXPECT_EQ(sig.info.si_band, EPOLLIN | EPOLLRDNORM); -} - -TEST_F(FcntlSignalTest, SetSigDupUnregisterOld) { - const auto sigio_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGIO)); - const auto sigusr1_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGUSR1)); - const auto sigusr2_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGUSR2)); - RegisterFD(pipe_read_fd_, SIGUSR1); - DupReadFD(); - RegisterFD(pipe_read_fd_dup_, SIGUSR2); - RegisterFD(pipe_read_fd_, 0); // Should go back to SIGIO behavior. - GenerateIOEvent(); - WaitForSignalDelivery(absl::Seconds(1)); - ASSERT_EQ(num_signals_received_, 1); - SignalDelivery sig = signals_received_.front(); - // We get a signal with SIGIO. - EXPECT_EQ(sig.num, SIGIO); - // siginfo is undefined in this case. -} - -TEST_F(FcntlSignalTest, SetSigDupUnregisterNew) { - const auto sigio_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGIO)); - const auto sigusr1_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGUSR1)); - const auto sigusr2_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGUSR2)); - RegisterFD(pipe_read_fd_, SIGUSR1); - DupReadFD(); - RegisterFD(pipe_read_fd_dup_, SIGUSR2); - RegisterFD(pipe_read_fd_dup_, 0); // Should go back to SIGIO behavior. - GenerateIOEvent(); - WaitForSignalDelivery(absl::Seconds(1)); - ASSERT_EQ(num_signals_received_, 1); - SignalDelivery sig = signals_received_.front(); - // We get a signal with SIGIO. - EXPECT_EQ(sig.num, SIGIO); - // siginfo is undefined in this case. -} - -// Make sure that making multiple concurrent changes to async signal generation -// does not cause any race issues. -TEST(FcntlTest, SetFlSetOwnSetSigDoNotRace) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - pid_t pid; - EXPECT_THAT(pid = getpid(), SyscallSucceeds()); - - constexpr absl::Duration runtime = absl::Milliseconds(300); - auto set_async = [&s, &runtime] { - for (auto start = absl::Now(); absl::Now() - start < runtime;) { - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETFL, O_ASYNC), - SyscallSucceeds()); - sched_yield(); - } - }; - auto reset_async = [&s, &runtime] { - for (auto start = absl::Now(); absl::Now() - start < runtime;) { - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETFL, 0), SyscallSucceeds()); - sched_yield(); - } - }; - auto set_own = [&s, &pid, &runtime] { - for (auto start = absl::Now(); absl::Now() - start < runtime;) { - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN, pid), - SyscallSucceeds()); - sched_yield(); - } - }; - auto set_sig = [&s, &runtime] { - for (auto start = absl::Now(); absl::Now() - start < runtime;) { - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETSIG, SIGUSR1), - SyscallSucceeds()); - sched_yield(); - } - }; - - std::list<ScopedThread> threads; - for (int i = 0; i < 10; i++) { - threads.emplace_back(set_async); - threads.emplace_back(reset_async); - threads.emplace_back(set_own); - threads.emplace_back(set_sig); - } -} - -TEST_F(FcntlLockTest, GetLockOnNothing) { - SKIP_IF(IsRunningWithVFS1()); - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 40; - ASSERT_THAT(fcntl(fd.get(), F_GETLK, &fl), SyscallSucceeds()); - ASSERT_TRUE(fl.l_type == F_UNLCK); -} - -TEST_F(FcntlLockTest, GetLockOnLockSameProcess) { - SKIP_IF(IsRunningWithVFS1()); - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 40; - ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - ASSERT_THAT(fcntl(fd.get(), F_GETLK, &fl), SyscallSucceeds()); - ASSERT_TRUE(fl.l_type == F_UNLCK); - - fl.l_type = F_WRLCK; - ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - ASSERT_THAT(fcntl(fd.get(), F_GETLK, &fl), SyscallSucceeds()); - ASSERT_TRUE(fl.l_type == F_UNLCK); -} - -TEST_F(FcntlLockTest, GetReadLockOnReadLock) { - SKIP_IF(IsRunningWithVFS1()); - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 40; - ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - - pid_t child_pid = fork(); - if (child_pid == 0) { - TEST_CHECK(fcntl(fd.get(), F_GETLK, &fl) >= 0); - TEST_CHECK(fl.l_type == F_UNLCK); - _exit(0); - } - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); -} - -TEST_F(FcntlLockTest, GetReadLockOnWriteLock) { - SKIP_IF(IsRunningWithVFS1()); - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 40; - ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - - fl.l_type = F_RDLCK; - pid_t child_pid = fork(); - if (child_pid == 0) { - TEST_CHECK(fcntl(fd.get(), F_GETLK, &fl) >= 0); - TEST_CHECK(fl.l_type == F_WRLCK); - TEST_CHECK(fl.l_pid == getppid()); - _exit(0); - } - - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); -} - -TEST_F(FcntlLockTest, GetWriteLockOnReadLock) { - SKIP_IF(IsRunningWithVFS1()); - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 40; - ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - - fl.l_type = F_WRLCK; - pid_t child_pid = fork(); - if (child_pid == 0) { - TEST_CHECK(fcntl(fd.get(), F_GETLK, &fl) >= 0); - TEST_CHECK(fl.l_type == F_RDLCK); - TEST_CHECK(fl.l_pid == getppid()); - _exit(0); - } - - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); -} - -TEST_F(FcntlLockTest, GetWriteLockOnWriteLock) { - SKIP_IF(IsRunningWithVFS1()); - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 40; - ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - - pid_t child_pid = fork(); - if (child_pid == 0) { - TEST_CHECK(fcntl(fd.get(), F_GETLK, &fl) >= 0); - TEST_CHECK(fl.l_type == F_WRLCK); - TEST_CHECK(fl.l_pid == getppid()); - _exit(0); - } - - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); -} - -// Tests that the pid returned from F_GETLK is relative to the caller's PID -// namespace. -TEST_F(FcntlLockTest, GetLockRespectsPIDNamespace) { - SKIP_IF(IsRunningWithVFS1()); - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - std::string filename = file.path(); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(filename, O_RDWR, 0666)); - - // Lock in the parent process. - struct flock fl; - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 40; - ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - - auto child_getlk = [](void* filename) { - int fd = open((char*)filename, O_RDWR, 0666); - TEST_CHECK(fd >= 0); - - struct flock fl; - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 40; - TEST_CHECK(fcntl(fd, F_GETLK, &fl) >= 0); - TEST_CHECK(fl.l_type == F_WRLCK); - // Parent PID should be 0 in the child PID namespace. - TEST_CHECK(fl.l_pid == 0); - close(fd); - return 0; - }; - - // Set up child process in a new PID namespace. - constexpr int kStackSize = 4096; - Mapping stack = ASSERT_NO_ERRNO_AND_VALUE( - Mmap(nullptr, kStackSize, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0)); - pid_t child_pid; - ASSERT_THAT( - child_pid = clone(child_getlk, (char*)stack.ptr() + stack.len(), - CLONE_NEWPID | SIGCHLD, (void*)filename.c_str()), - SyscallSucceeds()); - - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); -} - -} // namespace - -} // namespace testing -} // namespace gvisor - -int set_lock() { - const std::string set_lock_on = absl::GetFlag(FLAGS_child_set_lock_on); - int socket_fd = absl::GetFlag(FLAGS_socket_fd); - int fd = open(set_lock_on.c_str(), O_RDWR, 0666); - if (fd == -1 && errno != 0) { - int err = errno; - std::cerr << "CHILD open " << set_lock_on << " failed: " << err - << std::endl; - return err; - } - - struct flock fl; - if (absl::GetFlag(FLAGS_child_set_lock_write)) { - fl.l_type = F_WRLCK; - } else { - fl.l_type = F_RDLCK; - } - fl.l_whence = SEEK_SET; - fl.l_start = absl::GetFlag(FLAGS_child_set_lock_start); - fl.l_len = absl::GetFlag(FLAGS_child_set_lock_len); - - if (socket_fd != -1) { - // Send signal to the parent. - char c = 0; - gvisor::testing::WriteFd(socket_fd, reinterpret_cast<void*>(&c), - sizeof(c)); - } - // Test the fcntl. - int err = 0; - int ret = 0; - - gvisor::testing::MonotonicTimer timer; - timer.Start(); - do { - ret = fcntl(fd, absl::GetFlag(FLAGS_blocking) ? F_SETLKW : F_SETLK, &fl); - } while (absl::GetFlag(FLAGS_retry_eintr) && ret == -1 && errno == EINTR); - auto usec = absl::ToInt64Microseconds(timer.Duration()); - - if (ret == -1 && errno != 0) { - err = errno; - std::cerr << "CHILD lock " << set_lock_on << " failed " << err << std::endl; - } - - // If there is a socket fd let's send back the time in microseconds it took - // to execute this syscall. - if (socket_fd != -1) { - gvisor::testing::WriteFd(socket_fd, reinterpret_cast<void*>(&usec), - sizeof(usec)); - close(socket_fd); - } - - close(fd); - return err; -} - -int main(int argc, char** argv) { - gvisor::testing::TestInit(&argc, &argv); - - if (!absl::GetFlag(FLAGS_child_set_lock_on).empty()) { - exit(set_lock()); - } - - return gvisor::testing::RunAllTests(); -} |