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