Jan Kara | b432e8b | 2014-11-10 18:11:58 +0100 | [diff] [blame] | 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 | #include "config.h" |
| 30 | |
| 31 | #include <stdio.h> |
| 32 | #include <sys/stat.h> |
| 33 | #include <sys/types.h> |
| 34 | #include <sys/fcntl.h> |
| 35 | #include <errno.h> |
| 36 | #include <string.h> |
| 37 | #include <sys/syscall.h> |
| 38 | #include "test.h" |
Jan Kara | b432e8b | 2014-11-10 18:11:58 +0100 | [diff] [blame] | 39 | #include "linux_syscall_numbers.h" |
| 40 | #include "fanotify.h" |
| 41 | #include "safe_macros.h" |
| 42 | |
| 43 | char *TCID = "fanotify06"; |
| 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 | |
| 54 | static void setup(void); |
| 55 | static void cleanup(void); |
| 56 | |
| 57 | unsigned int fanotify_prio[] = { |
| 58 | FAN_CLASS_PRE_CONTENT, |
| 59 | FAN_CLASS_CONTENT, |
| 60 | FAN_CLASS_NOTIF |
| 61 | }; |
| 62 | #define FANOTIFY_PRIORITIES ARRAY_SIZE(fanotify_prio) |
| 63 | |
| 64 | #define GROUPS_PER_PRIO 3 |
| 65 | |
| 66 | int TST_TOTAL = GROUPS_PER_PRIO * FANOTIFY_PRIORITIES; |
| 67 | |
| 68 | #define BUF_SIZE 256 |
| 69 | static char fname[BUF_SIZE]; |
| 70 | static int fd; |
| 71 | static int fd_notify[FANOTIFY_PRIORITIES][GROUPS_PER_PRIO]; |
| 72 | |
| 73 | static char event_buf[EVENT_BUF_LEN]; |
| 74 | |
| 75 | static void create_fanotify_groups(void) |
| 76 | { |
| 77 | unsigned int p, i; |
| 78 | int ret; |
| 79 | |
| 80 | for (p = 0; p < FANOTIFY_PRIORITIES; p++) { |
| 81 | for (i = 0; i < GROUPS_PER_PRIO; i++) { |
| 82 | fd_notify[p][i] = fanotify_init(fanotify_prio[p] | |
| 83 | FAN_NONBLOCK, |
| 84 | O_RDONLY); |
| 85 | if (fd_notify[p][i] < 0) { |
| 86 | if (errno == ENOSYS) { |
| 87 | tst_brkm(TCONF, cleanup, |
| 88 | "fanotify is not configured in" |
| 89 | " this kernel."); |
| 90 | } else { |
| 91 | tst_brkm(TBROK | TERRNO, cleanup, |
| 92 | "fanotify_init failed"); |
| 93 | } |
| 94 | } |
| 95 | /* Add mount mark for each group */ |
| 96 | ret = fanotify_mark(fd_notify[p][i], |
| 97 | FAN_MARK_ADD | FAN_MARK_MOUNT, |
| 98 | FAN_MODIFY, |
| 99 | AT_FDCWD, "."); |
| 100 | if (ret < 0) { |
| 101 | tst_brkm(TBROK | TERRNO, cleanup, |
| 102 | "fanotify_mark(%d, FAN_MARK_ADD | " |
| 103 | "FAN_MARK_MOUNT, FAN_MODIFY, AT_FDCWD," |
| 104 | " '.') failed", fd_notify[p][i]); |
| 105 | } |
| 106 | /* Add ignore mark for groups with higher priority */ |
| 107 | if (p == 0) |
| 108 | continue; |
| 109 | ret = fanotify_mark(fd_notify[p][i], |
| 110 | FAN_MARK_ADD | |
| 111 | FAN_MARK_IGNORED_MASK | |
| 112 | FAN_MARK_IGNORED_SURV_MODIFY, |
| 113 | FAN_MODIFY, AT_FDCWD, fname); |
| 114 | if (ret < 0) { |
| 115 | tst_brkm(TBROK | TERRNO, cleanup, |
| 116 | "fanotify_mark(%d, FAN_MARK_ADD | " |
| 117 | "FAN_MARK_IGNORED_MASK | " |
| 118 | "FAN_MARK_IGNORED_SURV_MODIFY, " |
| 119 | "FAN_MODIFY, AT_FDCWD, %s) failed", |
| 120 | fd_notify[p][i], fname); |
| 121 | } |
| 122 | } |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | static void cleanup_fanotify_groups(void) |
| 127 | { |
| 128 | unsigned int i, p; |
| 129 | |
| 130 | for (p = 0; p < FANOTIFY_PRIORITIES; p++) { |
| 131 | for (i = 0; i < GROUPS_PER_PRIO; i++) { |
| 132 | if (fd_notify[p][i] && fd_notify[p][i] != -1) { |
| 133 | if (close(fd_notify[p][i]) == -1) |
| 134 | tst_resm(TWARN, "close(%d) failed", |
| 135 | fd_notify[p][i]); |
| 136 | fd_notify[p][i] = 0; |
| 137 | } |
| 138 | } |
| 139 | } |
| 140 | } |
| 141 | |
| 142 | static void verify_event(int group, struct fanotify_event_metadata *event) |
| 143 | { |
| 144 | if (event->mask != FAN_MODIFY) { |
| 145 | tst_resm(TFAIL, "group %d get event: mask %llx (expected %llx) " |
| 146 | "pid=%u fd=%u", group, (unsigned long long)event->mask, |
| 147 | (unsigned long long)FAN_MODIFY, |
| 148 | (unsigned)event->pid, event->fd); |
| 149 | } else if (event->pid != getpid()) { |
| 150 | tst_resm(TFAIL, "group %d get event: mask %llx pid=%u " |
| 151 | "(expected %u) fd=%u", group, |
| 152 | (unsigned long long)event->mask, (unsigned)event->pid, |
| 153 | (unsigned)getpid(), event->fd); |
| 154 | } else { |
| 155 | tst_resm(TPASS, "group %d get event: mask %llx pid=%u fd=%u", |
| 156 | group, (unsigned long long)event->mask, |
| 157 | (unsigned)event->pid, event->fd); |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | int main(int ac, char **av) |
| 162 | { |
| 163 | int lc; |
| 164 | const char *msg; |
| 165 | |
| 166 | if ((msg = parse_opts(ac, av, NULL, NULL)) != NULL) |
| 167 | tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg); |
| 168 | |
| 169 | setup(); |
| 170 | |
| 171 | for (lc = 0; TEST_LOOPING(lc); lc++) { |
| 172 | int ret; |
| 173 | unsigned int p, i; |
| 174 | struct fanotify_event_metadata *event; |
| 175 | |
| 176 | create_fanotify_groups(); |
| 177 | |
| 178 | /* |
| 179 | * generate sequence of events |
| 180 | */ |
| 181 | fd = SAFE_OPEN(cleanup, fname, O_RDWR); |
| 182 | SAFE_WRITE(cleanup, 1, fd, fname, strlen(fname)); |
| 183 | SAFE_CLOSE(cleanup, fd); |
| 184 | |
| 185 | /* First verify all groups without ignore mask got the event */ |
| 186 | for (i = 0; i < GROUPS_PER_PRIO; i++) { |
| 187 | ret = read(fd_notify[0][i], event_buf, EVENT_BUF_LEN); |
| 188 | if (ret < 0) { |
| 189 | if (errno == EAGAIN) { |
| 190 | tst_resm(TFAIL, "group %d did not get " |
| 191 | "event", i); |
| 192 | } |
| 193 | tst_brkm(TBROK | TERRNO, cleanup, |
| 194 | "reading fanotify events failed"); |
| 195 | } |
| 196 | if (ret < (int)FAN_EVENT_METADATA_LEN) { |
| 197 | tst_brkm(TBROK, cleanup, |
| 198 | "short read when reading fanotify " |
| 199 | "events (%d < %d)", ret, |
| 200 | (int)EVENT_BUF_LEN); |
| 201 | } |
| 202 | event = (struct fanotify_event_metadata *)event_buf; |
| 203 | if (ret > (int)event->event_len) { |
| 204 | tst_resm(TFAIL, "group %d got more than one " |
| 205 | "event (%d > %d)", i, ret, |
| 206 | event->event_len); |
| 207 | } else |
| 208 | verify_event(i, event); |
| 209 | close(event->fd); |
| 210 | } |
| 211 | for (p = 1; p < FANOTIFY_PRIORITIES; p++) { |
| 212 | for (i = 0; i < GROUPS_PER_PRIO; i++) { |
| 213 | ret = read(fd_notify[p][i], event_buf, EVENT_BUF_LEN); |
| 214 | if (ret > 0) { |
| 215 | tst_resm(TFAIL, "group %d got event", |
| 216 | p*GROUPS_PER_PRIO + i); |
| 217 | } else if (ret == 0) { |
| 218 | tst_brkm(TBROK, cleanup, "zero length " |
| 219 | "read from fanotify fd"); |
| 220 | } else if (errno != EAGAIN) { |
| 221 | tst_brkm(TBROK | TERRNO, cleanup, |
| 222 | "reading fanotify events failed"); |
| 223 | } else { |
| 224 | tst_resm(TPASS, "group %d got no event", |
| 225 | p*GROUPS_PER_PRIO + i); |
| 226 | } |
| 227 | } |
| 228 | } |
| 229 | cleanup_fanotify_groups(); |
| 230 | } |
| 231 | |
| 232 | cleanup(); |
| 233 | tst_exit(); |
| 234 | } |
| 235 | |
| 236 | static void setup(void) |
| 237 | { |
| 238 | tst_sig(NOFORK, DEF_HANDLER, cleanup); |
| 239 | |
| 240 | TEST_PAUSE; |
| 241 | |
| 242 | tst_tmpdir(); |
| 243 | |
| 244 | sprintf(fname, "tfile_%d", getpid()); |
| 245 | fd = SAFE_OPEN(cleanup, fname, O_RDWR | O_CREAT, 0700); |
| 246 | SAFE_WRITE(cleanup, 1, fd, fname, 1); |
| 247 | |
| 248 | /* close the file we have open */ |
| 249 | SAFE_CLOSE(cleanup, fd); |
| 250 | } |
| 251 | |
| 252 | static void cleanup(void) |
| 253 | { |
| 254 | cleanup_fanotify_groups(); |
Jan Kara | b432e8b | 2014-11-10 18:11:58 +0100 | [diff] [blame] | 255 | tst_rmdir(); |
| 256 | } |
| 257 | |
| 258 | #else |
| 259 | |
| 260 | int main(void) |
| 261 | { |
| 262 | tst_brkm(TCONF, NULL, "system doesn't have required fanotify support"); |
| 263 | } |
| 264 | |
| 265 | #endif |