1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
2f08c3bdfSopenharmony_ci/*
3f08c3bdfSopenharmony_ci * Copyright (c) 2019 CTERA Networks. All Rights Reserved.
4f08c3bdfSopenharmony_ci *
5f08c3bdfSopenharmony_ci * Started by Amir Goldstein <amir73il@gmail.com>
6f08c3bdfSopenharmony_ci * Modified by Matthew Bobrowski <mbobrowski@mbobrowski.org>
7f08c3bdfSopenharmony_ci */
8f08c3bdfSopenharmony_ci
9f08c3bdfSopenharmony_ci/*\
10f08c3bdfSopenharmony_ci * [Description]
11f08c3bdfSopenharmony_ci * Test file that has been purposely designed to verify FAN_REPORT_FID
12f08c3bdfSopenharmony_ci * functionality while using newly defined dirent events.
13f08c3bdfSopenharmony_ci */
14f08c3bdfSopenharmony_ci
15f08c3bdfSopenharmony_ci/*
16f08c3bdfSopenharmony_ci * Test case #1 is a regression test for commit f367a62a7cad:
17f08c3bdfSopenharmony_ci *      fanotify: merge duplicate events on parent and child
18f08c3bdfSopenharmony_ci */
19f08c3bdfSopenharmony_ci
20f08c3bdfSopenharmony_ci#define _GNU_SOURCE
21f08c3bdfSopenharmony_ci#include "config.h"
22f08c3bdfSopenharmony_ci
23f08c3bdfSopenharmony_ci#include <string.h>
24f08c3bdfSopenharmony_ci#include <errno.h>
25f08c3bdfSopenharmony_ci#include <sys/statfs.h>
26f08c3bdfSopenharmony_ci#include <sys/types.h>
27f08c3bdfSopenharmony_ci#include "tst_test.h"
28f08c3bdfSopenharmony_ci
29f08c3bdfSopenharmony_ci#ifdef HAVE_SYS_FANOTIFY_H
30f08c3bdfSopenharmony_ci#include "fanotify.h"
31f08c3bdfSopenharmony_ci
32f08c3bdfSopenharmony_ci#define EVENT_MAX 10
33f08c3bdfSopenharmony_ci
34f08c3bdfSopenharmony_ci/* Size of the event structure, not including file handle */
35f08c3bdfSopenharmony_ci#define EVENT_SIZE (sizeof(struct fanotify_event_metadata) + \
36f08c3bdfSopenharmony_ci		    sizeof(struct fanotify_event_info_fid))
37f08c3bdfSopenharmony_ci
38f08c3bdfSopenharmony_ci/* Double events buffer size to account for file handles */
39f08c3bdfSopenharmony_ci#define EVENT_BUF_LEN (EVENT_MAX * EVENT_SIZE * 2)
40f08c3bdfSopenharmony_ci
41f08c3bdfSopenharmony_ci#define MOUNT_POINT "mntpoint"
42f08c3bdfSopenharmony_ci#define TEST_DIR MOUNT_POINT"/test_dir"
43f08c3bdfSopenharmony_ci#define DIR1 TEST_DIR"/dir1"
44f08c3bdfSopenharmony_ci#define DIR2 TEST_DIR"/dir2"
45f08c3bdfSopenharmony_ci#define FILE1 TEST_DIR"/file1"
46f08c3bdfSopenharmony_ci#define FILE2 TEST_DIR"/file2"
47f08c3bdfSopenharmony_ci
48f08c3bdfSopenharmony_ci#if defined(HAVE_NAME_TO_HANDLE_AT)
49f08c3bdfSopenharmony_cistruct event_t {
50f08c3bdfSopenharmony_ci	unsigned long long mask;
51f08c3bdfSopenharmony_ci	struct fanotify_fid_t *fid;
52f08c3bdfSopenharmony_ci};
53f08c3bdfSopenharmony_ci
54f08c3bdfSopenharmony_cistatic int fanotify_fd;
55f08c3bdfSopenharmony_cistatic char events_buf[EVENT_BUF_LEN];
56f08c3bdfSopenharmony_cistatic struct event_t event_set[EVENT_MAX];
57f08c3bdfSopenharmony_ci
58f08c3bdfSopenharmony_cistatic struct test_case_t {
59f08c3bdfSopenharmony_ci	const char *tname;
60f08c3bdfSopenharmony_ci	struct fanotify_mark_type mark;
61f08c3bdfSopenharmony_ci	unsigned long mask;
62f08c3bdfSopenharmony_ci} test_cases[] = {
63f08c3bdfSopenharmony_ci	{
64f08c3bdfSopenharmony_ci		"FAN_REPORT_FID on filesystem including FAN_DELETE_SELF",
65f08c3bdfSopenharmony_ci		INIT_FANOTIFY_MARK_TYPE(FILESYSTEM),
66f08c3bdfSopenharmony_ci		FAN_DELETE_SELF,
67f08c3bdfSopenharmony_ci	},
68f08c3bdfSopenharmony_ci	{
69f08c3bdfSopenharmony_ci		"FAN_REPORT_FID on directory with FAN_EVENT_ON_CHILD",
70f08c3bdfSopenharmony_ci		INIT_FANOTIFY_MARK_TYPE(INODE),
71f08c3bdfSopenharmony_ci		FAN_EVENT_ON_CHILD,
72f08c3bdfSopenharmony_ci	},
73f08c3bdfSopenharmony_ci};
74f08c3bdfSopenharmony_ci
75f08c3bdfSopenharmony_cistatic void do_test(unsigned int number)
76f08c3bdfSopenharmony_ci{
77f08c3bdfSopenharmony_ci	int i, fd, len, count = 0;
78f08c3bdfSopenharmony_ci
79f08c3bdfSopenharmony_ci	struct file_handle *event_file_handle;
80f08c3bdfSopenharmony_ci	struct fanotify_event_metadata *metadata;
81f08c3bdfSopenharmony_ci	struct fanotify_event_info_fid *event_fid;
82f08c3bdfSopenharmony_ci	struct test_case_t *tc = &test_cases[number];
83f08c3bdfSopenharmony_ci	struct fanotify_mark_type *mark = &tc->mark;
84f08c3bdfSopenharmony_ci	struct fanotify_fid_t root_fid, dir_fid, file_fid;
85f08c3bdfSopenharmony_ci
86f08c3bdfSopenharmony_ci	tst_res(TINFO, "Test #%d: %s", number, tc->tname);
87f08c3bdfSopenharmony_ci
88f08c3bdfSopenharmony_ci	SAFE_FANOTIFY_MARK(fanotify_fd, FAN_MARK_ADD | mark->flag, tc->mask |
89f08c3bdfSopenharmony_ci				FAN_CREATE | FAN_DELETE | FAN_MOVE |
90f08c3bdfSopenharmony_ci				FAN_MODIFY | FAN_ONDIR,
91f08c3bdfSopenharmony_ci				AT_FDCWD, TEST_DIR);
92f08c3bdfSopenharmony_ci
93f08c3bdfSopenharmony_ci	/* Save the test root dir fid */
94f08c3bdfSopenharmony_ci	fanotify_save_fid(TEST_DIR, &root_fid);
95f08c3bdfSopenharmony_ci
96f08c3bdfSopenharmony_ci	/* All dirent events on testdir are merged */
97f08c3bdfSopenharmony_ci	event_set[count].mask = FAN_CREATE | FAN_MOVE | FAN_DELETE;
98f08c3bdfSopenharmony_ci	event_set[count].fid = &root_fid;
99f08c3bdfSopenharmony_ci	count++;
100f08c3bdfSopenharmony_ci
101f08c3bdfSopenharmony_ci	fd = SAFE_CREAT(FILE1, 0644);
102f08c3bdfSopenharmony_ci	SAFE_CLOSE(fd);
103f08c3bdfSopenharmony_ci
104f08c3bdfSopenharmony_ci	/* Save the file fid */
105f08c3bdfSopenharmony_ci	fanotify_save_fid(FILE1, &file_fid);
106f08c3bdfSopenharmony_ci
107f08c3bdfSopenharmony_ci	/* Recursive watch file for events "on self" */
108f08c3bdfSopenharmony_ci	if (mark->flag == FAN_MARK_INODE &&
109f08c3bdfSopenharmony_ci	    fanotify_mark(fanotify_fd, FAN_MARK_ADD | mark->flag,
110f08c3bdfSopenharmony_ci			  FAN_MODIFY | FAN_DELETE_SELF,
111f08c3bdfSopenharmony_ci			  AT_FDCWD, FILE1) == -1) {
112f08c3bdfSopenharmony_ci		tst_brk(TBROK | TERRNO,
113f08c3bdfSopenharmony_ci			"fanotify_mark(%d, FAN_MARK_ADD | %s, "
114f08c3bdfSopenharmony_ci			"FAN_DELETE_SELF, AT_FDCWD, %s) failed",
115f08c3bdfSopenharmony_ci			fanotify_fd, mark->name, FILE1);
116f08c3bdfSopenharmony_ci	}
117f08c3bdfSopenharmony_ci
118f08c3bdfSopenharmony_ci	/*
119f08c3bdfSopenharmony_ci	 * Event on child file is not merged with dirent events.
120f08c3bdfSopenharmony_ci	 * FAN_MODIFY event reported on file mark should be merged with the
121f08c3bdfSopenharmony_ci	 * FAN_MODIFY event reported on parent directory watch.
122f08c3bdfSopenharmony_ci	 */
123f08c3bdfSopenharmony_ci	event_set[count].mask = FAN_MODIFY;
124f08c3bdfSopenharmony_ci	event_set[count].fid = &file_fid;
125f08c3bdfSopenharmony_ci	count++;
126f08c3bdfSopenharmony_ci
127f08c3bdfSopenharmony_ci	SAFE_TRUNCATE(FILE1, 1);
128f08c3bdfSopenharmony_ci	SAFE_RENAME(FILE1, FILE2);
129f08c3bdfSopenharmony_ci
130f08c3bdfSopenharmony_ci	/*
131f08c3bdfSopenharmony_ci	 * FAN_DELETE_SELF may be merged with FAN_MODIFY event above.
132f08c3bdfSopenharmony_ci	 */
133f08c3bdfSopenharmony_ci	event_set[count].mask = FAN_DELETE_SELF;
134f08c3bdfSopenharmony_ci	event_set[count].fid = &file_fid;
135f08c3bdfSopenharmony_ci	count++;
136f08c3bdfSopenharmony_ci
137f08c3bdfSopenharmony_ci	SAFE_UNLINK(FILE2);
138f08c3bdfSopenharmony_ci
139f08c3bdfSopenharmony_ci	/* Read file events from the event queue */
140f08c3bdfSopenharmony_ci	len = SAFE_READ(0, fanotify_fd, events_buf, EVENT_BUF_LEN);
141f08c3bdfSopenharmony_ci
142f08c3bdfSopenharmony_ci	/*
143f08c3bdfSopenharmony_ci	 * Generate a sequence of events on a directory. Subsequent events
144f08c3bdfSopenharmony_ci	 * are merged, so it's required that we set FAN_ONDIR once in
145f08c3bdfSopenharmony_ci	 * order to acknowledge that changes related to a subdirectory
146f08c3bdfSopenharmony_ci	 * took place. Events on subdirectories are not merged with events
147f08c3bdfSopenharmony_ci	 * on non-subdirectories.
148f08c3bdfSopenharmony_ci	 */
149f08c3bdfSopenharmony_ci	event_set[count].mask = FAN_ONDIR | FAN_CREATE | FAN_MOVE | FAN_DELETE;
150f08c3bdfSopenharmony_ci	event_set[count].fid = &root_fid;
151f08c3bdfSopenharmony_ci	count++;
152f08c3bdfSopenharmony_ci
153f08c3bdfSopenharmony_ci	SAFE_MKDIR(DIR1, 0755);
154f08c3bdfSopenharmony_ci
155f08c3bdfSopenharmony_ci	/* Save the subdir fid */
156f08c3bdfSopenharmony_ci	fanotify_save_fid(DIR1, &dir_fid);
157f08c3bdfSopenharmony_ci
158f08c3bdfSopenharmony_ci	/* Recursive watch subdir for events "on self" */
159f08c3bdfSopenharmony_ci	if (mark->flag == FAN_MARK_INODE &&
160f08c3bdfSopenharmony_ci	    fanotify_mark(fanotify_fd, FAN_MARK_ADD | mark->flag,
161f08c3bdfSopenharmony_ci			  FAN_DELETE_SELF | FAN_ONDIR,
162f08c3bdfSopenharmony_ci			  AT_FDCWD, DIR1) == -1) {
163f08c3bdfSopenharmony_ci		tst_brk(TBROK | TERRNO,
164f08c3bdfSopenharmony_ci			"fanotify_mark(%d, FAN_MARK_ADD | %s, "
165f08c3bdfSopenharmony_ci			"FAN_DELETE_SELF | FAN_ONDIR, AT_FDCWD, %s) failed",
166f08c3bdfSopenharmony_ci			fanotify_fd, mark->name, DIR1);
167f08c3bdfSopenharmony_ci	}
168f08c3bdfSopenharmony_ci
169f08c3bdfSopenharmony_ci	SAFE_RENAME(DIR1, DIR2);
170f08c3bdfSopenharmony_ci
171f08c3bdfSopenharmony_ci	event_set[count].mask = FAN_ONDIR | FAN_DELETE_SELF;
172f08c3bdfSopenharmony_ci	event_set[count].fid = &dir_fid;
173f08c3bdfSopenharmony_ci	count++;
174f08c3bdfSopenharmony_ci
175f08c3bdfSopenharmony_ci	SAFE_RMDIR(DIR2);
176f08c3bdfSopenharmony_ci
177f08c3bdfSopenharmony_ci	/* Read dir events from the event queue */
178f08c3bdfSopenharmony_ci	len += SAFE_READ(0, fanotify_fd, events_buf + len, EVENT_BUF_LEN - len);
179f08c3bdfSopenharmony_ci
180f08c3bdfSopenharmony_ci	/*
181f08c3bdfSopenharmony_ci	 * Cleanup the mark
182f08c3bdfSopenharmony_ci	 */
183f08c3bdfSopenharmony_ci	SAFE_FANOTIFY_MARK(fanotify_fd, FAN_MARK_FLUSH | mark->flag, 0,
184f08c3bdfSopenharmony_ci			  AT_FDCWD, TEST_DIR);
185f08c3bdfSopenharmony_ci
186f08c3bdfSopenharmony_ci	/* Process each event in buffer */
187f08c3bdfSopenharmony_ci	for (i = 0, metadata = (struct fanotify_event_metadata *) events_buf;
188f08c3bdfSopenharmony_ci		FAN_EVENT_OK(metadata, len); i++) {
189f08c3bdfSopenharmony_ci		struct event_t *expected = &event_set[i];
190f08c3bdfSopenharmony_ci
191f08c3bdfSopenharmony_ci		event_fid = (struct fanotify_event_info_fid *) (metadata + 1);
192f08c3bdfSopenharmony_ci		event_file_handle = (struct file_handle *) event_fid->handle;
193f08c3bdfSopenharmony_ci
194f08c3bdfSopenharmony_ci		if (i >= count) {
195f08c3bdfSopenharmony_ci			tst_res(TFAIL,
196f08c3bdfSopenharmony_ci				"got unnecessary event: mask=%llx "
197f08c3bdfSopenharmony_ci				"pid=%u fd=%d",
198f08c3bdfSopenharmony_ci				(unsigned long long) metadata->mask,
199f08c3bdfSopenharmony_ci				metadata->pid,
200f08c3bdfSopenharmony_ci				metadata->fd);
201f08c3bdfSopenharmony_ci			metadata->mask = 0;
202f08c3bdfSopenharmony_ci		} else if (metadata->fd != FAN_NOFD) {
203f08c3bdfSopenharmony_ci			tst_res(TFAIL,
204f08c3bdfSopenharmony_ci				"Received unexpected file descriptor %d in "
205f08c3bdfSopenharmony_ci				"event. Expected to get FAN_NOFD(%d)",
206f08c3bdfSopenharmony_ci				metadata->fd, FAN_NOFD);
207f08c3bdfSopenharmony_ci		} else if (!(metadata->mask & expected->mask)) {
208f08c3bdfSopenharmony_ci			tst_res(TFAIL,
209f08c3bdfSopenharmony_ci				"Got event: mask=%llx (expected %llx) "
210f08c3bdfSopenharmony_ci				"pid=%u fd=%d",
211f08c3bdfSopenharmony_ci				(unsigned long long) metadata->mask,
212f08c3bdfSopenharmony_ci				expected->mask, (unsigned int) metadata->pid,
213f08c3bdfSopenharmony_ci				metadata->fd);
214f08c3bdfSopenharmony_ci		} else if (metadata->pid != getpid()) {
215f08c3bdfSopenharmony_ci			tst_res(TFAIL,
216f08c3bdfSopenharmony_ci				"Got event: mask=%llx pid=%u "
217f08c3bdfSopenharmony_ci				"(expected %u) fd=%d",
218f08c3bdfSopenharmony_ci				(unsigned long long) metadata->mask,
219f08c3bdfSopenharmony_ci				(unsigned int) metadata->pid,
220f08c3bdfSopenharmony_ci				(unsigned int) getpid(),
221f08c3bdfSopenharmony_ci				metadata->fd);
222f08c3bdfSopenharmony_ci		} else if (event_file_handle->handle_bytes !=
223f08c3bdfSopenharmony_ci			   expected->fid->handle.handle_bytes) {
224f08c3bdfSopenharmony_ci			tst_res(TFAIL,
225f08c3bdfSopenharmony_ci				"Got event: handle_bytes (%x) returned in "
226f08c3bdfSopenharmony_ci				"event does not equal handle_bytes (%x) "
227f08c3bdfSopenharmony_ci				"retunred in name_to_handle_at(2)",
228f08c3bdfSopenharmony_ci				event_file_handle->handle_bytes,
229f08c3bdfSopenharmony_ci				expected->fid->handle.handle_bytes);
230f08c3bdfSopenharmony_ci		} else if (event_file_handle->handle_type !=
231f08c3bdfSopenharmony_ci			   expected->fid->handle.handle_type) {
232f08c3bdfSopenharmony_ci			tst_res(TFAIL,
233f08c3bdfSopenharmony_ci				"handle_type (%x) returned in event does not "
234f08c3bdfSopenharmony_ci				"equal to handle_type (%x) returned in "
235f08c3bdfSopenharmony_ci				"name_to_handle_at(2)",
236f08c3bdfSopenharmony_ci				event_file_handle->handle_type,
237f08c3bdfSopenharmony_ci				expected->fid->handle.handle_type);
238f08c3bdfSopenharmony_ci		} else if (memcmp(event_file_handle->f_handle,
239f08c3bdfSopenharmony_ci				  expected->fid->handle.f_handle,
240f08c3bdfSopenharmony_ci				  expected->fid->handle.handle_bytes) != 0) {
241f08c3bdfSopenharmony_ci			tst_res(TFAIL,
242f08c3bdfSopenharmony_ci				"file_handle returned in event does not match "
243f08c3bdfSopenharmony_ci				"the file_handle returned in "
244f08c3bdfSopenharmony_ci				"name_to_handle_at(2)");
245f08c3bdfSopenharmony_ci		} else if (memcmp(&event_fid->fsid, &expected->fid->fsid,
246f08c3bdfSopenharmony_ci				  sizeof(event_fid->fsid)) != 0) {
247f08c3bdfSopenharmony_ci			tst_res(TFAIL,
248f08c3bdfSopenharmony_ci				"event_fid->fsid != stats.f_fsid that was "
249f08c3bdfSopenharmony_ci				"obtained via statfs(2)");
250f08c3bdfSopenharmony_ci		} else {
251f08c3bdfSopenharmony_ci			tst_res(TPASS,
252f08c3bdfSopenharmony_ci				"Got event: mask=%llx, pid=%u, "
253f08c3bdfSopenharmony_ci				"fid=%x.%x.%lx values",
254f08c3bdfSopenharmony_ci				metadata->mask,
255f08c3bdfSopenharmony_ci				getpid(),
256f08c3bdfSopenharmony_ci				FSID_VAL_MEMBER(event_fid->fsid, 0),
257f08c3bdfSopenharmony_ci				FSID_VAL_MEMBER(event_fid->fsid, 1),
258f08c3bdfSopenharmony_ci				*(unsigned long *)
259f08c3bdfSopenharmony_ci				event_file_handle->f_handle);
260f08c3bdfSopenharmony_ci		}
261f08c3bdfSopenharmony_ci		metadata->mask  &= ~expected->mask;
262f08c3bdfSopenharmony_ci		/* No events left in current mask? Go for next event */
263f08c3bdfSopenharmony_ci		if (metadata->mask == 0)
264f08c3bdfSopenharmony_ci			metadata = FAN_EVENT_NEXT(metadata, len);
265f08c3bdfSopenharmony_ci	}
266f08c3bdfSopenharmony_ci
267f08c3bdfSopenharmony_ci	for (; i < count; i++)
268f08c3bdfSopenharmony_ci		tst_res(TFAIL,
269f08c3bdfSopenharmony_ci			"Didn't receive event: mask=%llx",
270f08c3bdfSopenharmony_ci			event_set[i].mask);
271f08c3bdfSopenharmony_ci}
272f08c3bdfSopenharmony_ci
273f08c3bdfSopenharmony_cistatic void do_setup(void)
274f08c3bdfSopenharmony_ci{
275f08c3bdfSopenharmony_ci	SAFE_MKDIR(TEST_DIR, 0755);
276f08c3bdfSopenharmony_ci	REQUIRE_FANOTIFY_INIT_FLAGS_SUPPORTED_ON_FS(FAN_REPORT_FID, TEST_DIR);
277f08c3bdfSopenharmony_ci	fanotify_fd = SAFE_FANOTIFY_INIT(FAN_REPORT_FID, O_RDONLY);
278f08c3bdfSopenharmony_ci}
279f08c3bdfSopenharmony_ci
280f08c3bdfSopenharmony_cistatic void do_cleanup(void)
281f08c3bdfSopenharmony_ci{
282f08c3bdfSopenharmony_ci	if (fanotify_fd > 0)
283f08c3bdfSopenharmony_ci		SAFE_CLOSE(fanotify_fd);
284f08c3bdfSopenharmony_ci}
285f08c3bdfSopenharmony_ci
286f08c3bdfSopenharmony_cistatic struct tst_test test = {
287f08c3bdfSopenharmony_ci	.needs_root = 1,
288f08c3bdfSopenharmony_ci	.mount_device = 1,
289f08c3bdfSopenharmony_ci	.mntpoint = MOUNT_POINT,
290f08c3bdfSopenharmony_ci	.all_filesystems = 1,
291f08c3bdfSopenharmony_ci	.test = do_test,
292f08c3bdfSopenharmony_ci	.tcnt = ARRAY_SIZE(test_cases),
293f08c3bdfSopenharmony_ci	.setup = do_setup,
294f08c3bdfSopenharmony_ci	.cleanup = do_cleanup,
295f08c3bdfSopenharmony_ci	.tags = (const struct tst_tag[]) {
296f08c3bdfSopenharmony_ci		{"linux-git", "f367a62a7cad"},
297f08c3bdfSopenharmony_ci		{}
298f08c3bdfSopenharmony_ci	}
299f08c3bdfSopenharmony_ci};
300f08c3bdfSopenharmony_ci
301f08c3bdfSopenharmony_ci#else
302f08c3bdfSopenharmony_ci	TST_TEST_TCONF("System does not have required name_to_handle_at() support");
303f08c3bdfSopenharmony_ci#endif
304f08c3bdfSopenharmony_ci#else
305f08c3bdfSopenharmony_ci	TST_TEST_TCONF("System does not have required fanotify support");
306f08c3bdfSopenharmony_ci#endif
307