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 work for children of a directory |
| 27 | */ |
| 28 | #include "config.h" |
| 29 | |
| 30 | #include <stdio.h> |
| 31 | #include <sys/stat.h> |
| 32 | #include <sys/types.h> |
| 33 | #include <sys/fcntl.h> |
| 34 | #include <errno.h> |
| 35 | #include <string.h> |
| 36 | #include <sys/syscall.h> |
| 37 | #include "test.h" |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 38 | #include "linux_syscall_numbers.h" |
| 39 | #include "fanotify.h" |
| 40 | #include "safe_macros.h" |
| 41 | |
| 42 | char *TCID = "fanotify02"; |
| 43 | int TST_TOTAL = 8; |
| 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 | #define BUF_SIZE 256 |
| 58 | static char fname[BUF_SIZE]; |
| 59 | static char buf[BUF_SIZE]; |
| 60 | static int fd, fd_notify; |
| 61 | |
| 62 | static unsigned long long event_set[EVENT_MAX]; |
| 63 | |
| 64 | static char event_buf[EVENT_BUF_LEN]; |
| 65 | |
| 66 | int main(int ac, char **av) |
| 67 | { |
| 68 | int lc; |
Cyril Hrubis | 0b9589f | 2014-05-27 17:40:33 +0200 | [diff] [blame] | 69 | const char *msg; |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 70 | |
| 71 | if ((msg = parse_opts(ac, av, NULL, NULL)) != NULL) |
| 72 | tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg); |
| 73 | |
| 74 | setup(); |
| 75 | |
| 76 | for (lc = 0; TEST_LOOPING(lc); lc++) { |
| 77 | int ret, len, i = 0, test_num = 0; |
| 78 | |
| 79 | tst_count = 0; |
| 80 | |
Helge Deller | ff96476 | 2014-07-29 18:10:40 +0200 | [diff] [blame] | 81 | if (fanotify_mark(fd_notify, FAN_MARK_ADD, FAN_ACCESS | |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 82 | FAN_MODIFY | FAN_CLOSE | FAN_OPEN | |
| 83 | FAN_EVENT_ON_CHILD, AT_FDCWD, ".") < 0) { |
| 84 | tst_brkm(TBROK | TERRNO, cleanup, |
| 85 | "fanotify_mark (%d, FAN_MARK_ADD, FAN_ACCESS | " |
| 86 | "FAN_MODIFY | FAN_CLOSE | FAN_OPEN | " |
| 87 | "FAN_EVENT_ON_CHILD, AT_FDCWD, '.') failed", |
| 88 | fd_notify); |
| 89 | } |
| 90 | |
| 91 | /* |
| 92 | * generate sequence of events |
| 93 | */ |
| 94 | fd = SAFE_OPEN(cleanup, fname, O_RDWR | O_CREAT, 0700); |
| 95 | event_set[tst_count] = FAN_OPEN; |
| 96 | tst_count++; |
| 97 | |
| 98 | SAFE_WRITE(cleanup, 1, fd, fname, strlen(fname)); |
| 99 | event_set[tst_count] = FAN_MODIFY; |
| 100 | tst_count++; |
| 101 | |
| 102 | SAFE_CLOSE(cleanup, fd); |
| 103 | event_set[tst_count] = FAN_CLOSE_WRITE; |
| 104 | tst_count++; |
| 105 | |
| 106 | /* |
| 107 | * Get list of events so far. We get events here to avoid |
| 108 | * merging of following events with the previous ones. |
| 109 | */ |
| 110 | ret = SAFE_READ(cleanup, 0, fd_notify, event_buf, |
| 111 | EVENT_BUF_LEN); |
| 112 | len = ret; |
| 113 | |
| 114 | fd = SAFE_OPEN(cleanup, fname, O_RDONLY); |
| 115 | event_set[tst_count] = FAN_OPEN; |
| 116 | tst_count++; |
| 117 | |
| 118 | SAFE_READ(cleanup, 0, fd, buf, BUF_SIZE); |
| 119 | event_set[tst_count] = FAN_ACCESS; |
| 120 | tst_count++; |
| 121 | |
| 122 | SAFE_CLOSE(cleanup, fd); |
| 123 | event_set[tst_count] = FAN_CLOSE_NOWRITE; |
| 124 | tst_count++; |
| 125 | |
| 126 | /* |
| 127 | * get next events |
| 128 | */ |
| 129 | ret = SAFE_READ(cleanup, 0, fd_notify, event_buf + len, |
| 130 | EVENT_BUF_LEN - len); |
| 131 | len += ret; |
| 132 | |
| 133 | /* |
| 134 | * now remove child mark |
| 135 | */ |
Helge Deller | ff96476 | 2014-07-29 18:10:40 +0200 | [diff] [blame] | 136 | if (fanotify_mark(fd_notify, FAN_MARK_REMOVE, |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 137 | FAN_EVENT_ON_CHILD, AT_FDCWD, ".") < 0) { |
| 138 | tst_brkm(TBROK | TERRNO, cleanup, |
| 139 | "fanotify_mark (%d, FAN_MARK REMOVE, " |
| 140 | "FAN_EVENT_ON_CHILD, AT_FDCWD, '.') failed", |
| 141 | fd_notify); |
| 142 | } |
| 143 | |
| 144 | /* |
| 145 | * Do something to verify events didn't get generated |
| 146 | */ |
| 147 | fd = SAFE_OPEN(cleanup, fname, O_RDONLY); |
| 148 | |
| 149 | SAFE_CLOSE(cleanup, fd); |
| 150 | |
| 151 | fd = SAFE_OPEN(cleanup, ".", O_RDONLY | O_DIRECTORY); |
| 152 | event_set[tst_count] = FAN_OPEN; |
| 153 | tst_count++; |
| 154 | |
| 155 | SAFE_CLOSE(cleanup, fd); |
| 156 | event_set[tst_count] = FAN_CLOSE_NOWRITE; |
| 157 | tst_count++; |
| 158 | |
| 159 | /* |
| 160 | * Check events got generated only for the directory |
| 161 | */ |
| 162 | ret = SAFE_READ(cleanup, 0, fd_notify, event_buf + len, |
| 163 | EVENT_BUF_LEN - len); |
| 164 | len += ret; |
| 165 | |
| 166 | if (TST_TOTAL != tst_count) { |
| 167 | tst_brkm(TBROK, cleanup, |
| 168 | "TST_TOTAL and tst_count are not equal"); |
| 169 | } |
| 170 | tst_count = 0; |
| 171 | |
| 172 | /* |
| 173 | * check events |
| 174 | */ |
| 175 | while (i < len) { |
| 176 | struct fanotify_event_metadata *event; |
| 177 | |
| 178 | event = (struct fanotify_event_metadata *)&event_buf[i]; |
| 179 | if (test_num >= TST_TOTAL) { |
| 180 | tst_resm(TFAIL, |
| 181 | "get unnecessary event: mask=%llx " |
| 182 | "pid=%u fd=%u", |
| 183 | (unsigned long long)event->mask, |
| 184 | (unsigned)event->pid, event->fd); |
| 185 | } else if (!(event->mask & event_set[test_num])) { |
| 186 | tst_resm(TFAIL, |
| 187 | "get event: mask=%llx (expected %llx) " |
| 188 | "pid=%u fd=%u", |
| 189 | (unsigned long long)event->mask, |
| 190 | event_set[test_num], |
| 191 | (unsigned)event->pid, event->fd); |
| 192 | } else if (event->pid != getpid()) { |
| 193 | tst_resm(TFAIL, |
| 194 | "get event: mask=%llx pid=%u " |
| 195 | "(expected %u) fd=%u", |
| 196 | (unsigned long long)event->mask, |
| 197 | (unsigned)event->pid, |
| 198 | (unsigned)getpid(), |
| 199 | event->fd); |
| 200 | } else { |
| 201 | tst_resm(TPASS, |
| 202 | "get event: mask=%llx pid=%u fd=%u", |
| 203 | (unsigned long long)event->mask, |
| 204 | (unsigned)event->pid, event->fd); |
| 205 | } |
| 206 | event->mask &= ~event_set[test_num]; |
| 207 | /* No events left in current mask? Go for next event */ |
| 208 | if (event->mask == 0) { |
| 209 | i += event->event_len; |
| 210 | close(event->fd); |
| 211 | } |
| 212 | test_num++; |
| 213 | } |
| 214 | for (; test_num < TST_TOTAL; test_num++) { |
| 215 | tst_resm(TFAIL, "didn't get event: mask=%llx", |
| 216 | event_set[test_num]); |
| 217 | |
| 218 | } |
| 219 | } |
| 220 | |
| 221 | cleanup(); |
| 222 | tst_exit(); |
| 223 | } |
| 224 | |
| 225 | static void setup(void) |
| 226 | { |
| 227 | tst_sig(NOFORK, DEF_HANDLER, cleanup); |
| 228 | |
| 229 | TEST_PAUSE; |
| 230 | |
| 231 | tst_tmpdir(); |
| 232 | sprintf(fname, "fname_%d", getpid()); |
| 233 | |
Helge Deller | ff96476 | 2014-07-29 18:10:40 +0200 | [diff] [blame] | 234 | if ((fd_notify = fanotify_init(FAN_CLASS_NOTIF, O_RDONLY)) < 0) { |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 235 | if (errno == ENOSYS) { |
| 236 | tst_brkm(TCONF, cleanup, |
| 237 | "fanotify is not configured in this kernel."); |
| 238 | } else { |
| 239 | tst_brkm(TBROK | TERRNO, cleanup, |
| 240 | "fanotify_init failed"); |
| 241 | } |
| 242 | } |
| 243 | } |
| 244 | |
| 245 | static void cleanup(void) |
| 246 | { |
| 247 | if (close(fd_notify) == -1) |
| 248 | tst_resm(TWARN, "close(%d) failed", fd_notify); |
| 249 | |
Jan Kara | f057c1c | 2013-11-20 23:43:13 +0100 | [diff] [blame] | 250 | tst_rmdir(); |
| 251 | } |
| 252 | |
| 253 | #else |
| 254 | |
| 255 | int main(void) |
| 256 | { |
| 257 | tst_brkm(TCONF, NULL, "system doesn't have required fanotify support"); |
| 258 | } |
| 259 | |
| 260 | #endif |