1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2021 CTERA Networks. All Rights Reserved.
4 *
5 * Started by Amir Goldstein <amir73il@gmail.com>
6 */
7
8/*\
9 * [Description]
10 *
11 * Check that dnotify DN_RENAME event is reported only on rename inside same parent.
12 */
13
14#include <fcntl.h>
15#include <signal.h>
16#include <stdio.h>
17#include <unistd.h>
18#include "tst_test.h"
19#include "lapi/fcntl.h"
20
21#define	TEST_DIR	"test_dir"
22#define	TEST_DIR2	"test_dir2"
23#define	TEST_FILE	"test_file"
24
25#define TEST_SIG (SIGRTMIN+1)
26
27static int parent_fd, subdir_fd;
28static int got_parent_event, got_subdir_event;
29
30static void dnotify_handler(int sig, siginfo_t *si, void *data LTP_ATTRIBUTE_UNUSED)
31{
32	if (si->si_fd == parent_fd)
33		got_parent_event = 1;
34	else if (si->si_fd == subdir_fd)
35		got_subdir_event = 1;
36	else
37		tst_brk(TBROK, "Got unexpected signal %d with si_fd %d", sig, si->si_fd);
38}
39
40static void setup_dnotify(int fd)
41{
42	struct sigaction act;
43
44	act.sa_sigaction = dnotify_handler;
45	sigemptyset(&act.sa_mask);
46	act.sa_flags = SA_SIGINFO;
47	sigaction(TEST_SIG, &act, NULL);
48
49	TEST(fcntl(fd, F_SETSIG, TEST_SIG));
50	if (TST_RET != 0) {
51		tst_brk(TBROK, "F_SETSIG failed errno = %d : %s",
52			TST_ERR, strerror(TST_ERR));
53	}
54
55	TEST(fcntl(fd, F_NOTIFY, DN_RENAME|DN_MULTISHOT));
56	if (TST_RET != 0) {
57		tst_brk(TBROK, "F_NOTIFY failed errno = %d : %s",
58			TST_ERR, strerror(TST_ERR));
59	}
60}
61
62static void verify_dnotify(void)
63{
64	parent_fd = SAFE_OPEN(".", O_RDONLY);
65	subdir_fd = SAFE_OPEN(TEST_DIR, O_RDONLY);
66
67	/* Watch renames inside ".", but not in and out of "." */
68	setup_dnotify(parent_fd);
69
70	/* Also watch for renames inside subdir, but not in and out of subdir */
71	setup_dnotify(subdir_fd);
72
73	/* Rename file from "." to subdir should not generate DN_RENAME on either */
74	tst_res(TINFO, "Testing no DN_RENAME on rename from parent to subdir");
75	SAFE_RENAME(TEST_FILE, TEST_DIR "/" TEST_FILE);
76
77	if (got_parent_event)
78		tst_res(TFAIL, "Got unexpected event on parent");
79	else
80		tst_res(TPASS, "No event on parent as expected");
81
82	if (got_subdir_event)
83		tst_res(TFAIL, "Got unexpected event on subdir");
84	else
85		tst_res(TPASS, "No event on subdir as expected");
86
87	/* Rename subdir itself should generate DN_RENAME on ".", but not on itself */
88	tst_res(TINFO, "Testing DN_RENAME on rename of subdir itself");
89	SAFE_RENAME(TEST_DIR, TEST_DIR2);
90
91	if (got_parent_event)
92		tst_res(TPASS, "Got event on parent as expected");
93	else
94		tst_res(TFAIL, "Missing event on parent");
95
96	if (got_subdir_event)
97		tst_res(TFAIL, "Got unexpected event on subdir");
98	else
99		tst_res(TPASS, "No event on subdir as expected");
100
101	SAFE_CLOSE(parent_fd);
102	SAFE_CLOSE(subdir_fd);
103
104	/* Cleanup before rerun */
105	SAFE_RENAME(TEST_DIR2 "/" TEST_FILE, TEST_FILE);
106	SAFE_RENAME(TEST_DIR2, TEST_DIR);
107	got_parent_event = 0;
108	got_subdir_event = 0;
109}
110
111static void setup(void)
112{
113	SAFE_MKDIR(TEST_DIR, 00700);
114	SAFE_TOUCH(TEST_FILE, 0666, NULL);
115}
116
117static void cleanup(void)
118{
119	if (parent_fd > 0)
120		SAFE_CLOSE(parent_fd);
121
122	if (subdir_fd > 0)
123		SAFE_CLOSE(subdir_fd);
124}
125
126static struct tst_test test = {
127	.needs_tmpdir = 1,
128	.setup = setup,
129	.cleanup = cleanup,
130	.test_all = verify_dnotify,
131	.needs_kconfigs = (const char *[]) { "CONFIG_DNOTIFY=y", NULL },
132};
133