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