Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 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 permission events work |
| 27 | */ |
Waldemar Brodkorb | dd9240e | 2016-03-05 09:54:10 +0100 | [diff] [blame] | 28 | #define _GNU_SOURCE |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 29 | #include "config.h" |
| 30 | |
| 31 | #include <stdio.h> |
| 32 | #include <unistd.h> |
| 33 | #include <sys/stat.h> |
| 34 | #include <sys/types.h> |
Steven Jackson | a7fa8561 | 2016-12-13 15:10:28 +0000 | [diff] [blame] | 35 | #include <fcntl.h> |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 36 | #include <sys/wait.h> |
| 37 | #include <errno.h> |
| 38 | #include <string.h> |
| 39 | #include <signal.h> |
| 40 | #include <sys/syscall.h> |
Xiong Zhou | 9964e88 | 2017-08-16 15:15:36 +0800 | [diff] [blame] | 41 | #include <stdlib.h> |
| 42 | #include "tst_test.h" |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 43 | #include "fanotify.h" |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 44 | |
| 45 | #if defined(HAVE_SYS_FANOTIFY_H) |
| 46 | #include <sys/fanotify.h> |
| 47 | |
| 48 | #define EVENT_MAX 1024 |
| 49 | /* size of the event structure, not counting name */ |
| 50 | #define EVENT_SIZE (sizeof (struct fanotify_event_metadata)) |
| 51 | /* reasonable guess as to size of 1024 events */ |
| 52 | #define EVENT_BUF_LEN (EVENT_MAX * EVENT_SIZE) |
| 53 | |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 54 | #define BUF_SIZE 256 |
Xiong Zhou | 9964e88 | 2017-08-16 15:15:36 +0800 | [diff] [blame] | 55 | #define TST_TOTAL 3 |
| 56 | |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 57 | static char fname[BUF_SIZE]; |
| 58 | static char buf[BUF_SIZE]; |
| 59 | static volatile int fd_notify; |
| 60 | |
| 61 | static pid_t child_pid; |
| 62 | |
| 63 | static unsigned long long event_set[EVENT_MAX]; |
| 64 | static unsigned int event_resp[EVENT_MAX]; |
| 65 | |
| 66 | static char event_buf[EVENT_BUF_LEN]; |
| 67 | |
| 68 | static void generate_events(void) |
| 69 | { |
| 70 | int fd; |
| 71 | |
| 72 | /* |
| 73 | * generate sequence of events |
| 74 | */ |
| 75 | if ((fd = open(fname, O_RDWR | O_CREAT, 0700)) == -1) |
| 76 | exit(1); |
| 77 | if (write(fd, fname, 1) == -1) |
| 78 | exit(2); |
| 79 | |
| 80 | lseek(fd, 0, SEEK_SET); |
| 81 | if (read(fd, buf, BUF_SIZE) != -1) |
| 82 | exit(3); |
| 83 | |
| 84 | if (close(fd) == -1) |
| 85 | exit(4); |
| 86 | } |
| 87 | |
| 88 | static void child_handler(int tmp) |
| 89 | { |
Xiong Zhou | 9964e88 | 2017-08-16 15:15:36 +0800 | [diff] [blame] | 90 | (void)tmp; |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 91 | /* |
| 92 | * Close notification fd so that we cannot block while reading |
| 93 | * from it |
| 94 | */ |
| 95 | close(fd_notify); |
| 96 | fd_notify = -1; |
| 97 | } |
| 98 | |
| 99 | static void run_child(void) |
| 100 | { |
| 101 | struct sigaction child_action; |
| 102 | |
| 103 | child_action.sa_handler = child_handler; |
| 104 | sigemptyset(&child_action.sa_mask); |
| 105 | child_action.sa_flags = SA_NOCLDSTOP; |
| 106 | |
| 107 | if (sigaction(SIGCHLD, &child_action, NULL) < 0) { |
Xiong Zhou | 9964e88 | 2017-08-16 15:15:36 +0800 | [diff] [blame] | 108 | tst_brk(TBROK | TERRNO, |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 109 | "sigaction(SIGCHLD, &child_action, NULL) failed"); |
| 110 | } |
| 111 | |
Xiong Zhou | 9964e88 | 2017-08-16 15:15:36 +0800 | [diff] [blame] | 112 | child_pid = SAFE_FORK(); |
| 113 | if (child_pid == 0) { |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 114 | /* Child will generate events now */ |
| 115 | close(fd_notify); |
| 116 | generate_events(); |
| 117 | exit(0); |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 118 | } |
| 119 | } |
| 120 | |
| 121 | static void check_child(void) |
| 122 | { |
| 123 | struct sigaction child_action; |
| 124 | int child_ret; |
| 125 | |
| 126 | child_action.sa_handler = SIG_IGN; |
| 127 | sigemptyset(&child_action.sa_mask); |
| 128 | child_action.sa_flags = SA_NOCLDSTOP; |
| 129 | if (sigaction(SIGCHLD, &child_action, NULL) < 0) { |
Xiong Zhou | 9964e88 | 2017-08-16 15:15:36 +0800 | [diff] [blame] | 130 | tst_brk(TBROK | TERRNO, |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 131 | "sigaction(SIGCHLD, &child_action, NULL) failed"); |
| 132 | } |
Cyril Hrubis | 66374d5 | 2017-10-03 13:46:52 +0200 | [diff] [blame^] | 133 | SAFE_WAITPID(-1, &child_ret, 0); |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 134 | |
| 135 | if (WIFSIGNALED(child_ret)) { |
Xiong Zhou | 9964e88 | 2017-08-16 15:15:36 +0800 | [diff] [blame] | 136 | tst_res(TFAIL, "child exited due to signal %d", |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 137 | WTERMSIG(child_ret)); |
| 138 | } else if (WIFEXITED(child_ret)) { |
| 139 | if (WEXITSTATUS(child_ret) == 0) |
Xiong Zhou | 9964e88 | 2017-08-16 15:15:36 +0800 | [diff] [blame] | 140 | tst_res(TPASS, "child exited correctly"); |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 141 | else |
Xiong Zhou | 9964e88 | 2017-08-16 15:15:36 +0800 | [diff] [blame] | 142 | tst_res(TFAIL, "child exited with status %d", |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 143 | WEXITSTATUS(child_ret)); |
| 144 | } else { |
Xiong Zhou | 9964e88 | 2017-08-16 15:15:36 +0800 | [diff] [blame] | 145 | tst_res(TFAIL, "child exited for unknown reason (status %d)", |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 146 | child_ret); |
| 147 | } |
| 148 | } |
| 149 | |
Xiong Zhou | 9964e88 | 2017-08-16 15:15:36 +0800 | [diff] [blame] | 150 | void test01(void) |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 151 | { |
Xiong Zhou | 9964e88 | 2017-08-16 15:15:36 +0800 | [diff] [blame] | 152 | int tst_count, fd_notify_backup = -1; |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 153 | |
Xiong Zhou | 9964e88 | 2017-08-16 15:15:36 +0800 | [diff] [blame] | 154 | int ret, len = 0, i = 0, test_num = 0; |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 155 | |
Xiong Zhou | 9964e88 | 2017-08-16 15:15:36 +0800 | [diff] [blame] | 156 | if (fd_notify_backup == -1) { |
| 157 | fd_notify_backup = dup(fd_notify); |
| 158 | if (fd_notify_backup < 0) |
| 159 | tst_brk(TBROK | TERRNO, |
| 160 | "dup(%d) failed", fd_notify); |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 161 | } |
Xiong Zhou | 9964e88 | 2017-08-16 15:15:36 +0800 | [diff] [blame] | 162 | run_child(); |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 163 | |
Xiong Zhou | 9964e88 | 2017-08-16 15:15:36 +0800 | [diff] [blame] | 164 | tst_count = 0; |
| 165 | |
| 166 | event_set[tst_count] = FAN_OPEN_PERM; |
| 167 | event_resp[tst_count++] = FAN_ALLOW; |
| 168 | event_set[tst_count] = FAN_ACCESS_PERM; |
| 169 | event_resp[tst_count++] = FAN_DENY; |
| 170 | |
| 171 | /* tst_count + 1 is for checking child return value */ |
| 172 | if (TST_TOTAL != tst_count + 1) { |
| 173 | tst_brk(TBROK, |
| 174 | "TST_TOTAL and tst_count do not match"); |
| 175 | } |
| 176 | tst_count = 0; |
| 177 | |
| 178 | /* |
| 179 | * check events |
| 180 | */ |
| 181 | while (test_num < TST_TOTAL && fd_notify != -1) { |
| 182 | struct fanotify_event_metadata *event; |
| 183 | |
| 184 | if (i == len) { |
| 185 | /* Get more events */ |
| 186 | ret = read(fd_notify, event_buf + len, |
| 187 | EVENT_BUF_LEN - len); |
| 188 | if (fd_notify == -1) |
| 189 | break; |
| 190 | if (ret < 0) { |
| 191 | tst_brk(TBROK, |
| 192 | "read(%d, buf, %zu) failed", |
| 193 | fd_notify, EVENT_BUF_LEN); |
| 194 | } |
| 195 | len += ret; |
| 196 | } |
| 197 | |
| 198 | event = (struct fanotify_event_metadata *)&event_buf[i]; |
| 199 | if (!(event->mask & event_set[test_num])) { |
| 200 | tst_res(TFAIL, |
| 201 | "get event: mask=%llx (expected %llx) " |
| 202 | "pid=%u fd=%u", |
| 203 | (unsigned long long)event->mask, |
| 204 | event_set[test_num], |
| 205 | (unsigned)event->pid, event->fd); |
| 206 | } else if (event->pid != child_pid) { |
| 207 | tst_res(TFAIL, |
| 208 | "get event: mask=%llx pid=%u " |
| 209 | "(expected %u) fd=%u", |
| 210 | (unsigned long long)event->mask, |
| 211 | (unsigned)event->pid, |
| 212 | (unsigned)child_pid, |
| 213 | event->fd); |
| 214 | } else { |
| 215 | tst_res(TPASS, |
| 216 | "get event: mask=%llx pid=%u fd=%u", |
| 217 | (unsigned long long)event->mask, |
| 218 | (unsigned)event->pid, event->fd); |
| 219 | } |
| 220 | /* Write response to permission event */ |
| 221 | if (event_set[test_num] & FAN_ALL_PERM_EVENTS) { |
| 222 | struct fanotify_response resp; |
| 223 | |
| 224 | resp.fd = event->fd; |
| 225 | resp.response = event_resp[test_num]; |
| 226 | SAFE_WRITE(1, fd_notify, &resp, |
| 227 | sizeof(resp)); |
| 228 | } |
| 229 | event->mask &= ~event_set[test_num]; |
| 230 | /* No events left in current mask? Go for next event */ |
| 231 | if (event->mask == 0) { |
| 232 | i += event->event_len; |
| 233 | close(event->fd); |
| 234 | } |
| 235 | test_num++; |
| 236 | } |
| 237 | for (; test_num < TST_TOTAL - 1; test_num++) { |
| 238 | tst_res(TFAIL, "didn't get event: mask=%llx", |
| 239 | event_set[test_num]); |
| 240 | |
| 241 | } |
| 242 | check_child(); |
| 243 | /* We got SIGCHLD while running, resetup fd_notify */ |
| 244 | if (fd_notify == -1) { |
| 245 | fd_notify = fd_notify_backup; |
| 246 | fd_notify_backup = -1; |
| 247 | } |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 248 | } |
| 249 | |
| 250 | static void setup(void) |
| 251 | { |
| 252 | int fd; |
| 253 | |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 254 | sprintf(fname, "fname_%d", getpid()); |
Xiong Zhou | 9964e88 | 2017-08-16 15:15:36 +0800 | [diff] [blame] | 255 | fd = SAFE_OPEN(fname, O_CREAT | O_RDWR, 0644); |
| 256 | SAFE_WRITE(1, fd, fname, 1); |
| 257 | SAFE_CLOSE(fd); |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 258 | |
Xiong Zhou | 9964e88 | 2017-08-16 15:15:36 +0800 | [diff] [blame] | 259 | fd_notify = SAFE_FANOTIFY_INIT(FAN_CLASS_CONTENT, O_RDONLY); |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 260 | |
Helge Deller | ff96476 | 2014-07-29 18:10:40 +0200 | [diff] [blame] | 261 | if (fanotify_mark(fd_notify, FAN_MARK_ADD, FAN_ACCESS_PERM | |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 262 | FAN_OPEN_PERM, AT_FDCWD, fname) < 0) { |
Helge Deller | ff96476 | 2014-07-29 18:10:40 +0200 | [diff] [blame] | 263 | if (errno == EINVAL) { |
Xiong Zhou | 9964e88 | 2017-08-16 15:15:36 +0800 | [diff] [blame] | 264 | tst_brk(TCONF | TERRNO, |
Helge Deller | ff96476 | 2014-07-29 18:10:40 +0200 | [diff] [blame] | 265 | "CONFIG_FANOTIFY_ACCESS_PERMISSIONS not " |
| 266 | "configured in kernel?"); |
| 267 | } else { |
Xiong Zhou | 9964e88 | 2017-08-16 15:15:36 +0800 | [diff] [blame] | 268 | tst_brk(TBROK | TERRNO, |
Helge Deller | ff96476 | 2014-07-29 18:10:40 +0200 | [diff] [blame] | 269 | "fanotify_mark (%d, FAN_MARK_ADD, FAN_ACCESS_PERM | " |
| 270 | "FAN_OPEN_PERM, AT_FDCWD, %s) failed.", fd_notify, fname); |
| 271 | } |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 272 | } |
| 273 | |
| 274 | } |
| 275 | |
| 276 | static void cleanup(void) |
| 277 | { |
Xiong Zhou | 9964e88 | 2017-08-16 15:15:36 +0800 | [diff] [blame] | 278 | if (fd_notify > 0) |
| 279 | SAFE_CLOSE(fd_notify); |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 280 | } |
| 281 | |
Xiong Zhou | 9964e88 | 2017-08-16 15:15:36 +0800 | [diff] [blame] | 282 | static struct tst_test test = { |
| 283 | .test_all = test01, |
| 284 | .setup = setup, |
| 285 | .cleanup = cleanup, |
| 286 | .needs_tmpdir = 1, |
Xiao Liang | 761577a | 2017-08-21 13:07:19 +0800 | [diff] [blame] | 287 | .forks_child = 1, |
Xiong Zhou | 9964e88 | 2017-08-16 15:15:36 +0800 | [diff] [blame] | 288 | .needs_root = 1 |
| 289 | }; |
| 290 | |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 291 | #else |
Xiong Zhou | 9964e88 | 2017-08-16 15:15:36 +0800 | [diff] [blame] | 292 | TST_TEST_TCONF("system doesn't have required fanotify support"); |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 293 | #endif |