• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
setup(void)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, SIGCHLD);
113 	if (sigaction(SIGCHLD, &act, NULL) == -1)
114 		tst_brkm(TFAIL | TERRNO, cleanup, "SIGCHLD signal setup failed");
115 }
116 
cleanup(void)117 void cleanup(void)
118 {
119 	SAFE_CLOSE(NULL, fd);
120 
121 	tst_rmdir();
122 
123 }
124 
do_child(void)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 
do_lock(int cmd,short type,short whence,int start,int len)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 
do_test(struct flock * fl,short type,short whence,int start,int len)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
compare_lock(struct flock * fl,short type,short whence,int start,int len,pid_t pid)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 
unlock_file(void)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 
str_type(int type)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 
parent_put(struct flock * l)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 
parent_get(struct flock * l)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 
child_put(struct flock * l)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 
child_get(struct flock * l)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 
stop_child(void)263 void stop_child(void)
264 {
265 	struct flock fl;
266 
267 	signal(SIGCHLD, SIG_DFL);
268 	fl.l_type = STOP;
269 	parent_put(&fl);
270 	wait(0);
271 }
272 
catch_child(void)273 void catch_child(void)
274 {
275 	tst_resm(TFAIL, "Unexpected death of child process");
276 	cleanup();
277 }
278 
main(int ac,char ** av)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