xref: /aosp_15_r20/external/ltp/testcases/kernel/syscalls/fanotify/fanotify06.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2014 SUSE.  All Rights Reserved.
4  *
5  * Started by Jan Kara <[email protected]>
6  */
7 
8 /*\
9  * [Description]
10  * Check that fanotify properly merges ignore mask of an inode and mountpoint.
11  */
12 
13 /*
14  * This is a regression test for:
15  *
16  *  commit 8edc6e1688fc8f02c8c1f53a2ec4928cb1055f4d
17  *  Author: Jan Kara <[email protected]>
18  *  Date:   Thu Nov 13 15:19:33 2014 -0800
19  *
20  *      fanotify: fix notification of groups with inode & mount marks
21  *
22  * The overlayfs test case is a regression test for:
23  *
24  *  commit d989903058a83e8536cc7aadf9256a47d5c173fe
25  *  Author: Amir Goldstein <[email protected]>
26  *  Date:   Wed Apr 24 19:39:50 2019 +0300
27  *
28  *      ovl: do not generate duplicate fsnotify events for "fake" path
29  */
30 
31 #define _GNU_SOURCE
32 #include "config.h"
33 
34 #include <stdio.h>
35 #include <sys/stat.h>
36 #include <sys/types.h>
37 #include <errno.h>
38 #include <string.h>
39 #include <sys/mount.h>
40 #include <sys/syscall.h>
41 #include "tst_test.h"
42 
43 #ifdef HAVE_SYS_FANOTIFY_H
44 #include "fanotify.h"
45 
46 #define EVENT_MAX 1024
47 /* size of the event structure, not counting name */
48 #define EVENT_SIZE  (sizeof(struct fanotify_event_metadata))
49 /* reasonable guess as to size of 1024 events */
50 #define EVENT_BUF_LEN        (EVENT_MAX * EVENT_SIZE)
51 
52 static unsigned int fanotify_prio[] = {
53 	FAN_CLASS_PRE_CONTENT,
54 	FAN_CLASS_CONTENT,
55 	FAN_CLASS_NOTIF
56 };
57 #define FANOTIFY_PRIORITIES ARRAY_SIZE(fanotify_prio)
58 
59 #define GROUPS_PER_PRIO 3
60 
61 #define BUF_SIZE 256
62 static char fname[BUF_SIZE];
63 static int fd_notify[FANOTIFY_PRIORITIES][GROUPS_PER_PRIO];
64 
65 static char event_buf[EVENT_BUF_LEN];
66 
67 static const char mntpoint[] = OVL_BASE_MNTPOINT;
68 
69 static int ovl_mounted;
70 
71 static struct tcase {
72 	const char *tname;
73 	const char *mnt;
74 	int use_overlay;
75 } tcases[] = {
76 	{ "Fanotify merge mount mark", mntpoint, 0 },
77 	{ "Fanotify merge overlayfs mount mark", OVL_MNT, 1 },
78 };
79 
create_fanotify_groups(void)80 static void create_fanotify_groups(void)
81 {
82 	unsigned int p, i;
83 
84 	for (p = 0; p < FANOTIFY_PRIORITIES; p++) {
85 		for (i = 0; i < GROUPS_PER_PRIO; i++) {
86 			fd_notify[p][i] = SAFE_FANOTIFY_INIT(fanotify_prio[p] |
87 							     FAN_NONBLOCK,
88 							     O_RDONLY);
89 
90 			/* Add mount mark for each group */
91 			SAFE_FANOTIFY_MARK(fd_notify[p][i],
92 					    FAN_MARK_ADD | FAN_MARK_MOUNT,
93 					    FAN_MODIFY,
94 					    AT_FDCWD, fname);
95 
96 			/* Add ignore mark for groups with higher priority */
97 			if (p == 0)
98 				continue;
99 
100 			SAFE_FANOTIFY_MARK(fd_notify[p][i],
101 					    FAN_MARK_ADD |
102 					    FAN_MARK_IGNORED_MASK |
103 					    FAN_MARK_IGNORED_SURV_MODIFY,
104 					    FAN_MODIFY, AT_FDCWD, fname);
105 		}
106 	}
107 }
108 
cleanup_fanotify_groups(void)109 static void cleanup_fanotify_groups(void)
110 {
111 	unsigned int i, p;
112 
113 	for (p = 0; p < FANOTIFY_PRIORITIES; p++) {
114 		for (i = 0; i < GROUPS_PER_PRIO; i++) {
115 			if (fd_notify[p][i] > 0)
116 				SAFE_CLOSE(fd_notify[p][i]);
117 		}
118 	}
119 }
120 
verify_event(int group,struct fanotify_event_metadata * event)121 static void verify_event(int group, struct fanotify_event_metadata *event)
122 {
123 	if (event->mask != FAN_MODIFY) {
124 		tst_res(TFAIL, "group %d got event: mask %llx (expected %llx) "
125 			"pid=%u fd=%d", group, (unsigned long long)event->mask,
126 			(unsigned long long)FAN_MODIFY,
127 			(unsigned int)event->pid, event->fd);
128 	} else if (event->pid != getpid()) {
129 		tst_res(TFAIL, "group %d got event: mask %llx pid=%u "
130 			"(expected %u) fd=%d", group,
131 			(unsigned long long)event->mask, (unsigned int)event->pid,
132 			(unsigned int)getpid(), event->fd);
133 	} else {
134 		tst_res(TPASS, "group %d got event: mask %llx pid=%u fd=%d",
135 			group, (unsigned long long)event->mask,
136 			(unsigned int)event->pid, event->fd);
137 	}
138 }
139 
140 /* Close all file descriptors of read events */
close_events_fd(struct fanotify_event_metadata * event,int buflen)141 static void close_events_fd(struct fanotify_event_metadata *event, int buflen)
142 {
143 	while (buflen >= (int)FAN_EVENT_METADATA_LEN) {
144 		if (event->fd != FAN_NOFD)
145 			SAFE_CLOSE(event->fd);
146 		buflen -= (int)FAN_EVENT_METADATA_LEN;
147 		event++;
148 	}
149 }
150 
test_fanotify(unsigned int n)151 static void test_fanotify(unsigned int n)
152 {
153 	int ret;
154 	unsigned int p, i;
155 	struct fanotify_event_metadata *event;
156 	struct tcase *tc = &tcases[n];
157 
158 	tst_res(TINFO, "Test #%d: %s", n, tc->tname);
159 
160 	if (tc->use_overlay && !ovl_mounted) {
161 		tst_res(TCONF, "overlayfs is not configured in this kernel");
162 		return;
163 	}
164 
165 	sprintf(fname, "%s/tfile_%d", tc->mnt, getpid());
166 	SAFE_TOUCH(fname, 0644, NULL);
167 
168 	create_fanotify_groups();
169 
170 	/*
171 	 * generate sequence of events
172 	 */
173 	SAFE_FILE_PRINTF(fname, "1");
174 
175 	/* First verify all groups without ignore mask got the event */
176 	for (i = 0; i < GROUPS_PER_PRIO; i++) {
177 		ret = read(fd_notify[0][i], event_buf, EVENT_BUF_LEN);
178 		if (ret < 0) {
179 			if (errno == EAGAIN) {
180 				tst_res(TFAIL, "group %d did not get "
181 					"event", i);
182 			}
183 			tst_brk(TBROK | TERRNO,
184 				"reading fanotify events failed");
185 		}
186 		if (ret < (int)FAN_EVENT_METADATA_LEN) {
187 			tst_brk(TBROK,
188 				"short read when reading fanotify "
189 				"events (%d < %d)", ret,
190 				(int)EVENT_BUF_LEN);
191 		}
192 		event = (struct fanotify_event_metadata *)event_buf;
193 		if (ret > (int)event->event_len) {
194 			tst_res(TFAIL, "group %d got more than one "
195 				"event (%d > %d)", i, ret,
196 				event->event_len);
197 		} else {
198 			verify_event(i, event);
199 		}
200 		close_events_fd(event, ret);
201 	}
202 
203 	for (p = 1; p < FANOTIFY_PRIORITIES; p++) {
204 		for (i = 0; i < GROUPS_PER_PRIO; i++) {
205 			ret = read(fd_notify[p][i], event_buf, EVENT_BUF_LEN);
206 			if (ret > 0) {
207 				tst_res(TFAIL, "group %d got event",
208 					p*GROUPS_PER_PRIO + i);
209 				close_events_fd((void *)event_buf, ret);
210 			} else if (ret == 0) {
211 				tst_brk(TBROK, "zero length "
212 					"read from fanotify fd");
213 			} else if (errno != EAGAIN) {
214 				tst_brk(TBROK | TERRNO,
215 					"reading fanotify events failed");
216 			} else {
217 				tst_res(TPASS, "group %d got no event",
218 					p*GROUPS_PER_PRIO + i);
219 			}
220 		}
221 	}
222 	cleanup_fanotify_groups();
223 }
224 
setup(void)225 static void setup(void)
226 {
227 	ovl_mounted = TST_MOUNT_OVERLAY();
228 }
229 
cleanup(void)230 static void cleanup(void)
231 {
232 	cleanup_fanotify_groups();
233 
234 	if (ovl_mounted)
235 		SAFE_UMOUNT(OVL_MNT);
236 }
237 
238 static struct tst_test test = {
239 	.test = test_fanotify,
240 	.tcnt = ARRAY_SIZE(tcases),
241 	.setup = setup,
242 	.cleanup = cleanup,
243 	.needs_root = 1,
244 	.mount_device = 1,
245 	.mntpoint = mntpoint,
246 	.tags = (const struct tst_tag[]) {
247 		{"linux-git", "8edc6e1688fc"},
248 		{"linux-git", "d989903058a8"},
249 		{}
250 	}
251 };
252 
253 #else
254 	TST_TEST_TCONF("system doesn't have required fanotify support");
255 #endif
256