• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2020 CTERA Networks. All Rights Reserved.
4  *
5  * Started by Amir Goldstein <amir73il@gmail.com>
6  *
7  * DESCRIPTION
8  *     Check that event is reported to watching parent and watching child
9  *     based on their interest
10  *
11  * Test case #3 is a regression test for commit fecc4559780d that fixes
12  * a bug introduced in kernel v5.9:
13  *
14  *     fsnotify: fix events reported to watching parent and child
15  */
16 
17 #include "config.h"
18 
19 #if defined(HAVE_SYS_INOTIFY_H)
20 # include <sys/inotify.h>
21 #endif
22 #include <errno.h>
23 #include <string.h>
24 #include "tst_test.h"
25 #include "inotify.h"
26 
27 #if defined(HAVE_SYS_INOTIFY_H)
28 
29 #define EVENT_MAX 10
30 /* Size of the event structure, not including the name */
31 #define EVENT_SIZE  (sizeof(struct inotify_event))
32 #define EVENT_BUF_LEN        (EVENT_MAX * (EVENT_SIZE + 16))
33 
34 
35 #define BUF_SIZE 256
36 
37 struct event_t {
38 	char name[BUF_SIZE];
39 	unsigned int mask;
40 	int wd;
41 };
42 
43 #define	TEST_DIR	"test_dir"
44 #define	TEST_FILE	"test_file"
45 
46 static struct tcase {
47 	const char *tname;
48 	unsigned int parent_mask;
49 	unsigned int subdir_mask;
50 	unsigned int child_mask;
51 	unsigned int parent_mask_other;
52 	unsigned int subdir_mask_other;
53 	unsigned int child_mask_other;
54 } tcases[] = {
55 	{
56 		"Group with parent and child watches",
57 		IN_ATTRIB, IN_ATTRIB, IN_ATTRIB,
58 		0, 0, 0,
59 	},
60 	{
61 		"Group with child watches and other group with parent watch",
62 		0, IN_ATTRIB, IN_ATTRIB,
63 		IN_ATTRIB, 0, 0,
64 	},
65 	{
66 		"Group with parent watch and other group with child watches",
67 		IN_ATTRIB, 0, 0,
68 		0, IN_ATTRIB, IN_ATTRIB,
69 	},
70 	{
71 		"Two Groups with parent and child watches for different events",
72 		IN_ATTRIB, IN_OPEN, IN_OPEN,
73 		IN_OPEN, IN_ATTRIB, IN_ATTRIB,
74 	},
75 };
76 
77 struct event_t event_set[EVENT_MAX];
78 
79 char event_buf[EVENT_BUF_LEN];
80 
81 int fd_notify, fd_notify_other;
82 
verify_inotify(unsigned int n)83 static void verify_inotify(unsigned int n)
84 {
85 	struct tcase *tc = &tcases[n];
86 	int i = 0, test_num = 0, len;
87 	int wd_parent = 0, wd_subdir = 0, wd_child = 0;
88 	int test_cnt = 0;
89 
90 	tst_res(TINFO, "Test #%d: %s", n, tc->tname);
91 
92 	fd_notify = SAFE_MYINOTIFY_INIT();
93 	fd_notify_other = SAFE_MYINOTIFY_INIT();
94 
95 	/* Setup watches on parent dir and children */
96 	if (tc->parent_mask)
97 		wd_parent = SAFE_MYINOTIFY_ADD_WATCH(fd_notify, ".", tc->parent_mask);
98 	if (tc->subdir_mask)
99 		wd_subdir = SAFE_MYINOTIFY_ADD_WATCH(fd_notify, TEST_DIR, tc->subdir_mask);
100 	if (tc->child_mask)
101 		wd_child = SAFE_MYINOTIFY_ADD_WATCH(fd_notify, TEST_FILE, tc->child_mask);
102 	/*
103 	 * Setup watches on "other" group to verify no intereferecne with our group.
104 	 * We do not check events reported to the "other" group.
105 	 */
106 	if (tc->parent_mask_other)
107 		SAFE_MYINOTIFY_ADD_WATCH(fd_notify_other, ".", tc->parent_mask_other);
108 	if (tc->subdir_mask_other)
109 		SAFE_MYINOTIFY_ADD_WATCH(fd_notify_other, TEST_DIR, tc->subdir_mask_other);
110 	if (tc->child_mask_other)
111 		SAFE_MYINOTIFY_ADD_WATCH(fd_notify_other, TEST_FILE, tc->child_mask_other);
112 
113 	/*
114 	 * Generate IN_ATTRIB events on file and subdir that should be reported to parent
115 	 * dir with name and to children without name if they have IN_ATTRIB in their mask.
116 	 */
117 	SAFE_CHMOD(TEST_DIR, 0755);
118 	SAFE_CHMOD(TEST_FILE, 0644);
119 
120 	if (wd_parent && (tc->parent_mask & IN_ATTRIB)) {
121 		event_set[test_cnt].wd = wd_parent;
122 		event_set[test_cnt].mask = tc->parent_mask | IN_ISDIR;
123 		strcpy(event_set[test_cnt].name, TEST_DIR);
124 		test_cnt++;
125 	}
126 	if (wd_subdir && (tc->subdir_mask & IN_ATTRIB)) {
127 		event_set[test_cnt].wd = wd_subdir;
128 		event_set[test_cnt].mask = tc->subdir_mask | IN_ISDIR;
129 		strcpy(event_set[test_cnt].name, "");
130 		test_cnt++;
131 	}
132 	if (wd_parent && (tc->parent_mask & IN_ATTRIB)) {
133 		event_set[test_cnt].wd = wd_parent;
134 		event_set[test_cnt].mask = tc->parent_mask;
135 		strcpy(event_set[test_cnt].name, TEST_FILE);
136 		test_cnt++;
137 	}
138 	if (wd_child && (tc->child_mask & IN_ATTRIB)) {
139 		event_set[test_cnt].wd = wd_child;
140 		event_set[test_cnt].mask = tc->child_mask;
141 		strcpy(event_set[test_cnt].name, "");
142 		test_cnt++;
143 	}
144 
145 	len = read(fd_notify, event_buf, EVENT_BUF_LEN);
146 	if (len == -1)
147 		tst_brk(TBROK | TERRNO, "read failed");
148 
149 	while (i < len) {
150 		struct event_t *expected = &event_set[test_num];
151 		struct inotify_event *event;
152 		event = (struct inotify_event *)&event_buf[i];
153 		if (test_num >= test_cnt) {
154 			tst_res(TFAIL,
155 				"got unnecessary event: "
156 				"wd=%d mask=%04x len=%u "
157 				"name=\"%.*s\"", event->wd, event->mask,
158 				event->len, event->len, event->name);
159 
160 		} else if (expected->wd == event->wd &&
161 			   expected->mask == event->mask &&
162 			   !strncmp(expected->name, event->name, event->len)) {
163 			tst_res(TPASS,
164 				"got event: wd=%d mask=%04x "
165 				"cookie=%u len=%u name=\"%.*s\"",
166 				event->wd, event->mask, event->cookie,
167 				event->len, event->len, event->name);
168 
169 		} else {
170 			tst_res(TFAIL, "got event: wd=%d (expected %d) "
171 				"mask=%04x (expected %x) len=%u "
172 				"name=\"%.*s\" (expected \"%s\")",
173 				event->wd, expected->wd,
174 				event->mask, expected->mask,
175 				event->len, event->len,
176 				event->name, expected->name);
177 		}
178 		test_num++;
179 		i += EVENT_SIZE + event->len;
180 	}
181 
182 	for (; test_num < test_cnt; test_num++) {
183 		tst_res(TFAIL, "didn't get event: mask=%04x ",
184 			event_set[test_num].mask);
185 	}
186 
187 	SAFE_CLOSE(fd_notify);
188 	SAFE_CLOSE(fd_notify_other);
189 }
190 
setup(void)191 static void setup(void)
192 {
193 	SAFE_MKDIR(TEST_DIR, 00700);
194 	SAFE_FILE_PRINTF(TEST_FILE, "1");
195 }
196 
cleanup(void)197 static void cleanup(void)
198 {
199 	if (fd_notify > 0)
200 		SAFE_CLOSE(fd_notify);
201 	if (fd_notify_other > 0)
202 		SAFE_CLOSE(fd_notify_other);
203 }
204 
205 static struct tst_test test = {
206 	.needs_tmpdir = 1,
207 	.setup = setup,
208 	.cleanup = cleanup,
209 	.test = verify_inotify,
210 	.tcnt = ARRAY_SIZE(tcases),
211 	.tags = (const struct tst_tag[]) {
212 		{"linux-git", "fecc4559780d"},
213 		{}
214 	}
215 };
216 
217 #else
218 	TST_TEST_TCONF("system doesn't have required inotify support");
219 #endif /* defined(HAVE_SYS_INOTIFY_H) */
220