summaryrefslogtreecommitdiffhomepage
path: root/test/syscalls/linux/inotify.cc
diff options
context:
space:
mode:
Diffstat (limited to 'test/syscalls/linux/inotify.cc')
-rw-r--r--test/syscalls/linux/inotify.cc94
1 files changed, 66 insertions, 28 deletions
diff --git a/test/syscalls/linux/inotify.cc b/test/syscalls/linux/inotify.cc
index 8137f0e29..a88c89e20 100644
--- a/test/syscalls/linux/inotify.cc
+++ b/test/syscalls/linux/inotify.cc
@@ -36,6 +36,7 @@
#include "test/util/epoll_util.h"
#include "test/util/file_descriptor.h"
#include "test/util/fs_util.h"
+#include "test/util/multiprocess_util.h"
#include "test/util/posix_error.h"
#include "test/util/temp_path.h"
#include "test/util/test_util.h"
@@ -315,8 +316,7 @@ PosixErrorOr<std::vector<Event>> DrainEvents(int fd) {
}
PosixErrorOr<FileDescriptor> InotifyInit1(int flags) {
- int fd;
- EXPECT_THAT(fd = inotify_init1(flags), SyscallSucceeds());
+ int fd = inotify_init1(flags);
if (fd < 0) {
return PosixError(errno, "inotify_init1() failed");
}
@@ -325,9 +325,7 @@ PosixErrorOr<FileDescriptor> InotifyInit1(int flags) {
PosixErrorOr<int> InotifyAddWatch(int fd, const std::string& path,
uint32_t mask) {
- int wd;
- EXPECT_THAT(wd = inotify_add_watch(fd, path.c_str(), mask),
- SyscallSucceeds());
+ int wd = inotify_add_watch(fd, path.c_str(), mask);
if (wd < 0) {
return PosixError(errno, "inotify_add_watch() failed");
}
@@ -784,6 +782,38 @@ TEST(Inotify, MoveWatchedTargetGeneratesEvents) {
EXPECT_EQ(events[0].cookie, events[1].cookie);
}
+// Tests that close events are only emitted when a file description drops its
+// last reference.
+TEST(Inotify, DupFD) {
+ SKIP_IF(IsRunningWithVFS1());
+
+ const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+ const FileDescriptor inotify_fd =
+ ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK));
+
+ const int wd = ASSERT_NO_ERRNO_AND_VALUE(
+ InotifyAddWatch(inotify_fd.get(), file.path(), IN_ALL_EVENTS));
+
+ FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY));
+ FileDescriptor fd2 = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup());
+
+ std::vector<Event> events =
+ ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get()));
+ EXPECT_THAT(events, Are({
+ Event(IN_OPEN, wd),
+ }));
+
+ fd.reset();
+ events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get()));
+ EXPECT_THAT(events, Are({}));
+
+ fd2.reset();
+ events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get()));
+ EXPECT_THAT(events, Are({
+ Event(IN_CLOSE_NOWRITE, wd),
+ }));
+}
+
TEST(Inotify, CoalesceEvents) {
const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
const FileDescriptor fd =
@@ -1779,11 +1809,9 @@ TEST(Inotify, Sendfile) {
EXPECT_THAT(out_events, Are({Event(IN_MODIFY, out_wd)}));
}
-// On Linux, inotify behavior is not very consistent with splice(2). We try our
-// best to emulate Linux for very basic calls to splice.
TEST(Inotify, SpliceOnWatchTarget) {
- int pipes[2];
- ASSERT_THAT(pipe2(pipes, O_NONBLOCK), SyscallSucceeds());
+ int pipefds[2];
+ ASSERT_THAT(pipe2(pipefds, O_NONBLOCK), SyscallSucceeds());
const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
const FileDescriptor inotify_fd =
@@ -1798,15 +1826,20 @@ TEST(Inotify, SpliceOnWatchTarget) {
const int file_wd = ASSERT_NO_ERRNO_AND_VALUE(
InotifyAddWatch(inotify_fd.get(), file.path(), IN_ALL_EVENTS));
- EXPECT_THAT(splice(fd.get(), nullptr, pipes[1], nullptr, 1, /*flags=*/0),
+ EXPECT_THAT(splice(fd.get(), nullptr, pipefds[1], nullptr, 1, /*flags=*/0),
SyscallSucceedsWithValue(1));
- // Surprisingly, events are not generated in Linux if we read from a file.
+ // Surprisingly, events may not be generated in Linux if we read from a file.
+ // fs/splice.c:generic_file_splice_read, which is used most often, does not
+ // generate events, whereas fs/splice.c:default_file_splice_read does.
std::vector<Event> events =
ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get()));
- ASSERT_THAT(events, Are({}));
+ if (IsRunningOnGvisor() && !IsRunningWithVFS1()) {
+ ASSERT_THAT(events, Are({Event(IN_ACCESS, dir_wd, Basename(file.path())),
+ Event(IN_ACCESS, file_wd)}));
+ }
- EXPECT_THAT(splice(pipes[0], nullptr, fd.get(), nullptr, 1, /*flags=*/0),
+ EXPECT_THAT(splice(pipefds[0], nullptr, fd.get(), nullptr, 1, /*flags=*/0),
SyscallSucceedsWithValue(1));
events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get()));
@@ -1817,8 +1850,8 @@ TEST(Inotify, SpliceOnWatchTarget) {
}
TEST(Inotify, SpliceOnInotifyFD) {
- int pipes[2];
- ASSERT_THAT(pipe2(pipes, O_NONBLOCK), SyscallSucceeds());
+ int pipefds[2];
+ ASSERT_THAT(pipe2(pipefds, O_NONBLOCK), SyscallSucceeds());
const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
const FileDescriptor fd =
@@ -1834,11 +1867,11 @@ TEST(Inotify, SpliceOnInotifyFD) {
char buf;
EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds());
- EXPECT_THAT(splice(fd.get(), nullptr, pipes[1], nullptr,
+ EXPECT_THAT(splice(fd.get(), nullptr, pipefds[1], nullptr,
sizeof(struct inotify_event) + 1, SPLICE_F_NONBLOCK),
SyscallSucceedsWithValue(sizeof(struct inotify_event)));
- const FileDescriptor read_fd(pipes[0]);
+ const FileDescriptor read_fd(pipefds[0]);
const std::vector<Event> events =
ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(read_fd.get()));
ASSERT_THAT(events, Are({Event(IN_ACCESS, watcher)}));
@@ -1936,24 +1969,29 @@ TEST(Inotify, Xattr) {
}
TEST(Inotify, Exec) {
- const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
- const TempPath bin = ASSERT_NO_ERRNO_AND_VALUE(
- TempPath::CreateSymlinkTo(dir.path(), "/bin/true"));
-
+ SKIP_IF(IsRunningWithVFS1());
const FileDescriptor fd =
ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK));
const int wd = ASSERT_NO_ERRNO_AND_VALUE(
- InotifyAddWatch(fd.get(), bin.path(), IN_ALL_EVENTS));
+ InotifyAddWatch(fd.get(), "/bin/true", IN_ALL_EVENTS));
// Perform exec.
- ScopedThread t([&bin]() {
- ASSERT_THAT(execl(bin.path().c_str(), bin.path().c_str(), (char*)nullptr),
- SyscallSucceeds());
- });
- t.Join();
+ pid_t child = -1;
+ int execve_errno = -1;
+ auto kill = ASSERT_NO_ERRNO_AND_VALUE(
+ ForkAndExec("/bin/true", {}, {}, nullptr, &child, &execve_errno));
+ ASSERT_EQ(0, execve_errno);
+
+ int status;
+ ASSERT_THAT(RetryEINTR(waitpid)(child, &status, 0), SyscallSucceeds());
+ EXPECT_EQ(0, status);
+
+ // Process cleanup no longer needed.
+ kill.Release();
std::vector<Event> events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get()));
- EXPECT_THAT(events, Are({Event(IN_OPEN, wd), Event(IN_ACCESS, wd)}));
+ EXPECT_THAT(events, Are({Event(IN_OPEN, wd), Event(IN_ACCESS, wd),
+ Event(IN_CLOSE_NOWRITE, wd)}));
}
// Watches without IN_EXCL_UNLINK, should continue to emit events for file