fcntl16.c revision ec6edca7aa42b6affd989ef91b5897f96795e40f
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 * 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 (void)sigaction(SIGUSR1, &sact, NULL); 314 315 /* Lock should succeed after blocking and parent releases lock */ 316 if (kid) { 317 if ((kill(parent, SIGUSR2)) < 0) { 318 tst_resm(TFAIL, "Attempt to send signal to parent " 319 "failed"); 320 tst_resm(TFAIL, "Test case %d, child %d, errno = %d", 321 test + 1, kid, errno); 322 exit(1); 323 } 324 } else { 325 if ((kill(parent, SIGUSR1)) < 0) { 326 tst_resm(TFAIL, "Attempt to send signal to parent " 327 "failed"); 328 tst_resm(TFAIL, "Test case %d, child %d, errno = %d", 329 test + 1, kid, errno); 330 exit(1); 331 } 332 } 333 334 if ((fcntl(fd, F_SETLKW, thislock)) < 0) { 335 if (errno == EINTR && parent_flag) { 336 /* 337 * signal received is waiting for lock to clear, 338 * this is expected if flag = WILLBLOCK 339 */ 340 exit(1); 341 } else { 342 tst_resm(TFAIL, "Attempt to set child BLOCKING lock " 343 "failed"); 344 tst_resm(TFAIL, "Test case %d, errno = %d", test + 1, 345 errno); 346 exit(2); 347 } 348 } 349 exit(0); 350} /* end of child process */ 351 352#ifdef UCLINUX 353static int kid_uc; 354 355void dochild_uc() 356{ 357 dochild(kid_uc); 358} 359#endif 360 361void catch_alarm() 362{ 363 alarm_flag = 1; 364} 365 366void catch_usr1() 367{ /* invoked on catching SIGUSR1 */ 368 /* 369 * Set flag to let parent know that child #1 is ready to have the 370 * lock removed 371 */ 372 child_flag1 = 1; 373} 374 375void catch_usr2() 376{ /* invoked on catching SIGUSR2 */ 377 /* 378 * Set flag to let parent know that child #2 is ready to have the 379 * lock removed 380 */ 381 child_flag2 = 1; 382} 383 384void catch_int() 385{ /* invoked on child catching SIGUSR1 */ 386 /* 387 * Set flag to interrupt fcntl call in child and force a controlled 388 * exit 389 */ 390 parent_flag = 1; 391} 392 393void child_sig(int sig, int nkids) 394{ 395 int i; 396 397 for (i = 0; i < nkids; i++) { 398 if (kill(child_pid[i], 0) == 0) { 399 if ((kill(child_pid[i], sig)) < 0) { 400 tst_resm(TFAIL, "Attempt to signal child %d, " 401 "failed", i + 1); 402 } 403 } 404 } 405} 406 407/* 408 * setup - performs all ONE TIME steup for this test 409 */ 410void setup(void) 411{ 412 struct sigaction sact; 413 414 tst_sig(FORK, DEF_HANDLER, cleanup); 415 416 umask(0); 417 418 /* Pause if option was specified */ 419 TEST_PAUSE; 420 421 parent = getpid(); 422 423 tst_tmpdir(); 424 425 /* set up temp filename */ 426 sprintf(tmpname, "fcntl4.%d", parent); 427 428 /* 429 * Set up signal handling functions 430 */ 431 memset(&sact, 0, sizeof(sact)); 432 sact.sa_handler = catch_usr1; 433 sigemptyset(&sact.sa_mask); 434 sigaddset(&sact.sa_mask, SIGUSR1); 435 sigaction(SIGUSR1, &sact, NULL); 436 437 memset(&sact, 0, sizeof(sact)); 438 sact.sa_handler = catch_usr2; 439 sigemptyset(&sact.sa_mask); 440 sigaddset(&sact.sa_mask, SIGUSR2); 441 sigaction(SIGUSR2, &sact, NULL); 442 443 memset(&sact, 0, sizeof(sact)); 444 sact.sa_handler = catch_alarm; 445 sigemptyset(&sact.sa_mask); 446 sigaddset(&sact.sa_mask, SIGALRM); 447 sigaction(SIGALRM, &sact, NULL); 448} 449 450int run_test(int file_flag, int file_mode, int start, int end) 451{ 452 int child_count; 453 int child; 454 int nexited; 455 int status, expect_stat; 456 int i, fail = 0; 457 458 /* loop through all test cases */ 459 for (test = start; test < end; test++) { 460 /* open a temp file to lock */ 461 fd = open(tmpname, file_flag, file_mode); 462 if (fd < 0) { 463 tst_brkm(TBROK, cleanup, "open failed"); 464 } 465 466 /* write some dummy data to the file */ 467 (void)write(fd, FILEDATA, 10); 468 469 /* Initialize first parent lock structure */ 470 thiscase = &testcases[test]; 471 thislock = &thiscase->parent_a; 472 473 /* set the initial parent lock on the file */ 474 if ((fcntl(fd, F_SETLK, thislock)) < 0) { 475 tst_resm(TFAIL, "First parent lock failed"); 476 tst_resm(TFAIL, "Test case %d, errno = %d", test + 1, 477 errno); 478 unlink(tmpname); 479 return 1; 480 } 481 482 /* Initialize second parent lock structure */ 483 thislock = &thiscase->parent_b; 484 485 if ((thislock->type) != IGNORED) { /*SKIPVAL */ 486 /* set the second parent lock */ 487 if ((fcntl(fd, F_SETLK, thislock)) < 0) { 488 tst_resm(TFAIL, "Second parent lock failed"); 489 tst_resm(TFAIL, "Test case %d, errno = %d", 490 test + 1, errno); 491 unlink(tmpname); 492 return 1; 493 } 494 } 495 496 /* Initialize first child lock structure */ 497 thislock = &thiscase->child_a; 498 499 /* Initialize child counter and flags */ 500 alarm_flag = parent_flag = 0; 501 child_flag1 = child_flag2 = 0; 502 child_count = 0; 503 504 /* spawn child processes */ 505 for (i = 0; i < 2; i++) { 506 if (thislock->type != IGNORED) { 507 if ((child = FORK_OR_VFORK()) == 0) { 508#ifdef UCLINUX 509 if (self_exec(argv0, "ddddd", i, parent, 510 test, thislock, fd) < 0) { 511 perror("self_exec failed"); 512 return 1; 513 } 514#else 515 dochild(i); 516#endif 517 } 518 if (child < 0) { 519 perror("Fork failed"); 520 return 1; 521 } 522 child_count++; 523 child_pid[i] = child; 524 flag[i] = thislock->flag; 525 } 526 /* Initialize second child lock structure */ 527 thislock = &thiscase->child_b; 528 } 529 /* parent process */ 530 531 /* 532 * Wait for children to signal they are ready. Set a timeout 533 * just in case they don't signal at all. 534 */ 535 alarm(TIME_OUT); 536 537 while (!alarm_flag 538 && (child_flag1 + child_flag2 != child_count)) { 539 pause(); 540 } 541 542 /* 543 * Turn off alarm and unmask signals 544 */ 545 alarm((unsigned)0); 546 547 if (child_flag1 + child_flag2 != child_count) { 548 tst_resm(TFAIL, "Test case %d: kids didn't signal", 549 test + 1); 550 fail = 1; 551 } 552 child_flag1 = child_flag2 = alarm_flag = 0; 553 554 thislock = &thiscase->parent_c; 555 556 /* set the third parent lock on the file */ 557 if ((fcntl(fd, F_SETLK, thislock)) < 0) { 558 tst_resm(TFAIL, "Third parent lock failed"); 559 tst_resm(TFAIL, "Test case %d, errno = %d", 560 test + 1, errno); 561 unlink(tmpname); 562 return 1; 563 } 564 565 /* Initialize fourth parent lock structure */ 566 thislock = &thiscase->parent_d; 567 568 if ((thislock->type) != IGNORED) { /*SKIPVAL */ 569 /* set the fourth parent lock */ 570 if ((fcntl(fd, F_SETLK, thislock)) < 0) { 571 tst_resm(TINFO, "Fourth parent lock failed"); 572 tst_resm(TINFO, "Test case %d, errno = %d", 573 test + 1, errno); 574 unlink(tmpname); 575 return 1; 576 } 577 } 578 579 /* 580 * Wait for children to exit, or for timeout to occur. 581 * Timeouts are expected for testcases where kids are 582 * 'WILLBLOCK', In that case, send kids a wakeup interrupt 583 * and wait again for them. If a second timeout occurs, then 584 * something is wrong. 585 */ 586 alarm_flag = nexited = 0; 587 while (nexited < child_count) { 588 alarm(TIME_OUT); 589 child = wait(&status); 590 alarm(0); 591 592 if (child == -1) { 593 if (errno != EINTR || alarm_flag != 1) { 594 /* 595 * Some error other than a timeout, 596 * or else this is the second 597 * timeout. Both cases are errors. 598 */ 599 break; 600 } 601 602 /* 603 * Expected timeout case. Signal kids then 604 * go back and wait again 605 */ 606 child_sig(SIGUSR1, child_count); 607 continue; 608 } 609 610 for (i = 0; i < child_count; i++) 611 if (child == child_pid[i]) 612 break; 613 if (i == child_count) { 614 /* 615 * Ignore unexpected kid, it could be a 616 * leftover from a previous iteration that 617 * timed out. 618 */ 619 continue; 620 } 621 622 /* Found the right kid, check his status */ 623 nexited++; 624 625 expect_stat = (flag[i] == NOBLOCK) ? 0 : 1; 626 627 if (!WIFEXITED(status) 628 || WEXITSTATUS(status) != expect_stat) { 629 /* got unexpected exit status from kid */ 630 tst_resm(TFAIL, "Test case %d: child %d %s " 631 "or got bad status (x%x)", test + 1, 632 i, (flag[i] == NOBLOCK) ? 633 "BLOCKED unexpectedly" : 634 "failed to BLOCK", status); 635 fail = 1; 636 } 637 } 638 639 if (nexited != child_count) { 640 tst_resm(TFAIL, "Test case %d, caught %d expected %d " 641 "children", test + 1, nexited, child_count); 642 child_sig(SIGKILL, nexited); 643 fail = 1; 644 } 645 close(fd); 646 } 647 unlink(tmpname); 648 if (fail) { 649 return 1; 650 } else { 651 return 0; 652 } 653 return 0; 654} 655 656int main(int ac, char **av) 657{ 658 659 int lc; /* loop counter */ 660 char *msg; /* message returned from parse_opts */ 661 662 /* parse standard options */ 663 if ((msg = parse_opts(ac, av, NULL, NULL)) != NULL) { 664 tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg); 665 } 666#ifdef UCLINUX 667 maybe_run_child(dochild_uc, "ddddd", &kid_uc, &parent, &test, 668 &thislock, &fd); 669 argv0 = av[0]; 670#endif 671 672 setup(); /* global setup */ 673 674 for (lc = 0; TEST_LOOPING(lc); lc++) { 675 /* reset Tst_count in case we are looping */ 676 Tst_count = 0; 677 678/* //block1: */ 679 /* 680 * Check file locks on an ordinary file without 681 * mandatory locking 682 */ 683 tst_resm(TINFO, "Entering block 1"); 684 if (run_test(O_CREAT | O_RDWR | O_TRUNC, 0777, 0, 11)) { 685 tst_resm(TINFO, "Test case 1: without mandatory " 686 "locking FAILED"); 687 } else { 688 tst_resm(TINFO, "Test case 1: without manadatory " 689 "locking PASSED"); 690 } 691 tst_resm(TINFO, "Exiting block 1"); 692 693/* //block2: */ 694 /* 695 * Check the file locks on a file with mandatory record 696 * locking 697 */ 698 tst_resm(TINFO, "Entering block 2"); 699 if (run_test(O_CREAT | O_RDWR | O_TRUNC, S_ISGID | 700 S_IRUSR | S_IWUSR, 0, 11)) { 701 tst_resm(TINFO, "Test case 2: with mandatory record " 702 "locking FAILED"); 703 } else { 704 tst_resm(TINFO, "Test case 2: with mandatory record " 705 "locking PASSED"); 706 } 707 tst_resm(TINFO, "Exiting block 2"); 708 709/* //block3: */ 710 /* 711 * Check file locks on a file with mandatory record locking 712 * and no delay 713 */ 714 tst_resm(TINFO, "Entering block 3"); 715 if (run_test(O_CREAT | O_RDWR | O_TRUNC | O_NDELAY, 716 S_ISGID | S_IRUSR | S_IWUSR, 0, 11)) { 717 tst_resm(TINFO, "Test case 3: mandatory locking with " 718 "NODELAY FAILED"); 719 } else { 720 tst_resm(TINFO, "Test case 3: mandatory locking with " 721 "NODELAY PASSED"); 722 } 723 tst_resm(TINFO, "Exiting block 3"); 724 } 725 cleanup(); 726 tst_exit(); 727} 728