1 /*
2  * Copyright (c) 2008 Parallels.  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  * Started by Andrew Vagin <avagin@gmail.com>
24  *
25  * DESCRIPTION
26  *	Check that inotify get IN_UNMOUNT event and
27  *	don't block the umount command.
28  *
29  * ALGORITHM
30  *	Execute sequence file's operation and check return events
31  *
32  */
33 #include "config.h"
34 
35 #include <stdio.h>
36 #include <sys/mount.h>
37 #include <sys/stat.h>
38 #include <sys/types.h>
39 #include <fcntl.h>
40 #include <errno.h>
41 #include <string.h>
42 #include <sys/syscall.h>
43 #include <signal.h>
44 #include "tst_test.h"
45 #include "inotify.h"
46 
47 #if defined(HAVE_SYS_INOTIFY_H)
48 #include <sys/inotify.h>
49 
50 #define EVENT_MAX 1024
51 /* size of the event structure, not counting name */
52 #define EVENT_SIZE (sizeof(struct inotify_event))
53 /* reasonable guess as to size of 1024 events */
54 #define EVENT_BUF_LEN		(EVENT_MAX * (EVENT_SIZE + 16))
55 
56 #define BUF_SIZE 1024
57 static char fname[BUF_SIZE];
58 static int fd, fd_notify;
59 static int wd;
60 
61 static unsigned int event_set[EVENT_MAX];
62 
63 static char event_buf[EVENT_BUF_LEN];
64 
65 #define DIR_MODE	(S_IRWXU | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP)
66 
67 static char *mntpoint = "mntpoint";
68 static int mount_flag;
69 
verify_inotify(void)70 void verify_inotify(void)
71 {
72 	int ret;
73 	int len, i, test_num;
74 
75 	int test_cnt = 0;
76 
77 	SAFE_MOUNT(tst_device->dev, mntpoint, tst_device->fs_type, 0, NULL);
78 	mount_flag = 1;
79 
80 	wd = myinotify_add_watch(fd_notify, fname, IN_ALL_EVENTS);
81 	if (wd < 0) {
82 		tst_brk(TBROK | TERRNO,
83 			"inotify_add_watch (%d, %s, IN_ALL_EVENTS) failed.",
84 			fd_notify, fname);
85 	}
86 
87 	event_set[test_cnt] = IN_UNMOUNT;
88 	test_cnt++;
89 	event_set[test_cnt] = IN_IGNORED;
90 	test_cnt++;
91 
92 	/*check exit code from inotify_rm_watch */
93 	test_cnt++;
94 
95 	tst_res(TINFO, "umount %s", tst_device->dev);
96 	TEST(tst_umount(mntpoint));
97 	if (TST_RET != 0) {
98 		tst_brk(TBROK, "umount(2) Failed "
99 			"while unmounting errno = %d : %s",
100 			TST_ERR, strerror(TST_ERR));
101 	}
102 	mount_flag = 0;
103 
104 	len = read(fd_notify, event_buf, EVENT_BUF_LEN);
105 	if (len < 0) {
106 		tst_brk(TBROK | TERRNO,
107 			"read(%d, buf, %zu) failed", fd_notify, EVENT_BUF_LEN);
108 	}
109 
110 	/* check events */
111 	test_num = 0;
112 	i = 0;
113 	while (i < len) {
114 		struct inotify_event *event;
115 		event = (struct inotify_event *)&event_buf[i];
116 		if (test_num >= (test_cnt - 1)) {
117 			tst_res(TFAIL,
118 				"get unnecessary event: wd=%d mask=%x "
119 				"cookie=%u len=%u",
120 				event->wd, event->mask,
121 				event->cookie, event->len);
122 		} else if (event_set[test_num] == event->mask) {
123 			tst_res(TPASS, "get event: wd=%d mask=%x"
124 				" cookie=%u len=%u",
125 				event->wd, event->mask,
126 				event->cookie, event->len);
127 
128 		} else {
129 			tst_res(TFAIL, "get event: wd=%d mask=%x "
130 				"(expected %x) cookie=%u len=%u",
131 				event->wd, event->mask,
132 				event_set[test_num],
133 				event->cookie, event->len);
134 		}
135 		test_num++;
136 		i += EVENT_SIZE + event->len;
137 	}
138 	for (; test_num < test_cnt - 1; test_num++) {
139 		tst_res(TFAIL, "don't get event: mask=%x ",
140 			event_set[test_num]);
141 
142 	}
143 	ret = myinotify_rm_watch(fd_notify, wd);
144 	if (ret != -1 || errno != EINVAL)
145 		tst_res(TFAIL | TERRNO,
146 			"inotify_rm_watch (%d, %d) didn't return EINVAL",
147 			fd_notify, wd);
148 	else
149 		tst_res(TPASS, "inotify_rm_watch (%d, %d) returned EINVAL",
150 			fd_notify, wd);
151 }
152 
setup(void)153 static void setup(void)
154 {
155 	int ret;
156 
157 	SAFE_MKDIR(mntpoint, DIR_MODE);
158 
159 	SAFE_MOUNT(tst_device->dev, mntpoint, tst_device->fs_type, 0, NULL);
160 	mount_flag = 1;
161 
162 	sprintf(fname, "%s/tfile_%d", mntpoint, getpid());
163 	fd = SAFE_OPEN(fname, O_RDWR | O_CREAT, 0700);
164 
165 	ret = write(fd, fname, 1);
166 	if (ret == -1) {
167 		tst_brk(TBROK | TERRNO,
168 			 "write(%d, %s, 1) failed", fd, fname);
169 	}
170 
171 	/* close the file we have open */
172 	SAFE_CLOSE(fd);
173 
174 	fd_notify = myinotify_init();
175 	if (fd_notify < 0) {
176 		if (errno == ENOSYS)
177 			tst_brk(TCONF,
178 				"inotify is not configured in this kernel.");
179 		else
180 			tst_brk(TBROK | TERRNO,
181 				"inotify_init failed");
182 	}
183 
184 	tst_umount(mntpoint);
185 	mount_flag = 0;
186 }
187 
cleanup(void)188 static void cleanup(void)
189 {
190 	if (fd_notify > 0)
191 		SAFE_CLOSE(fd_notify);
192 
193 	if (mount_flag) {
194 		TEST(tst_umount(mntpoint));
195 		if (TST_RET != 0)
196 			tst_res(TWARN | TTERRNO, "umount(%s) failed",
197 				mntpoint);
198 	}
199 }
200 
201 static struct tst_test test = {
202 	.needs_root = 1,
203 	.needs_tmpdir = 1,
204 	.format_device = 1,
205 	.setup = setup,
206 	.cleanup = cleanup,
207 	.test_all = verify_inotify,
208 };
209 
210 #else
211 	TST_TEST_TCONF("system doesn't have required inotify support");
212 #endif
213