1 /*
2  * Copyright (c) 2013 SUSE.  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 Jan Kara <jack@suse.cz>
24  *
25  * DESCRIPTION
26  *     Check that fanotify work for children of a directory
27  */
28 #define _GNU_SOURCE
29 #include "config.h"
30 
31 #include <stdio.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <fcntl.h>
35 #include <errno.h>
36 #include <string.h>
37 #include <sys/syscall.h>
38 #include "tst_test.h"
39 #include "fanotify.h"
40 
41 #if defined(HAVE_SYS_FANOTIFY_H)
42 #include <sys/fanotify.h>
43 
44 #define EVENT_MAX 1024
45 /* size of the event structure, not counting name */
46 #define EVENT_SIZE  (sizeof (struct fanotify_event_metadata))
47 /* reasonable guess as to size of 1024 events */
48 #define EVENT_BUF_LEN        (EVENT_MAX * EVENT_SIZE)
49 
50 #define BUF_SIZE 256
51 #define TST_TOTAL 8
52 
53 static char fname[BUF_SIZE];
54 static char buf[BUF_SIZE];
55 static int fd, fd_notify;
56 
57 static unsigned long long event_set[EVENT_MAX];
58 
59 static char event_buf[EVENT_BUF_LEN];
60 
61 void test01(void)
62 {
63 	int ret, len, i = 0, test_num = 0;
64 
65 	int tst_count = 0;
66 
67 	if (fanotify_mark(fd_notify, FAN_MARK_ADD, FAN_ACCESS |
68 			    FAN_MODIFY | FAN_CLOSE | FAN_OPEN |
69 			    FAN_EVENT_ON_CHILD | FAN_ONDIR, AT_FDCWD,
70 			  ".") < 0) {
71 		tst_brk(TBROK | TERRNO,
72 		    "fanotify_mark (%d, FAN_MARK_ADD, FAN_ACCESS | "
73 		    "FAN_MODIFY | FAN_CLOSE | FAN_OPEN | "
74 		    "FAN_EVENT_ON_CHILD | FAN_ONDIR, AT_FDCWD, '.') "
75 		    "failed", fd_notify);
76 	}
77 
78 	/*
79 	 * generate sequence of events
80 	 */
81 	fd = SAFE_OPEN(fname, O_RDWR | O_CREAT, 0700);
82 	event_set[tst_count] = FAN_OPEN;
83 	tst_count++;
84 
85 	SAFE_WRITE(1, fd, fname, strlen(fname));
86 	event_set[tst_count] = FAN_MODIFY;
87 	tst_count++;
88 
89 	SAFE_CLOSE(fd);
90 	event_set[tst_count] = FAN_CLOSE_WRITE;
91 	tst_count++;
92 
93 	/*
94 	 * Get list of events so far. We get events here to avoid
95 	 * merging of following events with the previous ones.
96 	 */
97 	ret = SAFE_READ(0, fd_notify, event_buf,
98 			EVENT_BUF_LEN);
99 	len = ret;
100 
101 	fd = SAFE_OPEN(fname, O_RDONLY);
102 	event_set[tst_count] = FAN_OPEN;
103 	tst_count++;
104 
105 	SAFE_READ(0, fd, buf, BUF_SIZE);
106 	event_set[tst_count] = FAN_ACCESS;
107 	tst_count++;
108 
109 	SAFE_CLOSE(fd);
110 	event_set[tst_count] = FAN_CLOSE_NOWRITE;
111 	tst_count++;
112 
113 	/*
114 	 * get next events
115 	 */
116 	ret = SAFE_READ(0, fd_notify, event_buf + len,
117 			EVENT_BUF_LEN - len);
118 	len += ret;
119 
120 	/*
121 	 * now remove child mark
122 	 */
123 	if (fanotify_mark(fd_notify, FAN_MARK_REMOVE,
124 			    FAN_EVENT_ON_CHILD, AT_FDCWD, ".") < 0) {
125 		tst_brk(TBROK | TERRNO,
126 		    "fanotify_mark (%d, FAN_MARK REMOVE, "
127 		    "FAN_EVENT_ON_CHILD, AT_FDCWD, '.') failed",
128 		    fd_notify);
129 	}
130 
131 	/*
132 	 * Do something to verify events didn't get generated
133 	 */
134 	fd = SAFE_OPEN(fname, O_RDONLY);
135 
136 	SAFE_CLOSE(fd);
137 
138 	fd = SAFE_OPEN(".", O_RDONLY | O_DIRECTORY);
139 	event_set[tst_count] = FAN_OPEN;
140 	tst_count++;
141 
142 	SAFE_CLOSE(fd);
143 	event_set[tst_count] = FAN_CLOSE_NOWRITE;
144 	tst_count++;
145 
146 	/*
147 	 * Check events got generated only for the directory
148 	 */
149 	ret = SAFE_READ(0, fd_notify, event_buf + len,
150 			EVENT_BUF_LEN - len);
151 	len += ret;
152 
153 	if (TST_TOTAL != tst_count) {
154 		tst_brk(TBROK,
155 			 "TST_TOTAL and tst_count are not equal");
156 	}
157 	tst_count = 0;
158 
159 	/*
160 	 * check events
161 	 */
162 	while (i < len) {
163 		struct fanotify_event_metadata *event;
164 
165 		event = (struct fanotify_event_metadata *)&event_buf[i];
166 		if (test_num >= TST_TOTAL) {
167 			tst_res(TFAIL,
168 				 "get unnecessary event: mask=%llx "
169 				 "pid=%u fd=%u",
170 				 (unsigned long long)event->mask,
171 				 (unsigned)event->pid, event->fd);
172 		} else if (!(event->mask & event_set[test_num])) {
173 			tst_res(TFAIL,
174 				 "get event: mask=%llx (expected %llx) "
175 				 "pid=%u fd=%u",
176 				 (unsigned long long)event->mask,
177 				 event_set[test_num],
178 				 (unsigned)event->pid, event->fd);
179 		} else if (event->pid != getpid()) {
180 			tst_res(TFAIL,
181 				 "get event: mask=%llx pid=%u "
182 				 "(expected %u) fd=%u",
183 				 (unsigned long long)event->mask,
184 				 (unsigned)event->pid,
185 				 (unsigned)getpid(),
186 				 event->fd);
187 		} else {
188 			tst_res(TPASS,
189 				    "get event: mask=%llx pid=%u fd=%u",
190 				    (unsigned long long)event->mask,
191 				    (unsigned)event->pid, event->fd);
192 		}
193 		event->mask &= ~event_set[test_num];
194 		/* No events left in current mask? Go for next event */
195 		if (event->mask == 0) {
196 			i += event->event_len;
197 			close(event->fd);
198 		}
199 		test_num++;
200 	}
201 	for (; test_num < TST_TOTAL; test_num++) {
202 		tst_res(TFAIL, "didn't get event: mask=%llx",
203 			 event_set[test_num]);
204 
205 	}
206 }
207 
208 static void setup(void)
209 {
210 	sprintf(fname, "fname_%d", getpid());
211 	fd_notify = SAFE_FANOTIFY_INIT(FAN_CLASS_NOTIF, O_RDONLY);
212 }
213 
214 static void cleanup(void)
215 {
216 	if (fd_notify > 0)
217 		SAFE_CLOSE(fd_notify);
218 }
219 
220 static struct tst_test test = {
221 	.test_all = test01,
222 	.setup = setup,
223 	.cleanup = cleanup,
224 	.needs_tmpdir = 1,
225 	.needs_root = 1
226 };
227 
228 #else
229 	TST_TEST_TCONF("system doesn't have required fanotify support");
230 #endif
231