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
8/*\
9 * [Description]
10 * Check that fanotify work for children of a directory.
11 */
12
13#define _GNU_SOURCE
14#include "config.h"
15
16#include <stdio.h>
17#include <sys/stat.h>
18#include <sys/types.h>
19#include <errno.h>
20#include <string.h>
21#include <sys/syscall.h>
22#include "tst_test.h"
23
24#ifdef HAVE_SYS_FANOTIFY_H
25#include "fanotify.h"
26
27#define EVENT_MAX 1024
28/* size of the event structure, not counting name */
29#define EVENT_SIZE  (sizeof(struct fanotify_event_metadata))
30/* reasonable guess as to size of 1024 events */
31#define EVENT_BUF_LEN        (EVENT_MAX * EVENT_SIZE)
32
33#define BUF_SIZE 256
34#define TST_TOTAL 8
35
36static char fname[BUF_SIZE];
37static char buf[BUF_SIZE];
38static int fd, fd_notify;
39
40static unsigned long long event_set[EVENT_MAX];
41
42static char event_buf[EVENT_BUF_LEN];
43
44static void test01(void)
45{
46	int ret, len, i = 0, test_num = 0;
47
48	int tst_count = 0;
49
50	SAFE_FANOTIFY_MARK(fd_notify, FAN_MARK_ADD, FAN_ACCESS |
51			  FAN_MODIFY | FAN_CLOSE | FAN_OPEN | FAN_EVENT_ON_CHILD |
52			  FAN_ONDIR, AT_FDCWD, ".");
53
54	/*
55	 * generate sequence of events
56	 */
57	fd = SAFE_OPEN(fname, O_RDWR | O_CREAT, 0700);
58	event_set[tst_count] = FAN_OPEN;
59	tst_count++;
60
61	SAFE_WRITE(SAFE_WRITE_ALL, fd, fname, strlen(fname));
62	event_set[tst_count] = FAN_MODIFY;
63	tst_count++;
64
65	SAFE_CLOSE(fd);
66	event_set[tst_count] = FAN_CLOSE_WRITE;
67	tst_count++;
68
69	/*
70	 * Get list of events so far. We get events here to avoid
71	 * merging of following events with the previous ones.
72	 */
73	ret = SAFE_READ(0, fd_notify, event_buf,
74			EVENT_BUF_LEN);
75	len = ret;
76
77	fd = SAFE_OPEN(fname, O_RDONLY);
78	event_set[tst_count] = FAN_OPEN;
79	tst_count++;
80
81	SAFE_READ(0, fd, buf, BUF_SIZE);
82	event_set[tst_count] = FAN_ACCESS;
83	tst_count++;
84
85	SAFE_CLOSE(fd);
86	event_set[tst_count] = FAN_CLOSE_NOWRITE;
87	tst_count++;
88
89	/*
90	 * get next events
91	 */
92	ret = SAFE_READ(0, fd_notify, event_buf + len,
93			EVENT_BUF_LEN - len);
94	len += ret;
95
96	/*
97	 * now remove child mark
98	 */
99	SAFE_FANOTIFY_MARK(fd_notify, FAN_MARK_REMOVE,
100			  FAN_EVENT_ON_CHILD, AT_FDCWD, ".");
101
102	/*
103	 * Do something to verify events didn't get generated
104	 */
105	fd = SAFE_OPEN(fname, O_RDONLY);
106
107	SAFE_CLOSE(fd);
108
109	fd = SAFE_OPEN(".", O_RDONLY | O_DIRECTORY);
110	event_set[tst_count] = FAN_OPEN;
111	tst_count++;
112
113	SAFE_CLOSE(fd);
114	event_set[tst_count] = FAN_CLOSE_NOWRITE;
115	tst_count++;
116
117	/*
118	 * Check events got generated only for the directory
119	 */
120	ret = SAFE_READ(0, fd_notify, event_buf + len,
121			EVENT_BUF_LEN - len);
122	len += ret;
123
124	if (TST_TOTAL != tst_count) {
125		tst_brk(TBROK,
126			"TST_TOTAL and tst_count are not equal");
127	}
128	tst_count = 0;
129
130	/*
131	 * check events
132	 */
133	while (i < len) {
134		struct fanotify_event_metadata *event;
135
136		event = (struct fanotify_event_metadata *)&event_buf[i];
137		if (test_num >= TST_TOTAL) {
138			tst_res(TFAIL,
139				"get unnecessary event: mask=%llx "
140				"pid=%u fd=%d",
141				(unsigned long long)event->mask,
142				(unsigned int)event->pid, event->fd);
143		} else if (!(event->mask & event_set[test_num])) {
144			tst_res(TFAIL,
145				"got event: mask=%llx (expected %llx) "
146				"pid=%u fd=%d",
147				(unsigned long long)event->mask,
148				event_set[test_num],
149				(unsigned int)event->pid, event->fd);
150		} else if (event->pid != getpid()) {
151			tst_res(TFAIL,
152				"got event: mask=%llx pid=%u "
153				"(expected %u) fd=%d",
154				(unsigned long long)event->mask,
155				(unsigned int)event->pid,
156				(unsigned int)getpid(),
157				event->fd);
158		} else {
159			tst_res(TPASS,
160				"got event: mask=%llx pid=%u fd=%u",
161				(unsigned long long)event->mask,
162				(unsigned int)event->pid, event->fd);
163		}
164		event->mask &= ~event_set[test_num];
165		/* No events left in current mask? Go for next event */
166		if (event->mask == 0) {
167			i += event->event_len;
168			if (event->fd != FAN_NOFD)
169				SAFE_CLOSE(event->fd);
170		}
171		test_num++;
172	}
173	for (; test_num < TST_TOTAL; test_num++) {
174		tst_res(TFAIL, "didn't get event: mask=%llx",
175			event_set[test_num]);
176
177	}
178}
179
180static void setup(void)
181{
182	sprintf(fname, "fname_%d", getpid());
183	fd_notify = SAFE_FANOTIFY_INIT(FAN_CLASS_NOTIF, O_RDONLY);
184}
185
186static void cleanup(void)
187{
188	if (fd_notify > 0)
189		SAFE_CLOSE(fd_notify);
190}
191
192static struct tst_test test = {
193	.test_all = test01,
194	.setup = setup,
195	.cleanup = cleanup,
196	.needs_tmpdir = 1,
197	.needs_root = 1
198};
199
200#else
201	TST_TEST_TCONF("system doesn't have required fanotify support");
202#endif
203