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