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