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
52char *TCID = "inotify04";
53int 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
66struct 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
74struct event_t event_set[EVENT_MAX];
75
76char event_buf[EVENT_BUF_LEN];
77
78int fd_notify, reap_wd_file, reap_wd_dir, wd_dir, wd_file;
79
80static struct tst_kern_exv kvers[] = {
81	{ "RHEL5", "2.6.18-132" },
82	{ NULL, NULL },
83};
84
85static 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
110static 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
150int 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
264int main(void)
265{
266	tst_brkm(TCONF, NULL, "system doesn't have required inotify support");
267}
268#endif /* defined(HAVE_SYS_INOTIFY_H) */
269