WSL/test/linux/unit_tests/inotify.c

997 lines
30 KiB
C

/*++
Copyright (c) Microsoft. All rights reserved.
Module Name:
inotify.c
Abstract:
This file contains extensive inotify unit tests.
--*/
#include "lxtcommon.h"
#include "unittests.h"
#include "lxtfs.h"
#include <poll.h>
#include <dirent.h>
#include <sys/epoll.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/inotify.h>
#include <sys/mount.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#define LXT_NAME "INOTIFY"
#define INOTIFY_TEST_BASE_DIR_LXFS "/data/inotify_test/"
#define INOTIFY_TEST_PROCFS_MAX_QUEUED_EVENTS_FILE "/proc/sys/fs/inotify/max_queued_events"
int TestInotifyComprehensive1Common(char* BaseDir);
int TestInotifyComprehensive2Common(char* BaseDir);
LXT_VARIATION_HANDLER TestInotifyNonBlockRead;
LXT_VARIATION_HANDLER TestInotifyEventQueueOverflow;
LXT_VARIATION_HANDLER TestInotifyEpollLxfs;
LXT_VARIATION_HANDLER TestInotifyBasicLxfs;
LXT_VARIATION_HANDLER TestInotifyComprehensive1Lxfs;
LXT_VARIATION_HANDLER TestInotifyComprehensive2Lxfs;
LXT_VARIATION_HANDLER TestInotifyPosixUnlinkRenameLxfs;
LXT_VARIATION_HANDLER TestInotifyUnmountBindLxfs;
LXT_VARIATION_HANDLER TestInotifyFtruncateLxfs;
LXT_VARIATION_HANDLER TestInotifyPseudoPlugin;
//
// Global constants
//
static const LXT_VARIATION g_LxtVariations[] = {
{"Test non-blocking read of inotify descriptor", TestInotifyNonBlockRead},
{"Test overflow of inotify event queue", TestInotifyEventQueueOverflow},
{"Test inotify with epoll - lxfs", TestInotifyEpollLxfs},
{"Test inotify watching basic paths - lxfs", TestInotifyBasicLxfs},
{"Comprehensive inotify tests 1 - lxfs", TestInotifyComprehensive1Lxfs},
{"Comprehensive inotify tests 2 - lxfs", TestInotifyComprehensive2Lxfs},
{"Test inotify with POSIX unlink/rename - lxfs", TestInotifyPosixUnlinkRenameLxfs},
{"Test unmounting of a bind mount - lxfs", TestInotifyUnmountBindLxfs},
{"Test ftruncate - lxfs", TestInotifyFtruncateLxfs},
{"Test inotify pseudo plugin", TestInotifyPseudoPlugin}};
int InotifyTestEntry(int Argc, char* Argv[])
/*++
--*/
{
LXT_ARGS Args;
int Result;
LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));
LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));
ErrorExit:
LxtUninitialize();
return !LXT_SUCCESS(Result);
}
int TestInotifyNonBlockRead(PLXT_ARGS Args)
{
int Id;
char Buf[10];
int Result;
//
// There is nothing to read here, but should not block.
//
LxtCheckErrno(Id = inotify_init1(IN_NONBLOCK));
LxtCheckErrnoFailure(Result = read(Id, Buf, 1), EAGAIN);
LxtCheckErrnoZeroSuccess(close(Id));
Result = LXT_RESULT_SUCCESS;
ErrorExit:
return Result;
}
int TestInotifyEventQueueOverflow(PLXT_ARGS Args)
{
int Id;
int Fd;
int ProcFd;
char Buf[11];
int Result;
int OriginalMaxQueuedEvents;
char InotifyBuf[500];
struct inotify_event* Events[INOTIFY_TEST_EVENTS_BUF_SIZE];
int NumEvents;
char TestFile1[PATH_MAX];
char TestFile2[PATH_MAX];
char TestFile1Hlink[PATH_MAX];
char TestFile1Slink[PATH_MAX];
//
// Initialize and also do cleanup if the files have not been removed.
//
sprintf(TestFile1, "%s%s", INOTIFY_TEST_BASE_DIR_LXFS, INOTIFY_TEST_FILE1_NAME_ONLY);
sprintf(TestFile2, "%s%s", INOTIFY_TEST_BASE_DIR_LXFS, INOTIFY_TEST_FILE2_NAME_ONLY);
sprintf(TestFile1Hlink, "%s%s", INOTIFY_TEST_BASE_DIR_LXFS, INOTIFY_TEST_FILE1_HLINK_NAME_ONLY);
sprintf(TestFile1Slink, "%s%s", INOTIFY_TEST_BASE_DIR_LXFS, INOTIFY_TEST_FILE1_SLINK_NAME_ONLY);
unlink(TestFile1);
unlink(TestFile2);
unlink(TestFile1Hlink);
unlink(TestFile1Slink);
rmdir(INOTIFY_TEST_BASE_DIR_LXFS);
LxtCheckErrnoZeroSuccess(mkdir(INOTIFY_TEST_BASE_DIR_LXFS, 0777));
LxtCheckErrno(Fd = creat(TestFile1, 0777));
LxtCheckErrnoZeroSuccess(close(Fd));
//
// Read the procFS value /proc/sys/fs/inotify/max_queued_events.
//
LxtCheckErrno(ProcFd = open(INOTIFY_TEST_PROCFS_MAX_QUEUED_EVENTS_FILE, O_RDWR));
LxtCheckErrno(Result = read(ProcFd, Buf, sizeof(Buf)));
OriginalMaxQueuedEvents = atoi(Buf);
LxtCheckNotEqual(OriginalMaxQueuedEvents, 0, "%d");
//
// Change the value to -1, verify failed.
//
sprintf(Buf, "-1");
LxtCheckErrnoFailure(Result = write(ProcFd, Buf, strlen(Buf)), EINVAL);
//
// Change the value to INT_MAX + 1 (2^31), verify failed.
//
sprintf(Buf, "2147483648");
LxtCheckErrnoFailure(Result = write(ProcFd, Buf, strlen(Buf)), EINVAL);
//
// Change the value to INT_MAX (2^31 - 1), verify succeeded.
//
sprintf(Buf, "2147483647");
LxtCheckErrno(Result = write(ProcFd, Buf, strlen(Buf)));
LxtCheckEqual(atoi(Buf), INT_MAX, "%d");
//
// Change the value to 2, and then read it back to verify.
//
sprintf(Buf, "2");
LxtCheckErrno(Result = write(ProcFd, Buf, strlen(Buf)));
LxtCheckErrno(Result = read(ProcFd, Buf, sizeof(Buf)));
LxtCheckEqual(atoi(Buf), 2, "%d");
//
// Generate 2 inotify events, verify that there is no overflow.
//
LxtCheckErrno(Id = inotify_init());
LxtCheckErrno(Result = inotify_add_watch(Id, TestFile1, IN_ALL_EVENTS));
LxtCheckErrno(Fd = open(TestFile1, O_RDWR));
LxtCheckErrnoZeroSuccess(close(Fd));
LxtCheckErrno(LxtFsInotifyReadAndProcess(Id, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));
LxtCheckEqual(NumEvents, 2, "%d");
LxtCheckEqual(Events[0]->mask, IN_OPEN, "%d");
LxtCheckEqual(Events[1]->mask, IN_CLOSE_WRITE, "%d");
//
// Generate 3 inotify events, verify that there is an overflow.
//
LxtCheckErrno(Fd = open(TestFile1, O_RDWR));
LxtCheckErrnoZeroSuccess(fchmod(Fd, 0666));
LxtCheckErrnoZeroSuccess(fchmod(Fd, 0777));
LxtCheckErrnoZeroSuccess(close(Fd));
LxtCheckErrno(LxtFsInotifyReadAndProcess(Id, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));
LxtCheckEqual(NumEvents, 3, "%d");
LxtCheckEqual(Events[0]->mask, IN_OPEN, "%d");
LxtCheckEqual(Events[1]->mask, IN_ATTRIB, "%d");
LxtCheckEqual(Events[2]->mask, IN_Q_OVERFLOW, "%d");
LxtCheckEqual(Events[2]->wd, -1, "%d");
LxtCheckEqual(Events[2]->cookie, 0, "%d");
LxtCheckEqual(Events[1]->len, 0, "%d");
//
// Restore the max_queued_events value to the original value read
// in the beginning, and verify.
//
sprintf(Buf, "%d", OriginalMaxQueuedEvents);
LxtCheckErrno(Result = write(ProcFd, Buf, strlen(Buf)));
LxtCheckErrno(Result = read(ProcFd, Buf, sizeof(Buf)));
LxtCheckEqual(atoi(Buf), OriginalMaxQueuedEvents, "%d");
Result = LXT_RESULT_SUCCESS;
ErrorExit:
close(Id);
close(Fd);
close(ProcFd);
unlink(TestFile1);
unlink(TestFile2);
unlink(TestFile1Hlink);
unlink(TestFile1Slink);
rmdir(INOTIFY_TEST_BASE_DIR_LXFS);
return Result;
}
int TestInotifyEpollLxfs(PLXT_ARGS Args)
{
return LxtFsInotifyEpollCommon(INOTIFY_TEST_BASE_DIR_LXFS);
}
int TestInotifyBasicLxfs(PLXT_ARGS Args)
{
int Id;
int Result;
int Wd[10];
//
// Test watching basic Lxfs paths.
//
LxtCheckErrno(Id = inotify_init());
LxtCheckErrno(Wd[0] = inotify_add_watch(Id, "/", IN_ALL_EVENTS));
LxtCheckErrno(Wd[1] = inotify_add_watch(Id, "/mnt", IN_ALL_EVENTS));
LxtCheckErrno(Wd[2] = inotify_add_watch(Id, "/mnt/", IN_ALL_EVENTS));
LxtCheckErrno(Wd[3] = inotify_add_watch(Id, "/proc", IN_ALL_EVENTS));
LxtCheckErrno(Wd[4] = inotify_add_watch(Id, "/sys", IN_ALL_EVENTS));
Result = LXT_RESULT_SUCCESS;
ErrorExit:
close(Id);
return Result;
}
int TestInotifyComprehensive1Lxfs(PLXT_ARGS Args)
{
return TestInotifyComprehensive1Common(INOTIFY_TEST_BASE_DIR_LXFS);
}
int TestInotifyComprehensive1Common(char* BaseDir)
/*++
--*/
{
int Fd;
int Id1;
int Id2;
int Wd[10];
char Buf[10];
char InotifyBuf[500];
struct inotify_event* Events[INOTIFY_TEST_EVENTS_BUF_SIZE];
int NumEvents;
int Bytes;
int Result;
int Index;
int AttribEvent;
int CreateEvent;
char TestFile1[PATH_MAX];
char TestFile2[PATH_MAX];
char TestFile1Hlink[PATH_MAX];
char TestFile1Slink[PATH_MAX];
//
// Initialize and also do cleanup if the files have not been removed.
//
sprintf(TestFile1, "%s%s", BaseDir, INOTIFY_TEST_FILE1_NAME_ONLY);
sprintf(TestFile2, "%s%s", BaseDir, INOTIFY_TEST_FILE2_NAME_ONLY);
sprintf(TestFile1Hlink, "%s%s", BaseDir, INOTIFY_TEST_FILE1_HLINK_NAME_ONLY);
sprintf(TestFile1Slink, "%s%s", BaseDir, INOTIFY_TEST_FILE1_SLINK_NAME_ONLY);
unlink(TestFile1);
unlink(TestFile2);
unlink(TestFile1Hlink);
unlink(TestFile1Slink);
rmdir(BaseDir);
LxtCheckErrnoZeroSuccess(mkdir(BaseDir, 0777));
LxtCheckErrno(Fd = creat(TestFile1, 0777));
LxtCheckErrnoZeroSuccess(close(Fd));
//
// Setup inotify.
//
LxtCheckErrno(Id1 = inotify_init());
LxtCheckErrno(Id2 = inotify_init());
LxtCheckErrno(Wd[0] = inotify_add_watch(Id1, TestFile1, IN_ALL_EVENTS));
//
// Check that "output params" can also be specified as input.
//
LxtCheckErrno(Wd[1] = inotify_add_watch(Id1, BaseDir, IN_ALL_EVENTS | IN_IGNORED | IN_ISDIR | IN_Q_OVERFLOW | IN_UNMOUNT));
LxtCheckEqual(Wd[0], 1, "%d");
LxtCheckEqual(Wd[1], 2, "%d");
//
// Test IN_OPEN, IN_ATTRIB, IN_MODIFY, IN_ACCESS, IN_CLOSE_WRITE.
//
LxtCheckErrno(Fd = open(TestFile1, O_RDWR));
LxtCheckErrnoZeroSuccess(fchmod(Fd, 0666));
LxtCheckErrno(Bytes = write(Fd, Buf, 10));
LxtCheckEqual(Bytes, 10, "%d");
LxtCheckErrno(Bytes = write(Fd, Buf, 10));
LxtCheckEqual(Bytes, 10, "%d");
LxtCheckErrnoZeroSuccess(lseek(Fd, 0, SEEK_SET));
LxtCheckErrno(Bytes = read(Fd, Buf, 10));
LxtCheckEqual(Bytes, 10, "%d");
LxtCheckErrno(Bytes = read(Fd, Buf, 10));
LxtCheckEqual(Bytes, 10, "%d");
LxtCheckErrnoZeroSuccess(fchmod(Fd, 0777));
LxtCheckErrnoZeroSuccess(close(Fd));
//
// Verify.
//
LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));
LxtCheckEqual(NumEvents, 16, "%d");
for (Index = 0; Index < 2; Index++)
{
LxtCheckEqual(Events[0 + Index]->mask, IN_OPEN, "%d");
LxtCheckEqual(Events[2 + Index]->mask, IN_ATTRIB, "%d");
LxtCheckEqual(Events[4 + Index]->mask, IN_MODIFY, "%d");
LxtCheckEqual(Events[6 + Index]->mask, IN_MODIFY, "%d");
LxtCheckEqual(Events[8 + Index]->mask, IN_ACCESS, "%d");
LxtCheckEqual(Events[10 + Index]->mask, IN_ACCESS, "%d");
LxtCheckEqual(Events[12 + Index]->mask, IN_ATTRIB, "%d");
LxtCheckEqual(Events[14 + Index]->mask, IN_CLOSE_WRITE, "%d");
}
for (Index = 0; Index < NumEvents; Index++)
{
LxtCheckEqual(Events[Index]->cookie, 0, "%d");
if ((Index % 2) == 0)
{
//
// The parent directory.
//
LxtCheckEqual(Events[Index]->wd, 2, "%d");
LxtCheckTrue(strcmp(Events[Index]->name, INOTIFY_TEST_FILE1_NAME_ONLY) == 0);
LxtCheckNotEqual(Events[Index]->len, 0, "%d");
}
else
{
//
// The file (child).
//
LxtCheckEqual(Events[Index]->wd, 1, "%d");
LxtCheckEqual(Events[Index]->len, 0, "%d");
}
}
//
// Test IN_CLOSE_NOWRITE.
//
LxtCheckErrno(Fd = open(TestFile1, O_RDONLY));
LxtCheckErrno(Bytes = read(Fd, Buf, 10));
LxtCheckEqual(Bytes, 10, "%d");
LxtCheckErrno(Bytes = read(Fd, Buf, 10));
LxtCheckEqual(Bytes, 10, "%d");
LxtCheckErrnoZeroSuccess(close(Fd));
//
// Verify.
//
LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));
LxtCheckEqual(NumEvents, 8, "%d");
//
// Test that opening an existing file with O_TRUNC generates IN_MODIFY,
// even if the open is for read-only access.
//
LxtCheckErrno(Fd = open(TestFile1, O_RDONLY | O_TRUNC));
LxtCheckErrnoZeroSuccess(close(Fd));
//
// Verify.
//
LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));
LxtCheckEqual(NumEvents, 6, "%d");
LxtCheckEqual(Events[0]->mask, IN_MODIFY, "%d");
LxtCheckEqual(Events[1]->mask, IN_MODIFY, "%d");
LxtCheckEqual(Events[2]->mask, IN_OPEN, "%d");
LxtCheckEqual(Events[3]->mask, IN_OPEN, "%d");
LxtCheckEqual(Events[4]->mask, IN_CLOSE_NOWRITE, "%d");
LxtCheckEqual(Events[5]->mask, IN_CLOSE_NOWRITE, "%d");
//
// Test that opening an existing file with only O_PATH generates IN_OPEN.
//
LxtCheckErrno(Fd = open(TestFile1, O_PATH));
LxtCheckErrnoZeroSuccess(close(Fd));
//
// Verify.
//
LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));
LxtCheckEqual(NumEvents, 4, "%d");
LxtCheckEqual(Events[0]->mask, IN_OPEN, "%d");
LxtCheckEqual(Events[1]->mask, IN_OPEN, "%d");
LxtCheckEqual(Events[2]->mask, IN_CLOSE_NOWRITE, "%d");
LxtCheckEqual(Events[3]->mask, IN_CLOSE_NOWRITE, "%d");
//
// Test IN_MOVED_FROM, IN_MOVED_TO, IN_MOVE_SELF
// (rename with no overwrite).
//
LxtCheckErrnoZeroSuccess(rename(TestFile1, TestFile2));
LxtCheckErrnoZeroSuccess(rename(TestFile2, TestFile1));
//
// Verify.
//
LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));
LxtCheckEqual(NumEvents, 6, "%d");
LxtCheckEqual(Events[0]->mask, IN_MOVED_FROM, "%d");
LxtCheckEqual(Events[1]->mask, IN_MOVED_TO, "%d");
LxtCheckEqual(Events[2]->mask, IN_MOVE_SELF, "%d");
LxtCheckEqual(Events[0]->cookie, Events[1]->cookie, "%d");
LxtCheckTrue(strcmp(Events[0]->name, INOTIFY_TEST_FILE1_NAME_ONLY) == 0);
LxtCheckTrue(strcmp(Events[1]->name, INOTIFY_TEST_FILE2_NAME_ONLY) == 0);
//
// Test IN_DELETE and IN_DELETE_SELF.
//
LxtCheckErrnoZeroSuccess(unlink(TestFile1));
//
// Verify.
//
LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));
LxtCheckEqual(NumEvents, 4, "%d");
LxtCheckEqual(Events[0]->mask, IN_ATTRIB, "%d");
LxtCheckEqual(Events[1]->mask, IN_DELETE_SELF, "%d");
LxtCheckEqual(Events[2]->mask, IN_IGNORED, "%d");
LxtCheckEqual(Events[3]->mask, IN_DELETE, "%d");
LxtCheckEqual(Events[0]->wd, 1, "%d");
LxtCheckEqual(Events[1]->wd, 1, "%d");
LxtCheckEqual(Events[2]->wd, 1, "%d");
LxtCheckEqual(Events[3]->wd, 2, "%d");
//
// Test IN_CREATE, and that inotify_rm_watch() generates IN_IGNORED.
//
LxtCheckErrno(Fd = creat(TestFile1, 0777));
LxtCheckErrnoZeroSuccess(close(Fd));
LxtCheckErrno(Wd[0] = inotify_add_watch(Id1, TestFile1, IN_ALL_EVENTS));
LxtCheckErrnoZeroSuccess(inotify_rm_watch(Id1, Wd[0]));
LxtCheckErrnoZeroSuccess(inotify_rm_watch(Id1, Wd[1]));
//
// Verify.
//
LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));
LxtCheckEqual(NumEvents, 5, "%d");
LxtCheckEqual(Events[0]->mask, IN_CREATE, "%d");
LxtCheckEqual(Events[1]->mask, IN_OPEN, "%d");
LxtCheckEqual(Events[2]->mask, IN_CLOSE_WRITE, "%d");
LxtCheckEqual(Events[3]->mask, IN_IGNORED, "%d");
LxtCheckEqual(Events[4]->mask, IN_IGNORED, "%d");
LxtCheckEqual(Events[0]->wd, 2, "%d");
LxtCheckEqual(Events[1]->wd, 2, "%d");
LxtCheckEqual(Events[2]->wd, 2, "%d");
LxtCheckEqual(Events[3]->wd, 3, "%d");
LxtCheckEqual(Events[4]->wd, 2, "%d");
//
// Test that IN_ONESHOT generates only one event and then IN_IGNORED.
//
LxtCheckErrno(Wd[0] = inotify_add_watch(Id1, TestFile1, IN_ALL_EVENTS | IN_ONESHOT));
LxtCheckErrno(Fd = open(TestFile1, O_RDONLY));
LxtCheckErrnoZeroSuccess(close(Fd));
//
// Verify.
//
LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));
LxtCheckEqual(NumEvents, 2, "%d");
LxtCheckEqual(Events[0]->mask, IN_OPEN, "%d");
LxtCheckEqual(Events[1]->mask, IN_IGNORED, "%d");
//
// Test IN_ONLYDIR on file, should fail.
//
LxtCheckErrnoFailure(Wd[1] = inotify_add_watch(Id1, TestFile1, IN_ALL_EVENTS | IN_ONLYDIR), ENOTDIR);
//
// Test operations on directories.
//
LxtCheckErrno(Wd[1] = inotify_add_watch(Id1, BaseDir, IN_ALL_EVENTS | IN_ONLYDIR));
LxtCheckErrno(Fd = open(BaseDir, O_RDONLY));
LxtCheckErrnoZeroSuccess(fchmod(Fd, 0666));
LxtCheckErrnoZeroSuccess(fchmod(Fd, 0777));
LxtCheckErrnoZeroSuccess(close(Fd));
//
// Verify.
//
LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));
LxtCheckEqual(NumEvents, 3, "%d");
LxtCheckEqual(Events[0]->mask, IN_OPEN | IN_ISDIR, "%d");
LxtCheckEqual(Events[1]->mask, IN_ATTRIB | IN_ISDIR, "%d");
LxtCheckEqual(Events[2]->mask, IN_CLOSE_NOWRITE | IN_ISDIR, "%d");
//
// Test creating a symbolic link.
//
LxtCheckErrno(Wd[0] = inotify_add_watch(Id1, TestFile1, IN_ALL_EVENTS));
LxtCheckErrnoZeroSuccess(symlink(TestFile1, TestFile1Slink));
//
// Verify.
//
LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));
LxtCheckEqual(NumEvents, 1, "%d");
LxtCheckEqual(Events[0]->mask, IN_CREATE, "%d");
LxtCheckEqual(Events[0]->wd, 5, "%d");
LxtCheckTrue(strcmp(Events[0]->name, INOTIFY_TEST_FILE1_SLINK_NAME_ONLY) == 0);
//
// Test creating a hard link.
//
LxtCheckErrnoZeroSuccess(link(TestFile1, TestFile1Hlink));
//
// Verify. Note that Ubuntu generates 2 events, whereas WSL generates 4 events.
// This is due to WSL performing unnecessary file opens, which will be fixed
// in the future. Also, the ordering of the events differs between Ubuntu and WSL.
//
LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));
LxtCheckTrue((NumEvents == 2) || (NumEvents == 4));
if (Events[0]->mask == IN_ATTRIB)
{
AttribEvent = 0;
CreateEvent = 1;
}
else
{
AttribEvent = 1;
CreateEvent = 0;
}
LxtCheckEqual(Events[AttribEvent]->mask, IN_ATTRIB, "%d");
LxtCheckEqual(Events[AttribEvent]->wd, 6, "%d");
LxtCheckEqual(Events[CreateEvent]->mask, IN_CREATE, "%d");
LxtCheckEqual(Events[CreateEvent]->wd, 5, "%d");
LxtCheckTrue(strcmp(Events[CreateEvent]->name, INOTIFY_TEST_FILE1_HLINK_NAME_ONLY) == 0);
Result = LXT_RESULT_SUCCESS;
ErrorExit:
close(Id1);
close(Id2);
close(Fd);
unlink(TestFile1);
unlink(TestFile2);
unlink(TestFile1Hlink);
unlink(TestFile1Slink);
rmdir(BaseDir);
return Result;
}
int TestInotifyComprehensive2Lxfs(PLXT_ARGS Args)
{
return TestInotifyComprehensive2Common(INOTIFY_TEST_BASE_DIR_LXFS);
}
int TestInotifyComprehensive2Common(char* BaseDir)
/*++
--*/
{
int Fd;
int Id1;
int Id2;
int Wd[10];
char Buf[10];
char InotifyBuf[500];
struct inotify_event* Events[INOTIFY_TEST_EVENTS_BUF_SIZE];
int NumEvents;
int Bytes;
int Result;
char TestFile1[PATH_MAX];
char TestFile2[PATH_MAX];
char TestFile1Hlink[PATH_MAX];
char TestFile1Slink[PATH_MAX];
//
// Initialize and also do cleanup if the files have not been removed.
//
sprintf(TestFile1, "%s%s", BaseDir, INOTIFY_TEST_FILE1_NAME_ONLY);
sprintf(TestFile2, "%s%s", BaseDir, INOTIFY_TEST_FILE2_NAME_ONLY);
sprintf(TestFile1Hlink, "%s%s", BaseDir, INOTIFY_TEST_FILE1_HLINK_NAME_ONLY);
sprintf(TestFile1Slink, "%s%s", BaseDir, INOTIFY_TEST_FILE1_SLINK_NAME_ONLY);
unlink(TestFile1);
unlink(TestFile2);
unlink(TestFile1Hlink);
unlink(TestFile1Slink);
rmdir(BaseDir);
LxtCheckErrnoZeroSuccess(mkdir(BaseDir, 0777));
LxtCheckErrno(Fd = creat(TestFile1, 0777));
LxtCheckErrnoZeroSuccess(close(Fd));
//
// Setup inotify.
//
LxtCheckErrno(Id1 = inotify_init());
LxtCheckErrno(Id2 = inotify_init());
//
// Test IN_EXCL_UNLINK on both the directory and the file to be unlinked.
// Also test deleting a file that has open handles to it.
//
LxtCheckErrno(
Wd[0] = // wd: 1
inotify_add_watch(Id1, TestFile1, IN_ALL_EVENTS | IN_EXCL_UNLINK));
LxtCheckErrno(
Wd[1] = // wd: 2
inotify_add_watch(Id1, BaseDir, IN_ALL_EVENTS | IN_EXCL_UNLINK));
LxtCheckErrno(Fd = open(TestFile1, O_RDWR));
unlink(TestFile1);
LxtCheckErrno(Bytes = write(Fd, Buf, 10));
LxtCheckErrnoZeroSuccess(close(Fd));
//
// Verify. Note that the write and close on the unlinked file did not
// generate any events on either the directory or the file since the
// IN_EXCL_UNLINK flag was set on both.
//
LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));
LxtCheckEqual(NumEvents, 6, "%d");
LxtCheckEqual(Wd[0], 1, "%d");
LxtCheckEqual(Wd[1], 2, "%d");
LxtCheckEqual(Events[0]->mask, IN_OPEN, "%d");
LxtCheckEqual(Events[1]->mask, IN_OPEN, "%d");
LxtCheckEqual(Events[2]->mask, IN_ATTRIB, "%d");
LxtCheckEqual(Events[3]->mask, IN_DELETE, "%d");
LxtCheckEqual(Events[4]->mask, IN_DELETE_SELF, "%d");
LxtCheckEqual(Events[5]->mask, IN_IGNORED, "%d");
LxtCheckEqual(Events[0]->wd, 2, "%d");
LxtCheckEqual(Events[1]->wd, 1, "%d");
LxtCheckEqual(Events[2]->wd, 1, "%d");
LxtCheckEqual(Events[3]->wd, 2, "%d");
LxtCheckEqual(Events[4]->wd, 1, "%d");
LxtCheckEqual(Events[5]->wd, 1, "%d");
//
// Test IN_EXCL_UNLINK on the directory only.
//
LxtCheckErrno(Fd = creat(TestFile1, 0777));
LxtCheckErrnoZeroSuccess(close(Fd));
LxtCheckErrnoZeroSuccess(close(Id1));
LxtCheckErrno(Id1 = inotify_init());
LxtCheckErrno(
Wd[0] = // wd: 1
inotify_add_watch(Id1, TestFile1, IN_ALL_EVENTS));
LxtCheckErrno(
Wd[1] = // wd: 2
inotify_add_watch(Id1, BaseDir, IN_ALL_EVENTS | IN_EXCL_UNLINK));
LxtCheckErrno(Fd = open(TestFile1, O_RDWR));
unlink(TestFile1);
LxtCheckErrno(Bytes = write(Fd, Buf, 10));
LxtCheckErrnoZeroSuccess(close(Fd));
//
// Verify. Note that the file events are still generated even though it was
// unlinked, because the file does not have the IN_EXCL_UNLINK flag set.
//
LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));
LxtCheckEqual(NumEvents, 8, "%d");
LxtCheckEqual(Wd[0], 1, "%d");
LxtCheckEqual(Wd[1], 2, "%d");
LxtCheckEqual(Events[0]->mask, IN_OPEN, "%d");
LxtCheckEqual(Events[1]->mask, IN_OPEN, "%d");
LxtCheckEqual(Events[2]->mask, IN_ATTRIB, "%d");
LxtCheckEqual(Events[3]->mask, IN_DELETE, "%d");
LxtCheckEqual(Events[4]->mask, IN_MODIFY, "%d");
LxtCheckEqual(Events[5]->mask, IN_CLOSE_WRITE, "%d");
LxtCheckEqual(Events[6]->mask, IN_DELETE_SELF, "%d");
LxtCheckEqual(Events[7]->mask, IN_IGNORED, "%d");
LxtCheckEqual(Events[0]->wd, 2, "%d");
LxtCheckEqual(Events[1]->wd, 1, "%d");
LxtCheckEqual(Events[2]->wd, 1, "%d");
LxtCheckEqual(Events[3]->wd, 2, "%d");
LxtCheckEqual(Events[4]->wd, 1, "%d");
LxtCheckEqual(Events[5]->wd, 1, "%d");
LxtCheckEqual(Events[6]->wd, 1, "%d");
LxtCheckEqual(Events[7]->wd, 1, "%d");
//
// Test IN_EXCL_UNLINK on the unlinked file only.
//
LxtCheckErrno(Fd = creat(TestFile1, 0777));
LxtCheckErrnoZeroSuccess(close(Fd));
LxtCheckErrnoZeroSuccess(close(Id1));
LxtCheckErrno(Id1 = inotify_init());
LxtCheckErrno(
Wd[0] = // wd: 1
inotify_add_watch(Id1, TestFile1, IN_ALL_EVENTS | IN_EXCL_UNLINK));
LxtCheckErrno(
Wd[1] = // wd: 2
inotify_add_watch(Id1, BaseDir, IN_ALL_EVENTS));
LxtCheckErrno(Fd = open(TestFile1, O_RDWR));
unlink(TestFile1);
LxtCheckErrno(Bytes = write(Fd, Buf, 10));
LxtCheckErrnoZeroSuccess(close(Fd));
//
// Verify. Note that the directory still receives the events from the unlinked
// child, because the directory does not have the IN_EXCL_UNLINK flag set.
//
LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));
LxtCheckEqual(NumEvents, 8, "%d");
LxtCheckEqual(Wd[0], 1, "%d");
LxtCheckEqual(Wd[1], 2, "%d");
LxtCheckEqual(Events[0]->mask, IN_OPEN, "%d");
LxtCheckEqual(Events[1]->mask, IN_OPEN, "%d");
LxtCheckEqual(Events[2]->mask, IN_ATTRIB, "%d");
LxtCheckEqual(Events[3]->mask, IN_DELETE, "%d");
LxtCheckEqual(Events[4]->mask, IN_MODIFY, "%d");
LxtCheckEqual(Events[5]->mask, IN_CLOSE_WRITE, "%d");
LxtCheckEqual(Events[6]->mask, IN_DELETE_SELF, "%d");
LxtCheckEqual(Events[7]->mask, IN_IGNORED, "%d");
LxtCheckEqual(Events[0]->wd, 2, "%d");
LxtCheckEqual(Events[1]->wd, 1, "%d");
LxtCheckEqual(Events[2]->wd, 1, "%d");
LxtCheckEqual(Events[3]->wd, 2, "%d");
LxtCheckEqual(Events[4]->wd, 2, "%d");
LxtCheckEqual(Events[5]->wd, 2, "%d");
LxtCheckEqual(Events[6]->wd, 1, "%d");
LxtCheckEqual(Events[7]->wd, 1, "%d");
//
// Test watching the same file twice.
//
LxtCheckErrno(Fd = creat(TestFile1, 0777));
LxtCheckErrnoZeroSuccess(close(Fd));
LxtCheckErrnoZeroSuccess(close(Id1));
LxtCheckErrno(Id1 = inotify_init());
LxtCheckErrno(
Wd[0] = // wd: 1
inotify_add_watch(Id1, TestFile1, IN_OPEN));
LxtCheckErrno(
Wd[1] = // wd: 1
inotify_add_watch(Id1, TestFile1, IN_CLOSE));
LxtCheckErrno(Fd = open(TestFile1, O_WRONLY));
LxtCheckErrnoZeroSuccess(close(Fd));
//
// Verify that IN_CLOSE_WRITE is received, and that IN_OPEN is not received.
//
LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));
LxtCheckEqual(NumEvents, 1, "%d");
LxtCheckEqual(Events[0]->mask, IN_CLOSE_WRITE, "%d");
LxtCheckEqual(Events[0]->wd, 1, "%d");
LxtCheckEqual(Wd[0], Wd[1], "%d");
//
// Test watching the same file twice, but with IN_MASK_ADD.
//
LxtCheckErrno(Fd = creat(TestFile1, 0777));
LxtCheckErrnoZeroSuccess(close(Fd));
LxtCheckErrnoZeroSuccess(close(Id1));
LxtCheckErrno(Id1 = inotify_init());
LxtCheckErrno(
Wd[0] = // wd: 1
inotify_add_watch(Id1, TestFile1, IN_OPEN));
LxtCheckErrno(
Wd[1] = // wd: 1
inotify_add_watch(Id1, TestFile1, IN_CLOSE | IN_MASK_ADD));
LxtCheckErrno(Fd = open(TestFile1, O_RDONLY));
LxtCheckErrnoZeroSuccess(close(Fd));
//
// Verify that both IN_OPEN and IN_CLOSE_NOWRITE are received.
//
LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));
LxtCheckEqual(NumEvents, 2, "%d");
LxtCheckEqual(Events[0]->mask, IN_OPEN, "%d");
LxtCheckEqual(Events[1]->mask, IN_CLOSE_NOWRITE, "%d");
LxtCheckEqual(Events[0]->wd, 1, "%d");
LxtCheckEqual(Events[1]->wd, 1, "%d");
LxtCheckEqual(Wd[0], Wd[1], "%d");
Result = LXT_RESULT_SUCCESS;
ErrorExit:
close(Id1);
close(Id2);
close(Fd);
unlink(TestFile1);
rmdir(BaseDir);
return Result;
}
int TestInotifyPosixUnlinkRenameLxfs(PLXT_ARGS Args)
{
return LxtFsInotifyPosixUnlinkRenameCommon(INOTIFY_TEST_BASE_DIR_LXFS);
}
int TestInotifyUnmountBindLxfs(PLXT_ARGS Args)
{
return LxtFsInotifyUnmountBindCommon(INOTIFY_TEST_BASE_DIR_LXFS);
}
int TestInotifyFtruncateLxfs(PLXT_ARGS Args)
{
int ChildPid;
int Fd;
struct pollfd PollFd;
int Result;
char TestFile1[PATH_MAX];
ChildPid = -1;
Fd = -1;
//
// Initialize and also do cleanup if the files have not been removed.
//
sprintf(TestFile1, "%s%s", INOTIFY_TEST_BASE_DIR_LXFS, INOTIFY_TEST_FILE1_NAME_ONLY);
unlink(TestFile1);
rmdir(INOTIFY_TEST_BASE_DIR_LXFS);
LxtCheckErrnoZeroSuccess(mkdir(INOTIFY_TEST_BASE_DIR_LXFS, 0777));
LxtCheckErrno(Fd = creat(TestFile1, 0777));
LxtCheckErrno(ftruncate(Fd, 1024));
LxtCheckErrno(ChildPid = fork());
if (ChildPid == 0)
{
sleep(2);
LxtCheckErrno(ftruncate(Fd, 1024));
fsync(Fd);
goto ErrorExit;
}
int Id = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
LxtCheckErrno(inotify_add_watch(Id, TestFile1, IN_ALL_EVENTS));
PollFd.fd = Id;
PollFd.events = POLLIN;
LxtCheckErrno(ppoll(&PollFd, 1, NULL, NULL));
Result = LXT_RESULT_SUCCESS;
ErrorExit:
if (Fd != -1)
{
LxtClose(Fd);
}
if (ChildPid == 0)
{
_exit(Result);
}
return Result;
}
int TestInotifyPseudoPlugin(PLXT_ARGS Args)
{
int Id;
int Result;
LxtCheckErrno(Id = inotify_init());
LxtCheckErrno(inotify_add_watch(Id, "/proc/self/ns/pid", IN_ALL_EVENTS));
ErrorExit:
if (Id != -1)
{
LxtClose(Id);
}
return Result;
}