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