1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2007 SWSoft. All Rights Reserved.
4 * Author: Andrew Vagin <avagin@sw.ru>
5 *
6 * DESCRIPTION
7 * Check that inotify work for a directory
8 *
9 * ALGORITHM
10 * Execute sequence file's operation and check return events
11 */
12
13 #include "config.h"
14
15 #include <stdio.h>
16 #include <sys/stat.h>
17 #include <sys/types.h>
18 #include <fcntl.h>
19 #include <errno.h>
20 #include <string.h>
21 #include <sys/syscall.h>
22 #include <limits.h>
23 #include "tst_test.h"
24 #include "inotify.h"
25
26 #if defined(HAVE_SYS_INOTIFY_H)
27 #include <sys/inotify.h>
28
29 #ifndef IN_MOVE_SELF
30 #define IN_MOVE_SELF 0x00000800
31 #endif
32
33 #define EVENT_MAX 1024
34 /* size of the event structure, not counting name */
35 #define EVENT_SIZE (sizeof (struct inotify_event))
36 /* reasonable guess as to size of 1024 events */
37 #define EVENT_BUF_LEN (EVENT_MAX * (EVENT_SIZE + 16))
38
39 #define BUF_SIZE 256
40 static char fname1[BUF_SIZE], fname2[BUF_SIZE], fname3[BUF_SIZE];
41 static int fd, fd_notify, reap_wd;
42 static int wd;
43
44 struct event_t {
45 char name[BUF_SIZE];
46 unsigned int mask;
47 };
48 #define FILE_NAME1 "test_file1"
49 #define FILE_NAME2 "test_file2"
50
51 static struct event_t event_set[EVENT_MAX];
52
53 static char event_buf[EVENT_BUF_LEN];
54
verify_inotify(void)55 void verify_inotify(void)
56 {
57 unsigned int stored_cookie = UINT_MAX;
58
59 int test_cnt = 0;
60
61 /*
62 * generate sequence of events
63 */
64 SAFE_CHMOD(".", 0755);
65 event_set[test_cnt].mask = IN_ISDIR | IN_ATTRIB;
66 strcpy(event_set[test_cnt].name, "");
67 test_cnt++;
68
69 if ((fd = creat(FILE_NAME1, 0755)) == -1) {
70 tst_brk(TBROK | TERRNO,
71 "creat(\"%s\", 755) failed", FILE_NAME1);
72 }
73
74 event_set[test_cnt].mask = IN_CREATE;
75 strcpy(event_set[test_cnt].name, FILE_NAME1);
76 test_cnt++;
77 event_set[test_cnt].mask = IN_OPEN;
78 strcpy(event_set[test_cnt].name, FILE_NAME1);
79 test_cnt++;
80
81 SAFE_CLOSE(fd);
82 event_set[test_cnt].mask = IN_CLOSE_WRITE;
83 strcpy(event_set[test_cnt].name, FILE_NAME1);
84 test_cnt++;
85
86 SAFE_RENAME(FILE_NAME1, FILE_NAME2);
87 event_set[test_cnt].mask = IN_MOVED_FROM;
88 strcpy(event_set[test_cnt].name, FILE_NAME1);
89 test_cnt++;
90 event_set[test_cnt].mask = IN_MOVED_TO;
91 strcpy(event_set[test_cnt].name, FILE_NAME2);
92 test_cnt++;
93
94 if (getcwd(fname1, BUF_SIZE) == NULL) {
95 tst_brk(TBROK | TERRNO,
96 "getcwd(%p, %d) failed", fname1, BUF_SIZE);
97 }
98
99 snprintf(fname2, BUF_SIZE, "%s.rename1", fname1);
100 SAFE_RENAME(fname1, fname2);
101 event_set[test_cnt].mask = IN_MOVE_SELF;
102 strcpy(event_set[test_cnt].name, "");
103 test_cnt++;
104
105 SAFE_UNLINK(FILE_NAME2);
106 event_set[test_cnt].mask = IN_DELETE;
107 strcpy(event_set[test_cnt].name, FILE_NAME2);
108 test_cnt++;
109
110 /*
111 * test that duplicate events will be coalesced into
112 * a single event. This test case should be last, that
113 * we can correct determine kernel bug which exist before
114 * 2.6.25. See comment below.
115 */
116 snprintf(fname3, BUF_SIZE, "%s.rename2", fname1);
117 SAFE_RENAME(fname2, fname3);
118
119 SAFE_RENAME(fname3, fname1);
120 event_set[test_cnt].mask = IN_MOVE_SELF;
121 strcpy(event_set[test_cnt].name, "");
122 test_cnt++;
123
124 int len, i = 0, test_num = 0;
125 if ((len = read(fd_notify, event_buf, EVENT_BUF_LEN)) == -1) {
126 tst_brk(TBROK | TERRNO,
127 "read(%d, buf, %zu) failed",
128 fd_notify, EVENT_BUF_LEN);
129
130 }
131
132 while (i < len) {
133 struct inotify_event *event;
134 event = (struct inotify_event *)&event_buf[i];
135 if (test_num >= test_cnt) {
136 if (tst_kvercmp(2, 6, 25) < 0
137 && event_set[test_cnt - 1].mask ==
138 event->mask)
139 tst_res(TWARN,
140 "This may be kernel bug. "
141 "Before kernel 2.6.25, a kernel bug "
142 "meant that the kernel code that was "
143 "intended to coalesce successive identical "
144 "events (i.e., the two most recent "
145 "events could potentially be coalesced "
146 "if the older had not yet been read) "
147 "instead checked if the most recent event "
148 "could be coalesced with the oldest "
149 "unread event. This has been fixed by commit"
150 "1c17d18e3775485bf1e0ce79575eb637a94494a2.");
151 tst_res(TFAIL,
152 "get unnecessary event: "
153 "wd=%d mask=%08x cookie=%-5u len=%-2u "
154 "name=\"%.*s\"", event->wd, event->mask,
155 event->cookie, event->len, event->len,
156 event->name);
157
158 } else if ((event_set[test_num].mask == event->mask)
159 &&
160 (!strncmp
161 (event_set[test_num].name, event->name,
162 event->len))) {
163 int fail = 0;
164
165 if (event->mask == IN_MOVED_FROM) {
166 if (event->cookie == 0)
167 fail = 1;
168 else
169 stored_cookie = event->cookie;
170 } else if (event->mask == IN_MOVED_TO) {
171 if (event->cookie != stored_cookie)
172 fail = 1;
173 else
174 stored_cookie = UINT_MAX;
175 } else {
176 if (event->cookie != 0)
177 fail = 1;
178 }
179 if (!fail) {
180 tst_res(TPASS,
181 "get event: wd=%d mask=%08x "
182 "cookie=%-5u len=%-2u name=\"%.*s\"",
183 event->wd, event->mask,
184 event->cookie, event->len,
185 event->len, event->name);
186 } else {
187 tst_res(TFAIL,
188 "get event: wd=%d mask=%08x "
189 "cookie=%-5u (wrong) len=%-2u "
190 "name=\"%s\"",
191 event->wd, event->mask,
192 event->cookie, event->len,
193 event->name);
194 }
195 } else {
196 tst_res(TFAIL, "get event: wd=%d mask=%08x "
197 "(expected %x) cookie=%-5u len=%-2u "
198 "name=\"%s\" (expected \"%s\") %d",
199 event->wd, event->mask,
200 event_set[test_num].mask,
201 event->cookie, event->len, event->name,
202 event_set[test_num].name,
203 strcmp(event_set[test_num].name,
204 event->name));
205 }
206 test_num++;
207 i += EVENT_SIZE + event->len;
208 }
209
210 for (; test_num < test_cnt; test_num++) {
211 tst_res(TFAIL, "didn't get event: mask=%08x ",
212 event_set[test_num].mask);
213 }
214 }
215
setup(void)216 static void setup(void)
217 {
218 fd_notify = SAFE_MYINOTIFY_INIT();
219
220 wd = SAFE_MYINOTIFY_ADD_WATCH(fd_notify, ".", IN_ALL_EVENTS);
221 reap_wd = 1;
222 }
223
cleanup(void)224 static void cleanup(void)
225 {
226 if (reap_wd && myinotify_rm_watch(fd_notify, wd) < 0) {
227 tst_res(TWARN,
228 "inotify_rm_watch (%d, %d) failed,", fd_notify, wd);
229 }
230
231 if (fd_notify > 0)
232 SAFE_CLOSE(fd_notify);
233 }
234
235 static struct tst_test test = {
236 .needs_tmpdir = 1,
237 .setup = setup,
238 .cleanup = cleanup,
239 .test_all = verify_inotify,
240 };
241
242 #else
243 TST_TEST_TCONF("system doesn't have required inotify support");
244 #endif
245