1 /*
2 * Copyright (c) 2012 Linux Test Project. 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 * Garrett Cooper, April 2012
24 */
25
26 /****************************************************************************
27 * DESCRIPTION
28 * verify that IN_DELETE_SELF functions as expected
29 *
30 * ALGORITHM
31 * This testcase creates a temporary directory, then add watches to a
32 * predefined file and subdirectory, and delete the file and directory to
33 * ensure that the IN_DELETE_SELF event is captured properly.
34 *
35 * Because of how the inotify(7) API is designed, we also need to catch the
36 * IN_ATTRIB and IN_IGNORED events.
37 *
38 ****************************************************************************/
39
40 #include "config.h"
41
42 #if defined(HAVE_SYS_INOTIFY_H)
43 #include <sys/inotify.h>
44 #endif
45 #include <errno.h>
46 #include <string.h>
47 #include "test.h"
48 #include "linux_syscall_numbers.h"
49 #include "inotify.h"
50 #include "safe_macros.h"
51
52 char *TCID = "inotify04";
53 int TST_TOTAL = 4;
54
55 #if defined(HAVE_SYS_INOTIFY_H)
56
57 #define EVENT_MAX 1024
58 /* size of the event structure, not counting name */
59 #define EVENT_SIZE (sizeof(struct inotify_event))
60 /* reasonable guess as to size of 1024 events */
61 #define EVENT_BUF_LEN (EVENT_MAX * (EVENT_SIZE + 16))
62
63
64 #define BUF_SIZE 256
65
66 struct event_t {
67 char name[BUF_SIZE];
68 unsigned int mask;
69 };
70
71 #define TEST_DIR "test_dir"
72 #define TEST_FILE "test_file"
73
74 struct event_t event_set[EVENT_MAX];
75
76 char event_buf[EVENT_BUF_LEN];
77
78 int fd_notify, reap_wd_file, reap_wd_dir, wd_dir, wd_file;
79
80 static struct tst_kern_exv kvers[] = {
81 { "RHEL5", "2.6.18-132" },
82 { NULL, NULL },
83 };
84
cleanup(void)85 static void cleanup(void)
86 {
87
88 if (reap_wd_dir && myinotify_rm_watch(fd_notify, wd_dir) == -1)
89 tst_resm(TWARN,
90 "inotify_rm_watch(%d, %d) [1] failed", fd_notify,
91 wd_dir);
92
93 if (reap_wd_file && myinotify_rm_watch(fd_notify, wd_file) == -1)
94 tst_resm(TWARN,
95 "inotify_rm_watch(%d, %d) [2] failed", fd_notify,
96 wd_file);
97
98 if (fd_notify > 0 && close(fd_notify))
99 tst_resm(TWARN, "close(%d) [1] failed", fd_notify);
100
101 if (wd_dir > 0 && close(wd_dir))
102 tst_resm(TWARN, "close(%d) [2] failed", wd_dir);
103
104 if (wd_file > 0 && close(wd_file))
105 tst_resm(TWARN, "close(%d) [3] failed", wd_file);
106
107 tst_rmdir();
108 }
109
setup(void)110 static void setup(void)
111 {
112
113 tst_sig(NOFORK, DEF_HANDLER, cleanup);
114
115 TEST_PAUSE;
116
117 tst_tmpdir();
118
119 fd_notify = myinotify_init();
120 if (fd_notify == -1) {
121 if (errno == ENOSYS) {
122 tst_brkm(TCONF, cleanup,
123 "inotify is not configured in this kernel.");
124 } else {
125 tst_brkm(TBROK | TERRNO, cleanup,
126 "inotify_init failed");
127 }
128 }
129
130 SAFE_MKDIR(cleanup, TEST_DIR, 00700);
131
132 close(SAFE_CREAT(cleanup, TEST_FILE, 00600));
133
134 wd_dir = myinotify_add_watch(fd_notify, TEST_DIR, IN_ALL_EVENTS);
135 if (wd_dir == -1) {
136 tst_brkm(TBROK | TERRNO, cleanup,
137 "inotify_add_watch(%d, \"%s\", IN_ALL_EVENTS) [1] failed",
138 fd_notify, TEST_DIR);
139 }
140 reap_wd_dir = 1;
141
142 wd_file = myinotify_add_watch(fd_notify, TEST_FILE, IN_ALL_EVENTS);
143 if (wd_file == -1)
144 tst_brkm(TBROK | TERRNO, cleanup,
145 "inotify_add_watch(%d, \"%s\", IN_ALL_EVENTS) [2] failed",
146 fd_notify, TEST_FILE);
147 reap_wd_file = 1;
148 }
149
main(int argc,char ** argv)150 int main(int argc, char **argv)
151 {
152 int i, test_num, len;
153
154 i = 0;
155 test_num = 0;
156
157 tst_parse_opts(argc, argv, NULL, NULL);
158
159 setup();
160
161 tst_count = 0;
162
163 rmdir(TEST_DIR);
164 event_set[tst_count].mask = IN_DELETE_SELF;
165 strcpy(event_set[tst_count].name, "");
166 tst_count++;
167 event_set[tst_count].mask = IN_IGNORED;
168 strcpy(event_set[tst_count].name, "");
169 tst_count++;
170
171 unlink(TEST_FILE);
172 /*
173 * When a file is unlinked, the link count is reduced by 1, and when it
174 * hits 0 the file is removed.
175 *
176 * This isn't well documented in inotify(7), but it's intuitive if you
177 * understand how Unix works.
178 */
179 if (tst_kvercmp2(2, 6, 25, kvers) >= 0) {
180 event_set[tst_count].mask = IN_ATTRIB;
181 strcpy(event_set[tst_count].name, "");
182 tst_count++;
183 TST_TOTAL++;
184 }
185 event_set[tst_count].mask = IN_DELETE_SELF;
186 strcpy(event_set[tst_count].name, TEST_FILE);
187 tst_count++;
188 event_set[tst_count].mask = IN_IGNORED;
189 strcpy(event_set[tst_count].name, "");
190 tst_count++;
191
192 if (tst_count != TST_TOTAL)
193 tst_brkm(TBROK, cleanup,
194 "tst_count and TST_TOTAL are not equal");
195
196 tst_count = 0;
197
198 len = read(fd_notify, event_buf, EVENT_BUF_LEN);
199 if (len == -1)
200 tst_brkm(TBROK | TERRNO, cleanup, "read failed");
201
202 reap_wd_dir = 0;
203 reap_wd_file = 0;
204
205 while (i < len) {
206 struct inotify_event *event;
207 event = (struct inotify_event *)&event_buf[i];
208 if (test_num >= TST_TOTAL) {
209 if (tst_kvercmp(2, 6, 25) < 0
210 && event_set[TST_TOTAL - 1].mask == event->mask)
211 tst_resm(TWARN,
212 "This may be kernel bug. "
213 "Before kernel 2.6.25, a kernel bug "
214 "meant that the kernel code that was "
215 "intended to coalesce successive identical "
216 "events (i.e., the two most recent "
217 "events could potentially be coalesced "
218 "if the older had not yet been read) "
219 "instead checked if the most recent event "
220 "could be coalesced with the oldest "
221 "unread event. This has been fixed by commit"
222 "1c17d18e3775485bf1e0ce79575eb637a94494a2.");
223 tst_resm(TFAIL,
224 "got unnecessary event: "
225 "wd=%d mask=%x cookie=%u len=%u "
226 "name=\"%.*s\"", event->wd, event->mask,
227 event->cookie, event->len, event->len, event->name);
228
229 } else if ((event_set[test_num].mask == event->mask)
230 &&
231 (!strncmp
232 (event_set[test_num].name, event->name,
233 event->len))) {
234 tst_resm(TPASS,
235 "got event: wd=%d mask=%x "
236 "cookie=%u len=%u name=\"%.*s\"",
237 event->wd, event->mask, event->cookie,
238 event->len, event->len, event->name);
239
240 } else {
241 tst_resm(TFAIL, "got event: wd=%d mask=%x "
242 "(expected %x) cookie=%u len=%u "
243 "name=\"%.*s\" (expected \"%s\") %d",
244 event->wd, event->mask,
245 event_set[test_num].mask,
246 event->cookie, event->len,
247 event->len, event->name,
248 event_set[test_num].name,
249 strncmp(event_set[test_num].name, event->name, event->len));
250 }
251 test_num++;
252 i += EVENT_SIZE + event->len;
253 }
254
255 for (; test_num < TST_TOTAL; test_num++) {
256 tst_resm(TFAIL, "didn't get event: mask=%x ",
257 event_set[test_num].mask);
258 }
259
260 cleanup();
261 tst_exit();
262 }
263 #else
main(void)264 int main(void)
265 {
266 tst_brkm(TCONF, NULL, "system doesn't have required inotify support");
267 }
268 #endif /* defined(HAVE_SYS_INOTIFY_H) */
269