• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2013 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 permission events work
27  */
28 #define _GNU_SOURCE
29 #include "config.h"
30 
31 #include <stdio.h>
32 #include <unistd.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <sys/fcntl.h>
36 #include <sys/wait.h>
37 #include <errno.h>
38 #include <string.h>
39 #include <signal.h>
40 #include <sys/syscall.h>
41 #include "test.h"
42 #include "linux_syscall_numbers.h"
43 #include "fanotify.h"
44 #include "safe_macros.h"
45 
46 char *TCID = "fanotify03";
47 int TST_TOTAL = 3;
48 
49 #if defined(HAVE_SYS_FANOTIFY_H)
50 #include <sys/fanotify.h>
51 
52 #define EVENT_MAX 1024
53 /* size of the event structure, not counting name */
54 #define EVENT_SIZE  (sizeof (struct fanotify_event_metadata))
55 /* reasonable guess as to size of 1024 events */
56 #define EVENT_BUF_LEN        (EVENT_MAX * EVENT_SIZE)
57 
58 static void setup(void);
59 static void cleanup(void);
60 
61 #define BUF_SIZE 256
62 static char fname[BUF_SIZE];
63 static char buf[BUF_SIZE];
64 static volatile int fd_notify;
65 
66 static pid_t child_pid;
67 
68 static unsigned long long event_set[EVENT_MAX];
69 static unsigned int event_resp[EVENT_MAX];
70 
71 static char event_buf[EVENT_BUF_LEN];
72 
generate_events(void)73 static void generate_events(void)
74 {
75 	int fd;
76 
77 	/*
78 	 * generate sequence of events
79 	 */
80 	if ((fd = open(fname, O_RDWR | O_CREAT, 0700)) == -1)
81 		exit(1);
82 	if (write(fd, fname, 1) == -1)
83 		exit(2);
84 
85 	lseek(fd, 0, SEEK_SET);
86 	if (read(fd, buf, BUF_SIZE) != -1)
87 		exit(3);
88 
89 	if (close(fd) == -1)
90 		exit(4);
91 }
92 
child_handler(int tmp)93 static void child_handler(int tmp)
94 {
95 	/*
96 	 * Close notification fd so that we cannot block while reading
97 	 * from it
98 	 */
99 	close(fd_notify);
100 	fd_notify = -1;
101 }
102 
run_child(void)103 static void run_child(void)
104 {
105 	struct sigaction child_action;
106 
107 	child_action.sa_handler = child_handler;
108 	sigemptyset(&child_action.sa_mask);
109 	child_action.sa_flags = SA_NOCLDSTOP;
110 
111 	if (sigaction(SIGCHLD, &child_action, NULL) < 0) {
112 		tst_brkm(TBROK | TERRNO, cleanup,
113 			 "sigaction(SIGCHLD, &child_action, NULL) failed");
114 	}
115 
116 	switch (child_pid = fork()) {
117 	case 0:
118 		/* Child will generate events now */
119 		close(fd_notify);
120 		generate_events();
121 		exit(0);
122 	case -1:
123 		tst_brkm(TBROK | TERRNO, cleanup, "fork() failed");
124 	}
125 }
126 
check_child(void)127 static void check_child(void)
128 {
129 	struct sigaction child_action;
130 	int child_ret;
131 
132 	child_action.sa_handler = SIG_IGN;
133 	sigemptyset(&child_action.sa_mask);
134 	child_action.sa_flags = SA_NOCLDSTOP;
135 	if (sigaction(SIGCHLD, &child_action, NULL) < 0) {
136 		tst_brkm(TBROK | TERRNO, cleanup,
137 			 "sigaction(SIGCHLD, &child_action, NULL) failed");
138 	}
139 	if (waitpid(-1, &child_ret, 0) < 0) {
140 		tst_brkm(TBROK | TERRNO, cleanup,
141 				 "waitpid(-1, &child_ret, 0) failed");
142 	}
143 
144 	if (WIFSIGNALED(child_ret)) {
145 		tst_resm(TFAIL, "child exited due to signal %d",
146 			 WTERMSIG(child_ret));
147 	} else if (WIFEXITED(child_ret)) {
148 		if (WEXITSTATUS(child_ret) == 0)
149 			tst_resm(TPASS, "child exited correctly");
150 		else
151 			tst_resm(TFAIL, "child exited with status %d",
152 				 WEXITSTATUS(child_ret));
153 	} else {
154 		tst_resm(TFAIL, "child exited for unknown reason (status %d)",
155 			 child_ret);
156 	}
157 }
158 
main(int ac,char ** av)159 int main(int ac, char **av)
160 {
161 	int lc;
162 	int fd_notify_backup = -1;
163 
164 	tst_parse_opts(ac, av, NULL, NULL);
165 
166 	setup();
167 
168 	for (lc = 0; TEST_LOOPING(lc); lc++) {
169 		int ret, len = 0, i = 0, test_num = 0;
170 
171 		if (fd_notify_backup == -1) {
172 			fd_notify_backup = dup(fd_notify);
173 			if (fd_notify_backup < 0)
174 				tst_brkm(TBROK | TERRNO, cleanup,
175 					 "dup(%d) failed", fd_notify);
176 		}
177 		run_child();
178 
179 		tst_count = 0;
180 
181 		event_set[tst_count] = FAN_OPEN_PERM;
182 		event_resp[tst_count++] = FAN_ALLOW;
183 		event_set[tst_count] = FAN_ACCESS_PERM;
184 		event_resp[tst_count++] = FAN_DENY;
185 
186 		/* tst_count + 1 is for checking child return value */
187 		if (TST_TOTAL != tst_count + 1) {
188 			tst_brkm(TBROK, cleanup,
189 				 "TST_TOTAL and tst_count do not match");
190 		}
191 		tst_count = 0;
192 
193 		/*
194 		 * check events
195 		 */
196 		while (test_num < TST_TOTAL && fd_notify != -1) {
197 			struct fanotify_event_metadata *event;
198 
199 			if (i == len) {
200 				/* Get more events */
201 				ret = read(fd_notify, event_buf + len,
202 					   EVENT_BUF_LEN - len);
203 				if (fd_notify == -1)
204 					break;
205 				if (ret < 0) {
206 					tst_brkm(TBROK, cleanup,
207 						 "read(%d, buf, %zu) failed",
208 						 fd_notify, EVENT_BUF_LEN);
209 				}
210 				len += ret;
211 			}
212 
213 			event = (struct fanotify_event_metadata *)&event_buf[i];
214 			if (!(event->mask & event_set[test_num])) {
215 				tst_resm(TFAIL,
216 					 "get event: mask=%llx (expected %llx) "
217 					 "pid=%u fd=%u",
218 					 (unsigned long long)event->mask,
219 					 event_set[test_num],
220 					 (unsigned)event->pid, event->fd);
221 			} else if (event->pid != child_pid) {
222 				tst_resm(TFAIL,
223 					 "get event: mask=%llx pid=%u "
224 					 "(expected %u) fd=%u",
225 					 (unsigned long long)event->mask,
226 					 (unsigned)event->pid,
227 					 (unsigned)child_pid,
228 					 event->fd);
229 			} else {
230 				tst_resm(TPASS,
231 					    "get event: mask=%llx pid=%u fd=%u",
232 					    (unsigned long long)event->mask,
233 					    (unsigned)event->pid, event->fd);
234 			}
235 			/* Write response to permission event */
236 			if (event_set[test_num] & FAN_ALL_PERM_EVENTS) {
237 				struct fanotify_response resp;
238 
239 				resp.fd = event->fd;
240 				resp.response = event_resp[test_num];
241 				SAFE_WRITE(cleanup, 1, fd_notify, &resp,
242 					   sizeof(resp));
243 			}
244 			event->mask &= ~event_set[test_num];
245 			/* No events left in current mask? Go for next event */
246 			if (event->mask == 0) {
247 				i += event->event_len;
248 				close(event->fd);
249 			}
250 			test_num++;
251 		}
252 		for (; test_num < TST_TOTAL - 1; test_num++) {
253 			tst_resm(TFAIL, "didn't get event: mask=%llx",
254 				 event_set[test_num]);
255 
256 		}
257 		check_child();
258 		/* We got SIGCHLD while running, resetup fd_notify */
259 		if (fd_notify == -1) {
260 			fd_notify = fd_notify_backup;
261 			fd_notify_backup = -1;
262 		}
263 	}
264 
265 	cleanup();
266 	tst_exit();
267 }
268 
setup(void)269 static void setup(void)
270 {
271 	int fd;
272 
273 	tst_sig(FORK, DEF_HANDLER, cleanup);
274 
275 	TEST_PAUSE;
276 
277 	tst_tmpdir();
278 	sprintf(fname, "fname_%d", getpid());
279 	fd = SAFE_OPEN(cleanup, fname, O_CREAT | O_RDWR, 0644);
280 	SAFE_WRITE(cleanup, 1, fd, fname, 1);
281 	SAFE_CLOSE(cleanup, fd);
282 
283 	if ((fd_notify = fanotify_init(FAN_CLASS_CONTENT, O_RDONLY)) < 0) {
284 		if (errno == ENOSYS) {
285 			tst_brkm(TCONF, cleanup,
286 				 "fanotify is not configured in this kernel.");
287 		} else {
288 			tst_brkm(TBROK | TERRNO, cleanup,
289 				 "fanotify_init failed");
290 		}
291 	}
292 
293 	if (fanotify_mark(fd_notify, FAN_MARK_ADD, FAN_ACCESS_PERM |
294 			    FAN_OPEN_PERM, AT_FDCWD, fname) < 0) {
295 		if (errno == EINVAL) {
296 			tst_brkm(TCONF | TERRNO, cleanup,
297 				 "CONFIG_FANOTIFY_ACCESS_PERMISSIONS not "
298 				 "configured in kernel?");
299 		} else {
300 			tst_brkm(TBROK | TERRNO, cleanup,
301 				 "fanotify_mark (%d, FAN_MARK_ADD, FAN_ACCESS_PERM | "
302 				 "FAN_OPEN_PERM, AT_FDCWD, %s) failed.", fd_notify, fname);
303 		}
304 	}
305 
306 }
307 
cleanup(void)308 static void cleanup(void)
309 {
310 	if (fd_notify > 0 && close(fd_notify))
311 		tst_resm(TWARN | TERRNO, "close(%d) failed", fd_notify);
312 
313 	tst_rmdir();
314 }
315 
316 #else
317 
main(void)318 int main(void)
319 {
320 	tst_brkm(TCONF, NULL, "system doesn't have required fanotify support");
321 }
322 
323 #endif
324