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