Home | History | Annotate | Download | only in fcntl
      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  *	fcntl15.c
     23  *
     24  * DESCRIPTION
     25  *	Check that file locks are removed when file closed
     26  *
     27  * ALGORITHM
     28  *	Use three testcases to check removal of locks when a file is closed.
     29  *
     30  *	Case 1: Parent opens a file and duplicates it, places locks using
     31  *	both file descriptors then closes one descriptor, all locks should
     32  *	be removed.
     33  *
     34  *	Case 2: Open same file twice using(open), place locks using both
     35  *	descriptors then close on descriptor, locks on the file should be
     36  *	lost
     37  *
     38  *	Case 3: Open file twice, one by each process, set the locks and have
     39  *	a child check the locks. Remove the first file and have the child
     40  *	check the locks. Remove the first file and have child check locks
     41  *	again. Only locks set on first file should have been removed
     42  *
     43  * USAGE
     44  *	fcntl15
     45  *
     46  * HISTORY
     47  *	07/2001 Ported by Wayne Boyer
     48  * MODIFIED: - mridge (at) us.ibm.com -- changed getpid to syscall(get thread ID) for unique ID on NPTL threading
     49  *
     50  * RESTRICTIONS
     51  *	None
     52  */
     53 
     54 #include <signal.h>
     55 #include <fcntl.h>
     56 #include "test.h"
     57 #include <sys/types.h>
     58 #include <sys/wait.h>
     59 #include <sys/types.h>
     60 #include <sys/syscall.h>
     61 #include <linux/unistd.h>
     62 
     63 #define	DATA	"ABCDEFGHIJ"
     64 #define	DUP	0
     65 #define	OPEN	1
     66 #define	FORK_	2
     67 
     68 char *TCID = "fcntl15";
     69 int TST_TOTAL = 1;
     70 
     71 static int parent, child1, child2, status;
     72 static volatile sig_atomic_t parent_flag, child_flag, alarm_flag;
     73 static char tmpname[40];
     74 struct flock flock;
     75 
     76 #ifdef UCLINUX
     77 static char *argv0;		/* set by main, passed to self_exec */
     78 #endif
     79 
     80 
     81 void alarm_sig(int sig)
     82 {
     83 	signal(SIGALRM, alarm_sig);
     84 	alarm_flag = 1;
     85 	if ((syscall(__NR_gettid)) == parent) {
     86 		tst_resm(TINFO, "Alarm caught by parent");
     87 	} else {
     88 		tst_resm(TINFO, "Alarm caught by child");
     89 	}
     90 }
     91 
     92 void child_sig(int sig)
     93 {
     94 	signal(SIGUSR1, child_sig);
     95 	child_flag++;
     96 }
     97 
     98 void parent_sig(int sig)
     99 {
    100 	signal(SIGUSR2, parent_sig);
    101 	parent_flag++;
    102 }
    103 
    104 int dochild1(int file_flag, int file_mode)
    105 {
    106 	int fd_B;
    107 	sigset_t newmask, zeromask, oldmask;
    108 
    109 	if ((fd_B = open(tmpname, file_flag, file_mode)) < 0) {
    110 		perror("open on child1 file failed");
    111 		exit(1);
    112 	}
    113 
    114 	/* initialize lock structure for second 5 bytes of file */
    115 	flock.l_type = F_WRLCK;
    116 	flock.l_whence = 0;
    117 	flock.l_start = 5L;
    118 	flock.l_len = 5L;
    119 
    120 	/* set lock on child file descriptor */
    121 	if ((fcntl(fd_B, F_SETLK, &flock)) < 0) {
    122 		perror("child lock failed should have succeeded");
    123 		exit(1);
    124 	}
    125 
    126 	sigemptyset(&zeromask);
    127 	sigemptyset(&newmask);
    128 	sigaddset(&newmask, SIGUSR1);
    129 	sigaddset(&newmask, SIGUSR2);
    130 	sigaddset(&newmask, SIGALRM);
    131 	if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) {
    132 		perror("child1 sigprocmask SIG_BLOCK fail");
    133 		exit(1);
    134 	}
    135 	/*
    136 	 * send signal to parent here to tell parent we have locked the
    137 	 * file, thus allowing parent to proceed
    138 	 */
    139 	if ((kill(parent, SIGUSR1)) < 0) {
    140 		perror("child1 signal to parent failed");
    141 		exit(1);
    142 	}
    143 
    144 	/*
    145 	 * set alarm to break pause if parent fails to signal then spin till
    146 	 * parent ready
    147 	 */
    148 	alarm(60);
    149 	while (parent_flag == 0 && alarm_flag == 0)
    150 		sigsuspend(&zeromask);
    151 	alarm((unsigned)0);
    152 	if (parent_flag != 1) {
    153 		perror("pause in child1 terminated without "
    154 		       "SIGUSR2 signal from parent");
    155 		exit(1);
    156 	}
    157 	parent_flag = 0;
    158 	alarm_flag = 0;
    159 	if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) {
    160 		perror("child1 sigprocmask SIG_SETMASK fail");
    161 		exit(1);
    162 	}
    163 
    164 	/* wait for child2 to complete then cleanup */
    165 	sleep(10);
    166 	close(fd_B);
    167 	exit(0);
    168 }
    169 
    170 #ifdef UCLINUX
    171 int uc_file_flag, uc_file_mode, uc_dup_flag;
    172 
    173 void dochild1_uc(void)
    174 {
    175 	dochild1(uc_file_flag, uc_file_mode);
    176 }
    177 
    178 void dochild2_uc(void)
    179 {
    180 	dochild2(uc_file_flag, uc_dup_flag);
    181 }
    182 #endif
    183 
    184 int dofork(int file_flag, int file_mode)
    185 {
    186 	/* create child process */
    187 	if ((child1 = FORK_OR_VFORK()) < 0) {
    188 		perror("Fork failure");
    189 		return 1;
    190 	}
    191 
    192 	/* child1 */
    193 	if (child1 == 0) {
    194 #ifdef UCLINUX
    195 		if (self_exec(argv0, "nddds", 1, file_flag, file_mode,
    196 			      parent, tmpname) < 0) {
    197 			perror("self_exec failure");
    198 			return 1;
    199 		}
    200 #else
    201 		dochild1(file_flag, file_mode);
    202 #endif
    203 	} else {
    204 		/*
    205 		 * need to wait for child1 to open, and lock the area of the
    206 		 * file prior to continuing on from here
    207 		 */
    208 		sigset_t newmask, zeromask, oldmask;
    209 		sigemptyset(&zeromask);
    210 		sigemptyset(&newmask);
    211 		sigaddset(&newmask, SIGUSR1);
    212 		sigaddset(&newmask, SIGUSR2);
    213 		sigaddset(&newmask, SIGALRM);
    214 		if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) {
    215 			perror("parent sigprocmask SIG_BLOCK fail");
    216 			exit(1);
    217 		}
    218 
    219 		/*
    220 		 * set alarm to break pause if parent fails to signal then spin till
    221 		 * parent ready
    222 		 */
    223 		alarm(60);
    224 		while (child_flag == 0 && alarm_flag == 0)
    225 			sigsuspend(&zeromask);
    226 		alarm((unsigned)0);
    227 		if (child_flag != 1) {
    228 			perror("parent paused without SIGUSR1 " "from child");
    229 			exit(1);
    230 		}
    231 		child_flag = 0;
    232 		alarm_flag = 0;
    233 		if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) {
    234 			perror("parent sigprocmask SIG_SETMASK fail");
    235 			exit(1);
    236 		}
    237 	}
    238 	return 0;
    239 }
    240 
    241 int dochild2(int file_flag, int file_mode, int dup_flag)
    242 {
    243 	int fd_C;
    244 	sigset_t newmask, zeromask, oldmask;
    245 
    246 	if ((fd_C = open(tmpname, file_flag, file_mode)) < 0) {
    247 		perror("open on child2 file failed");
    248 		exit(1);
    249 	}
    250 
    251 	/* initialize lock structure for first 5 bytes of file */
    252 	flock.l_type = F_WRLCK;
    253 	flock.l_whence = 0;
    254 	flock.l_start = 0L;
    255 	flock.l_len = 5L;
    256 
    257 	/* Set lock on child file descriptor */
    258 	if ((fcntl(fd_C, F_SETLK, &flock)) >= 0) {
    259 		tst_resm(TFAIL, "First child2 lock succeeded should "
    260 			 "have failed");
    261 		exit(1);
    262 	}
    263 
    264 	/* initialize lock structure for second 5 bytes of file */
    265 	flock.l_type = F_WRLCK;
    266 	flock.l_whence = 0;
    267 	flock.l_start = 5L;
    268 	flock.l_len = 5L;
    269 
    270 	/* set lock on child file descriptor */
    271 	if ((fcntl(fd_C, F_SETLK, &flock)) >= 0) {
    272 		tst_resm(TFAIL, "second child2 lock succeeded should have "
    273 			 "failed");
    274 		exit(1);
    275 	}
    276 
    277 	sigemptyset(&zeromask);
    278 	sigemptyset(&newmask);
    279 	sigaddset(&newmask, SIGUSR1);
    280 	sigaddset(&newmask, SIGUSR2);
    281 	sigaddset(&newmask, SIGALRM);
    282 	if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) {
    283 		perror("child2 sigprocmask SIG_BLOCK fail");
    284 		exit(1);
    285 	}
    286 	/*
    287 	 * send signal to parent here to tell parent we have locked the
    288 	 * file, thus allowing parent to proceed
    289 	 */
    290 	if ((kill(parent, SIGUSR1)) < 0) {
    291 		perror("child2 signal to parent failed");
    292 		exit(1);
    293 	}
    294 
    295 	/*
    296 	 * set alarm to break pause if parent fails to signal then spin till
    297 	 * parent ready
    298 	 */
    299 	alarm(60);
    300 	while (parent_flag == 0 && alarm_flag == 0)
    301 		sigsuspend(&zeromask);
    302 	alarm((unsigned)0);
    303 	if (parent_flag != 1) {
    304 		perror("pause in child2 terminated without "
    305 		       "SIGUSR2 signal from parent");
    306 		exit(1);
    307 	}
    308 	parent_flag = 0;
    309 	alarm_flag = 0;
    310 	if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) {
    311 		perror("child2 sigprocmask SIG_SETMASK fail");
    312 		exit(1);
    313 	}
    314 
    315 	/* initialize lock structure for first 5 bytes of file */
    316 	flock.l_type = F_WRLCK;
    317 	flock.l_whence = 0;
    318 	flock.l_start = 0L;
    319 	flock.l_len = 5L;
    320 
    321 	/* set lock on child file descriptor */
    322 	if ((fcntl(fd_C, F_SETLK, &flock)) < 0) {
    323 		tst_resm(TFAIL, "third child2 lock failed should have "
    324 			 "succeeded");
    325 		exit(1);
    326 	}
    327 
    328 	/* Initialize lock structure for second 5 bytes of file */
    329 	flock.l_type = F_WRLCK;
    330 	flock.l_whence = 0;
    331 	flock.l_start = 5L;
    332 	flock.l_len = 5L;
    333 
    334 	/* set lock on child file descriptor */
    335 	if (dup_flag == FORK_) {
    336 		if ((fcntl(fd_C, F_SETLK, &flock)) >= 0) {
    337 			tst_resm(TFAIL, "fourth child2 lock succeeded "
    338 				 "should have failed");
    339 			exit(1);
    340 		}
    341 	} else {
    342 		if ((fcntl(fd_C, F_SETLK, &flock)) < 0) {
    343 			tst_resm(TFAIL, "fourth child2 lock failed "
    344 				 "should have succeeded");
    345 			exit(1);
    346 		}
    347 	}
    348 	close(fd_C);
    349 	exit(0);
    350 }
    351 
    352 void setup(void)
    353 {
    354 	tst_sig(FORK, DEF_HANDLER, NULL);
    355 
    356 	TEST_PAUSE;
    357 }
    358 
    359 int run_test(int file_flag, int file_mode, int dup_flag)
    360 {
    361 	int fd_A, fd_B;
    362 	fd_B = -1;
    363 	sigset_t newmask, zeromask, oldmask;
    364 
    365 	/* setup to catch SIGUSR1 signal from child process */
    366 	if ((signal(SIGUSR1, child_sig)) == SIG_ERR) {
    367 		perror("Signal setup for SIGUSR1 failed");
    368 	}
    369 
    370 	/* setup to catch SIGUSR2 signal from parent */
    371 	if ((signal(SIGUSR2, parent_sig)) == SIG_ERR) {
    372 		perror("Signal setup for SIGUSR1 failed");
    373 	}
    374 
    375 	parent = syscall(__NR_gettid);
    376 
    377 	tst_tmpdir();
    378 	/* setup temporary file name */
    379 	sprintf(tmpname, "fcntl15.%d", parent);
    380 
    381 	/* initialize signal flags */
    382 	child_flag = parent_flag = alarm_flag = 0;
    383 
    384 	if ((fd_A = open(tmpname, file_flag, file_mode)) < 0) {
    385 		perror("open first parent file failed");
    386 		tst_rmdir();
    387 		return 1;
    388 	}
    389 
    390 	/* write some data to the file */
    391 	(void)write(fd_A, DATA, 10);
    392 
    393 	if (dup_flag) {
    394 		if (dup_flag == FORK_) {
    395 			dofork(file_flag, file_mode);
    396 		} else {
    397 			if ((fd_B = open(tmpname, file_flag, file_mode)) < 0) {
    398 				perror("open second parent file failed");
    399 				tst_rmdir();
    400 				return 1;
    401 			}
    402 		}
    403 	} else {
    404 		/* create a second file descriptor from first file */
    405 		if ((fd_B = fcntl(fd_A, F_DUPFD, 0)) < 0) {
    406 			perror("dup of second parent file failed");
    407 			tst_rmdir();
    408 			return 1;
    409 		}
    410 	}
    411 
    412 	/*
    413 	 * initialize lock structure for first lock on first
    414 	 * 5 bytes of file
    415 	 */
    416 	flock.l_type = F_WRLCK;
    417 	flock.l_whence = 0;
    418 	flock.l_start = 0L;
    419 	flock.l_len = 5L;
    420 
    421 	/* set lock on first file descriptor */
    422 	if ((fcntl(fd_A, F_SETLK, &flock)) < 0) {
    423 		perror("Attempt to set first parent lock failed");
    424 		tst_rmdir();
    425 		return 1;
    426 	}
    427 
    428 	if (dup_flag != FORK_) {
    429 		/* initialize lock structure for last 5 bytes of file */
    430 		flock.l_type = F_WRLCK;
    431 		flock.l_whence = 0;
    432 		flock.l_start = 5L;
    433 		flock.l_len = 5L;
    434 
    435 		/* set lock on second file descriptor */
    436 		if ((fcntl(fd_B, F_SETLK, &flock)) < 0) {
    437 			perror("Attempt to set second parent lock failed");
    438 			tst_rmdir();
    439 			return 1;
    440 		}
    441 	}
    442 
    443 	/* create child process */
    444 	if ((child2 = FORK_OR_VFORK()) < 0) {
    445 		perror("Fork failure");
    446 		tst_rmdir();
    447 		return 1;
    448 	} else if (child2 == 0) {	/* child */
    449 #ifdef UCLINUX
    450 		if (self_exec(argv0, "ndddds", 2, file_flag, file_mode,
    451 			      dup_flag, parent, tmpname) < 0)
    452 			tst_brkm(TBROK | TERRNO, NULL, "self_exec failed");
    453 #else
    454 		dochild2(file_flag, file_mode, dup_flag);
    455 #endif
    456 	}
    457 
    458 	/* parent */
    459 
    460 	/*
    461 	 * Set alarm to break pause if child fails to signal then spin till
    462 	 * child is ready
    463 	 */
    464 
    465 	sigemptyset(&zeromask);
    466 	sigemptyset(&newmask);
    467 	sigaddset(&newmask, SIGUSR1);
    468 	sigaddset(&newmask, SIGUSR2);
    469 	sigaddset(&newmask, SIGALRM);
    470 	if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) {
    471 		perror("parent sigprocmask SIG_BLOCK fail");
    472 		exit(1);
    473 	}
    474 
    475 	/*
    476 	 * set alarm to break pause if parent fails to signal then spin till
    477 	 * parent ready
    478 	 */
    479 	alarm(60);
    480 	while (child_flag == 0 && alarm_flag == 0)
    481 		sigsuspend(&zeromask);
    482 	alarm((unsigned)0);
    483 	if (child_flag != 1) {
    484 		perror("parent paused without SIGUSR1 " "from child");
    485 		exit(1);
    486 	}
    487 	child_flag = 0;
    488 	alarm_flag = 0;
    489 	if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) {
    490 		perror("parent sigprocmask SIG_SETMASK fail");
    491 		exit(1);
    492 	}
    493 
    494 	/* close the first file then signal child to test locks */
    495 	close(fd_A);
    496 	if ((kill(child2, SIGUSR2)) < 0) {
    497 		perror("Signal to child2 failed");
    498 		tst_rmdir();
    499 		return 1;
    500 	}
    501 
    502 	if (dup_flag == FORK_) {
    503 		if ((kill(child1, SIGUSR2)) < 0) {
    504 			perror("Signal to child1 failed");
    505 			tst_rmdir();
    506 			return 1;
    507 		}
    508 	}
    509 	/* wait for child to complete then cleanup */
    510 	while ((wait(&status)) > 0) {
    511 		if (status >> 8 != 0) {
    512 			tst_resm(TFAIL, "Expected 0 got %d", status >> 8);
    513 			tst_rmdir();
    514 			return 1;
    515 		}
    516 	}
    517 	if (dup_flag != FORK_) {
    518 		close(fd_B);
    519 	}
    520 	unlink(tmpname);
    521 	tst_rmdir();
    522 	return 0;
    523 }
    524 
    525 int main(int ac, char **av)
    526 {
    527 	int lc;
    528 
    529 	tst_parse_opts(ac, av, NULL, NULL);
    530 #ifdef UCLINUX
    531 	maybe_run_child(&dochild1_uc, "nddds", 1, &uc_file_flag,
    532 			&uc_file_mode, &parent, tmpname);
    533 	maybe_run_child(&dochild2_uc, "nddds", 1, &uc_file_flag,
    534 			&uc_file_mode, &uc_dup_flag, &parent, tmpname);
    535 	argv0 = av[0];
    536 #endif
    537 
    538 	setup();
    539 
    540 	for (lc = 0; TEST_LOOPING(lc); lc++) {
    541 		tst_count = 0;
    542 
    543 		if ((signal(SIGALRM, alarm_sig)) == SIG_ERR) {
    544 			perror("SIGALRM signal set up failed");
    545 			exit(1);
    546 		}
    547 
    548 		if (run_test(O_CREAT | O_RDWR | O_TRUNC, 0777, DUP))
    549 			tst_resm(TFAIL, "Test 1: test with \"dup\" FAILED");
    550 		else
    551 			tst_resm(TPASS, "Test 1: test with \"dup\" PASSED");
    552 
    553 		if (run_test(O_CREAT | O_RDWR | O_TRUNC, 0777, OPEN))
    554 			tst_resm(TFAIL, "Test 2: test with \"open\" FAILED");
    555 		else
    556 			tst_resm(TPASS, "Test 2: test with \"open\" PASSED");
    557 
    558 		if (run_test(O_CREAT | O_RDWR | O_TRUNC, 0777, FORK_))
    559 			tst_resm(TFAIL, "Test 3: test with \"fork\" FAILED");
    560 		else
    561 			tst_resm(TPASS, "Test 3: test with \"fork\" PASSED");
    562 	}
    563 	tst_exit();
    564 }
    565