565 lines
13 KiB
C
565 lines
13 KiB
C
/*
|
|
*
|
|
* 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 <signal.h>
|
|
#include <fcntl.h>
|
|
#include "test.h"
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/types.h>
|
|
#include <sys/syscall.h>
|
|
#include <linux/unistd.h>
|
|
|
|
#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();
|
|
}
|