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