1 /* 2 * Copyright (c) 2015 Fujitsu Ltd. 3 * Author: Guangwen Feng <fenggw-fnst@cn.fujitsu.com> 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms of version 2 of the GNU General Public License as 7 * published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope that it would be useful, but 10 * WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 * 13 * You should have received a copy of the GNU General Public License 14 * alone with this program. 15 */ 16 17 /* 18 * DESCRIPTION 19 * Test for feature F_SETLEASE of fcntl(2). 20 * "F_SETLEASE is used to establish a lease which provides a mechanism: 21 * When a process (the lease breaker) performs an open(2) or truncate(2) 22 * that conflicts with the lease, the system call will be blocked by 23 * kernel, meanwhile the kernel notifies the lease holder by sending 24 * it a signal (SIGIO by default), after the lease holder successes 25 * to downgrade or remove the lease, the kernel permits the system 26 * call of the lease breaker to proceed." 27 */ 28 29 #include <errno.h> 30 31 #include "test.h" 32 #include "safe_macros.h" 33 34 /* 35 * MIN_TIME_LIMIT is defined to 5 senconds as a minimal acceptable 36 * amount of time for the lease breaker waiting for unblock via 37 * lease holder voluntarily downgrade or remove the lease, if the 38 * lease breaker is unblocked within MIN_TIME_LIMIT we may consider 39 * that the feature of the lease mechanism works well. 40 * 41 * The lease-break-time is set to 45 seconds for timeout in kernel. 42 */ 43 #define MIN_TIME_LIMIT 5 44 45 #define OP_OPEN_RDONLY 0 46 #define OP_OPEN_WRONLY 1 47 #define OP_OPEN_RDWR 2 48 #define OP_TRUNCATE 3 49 50 #define FILE_MODE (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID) 51 #define PATH_LS_BRK_T "/proc/sys/fs/lease-break-time" 52 53 static void setup(void); 54 static void do_test(int); 55 static int do_child(int); 56 static void cleanup(void); 57 58 static int fd; 59 static int ls_brk_t; 60 static long type; 61 static sigset_t newset, oldset; 62 63 /* Time limit for lease holder to receive SIGIO. */ 64 static struct timespec timeout = {.tv_sec = 5}; 65 66 static struct test_case_t { 67 int lease_type; 68 int op_type; 69 char *desc; 70 } test_cases[] = { 71 {F_WRLCK, OP_OPEN_RDONLY, 72 "open(O_RDONLY) conflicts with fcntl(F_SETLEASE, F_WRLCK)"}, 73 {F_WRLCK, OP_OPEN_WRONLY, 74 "open(O_WRONLY) conflicts with fcntl(F_SETLEASE, F_WRLCK)"}, 75 {F_WRLCK, OP_OPEN_RDWR, 76 "open(O_RDWR) conflicts with fcntl(F_SETLEASE, F_WRLCK)"}, 77 {F_WRLCK, OP_TRUNCATE, 78 "truncate() conflicts with fcntl(F_SETLEASE, F_WRLCK)"}, 79 {F_RDLCK, OP_OPEN_WRONLY, 80 "open(O_WRONLY) conflicts with fcntl(F_SETLEASE, F_RDLCK)"}, 81 {F_RDLCK, OP_OPEN_RDWR, 82 "open(O_RDWR) conflicts with fcntl(F_SETLEASE, F_RDLCK)"}, 83 {F_RDLCK, OP_TRUNCATE, 84 "truncate() conflicts with fcntl(F_SETLEASE, F_RDLCK)"}, 85 }; 86 87 char *TCID = "fcntl33"; 88 int TST_TOTAL = ARRAY_SIZE(test_cases); 89 90 int main(int ac, char **av) 91 { 92 int lc; 93 int tc; 94 95 tst_parse_opts(ac, av, NULL, NULL); 96 97 setup(); 98 99 for (lc = 0; TEST_LOOPING(lc); lc++) { 100 tst_count = 0; 101 102 for (tc = 0; tc < TST_TOTAL; tc++) 103 do_test(tc); 104 } 105 106 cleanup(); 107 tst_exit(); 108 } 109 110 static void setup(void) 111 { 112 tst_sig(FORK, DEF_HANDLER, cleanup); 113 114 tst_require_root(); 115 116 tst_timer_check(CLOCK_MONOTONIC); 117 118 /* Backup and set the lease-break-time. */ 119 SAFE_FILE_SCANF(NULL, PATH_LS_BRK_T, "%d", &ls_brk_t); 120 SAFE_FILE_PRINTF(NULL, PATH_LS_BRK_T, "%d", 45); 121 122 tst_tmpdir(); 123 124 switch ((type = tst_fs_type(cleanup, "."))) { 125 case TST_NFS_MAGIC: 126 case TST_RAMFS_MAGIC: 127 case TST_TMPFS_MAGIC: 128 tst_brkm(TCONF, cleanup, 129 "Cannot do fcntl(F_SETLEASE, F_WRLCK) " 130 "on %s filesystem", 131 tst_fs_type_name(type)); 132 default: 133 break; 134 } 135 136 SAFE_TOUCH(cleanup, "file", FILE_MODE, NULL); 137 138 sigemptyset(&newset); 139 sigaddset(&newset, SIGIO); 140 141 if (sigprocmask(SIG_SETMASK, &newset, &oldset) < 0) 142 tst_brkm(TBROK | TERRNO, cleanup, "sigprocmask() failed"); 143 144 TEST_PAUSE; 145 } 146 147 static void do_test(int i) 148 { 149 fd = SAFE_OPEN(cleanup, "file", O_RDONLY); 150 151 pid_t cpid = tst_fork(); 152 153 if (cpid < 0) 154 tst_brkm(TBROK | TERRNO, cleanup, "fork() failed"); 155 156 if (cpid == 0) { 157 SAFE_CLOSE(NULL, fd); 158 do_child(i); 159 } 160 161 TEST(fcntl(fd, F_SETLEASE, test_cases[i].lease_type)); 162 if (TEST_RETURN == -1) { 163 tst_resm(TFAIL | TTERRNO, "fcntl() failed to set lease"); 164 SAFE_WAITPID(cleanup, cpid, NULL, 0); 165 SAFE_CLOSE(cleanup, fd); 166 fd = 0; 167 return; 168 } 169 170 /* Wait for SIGIO caused by lease breaker. */ 171 TEST(sigtimedwait(&newset, NULL, &timeout)); 172 if (TEST_RETURN == -1) { 173 if (TEST_ERRNO == EAGAIN) { 174 tst_resm(TFAIL | TTERRNO, "failed to receive SIGIO " 175 "within %lis", timeout.tv_sec); 176 SAFE_WAITPID(cleanup, cpid, NULL, 0); 177 SAFE_CLOSE(cleanup, fd); 178 fd = 0; 179 return; 180 } 181 tst_brkm(TBROK | TTERRNO, cleanup, "sigtimedwait() failed"); 182 } 183 184 /* Try to downgrade or remove the lease. */ 185 switch (test_cases[i].lease_type) { 186 case F_WRLCK: 187 TEST(fcntl(fd, F_SETLEASE, F_RDLCK)); 188 if (TEST_RETURN == 0) 189 break; 190 case F_RDLCK: 191 TEST(fcntl(fd, F_SETLEASE, F_UNLCK)); 192 if (TEST_RETURN == -1) { 193 tst_resm(TFAIL | TTERRNO, 194 "fcntl() failed to remove the lease"); 195 } 196 break; 197 default: 198 break; 199 } 200 201 tst_record_childstatus(cleanup, cpid); 202 203 SAFE_CLOSE(cleanup, fd); 204 fd = 0; 205 } 206 207 static int do_child(int i) 208 { 209 long long elapsed_ms; 210 211 if (tst_process_state_wait2(getppid(), 'S') != 0) { 212 tst_brkm(TBROK | TERRNO, NULL, 213 "failed to wait for parent process's state"); 214 } 215 216 tst_timer_start(CLOCK_MONOTONIC); 217 218 switch (test_cases[i].op_type) { 219 case OP_OPEN_RDONLY: 220 SAFE_OPEN(NULL, "file", O_RDONLY); 221 break; 222 case OP_OPEN_WRONLY: 223 SAFE_OPEN(NULL, "file", O_WRONLY); 224 break; 225 case OP_OPEN_RDWR: 226 SAFE_OPEN(NULL, "file", O_RDWR); 227 break; 228 case OP_TRUNCATE: 229 SAFE_TRUNCATE(NULL, "file", 0); 230 break; 231 default: 232 break; 233 } 234 235 tst_timer_stop(); 236 237 elapsed_ms = tst_timer_elapsed_ms(); 238 239 if (elapsed_ms < MIN_TIME_LIMIT * 1000) { 240 tst_resm(TPASS, "%s, unblocked within %ds", 241 test_cases[i].desc, MIN_TIME_LIMIT); 242 } else { 243 tst_resm(TFAIL, "%s, blocked too long %llims, " 244 "expected within %ds", 245 test_cases[i].desc, elapsed_ms, MIN_TIME_LIMIT); 246 } 247 248 tst_exit(); 249 } 250 251 static void cleanup(void) 252 { 253 if (sigprocmask(SIG_SETMASK, &oldset, NULL) < 0) 254 tst_resm(TWARN | TERRNO, "sigprocmask restore oldset failed"); 255 256 if (fd > 0 && close(fd)) 257 tst_resm(TWARN | TERRNO, "failed to close file"); 258 259 tst_rmdir(); 260 261 /* Restore the lease-break-time. */ 262 FILE_PRINTF(PATH_LS_BRK_T, "%d", ls_brk_t); 263 } 264