Add inotify regression tests and gVisor tests

Signed-off-by: Zhenchen Wang <m202372036@hust.edu.cn>
This commit is contained in:
Zhenchen Wang 2025-11-17 23:27:59 +08:00 committed by Tate, Hongliang Tian
parent cf4f6e306c
commit eec3dd0f34
9 changed files with 411 additions and 1 deletions

View File

@ -568,7 +568,7 @@ impl InotifyEvent {
let actual_name_len = name.as_ref().map_or(0, |name| name.len() + 1);
// Calculate padded name length aligned to sizeof(struct inotify_event)
let pad_name_len = Self::round_event_name_len(actual_name_len);
Self {
header: InotifyEventHeader {
wd,

View File

@ -28,6 +28,7 @@ TEST_APPS := \
getcpu \
getpid \
hello_pie \
inotify \
itimer \
mmap \
mongoose \

View File

@ -0,0 +1,5 @@
# SPDX-License-Identifier: MPL-2.0
include ../test_common.mk
EXTRA_C_FLAGS :=

View File

@ -0,0 +1,238 @@
// SPDX-License-Identifier: MPL-2.0
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/inotify.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <time.h>
#include <unistd.h>
#define INOTIFY_EVENT_SIZE 16 // sizeof(struct inotify_event)
static void die(const char *msg)
{
perror(msg);
exit(1);
}
static void ensure_dir(const char *path)
{
if (mkdir(path, 0700) < 0 && errno != EEXIST)
die("mkdir");
}
static void create_file(const char *path)
{
int fd = open(path, O_CREAT | O_WRONLY, 0600);
if (fd < 0)
die("open");
close(fd);
}
// Round up to the nearest multiple of 16
static size_t round_to_16(size_t n)
{
return (n + 15) & ~15;
}
// Calculate expected event size based on name length
static size_t expected_event_size(const char *name)
{
size_t name_len = name ? strlen(name) + 1 : 0; // +1 for null terminator
size_t header_size = INOTIFY_EVENT_SIZE;
size_t padded_name_len = round_to_16(name_len);
return header_size + padded_name_len;
}
int main(void)
{
const char *dir = "inotify_align_tmp";
// Test files with different name lengths to test alignment
const char *test_files[] = {
"inotify_align_tmp/a", // 2 bytes name (1 char + null)
"inotify_align_tmp/ab", // 3 bytes name
"inotify_align_tmp/abc", // 4 bytes name
"inotify_align_tmp/abcd", // 5 bytes name
"inotify_align_tmp/abcdefgh", // 9 bytes name
"inotify_align_tmp/abcdefghijklmnop", // 17 bytes name
NULL
};
ensure_dir(dir);
int ifd = inotify_init1(0);
if (ifd < 0)
die("inotify_init1");
int wd = inotify_add_watch(ifd, dir, IN_CREATE);
if (wd < 0)
die("inotify_add_watch");
// Create test files to generate events
for (int i = 0; test_files[i] != NULL; i++) {
create_file(test_files[i]);
}
// Wait a bit for events to be queued
struct timespec ts = { .tv_sec = 0, .tv_nsec = 100 * 1000 * 1000 };
nanosleep(&ts, NULL);
// Check FIONREAD before reading
int pending = 0;
if (ioctl(ifd, FIONREAD, &pending) < 0)
die("ioctl(FIONREAD)");
printf("FIONREAD reports %d bytes pending\n", pending);
// Read all events
char buf[4096]
__attribute__((aligned(__alignof__(struct inotify_event))));
ssize_t total_read = read(ifd, buf, sizeof(buf));
if (total_read < 0)
die("read");
printf("Read %zd bytes total\n", total_read);
// Verify FIONREAD matches actual read size
if (total_read != pending) {
fprintf(stderr,
"ERROR: FIONREAD (%d) != actual read size (%zd)\n",
pending, total_read);
return 1;
}
// Parse and verify events
size_t offset = 0;
int event_count = 0;
const char *expected_names[] = { "a", "ab", "abc",
"abcd", "abcdefgh", "abcdefghijklmnop",
NULL };
while (offset < (size_t)total_read) {
if (offset + INOTIFY_EVENT_SIZE > (size_t)total_read) {
fprintf(stderr,
"ERROR: Incomplete event at offset %zu\n",
offset);
return 1;
}
struct inotify_event *event =
(struct inotify_event *)(buf + offset);
// Calculate expected size for this event
const char *expected_name = expected_names[event_count];
// Verify event is aligned
if ((uintptr_t)event % __alignof__(struct inotify_event) != 0) {
fprintf(stderr,
"ERROR: Event at offset %zu is not aligned\n",
offset);
return 1;
}
// Verify len field matches expected padded length
size_t actual_name_len =
expected_name ? strlen(expected_name) + 1 : 0;
size_t expected_padded_len = round_to_16(actual_name_len);
if (event->len != expected_padded_len) {
fprintf(stderr,
"ERROR: Event %d: len field is %u, expected %zu (name_len=%zu)\n",
event_count, event->len, expected_padded_len,
actual_name_len);
return 1;
}
// Verify event size is aligned to 16 bytes
size_t event_size = INOTIFY_EVENT_SIZE + event->len;
size_t expected_size = expected_event_size(expected_name);
if (event_size != expected_size) {
fprintf(stderr,
"ERROR: Event %d: total size %zu doesn't match expected %zu\n",
event_count, event_size, expected_size);
return 1;
}
if (event_size % INOTIFY_EVENT_SIZE != 0) {
fprintf(stderr,
"ERROR: Event %d: total size %zu is not aligned to %d bytes\n",
event_count, event_size, INOTIFY_EVENT_SIZE);
return 1;
}
// Verify name if present
if (expected_name && event->len > 0) {
if (strcmp(event->name, expected_name) != 0) {
fprintf(stderr,
"ERROR: Event %d: name mismatch: got '%s', expected '%s'\n",
event_count, event->name,
expected_name);
return 1;
}
// Verify padding bytes are zero
size_t actual_name_len_with_null =
strlen(expected_name) + 1;
if (event->len > actual_name_len_with_null) {
size_t padding_start =
actual_name_len_with_null;
for (size_t i = padding_start; i < event->len;
i++) {
if (event->name[i] != '\0') {
fprintf(stderr,
"ERROR: Event %d: padding byte at offset %zu is not zero (0x%02x)\n",
event_count, i,
(unsigned char)
event->name[i]);
return 1;
}
}
}
}
printf("Event %d: wd=%d, mask=0x%x, len=%u, name='%s', size=%zu (aligned: %s)\n",
event_count, event->wd, event->mask, event->len,
event->len > 0 ? event->name : "(none)", event_size,
(event_size % INOTIFY_EVENT_SIZE == 0) ? "yes" : "no");
// Move to next event
offset += event_size;
event_count++;
// Verify next event offset is aligned
if (offset < (size_t)total_read &&
offset % INOTIFY_EVENT_SIZE != 0) {
fprintf(stderr,
"ERROR: Next event offset %zu is not aligned to %d bytes\n",
offset, INOTIFY_EVENT_SIZE);
return 1;
}
}
// Cleanup
close(ifd);
for (int i = 0; test_files[i] != NULL; i++) {
unlink(test_files[i]);
}
rmdir(dir);
printf("\nAll alignment tests passed! Processed %d events.\n",
event_count);
printf("Summary:\n");
printf(" - FIONREAD matched actual read size: ✓\n");
printf(" - All events aligned to 16 bytes: ✓\n");
printf(" - All len fields correct (padded): ✓\n");
printf(" - All padding bytes are zero: ✓\n");
printf(" - Event offsets are aligned: ✓\n");
return 0;
}

View File

@ -0,0 +1,140 @@
// SPDX-License-Identifier: MPL-2.0
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <signal.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/inotify.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <sys/ioctl.h>
static void die(const char *msg)
{
perror(msg);
exit(1);
}
static void ensure_dir(const char *path)
{
if (mkdir(path, 0700) < 0 && errno != EEXIST)
die("mkdir");
}
static void touch_after_delay(const char *path)
{
struct timespec ts = { .tv_sec = 0, .tv_nsec = 200 * 1000 * 1000 };
nanosleep(&ts, NULL);
int fd = open(path, O_CREAT | O_WRONLY, 0600);
if (fd < 0)
die("open");
ssize_t wr = write(fd, "x", 1);
if (wr < 0)
die("write");
if (wr != 1) {
fprintf(stderr, "short write\n");
exit(1);
}
close(fd);
}
int main(void)
{
const char *dir = "inotify_tmp";
const char *file = "inotify_tmp/testfile";
ensure_dir(dir);
int ifd = inotify_init1(0);
if (ifd < 0)
die("inotify_init1");
int wd = inotify_add_watch(ifd, dir,
IN_CREATE | IN_MODIFY | IN_MOVED_TO);
if (wd < 0)
die("inotify_add_watch");
/* Subtest 0: without events, poll(0) should timeout */
{
struct pollfd p0 = { .fd = ifd,
.events = POLLIN,
.revents = 0 };
int r0 = poll(&p0, 1, 0);
if (r0 < 0)
die("poll");
if (r0 != 0) {
fprintf(stderr,
"unexpected ready without events: revents=0x%x\n",
p0.revents);
return 4;
}
}
pid_t pid = fork();
if (pid < 0)
die("fork");
if (pid == 0) {
touch_after_delay(file);
_exit(0);
}
struct pollfd pfd = { .fd = ifd, .events = POLLIN, .revents = 0 };
int pret = poll(&pfd, 1, 5000);
if (pret < 0)
die("poll");
if (pret == 0) {
fprintf(stderr, "poll timeout without events\n");
return 2;
}
if (!(pfd.revents & POLLIN)) {
fprintf(stderr, "unexpected revents: 0x%x\n", pfd.revents);
return 3;
}
char buf[4096]
__attribute__((aligned(__alignof__(struct inotify_event))));
/* FIONREAD should indicate pending bytes before read */
int pending = 0;
if (ioctl(ifd, FIONREAD, &pending) < 0)
die("ioctl(FIONREAD)");
if (pending <= 0) {
fprintf(stderr, "FIONREAD should be > 0 when POLLIN set\n");
return 5;
}
ssize_t len = read(ifd, buf, sizeof(buf));
if (len < 0)
die("read");
/* After drain, poll(0) should not fire */
{
struct pollfd p1 = { .fd = ifd,
.events = POLLIN,
.revents = 0 };
int r1 = poll(&p1, 1, 0);
if (r1 < 0)
die("poll");
if (r1 != 0) {
fprintf(stderr,
"still ready after drain: revents=0x%x\n",
p1.revents);
return 6;
}
}
int status = 0;
waitpid(pid, &status, 0);
close(ifd);
unlink(file);
rmdir(dir);
printf("inotify poll basic test: OK (bytes=%zd)\n", len);
return 0;
}

View File

@ -28,6 +28,8 @@ getcpu/getcpu
getpid/getpid
hello_pie/hello
hello_world/hello_world
inotify/inotify_align
inotify/inotify_poll
itimer/setitimer
itimer/timer_create
mmap/mmap_and_fork

View File

@ -19,6 +19,7 @@ TESTS ?= \
fsync_test \
futex_test \
getdents_test \
inotify_test \
ioctl_test \
link_test \
lseek_test \

View File

@ -0,0 +1,11 @@
Inotify.SymlinkGeneratesCreateEvent
Inotify.LinkGeneratesAttribAndCreateEvents
Inotify.HardlinksReuseSameWatch
Inotify.Fallocate
Inotify.LinkOnOtherParent
Inotify.Xattr
InotifyTest.NotifyNoDeadlock_NoRandomSave
Inotify.RemoveWatchAfterDeletingFileFails
Inotify.DeletingChildGeneratesEvents
Inotify.RmdirOnWatchedTargetGeneratesEvent
Inotify.UnmatchedEventsAreDiscarded

View File

@ -0,0 +1,12 @@
Inotify.MoveGeneratesEvents
Inotify.MoveWatchedTargetGeneratesEvents
Inotify.SpliceOnInotifyFD
Inotify.SpliceOnWatchTarget
Inotify.Exec
Inotify.IncludeUnlinkedFile_NoRandomSave
Inotify.ExcludeUnlink_NoRandomSave
Inotify.ExcludeUnlinkDirectory_NoRandomSave
Inotify.ExcludeUnlinkMultipleChildren_NoRandomSave
Inotify.ExcludeUnlinkInodeEvents_NoRandomSave
Inotify.OneShot
InotifyTest.InotifyAndTargetDestructionDoNotDeadlock_NoRandomSave