Home | History | Annotate | Download | only in inotify
      1 /*
      2  * Copyright (c) 2012 Linux Test Project.  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  * Ngie Cooper, April 2012
     24  */
     25 
     26 /****************************************************************************
     27  * DESCRIPTION
     28  *     verify that IN_DELETE_SELF functions as expected
     29  *
     30  * ALGORITHM
     31  *     This testcase creates a temporary directory, then add watches to a
     32  *     predefined file and subdirectory, and delete the file and directory to
     33  *     ensure that the IN_DELETE_SELF event is captured properly.
     34  *
     35  *     Because of how the inotify(7) API is designed, we also need to catch the
     36  *     IN_ATTRIB and IN_IGNORED events.
     37  *
     38  ****************************************************************************/
     39 
     40 #include "config.h"
     41 
     42 #if defined(HAVE_SYS_INOTIFY_H)
     43 #include <sys/inotify.h>
     44 #endif
     45 #include <errno.h>
     46 #include <string.h>
     47 #include "test.h"
     48 #include "lapi/syscalls.h"
     49 #include "inotify.h"
     50 #include "safe_macros.h"
     51 
     52 char *TCID = "inotify04";
     53 int TST_TOTAL = 4;
     54 
     55 #if defined(HAVE_SYS_INOTIFY_H)
     56 
     57 #define EVENT_MAX 1024
     58 /* size of the event structure, not counting name */
     59 #define EVENT_SIZE  (sizeof(struct inotify_event))
     60 /* reasonable guess as to size of 1024 events */
     61 #define EVENT_BUF_LEN        (EVENT_MAX * (EVENT_SIZE + 16))
     62 
     63 
     64 #define BUF_SIZE 256
     65 
     66 struct event_t {
     67 	char name[BUF_SIZE];
     68 	unsigned int mask;
     69 };
     70 
     71 #define	TEST_DIR	"test_dir"
     72 #define	TEST_FILE	"test_file"
     73 
     74 struct event_t event_set[EVENT_MAX];
     75 
     76 char event_buf[EVENT_BUF_LEN];
     77 
     78 int fd_notify, reap_wd_file, reap_wd_dir, wd_dir, wd_file;
     79 
     80 static struct tst_kern_exv kvers[] = {
     81 	{ "RHEL5", "2.6.18-132" },
     82 	{ NULL, NULL },
     83 };
     84 
     85 static void cleanup(void)
     86 {
     87 
     88 	if (reap_wd_dir && myinotify_rm_watch(fd_notify, wd_dir) == -1)
     89 		tst_resm(TWARN,
     90 			 "inotify_rm_watch(%d, %d) [1] failed", fd_notify,
     91 			 wd_dir);
     92 
     93 	if (reap_wd_file && myinotify_rm_watch(fd_notify, wd_file) == -1)
     94 		tst_resm(TWARN,
     95 			 "inotify_rm_watch(%d, %d) [2] failed", fd_notify,
     96 			 wd_file);
     97 
     98 	if (fd_notify > 0 && close(fd_notify))
     99 		tst_resm(TWARN, "close(%d) [1] failed", fd_notify);
    100 
    101 	if (wd_dir > 0 && close(wd_dir))
    102 		tst_resm(TWARN, "close(%d) [2] failed", wd_dir);
    103 
    104 	if (wd_file > 0 && close(wd_file))
    105 		tst_resm(TWARN, "close(%d) [3] failed", wd_file);
    106 
    107 	tst_rmdir();
    108 }
    109 
    110 static void setup(void)
    111 {
    112 
    113 	tst_sig(NOFORK, DEF_HANDLER, cleanup);
    114 
    115 	TEST_PAUSE;
    116 
    117 	tst_tmpdir();
    118 
    119 	fd_notify = myinotify_init();
    120 	if (fd_notify == -1) {
    121 		if (errno == ENOSYS) {
    122 			tst_brkm(TCONF, cleanup,
    123 				 "inotify is not configured in this kernel.");
    124 		} else {
    125 			tst_brkm(TBROK | TERRNO, cleanup,
    126 				 "inotify_init failed");
    127 		}
    128 	}
    129 
    130 	SAFE_MKDIR(cleanup, TEST_DIR, 00700);
    131 
    132 	close(SAFE_CREAT(cleanup, TEST_FILE, 00600));
    133 
    134 	wd_dir = myinotify_add_watch(fd_notify, TEST_DIR, IN_ALL_EVENTS);
    135 	if (wd_dir == -1) {
    136 		tst_brkm(TBROK | TERRNO, cleanup,
    137 			 "inotify_add_watch(%d, \"%s\", IN_ALL_EVENTS) [1] failed",
    138 			 fd_notify, TEST_DIR);
    139 	}
    140 	reap_wd_dir = 1;
    141 
    142 	wd_file = myinotify_add_watch(fd_notify, TEST_FILE, IN_ALL_EVENTS);
    143 	if (wd_file == -1)
    144 		tst_brkm(TBROK | TERRNO, cleanup,
    145 			 "inotify_add_watch(%d, \"%s\", IN_ALL_EVENTS) [2] failed",
    146 			 fd_notify, TEST_FILE);
    147 	reap_wd_file = 1;
    148 }
    149 
    150 int main(int argc, char **argv)
    151 {
    152 	int i, test_num, len;
    153 
    154 	i = 0;
    155 	test_num = 0;
    156 
    157 	tst_parse_opts(argc, argv, NULL, NULL);
    158 
    159 	setup();
    160 
    161 	tst_count = 0;
    162 
    163 	rmdir(TEST_DIR);
    164 	event_set[tst_count].mask = IN_DELETE_SELF;
    165 	strcpy(event_set[tst_count].name, "");
    166 	tst_count++;
    167 	event_set[tst_count].mask = IN_IGNORED;
    168 	strcpy(event_set[tst_count].name, "");
    169 	tst_count++;
    170 
    171 	unlink(TEST_FILE);
    172 	/*
    173 	 * When a file is unlinked, the link count is reduced by 1, and when it
    174 	 * hits 0 the file is removed.
    175 	 *
    176 	 * This isn't well documented in inotify(7), but it's intuitive if you
    177 	 * understand how Unix works.
    178 	 */
    179 	if (tst_kvercmp2(2, 6, 25, kvers) >= 0) {
    180 		event_set[tst_count].mask = IN_ATTRIB;
    181 		strcpy(event_set[tst_count].name, "");
    182 		tst_count++;
    183 		TST_TOTAL++;
    184 	}
    185 	event_set[tst_count].mask = IN_DELETE_SELF;
    186 	strcpy(event_set[tst_count].name, TEST_FILE);
    187 	tst_count++;
    188 	event_set[tst_count].mask = IN_IGNORED;
    189 	strcpy(event_set[tst_count].name, "");
    190 	tst_count++;
    191 
    192 	if (tst_count != TST_TOTAL)
    193 		tst_brkm(TBROK, cleanup,
    194 			 "tst_count and TST_TOTAL are not equal");
    195 
    196 	tst_count = 0;
    197 
    198 	len = read(fd_notify, event_buf, EVENT_BUF_LEN);
    199 	if (len == -1)
    200 		tst_brkm(TBROK | TERRNO, cleanup, "read failed");
    201 
    202 	reap_wd_dir = 0;
    203 	reap_wd_file = 0;
    204 
    205 	while (i < len) {
    206 		struct inotify_event *event;
    207 		event = (struct inotify_event *)&event_buf[i];
    208 		if (test_num >= TST_TOTAL) {
    209 			if (tst_kvercmp(2, 6, 25) < 0
    210 			    && event_set[TST_TOTAL - 1].mask == event->mask)
    211 				tst_resm(TWARN,
    212 					 "This may be kernel bug. "
    213 					 "Before kernel 2.6.25, a kernel bug "
    214 					 "meant that the kernel code that was "
    215 					 "intended to coalesce successive identical "
    216 					 "events (i.e., the two most recent "
    217 					 "events could potentially be coalesced "
    218 					 "if the older had not yet been read) "
    219 					 "instead checked if the most recent event "
    220 					 "could be coalesced with the oldest "
    221 					 "unread event. This has been fixed by commit"
    222 					 "1c17d18e3775485bf1e0ce79575eb637a94494a2.");
    223 			tst_resm(TFAIL,
    224 				 "got unnecessary event: "
    225 				 "wd=%d mask=%x cookie=%u len=%u "
    226 				 "name=\"%.*s\"", event->wd, event->mask,
    227 				 event->cookie, event->len, event->len, event->name);
    228 
    229 		} else if ((event_set[test_num].mask == event->mask)
    230 			   &&
    231 			   (!strncmp
    232 			    (event_set[test_num].name, event->name,
    233 			     event->len))) {
    234 			tst_resm(TPASS,
    235 				 "got event: wd=%d mask=%x "
    236 				 "cookie=%u len=%u name=\"%.*s\"",
    237 				 event->wd, event->mask, event->cookie,
    238 				 event->len, event->len, event->name);
    239 
    240 		} else {
    241 			tst_resm(TFAIL, "got event: wd=%d mask=%x "
    242 				 "(expected %x) cookie=%u len=%u "
    243 				 "name=\"%.*s\" (expected \"%s\") %d",
    244 				 event->wd, event->mask,
    245 				 event_set[test_num].mask,
    246 				 event->cookie, event->len,
    247 				 event->len, event->name,
    248 				 event_set[test_num].name,
    249 				 strncmp(event_set[test_num].name, event->name, event->len));
    250 		}
    251 		test_num++;
    252 		i += EVENT_SIZE + event->len;
    253 	}
    254 
    255 	for (; test_num < TST_TOTAL; test_num++) {
    256 		tst_resm(TFAIL, "didn't get event: mask=%x ",
    257 			 event_set[test_num].mask);
    258 	}
    259 
    260 	cleanup();
    261 	tst_exit();
    262 }
    263 #else
    264 int main(void)
    265 {
    266 	tst_brkm(TCONF, NULL, "system doesn't have required inotify support");
    267 }
    268 #endif /* defined(HAVE_SYS_INOTIFY_H) */
    269