// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2022 CTERA Networks. All Rights Reserved. * * Started by Amir Goldstein * based on reproducer from Ivan Delalande */ /*\ * [Description] * * Test opening files after receiving IN_DELETE. * * Kernel v5.13 has a regression allowing files to be open after IN_DELETE. * * The problem has been fixed by commit: * a37d9a17f099 ("fsnotify: invalidate dcache before IN_DELETE event"). */ #include "config.h" #include #include #include #include #include #include "tst_test.h" #include "tst_safe_macros.h" #include "inotify.h" #if defined(HAVE_SYS_INOTIFY_H) #include /* Number of files to test */ #define CHURN_FILES 9999 #define EVENT_MAX 32 /* Size of the event structure, not including the name */ #define EVENT_SIZE (sizeof(struct inotify_event)) #define EVENT_BUF_LEN (EVENT_MAX * (EVENT_SIZE + 16)) static pid_t pid; static char event_buf[EVENT_BUF_LEN]; static void churn(void) { char path[10]; int i; for (i = 0; i <= CHURN_FILES; ++i) { snprintf(path, sizeof(path), "%d", i); SAFE_FILE_PRINTF(path, "1"); SAFE_UNLINK(path); } } static void verify_inotify(void) { int nevents = 0, opened = 0; struct inotify_event *event; int inotify_fd; inotify_fd = SAFE_MYINOTIFY_INIT(); SAFE_MYINOTIFY_ADD_WATCH(inotify_fd, ".", IN_DELETE); pid = SAFE_FORK(); if (pid == 0) { SAFE_CLOSE(inotify_fd); churn(); return; } while (!opened && nevents < CHURN_FILES) { int i, fd, len; len = SAFE_READ(0, inotify_fd, event_buf, EVENT_BUF_LEN); for (i = 0; i < len; i += EVENT_SIZE + event->len) { event = (struct inotify_event *)&event_buf[i]; if (!(event->mask & IN_DELETE)) continue; nevents++; /* Open file after IN_DELETE should fail */ fd = open(event->name, O_RDONLY); if (fd < 0) continue; tst_res(TFAIL, "File %s opened after IN_DELETE", event->name); SAFE_CLOSE(fd); opened = 1; break; } } SAFE_CLOSE(inotify_fd); if (!nevents) tst_res(TFAIL, "Didn't get any IN_DELETE events"); else if (!opened) tst_res(TPASS, "Got %d IN_DELETE events", nevents); /* Kill the child creating / deleting files and wait for it */ SAFE_KILL(pid, SIGKILL); pid = 0; SAFE_WAIT(NULL); } static void cleanup(void) { if (pid) { SAFE_KILL(pid, SIGKILL); SAFE_WAIT(NULL); } } static struct tst_test test = { .needs_tmpdir = 1, .forks_child = 1, .cleanup = cleanup, .test_all = verify_inotify, .tags = (const struct tst_tag[]) { {"linux-git", "a37d9a17f099"}, {} } }; #else TST_TEST_TCONF("system doesn't have required inotify support"); #endif