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 * Garrett 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 "linux_syscall_numbers.h" 49#include "inotify.h" 50#include "safe_macros.h" 51 52char *TCID = "inotify04"; 53int 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 66struct 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 74struct event_t event_set[EVENT_MAX]; 75 76char event_buf[EVENT_BUF_LEN]; 77 78int fd_notify, reap_wd_file, reap_wd_dir, wd_dir, wd_file; 79 80static struct tst_kern_exv kvers[] = { 81 { "RHEL5", "2.6.18-132" }, 82 { NULL, NULL }, 83}; 84 85static 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 110static 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 150int 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 264int main(void) 265{ 266 tst_brkm(TCONF, NULL, "system doesn't have required inotify support"); 267} 268#endif /* defined(HAVE_SYS_INOTIFY_H) */ 269