/* * * Copyright (c) International Business Machines Corp., 2001 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * NAME * fcntl15.c * * DESCRIPTION * Check that file locks are removed when file closed * * ALGORITHM * Use three testcases to check removal of locks when a file is closed. * * Case 1: Parent opens a file and duplicates it, places locks using * both file descriptors then closes one descriptor, all locks should * be removed. * * Case 2: Open same file twice using(open), place locks using both * descriptors then close on descriptor, locks on the file should be * lost * * Case 3: Open file twice, one by each process, set the locks and have * a child check the locks. Remove the first file and have the child * check the locks. Remove the first file and have child check locks * again. Only locks set on first file should have been removed * * USAGE * fcntl15 * * HISTORY * 07/2001 Ported by Wayne Boyer * MODIFIED: - mridge@us.ibm.com -- changed getpid to syscall(get thread ID) for unique ID on NPTL threading * * RESTRICTIONS * None */ #include #include #include "test.h" #include #include #include #include #include #define DATA "ABCDEFGHIJ" #define DUP 0 #define OPEN 1 #define FORK_ 2 char *TCID = "fcntl15"; int TST_TOTAL = 1; static int parent, child1, child2, status; static volatile sig_atomic_t parent_flag, child_flag, alarm_flag; static char tmpname[40]; struct flock flock; #ifdef UCLINUX static char *argv0; /* set by main, passed to self_exec */ #endif void alarm_sig(int sig) { signal(SIGALRM, alarm_sig); alarm_flag = 1; if ((syscall(__NR_gettid)) == parent) { tst_resm(TINFO, "Alarm caught by parent"); } else { tst_resm(TINFO, "Alarm caught by child"); } } void child_sig(int sig) { signal(SIGUSR1, child_sig); child_flag++; } void parent_sig(int sig) { signal(SIGUSR2, parent_sig); parent_flag++; } int dochild1(int file_flag, int file_mode) { int fd_B; sigset_t newmask, zeromask, oldmask; if ((fd_B = open(tmpname, file_flag, file_mode)) < 0) { perror("open on child1 file failed"); exit(1); } /* initialize lock structure for second 5 bytes of file */ flock.l_type = F_WRLCK; flock.l_whence = 0; flock.l_start = 5L; flock.l_len = 5L; /* set lock on child file descriptor */ if ((fcntl(fd_B, F_SETLK, &flock)) < 0) { perror("child lock failed should have succeeded"); exit(1); } sigemptyset(&zeromask); sigemptyset(&newmask); sigaddset(&newmask, SIGUSR1); sigaddset(&newmask, SIGUSR2); sigaddset(&newmask, SIGALRM); if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) { perror("child1 sigprocmask SIG_BLOCK fail"); exit(1); } /* * send signal to parent here to tell parent we have locked the * file, thus allowing parent to proceed */ if ((kill(parent, SIGUSR1)) < 0) { perror("child1 signal to parent failed"); exit(1); } /* * set alarm to break pause if parent fails to signal then spin till * parent ready */ alarm(60); while (parent_flag == 0 && alarm_flag == 0) sigsuspend(&zeromask); alarm((unsigned)0); if (parent_flag != 1) { perror("pause in child1 terminated without " "SIGUSR2 signal from parent"); exit(1); } parent_flag = 0; alarm_flag = 0; if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) { perror("child1 sigprocmask SIG_SETMASK fail"); exit(1); } /* wait for child2 to complete then cleanup */ sleep(10); close(fd_B); exit(0); } #ifdef UCLINUX int uc_file_flag, uc_file_mode, uc_dup_flag; void dochild1_uc(void) { dochild1(uc_file_flag, uc_file_mode); } void dochild2_uc(void) { dochild2(uc_file_flag, uc_dup_flag); } #endif int dofork(int file_flag, int file_mode) { /* create child process */ if ((child1 = FORK_OR_VFORK()) < 0) { perror("Fork failure"); return 1; } /* child1 */ if (child1 == 0) { #ifdef UCLINUX if (self_exec(argv0, "nddds", 1, file_flag, file_mode, parent, tmpname) < 0) { perror("self_exec failure"); return 1; } #else dochild1(file_flag, file_mode); #endif } else { /* * need to wait for child1 to open, and lock the area of the * file prior to continuing on from here */ sigset_t newmask, zeromask, oldmask; sigemptyset(&zeromask); sigemptyset(&newmask); sigaddset(&newmask, SIGUSR1); sigaddset(&newmask, SIGUSR2); sigaddset(&newmask, SIGALRM); if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) { perror("parent sigprocmask SIG_BLOCK fail"); exit(1); } /* * set alarm to break pause if parent fails to signal then spin till * parent ready */ alarm(60); while (child_flag == 0 && alarm_flag == 0) sigsuspend(&zeromask); alarm((unsigned)0); if (child_flag != 1) { perror("parent paused without SIGUSR1 " "from child"); exit(1); } child_flag = 0; alarm_flag = 0; if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) { perror("parent sigprocmask SIG_SETMASK fail"); exit(1); } } return 0; } int dochild2(int file_flag, int file_mode, int dup_flag) { int fd_C; sigset_t newmask, zeromask, oldmask; if ((fd_C = open(tmpname, file_flag, file_mode)) < 0) { perror("open on child2 file failed"); exit(1); } /* initialize lock structure for first 5 bytes of file */ flock.l_type = F_WRLCK; flock.l_whence = 0; flock.l_start = 0L; flock.l_len = 5L; /* Set lock on child file descriptor */ if ((fcntl(fd_C, F_SETLK, &flock)) >= 0) { tst_resm(TFAIL, "First child2 lock succeeded should " "have failed"); exit(1); } /* initialize lock structure for second 5 bytes of file */ flock.l_type = F_WRLCK; flock.l_whence = 0; flock.l_start = 5L; flock.l_len = 5L; /* set lock on child file descriptor */ if ((fcntl(fd_C, F_SETLK, &flock)) >= 0) { tst_resm(TFAIL, "second child2 lock succeeded should have " "failed"); exit(1); } sigemptyset(&zeromask); sigemptyset(&newmask); sigaddset(&newmask, SIGUSR1); sigaddset(&newmask, SIGUSR2); sigaddset(&newmask, SIGALRM); if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) { perror("child2 sigprocmask SIG_BLOCK fail"); exit(1); } /* * send signal to parent here to tell parent we have locked the * file, thus allowing parent to proceed */ if ((kill(parent, SIGUSR1)) < 0) { perror("child2 signal to parent failed"); exit(1); } /* * set alarm to break pause if parent fails to signal then spin till * parent ready */ alarm(60); while (parent_flag == 0 && alarm_flag == 0) sigsuspend(&zeromask); alarm((unsigned)0); if (parent_flag != 1) { perror("pause in child2 terminated without " "SIGUSR2 signal from parent"); exit(1); } parent_flag = 0; alarm_flag = 0; if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) { perror("child2 sigprocmask SIG_SETMASK fail"); exit(1); } /* initialize lock structure for first 5 bytes of file */ flock.l_type = F_WRLCK; flock.l_whence = 0; flock.l_start = 0L; flock.l_len = 5L; /* set lock on child file descriptor */ if ((fcntl(fd_C, F_SETLK, &flock)) < 0) { tst_resm(TFAIL, "third child2 lock failed should have " "succeeded"); exit(1); } /* Initialize lock structure for second 5 bytes of file */ flock.l_type = F_WRLCK; flock.l_whence = 0; flock.l_start = 5L; flock.l_len = 5L; /* set lock on child file descriptor */ if (dup_flag == FORK_) { if ((fcntl(fd_C, F_SETLK, &flock)) >= 0) { tst_resm(TFAIL, "fourth child2 lock succeeded " "should have failed"); exit(1); } } else { if ((fcntl(fd_C, F_SETLK, &flock)) < 0) { tst_resm(TFAIL, "fourth child2 lock failed " "should have succeeded"); exit(1); } } close(fd_C); exit(0); } void setup(void) { tst_sig(FORK, DEF_HANDLER, NULL); TEST_PAUSE; } int run_test(int file_flag, int file_mode, int dup_flag) { int fd_A, fd_B; fd_B = -1; sigset_t newmask, zeromask, oldmask; /* setup to catch SIGUSR1 signal from child process */ if ((signal(SIGUSR1, child_sig)) == SIG_ERR) { perror("Signal setup for SIGUSR1 failed"); } /* setup to catch SIGUSR2 signal from parent */ if ((signal(SIGUSR2, parent_sig)) == SIG_ERR) { perror("Signal setup for SIGUSR1 failed"); } parent = syscall(__NR_gettid); tst_tmpdir(); /* setup temporary file name */ sprintf(tmpname, "fcntl15.%d", parent); /* initialize signal flags */ child_flag = parent_flag = alarm_flag = 0; if ((fd_A = open(tmpname, file_flag, file_mode)) < 0) { perror("open first parent file failed"); tst_rmdir(); return 1; } /* write some data to the file */ (void)write(fd_A, DATA, 10); if (dup_flag) { if (dup_flag == FORK_) { dofork(file_flag, file_mode); } else { if ((fd_B = open(tmpname, file_flag, file_mode)) < 0) { perror("open second parent file failed"); tst_rmdir(); return 1; } } } else { /* create a second file descriptor from first file */ if ((fd_B = fcntl(fd_A, F_DUPFD, 0)) < 0) { perror("dup of second parent file failed"); tst_rmdir(); return 1; } } /* * initialize lock structure for first lock on first * 5 bytes of file */ flock.l_type = F_WRLCK; flock.l_whence = 0; flock.l_start = 0L; flock.l_len = 5L; /* set lock on first file descriptor */ if ((fcntl(fd_A, F_SETLK, &flock)) < 0) { perror("Attempt to set first parent lock failed"); tst_rmdir(); return 1; } if (dup_flag != FORK_) { /* initialize lock structure for last 5 bytes of file */ flock.l_type = F_WRLCK; flock.l_whence = 0; flock.l_start = 5L; flock.l_len = 5L; /* set lock on second file descriptor */ if ((fcntl(fd_B, F_SETLK, &flock)) < 0) { perror("Attempt to set second parent lock failed"); tst_rmdir(); return 1; } } /* create child process */ if ((child2 = FORK_OR_VFORK()) < 0) { perror("Fork failure"); tst_rmdir(); return 1; } else if (child2 == 0) { /* child */ #ifdef UCLINUX if (self_exec(argv0, "ndddds", 2, file_flag, file_mode, dup_flag, parent, tmpname) < 0) tst_brkm(TBROK | TERRNO, NULL, "self_exec failed"); #else dochild2(file_flag, file_mode, dup_flag); #endif } /* parent */ /* * Set alarm to break pause if child fails to signal then spin till * child is ready */ sigemptyset(&zeromask); sigemptyset(&newmask); sigaddset(&newmask, SIGUSR1); sigaddset(&newmask, SIGUSR2); sigaddset(&newmask, SIGALRM); if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) { perror("parent sigprocmask SIG_BLOCK fail"); exit(1); } /* * set alarm to break pause if parent fails to signal then spin till * parent ready */ alarm(60); while (child_flag == 0 && alarm_flag == 0) sigsuspend(&zeromask); alarm((unsigned)0); if (child_flag != 1) { perror("parent paused without SIGUSR1 " "from child"); exit(1); } child_flag = 0; alarm_flag = 0; if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) { perror("parent sigprocmask SIG_SETMASK fail"); exit(1); } /* close the first file then signal child to test locks */ close(fd_A); if ((kill(child2, SIGUSR2)) < 0) { perror("Signal to child2 failed"); tst_rmdir(); return 1; } if (dup_flag == FORK_) { if ((kill(child1, SIGUSR2)) < 0) { perror("Signal to child1 failed"); tst_rmdir(); return 1; } } /* wait for child to complete then cleanup */ while ((wait(&status)) > 0) { if (status >> 8 != 0) { tst_resm(TFAIL, "Expected 0 got %d", status >> 8); tst_rmdir(); return 1; } } if (dup_flag != FORK_) { close(fd_B); } unlink(tmpname); tst_rmdir(); return 0; } int main(int ac, char **av) { int lc; tst_parse_opts(ac, av, NULL, NULL); #ifdef UCLINUX maybe_run_child(&dochild1_uc, "nddds", 1, &uc_file_flag, &uc_file_mode, &parent, tmpname); maybe_run_child(&dochild2_uc, "nddds", 1, &uc_file_flag, &uc_file_mode, &uc_dup_flag, &parent, tmpname); argv0 = av[0]; #endif setup(); for (lc = 0; TEST_LOOPING(lc); lc++) { tst_count = 0; if ((signal(SIGALRM, alarm_sig)) == SIG_ERR) { perror("SIGALRM signal set up failed"); exit(1); } if (run_test(O_CREAT | O_RDWR | O_TRUNC, 0777, DUP)) tst_resm(TFAIL, "Test 1: test with \"dup\" FAILED"); else tst_resm(TPASS, "Test 1: test with \"dup\" PASSED"); if (run_test(O_CREAT | O_RDWR | O_TRUNC, 0777, OPEN)) tst_resm(TFAIL, "Test 2: test with \"open\" FAILED"); else tst_resm(TPASS, "Test 2: test with \"open\" PASSED"); if (run_test(O_CREAT | O_RDWR | O_TRUNC, 0777, FORK_)) tst_resm(TFAIL, "Test 3: test with \"fork\" FAILED"); else tst_resm(TPASS, "Test 3: test with \"fork\" PASSED"); } tst_exit(); }