fcntl16.c revision 4644c7eaba9d981f16645154c8986cb19bdc8a64
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 */ 19 20/* 21 * NAME 22 * fcntl04.c 23 * 24 * DESCRIPTION 25 * Additional file locking test cases for checking proper notifictaion 26 * of processes on lock change 27 * 28 * ALGORITHM 29 * Various test cases are used to lock a file opened without mandatory 30 * locking, with madatory locking and mandatory locking with NOBLOCK. 31 * Checking that processes waiting on lock boundaries are notified 32 * properly when boundaries change 33 * 34 * USAGE 35 * fcntl04 36 * 37 * HISTORY 38 * 07/2001 Ported by Wayne Boyer 39 * 04/2002 wjhuie sigset cleanups 40 * 41 * RESTRICTIONS 42 * None 43 */ 44 45#include <fcntl.h> 46#include <sys/stat.h> 47#include <signal.h> 48#include <errno.h> 49#include <sys/types.h> 50#include <test.h> 51#include <usctest.h> 52#include <sys/wait.h> 53 54#define SKIP 0, 0, 0L, 0L, 0 55 56#define IGNORED 0 57#define NOBLOCK 2 /* immediate success */ 58#define WILLBLOCK 3 /* blocks, succeeds, parent unlocks records */ 59#define TIME_OUT 10 60 61typedef struct { 62 short type; 63 short whence; 64 long start; 65 long len; 66 short flag; 67} lock; 68 69typedef struct { 70 lock parent_a; 71 lock parent_b; 72 lock child_a; 73 lock child_b; 74 lock parent_c; 75 lock parent_d; 76} testcase; 77 78static testcase testcases[] = { 79 /* #1 Parent_a making a write lock on entire file */ 80 { {F_WRLCK, 0, 0L, 0L, IGNORED}, 81 /* Parent_b skipped */ 82 {SKIP}, 83 /* Child_a read lock on byte 1 to byte 5 */ 84 {F_RDLCK, 0, 0L, 5L, NOBLOCK}, 85 /* Child_b read lock on byte 6 to byte 10 */ 86 {F_RDLCK, 0, 6L, 5L, NOBLOCK}, 87 /* 88 * Parent_c read lock on entire file 89 */ 90 {F_RDLCK, 0, 0L, 0L, IGNORED}, 91 /* Parent_d skipped */ 92 {SKIP},}, 93 94 /* #2 Parent_a making a write lock on entire file */ 95 {{F_WRLCK, 0, 0L, 0L, IGNORED}, 96 /* Parent_b skipped */ 97 {SKIP}, 98 /* Child_a read lock on byte 1 to byte 5 */ 99 {F_RDLCK, 0, 0L, 5L, WILLBLOCK}, 100 /* Child_b read lock on byte 6 to byte 10 */ 101 {F_RDLCK, 0, 6L, 5L, WILLBLOCK}, 102 /* 103 * Parent_c write lock on entire 104 * file 105 */ 106 {F_WRLCK, 0, 0L, 0L, IGNORED}, 107 /* Parent_d skipped */ 108 {SKIP},}, 109 110 /* #3 Parent_a making a write lock on entire file */ 111 {{F_WRLCK, 0, 0L, 0L, IGNORED}, 112 /* Parent_b skipped */ 113 {SKIP}, 114 /* Child_a read lock on byte 2 to byte 4 */ 115 {F_RDLCK, 0, 2L, 3L, WILLBLOCK}, 116 /* Child_b read lock on byte 6 to byte 8 */ 117 {F_RDLCK, 0, 6L, 3L, WILLBLOCK}, 118 /* 119 * Parent_c read lock on byte 3 to 120 * byte 7 121 */ 122 {F_RDLCK, 0, 3L, 5L, IGNORED}, 123 /* Parent_d skipped */ 124 {SKIP},}, 125 126 /* #4 Parent_a making a write lock on entire file */ 127 {{F_WRLCK, 0, 0L, 0L, IGNORED}, 128 /* Parent_b skipped */ 129 {SKIP}, 130 /* Child_a read lock on byte 2 to byte 4 */ 131 {F_RDLCK, 0, 2L, 3L, WILLBLOCK}, 132 /* Child_b read lock on byte 6 to byte 8 */ 133 {F_RDLCK, 0, 6L, 3L, NOBLOCK}, 134 /* 135 * Parent_c read lock on byte 5 to 136 * byte 9 137 */ 138 {F_RDLCK, 0, 5L, 5L, IGNORED}, 139 /* Parent_d skipped */ 140 {SKIP},}, 141 142 /* #5 Parent_a making a write lock on entire file */ 143 {{F_WRLCK, 0, 0L, 0L, IGNORED}, 144 /* Parent_b skipped */ 145 {SKIP}, 146 /* Child_a read lock on byte 3 to byte 7 */ 147 {F_RDLCK, 0, 3L, 5L, NOBLOCK}, 148 /* Child_b read lock on byte 5 to byte 10 */ 149 {F_RDLCK, 0, 5L, 6L, WILLBLOCK}, 150 /* 151 * Parent_c read lock on byte 2 to 152 * byte 8 153 */ 154 {F_RDLCK, 0, 2L, 7L, IGNORED}, 155 /* Parent_d skipped */ 156 {SKIP},}, 157 158 /* #6 Parent_a making a write lock on entire file */ 159 {{F_WRLCK, 0, 0L, 0L, IGNORED}, 160 /* Parent_b skipped */ 161 {SKIP}, 162 /* Child_a read lock on byte 2 to byte 4 */ 163 {F_RDLCK, 0, 2L, 3L, WILLBLOCK}, 164 /* Child_b write lock on byte 6 to byte 8 */ 165 {F_RDLCK, 0, 6L, 3L, NOBLOCK}, 166 /* Parent_c no lock on byte 3 to 9 */ 167 {F_UNLCK, 0, 3L, 7L, IGNORED}, 168 /* Parent_d skipped */ 169 {SKIP},}, 170 171 /* #7 Parent_a making a write lock on entire file */ 172 {{F_WRLCK, 0, 0L, 0L, IGNORED}, 173 /* Parent_b read lock on byte 3 to byte 7 */ 174 {F_RDLCK, 0, 3L, 5L, IGNORED}, 175 /* Child_a read lock on byte 2 to byte 4 */ 176 {F_RDLCK, 0, 2L, 3L, NOBLOCK}, 177 /* Child_b read lock on byte 6 to byte 8 */ 178 {F_RDLCK, 0, 6L, 3L, NOBLOCK}, 179 /* 180 * Parent_c read lock on byte 1 to 181 * byte 9 182 */ 183 {F_RDLCK, 0, 1L, 9L, IGNORED}, 184 /* Parent_d skipped */ 185 {SKIP},}, 186 187 /* #8 Parent_a making a write lock on byte 2 to byte 4 */ 188 {{F_WRLCK, 0, 2L, 3L, IGNORED}, 189 /* Parent_b write lock on byte 6 to byte 8 */ 190 {F_WRLCK, 0, 6L, 3L, IGNORED}, 191 /* Child_a read lock on byte 3 to byte 7 */ 192 {F_RDLCK, 0, 3L, 5L, NOBLOCK}, 193 /* Child_b skipped */ 194 {SKIP}, 195 /* 196 * Parent_c read lock on byte 1 to 197 * byte 5 198 */ 199 {F_RDLCK, 0, 1L, 5L, IGNORED}, 200 /* 201 * Parent_d read lock on 202 * byte 5 to byte 9 203 */ 204 {F_RDLCK, 0, 5L, 5L, 205 IGNORED},}, 206 207 /* #9 Parent_a making a write lock on entire file */ 208 {{F_WRLCK, 0, 0L, 0L, IGNORED}, 209 /* Parent_b read lock on byte 3 to byte 7 */ 210 {F_RDLCK, 0, 3L, 5L, IGNORED}, 211 /* Child_a read lock on byte 2 to byte 4 */ 212 {F_RDLCK, 0, 2L, 3L, NOBLOCK}, 213 /* Child_b read lock on byte 6 to byte 8 */ 214 {F_RDLCK, 0, 6L, 3L, NOBLOCK}, 215 /* 216 * Parent_c read lock on byte 1 to 217 * byte 3 218 */ 219 {F_RDLCK, 0, 1L, 3L, IGNORED}, 220 /* 221 * Parent_d read lock on 222 * byte 7 to byte 9 223 */ 224 {F_RDLCK, 0, 7L, 3L, 225 IGNORED},}, 226 227 /* #10 Parent_a making a write lock on entire file */ 228 {{F_WRLCK, 0, 0L, 0L, IGNORED}, 229 /* Parent_b skipped */ 230 {SKIP}, 231 /* Child_a read lock on byte 2 to byte 4 */ 232 {F_RDLCK, 0, 2L, 3L, NOBLOCK}, 233 /* Child_b read lock on byte 6 to byte 8 */ 234 {F_RDLCK, 0, 6L, 3L, NOBLOCK}, 235 /* 236 * Parent_c read lock on byte 1 to 237 * byte 7 238 */ 239 {F_RDLCK, 0, 1L, 7L, IGNORED}, 240 /* 241 * Parent_d read lock on 242 * byte 3 to byte 9 243 */ 244 {F_RDLCK, 0, 3L, 7L, 245 IGNORED},}, 246 247 /* #11 Parent_a making a write lock on entire file */ 248 {{F_WRLCK, 0, 0L, 0L, IGNORED}, 249 /* Parent_b skipped */ 250 {SKIP}, 251 /* Child_a read lock on byte 3 to byte 7 */ 252 {F_RDLCK, 0, 3L, 5L, NOBLOCK}, 253 /* Child_b read lock on byte 3 to byte 7 */ 254 {F_RDLCK, 0, 3L, 5L, NOBLOCK}, 255 /* 256 * Parent_c read lock on byte 3 to 257 * byte 7 258 */ 259 {F_RDLCK, 0, 3L, 5L, IGNORED}, 260 /* Parent_d skipped */ 261 {SKIP},}, 262}; 263 264static testcase *thiscase; 265static lock *thislock; 266static int parent; 267static int child_flag1 = 0; 268static int child_flag2 = 0; 269static int parent_flag = 0; 270static int alarm_flag = 0; 271static int child_pid[2], flag[2]; 272static int fd; 273static int test; 274static char tmpname[40]; 275 276#define FILEDATA "tenbytes!" 277 278extern void catch_usr1(); /* signal catching subroutine */ 279extern void catch_usr2(); /* signal catching subroutine */ 280extern void catch_int(); /* signal catching subroutine */ 281extern void catch_alarm(); /* signal catching subroutine */ 282 283char *TCID = "fcntl04"; 284int TST_TOTAL = 1; 285extern int Tst_count; 286 287void setup(void); 288void cleanup(void); 289 290main(int ac, char **av) 291{ 292 293 int lc; /* loop counter */ 294 char *msg; /* message returned from parse_opts */ 295 296 /* parse standard options */ 297 if ((msg = parse_opts(ac, av, (option_t *)NULL, NULL)) != (char *)NULL){ 298 tst_brkm(TBROK, cleanup, "OPTION PARSING ERROR - %s", msg); 299 } 300 301 setup(); /* global setup */ 302 303 /* check looping state if -i option given */ 304 for (lc = 0; TEST_LOOPING(lc); lc++) { 305 /* reset Tst_count in case we are looping */ 306 Tst_count = 0; 307 308block1: 309 /* 310 * Check file locks on an ordinary file without 311 * mandatory locking 312 */ 313 tst_resm(TINFO, "Entering block 1"); 314 if (run_test(O_CREAT | O_RDWR | O_TRUNC, 0777, 0, 11)) { 315 tst_resm(TINFO, "Test case 1: without mandatory " 316 "locking FAILED"); 317 } else { 318 tst_resm(TINFO, "Test case 1: without manadatory " 319 "locking PASSED"); 320 } 321 tst_resm(TINFO, "Exiting block 1"); 322 323block2: 324 /* 325 * Check the file locks on a file with mandatory record 326 * locking 327 */ 328 tst_resm(TINFO, "Entering block 2"); 329 if (run_test(O_CREAT | O_RDWR | O_TRUNC, S_ISGID | 330 S_IRUSR | S_IWUSR, 0, 11)) { 331 tst_resm(TINFO, "Test case 2: with mandatory record " 332 "locking FAILED"); 333 } else { 334 tst_resm(TINFO, "Test case 2: with mandatory record " 335 "locking PASSED"); 336 } 337 tst_resm(TINFO, "Exiting block 2"); 338 339block3: 340 /* 341 * Check file locks on a file with mandatory record locking 342 * and no delay 343 */ 344 tst_resm(TINFO, "Entering block 3"); 345 if (run_test(O_CREAT | O_RDWR | O_TRUNC | O_NDELAY, 346 S_ISGID | S_IRUSR | S_IWUSR, 0, 11)) { 347 tst_resm(TINFO, "Test case 3: mandatory locking with " 348 "NODELAY FAILED"); 349 } else { 350 tst_resm(TINFO, "Test case 3: mandatory locking with " 351 "NODELAY PASSED"); 352 } 353 tst_resm(TINFO, "Exiting block 3"); 354 } 355 cleanup(); 356} 357 358int 359run_test(int file_flag, int file_mode, int start, int end) 360{ 361 int child_count; 362 int child; 363 int nexited; 364 int status, expect_stat; 365 int i, fail = 0; 366 367 /* loop through all test cases */ 368 for (test = start; test < end; test++) { 369 /* open a temp file to lock */ 370 fd = open(tmpname, file_flag, file_mode); 371 if (fd < 0) { 372 tst_brkm(TBROK, cleanup, "open failed"); 373 /*NOTREACHED*/ 374 } 375 376 /* write some dummy data to the file */ 377 (void)write(fd, FILEDATA, 10); 378 379 /* Initialize first parent lock structure */ 380 thiscase = &testcases[test]; 381 thislock = &thiscase->parent_a; 382 383 /* set the initial parent lock on the file */ 384 if ((fcntl(fd, F_SETLK, thislock)) < 0) { 385 tst_resm(TFAIL, "First parent lock failed"); 386 tst_resm(TFAIL, "Test case %d, errno = %d", test + 1, 387 errno); 388 unlink(tmpname); 389 return(1); 390 } 391 392 /* Initialize second parent lock structure */ 393 thislock = &thiscase->parent_b; 394 395 if ((thislock->type) != IGNORED) { 396 /* set the second parent lock */ 397 if ((fcntl(fd, F_SETLK, thislock)) < 0) { 398 tst_resm(TFAIL, "Second parent lock failed"); 399 tst_resm(TFAIL, "Test case %d, errno = %d", 400 test + 1, errno); 401 unlink(tmpname); 402 return(1); 403 } 404 } 405 406 /* Initialize first child lock structure */ 407 thislock = &thiscase->child_a; 408 409 /* Initialize child counter and flags */ 410 alarm_flag = parent_flag = 0; 411 child_flag1 = child_flag2 = 0; 412 child_count = 0; 413 414 /* spawn child processes */ 415 for (i = 0; i < 2; i++) { 416 if (thislock->type != IGNORED) { 417 if ((child = fork()) == 0) { 418 dochild(i); 419 } 420 if (child < 0) { 421 perror("Fork failed"); 422 return(1); 423 } 424 child_count++; 425 child_pid[i] = child; 426 flag[i] = thislock->flag; 427 } 428 /* Initialize second child lock structure */ 429 thislock = &thiscase->child_b; 430 } 431 /* parent process */ 432 433 /* 434 * Wait for children to signal they are ready. Set a timeout 435 * just in case they don't signal at all. 436 */ 437 alarm(TIME_OUT); 438 439 while (!alarm_flag 440 && (child_flag1 + child_flag2 != child_count)) { 441 pause(); 442 } 443 444 /* 445 * Turn off alarm and unmask signals 446 */ 447 alarm((unsigned)0); 448 449 if (child_flag1 + child_flag2 != child_count) { 450 tst_resm(TFAIL, "Test case %d: kids didn't signal", 451 test + 1); 452 fail = 1; 453 } 454 child_flag1 = child_flag2 = alarm_flag = 0; 455 456 thislock = &thiscase->parent_c; 457 458 /* set the third parent lock on the file */ 459 if ((fcntl(fd, F_SETLK, thislock)) < 0) { 460 tst_resm(TFAIL, "Third parent lock failed"); 461 tst_resm(TFAIL, "Test case %d, errno = %d", 462 test + 1, errno); 463 unlink(tmpname); 464 return(1); 465 } 466 467 /* Initialize fourth parent lock structure */ 468 thislock = &thiscase->parent_d; 469 470 if ((thislock->type) != IGNORED) { 471 /* set the fourth parent lock */ 472 if ((fcntl(fd, F_SETLK, thislock)) < 0) { 473 tst_resm(TINFO, "Fourth parent lock failed"); 474 tst_resm(TINFO, "Test case %d, errno = %d", 475 test + 1, errno); 476 unlink(tmpname); 477 return(1); 478 } 479 } 480 481 /* 482 * Wait for children to exit, or for timeout to occur. 483 * Timeouts are expected for testcases where kids are 484 * 'WILLBLOCK', In that case, send kids a wakeup interrupt 485 * and wait again for them. If a second timeout occurs, then 486 * something is wrong. 487 */ 488 alarm_flag = nexited = 0; 489 while (nexited < child_count) { 490 alarm(TIME_OUT); 491 child = wait(&status); 492 alarm(0); 493 494 if (child == -1) { 495 if (errno != EINTR || alarm_flag != 1) { 496 /* 497 * Some error other than a timeout, 498 * or else this is the second 499 * timeout. Both cases are errors. 500 */ 501 break; 502 } 503 504 /* 505 * Expected timeout case. Signal kids then 506 * go back and wait again 507 */ 508 child_sig(SIGUSR1, child_count); 509 continue; 510 } 511 512 for (i = 0; i < child_count; i++) 513 if (child == child_pid[i]) 514 break; 515 if (i == child_count) { 516 /* 517 * Ignore unexpected kid, it could be a 518 * leftover from a previous iteration that 519 * timed out. 520 */ 521 continue; 522 } 523 524 /* Found the right kid, check his status */ 525 nexited++; 526 527 expect_stat = (flag[i] == NOBLOCK) ? 0 : 1; 528 529 if (!WIFEXITED(status) 530 || WEXITSTATUS(status) != expect_stat) { 531 /* got unexpected exit status from kid */ 532 tst_resm(TFAIL, "Test case %d: child %d %s " 533 "or got bad status (x%x)", test + 1, 534 i, (flag[i] == NOBLOCK) ? 535 "BLOCKED unexpectedly" : 536 "failed to BLOCK", status); 537 fail = 1; 538 } 539 } 540 541 if (nexited != child_count) { 542 tst_resm(TFAIL, "Test case %d, caught %d expected %d " 543 "children", test + 1, nexited, child_count); 544 child_sig(SIGKILL, nexited); 545 fail = 1; 546 } 547 close(fd); 548 } 549 unlink(tmpname); 550 if (fail) { 551 return(1); 552 } else { 553 return(0); 554 } 555} 556 557dochild(int kid) 558{ 559 /* child process */ 560 (void)signal(SIGUSR1, catch_int); 561 562 /* Lock should succeed after blocking and parent releases lock */ 563 if (kid) { 564 if ((kill(parent, SIGUSR2)) < 0) { 565 tst_resm(TFAIL, "Attempt to send signal to parent " 566 "failed"); 567 tst_resm(TFAIL, "Test case %d, child %d, errno = %d", 568 test + 1, kid, errno); 569 exit(1); 570 } 571 } else { 572 if ((kill(parent, SIGUSR1)) < 0) { 573 tst_resm(TFAIL, "Attempt to send signal to parent " 574 "failed"); 575 tst_resm(TFAIL, "Test case %d, child %d, errno = %d", 576 test + 1, kid, errno); 577 exit(1); 578 } 579 } 580 581 if ((fcntl(fd, F_SETLKW, thislock)) < 0) { 582 if (errno == EINTR && parent_flag) { 583 /* 584 * signal received is waiting for lock to clear, 585 * this is expected if flag = WILLBLOCK 586 */ 587 exit(1); 588 } else { 589 tst_resm(TFAIL, "Attempt to set child BLOCKING lock " 590 "failed"); 591 tst_resm(TFAIL, "Test case %d, errno = %d", test + 1, 592 errno); 593 exit(2); 594 } 595 } 596 exit(0); 597} /* end of child process */ 598 599void 600catch_alarm() 601{ 602 alarm_flag = 1; 603} 604 605void 606catch_usr1() /* invoked on catching SIGUSR1 */ 607{ 608 /* 609 * Set flag to let parent know that child #1 is ready to have the 610 * lock removed 611 */ 612 child_flag1 = 1; 613} 614 615void 616catch_usr2() /* invoked on catching SIGUSR2 */ 617{ 618 /* 619 * Set flag to let parent know that child #2 is ready to have the 620 * lock removed 621 */ 622 child_flag2 = 1; 623} 624 625void 626catch_int() /* invoked on child catching SIGUSR1 */ 627{ 628 /* 629 * Set flag to interrupt fcntl call in child and force a controlled 630 * exit 631 */ 632 parent_flag = 1; 633} 634 635child_sig(int sig, int nkids) 636{ 637 int i; 638 639 for (i = 0; i < nkids; i++) { 640 if (kill(child_pid[i], 0) == 0) { 641 if ((kill(child_pid[i], sig)) < 0) { 642 tst_resm(TFAIL, "Attempt to signal child %d, " 643 "failed", i + 1); 644 } 645 } 646 } 647} 648 649/* 650 * setup - performs all ONE TIME steup for this test 651 */ 652void 653setup(void) 654{ 655 /* capture signals */ 656 tst_sig(FORK, DEF_HANDLER, cleanup); 657 658 umask(0); 659 660 /* Pause if option was specified */ 661 TEST_PAUSE; 662 663 parent = getpid(); 664 665 tst_tmpdir(); 666 667 /* set up temp filename */ 668 sprintf(tmpname, "fcntl4.%d", parent); 669 670 /* 671 * Set up signal handling functions 672 */ 673 (void)signal(SIGUSR1, catch_usr1); 674 (void)signal(SIGUSR2, catch_usr2); 675 (void)signal(SIGALRM, catch_alarm); 676} 677 678/* 679 * cleanup - performs all the ONE TIME cleanup for this test at completion or 680 * premature exit 681 */ 682void 683cleanup(void) 684{ 685 TEST_CLEANUP; 686 687 tst_rmdir(); 688 689 tst_exit(); 690} 691