inotify04.c revision fed9641096e27f79a0f2d9adfe9839dd8d11dc0f
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 "usctest.h"
49#include "linux_syscall_numbers.h"
50#include "inotify.h"
51#include "safe_macros.h"
52
53char *TCID = "inotify04";
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
63int TST_TOTAL = 4;
64
65#define BUF_SIZE 256
66
67struct event_t {
68	char name[BUF_SIZE];
69	int mask;
70	size_t len;
71};
72
73#define	TEST_DIR	"test_dir"
74#define	TEST_FILE	"test_file"
75
76struct event_t event_set[EVENT_MAX];
77
78char event_buf[EVENT_BUF_LEN];
79
80int fd_notify, reap_wd_file, reap_wd_dir, wd_dir, wd_file;
81
82static void cleanup(void)
83{
84
85	if (reap_wd_dir && myinotify_rm_watch(fd_notify, wd_dir) == -1)
86		tst_resm(TWARN,
87		    "inotify_rm_watch(%d, %d) [1] failed", fd_notify, wd_dir);
88
89	if (reap_wd_file && myinotify_rm_watch(fd_notify, wd_file) == -1)
90		tst_resm(TWARN,
91		    "inotify_rm_watch(%d, %d) [2] failed", fd_notify, wd_file);
92
93	if (close(fd_notify) == -1)
94		tst_resm(TWARN, "close(%d) [1] failed", fd_notify);
95
96	if (close(wd_dir) == -1)
97		tst_resm(TWARN, "close(%d) [2] failed", wd_dir);
98
99	if (close(wd_file) == -1)
100		tst_resm(TWARN, "close(%d) [3] failed", wd_file);
101
102	TEST_CLEANUP;
103
104	tst_rmdir();
105}
106
107static void setup(void)
108{
109
110	tst_sig(NOFORK, DEF_HANDLER, cleanup);
111
112	TEST_PAUSE;
113
114	tst_tmpdir();
115
116	fd_notify = myinotify_init();
117	if (fd_notify == -1)
118		tst_brkm(TBROK|TERRNO, cleanup, "inotify_init() failed");
119
120	SAFE_MKDIR(cleanup, TEST_DIR, 00700);
121
122	close(SAFE_CREAT(cleanup, TEST_FILE, 00600));
123
124	wd_dir = myinotify_add_watch(fd_notify, TEST_DIR, IN_ALL_EVENTS);
125	if (wd_dir == -1) {
126		tst_brkm(TBROK|TERRNO, cleanup,
127		    "inotify_add_watch(%d, \"%s\", IN_ALL_EVENTS) [1] failed",
128		    fd_notify, TEST_DIR);
129	}
130	reap_wd_dir = 1;
131
132	wd_file = myinotify_add_watch(fd_notify, TEST_FILE, IN_ALL_EVENTS);
133	if (wd_file == -1)
134		tst_brkm(TBROK|TERRNO, cleanup,
135		    "inotify_add_watch(%d, \"%s\", IN_ALL_EVENTS) [2] failed",
136		    fd_notify, TEST_FILE);
137	reap_wd_file = 1;
138}
139
140int
141main(int argc, char **argv)
142{
143	char *msg;
144	size_t len;
145	int i, test_num;
146
147	i = 0;
148	test_num = 0;
149
150	msg = parse_opts(argc, argv, NULL, NULL);
151	if (msg != NULL)
152		tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg);
153
154	setup();
155
156	Tst_count = 0;
157
158	rmdir(TEST_DIR);
159	event_set[Tst_count].mask = IN_DELETE_SELF;
160	strcpy(event_set[Tst_count].name, "");
161	Tst_count++;
162	event_set[Tst_count].mask = IN_IGNORED;
163	strcpy(event_set[Tst_count].name, "");
164	Tst_count++;
165
166	unlink(TEST_FILE);
167	/*
168	 * When a file is unlinked, the link count is reduced by 1, and when it
169	 * hits 0 the file is removed.
170	 *
171	 * This isn't well documented in inotify(7), but it's intuitive if you
172	 * understand how Unix works.
173	 */
174	if (0 <= tst_kvercmp(2, 6, 25)) {
175		event_set[Tst_count].mask = IN_ATTRIB;
176		strcpy(event_set[Tst_count].name, "");
177		Tst_count++;
178		TST_TOTAL++;
179	}
180	event_set[Tst_count].mask = IN_DELETE_SELF;
181	strcpy(event_set[Tst_count].name, TEST_FILE);
182	Tst_count++;
183	event_set[Tst_count].mask = IN_IGNORED;
184	strcpy(event_set[Tst_count].name, "");
185	Tst_count++;
186
187	if (Tst_count != TST_TOTAL)
188		tst_brkm(TBROK, cleanup,
189			 "Tst_count and TST_TOTAL are not equal");
190
191	Tst_count = 0;
192
193	len = read(fd_notify, event_buf, EVENT_BUF_LEN);
194	if (len == -1)
195		tst_brkm(TBROK|TERRNO, cleanup, "read failed");
196
197	reap_wd_dir = 0;
198	reap_wd_file = 0;
199
200	while (i < len) {
201		struct inotify_event *event;
202		event = (struct inotify_event *)&event_buf[i];
203		if (test_num >= TST_TOTAL) {
204			if (tst_kvercmp(2, 6, 25) < 0
205			    && event_set[TST_TOTAL - 1].mask ==
206			    event->mask)
207				tst_resm(TWARN,
208					 "This may be kernel bug. "
209					 "Before kernel 2.6.25, a kernel bug "
210					 "meant that the kernel code that was "
211					 "intended to coalesce successive identical "
212					 "events (i.e., the two most recent "
213					 "events could potentially be coalesced "
214					 "if the older had not yet been read) "
215					 "instead checked if the most recent event "
216					 "could be coalesced with the oldest "
217					 "unread event. This has been fixed by commit"
218					 "1c17d18e3775485bf1e0ce79575eb637a94494a2.");
219			tst_resm(TFAIL,
220				 "got unnecessary event: "
221				 "wd=%d mask=%x cookie=%u len=%u "
222				 "name=\"%s\"", event->wd, event->mask,
223				 event->cookie, event->len,
224				 event->name);
225
226		} else if ((event_set[test_num].mask == event->mask)
227			   &&
228			   (!strncmp
229			    (event_set[test_num].name, event->name,
230			     event->len))) {
231			tst_resm(TPASS,
232				 "got event: wd=%d mask=%x "
233				 "cookie=%u len=%u name=\"%s\"",
234				 event->wd, event->mask, event->cookie,
235				 event->len, event->name);
236
237		} else {
238			tst_resm(TFAIL, "got event: wd=%d mask=%x "
239				 "(expected %x) cookie=%u len=%u "
240				 "name=\"%s\" (expected \"%s\") %d",
241				 event->wd, event->mask,
242				 event_set[test_num].mask,
243				 event->cookie, event->len, event->name,
244				 event_set[test_num].name,
245				 strcmp(event_set[test_num].name,
246					event->name));
247		}
248		test_num++;
249		i += EVENT_SIZE + event->len;
250	}
251
252	for (; test_num < TST_TOTAL; test_num++) {
253		tst_resm(TFAIL, "didn't get event: mask=%x ",
254			 event_set[test_num].mask);
255	}
256
257	cleanup();
258	tst_exit();
259}
260#else
261
262int TST_TOTAL;
263
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