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 various fanotify special flags 27 */ 28 29 #define _GNU_SOURCE 30 #include "config.h" 31 32 #include <stdio.h> 33 #include <sys/stat.h> 34 #include <sys/types.h> 35 #include <fcntl.h> 36 #include <errno.h> 37 #include <string.h> 38 #include <sys/syscall.h> 39 #include "test.h" 40 #include "linux_syscall_numbers.h" 41 #include "fanotify.h" 42 #include "safe_macros.h" 43 44 char *TCID = "fanotify04"; 45 int TST_TOTAL = 9; 46 47 #if defined(HAVE_SYS_FANOTIFY_H) 48 #include <sys/fanotify.h> 49 50 #define EVENT_MAX 1024 51 /* size of the event structure, not counting name */ 52 #define EVENT_SIZE (sizeof (struct fanotify_event_metadata)) 53 /* reasonable guess as to size of 1024 events */ 54 #define EVENT_BUF_LEN (EVENT_MAX * EVENT_SIZE) 55 56 static void setup(void); 57 static void cleanup(void); 58 59 #define BUF_SIZE 256 60 static char fname[BUF_SIZE]; 61 static char sname[BUF_SIZE]; 62 static char dir[BUF_SIZE]; 63 static int fd_notify; 64 65 static int len; 66 static char event_buf[EVENT_BUF_LEN]; 67 68 static char *expect_str_fail(int expect) 69 { 70 if (expect == 0) 71 return "failed"; 72 return "unexpectedly succeeded"; 73 } 74 75 static char *expect_str_pass(int expect) 76 { 77 if (expect == 0) 78 return "succeeded"; 79 return "failed"; 80 } 81 82 static void check_mark(char *file, unsigned long long flag, char *flagstr, 83 int expect, void (*test_event)(char *)) 84 { 85 if (fanotify_mark(fd_notify, FAN_MARK_ADD | flag, FAN_OPEN, AT_FDCWD, 86 file) != expect) { 87 tst_resm(TFAIL, 88 "fanotify_mark (%d, FAN_MARK_ADD | %s, FAN_OPEN, AT_FDCWD, " 89 "'%s') %s", fd_notify, flagstr, file, expect_str_fail(expect)); 90 } else { 91 tst_resm(TPASS, 92 "fanotify_mark (%d, FAN_MARK_ADD | %s, FAN_OPEN, AT_FDCWD, " 93 "'%s') %s", fd_notify, flagstr, file, expect_str_pass(expect)); 94 95 /* If we expected failure there's nothing to clean up */ 96 if (expect == -1) 97 return; 98 99 if (test_event) 100 test_event(file); 101 102 if (fanotify_mark(fd_notify, FAN_MARK_REMOVE | flag, 103 FAN_OPEN, AT_FDCWD, file) < 0) { 104 tst_brkm(TBROK | TERRNO, cleanup, 105 "fanotify_mark (%d, FAN_MARK_REMOVE | %s, " 106 "FAN_OPEN, AT_FDCWD, '%s') failed", 107 fd_notify, flagstr, file); 108 } 109 } 110 } 111 112 #define CHECK_MARK(file, flag, expect, func) check_mark(file, flag, #flag, expect, func) 113 114 static void do_open(char *file, int flag, char *flagstr) 115 { 116 int fd; 117 118 fd = SAFE_OPEN(cleanup, file, O_RDONLY | flag); 119 SAFE_CLOSE(cleanup, fd); 120 } 121 122 #define DO_OPEN(file, flag) do_open(file, flag, #flag) 123 124 static void open_file(char *file) 125 { 126 DO_OPEN(file, 0); 127 } 128 129 static void open_dir(char *file) 130 { 131 DO_OPEN(file, O_DIRECTORY); 132 } 133 134 static void verify_event(int mask) 135 { 136 int ret; 137 struct fanotify_event_metadata *event; 138 struct stat st; 139 140 /* Read the event */ 141 ret = SAFE_READ(cleanup, 0, fd_notify, event_buf + len, 142 EVENT_BUF_LEN - len); 143 event = (struct fanotify_event_metadata *)&event_buf[len]; 144 len += ret; 145 146 if (event->mask != FAN_OPEN) { 147 tst_resm(TFAIL, "got unexpected event %llx", 148 (unsigned long long)event->mask); 149 } else if (fstat(event->fd, &st) < 0) { 150 tst_resm(TFAIL, "failed to stat event->fd (%s)", 151 strerror(errno)); 152 } else if ((st.st_mode & S_IFMT) != mask) { 153 tst_resm(TFAIL, "event->fd points to object of different type " 154 "(%o != %o)", st.st_mode & S_IFMT, mask); 155 } else { 156 tst_resm(TPASS, "event generated properly for type %o", mask); 157 } 158 close(event->fd); 159 } 160 161 static void do_open_test(char *file, int flag, char *flagstr, int mask) 162 { 163 do_open(file, flag, flagstr); 164 165 verify_event(mask); 166 } 167 168 #define DO_OPEN_TEST(file, flag, mask) do_open_test(file, flag, #flag, mask) 169 170 static void test_open_file(char *file) 171 { 172 DO_OPEN_TEST(file, 0, S_IFREG); 173 } 174 175 static void verify_no_event(void) 176 { 177 int ret; 178 179 ret = read(fd_notify, event_buf + len, EVENT_BUF_LEN - len); 180 if (ret != -1) { 181 struct fanotify_event_metadata *event; 182 183 event = (struct fanotify_event_metadata *)&event_buf[len]; 184 tst_resm(TFAIL, "seen unexpected event (mask %llx)", 185 (unsigned long long)event->mask); 186 /* Cleanup fd from the event */ 187 close(event->fd); 188 } else if (errno != EAGAIN) { 189 tst_resm(TFAIL | TERRNO, "read(%d, buf, %zu) failed", fd_notify, 190 EVENT_BUF_LEN); 191 } else { 192 tst_resm(TPASS, "No event as expected"); 193 } 194 } 195 196 static void test_open_symlink(char *file) 197 { 198 /* Since mark is on a symlink, no event should be generated by opening a file */ 199 DO_OPEN(file, 0); 200 verify_no_event(); 201 } 202 203 int main(int ac, char **av) 204 { 205 int lc; 206 207 tst_parse_opts(ac, av, NULL, NULL); 208 209 setup(); 210 211 for (lc = 0; TEST_LOOPING(lc); lc++) { 212 /* Check ONLYDIR on a directory */ 213 CHECK_MARK(".", FAN_MARK_ONLYDIR, 0, NULL); 214 215 /* Check ONLYDIR without a directory */ 216 CHECK_MARK(fname, FAN_MARK_ONLYDIR, -1, NULL); 217 218 /* Check DONT_FOLLOW for a symlink */ 219 CHECK_MARK(sname, FAN_MARK_DONT_FOLLOW, 0, test_open_symlink); 220 221 /* Check without DONT_FOLLOW for a symlink */ 222 CHECK_MARK(sname, 0, 0, test_open_file); 223 224 /* Verify FAN_MARK_FLUSH destroys all inode marks */ 225 if (fanotify_mark(fd_notify, FAN_MARK_ADD, 226 FAN_OPEN, AT_FDCWD, fname) < 0) { 227 tst_brkm(TBROK | TERRNO, cleanup, 228 "fanotify_mark (%d, FAN_MARK_ADD, FAN_OPEN, " 229 "AT_FDCWD, '%s') failed", fd_notify, fname); 230 } 231 if (fanotify_mark(fd_notify, FAN_MARK_ADD, 232 FAN_OPEN | FAN_ONDIR, AT_FDCWD, dir) < 0) { 233 tst_brkm(TBROK | TERRNO, cleanup, 234 "fanotify_mark (%d, FAN_MARK_ADD, FAN_OPEN | " 235 "FAN_ONDIR, AT_FDCWD, '%s') failed", fd_notify, 236 dir); 237 } 238 open_file(fname); 239 verify_event(S_IFREG); 240 open_dir(dir); 241 verify_event(S_IFDIR); 242 if (fanotify_mark(fd_notify, FAN_MARK_FLUSH, 243 0, AT_FDCWD, ".") < 0) { 244 tst_brkm(TBROK | TERRNO, cleanup, 245 "fanotify_mark (%d, FAN_MARK_FLUSH, 0, " 246 "AT_FDCWD, '.') failed", fd_notify); 247 } 248 249 open_dir(dir); 250 verify_no_event(); 251 } 252 253 cleanup(); 254 tst_exit(); 255 } 256 257 static void setup(void) 258 { 259 int fd; 260 261 tst_sig(NOFORK, DEF_HANDLER, cleanup); 262 263 TEST_PAUSE; 264 265 tst_tmpdir(); 266 sprintf(fname, "fname_%d", getpid()); 267 fd = SAFE_OPEN(cleanup, fname, O_RDWR | O_CREAT, 0644); 268 SAFE_CLOSE(cleanup, fd); 269 270 sprintf(sname, "symlink_%d", getpid()); 271 SAFE_SYMLINK(cleanup, fname, sname); 272 273 sprintf(dir, "dir_%d", getpid()); 274 SAFE_MKDIR(cleanup, dir, 0755); 275 276 if ((fd_notify = fanotify_init(FAN_CLASS_NOTIF | FAN_NONBLOCK, 277 O_RDONLY)) < 0) { 278 if (errno == ENOSYS) { 279 tst_brkm(TCONF, cleanup, 280 "fanotify is not configured in this kernel."); 281 } else { 282 tst_brkm(TBROK | TERRNO, cleanup, 283 "fanotify_init failed"); 284 } 285 } 286 } 287 288 static void cleanup(void) 289 { 290 if (fd_notify > 0 && close(fd_notify)) 291 tst_resm(TWARN | TERRNO, "close(%d) failed", fd_notify); 292 293 tst_rmdir(); 294 } 295 296 #else 297 298 int main(void) 299 { 300 tst_brkm(TCONF, NULL, "system doesn't have required fanotify support"); 301 } 302 303 #endif 304