1 /* 2 * Copyright (c) 2014 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 properly merges ignore mask of an inode and 27 * mountpoint. 28 * 29 * This is a regression test for: 30 * 31 * commit 8edc6e1688fc8f02c8c1f53a2ec4928cb1055f4d 32 * Author: Jan Kara <jack@suse.cz> 33 * Date: Thu Nov 13 15:19:33 2014 -0800 34 * 35 * fanotify: fix notification of groups with inode & mount marks 36 */ 37 #define _GNU_SOURCE 38 #include "config.h" 39 40 #include <stdio.h> 41 #include <sys/stat.h> 42 #include <sys/types.h> 43 #include <fcntl.h> 44 #include <errno.h> 45 #include <string.h> 46 #include <sys/mount.h> 47 #include <sys/syscall.h> 48 #include "tst_test.h" 49 #include "fanotify.h" 50 51 #if defined(HAVE_SYS_FANOTIFY_H) 52 #include <sys/fanotify.h> 53 54 #define EVENT_MAX 1024 55 /* size of the event structure, not counting name */ 56 #define EVENT_SIZE (sizeof (struct fanotify_event_metadata)) 57 /* reasonable guess as to size of 1024 events */ 58 #define EVENT_BUF_LEN (EVENT_MAX * EVENT_SIZE) 59 60 static void setup(void); 61 static void cleanup(void); 62 63 unsigned int fanotify_prio[] = { 64 FAN_CLASS_PRE_CONTENT, 65 FAN_CLASS_CONTENT, 66 FAN_CLASS_NOTIF 67 }; 68 #define FANOTIFY_PRIORITIES ARRAY_SIZE(fanotify_prio) 69 70 #define GROUPS_PER_PRIO 3 71 72 #define BUF_SIZE 256 73 static char fname[BUF_SIZE]; 74 static int fd; 75 static int fd_notify[FANOTIFY_PRIORITIES][GROUPS_PER_PRIO]; 76 77 static char event_buf[EVENT_BUF_LEN]; 78 79 #define MOUNT_NAME "mntpoint" 80 static int mount_created; 81 82 static void create_fanotify_groups(void) 83 { 84 unsigned int p, i; 85 int ret; 86 87 for (p = 0; p < FANOTIFY_PRIORITIES; p++) { 88 for (i = 0; i < GROUPS_PER_PRIO; i++) { 89 fd_notify[p][i] = SAFE_FANOTIFY_INIT(fanotify_prio[p] | 90 FAN_NONBLOCK, 91 O_RDONLY); 92 93 /* Add mount mark for each group */ 94 ret = fanotify_mark(fd_notify[p][i], 95 FAN_MARK_ADD | FAN_MARK_MOUNT, 96 FAN_MODIFY, 97 AT_FDCWD, "."); 98 if (ret < 0) { 99 tst_brk(TBROK | TERRNO, 100 "fanotify_mark(%d, FAN_MARK_ADD | " 101 "FAN_MARK_MOUNT, FAN_MODIFY, AT_FDCWD," 102 " '.') failed", fd_notify[p][i]); 103 } 104 /* Add ignore mark for groups with higher priority */ 105 if (p == 0) 106 continue; 107 ret = fanotify_mark(fd_notify[p][i], 108 FAN_MARK_ADD | 109 FAN_MARK_IGNORED_MASK | 110 FAN_MARK_IGNORED_SURV_MODIFY, 111 FAN_MODIFY, AT_FDCWD, fname); 112 if (ret < 0) { 113 tst_brk(TBROK | TERRNO, 114 "fanotify_mark(%d, FAN_MARK_ADD | " 115 "FAN_MARK_IGNORED_MASK | " 116 "FAN_MARK_IGNORED_SURV_MODIFY, " 117 "FAN_MODIFY, AT_FDCWD, %s) failed", 118 fd_notify[p][i], fname); 119 } 120 } 121 } 122 } 123 124 static void cleanup_fanotify_groups(void) 125 { 126 unsigned int i, p; 127 128 for (p = 0; p < FANOTIFY_PRIORITIES; p++) { 129 for (i = 0; i < GROUPS_PER_PRIO; i++) { 130 if (fd_notify[p][i] && fd_notify[p][i] != -1) { 131 if (close(fd_notify[p][i]) == -1) 132 tst_res(TWARN, "close(%d) failed", 133 fd_notify[p][i]); 134 fd_notify[p][i] = 0; 135 } 136 } 137 } 138 } 139 140 static void verify_event(int group, struct fanotify_event_metadata *event) 141 { 142 if (event->mask != FAN_MODIFY) { 143 tst_res(TFAIL, "group %d get event: mask %llx (expected %llx) " 144 "pid=%u fd=%u", group, (unsigned long long)event->mask, 145 (unsigned long long)FAN_MODIFY, 146 (unsigned)event->pid, event->fd); 147 } else if (event->pid != getpid()) { 148 tst_res(TFAIL, "group %d get event: mask %llx pid=%u " 149 "(expected %u) fd=%u", group, 150 (unsigned long long)event->mask, (unsigned)event->pid, 151 (unsigned)getpid(), event->fd); 152 } else { 153 tst_res(TPASS, "group %d get event: mask %llx pid=%u fd=%u", 154 group, (unsigned long long)event->mask, 155 (unsigned)event->pid, event->fd); 156 } 157 } 158 159 void test01(void) 160 { 161 int ret; 162 unsigned int p, i; 163 struct fanotify_event_metadata *event; 164 165 create_fanotify_groups(); 166 167 /* 168 * generate sequence of events 169 */ 170 fd = SAFE_OPEN(fname, O_RDWR); 171 SAFE_WRITE(1, fd, fname, strlen(fname)); 172 SAFE_CLOSE(fd); 173 174 /* First verify all groups without ignore mask got the event */ 175 for (i = 0; i < GROUPS_PER_PRIO; i++) { 176 ret = read(fd_notify[0][i], event_buf, EVENT_BUF_LEN); 177 if (ret < 0) { 178 if (errno == EAGAIN) { 179 tst_res(TFAIL, "group %d did not get " 180 "event", i); 181 } 182 tst_brk(TBROK | TERRNO, 183 "reading fanotify events failed"); 184 } 185 if (ret < (int)FAN_EVENT_METADATA_LEN) { 186 tst_brk(TBROK, 187 "short read when reading fanotify " 188 "events (%d < %d)", ret, 189 (int)EVENT_BUF_LEN); 190 } 191 event = (struct fanotify_event_metadata *)event_buf; 192 if (ret > (int)event->event_len) { 193 tst_res(TFAIL, "group %d got more than one " 194 "event (%d > %d)", i, ret, 195 event->event_len); 196 } else 197 verify_event(i, event); 198 close(event->fd); 199 } 200 for (p = 1; p < FANOTIFY_PRIORITIES; p++) { 201 for (i = 0; i < GROUPS_PER_PRIO; i++) { 202 ret = read(fd_notify[p][i], event_buf, EVENT_BUF_LEN); 203 if (ret > 0) { 204 tst_res(TFAIL, "group %d got event", 205 p*GROUPS_PER_PRIO + i); 206 } else if (ret == 0) { 207 tst_brk(TBROK, "zero length " 208 "read from fanotify fd"); 209 } else if (errno != EAGAIN) { 210 tst_brk(TBROK | TERRNO, 211 "reading fanotify events failed"); 212 } else { 213 tst_res(TPASS, "group %d got no event", 214 p*GROUPS_PER_PRIO + i); 215 } 216 } 217 } 218 cleanup_fanotify_groups(); 219 } 220 221 static void setup(void) 222 { 223 SAFE_MKDIR(MOUNT_NAME, 0755); 224 SAFE_MOUNT(MOUNT_NAME, MOUNT_NAME, NULL, MS_BIND, NULL); 225 mount_created = 1; 226 SAFE_CHDIR(MOUNT_NAME); 227 228 sprintf(fname, "tfile_%d", getpid()); 229 fd = SAFE_OPEN(fname, O_RDWR | O_CREAT, 0700); 230 SAFE_WRITE(1, fd, fname, 1); 231 232 /* close the file we have open */ 233 SAFE_CLOSE(fd); 234 } 235 236 static void cleanup(void) 237 { 238 cleanup_fanotify_groups(); 239 240 SAFE_CHDIR("../"); 241 242 if (mount_created && tst_umount(MOUNT_NAME) < 0) 243 tst_brk(TBROK | TERRNO, "umount failed"); 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