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