1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (c) 2022 CTERA Networks. All Rights Reserved. 4 * 5 * Author: Amir Goldstein <amir73il@gmail.com> 6 */ 7 8/*\ 9 * [Description] 10 * Test special inotify mask flags. 11 * 12 * Regression test for kernel commit: 13 * a32e697cda27 ("inotify: show inotify mask flags in proc fdinfo") 14 */ 15 16#include "config.h" 17 18#include <stdio.h> 19#include <unistd.h> 20#include <fcntl.h> 21#include <signal.h> 22#include <sys/wait.h> 23 24#include "tst_test.h" 25#include "tst_safe_macros.h" 26#include "inotify.h" 27 28#if defined(HAVE_SYS_INOTIFY_H) 29#include <sys/inotify.h> 30 31#define EVENT_MAX 32 32/* Size of the event structure, not including the name */ 33#define EVENT_SIZE (sizeof(struct inotify_event)) 34#define EVENT_BUF_LEN (EVENT_MAX * (EVENT_SIZE + 16)) 35 36#define TEST_FILE "test_file" 37 38static char event_buf[EVENT_BUF_LEN]; 39 40static struct tcase { 41 const char *tname; 42 unsigned int mask; 43 int expect_events; 44} tcases[] = { 45 { 46 "Watch for multi events", 47 IN_MODIFY, 48 2, 49 }, 50 { 51 "Watch for single event", 52 IN_MODIFY | IN_ONESHOT, 53 1, 54 }, 55 { 56 "Watch for events on linked file", 57 IN_MODIFY | IN_EXCL_UNLINK, 58 1, 59 }, 60}; 61 62static int fd_notify; 63 64static void verify_inotify(unsigned int n) 65{ 66 struct tcase *tc = &tcases[n]; 67 int fd, len; 68 unsigned int tmpmask; 69 char procfdinfo[100]; 70 struct inotify_event *event = (struct inotify_event *)event_buf; 71 72 tst_res(TINFO, "Test #%d: %s", n, tc->tname); 73 74 fd_notify = SAFE_MYINOTIFY_INIT1(O_NONBLOCK); 75 76 SAFE_FILE_PRINTF(TEST_FILE, "1"); 77 78 SAFE_MYINOTIFY_ADD_WATCH(fd_notify, ".", tc->mask); 79 80 sprintf(procfdinfo, "/proc/%d/fdinfo/%d", (int)getpid(), fd_notify); 81 if (FILE_LINES_SCANF(procfdinfo, "inotify wd:%*d ino:%*x sdev:%*x mask:%x", 82 &tmpmask)) { 83 tst_res(TFAIL, "Could not parse inotify fdinfo"); 84 } else if (tmpmask != tc->mask) { 85 tst_res(TFAIL, "Incorrect mask %x in inotify fdinfo (expected %x)", 86 tmpmask, tc->mask); 87 } else { 88 tst_res(TPASS, "Correct mask in inotify fdinfo"); 89 } 90 91 fd = SAFE_OPEN(TEST_FILE, O_RDWR); 92 SAFE_WRITE(SAFE_WRITE_ALL, fd, "2", 1); 93 94 /* 95 * Read the 1st IN_MODIFY event 96 */ 97 len = SAFE_READ(0, fd_notify, event_buf, EVENT_BUF_LEN); 98 99 if (len < (int)sizeof(*event)) { 100 tst_res(TFAIL, "Got no events"); 101 } else if (event->mask == IN_MODIFY) { 102 tst_res(TPASS, "Got 1st event as expected"); 103 } else { 104 tst_res(TFAIL, "Got event 0x%x (expected 0x%x)", 105 event->mask, IN_MODIFY); 106 } 107 108 /* 109 * Unlink file so IN_EXCL_UNLINK won't get IN_ACCESS event. 110 * IN_ONESHOT won't get IN_ACCESS event because IN_MODIFY 111 * was already generated. 112 */ 113 SAFE_UNLINK(TEST_FILE); 114 SAFE_WRITE(SAFE_WRITE_ALL, fd, "3", 1); 115 SAFE_CLOSE(fd); 116 117 /* 118 * Possibly read the 2nd IN_MODIFY event 119 */ 120 errno = 0; 121 len = read(fd_notify, event_buf, EVENT_BUF_LEN); 122 SAFE_CLOSE(fd_notify); 123 if (len < 0 && errno == EAGAIN) { 124 /* Treat no event same as we treat IN_IGNORED */ 125 event->mask = IN_IGNORED; 126 } else if (len < (int)sizeof(*event)) { 127 tst_res(TFAIL | TERRNO, "Failed to read events"); 128 return; 129 } 130 131 if (event->mask == IN_MODIFY) { 132 if (tc->expect_events > 1) 133 tst_res(TPASS, "Got 2nd event as expected"); 134 else 135 tst_res(TFAIL, "Got unexpected 2nd event"); 136 } else if (event->mask == IN_IGNORED) { 137 if (tc->expect_events == 1) 138 tst_res(TPASS, "Got no more events as expected"); 139 else 140 tst_res(TFAIL, "Got only one event (expected %d)", 141 tc->expect_events); 142 } else { 143 tst_res(TFAIL, "Got unexpected event 0x%x", 144 event->mask); 145 } 146} 147 148static void cleanup(void) 149{ 150 if (fd_notify > 0) 151 SAFE_CLOSE(fd_notify); 152} 153 154static struct tst_test test = { 155 .max_runtime = 10, 156 .needs_tmpdir = 1, 157 .cleanup = cleanup, 158 .test = verify_inotify, 159 .tcnt = ARRAY_SIZE(tcases), 160 .tags = (const struct tst_tag[]) { 161 {"linux-git", "a32e697cda27"}, 162 {} 163 }, 164}; 165 166#else 167 TST_TEST_TCONF("system doesn't have required inotify support"); 168#endif 169