1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2007 SWSoft. All Rights Reserved.
4 * Author: Andrew Vagin <[email protected]>
5 */
6
7 /*\
8 * [Description]
9 *
10 * Basic test for inotify events on directory.
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 tst_res(TFAIL,
137 "get unnecessary event: "
138 "wd=%d mask=%08x cookie=%-5u len=%-2u "
139 "name=\"%.*s\"", event->wd, event->mask,
140 event->cookie, event->len, event->len,
141 event->name);
142
143 } else if ((event_set[test_num].mask == event->mask)
144 &&
145 (!strncmp
146 (event_set[test_num].name, event->name,
147 event->len))) {
148 int fail = 0;
149
150 if (event->mask == IN_MOVED_FROM) {
151 if (event->cookie == 0)
152 fail = 1;
153 else
154 stored_cookie = event->cookie;
155 } else if (event->mask == IN_MOVED_TO) {
156 if (event->cookie != stored_cookie)
157 fail = 1;
158 else
159 stored_cookie = UINT_MAX;
160 } else {
161 if (event->cookie != 0)
162 fail = 1;
163 }
164 if (!fail) {
165 tst_res(TPASS,
166 "get event: wd=%d mask=%08x "
167 "cookie=%-5u len=%-2u name=\"%.*s\"",
168 event->wd, event->mask,
169 event->cookie, event->len,
170 event->len, event->name);
171 } else {
172 tst_res(TFAIL,
173 "get event: wd=%d mask=%08x "
174 "cookie=%-5u (wrong) len=%-2u "
175 "name=\"%s\"",
176 event->wd, event->mask,
177 event->cookie, event->len,
178 event->name);
179 }
180 } else {
181 tst_res(TFAIL, "get event: wd=%d mask=%08x "
182 "(expected %x) cookie=%-5u len=%-2u "
183 "name=\"%s\" (expected \"%s\") %d",
184 event->wd, event->mask,
185 event_set[test_num].mask,
186 event->cookie, event->len, event->name,
187 event_set[test_num].name,
188 strcmp(event_set[test_num].name,
189 event->name));
190 }
191 test_num++;
192 i += EVENT_SIZE + event->len;
193 }
194
195 for (; test_num < test_cnt; test_num++) {
196 tst_res(TFAIL, "didn't get event: mask=%08x ",
197 event_set[test_num].mask);
198 }
199 }
200
setup(void)201 static void setup(void)
202 {
203 fd_notify = SAFE_MYINOTIFY_INIT();
204
205 wd = SAFE_MYINOTIFY_ADD_WATCH(fd_notify, ".", IN_ALL_EVENTS);
206 reap_wd = 1;
207 }
208
cleanup(void)209 static void cleanup(void)
210 {
211 if (reap_wd && myinotify_rm_watch(fd_notify, wd) < 0) {
212 tst_res(TWARN,
213 "inotify_rm_watch (%d, %d) failed,", fd_notify, wd);
214 }
215
216 if (fd_notify > 0)
217 SAFE_CLOSE(fd_notify);
218 }
219
220 static struct tst_test test = {
221 .needs_tmpdir = 1,
222 .setup = setup,
223 .cleanup = cleanup,
224 .test_all = verify_inotify,
225 };
226
227 #else
228 TST_TEST_TCONF("system doesn't have required inotify support");
229 #endif
230