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
43char *TCID = "fanotify01";
44int 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
55static void setup(void);
56static void cleanup(void);
57
58#define BUF_SIZE 256
59static char fname[BUF_SIZE];
60static char buf[BUF_SIZE];
61static int fd, fd_notify;
62
63static unsigned long long event_set[EVENT_MAX];
64
65static char event_buf[EVENT_BUF_LEN];
66
67int 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 {
289pass:
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
330static 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
356static 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
366int main(void)
367{
368	tst_brkm(TCONF, NULL, "system doesn't have required fanotify support");
369}
370
371#endif
372