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