• 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 
8 /*\
9  * [Description]
10  * Check various fanotify special flags.
11  */
12 
13 #define _GNU_SOURCE
14 #include "config.h"
15 
16 #include <stdio.h>
17 #include <sys/stat.h>
18 #include <sys/types.h>
19 #include <errno.h>
20 #include <string.h>
21 #include <sys/syscall.h>
22 #include "tst_test.h"
23 
24 #ifdef HAVE_SYS_FANOTIFY_H
25 #include "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 		SAFE_FANOTIFY_MARK(fd_notify, FAN_MARK_REMOVE | flag,
81 				  FAN_OPEN, AT_FDCWD, file);
82 	}
83 }
84 
85 #define CHECK_MARK(file, flag, expect, func) check_mark(file, flag, #flag, expect, func)
86 
do_open(char * file,int flag)87 static void do_open(char *file, int flag)
88 {
89 	int fd;
90 
91 	fd = SAFE_OPEN(file, O_RDONLY | flag);
92 	SAFE_CLOSE(fd);
93 }
94 
open_file(char * file)95 static void open_file(char *file)
96 {
97 	do_open(file, 0);
98 }
99 
open_dir(char * file)100 static void open_dir(char *file)
101 {
102 	do_open(file, O_DIRECTORY);
103 }
104 
verify_event(int mask)105 static void verify_event(int mask)
106 {
107 	int ret;
108 	struct fanotify_event_metadata *event;
109 	struct stat st;
110 
111 	/* Read the event */
112 	ret = SAFE_READ(0, fd_notify, event_buf + len,
113 			EVENT_BUF_LEN - len);
114 	event = (struct fanotify_event_metadata *)&event_buf[len];
115 	len += ret;
116 
117 	if (event->mask != FAN_OPEN) {
118 		tst_res(TFAIL, "got unexpected event %llx",
119 			(unsigned long long)event->mask);
120 	} else if (fstat(event->fd, &st) < 0) {
121 		tst_res(TFAIL, "failed to stat event->fd (%s)",
122 			strerror(errno));
123 	} else if ((int)(st.st_mode & S_IFMT) != mask) {
124 		tst_res(TFAIL, "event->fd points to object of different type "
125 			"(%o != %o)", st.st_mode & S_IFMT, mask);
126 	} else {
127 		tst_res(TPASS, "event generated properly for type %o", mask);
128 	}
129 	if (event->fd != FAN_NOFD)
130 		SAFE_CLOSE(event->fd);
131 }
132 
do_open_test(char * file,int flag,int mask)133 static void do_open_test(char *file, int flag, int mask)
134 {
135 	do_open(file, flag);
136 
137 	verify_event(mask);
138 }
139 
test_open_file(char * file)140 static void test_open_file(char *file)
141 {
142 	do_open_test(file, 0, S_IFREG);
143 }
144 
verify_no_event(void)145 static void verify_no_event(void)
146 {
147 	int ret;
148 
149 	ret = read(fd_notify, event_buf + len, EVENT_BUF_LEN - len);
150 	if (ret != -1) {
151 		struct fanotify_event_metadata *event;
152 
153 		event = (struct fanotify_event_metadata *)&event_buf[len];
154 		tst_res(TFAIL, "seen unexpected event (mask %llx)",
155 			(unsigned long long)event->mask);
156 		/* Cleanup fd from the event */
157 		if (event->fd != FAN_NOFD)
158 			SAFE_CLOSE(event->fd);
159 	} else if (errno != EAGAIN) {
160 		tst_res(TFAIL | TERRNO, "read(%d, buf, %zu) failed", fd_notify,
161 			EVENT_BUF_LEN);
162 	} else {
163 		tst_res(TPASS, "No event as expected");
164 	}
165 }
166 
test_open_symlink(char * file)167 static void test_open_symlink(char *file)
168 {
169 	/* Since mark is on a symlink, no event should be generated by opening a file */
170 	do_open(file, 0);
171 	verify_no_event();
172 }
173 
test01(void)174 void test01(void)
175 {
176 	/* Check ONLYDIR on a directory */
177 	CHECK_MARK(".", FAN_MARK_ONLYDIR, 0, NULL);
178 
179 	/* Check ONLYDIR without a directory */
180 	CHECK_MARK(fname, FAN_MARK_ONLYDIR, -1, NULL);
181 
182 	/* Check DONT_FOLLOW for a symlink */
183 	CHECK_MARK(sname, FAN_MARK_DONT_FOLLOW, 0, test_open_symlink);
184 
185 	/* Check without DONT_FOLLOW for a symlink */
186 	CHECK_MARK(sname, 0, 0, test_open_file);
187 
188 	/* Verify FAN_MARK_FLUSH destroys all inode marks */
189 	SAFE_FANOTIFY_MARK(fd_notify, FAN_MARK_ADD, FAN_OPEN, AT_FDCWD, fname);
190 	SAFE_FANOTIFY_MARK(fd_notify, FAN_MARK_ADD, FAN_OPEN | FAN_ONDIR,
191 			   AT_FDCWD, dir);
192 	open_file(fname);
193 	verify_event(S_IFREG);
194 	open_dir(dir);
195 	verify_event(S_IFDIR);
196 	SAFE_FANOTIFY_MARK(fd_notify, FAN_MARK_FLUSH, 0, AT_FDCWD, ".");
197 
198 	open_dir(dir);
199 	verify_no_event();
200 }
201 
setup(void)202 static void setup(void)
203 {
204 	int fd;
205 
206 	sprintf(fname, "fname_%d", getpid());
207 	fd = SAFE_OPEN(fname, O_RDWR | O_CREAT, 0644);
208 	SAFE_CLOSE(fd);
209 
210 	sprintf(sname, "symlink_%d", getpid());
211 	SAFE_SYMLINK(fname, sname);
212 
213 	sprintf(dir, "dir_%d", getpid());
214 	SAFE_MKDIR(dir, 0755);
215 
216 	fd_notify = SAFE_FANOTIFY_INIT(FAN_CLASS_NOTIF | FAN_NONBLOCK,
217 					O_RDONLY);
218 }
219 
cleanup(void)220 static void cleanup(void)
221 {
222 	if (fd_notify > 0)
223 		SAFE_CLOSE(fd_notify);
224 }
225 
226 static struct tst_test test = {
227 	.test_all = test01,
228 	.setup = setup,
229 	.cleanup = cleanup,
230 	.needs_tmpdir = 1,
231 	.needs_root = 1
232 };
233 
234 #else
235 	TST_TEST_TCONF("system doesn't have required fanotify support");
236 #endif
237