1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2013 SUSE.  All Rights Reserved.
4  *
5  * Started by Jan Kara <jack@suse.cz>
6  *
7  * DESCRIPTION
8  *     Check that fanotify permission events work
9  */
10 #define _GNU_SOURCE
11 #include "config.h"
12 
13 #include <stdio.h>
14 #include <unistd.h>
15 #include <sys/stat.h>
16 #include <sys/types.h>
17 #include <fcntl.h>
18 #include <sys/wait.h>
19 #include <errno.h>
20 #include <string.h>
21 #include <signal.h>
22 #include <sys/syscall.h>
23 #include <stdlib.h>
24 #include "tst_test.h"
25 #include "fanotify.h"
26 
27 #if defined(HAVE_SYS_FANOTIFY_H)
28 #include <sys/fanotify.h>
29 
30 #define EVENT_MAX 1024
31 /* size of the event structure, not counting name */
32 #define EVENT_SIZE  (sizeof (struct fanotify_event_metadata))
33 /* reasonable guess as to size of 1024 events */
34 #define EVENT_BUF_LEN        (EVENT_MAX * EVENT_SIZE)
35 
36 #define BUF_SIZE 256
37 #define TST_TOTAL 3
38 
39 static char fname[BUF_SIZE];
40 static char buf[BUF_SIZE];
41 static volatile int fd_notify;
42 
43 static pid_t child_pid;
44 
45 static unsigned long long event_set[EVENT_MAX];
46 static unsigned int event_resp[EVENT_MAX];
47 
48 static char event_buf[EVENT_BUF_LEN];
49 
50 static struct tcase {
51 	const char *tname;
52 	struct fanotify_mark_type mark;
53 } tcases[] = {
54 	{
55 		"inode mark permission events",
56 		INIT_FANOTIFY_MARK_TYPE(INODE),
57 	},
58 	{
59 		"mount mark permission events",
60 		INIT_FANOTIFY_MARK_TYPE(MOUNT),
61 	},
62 	{
63 		"filesystem mark permission events",
64 		INIT_FANOTIFY_MARK_TYPE(FILESYSTEM),
65 	},
66 };
67 
generate_events(void)68 static void generate_events(void)
69 {
70 	int fd;
71 
72 	/*
73 	 * generate sequence of events
74 	 */
75 	if ((fd = open(fname, O_RDWR | O_CREAT, 0700)) == -1)
76 		exit(1);
77 	if (write(fd, fname, 1) == -1)
78 		exit(2);
79 
80 	lseek(fd, 0, SEEK_SET);
81 	if (read(fd, buf, BUF_SIZE) != -1)
82 		exit(3);
83 
84 	if (close(fd) == -1)
85 		exit(4);
86 }
87 
child_handler(int tmp)88 static void child_handler(int tmp)
89 {
90 	(void)tmp;
91 	/*
92 	 * Close notification fd so that we cannot block while reading
93 	 * from it
94 	 */
95 	close(fd_notify);
96 	fd_notify = -1;
97 }
98 
run_child(void)99 static void run_child(void)
100 {
101 	struct sigaction child_action;
102 
103 	child_action.sa_handler = child_handler;
104 	sigemptyset(&child_action.sa_mask);
105 	child_action.sa_flags = SA_NOCLDSTOP;
106 
107 	if (sigaction(SIGCHLD, &child_action, NULL) < 0) {
108 		tst_brk(TBROK | TERRNO,
109 			"sigaction(SIGCHLD, &child_action, NULL) failed");
110 	}
111 
112 	child_pid = SAFE_FORK();
113 	if (child_pid == 0) {
114 		/* Child will generate events now */
115 		close(fd_notify);
116 		generate_events();
117 		exit(0);
118 	}
119 }
120 
check_child(void)121 static void check_child(void)
122 {
123 	struct sigaction child_action;
124 	int child_ret;
125 
126 	child_action.sa_handler = SIG_IGN;
127 	sigemptyset(&child_action.sa_mask);
128 	child_action.sa_flags = SA_NOCLDSTOP;
129 	if (sigaction(SIGCHLD, &child_action, NULL) < 0) {
130 		tst_brk(TBROK | TERRNO,
131 			"sigaction(SIGCHLD, &child_action, NULL) failed");
132 	}
133 	SAFE_WAITPID(-1, &child_ret, 0);
134 
135 	if (WIFEXITED(child_ret) && WEXITSTATUS(child_ret) == 0)
136 		tst_res(TPASS, "child exited correctly");
137 	else
138 		tst_res(TFAIL, "child %s", tst_strstatus(child_ret));
139 }
140 
setup_mark(unsigned int n)141 static int setup_mark(unsigned int n)
142 {
143 	struct tcase *tc = &tcases[n];
144 	struct fanotify_mark_type *mark = &tc->mark;
145 
146 	fd_notify = SAFE_FANOTIFY_INIT(FAN_CLASS_CONTENT, O_RDONLY);
147 
148 	if (fanotify_mark(fd_notify, FAN_MARK_ADD | mark->flag,
149 			  FAN_ACCESS_PERM | FAN_OPEN_PERM,
150 			  AT_FDCWD, fname) < 0) {
151 		if (errno == EINVAL && mark->flag == FAN_MARK_FILESYSTEM) {
152 			tst_res(TCONF,
153 				"FAN_MARK_FILESYSTEM not supported in kernel?");
154 			return -1;
155 		} else if (errno == EINVAL) {
156 			tst_brk(TCONF | TERRNO,
157 				"CONFIG_FANOTIFY_ACCESS_PERMISSIONS not "
158 				"configured in kernel?");
159 		} else {
160 			tst_brk(TBROK | TERRNO,
161 				"fanotify_mark (%d, FAN_MARK_ADD | %s, "
162 				"FAN_ACCESS_PERM | FAN_OPEN_PERM, "
163 				"AT_FDCWD, %s) failed.",
164 				fd_notify, mark->name, fname);
165 		}
166 	}
167 
168 	tst_res(TINFO, "Test #%d: %s", n, tc->tname);
169 	return 0;
170 }
171 
test_fanotify(unsigned int n)172 static void test_fanotify(unsigned int n)
173 {
174 	int tst_count;
175 	int ret, len = 0, i = 0, test_num = 0;
176 
177 	if (setup_mark(n) != 0)
178 		return;
179 
180 	run_child();
181 
182 	tst_count = 0;
183 
184 	event_set[tst_count] = FAN_OPEN_PERM;
185 	event_resp[tst_count++] = FAN_ALLOW;
186 	event_set[tst_count] = FAN_ACCESS_PERM;
187 	event_resp[tst_count++] = FAN_DENY;
188 
189 	/* tst_count + 1 is for checking child return value */
190 	if (TST_TOTAL != tst_count + 1) {
191 		tst_brk(TBROK,
192 			"TST_TOTAL and tst_count do not match");
193 	}
194 	tst_count = 0;
195 
196 	/*
197 	 * check events
198 	 */
199 	while (test_num < TST_TOTAL && fd_notify != -1) {
200 		struct fanotify_event_metadata *event;
201 
202 		if (i == len) {
203 			/* Get more events */
204 			ret = read(fd_notify, event_buf + len,
205 				   EVENT_BUF_LEN - len);
206 			if (fd_notify == -1)
207 				break;
208 			if (ret < 0) {
209 				tst_brk(TBROK,
210 					"read(%d, buf, %zu) failed",
211 					fd_notify, EVENT_BUF_LEN);
212 			}
213 			len += ret;
214 		}
215 
216 		event = (struct fanotify_event_metadata *)&event_buf[i];
217 		if (!(event->mask & event_set[test_num])) {
218 			tst_res(TFAIL,
219 				"got event: mask=%llx (expected %llx) "
220 				"pid=%u fd=%d",
221 				(unsigned long long)event->mask,
222 				event_set[test_num],
223 				(unsigned)event->pid, event->fd);
224 		} else if (event->pid != child_pid) {
225 			tst_res(TFAIL,
226 				"got event: mask=%llx pid=%u "
227 				"(expected %u) fd=%d",
228 				(unsigned long long)event->mask,
229 				(unsigned)event->pid,
230 				(unsigned)child_pid,
231 				event->fd);
232 		} else {
233 			tst_res(TPASS,
234 				"got event: mask=%llx pid=%u fd=%d",
235 				(unsigned long long)event->mask,
236 				(unsigned)event->pid, event->fd);
237 		}
238 		/* Write response to permission event */
239 		if (event_set[test_num] & FAN_ALL_PERM_EVENTS) {
240 			struct fanotify_response resp;
241 
242 			resp.fd = event->fd;
243 			resp.response = event_resp[test_num];
244 			SAFE_WRITE(1, fd_notify, &resp,
245 				   sizeof(resp));
246 		}
247 		event->mask &= ~event_set[test_num];
248 		/* No events left in current mask? Go for next event */
249 		if (event->mask == 0) {
250 			i += event->event_len;
251 			if (event->fd != FAN_NOFD)
252 				SAFE_CLOSE(event->fd);
253 		}
254 		test_num++;
255 	}
256 	for (; test_num < TST_TOTAL - 1; test_num++) {
257 		tst_res(TFAIL, "didn't get event: mask=%llx",
258 			event_set[test_num]);
259 
260 	}
261 	check_child();
262 
263 	if (fd_notify > 0)
264 		SAFE_CLOSE(fd_notify);
265 }
266 
setup(void)267 static void setup(void)
268 {
269 	sprintf(fname, "fname_%d", getpid());
270 	SAFE_FILE_PRINTF(fname, "1");
271 }
272 
cleanup(void)273 static void cleanup(void)
274 {
275 	if (fd_notify > 0)
276 		SAFE_CLOSE(fd_notify);
277 }
278 
279 static struct tst_test test = {
280 	.test = test_fanotify,
281 	.tcnt = ARRAY_SIZE(tcases),
282 	.setup = setup,
283 	.cleanup = cleanup,
284 	.needs_tmpdir = 1,
285 	.forks_child = 1,
286 	.needs_root = 1
287 };
288 
289 #else
290 	TST_TEST_TCONF("system doesn't have required fanotify support");
291 #endif
292