1 /*
2 * Copyright (c) 2007 SWSoft. All Rights Reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of version 2 of the GNU General Public License as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it would be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 *
12 * Further, this software is distributed without any warranty that it is
13 * free of the rightful claim of any third person regarding infringement
14 * or the like. Any license provided herein, whether implied or
15 * otherwise, applies only to this software file. Patent licenses, if
16 * any, provided herein do not apply to combinations of this program with
17 * other software, or any other product whatsoever.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 *
23 * Started by Andrew Vagin <avagin@sw.ru>
24 *
25 * DESCRIPTION
26 * Check that inotify work for a directory
27 *
28 * ALGORITHM
29 * Execute sequence file's operation and check return events
30 */
31
32 #include "config.h"
33
34 #include <stdio.h>
35 #include <sys/stat.h>
36 #include <sys/types.h>
37 #include <sys/fcntl.h>
38 #include <errno.h>
39 #include <string.h>
40 #include <sys/syscall.h>
41 #include "test.h"
42 #include "linux_syscall_numbers.h"
43 #include "inotify.h"
44
45 #if defined(HAVE_SYS_INOTIFY_H)
46 #include <sys/inotify.h>
47
48 #ifndef IN_MOVE_SELF
49 #define IN_MOVE_SELF 0x00000800
50 #endif
51
52 #define EVENT_MAX 1024
53 /* size of the event structure, not counting name */
54 #define EVENT_SIZE (sizeof (struct inotify_event))
55 /* reasonable guess as to size of 1024 events */
56 #define EVENT_BUF_LEN (EVENT_MAX * (EVENT_SIZE + 16))
57
58 static void setup(void);
59 static void cleanup(void);
60
61 char *TCID = "inotify02";
62 int TST_TOTAL = 9;
63
64 #define BUF_SIZE 256
65 static char fname1[BUF_SIZE], fname2[BUF_SIZE], fname3[BUF_SIZE];
66 static int fd, fd_notify, reap_wd;
67 static int wd;
68
69 struct event_t {
70 char name[BUF_SIZE];
71 unsigned int mask;
72 };
73 #define FILE_NAME1 "test_file1"
74 #define FILE_NAME2 "test_file2"
75
76 static struct event_t event_set[EVENT_MAX];
77
78 static char event_buf[EVENT_BUF_LEN];
79
main(int ac,char ** av)80 int main(int ac, char **av)
81 {
82 int lc;
83 unsigned int stored_cookie = UINT_MAX;
84
85 tst_parse_opts(ac, av, NULL, NULL);
86
87 setup();
88
89 for (lc = 0; TEST_LOOPING(lc); lc++) {
90
91 tst_count = 0;
92
93 /*
94 * generate sequence of events
95 */
96 if (chmod(".", 0755) < 0) {
97 tst_brkm(TBROK | TERRNO, cleanup,
98 "chmod(\".\", 0755) failed");
99 }
100 event_set[tst_count].mask = IN_ISDIR | IN_ATTRIB;
101 strcpy(event_set[tst_count].name, "");
102 tst_count++;
103
104 if ((fd = creat(FILE_NAME1, 0755)) == -1) {
105 tst_brkm(TBROK | TERRNO, cleanup,
106 "creat(\"%s\", 755) failed", FILE_NAME1);
107 }
108
109 event_set[tst_count].mask = IN_CREATE;
110 strcpy(event_set[tst_count].name, FILE_NAME1);
111 tst_count++;
112 event_set[tst_count].mask = IN_OPEN;
113 strcpy(event_set[tst_count].name, FILE_NAME1);
114 tst_count++;
115
116 if (close(fd) == -1) {
117 tst_brkm(TBROK | TERRNO, cleanup,
118 "close(%s) failed", FILE_NAME1);
119 }
120 event_set[tst_count].mask = IN_CLOSE_WRITE;
121 strcpy(event_set[tst_count].name, FILE_NAME1);
122 tst_count++;
123
124 if (rename(FILE_NAME1, FILE_NAME2) == -1) {
125 tst_brkm(TBROK | TERRNO, cleanup,
126 "rename(%s, %s) failed",
127 FILE_NAME1, FILE_NAME2);
128 }
129 event_set[tst_count].mask = IN_MOVED_FROM;
130 strcpy(event_set[tst_count].name, FILE_NAME1);
131 tst_count++;
132 event_set[tst_count].mask = IN_MOVED_TO;
133 strcpy(event_set[tst_count].name, FILE_NAME2);
134 tst_count++;
135
136 if (getcwd(fname1, BUF_SIZE) == NULL) {
137 tst_brkm(TBROK | TERRNO, cleanup,
138 "getcwd(%p, %d) failed", fname1, BUF_SIZE);
139 }
140
141 snprintf(fname2, BUF_SIZE, "%s.rename1", fname1);
142 if (rename(fname1, fname2) == -1) {
143 tst_brkm(TBROK | TERRNO, cleanup,
144 "rename(%s, %s) failed", fname1, fname2);
145 }
146 event_set[tst_count].mask = IN_MOVE_SELF;
147 strcpy(event_set[tst_count].name, "");
148 tst_count++;
149
150 if (unlink(FILE_NAME2) == -1) {
151 tst_brkm(TBROK | TERRNO, cleanup,
152 "unlink(%s) failed", FILE_NAME2);
153 }
154 event_set[tst_count].mask = IN_DELETE;
155 strcpy(event_set[tst_count].name, FILE_NAME2);
156 tst_count++;
157
158 /*
159 * test that duplicate events will be coalesced into
160 * a single event. This test case should be last, that
161 * we can correct determine kernel bug which exist before
162 * 2.6.25. See comment below.
163 */
164 snprintf(fname3, BUF_SIZE, "%s.rename2", fname1);
165 if (rename(fname2, fname3) == -1) {
166 tst_brkm(TBROK | TERRNO, cleanup,
167 "rename(%s, %s) failed", fname2, fname3);
168 }
169
170 if (rename(fname3, fname1) == -1) {
171 tst_brkm(TBROK | TERRNO, cleanup,
172 "rename(%s, %s) failed", fname3, fname1);
173 }
174 event_set[tst_count].mask = IN_MOVE_SELF;
175 strcpy(event_set[tst_count].name, "");
176 tst_count++;
177
178 if (tst_count != TST_TOTAL) {
179 tst_brkm(TBROK, cleanup,
180 "tst_count and TST_TOTAL are not equal");
181 }
182
183 tst_count = 0;
184
185 int len, i = 0, test_num = 0;
186 if ((len = read(fd_notify, event_buf, EVENT_BUF_LEN)) == -1) {
187 tst_brkm(TBROK | TERRNO, cleanup,
188 "read(%d, buf, %zu) failed",
189 fd_notify, EVENT_BUF_LEN);
190
191 }
192
193 while (i < len) {
194 struct inotify_event *event;
195 event = (struct inotify_event *)&event_buf[i];
196 if (test_num >= TST_TOTAL) {
197 if (tst_kvercmp(2, 6, 25) < 0
198 && event_set[TST_TOTAL - 1].mask ==
199 event->mask)
200 tst_resm(TWARN,
201 "This may be kernel bug. "
202 "Before kernel 2.6.25, a kernel bug "
203 "meant that the kernel code that was "
204 "intended to coalesce successive identical "
205 "events (i.e., the two most recent "
206 "events could potentially be coalesced "
207 "if the older had not yet been read) "
208 "instead checked if the most recent event "
209 "could be coalesced with the oldest "
210 "unread event. This has been fixed by commit"
211 "1c17d18e3775485bf1e0ce79575eb637a94494a2.");
212 tst_resm(TFAIL,
213 "get unnecessary event: "
214 "wd=%d mask=%x cookie=%u len=%u"
215 "name=\"%.*s\"", event->wd, event->mask,
216 event->cookie, event->len, event->len,
217 event->name);
218
219 } else if ((event_set[test_num].mask == event->mask)
220 &&
221 (!strncmp
222 (event_set[test_num].name, event->name,
223 event->len))) {
224 int fail = 0;
225
226 if (event->mask == IN_MOVED_FROM) {
227 if (event->cookie == 0)
228 fail = 1;
229 else
230 stored_cookie = event->cookie;
231 } else if (event->mask == IN_MOVED_TO) {
232 if (event->cookie != stored_cookie)
233 fail = 1;
234 else
235 stored_cookie = UINT_MAX;
236 } else {
237 if (event->cookie != 0)
238 fail = 1;
239 }
240 if (!fail) {
241 tst_resm(TPASS,
242 "get event: wd=%d mask=%x "
243 "cookie=%u len=%u name=\"%.*s\"",
244 event->wd, event->mask,
245 event->cookie, event->len,
246 event->len, event->name);
247 } else {
248 tst_resm(TFAIL,
249 "get event: wd=%d mask=%x "
250 "cookie=%u (wrong) len=%u "
251 "name=\"%s\"",
252 event->wd, event->mask,
253 event->cookie, event->len,
254 event->name);
255 }
256 } else {
257 tst_resm(TFAIL, "get event: wd=%d mask=%x "
258 "(expected %x) cookie=%u len=%u "
259 "name=\"%s\" (expected \"%s\") %d",
260 event->wd, event->mask,
261 event_set[test_num].mask,
262 event->cookie, event->len, event->name,
263 event_set[test_num].name,
264 strcmp(event_set[test_num].name,
265 event->name));
266 }
267 test_num++;
268 i += EVENT_SIZE + event->len;
269 }
270
271 for (; test_num < TST_TOTAL; test_num++) {
272 tst_resm(TFAIL, "didn't get event: mask=%x ",
273 event_set[test_num].mask);
274 }
275 }
276
277 cleanup();
278 tst_exit();
279 }
280
setup(void)281 static void setup(void)
282 {
283
284 tst_sig(NOFORK, DEF_HANDLER, cleanup);
285
286 TEST_PAUSE;
287
288 tst_tmpdir();
289
290 if ((fd_notify = myinotify_init()) < 0) {
291 if (errno == ENOSYS) {
292 tst_brkm(TCONF, cleanup,
293 "inotify is not configured in this kernel.");
294 } else {
295 tst_brkm(TBROK | TERRNO, cleanup,
296 "inotify_init () failed");
297 }
298 }
299
300 if ((wd = myinotify_add_watch(fd_notify, ".", IN_ALL_EVENTS)) < 0) {
301 tst_brkm(TBROK | TERRNO, cleanup,
302 "inotify_add_watch (%d, \".\", IN_ALL_EVENTS) failed",
303 fd_notify);
304 reap_wd = 1;
305 };
306
307 }
308
cleanup(void)309 static void cleanup(void)
310 {
311 if (reap_wd && myinotify_rm_watch(fd_notify, wd) < 0) {
312 tst_resm(TWARN,
313 "inotify_rm_watch (%d, %d) failed,", fd_notify, wd);
314
315 }
316
317 if (fd_notify > 0 && close(fd_notify))
318 tst_resm(TWARN, "close(%d) failed", fd_notify);
319
320 tst_rmdir();
321 }
322
323 #else
324
325 char *TCID = "inotify02";
326 int TST_TOTAL = 0;
327
main(void)328 int main(void)
329 {
330 tst_brkm(TCONF, NULL, "system doesn't have required inotify support");
331 }
332
333 #endif
334