1/* 2 * 3 * Copyright (c) International Business Machines Corp., 2001 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 13 * the GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 */ 19 20/* 21 * NAME 22 * fcntl15.c 23 * 24 * DESCRIPTION 25 * Check that file locks are removed when file closed 26 * 27 * ALGORITHM 28 * Use three testcases to check removal of locks when a file is closed. 29 * 30 * Case 1: Parent opens a file and duplicates it, places locks using 31 * both file descriptors then closes one descriptor, all locks should 32 * be removed. 33 * 34 * Case 2: Open same file twice using(open), place locks using both 35 * descriptors then close on descriptor, locks on the file should be 36 * lost 37 * 38 * Case 3: Open file twice, one by each process, set the locks and have 39 * a child check the locks. Remove the first file and have the child 40 * check the locks. Remove the first file and have child check locks 41 * again. Only locks set on first file should have been removed 42 * 43 * USAGE 44 * fcntl15 45 * 46 * HISTORY 47 * 07/2001 Ported by Wayne Boyer 48 * MODIFIED: - mridge@us.ibm.com -- changed getpid to syscall(get thread ID) for unique ID on NPTL threading 49 * 50 * RESTRICTIONS 51 * None 52 */ 53 54#include <signal.h> 55#include <fcntl.h> 56#include "test.h" 57#include <sys/types.h> 58#include <sys/wait.h> 59#include <sys/types.h> 60#include <sys/syscall.h> 61#include <linux/unistd.h> 62 63#define DATA "ABCDEFGHIJ" 64#define DUP 0 65#define OPEN 1 66#define FORK_ 2 67 68char *TCID = "fcntl15"; 69int TST_TOTAL = 1; 70 71static int parent, child1, child2, status; 72static volatile sig_atomic_t parent_flag, child_flag, alarm_flag; 73static char tmpname[40]; 74struct flock flock; 75 76#ifdef UCLINUX 77static char *argv0; /* set by main, passed to self_exec */ 78#endif 79 80 81void alarm_sig(int sig) 82{ 83 signal(SIGALRM, alarm_sig); 84 alarm_flag = 1; 85 if ((syscall(__NR_gettid)) == parent) { 86 tst_resm(TINFO, "Alarm caught by parent"); 87 } else { 88 tst_resm(TINFO, "Alarm caught by child"); 89 } 90} 91 92void child_sig(int sig) 93{ 94 signal(SIGUSR1, child_sig); 95 child_flag++; 96} 97 98void parent_sig(int sig) 99{ 100 signal(SIGUSR2, parent_sig); 101 parent_flag++; 102} 103 104int dochild1(int file_flag, int file_mode) 105{ 106 int fd_B; 107 sigset_t newmask, zeromask, oldmask; 108 109 if ((fd_B = open(tmpname, file_flag, file_mode)) < 0) { 110 perror("open on child1 file failed"); 111 exit(1); 112 } 113 114 /* initialize lock structure for second 5 bytes of file */ 115 flock.l_type = F_WRLCK; 116 flock.l_whence = 0; 117 flock.l_start = 5L; 118 flock.l_len = 5L; 119 120 /* set lock on child file descriptor */ 121 if ((fcntl(fd_B, F_SETLK, &flock)) < 0) { 122 perror("child lock failed should have succeeded"); 123 exit(1); 124 } 125 126 sigemptyset(&zeromask); 127 sigemptyset(&newmask); 128 sigaddset(&newmask, SIGUSR1); 129 sigaddset(&newmask, SIGUSR2); 130 sigaddset(&newmask, SIGALRM); 131 if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) { 132 perror("child1 sigprocmask SIG_BLOCK fail"); 133 exit(1); 134 } 135 /* 136 * send signal to parent here to tell parent we have locked the 137 * file, thus allowing parent to proceed 138 */ 139 if ((kill(parent, SIGUSR1)) < 0) { 140 perror("child1 signal to parent failed"); 141 exit(1); 142 } 143 144 /* 145 * set alarm to break pause if parent fails to signal then spin till 146 * parent ready 147 */ 148 alarm(60); 149 while (parent_flag == 0 && alarm_flag == 0) 150 sigsuspend(&zeromask); 151 alarm((unsigned)0); 152 if (parent_flag != 1) { 153 perror("pause in child1 terminated without " 154 "SIGUSR2 signal from parent"); 155 exit(1); 156 } 157 parent_flag = 0; 158 alarm_flag = 0; 159 if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) { 160 perror("child1 sigprocmask SIG_SETMASK fail"); 161 exit(1); 162 } 163 164 /* wait for child2 to complete then cleanup */ 165 sleep(10); 166 close(fd_B); 167 exit(0); 168} 169 170#ifdef UCLINUX 171int uc_file_flag, uc_file_mode, uc_dup_flag; 172 173void dochild1_uc(void) 174{ 175 dochild1(uc_file_flag, uc_file_mode); 176} 177 178void dochild2_uc(void) 179{ 180 dochild2(uc_file_flag, uc_dup_flag); 181} 182#endif 183 184int dofork(int file_flag, int file_mode) 185{ 186 /* create child process */ 187 if ((child1 = FORK_OR_VFORK()) < 0) { 188 perror("Fork failure"); 189 return 1; 190 } 191 192 /* child1 */ 193 if (child1 == 0) { 194#ifdef UCLINUX 195 if (self_exec(argv0, "nddds", 1, file_flag, file_mode, 196 parent, tmpname) < 0) { 197 perror("self_exec failure"); 198 return 1; 199 } 200#else 201 dochild1(file_flag, file_mode); 202#endif 203 } else { 204 /* 205 * need to wait for child1 to open, and lock the area of the 206 * file prior to continuing on from here 207 */ 208 sigset_t newmask, zeromask, oldmask; 209 sigemptyset(&zeromask); 210 sigemptyset(&newmask); 211 sigaddset(&newmask, SIGUSR1); 212 sigaddset(&newmask, SIGUSR2); 213 sigaddset(&newmask, SIGALRM); 214 if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) { 215 perror("parent sigprocmask SIG_BLOCK fail"); 216 exit(1); 217 } 218 219 /* 220 * set alarm to break pause if parent fails to signal then spin till 221 * parent ready 222 */ 223 alarm(60); 224 while (child_flag == 0 && alarm_flag == 0) 225 sigsuspend(&zeromask); 226 alarm((unsigned)0); 227 if (child_flag != 1) { 228 perror("parent paused without SIGUSR1 " "from child"); 229 exit(1); 230 } 231 child_flag = 0; 232 alarm_flag = 0; 233 if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) { 234 perror("parent sigprocmask SIG_SETMASK fail"); 235 exit(1); 236 } 237 } 238 return 0; 239} 240 241int dochild2(int file_flag, int file_mode, int dup_flag) 242{ 243 int fd_C; 244 sigset_t newmask, zeromask, oldmask; 245 246 if ((fd_C = open(tmpname, file_flag, file_mode)) < 0) { 247 perror("open on child2 file failed"); 248 exit(1); 249 } 250 251 /* initialize lock structure for first 5 bytes of file */ 252 flock.l_type = F_WRLCK; 253 flock.l_whence = 0; 254 flock.l_start = 0L; 255 flock.l_len = 5L; 256 257 /* Set lock on child file descriptor */ 258 if ((fcntl(fd_C, F_SETLK, &flock)) >= 0) { 259 tst_resm(TFAIL, "First child2 lock succeeded should " 260 "have failed"); 261 exit(1); 262 } 263 264 /* initialize lock structure for second 5 bytes of file */ 265 flock.l_type = F_WRLCK; 266 flock.l_whence = 0; 267 flock.l_start = 5L; 268 flock.l_len = 5L; 269 270 /* set lock on child file descriptor */ 271 if ((fcntl(fd_C, F_SETLK, &flock)) >= 0) { 272 tst_resm(TFAIL, "second child2 lock succeeded should have " 273 "failed"); 274 exit(1); 275 } 276 277 sigemptyset(&zeromask); 278 sigemptyset(&newmask); 279 sigaddset(&newmask, SIGUSR1); 280 sigaddset(&newmask, SIGUSR2); 281 sigaddset(&newmask, SIGALRM); 282 if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) { 283 perror("child2 sigprocmask SIG_BLOCK fail"); 284 exit(1); 285 } 286 /* 287 * send signal to parent here to tell parent we have locked the 288 * file, thus allowing parent to proceed 289 */ 290 if ((kill(parent, SIGUSR1)) < 0) { 291 perror("child2 signal to parent failed"); 292 exit(1); 293 } 294 295 /* 296 * set alarm to break pause if parent fails to signal then spin till 297 * parent ready 298 */ 299 alarm(60); 300 while (parent_flag == 0 && alarm_flag == 0) 301 sigsuspend(&zeromask); 302 alarm((unsigned)0); 303 if (parent_flag != 1) { 304 perror("pause in child2 terminated without " 305 "SIGUSR2 signal from parent"); 306 exit(1); 307 } 308 parent_flag = 0; 309 alarm_flag = 0; 310 if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) { 311 perror("child2 sigprocmask SIG_SETMASK fail"); 312 exit(1); 313 } 314 315 /* initialize lock structure for first 5 bytes of file */ 316 flock.l_type = F_WRLCK; 317 flock.l_whence = 0; 318 flock.l_start = 0L; 319 flock.l_len = 5L; 320 321 /* set lock on child file descriptor */ 322 if ((fcntl(fd_C, F_SETLK, &flock)) < 0) { 323 tst_resm(TFAIL, "third child2 lock failed should have " 324 "succeeded"); 325 exit(1); 326 } 327 328 /* Initialize lock structure for second 5 bytes of file */ 329 flock.l_type = F_WRLCK; 330 flock.l_whence = 0; 331 flock.l_start = 5L; 332 flock.l_len = 5L; 333 334 /* set lock on child file descriptor */ 335 if (dup_flag == FORK_) { 336 if ((fcntl(fd_C, F_SETLK, &flock)) >= 0) { 337 tst_resm(TFAIL, "fourth child2 lock succeeded " 338 "should have failed"); 339 exit(1); 340 } 341 } else { 342 if ((fcntl(fd_C, F_SETLK, &flock)) < 0) { 343 tst_resm(TFAIL, "fourth child2 lock failed " 344 "should have succeeded"); 345 exit(1); 346 } 347 } 348 close(fd_C); 349 exit(0); 350} 351 352void setup(void) 353{ 354 tst_sig(FORK, DEF_HANDLER, NULL); 355 356 TEST_PAUSE; 357} 358 359int run_test(int file_flag, int file_mode, int dup_flag) 360{ 361 int fd_A, fd_B; 362 fd_B = -1; 363 sigset_t newmask, zeromask, oldmask; 364 365 /* setup to catch SIGUSR1 signal from child process */ 366 if ((signal(SIGUSR1, child_sig)) == SIG_ERR) { 367 perror("Signal setup for SIGUSR1 failed"); 368 } 369 370 /* setup to catch SIGUSR2 signal from parent */ 371 if ((signal(SIGUSR2, parent_sig)) == SIG_ERR) { 372 perror("Signal setup for SIGUSR1 failed"); 373 } 374 375 parent = syscall(__NR_gettid); 376 377 tst_tmpdir(); 378 /* setup temporary file name */ 379 sprintf(tmpname, "fcntl15.%d", parent); 380 381 /* initialize signal flags */ 382 child_flag = parent_flag = alarm_flag = 0; 383 384 if ((fd_A = open(tmpname, file_flag, file_mode)) < 0) { 385 perror("open first parent file failed"); 386 tst_rmdir(); 387 return 1; 388 } 389 390 /* write some data to the file */ 391 (void)write(fd_A, DATA, 10); 392 393 if (dup_flag) { 394 if (dup_flag == FORK_) { 395 dofork(file_flag, file_mode); 396 } else { 397 if ((fd_B = open(tmpname, file_flag, file_mode)) < 0) { 398 perror("open second parent file failed"); 399 tst_rmdir(); 400 return 1; 401 } 402 } 403 } else { 404 /* create a second file descriptor from first file */ 405 if ((fd_B = fcntl(fd_A, F_DUPFD, 0)) < 0) { 406 perror("dup of second parent file failed"); 407 tst_rmdir(); 408 return 1; 409 } 410 } 411 412 /* 413 * initialize lock structure for first lock on first 414 * 5 bytes of file 415 */ 416 flock.l_type = F_WRLCK; 417 flock.l_whence = 0; 418 flock.l_start = 0L; 419 flock.l_len = 5L; 420 421 /* set lock on first file descriptor */ 422 if ((fcntl(fd_A, F_SETLK, &flock)) < 0) { 423 perror("Attempt to set first parent lock failed"); 424 tst_rmdir(); 425 return 1; 426 } 427 428 if (dup_flag != FORK_) { 429 /* initialize lock structure for last 5 bytes of file */ 430 flock.l_type = F_WRLCK; 431 flock.l_whence = 0; 432 flock.l_start = 5L; 433 flock.l_len = 5L; 434 435 /* set lock on second file descriptor */ 436 if ((fcntl(fd_B, F_SETLK, &flock)) < 0) { 437 perror("Attempt to set second parent lock failed"); 438 tst_rmdir(); 439 return 1; 440 } 441 } 442 443 /* create child process */ 444 if ((child2 = FORK_OR_VFORK()) < 0) { 445 perror("Fork failure"); 446 tst_rmdir(); 447 return 1; 448 } else if (child2 == 0) { /* child */ 449#ifdef UCLINUX 450 if (self_exec(argv0, "ndddds", 2, file_flag, file_mode, 451 dup_flag, parent, tmpname) < 0) 452 tst_brkm(TBROK | TERRNO, NULL, "self_exec failed"); 453#else 454 dochild2(file_flag, file_mode, dup_flag); 455#endif 456 } 457 458 /* parent */ 459 460 /* 461 * Set alarm to break pause if child fails to signal then spin till 462 * child is ready 463 */ 464 465 sigemptyset(&zeromask); 466 sigemptyset(&newmask); 467 sigaddset(&newmask, SIGUSR1); 468 sigaddset(&newmask, SIGUSR2); 469 sigaddset(&newmask, SIGALRM); 470 if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) { 471 perror("parent sigprocmask SIG_BLOCK fail"); 472 exit(1); 473 } 474 475 /* 476 * set alarm to break pause if parent fails to signal then spin till 477 * parent ready 478 */ 479 alarm(60); 480 while (child_flag == 0 && alarm_flag == 0) 481 sigsuspend(&zeromask); 482 alarm((unsigned)0); 483 if (child_flag != 1) { 484 perror("parent paused without SIGUSR1 " "from child"); 485 exit(1); 486 } 487 child_flag = 0; 488 alarm_flag = 0; 489 if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) { 490 perror("parent sigprocmask SIG_SETMASK fail"); 491 exit(1); 492 } 493 494 /* close the first file then signal child to test locks */ 495 close(fd_A); 496 if ((kill(child2, SIGUSR2)) < 0) { 497 perror("Signal to child2 failed"); 498 tst_rmdir(); 499 return 1; 500 } 501 502 if (dup_flag == FORK_) { 503 if ((kill(child1, SIGUSR2)) < 0) { 504 perror("Signal to child1 failed"); 505 tst_rmdir(); 506 return 1; 507 } 508 } 509 /* wait for child to complete then cleanup */ 510 while ((wait(&status)) > 0) { 511 if (status >> 8 != 0) { 512 tst_resm(TFAIL, "Expected 0 got %d", status >> 8); 513 tst_rmdir(); 514 return 1; 515 } 516 } 517 if (dup_flag != FORK_) { 518 close(fd_B); 519 } 520 unlink(tmpname); 521 tst_rmdir(); 522 return 0; 523} 524 525int main(int ac, char **av) 526{ 527 int lc; 528 529 tst_parse_opts(ac, av, NULL, NULL); 530#ifdef UCLINUX 531 maybe_run_child(&dochild1_uc, "nddds", 1, &uc_file_flag, 532 &uc_file_mode, &parent, tmpname); 533 maybe_run_child(&dochild2_uc, "nddds", 1, &uc_file_flag, 534 &uc_file_mode, &uc_dup_flag, &parent, tmpname); 535 argv0 = av[0]; 536#endif 537 538 setup(); 539 540 for (lc = 0; TEST_LOOPING(lc); lc++) { 541 tst_count = 0; 542 543 if ((signal(SIGALRM, alarm_sig)) == SIG_ERR) { 544 perror("SIGALRM signal set up failed"); 545 exit(1); 546 } 547 548 if (run_test(O_CREAT | O_RDWR | O_TRUNC, 0777, DUP)) 549 tst_resm(TFAIL, "Test 1: test with \"dup\" FAILED"); 550 else 551 tst_resm(TPASS, "Test 1: test with \"dup\" PASSED"); 552 553 if (run_test(O_CREAT | O_RDWR | O_TRUNC, 0777, OPEN)) 554 tst_resm(TFAIL, "Test 2: test with \"open\" FAILED"); 555 else 556 tst_resm(TPASS, "Test 2: test with \"open\" PASSED"); 557 558 if (run_test(O_CREAT | O_RDWR | O_TRUNC, 0777, FORK_)) 559 tst_resm(TFAIL, "Test 3: test with \"fork\" FAILED"); 560 else 561 tst_resm(TPASS, "Test 3: test with \"fork\" PASSED"); 562 } 563 tst_exit(); 564} 565