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  *	fcntl20.c
     23  *
     24  * DESCRIPTION
     25  *	Check locking of regions of a file
     26  *
     27  * ALGORITHM
     28  *	Test unlocking sections around a read lock
     29  *
     30  * USAGE
     31  *	fcntl20
     32  *
     33  * HISTORY
     34  *	07/2001 Ported by Wayne Boyer
     35  *
     36  * RESTRICTIONS
     37  *	None
     38  */
     39 
     40 #include <fcntl.h>
     41 #include <errno.h>
     42 #include <signal.h>
     43 #include <sys/stat.h>
     44 #include <sys/types.h>
     45 #include <sys/wait.h>
     46 #include <inttypes.h>
     47 #include "test.h"
     48 #include "safe_macros.h"
     49 
     50 #define STRINGSIZE	27
     51 #define STRING		"abcdefghijklmnopqrstuvwxyz\n"
     52 #define STOP		0xFFF0
     53 
     54 int parent_pipe[2];
     55 int child_pipe[2];
     56 int fd;
     57 pid_t parent_pid, child_pid;
     58 
     59 void parent_put();
     60 void parent_get();
     61 void child_put();
     62 void child_get();
     63 void stop_child();
     64 void compare_lock(struct flock *, short, short, int, int, pid_t);
     65 void unlock_file();
     66 void do_test(struct flock *, short, short, int, int);
     67 void catch_child();
     68 char *str_type();
     69 int do_lock(int, short, short, int, int);
     70 
     71 char *TCID = "fcntl20";
     72 int TST_TOTAL = 1;
     73 
     74 void setup(void);
     75 void cleanup(void);
     76 
     77 int fail = 0;
     78 
     79 /*
     80  * setup
     81  *	performs all ONE TIME setup for this test
     82  */
     83 void setup(void)
     84 {
     85 	char *buf = STRING;
     86 	char template[PATH_MAX];
     87 	struct sigaction act;
     88 
     89 	tst_sig(FORK, DEF_HANDLER, cleanup);
     90 
     91 	umask(0);
     92 
     93 	TEST_PAUSE;
     94 
     95 	parent_pid = getpid();
     96 
     97 	SAFE_PIPE(NULL, parent_pipe);
     98 	SAFE_PIPE(NULL, child_pipe);
     99 
    100 	tst_tmpdir();
    101 
    102 	snprintf(template, PATH_MAX, "fcntl20XXXXXX");
    103 
    104 	if ((fd = mkstemp(template)) == -1)
    105 		tst_resm(TFAIL | TERRNO, "mkstemp failed");
    106 
    107 	SAFE_WRITE(cleanup, 0, fd, buf, STRINGSIZE);
    108 
    109 	memset(&act, 0, sizeof(act));
    110 	act.sa_handler = catch_child;
    111 	sigemptyset(&act.sa_mask);
    112 	sigaddset(&act.sa_mask, SIGCLD);
    113 	if (sigaction(SIGCLD, &act, NULL) == -1)
    114 		tst_brkm(TFAIL | TERRNO, cleanup, "SIGCLD signal setup failed");
    115 }
    116 
    117 void cleanup(void)
    118 {
    119 	SAFE_CLOSE(NULL, fd);
    120 
    121 	tst_rmdir();
    122 
    123 }
    124 
    125 void do_child(void)
    126 {
    127 	struct flock fl;
    128 
    129 	close(parent_pipe[1]);
    130 	close(child_pipe[0]);
    131 	while (1) {
    132 		child_get(&fl);
    133 		if (fcntl(fd, F_GETLK, &fl) < 0) {
    134 			tst_resm(TFAIL | TERRNO, "fcntl on file failed");
    135 			fail = 1;
    136 		}
    137 		child_put(&fl);
    138 	}
    139 }
    140 
    141 int do_lock(int cmd, short type, short whence, int start, int len)
    142 {
    143 	struct flock fl;
    144 
    145 	fl.l_type = type;
    146 	fl.l_whence = whence;
    147 	fl.l_start = start;
    148 	fl.l_len = len;
    149 	return (fcntl(fd, cmd, &fl));
    150 }
    151 
    152 void do_test(struct flock *fl, short type, short whence, int start, int len)
    153 {
    154 	fl->l_type = type;
    155 	fl->l_whence = whence;
    156 	fl->l_start = start;
    157 	fl->l_len = len;
    158 	fl->l_pid = (short)0;
    159 
    160 	parent_put(fl);
    161 	parent_get(fl);
    162 }
    163 
    164 void
    165 compare_lock(struct flock *fl, short type, short whence, int start, int len,
    166 	     pid_t pid)
    167 {
    168 	if (fl->l_type != type) {
    169 		tst_resm(TFAIL, "lock type is wrong should be %s is %s",
    170 			 str_type(type), str_type(fl->l_type));
    171 		fail = 1;
    172 	}
    173 
    174 	if (fl->l_whence != whence) {
    175 		tst_resm(TFAIL, "lock whence is wrong should be %d is %d",
    176 			 whence, fl->l_whence);
    177 		fail = 1;
    178 	}
    179 
    180 	if (fl->l_start != start) {
    181 		tst_resm(TFAIL, "region starts in wrong place, should be"
    182 			 "%d is %" PRId64, start, (int64_t) fl->l_start);
    183 		fail = 1;
    184 	}
    185 
    186 	if (fl->l_len != len) {
    187 		tst_resm(TFAIL,
    188 			 "region length is wrong, should be %d is %" PRId64,
    189 			 len, (int64_t) fl->l_len);
    190 		fail = 1;
    191 	}
    192 
    193 	if (fl->l_pid != pid) {
    194 		tst_resm(TFAIL, "locking pid is wrong, should be %d is %d",
    195 			 pid, fl->l_pid);
    196 		fail = 1;
    197 	}
    198 }
    199 
    200 void unlock_file(void)
    201 {
    202 	struct flock fl;
    203 
    204 	if (do_lock(F_SETLK, (short)F_UNLCK, (short)0, 0, 0) < 0) {
    205 		tst_resm(TFAIL, "fcntl on file failed, errno =%d", errno);
    206 		fail = 1;
    207 	}
    208 	do_test(&fl, F_WRLCK, 0, 0, 0);
    209 	compare_lock(&fl, (short)F_UNLCK, (short)0, 0, 0, (pid_t) 0);
    210 }
    211 
    212 char *str_type(int type)
    213 {
    214 	static char buf[20];
    215 
    216 	switch (type) {
    217 	case 1:
    218 		return ("F_RDLCK");
    219 	case 2:
    220 		return ("F_WRLCK");
    221 	case 3:
    222 		return ("F_UNLCK");
    223 	default:
    224 		sprintf(buf, "BAD VALUE: %d", type);
    225 		return (buf);
    226 	}
    227 }
    228 
    229 void parent_put(struct flock *l)
    230 {
    231 	if (write(parent_pipe[1], l, sizeof(*l)) != sizeof(*l)) {
    232 		tst_resm(TFAIL, "couldn't send message to child");
    233 		fail = 1;
    234 	}
    235 }
    236 
    237 void parent_get(struct flock *l)
    238 {
    239 	if (read(child_pipe[0], l, sizeof(*l)) != sizeof(*l)) {
    240 		tst_resm(TFAIL, "couldn't get message from child");
    241 		fail = 1;
    242 	}
    243 }
    244 
    245 void child_put(struct flock *l)
    246 {
    247 	if (write(child_pipe[1], l, sizeof(*l)) != sizeof(*l)) {
    248 		tst_resm(TFAIL, "couldn't send message to parent");
    249 		fail = 1;
    250 	}
    251 }
    252 
    253 void child_get(struct flock *l)
    254 {
    255 	if (read(parent_pipe[0], l, sizeof(*l)) != sizeof(*l)) {
    256 		tst_resm(TFAIL, "couldn't get message from parent");
    257 		cleanup();
    258 	} else if (l->l_type == (short)STOP) {
    259 		exit(0);
    260 	}
    261 }
    262 
    263 void stop_child(void)
    264 {
    265 	struct flock fl;
    266 
    267 	signal(SIGCLD, SIG_DFL);
    268 	fl.l_type = STOP;
    269 	parent_put(&fl);
    270 	wait(0);
    271 }
    272 
    273 void catch_child(void)
    274 {
    275 	tst_resm(TFAIL, "Unexpected death of child process");
    276 	cleanup();
    277 }
    278 
    279 int main(int ac, char **av)
    280 {
    281 	struct flock tl;
    282 
    283 	int lc;
    284 
    285 	tst_parse_opts(ac, av, NULL, NULL);
    286 #ifdef UCLINUX
    287 	maybe_run_child(&do_child, "ddddd", &parent_pipe[0], &parent_pipe[1],
    288 			&child_pipe[0], &child_pipe[1], &fd);
    289 #endif
    290 
    291 	setup();		/* global setup */
    292 
    293 	/* Check for looping state if -i option is given */
    294 	for (lc = 0; TEST_LOOPING(lc); lc++) {
    295 		/* reset tst_count in case we are looping */
    296 		tst_count = 0;
    297 
    298 		if ((child_pid = FORK_OR_VFORK()) == 0) {	/* child */
    299 #ifdef UCLINUX
    300 			if (self_exec
    301 			    (av[0], "ddddd", parent_pipe[0], parent_pipe[1],
    302 			     child_pipe[0], child_pipe[1], fd) < 0) {
    303 				tst_resm(TFAIL, "self_exec failed");
    304 				cleanup();
    305 			}
    306 #else
    307 			do_child();
    308 #endif
    309 		}
    310 
    311 		if (child_pid < 0) {
    312 			tst_resm(TFAIL, "Fork failed");
    313 			cleanup();
    314 		}
    315 
    316 		(void)close(parent_pipe[0]);
    317 		(void)close(child_pipe[1]);
    318 
    319 /* //block1: */
    320 		tst_resm(TINFO, "Enter block 1");
    321 		/*
    322 		 * Add a read lock to the middle of the file and unlock a
    323 		 * section just before the lock
    324 		 */
    325 		if (do_lock(F_SETLK, (short)F_RDLCK, (short)0, 10, 5) < 0) {
    326 			tst_resm(TFAIL, "fcntl on file failed, errno =%d",
    327 				 errno);
    328 			fail = 1;
    329 		}
    330 
    331 		if (do_lock(F_SETLK, (short)F_UNLCK, (short)0, 5, 5) < 0) {
    332 			tst_resm(TFAIL, "fcntl on file failed, errno =%d",
    333 				 errno);
    334 			fail = 1;
    335 		}
    336 
    337 		/*
    338 		 * Test read lock
    339 		 */
    340 		do_test(&tl, (short)F_WRLCK, (short)0, 0, 0);
    341 		compare_lock(&tl, (short)F_RDLCK, (short)0, 10, 5, parent_pid);
    342 
    343 		/*
    344 		 * Test that the rest of the file is unlocked
    345 		 */
    346 		do_test(&tl, (short)F_WRLCK, (short)0, 15, 0);
    347 		compare_lock(&tl, (short)F_UNLCK, (short)0, 15, 0, (pid_t) 0);
    348 
    349 		/*
    350 		 * remove all the locks set above
    351 		 */
    352 		unlock_file();
    353 
    354 		if (fail) {
    355 			tst_resm(TINFO, "Test block 1: FAILED");
    356 		} else {
    357 			tst_resm(TINFO, "Test block 1: PASSED");
    358 		}
    359 		tst_resm(TINFO, "Exit block 1");
    360 
    361 /* //block2: */
    362 		tst_resm(TINFO, "Enter block 2");
    363 		fail = 0;
    364 		/*
    365 		 * Set a read lock in the middle and do an unlock that
    366 		 * ends at the first byte of the read lock.
    367 		 */
    368 		if (do_lock(F_SETLK, (short)F_RDLCK, (short)0, 10, 5) < 0) {
    369 			tst_resm(TFAIL, "fcntl on file failed, errno =%d",
    370 				 errno);
    371 			fail = 1;
    372 		}
    373 
    374 		if (do_lock(F_SETLK, (short)F_UNLCK, (short)0, 5, 6) < 0) {
    375 			tst_resm(TFAIL, "fcntl on file failed, errno =%d",
    376 				 errno);
    377 			fail = 1;
    378 		}
    379 
    380 		/*
    381 		 * Test read lock
    382 		 */
    383 		do_test(&tl, (short)F_WRLCK, (short)0, 0, 0);
    384 		compare_lock(&tl, (short)F_RDLCK, (short)0, 11, 4, parent_pid);
    385 
    386 		/*
    387 		 * Test to make sure the rest of the file is unlocked
    388 		 */
    389 		do_test(&tl, (short)F_WRLCK, (short)0, 15, 0);
    390 		compare_lock(&tl, (short)F_UNLCK, (short)0, 15, 0, (pid_t) 0);
    391 
    392 		/*
    393 		 * remove all the locks set above
    394 		 */
    395 		unlock_file();
    396 
    397 		if (fail)
    398 			tst_resm(TINFO, "Test block 2: FAILED");
    399 		else
    400 			tst_resm(TINFO, "Test block 2: PASSED");
    401 		tst_resm(TINFO, "Exit block 2");
    402 
    403 /* //block3: */
    404 		tst_resm(TINFO, "Enter block 3");
    405 		fail = 0;
    406 
    407 		/*
    408 		 * Set a read lock on the middle of the file and do an
    409 		 * unlock that overlaps the front of the read
    410 		 */
    411 		if (do_lock(F_SETLK, (short)F_RDLCK, (short)0, 10, 5) < 0) {
    412 			tst_resm(TFAIL, "fcntl on file failed, errno =%d",
    413 				 errno);
    414 			fail = 1;
    415 		}
    416 
    417 		if (do_lock(F_SETLK, (short)F_UNLCK, (short)0, 5, 8) < 0) {
    418 			tst_resm(TFAIL, "fcntl on file failed, errno =%d",
    419 				 errno);
    420 			fail = 1;
    421 		}
    422 
    423 		/*
    424 		 * Test the read lock
    425 		 */
    426 		do_test(&tl, (short)F_WRLCK, (short)0, 0, 0);
    427 		compare_lock(&tl, (short)F_RDLCK, (short)0, 13, 2, parent_pid);
    428 
    429 		/*
    430 		 * Test to make sure the rest of the file is unlocked
    431 		 */
    432 		do_test(&tl, (short)F_WRLCK, (short)0, 15, 0);
    433 		compare_lock(&tl, (short)F_UNLCK, (short)0, 15, 0, (pid_t) 0);
    434 
    435 		/*
    436 		 * remove all the locks set above
    437 		 */
    438 		unlock_file();
    439 
    440 		if (fail)
    441 			tst_resm(TINFO, "Test block 3: FAILED");
    442 		else
    443 			tst_resm(TINFO, "Test block 3: PASSED");
    444 		tst_resm(TINFO, "Exit block 3");
    445 
    446 /* //block4: */
    447 		tst_resm(TINFO, "Enter blcok 4");
    448 		fail = 0;
    449 
    450 		/*
    451 		 * Set a read lock in the middle of a file and unlock a
    452 		 * section in the middle of it
    453 		 */
    454 		if (do_lock(F_SETLK, (short)F_RDLCK, (short)0, 10, 10) < 0) {
    455 			tst_resm(TFAIL, "fcntl on file failed, errno =%d",
    456 				 errno);
    457 			fail = 1;
    458 		}
    459 
    460 		if (do_lock(F_SETLK, (short)F_UNLCK, (short)0, 13, 5) < 0) {
    461 			tst_resm(TFAIL, "fcntl on file failed, errno =%d",
    462 				 errno);
    463 			fail = 1;
    464 		}
    465 
    466 		/*
    467 		 * Test the first read lock
    468 		 */
    469 		do_test(&tl, (short)F_WRLCK, (short)0, 0, 0);
    470 		compare_lock(&tl, (short)F_RDLCK, (short)0, 10, 3, parent_pid);
    471 
    472 		/*
    473 		 * Test the second read lock
    474 		 */
    475 		do_test(&tl, (short)F_WRLCK, (short)0, 13, 0);
    476 		compare_lock(&tl, (short)F_RDLCK, (short)0, 18, 2, parent_pid);
    477 
    478 		/*
    479 		 * Test to make sure the rest of the file is unlocked
    480 		 */
    481 		do_test(&tl, (short)F_WRLCK, (short)0, 20, 0);
    482 		compare_lock(&tl, (short)F_UNLCK, (short)0, 20, 0, (pid_t) 0);
    483 
    484 		/*
    485 		 * remove all the locks set above
    486 		 */
    487 		unlock_file();
    488 
    489 		if (fail)
    490 			tst_resm(TINFO, "Test block 4: FAILED");
    491 		else
    492 			tst_resm(TINFO, "Test block 4: PASSED");
    493 		tst_resm(TINFO, "Exit block 4");
    494 
    495 /* //block5: */
    496 		tst_resm(TINFO, "Enter block 5");
    497 		fail = 0;
    498 
    499 		/*
    500 		 * Set a read lock in the middle of the file and do a
    501 		 * unlock that overlaps the end
    502 		 */
    503 		if (do_lock(F_SETLK, (short)F_RDLCK, (short)0, 10, 5) < 0) {
    504 			tst_resm(TFAIL, "fcntl on file failed, errno =%d",
    505 				 errno);
    506 			fail = 1;
    507 		}
    508 
    509 		if (do_lock(F_SETLK, (short)F_UNLCK, (short)0, 13, 5) < 0) {
    510 			tst_resm(TFAIL, "fcntl on file failed, errno =%d",
    511 				 errno);
    512 			fail = 1;
    513 		}
    514 
    515 		/*
    516 		 * Test the read lock
    517 		 */
    518 		do_test(&tl, (short)F_WRLCK, (short)0, 0, 0);
    519 		compare_lock(&tl, (short)F_RDLCK, (short)0, 10, 3, parent_pid);
    520 
    521 		/*
    522 		 * Test to make sure the rest of the file is unlocked
    523 		 */
    524 		do_test(&tl, (short)F_WRLCK, (short)0, 13, 0);
    525 		compare_lock(&tl, (short)F_UNLCK, (short)0, 13, 0, (pid_t) 0);
    526 
    527 		/*
    528 		 * remove all the locks set above
    529 		 */
    530 		unlock_file();
    531 
    532 		if (fail)
    533 			tst_resm(TINFO, "Test block 5: FAILED");
    534 		else
    535 			tst_resm(TINFO, "Test block 5: PASSED");
    536 		tst_resm(TINFO, "Exit block 5");
    537 
    538 /* //block6: */
    539 		tst_resm(TINFO, "Enter block 6");
    540 		fail = 0;
    541 
    542 		/*
    543 		 * Set read lock in the middle of the file and do an unlock
    544 		 * starting at the last byte of the read lock
    545 		 */
    546 		if (do_lock(F_SETLK, (short)F_RDLCK, (short)0, 10, 5) < 0) {
    547 			tst_resm(TFAIL, "fcntl on file failed, errno =%d",
    548 				 errno);
    549 			fail = 1;
    550 		}
    551 
    552 		if (do_lock(F_SETLK, (short)F_UNLCK, (short)0, 14, 5) < 0) {
    553 			tst_resm(TFAIL, "fcntl on file failed, errno =%d",
    554 				 errno);
    555 			fail = 1;
    556 		}
    557 
    558 		/*
    559 		 * Test read lock
    560 		 */
    561 		do_test(&tl, (short)F_WRLCK, (short)0, 10, 0);
    562 		compare_lock(&tl, (short)F_RDLCK, (short)0, 10, 4, parent_pid);
    563 
    564 		/*
    565 		 * Test to make sure the end of the file is unlocked
    566 		 */
    567 		do_test(&tl, (short)F_WRLCK, (short)0, 14, 0);
    568 		compare_lock(&tl, (short)F_UNLCK, (short)0, 14, 0, (pid_t) 0);
    569 
    570 		/*
    571 		 * remove all the locks set above
    572 		 */
    573 		unlock_file();
    574 
    575 		if (fail)
    576 			tst_resm(TINFO, "Test block 6: FAILED");
    577 		else
    578 			tst_resm(TINFO, "Test block 6: PASSED");
    579 		tst_resm(TINFO, "Exit block 6");
    580 
    581 /* //block7: */
    582 		tst_resm(TINFO, "Enter block 7");
    583 		fail = 0;
    584 
    585 		/*
    586 		 * Set a read lock at the middle of the file and do an
    587 		 * unlock that starts at the byte past the end of the read
    588 		 * lock
    589 		 */
    590 		if (do_lock(F_SETLK, (short)F_RDLCK, (short)0, 10, 5) < 0) {
    591 			tst_resm(TFAIL, "fcntl on file failed, errno =%d",
    592 				 errno);
    593 			fail = 1;
    594 		}
    595 
    596 		if (do_lock(F_SETLK, (short)F_UNLCK, (short)0, 16, 0) < 0) {
    597 			tst_resm(TFAIL, "fcntl on file failed, errno =%d",
    598 				 errno);
    599 			fail = 1;
    600 		}
    601 
    602 		/*
    603 		 * Test the read lock
    604 		 */
    605 		do_test(&tl, (short)F_WRLCK, (short)0, 0, 0);
    606 		compare_lock(&tl, (short)F_RDLCK, (short)0, 10, 5, parent_pid);
    607 
    608 		/*
    609 		 * Test to make sure the rest of the file is unlocked
    610 		 */
    611 		do_test(&tl, (short)F_WRLCK, (short)0, 16, 0);
    612 		compare_lock(&tl, (short)F_UNLCK, (short)0, 16, 0, (pid_t) 0);
    613 
    614 		/*
    615 		 * remove all the locks set above
    616 		 */
    617 		unlock_file();
    618 
    619 		if (fail)
    620 			tst_resm(TINFO, "Test block 7: FAILED");
    621 		else
    622 			tst_resm(TINFO, "Test block 7: PASSED");
    623 
    624 		tst_resm(TINFO, "Exit block 7");
    625 
    626 		stop_child();
    627 	}
    628 	cleanup();
    629 	tst_exit();
    630 }
    631