• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2014 SUSE.  All Rights Reserved.
4  *
5  * Started by Jan Kara <jack@suse.cz>
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 <jack@suse.cz>
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 <amir73il@gmail.com>
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 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)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)event->pid,
132 			(unsigned)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)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 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,
162 		        "overlayfs is not configured in this kernel.");
163 		return;
164 	}
165 
166 	sprintf(fname, "%s/tfile_%d", tc->mnt, getpid());
167 	SAFE_TOUCH(fname, 0644, NULL);
168 
169 	create_fanotify_groups();
170 
171 	/*
172 	 * generate sequence of events
173 	 */
174 	SAFE_FILE_PRINTF(fname, "1");
175 
176 	/* First verify all groups without ignore mask got the event */
177 	for (i = 0; i < GROUPS_PER_PRIO; i++) {
178 		ret = read(fd_notify[0][i], event_buf, EVENT_BUF_LEN);
179 		if (ret < 0) {
180 			if (errno == EAGAIN) {
181 				tst_res(TFAIL, "group %d did not get "
182 					"event", i);
183 			}
184 			tst_brk(TBROK | TERRNO,
185 				"reading fanotify events failed");
186 		}
187 		if (ret < (int)FAN_EVENT_METADATA_LEN) {
188 			tst_brk(TBROK,
189 				"short read when reading fanotify "
190 				"events (%d < %d)", ret,
191 				(int)EVENT_BUF_LEN);
192 		}
193 		event = (struct fanotify_event_metadata *)event_buf;
194 		if (ret > (int)event->event_len) {
195 			tst_res(TFAIL, "group %d got more than one "
196 				"event (%d > %d)", i, ret,
197 				event->event_len);
198 		} else {
199 			verify_event(i, event);
200 		}
201 		close_events_fd(event, ret);
202 	}
203 
204 	for (p = 1; p < FANOTIFY_PRIORITIES; p++) {
205 		for (i = 0; i < GROUPS_PER_PRIO; i++) {
206 			ret = read(fd_notify[p][i], event_buf, EVENT_BUF_LEN);
207 			if (ret > 0) {
208 				tst_res(TFAIL, "group %d got event",
209 					p*GROUPS_PER_PRIO + i);
210 				close_events_fd((void *)event_buf, ret);
211 			} else if (ret == 0) {
212 				tst_brk(TBROK, "zero length "
213 					"read from fanotify fd");
214 			} else if (errno != EAGAIN) {
215 				tst_brk(TBROK | TERRNO,
216 					"reading fanotify events failed");
217 			} else {
218 				tst_res(TPASS, "group %d got no event",
219 					p*GROUPS_PER_PRIO + i);
220 			}
221 		}
222 	}
223 	cleanup_fanotify_groups();
224 }
225 
setup(void)226 static void setup(void)
227 {
228 	ovl_mounted = TST_MOUNT_OVERLAY();
229 }
230 
cleanup(void)231 static void cleanup(void)
232 {
233 	cleanup_fanotify_groups();
234 
235 	if (ovl_mounted)
236 		SAFE_UMOUNT(OVL_MNT);
237 }
238 
239 static struct tst_test test = {
240 	.test = test_fanotify,
241 	.tcnt = ARRAY_SIZE(tcases),
242 	.setup = setup,
243 	.cleanup = cleanup,
244 	.needs_root = 1,
245 	.mount_device = 1,
246 	.mntpoint = mntpoint,
247 	.tags = (const struct tst_tag[]) {
248 		{"linux-git", "8edc6e1688fc"},
249 		{"linux-git", "d989903058a8"},
250 		{}
251 	}
252 };
253 
254 #else
255 	TST_TEST_TCONF("system doesn't have required fanotify support");
256 #endif
257