1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0
2f08c3bdfSopenharmony_ci/*
3f08c3bdfSopenharmony_ci * Copyright (c) 2014 SUSE.  All Rights Reserved.
4f08c3bdfSopenharmony_ci *
5f08c3bdfSopenharmony_ci * Started by Jan Kara <jack@suse.cz>
6f08c3bdfSopenharmony_ci */
7f08c3bdfSopenharmony_ci
8f08c3bdfSopenharmony_ci/*\
9f08c3bdfSopenharmony_ci * [Description]
10f08c3bdfSopenharmony_ci * Check that fanotify properly merges ignore mask of an inode and mountpoint.
11f08c3bdfSopenharmony_ci */
12f08c3bdfSopenharmony_ci
13f08c3bdfSopenharmony_ci/*
14f08c3bdfSopenharmony_ci * This is a regression test for:
15f08c3bdfSopenharmony_ci *
16f08c3bdfSopenharmony_ci *  commit 8edc6e1688fc8f02c8c1f53a2ec4928cb1055f4d
17f08c3bdfSopenharmony_ci *  Author: Jan Kara <jack@suse.cz>
18f08c3bdfSopenharmony_ci *  Date:   Thu Nov 13 15:19:33 2014 -0800
19f08c3bdfSopenharmony_ci *
20f08c3bdfSopenharmony_ci *      fanotify: fix notification of groups with inode & mount marks
21f08c3bdfSopenharmony_ci *
22f08c3bdfSopenharmony_ci * The overlayfs test case is a regression test for:
23f08c3bdfSopenharmony_ci *
24f08c3bdfSopenharmony_ci *  commit d989903058a83e8536cc7aadf9256a47d5c173fe
25f08c3bdfSopenharmony_ci *  Author: Amir Goldstein <amir73il@gmail.com>
26f08c3bdfSopenharmony_ci *  Date:   Wed Apr 24 19:39:50 2019 +0300
27f08c3bdfSopenharmony_ci *
28f08c3bdfSopenharmony_ci *      ovl: do not generate duplicate fsnotify events for "fake" path
29f08c3bdfSopenharmony_ci */
30f08c3bdfSopenharmony_ci
31f08c3bdfSopenharmony_ci#define _GNU_SOURCE
32f08c3bdfSopenharmony_ci#include "config.h"
33f08c3bdfSopenharmony_ci
34f08c3bdfSopenharmony_ci#include <stdio.h>
35f08c3bdfSopenharmony_ci#include <sys/stat.h>
36f08c3bdfSopenharmony_ci#include <sys/types.h>
37f08c3bdfSopenharmony_ci#include <errno.h>
38f08c3bdfSopenharmony_ci#include <string.h>
39f08c3bdfSopenharmony_ci#include <sys/mount.h>
40f08c3bdfSopenharmony_ci#include <sys/syscall.h>
41f08c3bdfSopenharmony_ci#include "tst_test.h"
42f08c3bdfSopenharmony_ci
43f08c3bdfSopenharmony_ci#ifdef HAVE_SYS_FANOTIFY_H
44f08c3bdfSopenharmony_ci#include "fanotify.h"
45f08c3bdfSopenharmony_ci
46f08c3bdfSopenharmony_ci#define EVENT_MAX 1024
47f08c3bdfSopenharmony_ci/* size of the event structure, not counting name */
48f08c3bdfSopenharmony_ci#define EVENT_SIZE  (sizeof(struct fanotify_event_metadata))
49f08c3bdfSopenharmony_ci/* reasonable guess as to size of 1024 events */
50f08c3bdfSopenharmony_ci#define EVENT_BUF_LEN        (EVENT_MAX * EVENT_SIZE)
51f08c3bdfSopenharmony_ci
52f08c3bdfSopenharmony_cistatic unsigned int fanotify_prio[] = {
53f08c3bdfSopenharmony_ci	FAN_CLASS_PRE_CONTENT,
54f08c3bdfSopenharmony_ci	FAN_CLASS_CONTENT,
55f08c3bdfSopenharmony_ci	FAN_CLASS_NOTIF
56f08c3bdfSopenharmony_ci};
57f08c3bdfSopenharmony_ci#define FANOTIFY_PRIORITIES ARRAY_SIZE(fanotify_prio)
58f08c3bdfSopenharmony_ci
59f08c3bdfSopenharmony_ci#define GROUPS_PER_PRIO 3
60f08c3bdfSopenharmony_ci
61f08c3bdfSopenharmony_ci#define BUF_SIZE 256
62f08c3bdfSopenharmony_cistatic char fname[BUF_SIZE];
63f08c3bdfSopenharmony_cistatic int fd_notify[FANOTIFY_PRIORITIES][GROUPS_PER_PRIO];
64f08c3bdfSopenharmony_ci
65f08c3bdfSopenharmony_cistatic char event_buf[EVENT_BUF_LEN];
66f08c3bdfSopenharmony_ci
67f08c3bdfSopenharmony_cistatic const char mntpoint[] = OVL_BASE_MNTPOINT;
68f08c3bdfSopenharmony_ci
69f08c3bdfSopenharmony_cistatic int ovl_mounted;
70f08c3bdfSopenharmony_ci
71f08c3bdfSopenharmony_cistatic struct tcase {
72f08c3bdfSopenharmony_ci	const char *tname;
73f08c3bdfSopenharmony_ci	const char *mnt;
74f08c3bdfSopenharmony_ci	int use_overlay;
75f08c3bdfSopenharmony_ci} tcases[] = {
76f08c3bdfSopenharmony_ci	{ "Fanotify merge mount mark", mntpoint, 0 },
77f08c3bdfSopenharmony_ci	{ "Fanotify merge overlayfs mount mark", OVL_MNT, 1 },
78f08c3bdfSopenharmony_ci};
79f08c3bdfSopenharmony_ci
80f08c3bdfSopenharmony_cistatic void create_fanotify_groups(void)
81f08c3bdfSopenharmony_ci{
82f08c3bdfSopenharmony_ci	unsigned int p, i;
83f08c3bdfSopenharmony_ci
84f08c3bdfSopenharmony_ci	for (p = 0; p < FANOTIFY_PRIORITIES; p++) {
85f08c3bdfSopenharmony_ci		for (i = 0; i < GROUPS_PER_PRIO; i++) {
86f08c3bdfSopenharmony_ci			fd_notify[p][i] = SAFE_FANOTIFY_INIT(fanotify_prio[p] |
87f08c3bdfSopenharmony_ci							     FAN_NONBLOCK,
88f08c3bdfSopenharmony_ci							     O_RDONLY);
89f08c3bdfSopenharmony_ci
90f08c3bdfSopenharmony_ci			/* Add mount mark for each group */
91f08c3bdfSopenharmony_ci			SAFE_FANOTIFY_MARK(fd_notify[p][i],
92f08c3bdfSopenharmony_ci					    FAN_MARK_ADD | FAN_MARK_MOUNT,
93f08c3bdfSopenharmony_ci					    FAN_MODIFY,
94f08c3bdfSopenharmony_ci					    AT_FDCWD, fname);
95f08c3bdfSopenharmony_ci
96f08c3bdfSopenharmony_ci			/* Add ignore mark for groups with higher priority */
97f08c3bdfSopenharmony_ci			if (p == 0)
98f08c3bdfSopenharmony_ci				continue;
99f08c3bdfSopenharmony_ci
100f08c3bdfSopenharmony_ci			SAFE_FANOTIFY_MARK(fd_notify[p][i],
101f08c3bdfSopenharmony_ci					    FAN_MARK_ADD |
102f08c3bdfSopenharmony_ci					    FAN_MARK_IGNORED_MASK |
103f08c3bdfSopenharmony_ci					    FAN_MARK_IGNORED_SURV_MODIFY,
104f08c3bdfSopenharmony_ci					    FAN_MODIFY, AT_FDCWD, fname);
105f08c3bdfSopenharmony_ci		}
106f08c3bdfSopenharmony_ci	}
107f08c3bdfSopenharmony_ci}
108f08c3bdfSopenharmony_ci
109f08c3bdfSopenharmony_cistatic void cleanup_fanotify_groups(void)
110f08c3bdfSopenharmony_ci{
111f08c3bdfSopenharmony_ci	unsigned int i, p;
112f08c3bdfSopenharmony_ci
113f08c3bdfSopenharmony_ci	for (p = 0; p < FANOTIFY_PRIORITIES; p++) {
114f08c3bdfSopenharmony_ci		for (i = 0; i < GROUPS_PER_PRIO; i++) {
115f08c3bdfSopenharmony_ci			if (fd_notify[p][i] > 0)
116f08c3bdfSopenharmony_ci				SAFE_CLOSE(fd_notify[p][i]);
117f08c3bdfSopenharmony_ci		}
118f08c3bdfSopenharmony_ci	}
119f08c3bdfSopenharmony_ci}
120f08c3bdfSopenharmony_ci
121f08c3bdfSopenharmony_cistatic void verify_event(int group, struct fanotify_event_metadata *event)
122f08c3bdfSopenharmony_ci{
123f08c3bdfSopenharmony_ci	if (event->mask != FAN_MODIFY) {
124f08c3bdfSopenharmony_ci		tst_res(TFAIL, "group %d got event: mask %llx (expected %llx) "
125f08c3bdfSopenharmony_ci			"pid=%u fd=%d", group, (unsigned long long)event->mask,
126f08c3bdfSopenharmony_ci			(unsigned long long)FAN_MODIFY,
127f08c3bdfSopenharmony_ci			(unsigned int)event->pid, event->fd);
128f08c3bdfSopenharmony_ci	} else if (event->pid != getpid()) {
129f08c3bdfSopenharmony_ci		tst_res(TFAIL, "group %d got event: mask %llx pid=%u "
130f08c3bdfSopenharmony_ci			"(expected %u) fd=%d", group,
131f08c3bdfSopenharmony_ci			(unsigned long long)event->mask, (unsigned int)event->pid,
132f08c3bdfSopenharmony_ci			(unsigned int)getpid(), event->fd);
133f08c3bdfSopenharmony_ci	} else {
134f08c3bdfSopenharmony_ci		tst_res(TPASS, "group %d got event: mask %llx pid=%u fd=%d",
135f08c3bdfSopenharmony_ci			group, (unsigned long long)event->mask,
136f08c3bdfSopenharmony_ci			(unsigned int)event->pid, event->fd);
137f08c3bdfSopenharmony_ci	}
138f08c3bdfSopenharmony_ci}
139f08c3bdfSopenharmony_ci
140f08c3bdfSopenharmony_ci/* Close all file descriptors of read events */
141f08c3bdfSopenharmony_cistatic void close_events_fd(struct fanotify_event_metadata *event, int buflen)
142f08c3bdfSopenharmony_ci{
143f08c3bdfSopenharmony_ci	while (buflen >= (int)FAN_EVENT_METADATA_LEN) {
144f08c3bdfSopenharmony_ci		if (event->fd != FAN_NOFD)
145f08c3bdfSopenharmony_ci			SAFE_CLOSE(event->fd);
146f08c3bdfSopenharmony_ci		buflen -= (int)FAN_EVENT_METADATA_LEN;
147f08c3bdfSopenharmony_ci		event++;
148f08c3bdfSopenharmony_ci	}
149f08c3bdfSopenharmony_ci}
150f08c3bdfSopenharmony_ci
151f08c3bdfSopenharmony_cistatic void test_fanotify(unsigned int n)
152f08c3bdfSopenharmony_ci{
153f08c3bdfSopenharmony_ci	int ret;
154f08c3bdfSopenharmony_ci	unsigned int p, i;
155f08c3bdfSopenharmony_ci	struct fanotify_event_metadata *event;
156f08c3bdfSopenharmony_ci	struct tcase *tc = &tcases[n];
157f08c3bdfSopenharmony_ci
158f08c3bdfSopenharmony_ci	tst_res(TINFO, "Test #%d: %s", n, tc->tname);
159f08c3bdfSopenharmony_ci
160f08c3bdfSopenharmony_ci	if (tc->use_overlay && !ovl_mounted) {
161f08c3bdfSopenharmony_ci		tst_res(TCONF, "overlayfs is not configured in this kernel");
162f08c3bdfSopenharmony_ci		return;
163f08c3bdfSopenharmony_ci	}
164f08c3bdfSopenharmony_ci
165f08c3bdfSopenharmony_ci	sprintf(fname, "%s/tfile_%d", tc->mnt, getpid());
166f08c3bdfSopenharmony_ci	SAFE_TOUCH(fname, 0644, NULL);
167f08c3bdfSopenharmony_ci
168f08c3bdfSopenharmony_ci	create_fanotify_groups();
169f08c3bdfSopenharmony_ci
170f08c3bdfSopenharmony_ci	/*
171f08c3bdfSopenharmony_ci	 * generate sequence of events
172f08c3bdfSopenharmony_ci	 */
173f08c3bdfSopenharmony_ci	SAFE_FILE_PRINTF(fname, "1");
174f08c3bdfSopenharmony_ci
175f08c3bdfSopenharmony_ci	/* First verify all groups without ignore mask got the event */
176f08c3bdfSopenharmony_ci	for (i = 0; i < GROUPS_PER_PRIO; i++) {
177f08c3bdfSopenharmony_ci		ret = read(fd_notify[0][i], event_buf, EVENT_BUF_LEN);
178f08c3bdfSopenharmony_ci		if (ret < 0) {
179f08c3bdfSopenharmony_ci			if (errno == EAGAIN) {
180f08c3bdfSopenharmony_ci				tst_res(TFAIL, "group %d did not get "
181f08c3bdfSopenharmony_ci					"event", i);
182f08c3bdfSopenharmony_ci			}
183f08c3bdfSopenharmony_ci			tst_brk(TBROK | TERRNO,
184f08c3bdfSopenharmony_ci				"reading fanotify events failed");
185f08c3bdfSopenharmony_ci		}
186f08c3bdfSopenharmony_ci		if (ret < (int)FAN_EVENT_METADATA_LEN) {
187f08c3bdfSopenharmony_ci			tst_brk(TBROK,
188f08c3bdfSopenharmony_ci				"short read when reading fanotify "
189f08c3bdfSopenharmony_ci				"events (%d < %d)", ret,
190f08c3bdfSopenharmony_ci				(int)EVENT_BUF_LEN);
191f08c3bdfSopenharmony_ci		}
192f08c3bdfSopenharmony_ci		event = (struct fanotify_event_metadata *)event_buf;
193f08c3bdfSopenharmony_ci		if (ret > (int)event->event_len) {
194f08c3bdfSopenharmony_ci			tst_res(TFAIL, "group %d got more than one "
195f08c3bdfSopenharmony_ci				"event (%d > %d)", i, ret,
196f08c3bdfSopenharmony_ci				event->event_len);
197f08c3bdfSopenharmony_ci		} else {
198f08c3bdfSopenharmony_ci			verify_event(i, event);
199f08c3bdfSopenharmony_ci		}
200f08c3bdfSopenharmony_ci		close_events_fd(event, ret);
201f08c3bdfSopenharmony_ci	}
202f08c3bdfSopenharmony_ci
203f08c3bdfSopenharmony_ci	for (p = 1; p < FANOTIFY_PRIORITIES; p++) {
204f08c3bdfSopenharmony_ci		for (i = 0; i < GROUPS_PER_PRIO; i++) {
205f08c3bdfSopenharmony_ci			ret = read(fd_notify[p][i], event_buf, EVENT_BUF_LEN);
206f08c3bdfSopenharmony_ci			if (ret > 0) {
207f08c3bdfSopenharmony_ci				tst_res(TFAIL, "group %d got event",
208f08c3bdfSopenharmony_ci					p*GROUPS_PER_PRIO + i);
209f08c3bdfSopenharmony_ci				close_events_fd((void *)event_buf, ret);
210f08c3bdfSopenharmony_ci			} else if (ret == 0) {
211f08c3bdfSopenharmony_ci				tst_brk(TBROK, "zero length "
212f08c3bdfSopenharmony_ci					"read from fanotify fd");
213f08c3bdfSopenharmony_ci			} else if (errno != EAGAIN) {
214f08c3bdfSopenharmony_ci				tst_brk(TBROK | TERRNO,
215f08c3bdfSopenharmony_ci					"reading fanotify events failed");
216f08c3bdfSopenharmony_ci			} else {
217f08c3bdfSopenharmony_ci				tst_res(TPASS, "group %d got no event",
218f08c3bdfSopenharmony_ci					p*GROUPS_PER_PRIO + i);
219f08c3bdfSopenharmony_ci			}
220f08c3bdfSopenharmony_ci		}
221f08c3bdfSopenharmony_ci	}
222f08c3bdfSopenharmony_ci	cleanup_fanotify_groups();
223f08c3bdfSopenharmony_ci}
224f08c3bdfSopenharmony_ci
225f08c3bdfSopenharmony_cistatic void setup(void)
226f08c3bdfSopenharmony_ci{
227f08c3bdfSopenharmony_ci	ovl_mounted = TST_MOUNT_OVERLAY();
228f08c3bdfSopenharmony_ci}
229f08c3bdfSopenharmony_ci
230f08c3bdfSopenharmony_cistatic void cleanup(void)
231f08c3bdfSopenharmony_ci{
232f08c3bdfSopenharmony_ci	cleanup_fanotify_groups();
233f08c3bdfSopenharmony_ci
234f08c3bdfSopenharmony_ci	if (ovl_mounted)
235f08c3bdfSopenharmony_ci		SAFE_UMOUNT(OVL_MNT);
236f08c3bdfSopenharmony_ci}
237f08c3bdfSopenharmony_ci
238f08c3bdfSopenharmony_cistatic struct tst_test test = {
239f08c3bdfSopenharmony_ci	.test = test_fanotify,
240f08c3bdfSopenharmony_ci	.tcnt = ARRAY_SIZE(tcases),
241f08c3bdfSopenharmony_ci	.setup = setup,
242f08c3bdfSopenharmony_ci	.cleanup = cleanup,
243f08c3bdfSopenharmony_ci	.needs_root = 1,
244f08c3bdfSopenharmony_ci	.mount_device = 1,
245f08c3bdfSopenharmony_ci	.mntpoint = mntpoint,
246f08c3bdfSopenharmony_ci	.tags = (const struct tst_tag[]) {
247f08c3bdfSopenharmony_ci		{"linux-git", "8edc6e1688fc"},
248f08c3bdfSopenharmony_ci		{"linux-git", "d989903058a8"},
249f08c3bdfSopenharmony_ci		{}
250f08c3bdfSopenharmony_ci	}
251f08c3bdfSopenharmony_ci};
252f08c3bdfSopenharmony_ci
253f08c3bdfSopenharmony_ci#else
254f08c3bdfSopenharmony_ci	TST_TEST_TCONF("system doesn't have required fanotify support");
255f08c3bdfSopenharmony_ci#endif
256