• 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  *	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 
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 	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, SIGCHLD);
116 	if ((sigaction(SIGCHLD, &act, NULL)) < 0) {
117 		tst_resm(TFAIL, "SIGCHLD signal setup failed, errno: %d", errno);
118 		fail = 1;
119 	}
120 }
121 
cleanup(void)122 void cleanup(void)
123 {
124 	tst_rmdir();
125 
126 }
127 
do_child(void)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 
do_lock(int cmd,short type,short whence,int start,int len)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 
do_test(struct flock * fl,short type,short whence,int start,int len)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
compare_lock(struct flock * fl,short type,short whence,int start,int len,pid_t pid)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 
unlock_file(void)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 
str_type(int type)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 
parent_put(struct flock * l)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 
parent_get(struct flock * l)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 
child_put(struct flock * l)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 
child_get(struct flock * l)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 
stop_child(void)264 void stop_child(void)
265 {
266 	struct flock fl;
267 
268 	signal(SIGCHLD, SIG_DFL);
269 	fl.l_type = STOP;
270 	parent_put(&fl);
271 	wait(0);
272 }
273 
catch_child(void)274 void catch_child(void)
275 {
276 	tst_resm(TFAIL, "Unexpected death of child process");
277 	cleanup();
278 }
279 
main(int ac,char ** av)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