• 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  * Ngie 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 "tst_test.h"
48 #include "inotify.h"
49 
50 #if defined(HAVE_SYS_INOTIFY_H)
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 
59 #define BUF_SIZE 256
60 
61 struct event_t {
62 	char name[BUF_SIZE];
63 	unsigned int mask;
64 };
65 
66 #define	TEST_DIR	"test_dir"
67 #define	TEST_FILE	"test_file"
68 
69 struct event_t event_set[EVENT_MAX];
70 
71 char event_buf[EVENT_BUF_LEN];
72 
73 int fd_notify, reap_wd_file, reap_wd_dir, wd_dir, wd_file;
74 
75 static struct tst_kern_exv kvers[] = {
76 	{ "RHEL5", "2.6.18-132" },
77 	{ NULL, NULL },
78 };
79 
cleanup(void)80 static void cleanup(void)
81 {
82 	if (reap_wd_dir && myinotify_rm_watch(fd_notify, wd_dir) == -1)
83 		tst_res(TWARN,
84 			"inotify_rm_watch(%d, %d) [1] failed", fd_notify,
85 			wd_dir);
86 
87 	if (reap_wd_file && myinotify_rm_watch(fd_notify, wd_file) == -1)
88 		tst_res(TWARN,
89 			"inotify_rm_watch(%d, %d) [2] failed", fd_notify,
90 			wd_file);
91 
92 	if (fd_notify > 0)
93 		SAFE_CLOSE(fd_notify);
94 }
95 
setup(void)96 static void setup(void)
97 {
98 	fd_notify = myinotify_init();
99 	if (fd_notify == -1) {
100 		if (errno == ENOSYS) {
101 			tst_brk(TCONF,
102 				"inotify is not configured in this kernel.");
103 		} else {
104 			tst_brk(TBROK | TERRNO,
105 				"inotify_init failed");
106 		}
107 	}
108 }
109 
verify_inotify(void)110 void verify_inotify(void)
111 {
112 	int i = 0, test_num = 0, len;
113 	int test_cnt = 0;
114 
115 	SAFE_MKDIR(TEST_DIR, 00700);
116 	close(SAFE_CREAT(TEST_FILE, 00600));
117 
118 	wd_dir = myinotify_add_watch(fd_notify, TEST_DIR, IN_ALL_EVENTS);
119 	if (wd_dir == -1) {
120 		tst_brk(TBROK | TERRNO,
121 			"inotify_add_watch(%d, \"%s\", IN_ALL_EVENTS) [1] failed",
122 			fd_notify, TEST_DIR);
123 	}
124 	reap_wd_dir = 1;
125 
126 	wd_file = myinotify_add_watch(fd_notify, TEST_FILE, IN_ALL_EVENTS);
127 	if (wd_file == -1)
128 		tst_brk(TBROK | TERRNO,
129 			"inotify_add_watch(%d, \"%s\", IN_ALL_EVENTS) [2] failed",
130 			fd_notify, TEST_FILE);
131 	reap_wd_file = 1;
132 
133 	SAFE_RMDIR(TEST_DIR);
134 	reap_wd_dir = 0;
135 
136 	event_set[test_cnt].mask = IN_DELETE_SELF;
137 	strcpy(event_set[test_cnt].name, "");
138 	test_cnt++;
139 	event_set[test_cnt].mask = IN_IGNORED;
140 	strcpy(event_set[test_cnt].name, "");
141 	test_cnt++;
142 
143 	SAFE_UNLINK(TEST_FILE);
144 	reap_wd_file = 0;
145 
146 	/*
147 	 * When a file is unlinked, the link count is reduced by 1, and when it
148 	 * hits 0 the file is removed.
149 	 *
150 	 * This isn't well documented in inotify(7), but it's intuitive if you
151 	 * understand how Unix works.
152 	 */
153 	if (tst_kvercmp2(2, 6, 25, kvers) >= 0) {
154 		event_set[test_cnt].mask = IN_ATTRIB;
155 		strcpy(event_set[test_cnt].name, "");
156 		test_cnt++;
157 	}
158 
159 	event_set[test_cnt].mask = IN_DELETE_SELF;
160 	strcpy(event_set[test_cnt].name, TEST_FILE);
161 	test_cnt++;
162 	event_set[test_cnt].mask = IN_IGNORED;
163 	strcpy(event_set[test_cnt].name, "");
164 	test_cnt++;
165 
166 	len = read(fd_notify, event_buf, EVENT_BUF_LEN);
167 	if (len == -1)
168 		tst_brk(TBROK | TERRNO, "read failed");
169 
170 	while (i < len) {
171 		struct inotify_event *event;
172 		event = (struct inotify_event *)&event_buf[i];
173 		if (test_num >= test_cnt) {
174 			if (tst_kvercmp(2, 6, 25) < 0
175 			    && event_set[test_cnt - 1].mask == event->mask)
176 				tst_res(TWARN,
177 					"This may be kernel bug. "
178 					"Before kernel 2.6.25, a kernel bug "
179 					"meant that the kernel code that was "
180 					"intended to coalesce successive identical "
181 					"events (i.e., the two most recent "
182 					"events could potentially be coalesced "
183 					"if the older had not yet been read) "
184 					"instead checked if the most recent event "
185 					"could be coalesced with the oldest "
186 					"unread event. This has been fixed by commit"
187 					"1c17d18e3775485bf1e0ce79575eb637a94494a2.");
188 			tst_res(TFAIL,
189 				"got unnecessary event: "
190 				"wd=%d mask=%04x cookie=%u len=%u "
191 				"name=\"%.*s\"", event->wd, event->mask,
192 				event->cookie, event->len, event->len, event->name);
193 
194 		} else if ((event_set[test_num].mask == event->mask)
195 			   &&
196 			   (!strncmp
197 			    (event_set[test_num].name, event->name,
198 			     event->len))) {
199 			tst_res(TPASS,
200 				"got event: wd=%d mask=%04x "
201 				"cookie=%u len=%u name=\"%.*s\"",
202 				event->wd, event->mask, event->cookie,
203 				event->len, event->len, event->name);
204 
205 		} else {
206 			tst_res(TFAIL, "got event: wd=%d mask=%04x "
207 				"(expected %x) cookie=%u len=%u "
208 				"name=\"%.*s\" (expected \"%s\") %d",
209 				event->wd, event->mask,
210 				event_set[test_num].mask,
211 				event->cookie, event->len,
212 				event->len, event->name,
213 				event_set[test_num].name,
214 				strncmp(event_set[test_num].name, event->name, event->len));
215 		}
216 		test_num++;
217 		i += EVENT_SIZE + event->len;
218 	}
219 
220 	for (; test_num < test_cnt; test_num++) {
221 		tst_res(TFAIL, "didn't get event: mask=%04x ",
222 			event_set[test_num].mask);
223 	}
224 
225 }
226 
227 static struct tst_test test = {
228 	.needs_tmpdir = 1,
229 	.setup = setup,
230 	.cleanup = cleanup,
231 	.test_all = verify_inotify,
232 };
233 
234 #else
235 	TST_TEST_TCONF("system doesn't have required inotify support");
236 #endif /* defined(HAVE_SYS_INOTIFY_H) */
237