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