• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2014 SUSE.  All Rights Reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of version 2 of the GNU General Public License as
6  * published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it would be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11  *
12  * Further, this software is distributed without any warranty that it is
13  * free of the rightful claim of any third person regarding infringement
14  * or the like.  Any license provided herein, whether implied or
15  * otherwise, applies only to this software file.  Patent licenses, if
16  * any, provided herein do not apply to combinations of this program with
17  * other software, or any other product whatsoever.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  *
23  * Started by Jan Kara <jack@suse.cz>
24  *
25  * DESCRIPTION
26  *     Check that fanotify properly merges ignore mask of an inode and
27  *     mountpoint.
28  *
29  * This is a regression test for:
30  *
31  *  commit 8edc6e1688fc8f02c8c1f53a2ec4928cb1055f4d
32  *  Author: Jan Kara <jack@suse.cz>
33  *  Date:   Thu Nov 13 15:19:33 2014 -0800
34  *
35  *      fanotify: fix notification of groups with inode & mount marks
36  */
37 #include "config.h"
38 
39 #include <stdio.h>
40 #include <sys/stat.h>
41 #include <sys/types.h>
42 #include <fcntl.h>
43 #include <errno.h>
44 #include <string.h>
45 #include <sys/mount.h>
46 #include <sys/syscall.h>
47 #include "test.h"
48 #include "linux_syscall_numbers.h"
49 #include "fanotify.h"
50 #include "safe_macros.h"
51 
52 char *TCID = "fanotify06";
53 
54 #if defined(HAVE_SYS_FANOTIFY_H)
55 #include <sys/fanotify.h>
56 
57 #define EVENT_MAX 1024
58 /* size of the event structure, not counting name */
59 #define EVENT_SIZE  (sizeof (struct fanotify_event_metadata))
60 /* reasonable guess as to size of 1024 events */
61 #define EVENT_BUF_LEN        (EVENT_MAX * EVENT_SIZE)
62 
63 static void setup(void);
64 static void cleanup(void);
65 
66 unsigned int fanotify_prio[] = {
67 	FAN_CLASS_PRE_CONTENT,
68 	FAN_CLASS_CONTENT,
69 	FAN_CLASS_NOTIF
70 };
71 #define FANOTIFY_PRIORITIES ARRAY_SIZE(fanotify_prio)
72 
73 #define GROUPS_PER_PRIO 3
74 
75 int TST_TOTAL = GROUPS_PER_PRIO * FANOTIFY_PRIORITIES;
76 
77 #define BUF_SIZE 256
78 static char fname[BUF_SIZE];
79 static int fd;
80 static int fd_notify[FANOTIFY_PRIORITIES][GROUPS_PER_PRIO];
81 
82 static char event_buf[EVENT_BUF_LEN];
83 
84 #define MOUNT_NAME "mntpoint"
85 static int mount_created;
86 
create_fanotify_groups(void)87 static void create_fanotify_groups(void)
88 {
89 	unsigned int p, i;
90 	int ret;
91 
92 	for (p = 0; p < FANOTIFY_PRIORITIES; p++) {
93 		for (i = 0; i < GROUPS_PER_PRIO; i++) {
94 			fd_notify[p][i] = fanotify_init(fanotify_prio[p] |
95 							FAN_NONBLOCK,
96 							O_RDONLY);
97 			if (fd_notify[p][i] < 0) {
98 				if (errno == ENOSYS) {
99 					tst_brkm(TCONF, cleanup,
100 						 "fanotify is not configured in"
101 						 " this kernel.");
102 				} else {
103 					tst_brkm(TBROK | TERRNO, cleanup,
104 						 "fanotify_init failed");
105 				}
106 			}
107 			/* Add mount mark for each group */
108 			ret = fanotify_mark(fd_notify[p][i],
109 					    FAN_MARK_ADD | FAN_MARK_MOUNT,
110 					    FAN_MODIFY,
111 					    AT_FDCWD, ".");
112 			if (ret < 0) {
113 				tst_brkm(TBROK | TERRNO, cleanup,
114 					 "fanotify_mark(%d, FAN_MARK_ADD | "
115 					 "FAN_MARK_MOUNT, FAN_MODIFY, AT_FDCWD,"
116 					 " '.') failed", fd_notify[p][i]);
117 			}
118 			/* Add ignore mark for groups with higher priority */
119 			if (p == 0)
120 				continue;
121 			ret = fanotify_mark(fd_notify[p][i],
122 					    FAN_MARK_ADD |
123 					    FAN_MARK_IGNORED_MASK |
124 					    FAN_MARK_IGNORED_SURV_MODIFY,
125 					    FAN_MODIFY, AT_FDCWD, fname);
126 			if (ret < 0) {
127 				tst_brkm(TBROK | TERRNO, cleanup,
128 					 "fanotify_mark(%d, FAN_MARK_ADD | "
129 					 "FAN_MARK_IGNORED_MASK | "
130 					 "FAN_MARK_IGNORED_SURV_MODIFY, "
131 					 "FAN_MODIFY, AT_FDCWD, %s) failed",
132 					 fd_notify[p][i], fname);
133 			}
134 		}
135 	}
136 }
137 
cleanup_fanotify_groups(void)138 static void cleanup_fanotify_groups(void)
139 {
140 	unsigned int i, p;
141 
142 	for (p = 0; p < FANOTIFY_PRIORITIES; p++) {
143 		for (i = 0; i < GROUPS_PER_PRIO; i++) {
144 			if (fd_notify[p][i] && fd_notify[p][i] != -1) {
145 				if (close(fd_notify[p][i]) == -1)
146 					tst_resm(TWARN, "close(%d) failed",
147 						 fd_notify[p][i]);
148 				fd_notify[p][i] = 0;
149 			}
150 		}
151 	}
152 }
153 
verify_event(int group,struct fanotify_event_metadata * event)154 static void verify_event(int group, struct fanotify_event_metadata *event)
155 {
156 	if (event->mask != FAN_MODIFY) {
157 		tst_resm(TFAIL, "group %d get event: mask %llx (expected %llx) "
158 			 "pid=%u fd=%u", group, (unsigned long long)event->mask,
159 			 (unsigned long long)FAN_MODIFY,
160 			 (unsigned)event->pid, event->fd);
161 	} else if (event->pid != getpid()) {
162 		tst_resm(TFAIL, "group %d get event: mask %llx pid=%u "
163 			 "(expected %u) fd=%u", group,
164 			 (unsigned long long)event->mask, (unsigned)event->pid,
165 			 (unsigned)getpid(), event->fd);
166 	} else {
167 		tst_resm(TPASS, "group %d get event: mask %llx pid=%u fd=%u",
168 			 group, (unsigned long long)event->mask,
169 			 (unsigned)event->pid, event->fd);
170 	}
171 }
172 
main(int ac,char ** av)173 int main(int ac, char **av)
174 {
175 	int lc;
176 
177 	tst_parse_opts(ac, av, NULL, NULL);
178 
179 	setup();
180 
181 	for (lc = 0; TEST_LOOPING(lc); lc++) {
182 		int ret;
183 		unsigned int p, i;
184 		struct fanotify_event_metadata *event;
185 
186 		create_fanotify_groups();
187 
188 		/*
189 		 * generate sequence of events
190 		 */
191 		fd = SAFE_OPEN(cleanup, fname, O_RDWR);
192 		SAFE_WRITE(cleanup, 1, fd, fname, strlen(fname));
193 		SAFE_CLOSE(cleanup, fd);
194 
195 		/* First verify all groups without ignore mask got the event */
196 		for (i = 0; i < GROUPS_PER_PRIO; i++) {
197 			ret = read(fd_notify[0][i], event_buf, EVENT_BUF_LEN);
198 			if (ret < 0) {
199 				if (errno == EAGAIN) {
200 					tst_resm(TFAIL, "group %d did not get "
201 						 "event", i);
202 				}
203 				tst_brkm(TBROK | TERRNO, cleanup,
204 					 "reading fanotify events failed");
205 			}
206 			if (ret < (int)FAN_EVENT_METADATA_LEN) {
207 				tst_brkm(TBROK, cleanup,
208 					 "short read when reading fanotify "
209 					 "events (%d < %d)", ret,
210 					 (int)EVENT_BUF_LEN);
211 			}
212 			event = (struct fanotify_event_metadata *)event_buf;
213 			if (ret > (int)event->event_len) {
214 				tst_resm(TFAIL, "group %d got more than one "
215 					 "event (%d > %d)", i, ret,
216 					 event->event_len);
217 			} else
218 				verify_event(i, event);
219 			close(event->fd);
220 		}
221 		for (p = 1; p < FANOTIFY_PRIORITIES; p++) {
222 			for (i = 0; i < GROUPS_PER_PRIO; i++) {
223 				ret = read(fd_notify[p][i], event_buf, EVENT_BUF_LEN);
224 				if (ret > 0) {
225 					tst_resm(TFAIL, "group %d got event",
226 						 p*GROUPS_PER_PRIO + i);
227 				} else if (ret == 0) {
228 					tst_brkm(TBROK, cleanup, "zero length "
229 						 "read from fanotify fd");
230 				} else if (errno != EAGAIN) {
231 					tst_brkm(TBROK | TERRNO, cleanup,
232 						 "reading fanotify events failed");
233 				} else {
234 					tst_resm(TPASS, "group %d got no event",
235 						 p*GROUPS_PER_PRIO + i);
236 				}
237 			}
238 		}
239 		cleanup_fanotify_groups();
240 	}
241 
242 	cleanup();
243 	tst_exit();
244 }
245 
setup(void)246 static void setup(void)
247 {
248 	tst_sig(NOFORK, DEF_HANDLER, cleanup);
249 
250 	TEST_PAUSE;
251 
252 	tst_require_root();
253 	tst_tmpdir();
254 
255 	SAFE_MKDIR(cleanup, MOUNT_NAME, 0755);
256 	SAFE_MOUNT(cleanup, MOUNT_NAME, MOUNT_NAME, NULL, MS_BIND, NULL);
257 	mount_created = 1;
258 	SAFE_CHDIR(cleanup, MOUNT_NAME);
259 
260 	sprintf(fname, "tfile_%d", getpid());
261 	fd = SAFE_OPEN(cleanup, fname, O_RDWR | O_CREAT, 0700);
262 	SAFE_WRITE(cleanup, 1, fd, fname, 1);
263 
264 	/* close the file we have open */
265 	SAFE_CLOSE(cleanup, fd);
266 }
267 
cleanup(void)268 static void cleanup(void)
269 {
270 	cleanup_fanotify_groups();
271 
272 	if (chdir(tst_get_tmpdir()) < 0)
273 		tst_brkm(TBROK, NULL, "chdir(tmpdir) failed");
274 
275 	if (mount_created && tst_umount(MOUNT_NAME) < 0)
276 		tst_brkm(TBROK | TERRNO, NULL, "umount failed");
277 
278 	tst_rmdir();
279 }
280 
281 #else
282 
main(void)283 int main(void)
284 {
285 	tst_brkm(TCONF, NULL, "system doesn't have required fanotify support");
286 }
287 
288 #endif
289