• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 various fanotify special flags
27  */
28 
29 #define _GNU_SOURCE
30 #include "config.h"
31 
32 #include <stdio.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <fcntl.h>
36 #include <errno.h>
37 #include <string.h>
38 #include <sys/syscall.h>
39 #include "tst_test.h"
40 #include "fanotify.h"
41 
42 #if defined(HAVE_SYS_FANOTIFY_H)
43 #include <sys/fanotify.h>
44 
45 #define EVENT_MAX 1024
46 /* size of the event structure, not counting name */
47 #define EVENT_SIZE  (sizeof (struct fanotify_event_metadata))
48 /* reasonable guess as to size of 1024 events */
49 #define EVENT_BUF_LEN        (EVENT_MAX * EVENT_SIZE)
50 
51 #define BUF_SIZE 256
52 #define TST_TOTAL 9
53 
54 static char fname[BUF_SIZE];
55 static char sname[BUF_SIZE];
56 static char dir[BUF_SIZE];
57 static int fd_notify;
58 
59 static int len;
60 static char event_buf[EVENT_BUF_LEN];
61 
expect_str_fail(int expect)62 static char *expect_str_fail(int expect)
63 {
64 	if (expect == 0)
65 		return "failed";
66 	return "unexpectedly succeeded";
67 }
68 
expect_str_pass(int expect)69 static char *expect_str_pass(int expect)
70 {
71 	if (expect == 0)
72 		return "succeeded";
73 	return "failed";
74 }
75 
check_mark(char * file,unsigned long long flag,char * flagstr,int expect,void (* test_event)(char *))76 static void check_mark(char *file, unsigned long long flag, char *flagstr,
77 		       int expect, void (*test_event)(char *))
78 {
79 	if (fanotify_mark(fd_notify, FAN_MARK_ADD | flag, FAN_OPEN, AT_FDCWD,
80 			    file) != expect) {
81 		tst_res(TFAIL,
82 		    "fanotify_mark (%d, FAN_MARK_ADD | %s, FAN_OPEN, AT_FDCWD, "
83 		    "'%s') %s", fd_notify, flagstr, file, expect_str_fail(expect));
84 	} else {
85 		tst_res(TPASS,
86 		    "fanotify_mark (%d, FAN_MARK_ADD | %s, FAN_OPEN, AT_FDCWD, "
87 		    "'%s') %s", fd_notify, flagstr, file, expect_str_pass(expect));
88 
89 		/* If we expected failure there's nothing to clean up */
90 		if (expect == -1)
91 			return;
92 
93 		if (test_event)
94 			test_event(file);
95 
96 		if (fanotify_mark(fd_notify, FAN_MARK_REMOVE | flag,
97 				    FAN_OPEN, AT_FDCWD, file) < 0) {
98 			tst_brk(TBROK | TERRNO,
99 			    "fanotify_mark (%d, FAN_MARK_REMOVE | %s, "
100 			    "FAN_OPEN, AT_FDCWD, '%s') failed",
101 			    fd_notify, flagstr, file);
102 		}
103 	}
104 }
105 
106 #define CHECK_MARK(file, flag, expect, func) check_mark(file, flag, #flag, expect, func)
107 
do_open(char * file,int flag)108 static void do_open(char *file, int flag)
109 {
110 	int fd;
111 
112 	fd = SAFE_OPEN(file, O_RDONLY | flag);
113 	SAFE_CLOSE(fd);
114 }
115 
open_file(char * file)116 static void open_file(char *file)
117 {
118 	do_open(file, 0);
119 }
120 
open_dir(char * file)121 static void open_dir(char *file)
122 {
123 	do_open(file, O_DIRECTORY);
124 }
125 
verify_event(int mask)126 static void verify_event(int mask)
127 {
128 	int ret;
129 	struct fanotify_event_metadata *event;
130 	struct stat st;
131 
132 	/* Read the event */
133 	ret = SAFE_READ(0, fd_notify, event_buf + len,
134 			EVENT_BUF_LEN - len);
135 	event = (struct fanotify_event_metadata *)&event_buf[len];
136 	len += ret;
137 
138 	if (event->mask != FAN_OPEN) {
139 		tst_res(TFAIL, "got unexpected event %llx",
140 			 (unsigned long long)event->mask);
141 	} else if (fstat(event->fd, &st) < 0) {
142 		tst_res(TFAIL, "failed to stat event->fd (%s)",
143 			 strerror(errno));
144 	} else if ((int)(st.st_mode & S_IFMT) != mask) {
145 		tst_res(TFAIL, "event->fd points to object of different type "
146 			 "(%o != %o)", st.st_mode & S_IFMT, mask);
147 	} else {
148 		tst_res(TPASS, "event generated properly for type %o", mask);
149 	}
150 	close(event->fd);
151 }
152 
do_open_test(char * file,int flag,int mask)153 static void do_open_test(char *file, int flag, int mask)
154 {
155 	do_open(file, flag);
156 
157 	verify_event(mask);
158 }
159 
test_open_file(char * file)160 static void test_open_file(char *file)
161 {
162 	do_open_test(file, 0, S_IFREG);
163 }
164 
verify_no_event(void)165 static void verify_no_event(void)
166 {
167 	int ret;
168 
169 	ret = read(fd_notify, event_buf + len, EVENT_BUF_LEN - len);
170 	if (ret != -1) {
171 		struct fanotify_event_metadata *event;
172 
173 		event = (struct fanotify_event_metadata *)&event_buf[len];
174 		tst_res(TFAIL, "seen unexpected event (mask %llx)",
175 			 (unsigned long long)event->mask);
176 		/* Cleanup fd from the event */
177 		close(event->fd);
178 	} else if (errno != EAGAIN) {
179 		tst_res(TFAIL | TERRNO, "read(%d, buf, %zu) failed", fd_notify,
180 			 EVENT_BUF_LEN);
181 	} else {
182 		tst_res(TPASS, "No event as expected");
183 	}
184 }
185 
test_open_symlink(char * file)186 static void test_open_symlink(char *file)
187 {
188 	/* Since mark is on a symlink, no event should be generated by opening a file */
189 	do_open(file, 0);
190 	verify_no_event();
191 }
192 
test01(void)193 void test01(void)
194 {
195 	/* Check ONLYDIR on a directory */
196 	CHECK_MARK(".", FAN_MARK_ONLYDIR, 0, NULL);
197 
198 	/* Check ONLYDIR without a directory */
199 	CHECK_MARK(fname, FAN_MARK_ONLYDIR, -1, NULL);
200 
201 	/* Check DONT_FOLLOW for a symlink */
202 	CHECK_MARK(sname, FAN_MARK_DONT_FOLLOW, 0, test_open_symlink);
203 
204 	/* Check without DONT_FOLLOW for a symlink */
205 	CHECK_MARK(sname, 0, 0, test_open_file);
206 
207 	/* Verify FAN_MARK_FLUSH destroys all inode marks */
208 	if (fanotify_mark(fd_notify, FAN_MARK_ADD,
209 			    FAN_OPEN, AT_FDCWD, fname) < 0) {
210 		tst_brk(TBROK | TERRNO,
211 		    "fanotify_mark (%d, FAN_MARK_ADD, FAN_OPEN, "
212 		    "AT_FDCWD, '%s') failed", fd_notify, fname);
213 	}
214 	if (fanotify_mark(fd_notify, FAN_MARK_ADD,
215 			    FAN_OPEN | FAN_ONDIR, AT_FDCWD, dir) < 0) {
216 		tst_brk(TBROK | TERRNO,
217 		    "fanotify_mark (%d, FAN_MARK_ADD, FAN_OPEN | "
218 		    "FAN_ONDIR, AT_FDCWD, '%s') failed", fd_notify,
219 		    dir);
220 	}
221 	open_file(fname);
222 	verify_event(S_IFREG);
223 	open_dir(dir);
224 	verify_event(S_IFDIR);
225 	if (fanotify_mark(fd_notify, FAN_MARK_FLUSH,
226 			    0, AT_FDCWD, ".") < 0) {
227 		tst_brk(TBROK | TERRNO,
228 		    "fanotify_mark (%d, FAN_MARK_FLUSH, 0, "
229 		    "AT_FDCWD, '.') failed", fd_notify);
230 	}
231 
232 	open_dir(dir);
233 	verify_no_event();
234 }
235 
setup(void)236 static void setup(void)
237 {
238 	int fd;
239 
240 	sprintf(fname, "fname_%d", getpid());
241 	fd = SAFE_OPEN(fname, O_RDWR | O_CREAT, 0644);
242 	SAFE_CLOSE(fd);
243 
244 	sprintf(sname, "symlink_%d", getpid());
245 	SAFE_SYMLINK(fname, sname);
246 
247 	sprintf(dir, "dir_%d", getpid());
248 	SAFE_MKDIR(dir, 0755);
249 
250 	fd_notify = SAFE_FANOTIFY_INIT(FAN_CLASS_NOTIF | FAN_NONBLOCK,
251 			O_RDONLY);
252 }
253 
cleanup(void)254 static void cleanup(void)
255 {
256 	if (fd_notify > 0)
257 		SAFE_CLOSE(fd_notify);
258 }
259 
260 static struct tst_test test = {
261 	.test_all = test01,
262 	.setup = setup,
263 	.cleanup = cleanup,
264 	.needs_tmpdir = 1,
265 	.needs_root = 1
266 };
267 
268 #else
269 	TST_TEST_TCONF("system doesn't have required fanotify support");
270 #endif
271