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 a file 27 */ 28#define _GNU_SOURCE 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" 39#include "linux_syscall_numbers.h" 40#include "fanotify.h" 41#include "safe_macros.h" 42 43char *TCID = "fanotify01"; 44int TST_TOTAL = 12; 45 46#if defined(HAVE_SYS_FANOTIFY_H) 47#include <sys/fanotify.h> 48 49#define EVENT_MAX 1024 50/* size of the event structure, not counting name */ 51#define EVENT_SIZE (sizeof (struct fanotify_event_metadata)) 52/* reasonable guess as to size of 1024 events */ 53#define EVENT_BUF_LEN (EVENT_MAX * EVENT_SIZE) 54 55static void setup(void); 56static void cleanup(void); 57 58#define BUF_SIZE 256 59static char fname[BUF_SIZE]; 60static char buf[BUF_SIZE]; 61static int fd, fd_notify; 62 63static unsigned long long event_set[EVENT_MAX]; 64 65static char event_buf[EVENT_BUF_LEN]; 66 67int main(int ac, char **av) 68{ 69 int lc; 70 71 tst_parse_opts(ac, av, NULL, NULL); 72 73 setup(); 74 75 for (lc = 0; TEST_LOOPING(lc); lc++) { 76 int ret, len, i = 0, test_num = 0; 77 78 tst_count = 0; 79 80 if (fanotify_mark(fd_notify, FAN_MARK_ADD, FAN_ACCESS | FAN_MODIFY | 81 FAN_CLOSE | FAN_OPEN, AT_FDCWD, fname) < 0) { 82 tst_brkm(TBROK | TERRNO, cleanup, 83 "fanotify_mark (%d, FAN_MARK_ADD, FAN_ACCESS | " 84 "FAN_MODIFY | FAN_CLOSE | FAN_OPEN, AT_FDCWD, %s) " 85 "failed", fd_notify, fname); 86 } 87 88 /* 89 * generate sequence of events 90 */ 91 fd = SAFE_OPEN(cleanup, fname, O_RDONLY); 92 event_set[tst_count] = FAN_OPEN; 93 tst_count++; 94 95 SAFE_READ(cleanup, 0, fd, buf, BUF_SIZE); 96 event_set[tst_count] = FAN_ACCESS; 97 tst_count++; 98 99 SAFE_CLOSE(cleanup, fd); 100 event_set[tst_count] = FAN_CLOSE_NOWRITE; 101 tst_count++; 102 103 /* 104 * Get list of events so far. We get events here to avoid 105 * merging of following events with the previous ones. 106 */ 107 ret = SAFE_READ(cleanup, 0, fd_notify, event_buf, EVENT_BUF_LEN); 108 len = ret; 109 110 fd = SAFE_OPEN(cleanup, fname, O_RDWR | O_CREAT, 0700); 111 event_set[tst_count] = FAN_OPEN; 112 tst_count++; 113 114 SAFE_WRITE(cleanup, 1, fd, fname, strlen(fname)); 115 event_set[tst_count] = FAN_MODIFY; 116 tst_count++; 117 118 SAFE_CLOSE(cleanup, fd); 119 event_set[tst_count] = FAN_CLOSE_WRITE; 120 tst_count++; 121 122 /* 123 * get another list of events 124 */ 125 ret = SAFE_READ(cleanup, 0, fd_notify, event_buf + len, 126 EVENT_BUF_LEN - len); 127 len += ret; 128 129 /* 130 * Ignore mask testing 131 */ 132 133 /* Ignore access events */ 134 if (fanotify_mark(fd_notify, 135 FAN_MARK_ADD | FAN_MARK_IGNORED_MASK, 136 FAN_ACCESS, AT_FDCWD, fname) < 0) { 137 tst_brkm(TBROK | TERRNO, cleanup, 138 "fanotify_mark (%d, FAN_MARK_ADD | " 139 "FAN_MARK_IGNORED_MASK, FAN_ACCESS, " 140 "AT_FDCWD, %s) failed", fd_notify, fname); 141 } 142 143 fd = SAFE_OPEN(cleanup, fname, O_RDWR); 144 event_set[tst_count] = FAN_OPEN; 145 tst_count++; 146 147 /* This event should be ignored */ 148 SAFE_READ(cleanup, 0, fd, buf, BUF_SIZE); 149 150 /* 151 * get another list of events to verify the last one got ignored 152 */ 153 ret = SAFE_READ(cleanup, 0, fd_notify, event_buf + len, 154 EVENT_BUF_LEN - len); 155 len += ret; 156 157 lseek(fd, 0, SEEK_SET); 158 /* Generate modify event to clear ignore mask */ 159 SAFE_WRITE(cleanup, 1, fd, fname, 1); 160 event_set[tst_count] = FAN_MODIFY; 161 tst_count++; 162 163 /* 164 * This event shouldn't be ignored because previous modification 165 * should have removed the ignore mask 166 */ 167 SAFE_READ(cleanup, 0, fd, buf, BUF_SIZE); 168 event_set[tst_count] = FAN_ACCESS; 169 tst_count++; 170 171 SAFE_CLOSE(cleanup, fd); 172 event_set[tst_count] = FAN_CLOSE_WRITE; 173 tst_count++; 174 175 /* Read events to verify previous access was properly generated */ 176 ret = SAFE_READ(cleanup, 0, fd_notify, event_buf + len, 177 EVENT_BUF_LEN - len); 178 len += ret; 179 180 /* 181 * Now ignore open & close events regardless of file 182 * modifications 183 */ 184 if (fanotify_mark(fd_notify, 185 FAN_MARK_ADD | FAN_MARK_IGNORED_MASK | FAN_MARK_IGNORED_SURV_MODIFY, 186 FAN_OPEN | FAN_CLOSE, AT_FDCWD, fname) < 0) { 187 tst_brkm(TBROK | TERRNO, cleanup, 188 "fanotify_mark (%d, FAN_MARK_ADD | " 189 "FAN_MARK_IGNORED_MASK | " 190 "FAN_MARK_IGNORED_SURV_MODIFY, FAN_OPEN | " 191 "FAN_CLOSE, AT_FDCWD, %s) failed", fd_notify, 192 fname); 193 } 194 195 /* This event should be ignored */ 196 fd = SAFE_OPEN(cleanup, fname, O_RDWR); 197 198 SAFE_WRITE(cleanup, 1, fd, fname, 1); 199 event_set[tst_count] = FAN_MODIFY; 200 tst_count++; 201 202 /* This event should be still ignored */ 203 SAFE_CLOSE(cleanup, fd); 204 205 /* This event should still be ignored */ 206 fd = SAFE_OPEN(cleanup, fname, O_RDWR); 207 208 /* Read events to verify open & close were ignored */ 209 ret = SAFE_READ(cleanup, 0, fd_notify, event_buf + len, 210 EVENT_BUF_LEN - len); 211 len += ret; 212 213 /* Now remove open and close from ignored mask */ 214 if (fanotify_mark(fd_notify, 215 FAN_MARK_REMOVE | FAN_MARK_IGNORED_MASK, 216 FAN_OPEN | FAN_CLOSE, AT_FDCWD, fname) < 0) { 217 tst_brkm(TBROK | TERRNO, cleanup, 218 "fanotify_mark (%d, FAN_MARK_REMOVE | " 219 "FAN_MARK_IGNORED_MASK, FAN_OPEN | " 220 "FAN_CLOSE, AT_FDCWD, %s) failed", fd_notify, 221 fname); 222 } 223 224 SAFE_CLOSE(cleanup, fd); 225 event_set[tst_count] = FAN_CLOSE_WRITE; 226 tst_count++; 227 228 /* Read events to verify close was generated */ 229 ret = SAFE_READ(cleanup, 0, fd_notify, event_buf + len, 230 EVENT_BUF_LEN - len); 231 len += ret; 232 233 if (TST_TOTAL != tst_count) { 234 tst_brkm(TBROK, cleanup, 235 "TST_TOTAL (%d) and tst_count (%d) are not " 236 "equal", TST_TOTAL, tst_count); 237 } 238 tst_count = 0; 239 240 /* 241 * check events 242 */ 243 while (i < len) { 244 struct fanotify_event_metadata *event; 245 246 event = (struct fanotify_event_metadata *)&event_buf[i]; 247 if (test_num >= TST_TOTAL) { 248 tst_resm(TFAIL, 249 "get unnecessary event: mask=%llx " 250 "pid=%u fd=%u", 251 (unsigned long long)event->mask, 252 (unsigned)event->pid, event->fd); 253 } else if (!(event->mask & event_set[test_num])) { 254 tst_resm(TFAIL, 255 "get event: mask=%llx (expected %llx) " 256 "pid=%u fd=%u", 257 (unsigned long long)event->mask, 258 event_set[test_num], 259 (unsigned)event->pid, event->fd); 260 } else if (event->pid != getpid()) { 261 tst_resm(TFAIL, 262 "get event: mask=%llx pid=%u " 263 "(expected %u) fd=%u", 264 (unsigned long long)event->mask, 265 (unsigned)event->pid, 266 (unsigned)getpid(), 267 event->fd); 268 } else { 269 if (event->fd == -2) 270 goto pass; 271 ret = read(event->fd, buf, BUF_SIZE); 272 if (ret != strlen(fname)) { 273 tst_resm(TFAIL, 274 "cannot read from returned fd " 275 "of event: mask=%llx pid=%u " 276 "fd=%u ret=%d (errno=%d)", 277 (unsigned long long)event->mask, 278 (unsigned)event->pid, 279 event->fd, ret, errno); 280 } else if (memcmp(buf, fname, strlen(fname))) { 281 tst_resm(TFAIL, 282 "wrong data read from returned fd " 283 "of event: mask=%llx pid=%u " 284 "fd=%u", 285 (unsigned long long)event->mask, 286 (unsigned)event->pid, 287 event->fd); 288 } else { 289pass: 290 tst_resm(TPASS, 291 "get event: mask=%llx pid=%u fd=%u", 292 (unsigned long long)event->mask, 293 (unsigned)event->pid, event->fd); 294 } 295 } 296 /* 297 * We have verified the data now so close fd and 298 * invalidate it so that we don't check it again 299 * unnecessarily 300 */ 301 close(event->fd); 302 event->fd = -2; 303 event->mask &= ~event_set[test_num]; 304 /* No events left in current mask? Go for next event */ 305 if (event->mask == 0) { 306 i += event->event_len; 307 } 308 test_num++; 309 } 310 for (; test_num < TST_TOTAL; test_num++) { 311 tst_resm(TFAIL, "didn't get event: mask=%llx", 312 event_set[test_num]); 313 314 } 315 /* Remove mark to clear FAN_MARK_IGNORED_SURV_MODIFY */ 316 if (fanotify_mark(fd_notify, FAN_MARK_REMOVE, FAN_ACCESS | FAN_MODIFY | 317 FAN_CLOSE | FAN_OPEN, AT_FDCWD, fname) < 0) { 318 tst_brkm(TBROK | TERRNO, cleanup, 319 "fanotify_mark (%d, FAN_MARK_REMOVE, FAN_ACCESS | " 320 "FAN_MODIFY | FAN_CLOSE | FAN_OPEN, AT_FDCWD, %s) " 321 "failed", fd_notify, fname); 322 } 323 324 } 325 326 cleanup(); 327 tst_exit(); 328} 329 330static void setup(void) 331{ 332 tst_sig(NOFORK, DEF_HANDLER, cleanup); 333 334 TEST_PAUSE; 335 336 tst_tmpdir(); 337 338 sprintf(fname, "tfile_%d", getpid()); 339 fd = SAFE_OPEN(cleanup, fname, O_RDWR | O_CREAT, 0700); 340 SAFE_WRITE(cleanup, 1, fd, fname, 1); 341 342 /* close the file we have open */ 343 SAFE_CLOSE(cleanup, fd); 344 345 if ((fd_notify = fanotify_init(FAN_CLASS_NOTIF, O_RDONLY)) < 0) { 346 if (errno == ENOSYS) { 347 tst_brkm(TCONF, cleanup, 348 "fanotify is not configured in this kernel."); 349 } else { 350 tst_brkm(TBROK | TERRNO, cleanup, 351 "fanotify_init failed"); 352 } 353 } 354} 355 356static void cleanup(void) 357{ 358 if (fd_notify > 0 && close(fd_notify)) 359 tst_resm(TWARN | TERRNO, "close(%d) failed", fd_notify); 360 361 tst_rmdir(); 362} 363 364#else 365 366int main(void) 367{ 368 tst_brkm(TCONF, NULL, "system doesn't have required fanotify support"); 369} 370 371#endif 372