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 work for a file
27 */
28 #define _GNU_SOURCE
29 #include "config.h"
30
31 #include <stdio.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <sys/fcntl.h>
35 #include <errno.h>
36 #include <string.h>
37 #include <sys/syscall.h>
38 #include "test.h"
39 #include "linux_syscall_numbers.h"
40 #include "fanotify.h"
41 #include "safe_macros.h"
42
43 char *TCID = "fanotify01";
44 int TST_TOTAL = 12;
45
46 #if defined(HAVE_SYS_FANOTIFY_H)
47 #include <sys/fanotify.h>
48
49 #define EVENT_MAX 1024
50 /* size of the event structure, not counting name */
51 #define EVENT_SIZE (sizeof (struct fanotify_event_metadata))
52 /* reasonable guess as to size of 1024 events */
53 #define EVENT_BUF_LEN (EVENT_MAX * EVENT_SIZE)
54
55 static void setup(void);
56 static void cleanup(void);
57
58 #define BUF_SIZE 256
59 static char fname[BUF_SIZE];
60 static char buf[BUF_SIZE];
61 static int fd, fd_notify;
62
63 static unsigned long long event_set[EVENT_MAX];
64
65 static char event_buf[EVENT_BUF_LEN];
66
main(int ac,char ** av)67 int main(int ac, char **av)
68 {
69 int lc;
70
71 tst_parse_opts(ac, av, NULL, NULL);
72
73 setup();
74
75 for (lc = 0; TEST_LOOPING(lc); lc++) {
76 int ret, len, i = 0, test_num = 0;
77
78 tst_count = 0;
79
80 if (fanotify_mark(fd_notify, FAN_MARK_ADD, FAN_ACCESS | FAN_MODIFY |
81 FAN_CLOSE | FAN_OPEN, AT_FDCWD, fname) < 0) {
82 tst_brkm(TBROK | TERRNO, cleanup,
83 "fanotify_mark (%d, FAN_MARK_ADD, FAN_ACCESS | "
84 "FAN_MODIFY | FAN_CLOSE | FAN_OPEN, AT_FDCWD, %s) "
85 "failed", fd_notify, fname);
86 }
87
88 /*
89 * generate sequence of events
90 */
91 fd = SAFE_OPEN(cleanup, fname, O_RDONLY);
92 event_set[tst_count] = FAN_OPEN;
93 tst_count++;
94
95 SAFE_READ(cleanup, 0, fd, buf, BUF_SIZE);
96 event_set[tst_count] = FAN_ACCESS;
97 tst_count++;
98
99 SAFE_CLOSE(cleanup, fd);
100 event_set[tst_count] = FAN_CLOSE_NOWRITE;
101 tst_count++;
102
103 /*
104 * Get list of events so far. We get events here to avoid
105 * merging of following events with the previous ones.
106 */
107 ret = SAFE_READ(cleanup, 0, fd_notify, event_buf, EVENT_BUF_LEN);
108 len = ret;
109
110 fd = SAFE_OPEN(cleanup, fname, O_RDWR | O_CREAT, 0700);
111 event_set[tst_count] = FAN_OPEN;
112 tst_count++;
113
114 SAFE_WRITE(cleanup, 1, fd, fname, strlen(fname));
115 event_set[tst_count] = FAN_MODIFY;
116 tst_count++;
117
118 SAFE_CLOSE(cleanup, fd);
119 event_set[tst_count] = FAN_CLOSE_WRITE;
120 tst_count++;
121
122 /*
123 * get another list of events
124 */
125 ret = SAFE_READ(cleanup, 0, fd_notify, event_buf + len,
126 EVENT_BUF_LEN - len);
127 len += ret;
128
129 /*
130 * Ignore mask testing
131 */
132
133 /* Ignore access events */
134 if (fanotify_mark(fd_notify,
135 FAN_MARK_ADD | FAN_MARK_IGNORED_MASK,
136 FAN_ACCESS, AT_FDCWD, fname) < 0) {
137 tst_brkm(TBROK | TERRNO, cleanup,
138 "fanotify_mark (%d, FAN_MARK_ADD | "
139 "FAN_MARK_IGNORED_MASK, FAN_ACCESS, "
140 "AT_FDCWD, %s) failed", fd_notify, fname);
141 }
142
143 fd = SAFE_OPEN(cleanup, fname, O_RDWR);
144 event_set[tst_count] = FAN_OPEN;
145 tst_count++;
146
147 /* This event should be ignored */
148 SAFE_READ(cleanup, 0, fd, buf, BUF_SIZE);
149
150 /*
151 * get another list of events to verify the last one got ignored
152 */
153 ret = SAFE_READ(cleanup, 0, fd_notify, event_buf + len,
154 EVENT_BUF_LEN - len);
155 len += ret;
156
157 lseek(fd, 0, SEEK_SET);
158 /* Generate modify event to clear ignore mask */
159 SAFE_WRITE(cleanup, 1, fd, fname, 1);
160 event_set[tst_count] = FAN_MODIFY;
161 tst_count++;
162
163 /*
164 * This event shouldn't be ignored because previous modification
165 * should have removed the ignore mask
166 */
167 SAFE_READ(cleanup, 0, fd, buf, BUF_SIZE);
168 event_set[tst_count] = FAN_ACCESS;
169 tst_count++;
170
171 SAFE_CLOSE(cleanup, fd);
172 event_set[tst_count] = FAN_CLOSE_WRITE;
173 tst_count++;
174
175 /* Read events to verify previous access was properly generated */
176 ret = SAFE_READ(cleanup, 0, fd_notify, event_buf + len,
177 EVENT_BUF_LEN - len);
178 len += ret;
179
180 /*
181 * Now ignore open & close events regardless of file
182 * modifications
183 */
184 if (fanotify_mark(fd_notify,
185 FAN_MARK_ADD | FAN_MARK_IGNORED_MASK | FAN_MARK_IGNORED_SURV_MODIFY,
186 FAN_OPEN | FAN_CLOSE, AT_FDCWD, fname) < 0) {
187 tst_brkm(TBROK | TERRNO, cleanup,
188 "fanotify_mark (%d, FAN_MARK_ADD | "
189 "FAN_MARK_IGNORED_MASK | "
190 "FAN_MARK_IGNORED_SURV_MODIFY, FAN_OPEN | "
191 "FAN_CLOSE, AT_FDCWD, %s) failed", fd_notify,
192 fname);
193 }
194
195 /* This event should be ignored */
196 fd = SAFE_OPEN(cleanup, fname, O_RDWR);
197
198 SAFE_WRITE(cleanup, 1, fd, fname, 1);
199 event_set[tst_count] = FAN_MODIFY;
200 tst_count++;
201
202 /* This event should be still ignored */
203 SAFE_CLOSE(cleanup, fd);
204
205 /* This event should still be ignored */
206 fd = SAFE_OPEN(cleanup, fname, O_RDWR);
207
208 /* Read events to verify open & close were ignored */
209 ret = SAFE_READ(cleanup, 0, fd_notify, event_buf + len,
210 EVENT_BUF_LEN - len);
211 len += ret;
212
213 /* Now remove open and close from ignored mask */
214 if (fanotify_mark(fd_notify,
215 FAN_MARK_REMOVE | FAN_MARK_IGNORED_MASK,
216 FAN_OPEN | FAN_CLOSE, AT_FDCWD, fname) < 0) {
217 tst_brkm(TBROK | TERRNO, cleanup,
218 "fanotify_mark (%d, FAN_MARK_REMOVE | "
219 "FAN_MARK_IGNORED_MASK, FAN_OPEN | "
220 "FAN_CLOSE, AT_FDCWD, %s) failed", fd_notify,
221 fname);
222 }
223
224 SAFE_CLOSE(cleanup, fd);
225 event_set[tst_count] = FAN_CLOSE_WRITE;
226 tst_count++;
227
228 /* Read events to verify close was generated */
229 ret = SAFE_READ(cleanup, 0, fd_notify, event_buf + len,
230 EVENT_BUF_LEN - len);
231 len += ret;
232
233 if (TST_TOTAL != tst_count) {
234 tst_brkm(TBROK, cleanup,
235 "TST_TOTAL (%d) and tst_count (%d) are not "
236 "equal", TST_TOTAL, tst_count);
237 }
238 tst_count = 0;
239
240 /*
241 * check events
242 */
243 while (i < len) {
244 struct fanotify_event_metadata *event;
245
246 event = (struct fanotify_event_metadata *)&event_buf[i];
247 if (test_num >= TST_TOTAL) {
248 tst_resm(TFAIL,
249 "get unnecessary event: mask=%llx "
250 "pid=%u fd=%u",
251 (unsigned long long)event->mask,
252 (unsigned)event->pid, event->fd);
253 } else if (!(event->mask & event_set[test_num])) {
254 tst_resm(TFAIL,
255 "get event: mask=%llx (expected %llx) "
256 "pid=%u fd=%u",
257 (unsigned long long)event->mask,
258 event_set[test_num],
259 (unsigned)event->pid, event->fd);
260 } else if (event->pid != getpid()) {
261 tst_resm(TFAIL,
262 "get event: mask=%llx pid=%u "
263 "(expected %u) fd=%u",
264 (unsigned long long)event->mask,
265 (unsigned)event->pid,
266 (unsigned)getpid(),
267 event->fd);
268 } else {
269 if (event->fd == -2)
270 goto pass;
271 ret = read(event->fd, buf, BUF_SIZE);
272 if (ret != strlen(fname)) {
273 tst_resm(TFAIL,
274 "cannot read from returned fd "
275 "of event: mask=%llx pid=%u "
276 "fd=%u ret=%d (errno=%d)",
277 (unsigned long long)event->mask,
278 (unsigned)event->pid,
279 event->fd, ret, errno);
280 } else if (memcmp(buf, fname, strlen(fname))) {
281 tst_resm(TFAIL,
282 "wrong data read from returned fd "
283 "of event: mask=%llx pid=%u "
284 "fd=%u",
285 (unsigned long long)event->mask,
286 (unsigned)event->pid,
287 event->fd);
288 } else {
289 pass:
290 tst_resm(TPASS,
291 "get event: mask=%llx pid=%u fd=%u",
292 (unsigned long long)event->mask,
293 (unsigned)event->pid, event->fd);
294 }
295 }
296 /*
297 * We have verified the data now so close fd and
298 * invalidate it so that we don't check it again
299 * unnecessarily
300 */
301 close(event->fd);
302 event->fd = -2;
303 event->mask &= ~event_set[test_num];
304 /* No events left in current mask? Go for next event */
305 if (event->mask == 0) {
306 i += event->event_len;
307 }
308 test_num++;
309 }
310 for (; test_num < TST_TOTAL; test_num++) {
311 tst_resm(TFAIL, "didn't get event: mask=%llx",
312 event_set[test_num]);
313
314 }
315 /* Remove mark to clear FAN_MARK_IGNORED_SURV_MODIFY */
316 if (fanotify_mark(fd_notify, FAN_MARK_REMOVE, FAN_ACCESS | FAN_MODIFY |
317 FAN_CLOSE | FAN_OPEN, AT_FDCWD, fname) < 0) {
318 tst_brkm(TBROK | TERRNO, cleanup,
319 "fanotify_mark (%d, FAN_MARK_REMOVE, FAN_ACCESS | "
320 "FAN_MODIFY | FAN_CLOSE | FAN_OPEN, AT_FDCWD, %s) "
321 "failed", fd_notify, fname);
322 }
323
324 }
325
326 cleanup();
327 tst_exit();
328 }
329
setup(void)330 static void setup(void)
331 {
332 tst_sig(NOFORK, DEF_HANDLER, cleanup);
333
334 TEST_PAUSE;
335
336 tst_tmpdir();
337
338 sprintf(fname, "tfile_%d", getpid());
339 fd = SAFE_OPEN(cleanup, fname, O_RDWR | O_CREAT, 0700);
340 SAFE_WRITE(cleanup, 1, fd, fname, 1);
341
342 /* close the file we have open */
343 SAFE_CLOSE(cleanup, fd);
344
345 if ((fd_notify = fanotify_init(FAN_CLASS_NOTIF, O_RDONLY)) < 0) {
346 if (errno == ENOSYS) {
347 tst_brkm(TCONF, cleanup,
348 "fanotify is not configured in this kernel.");
349 } else {
350 tst_brkm(TBROK | TERRNO, cleanup,
351 "fanotify_init failed");
352 }
353 }
354 }
355
cleanup(void)356 static void cleanup(void)
357 {
358 if (fd_notify > 0 && close(fd_notify))
359 tst_resm(TWARN | TERRNO, "close(%d) failed", fd_notify);
360
361 tst_rmdir();
362 }
363
364 #else
365
main(void)366 int main(void)
367 {
368 tst_brkm(TCONF, NULL, "system doesn't have required fanotify support");
369 }
370
371 #endif
372