1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
2f08c3bdfSopenharmony_ci/*
3f08c3bdfSopenharmony_ci * Copyright (c) 2022 CTERA Networks. All Rights Reserved.
4f08c3bdfSopenharmony_ci *
5f08c3bdfSopenharmony_ci * Started by Amir Goldstein <amir73il@gmail.com>
6f08c3bdfSopenharmony_ci * based on reproducer from Ivan Delalande <colona@arista.com>
7f08c3bdfSopenharmony_ci */
8f08c3bdfSopenharmony_ci
9f08c3bdfSopenharmony_ci/*\
10f08c3bdfSopenharmony_ci * [Description]
11f08c3bdfSopenharmony_ci * Test opening files after receiving IN_DELETE.
12f08c3bdfSopenharmony_ci *
13f08c3bdfSopenharmony_ci * Kernel v5.13 has a regression allowing files to be open after IN_DELETE.
14f08c3bdfSopenharmony_ci *
15f08c3bdfSopenharmony_ci * The problem has been fixed by commit:
16f08c3bdfSopenharmony_ci *  a37d9a17f099 "fsnotify: invalidate dcache before IN_DELETE event".
17f08c3bdfSopenharmony_ci */
18f08c3bdfSopenharmony_ci
19f08c3bdfSopenharmony_ci#include "config.h"
20f08c3bdfSopenharmony_ci
21f08c3bdfSopenharmony_ci#include <stdio.h>
22f08c3bdfSopenharmony_ci#include <unistd.h>
23f08c3bdfSopenharmony_ci#include <fcntl.h>
24f08c3bdfSopenharmony_ci#include <signal.h>
25f08c3bdfSopenharmony_ci#include <sys/wait.h>
26f08c3bdfSopenharmony_ci
27f08c3bdfSopenharmony_ci#include "tst_test.h"
28f08c3bdfSopenharmony_ci#include "tst_safe_macros.h"
29f08c3bdfSopenharmony_ci#include "inotify.h"
30f08c3bdfSopenharmony_ci
31f08c3bdfSopenharmony_ci#if defined(HAVE_SYS_INOTIFY_H)
32f08c3bdfSopenharmony_ci#include <sys/inotify.h>
33f08c3bdfSopenharmony_ci
34f08c3bdfSopenharmony_ci/* Number of files to test */
35f08c3bdfSopenharmony_ci#define CHURN_FILES 9999
36f08c3bdfSopenharmony_ci
37f08c3bdfSopenharmony_ci#define EVENT_MAX 32
38f08c3bdfSopenharmony_ci/* Size of the event structure, not including the name */
39f08c3bdfSopenharmony_ci#define EVENT_SIZE	(sizeof(struct inotify_event))
40f08c3bdfSopenharmony_ci#define EVENT_BUF_LEN	(EVENT_MAX * (EVENT_SIZE + 16))
41f08c3bdfSopenharmony_ci
42f08c3bdfSopenharmony_cistatic pid_t pid;
43f08c3bdfSopenharmony_ci
44f08c3bdfSopenharmony_cistatic char event_buf[EVENT_BUF_LEN];
45f08c3bdfSopenharmony_ci
46f08c3bdfSopenharmony_cistatic void churn(void)
47f08c3bdfSopenharmony_ci{
48f08c3bdfSopenharmony_ci	char path[10];
49f08c3bdfSopenharmony_ci	int i;
50f08c3bdfSopenharmony_ci
51f08c3bdfSopenharmony_ci	for (i = 0; i <= CHURN_FILES; ++i) {
52f08c3bdfSopenharmony_ci		snprintf(path, sizeof(path), "%d", i);
53f08c3bdfSopenharmony_ci		SAFE_FILE_PRINTF(path, "1");
54f08c3bdfSopenharmony_ci		SAFE_UNLINK(path);
55f08c3bdfSopenharmony_ci	}
56f08c3bdfSopenharmony_ci}
57f08c3bdfSopenharmony_ci
58f08c3bdfSopenharmony_cistatic void verify_inotify(void)
59f08c3bdfSopenharmony_ci{
60f08c3bdfSopenharmony_ci	int nevents = 0, opened = 0;
61f08c3bdfSopenharmony_ci	struct inotify_event *event;
62f08c3bdfSopenharmony_ci	int inotify_fd;
63f08c3bdfSopenharmony_ci
64f08c3bdfSopenharmony_ci	inotify_fd = SAFE_MYINOTIFY_INIT();
65f08c3bdfSopenharmony_ci	SAFE_MYINOTIFY_ADD_WATCH(inotify_fd, ".", IN_DELETE);
66f08c3bdfSopenharmony_ci
67f08c3bdfSopenharmony_ci	pid = SAFE_FORK();
68f08c3bdfSopenharmony_ci	if (pid == 0) {
69f08c3bdfSopenharmony_ci		SAFE_CLOSE(inotify_fd);
70f08c3bdfSopenharmony_ci		churn();
71f08c3bdfSopenharmony_ci		return;
72f08c3bdfSopenharmony_ci	}
73f08c3bdfSopenharmony_ci
74f08c3bdfSopenharmony_ci	while (!opened && nevents < CHURN_FILES) {
75f08c3bdfSopenharmony_ci		int i, fd, len;
76f08c3bdfSopenharmony_ci
77f08c3bdfSopenharmony_ci		len = SAFE_READ(0, inotify_fd, event_buf, EVENT_BUF_LEN);
78f08c3bdfSopenharmony_ci
79f08c3bdfSopenharmony_ci		for (i = 0; i < len; i += EVENT_SIZE + event->len) {
80f08c3bdfSopenharmony_ci			event = (struct inotify_event *)&event_buf[i];
81f08c3bdfSopenharmony_ci
82f08c3bdfSopenharmony_ci			if (!(event->mask & IN_DELETE))
83f08c3bdfSopenharmony_ci				continue;
84f08c3bdfSopenharmony_ci
85f08c3bdfSopenharmony_ci			nevents++;
86f08c3bdfSopenharmony_ci
87f08c3bdfSopenharmony_ci			/* Open file after IN_DELETE should fail */
88f08c3bdfSopenharmony_ci			fd = open(event->name, O_RDONLY);
89f08c3bdfSopenharmony_ci			if (fd < 0)
90f08c3bdfSopenharmony_ci				continue;
91f08c3bdfSopenharmony_ci
92f08c3bdfSopenharmony_ci			tst_res(TFAIL, "File %s opened after IN_DELETE", event->name);
93f08c3bdfSopenharmony_ci			SAFE_CLOSE(fd);
94f08c3bdfSopenharmony_ci			opened = 1;
95f08c3bdfSopenharmony_ci			break;
96f08c3bdfSopenharmony_ci		}
97f08c3bdfSopenharmony_ci	}
98f08c3bdfSopenharmony_ci
99f08c3bdfSopenharmony_ci	SAFE_CLOSE(inotify_fd);
100f08c3bdfSopenharmony_ci
101f08c3bdfSopenharmony_ci	if (!nevents)
102f08c3bdfSopenharmony_ci		tst_res(TFAIL, "Didn't get any IN_DELETE events");
103f08c3bdfSopenharmony_ci	else if (!opened)
104f08c3bdfSopenharmony_ci		tst_res(TPASS, "Got %d IN_DELETE events", nevents);
105f08c3bdfSopenharmony_ci
106f08c3bdfSopenharmony_ci	/* Kill the child creating / deleting files and wait for it */
107f08c3bdfSopenharmony_ci	SAFE_KILL(pid, SIGKILL);
108f08c3bdfSopenharmony_ci	pid = 0;
109f08c3bdfSopenharmony_ci	SAFE_WAIT(NULL);
110f08c3bdfSopenharmony_ci}
111f08c3bdfSopenharmony_ci
112f08c3bdfSopenharmony_cistatic void cleanup(void)
113f08c3bdfSopenharmony_ci{
114f08c3bdfSopenharmony_ci	if (pid) {
115f08c3bdfSopenharmony_ci		SAFE_KILL(pid, SIGKILL);
116f08c3bdfSopenharmony_ci		SAFE_WAIT(NULL);
117f08c3bdfSopenharmony_ci	}
118f08c3bdfSopenharmony_ci}
119f08c3bdfSopenharmony_ci
120f08c3bdfSopenharmony_cistatic struct tst_test test = {
121f08c3bdfSopenharmony_ci	.needs_tmpdir = 1,
122f08c3bdfSopenharmony_ci	.forks_child = 1,
123f08c3bdfSopenharmony_ci	.cleanup = cleanup,
124f08c3bdfSopenharmony_ci	.test_all = verify_inotify,
125f08c3bdfSopenharmony_ci	.tags = (const struct tst_tag[]) {
126f08c3bdfSopenharmony_ci		{"linux-git", "a37d9a17f099"},
127f08c3bdfSopenharmony_ci		{}
128f08c3bdfSopenharmony_ci	}
129f08c3bdfSopenharmony_ci};
130f08c3bdfSopenharmony_ci
131f08c3bdfSopenharmony_ci#else
132f08c3bdfSopenharmony_ci	TST_TEST_TCONF("system doesn't have required inotify support");
133f08c3bdfSopenharmony_ci#endif
134