• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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