fcntl16.c revision ec6edca7aa42b6affd989ef91b5897f96795e40f
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20/*
21 * NAME
22 *	fcntl16.c
23 *
24 * DESCRIPTION
25 *	Additional file locking test cases for checking proper notifictaion
26 *	of processes on lock change
27 *
28 * ALGORITHM
29 *	Various test cases are used to lock a file opened without mandatory
30 *	locking, with madatory locking and mandatory locking with NOBLOCK.
31 *	Checking that processes waiting on lock boundaries are notified
32 *	properly when boundaries change
33 *
34 * USAGE
35 *	fcntl16
36 *
37 * HISTORY
38 *	07/2001 Ported by Wayne Boyer
39 *	04/2002 wjhuie sigset cleanups
40 *
41 * RESTRICTIONS
42 *	None
43 */
44
45#include <fcntl.h>
46#include <signal.h>
47#include <errno.h>
48#include "test.h"
49#include "usctest.h"
50#include <sys/stat.h>
51#include <sys/types.h>
52#include <sys/wait.h>
53
54#define SKIPVAL 0x0f00
55//#define       SKIP    SKIPVAL, 0, 0L, 0L, IGNORED
56#define SKIP 0,0,0L,0L,0
57#if (SKIPVAL == F_RDLCK) || (SKIPVAL == F_WRLCK)
58#error invalid SKIP, must not be F_RDLCK or F_WRLCK
59#endif
60
61#define	IGNORED		0
62#define	NOBLOCK		2	/* immediate success */
63#define	WILLBLOCK	3	/* blocks, succeeds, parent unlocks records */
64#define	TIME_OUT	10
65
66typedef struct {
67	short type;
68	short whence;
69	long start;
70	long len;
71	short flag;
72} lock;
73
74typedef struct {
75	lock parent_a;
76	lock parent_b;
77	lock child_a;
78	lock child_b;
79	lock parent_c;
80	lock parent_d;
81} testcase;
82
83static testcase testcases[] = {
84	/* #1 Parent_a making a write lock on entire file */
85	{{F_WRLCK, 0, 0L, 0L, IGNORED},
86	 /* Parent_b skipped */
87	 {SKIP},
88	 /* Child_a read lock on byte 1 to byte 5 */
89	 {F_RDLCK, 0, 0L, 5L, NOBLOCK},
90	 /* Child_b read lock on byte 6 to byte 10 */
91	 {F_RDLCK, 0, 6L, 5L, NOBLOCK},
92	 /*
93	  * Parent_c read lock on entire file
94	  */
95	 {F_RDLCK, 0, 0L, 0L, IGNORED},
96	 /* Parent_d skipped */
97	 {SKIP},},
98
99	/* #2 Parent_a making a write lock on entire file */
100	{{F_WRLCK, 0, 0L, 0L, IGNORED},
101	 /* Parent_b skipped */
102	 {SKIP},
103	 /* Child_a read lock on byte 1 to byte 5 */
104	 {F_RDLCK, 0, 0L, 5L, WILLBLOCK},
105	 /* Child_b read lock on byte 6 to byte 10 */
106	 {F_RDLCK, 0, 6L, 5L, WILLBLOCK},
107	 /*
108	  * Parent_c write lock on entire
109	  * file
110	  */
111	 {F_WRLCK, 0, 0L, 0L, IGNORED},
112	 /* Parent_d skipped */
113	 {SKIP},},
114
115	/* #3 Parent_a making a write lock on entire file */
116	{{F_WRLCK, 0, 0L, 0L, IGNORED},
117	 /* Parent_b skipped */
118	 {SKIP},
119	 /* Child_a read lock on byte 2 to byte 4 */
120	 {F_RDLCK, 0, 2L, 3L, WILLBLOCK},
121	 /* Child_b read lock on byte 6 to byte 8 */
122	 {F_RDLCK, 0, 6L, 3L, WILLBLOCK},
123	 /*
124	  * Parent_c read lock on byte 3 to
125	  * byte 7
126	  */
127	 {F_RDLCK, 0, 3L, 5L, IGNORED},
128	 /* Parent_d skipped */
129	 {SKIP},},
130
131	/* #4 Parent_a making a write lock on entire file */
132	{{F_WRLCK, 0, 0L, 0L, IGNORED},
133	 /* Parent_b skipped */
134	 {SKIP},
135	 /* Child_a read lock on byte 2 to byte 4 */
136	 {F_RDLCK, 0, 2L, 3L, WILLBLOCK},
137	 /* Child_b read lock on byte 6 to byte 8 */
138	 {F_RDLCK, 0, 6L, 3L, NOBLOCK},
139	 /*
140	  * Parent_c read lock on byte 5 to
141	  * byte 9
142	  */
143	 {F_RDLCK, 0, 5L, 5L, IGNORED},
144	 /* Parent_d skipped */
145	 {SKIP},},
146
147	/* #5 Parent_a making a write lock on entire file */
148	{{F_WRLCK, 0, 0L, 0L, IGNORED},
149	 /* Parent_b skipped */
150	 {SKIP},
151	 /* Child_a read lock on byte 3 to byte 7 */
152	 {F_RDLCK, 0, 3L, 5L, NOBLOCK},
153	 /* Child_b read lock on byte 5 to byte 10 */
154	 {F_RDLCK, 0, 5L, 6L, WILLBLOCK},
155	 /*
156	  * Parent_c read lock on byte 2 to
157	  * byte 8
158	  */
159	 {F_RDLCK, 0, 2L, 7L, IGNORED},
160	 /* Parent_d skipped */
161	 {SKIP},},
162
163	/* #6 Parent_a making a write lock on entire file */
164	{{F_WRLCK, 0, 0L, 0L, IGNORED},
165	 /* Parent_b skipped */
166	 {SKIP},
167	 /* Child_a read lock on byte 2 to byte 4 */
168	 {F_RDLCK, 0, 2L, 3L, WILLBLOCK},
169	 /* Child_b write lock on byte 6 to byte 8 */
170	 {F_RDLCK, 0, 6L, 3L, NOBLOCK},
171	 /* Parent_c no lock on byte 3 to 9 */
172	 {F_UNLCK, 0, 3L, 7L, IGNORED},
173	 /* Parent_d skipped */
174	 {SKIP},},
175
176	/* #7 Parent_a making a write lock on entire file */
177	{{F_WRLCK, 0, 0L, 0L, IGNORED},
178	 /* Parent_b read lock on byte 3 to byte 7 */
179	 {F_RDLCK, 0, 3L, 5L, IGNORED},
180	 /* Child_a read lock on byte 2 to byte 4 */
181	 {F_RDLCK, 0, 2L, 3L, NOBLOCK},
182	 /* Child_b read lock on byte 6 to byte 8 */
183	 {F_RDLCK, 0, 6L, 3L, NOBLOCK},
184	 /*
185	  * Parent_c read lock on byte 1 to
186	  * byte 9
187	  */
188	 {F_RDLCK, 0, 1L, 9L, IGNORED},
189	 /* Parent_d skipped */
190	 {SKIP},},
191
192	/* #8 Parent_a making a write lock on byte 2 to byte 4 */
193	{{F_WRLCK, 0, 2L, 3L, IGNORED},
194	 /* Parent_b write lock on byte 6 to byte 8 */
195	 {F_WRLCK, 0, 6L, 3L, IGNORED},
196	 /* Child_a read lock on byte 3 to byte 7 */
197	 {F_RDLCK, 0, 3L, 5L, NOBLOCK},
198	 /* Child_b skipped */
199	 {SKIP},
200	 /*
201	  * Parent_c read lock on byte 1 to
202	  * byte 5
203	  */
204	 {F_RDLCK, 0, 1L, 5L, IGNORED},
205	 /*
206	  * Parent_d read lock on
207	  * byte 5 to byte 9
208	  */
209	 {F_RDLCK, 0, 5L, 5L,
210	  IGNORED},},
211
212	/* #9 Parent_a making a write lock on entire file */
213	{{F_WRLCK, 0, 0L, 0L, IGNORED},
214	 /* Parent_b read lock on byte 3 to byte 7 */
215	 {F_RDLCK, 0, 3L, 5L, IGNORED},
216	 /* Child_a read lock on byte 2 to byte 4 */
217	 {F_RDLCK, 0, 2L, 3L, NOBLOCK},
218	 /* Child_b read lock on byte 6 to byte 8 */
219	 {F_RDLCK, 0, 6L, 3L, NOBLOCK},
220	 /*
221	  * Parent_c read lock on byte 1 to
222	  * byte 3
223	  */
224	 {F_RDLCK, 0, 1L, 3L, IGNORED},
225	 /*
226	  * Parent_d read lock on
227	  * byte 7 to byte 9
228	  */
229	 {F_RDLCK, 0, 7L, 3L,
230	  IGNORED},},
231
232	/* #10 Parent_a making a write lock on entire file */
233	{{F_WRLCK, 0, 0L, 0L, IGNORED},
234	 /* Parent_b skipped */
235	 {SKIP},
236	 /* Child_a read lock on byte 2 to byte 4 */
237	 {F_RDLCK, 0, 2L, 3L, NOBLOCK},
238	 /* Child_b read lock on byte 6 to byte 8 */
239	 {F_RDLCK, 0, 6L, 3L, NOBLOCK},
240	 /*
241	  * Parent_c read lock on byte 1 to
242	  * byte 7
243	  */
244	 {F_RDLCK, 0, 1L, 7L, IGNORED},
245	 /*
246	  * Parent_d read lock on
247	  * byte 3 to byte 9
248	  */
249	 {F_RDLCK, 0, 3L, 7L,
250	  IGNORED},},
251
252	/* #11 Parent_a making a write lock on entire file */
253	{{F_WRLCK, 0, 0L, 0L, IGNORED},
254	 /* Parent_b skipped */
255	 {SKIP},
256	 /* Child_a read lock on byte 3 to byte 7 */
257	 {F_RDLCK, 0, 3L, 5L, NOBLOCK},
258	 /* Child_b read lock on byte 3 to byte 7 */
259	 {F_RDLCK, 0, 3L, 5L, NOBLOCK},
260	 /*
261	  * Parent_c read lock on byte 3 to
262	  * byte 7
263	  */
264	 {F_RDLCK, 0, 3L, 5L, IGNORED},
265	 /* Parent_d skipped */
266	 {SKIP},},
267};
268
269static testcase *thiscase;
270static lock *thislock;
271static int parent;
272static int child_flag1 = 0;
273static int child_flag2 = 0;
274static int parent_flag = 0;
275static int alarm_flag = 0;
276static int child_pid[2], flag[2];
277static int fd;
278static int test;
279static char tmpname[40];
280
281#define	FILEDATA	"tenbytes!"
282
283extern void catch_usr1();	/* signal catching subroutine */
284extern void catch_usr2();	/* signal catching subroutine */
285extern void catch_int();	/* signal catching subroutine */
286extern void catch_alarm();	/* signal catching subroutine */
287
288char *TCID = "fcntl16";
289int TST_TOTAL = 1;
290
291#ifdef UCLINUX
292static char *argv0;
293#endif
294
295/*
296 * cleanup - performs all the ONE TIME cleanup for this test at completion or
297 *	premature exit
298 */
299void cleanup(void)
300{
301	TEST_CLEANUP;
302
303	tst_rmdir();
304
305}
306
307void dochild(int kid)
308{
309	/* child process */
310	struct sigaction sact;
311	sact.sa_flags = 0;
312	sact.sa_handler = catch_int;
313	(void)sigaction(SIGUSR1, &sact, NULL);
314
315	/* Lock should succeed after blocking and parent releases lock */
316	if (kid) {
317		if ((kill(parent, SIGUSR2)) < 0) {
318			tst_resm(TFAIL, "Attempt to send signal to parent "
319				 "failed");
320			tst_resm(TFAIL, "Test case %d, child %d, errno = %d",
321				 test + 1, kid, errno);
322			exit(1);
323		}
324	} else {
325		if ((kill(parent, SIGUSR1)) < 0) {
326			tst_resm(TFAIL, "Attempt to send signal to parent "
327				 "failed");
328			tst_resm(TFAIL, "Test case %d, child %d, errno = %d",
329				 test + 1, kid, errno);
330			exit(1);
331		}
332	}
333
334	if ((fcntl(fd, F_SETLKW, thislock)) < 0) {
335		if (errno == EINTR && parent_flag) {
336			/*
337			 * signal received is waiting for lock to clear,
338			 * this is expected if flag = WILLBLOCK
339			 */
340			exit(1);
341		} else {
342			tst_resm(TFAIL, "Attempt to set child BLOCKING lock "
343				 "failed");
344			tst_resm(TFAIL, "Test case %d, errno = %d", test + 1,
345				 errno);
346			exit(2);
347		}
348	}
349	exit(0);
350}				/* end of child process */
351
352#ifdef UCLINUX
353static int kid_uc;
354
355void dochild_uc()
356{
357	dochild(kid_uc);
358}
359#endif
360
361void catch_alarm()
362{
363	alarm_flag = 1;
364}
365
366void catch_usr1()
367{				/* invoked on catching SIGUSR1 */
368	/*
369	 * Set flag to let parent know that child #1 is ready to have the
370	 * lock removed
371	 */
372	child_flag1 = 1;
373}
374
375void catch_usr2()
376{				/* invoked on catching SIGUSR2 */
377	/*
378	 * Set flag to let parent know that child #2 is ready to have the
379	 * lock removed
380	 */
381	child_flag2 = 1;
382}
383
384void catch_int()
385{				/* invoked on child catching SIGUSR1 */
386	/*
387	 * Set flag to interrupt fcntl call in child and force a controlled
388	 * exit
389	 */
390	parent_flag = 1;
391}
392
393void child_sig(int sig, int nkids)
394{
395	int i;
396
397	for (i = 0; i < nkids; i++) {
398		if (kill(child_pid[i], 0) == 0) {
399			if ((kill(child_pid[i], sig)) < 0) {
400				tst_resm(TFAIL, "Attempt to signal child %d, "
401					 "failed", i + 1);
402			}
403		}
404	}
405}
406
407/*
408 * setup - performs all ONE TIME steup for this test
409 */
410void setup(void)
411{
412	struct sigaction sact;
413
414	tst_sig(FORK, DEF_HANDLER, cleanup);
415
416	umask(0);
417
418	/* Pause if option was specified */
419	TEST_PAUSE;
420
421	parent = getpid();
422
423	tst_tmpdir();
424
425	/* set up temp filename */
426	sprintf(tmpname, "fcntl4.%d", parent);
427
428	/*
429	 * Set up signal handling functions
430	 */
431	memset(&sact, 0, sizeof(sact));
432	sact.sa_handler = catch_usr1;
433	sigemptyset(&sact.sa_mask);
434	sigaddset(&sact.sa_mask, SIGUSR1);
435	sigaction(SIGUSR1, &sact, NULL);
436
437	memset(&sact, 0, sizeof(sact));
438	sact.sa_handler = catch_usr2;
439	sigemptyset(&sact.sa_mask);
440	sigaddset(&sact.sa_mask, SIGUSR2);
441	sigaction(SIGUSR2, &sact, NULL);
442
443	memset(&sact, 0, sizeof(sact));
444	sact.sa_handler = catch_alarm;
445	sigemptyset(&sact.sa_mask);
446	sigaddset(&sact.sa_mask, SIGALRM);
447	sigaction(SIGALRM, &sact, NULL);
448}
449
450int run_test(int file_flag, int file_mode, int start, int end)
451{
452	int child_count;
453	int child;
454	int nexited;
455	int status, expect_stat;
456	int i, fail = 0;
457
458	/* loop through all test cases */
459	for (test = start; test < end; test++) {
460		/* open a temp file to lock */
461		fd = open(tmpname, file_flag, file_mode);
462		if (fd < 0) {
463			tst_brkm(TBROK, cleanup, "open failed");
464		 }
465
466		/* write some dummy data to the file */
467		(void)write(fd, FILEDATA, 10);
468
469		/* Initialize first parent lock structure */
470		thiscase = &testcases[test];
471		thislock = &thiscase->parent_a;
472
473		/* set the initial parent lock on the file */
474		if ((fcntl(fd, F_SETLK, thislock)) < 0) {
475			tst_resm(TFAIL, "First parent lock failed");
476			tst_resm(TFAIL, "Test case %d, errno = %d", test + 1,
477				 errno);
478			unlink(tmpname);
479			return 1;
480		}
481
482		/* Initialize second parent lock structure */
483		thislock = &thiscase->parent_b;
484
485		if ((thislock->type) != IGNORED) {	/*SKIPVAL */
486			/* set the second parent lock */
487			if ((fcntl(fd, F_SETLK, thislock)) < 0) {
488				tst_resm(TFAIL, "Second parent lock failed");
489				tst_resm(TFAIL, "Test case %d, errno = %d",
490					 test + 1, errno);
491				unlink(tmpname);
492				return 1;
493			}
494		}
495
496		/* Initialize first child lock structure */
497		thislock = &thiscase->child_a;
498
499		/* Initialize child counter and flags */
500		alarm_flag = parent_flag = 0;
501		child_flag1 = child_flag2 = 0;
502		child_count = 0;
503
504		/* spawn child processes */
505		for (i = 0; i < 2; i++) {
506			if (thislock->type != IGNORED) {
507				if ((child = FORK_OR_VFORK()) == 0) {
508#ifdef UCLINUX
509					if (self_exec(argv0, "ddddd", i, parent,
510						      test, thislock, fd) < 0) {
511						perror("self_exec failed");
512						return 1;
513					}
514#else
515					dochild(i);
516#endif
517				}
518				if (child < 0) {
519					perror("Fork failed");
520					return 1;
521				}
522				child_count++;
523				child_pid[i] = child;
524				flag[i] = thislock->flag;
525			}
526			/* Initialize second child lock structure */
527			thislock = &thiscase->child_b;
528		}
529		/* parent process */
530
531		/*
532		 * Wait for children to signal they are ready. Set a timeout
533		 * just in case they don't signal at all.
534		 */
535		alarm(TIME_OUT);
536
537		while (!alarm_flag
538		       && (child_flag1 + child_flag2 != child_count)) {
539			pause();
540		}
541
542		/*
543		 * Turn off alarm and unmask signals
544		 */
545		alarm((unsigned)0);
546
547		if (child_flag1 + child_flag2 != child_count) {
548			tst_resm(TFAIL, "Test case %d: kids didn't signal",
549				 test + 1);
550			fail = 1;
551		}
552		child_flag1 = child_flag2 = alarm_flag = 0;
553
554		thislock = &thiscase->parent_c;
555
556		/* set the third parent lock on the file */
557		if ((fcntl(fd, F_SETLK, thislock)) < 0) {
558			tst_resm(TFAIL, "Third parent lock failed");
559			tst_resm(TFAIL, "Test case %d, errno = %d",
560				 test + 1, errno);
561			unlink(tmpname);
562			return 1;
563		}
564
565		/* Initialize fourth parent lock structure */
566		thislock = &thiscase->parent_d;
567
568		if ((thislock->type) != IGNORED) {	/*SKIPVAL */
569			/* set the fourth parent lock */
570			if ((fcntl(fd, F_SETLK, thislock)) < 0) {
571				tst_resm(TINFO, "Fourth parent lock failed");
572				tst_resm(TINFO, "Test case %d, errno = %d",
573					 test + 1, errno);
574				unlink(tmpname);
575				return 1;
576			}
577		}
578
579		/*
580		 * Wait for children to exit, or for timeout to occur.
581		 * Timeouts are expected for testcases where kids are
582		 * 'WILLBLOCK', In that case, send kids a wakeup interrupt
583		 * and wait again for them. If a second timeout occurs, then
584		 * something is wrong.
585		 */
586		alarm_flag = nexited = 0;
587		while (nexited < child_count) {
588			alarm(TIME_OUT);
589			child = wait(&status);
590			alarm(0);
591
592			if (child == -1) {
593				if (errno != EINTR || alarm_flag != 1) {
594					/*
595					 * Some error other than a timeout,
596					 * or else this is the second
597					 * timeout. Both cases are errors.
598					 */
599					break;
600				}
601
602				/*
603				 * Expected timeout case. Signal kids then
604				 * go back and wait again
605				 */
606				child_sig(SIGUSR1, child_count);
607				continue;
608			}
609
610			for (i = 0; i < child_count; i++)
611				if (child == child_pid[i])
612					break;
613			if (i == child_count) {
614				/*
615				 * Ignore unexpected kid, it could be a
616				 * leftover from a previous iteration that
617				 * timed out.
618				 */
619				continue;
620			}
621
622			/* Found the right kid, check his status */
623			nexited++;
624
625			expect_stat = (flag[i] == NOBLOCK) ? 0 : 1;
626
627			if (!WIFEXITED(status)
628			    || WEXITSTATUS(status) != expect_stat) {
629				/* got unexpected exit status from kid */
630				tst_resm(TFAIL, "Test case %d: child %d %s "
631					 "or got bad status (x%x)", test + 1,
632					 i, (flag[i] == NOBLOCK) ?
633					 "BLOCKED unexpectedly" :
634					 "failed to BLOCK", status);
635				fail = 1;
636			}
637		}
638
639		if (nexited != child_count) {
640			tst_resm(TFAIL, "Test case %d, caught %d expected %d "
641				 "children", test + 1, nexited, child_count);
642			child_sig(SIGKILL, nexited);
643			fail = 1;
644		}
645		close(fd);
646	}
647	unlink(tmpname);
648	if (fail) {
649		return 1;
650	} else {
651		return 0;
652	}
653	return 0;
654}
655
656int main(int ac, char **av)
657{
658
659	int lc;			/* loop counter */
660	char *msg;		/* message returned from parse_opts */
661
662	/* parse standard options */
663	if ((msg = parse_opts(ac, av, NULL, NULL)) != NULL) {
664		tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg);
665	}
666#ifdef UCLINUX
667	maybe_run_child(dochild_uc, "ddddd", &kid_uc, &parent, &test,
668			&thislock, &fd);
669	argv0 = av[0];
670#endif
671
672	setup();		/* global setup */
673
674	for (lc = 0; TEST_LOOPING(lc); lc++) {
675		/* reset Tst_count in case we are looping */
676		Tst_count = 0;
677
678/* //block1: */
679		/*
680		 * Check file locks on an ordinary file without
681		 * mandatory locking
682		 */
683		tst_resm(TINFO, "Entering block 1");
684		if (run_test(O_CREAT | O_RDWR | O_TRUNC, 0777, 0, 11)) {
685			tst_resm(TINFO, "Test case 1: without mandatory "
686				 "locking FAILED");
687		} else {
688			tst_resm(TINFO, "Test case 1: without manadatory "
689				 "locking PASSED");
690		}
691		tst_resm(TINFO, "Exiting block 1");
692
693/* //block2: */
694		/*
695		 * Check the file locks on a file with mandatory record
696		 * locking
697		 */
698		tst_resm(TINFO, "Entering block 2");
699		if (run_test(O_CREAT | O_RDWR | O_TRUNC, S_ISGID |
700			     S_IRUSR | S_IWUSR, 0, 11)) {
701			tst_resm(TINFO, "Test case 2: with mandatory record "
702				 "locking FAILED");
703		} else {
704			tst_resm(TINFO, "Test case 2: with mandatory record "
705				 "locking PASSED");
706		}
707		tst_resm(TINFO, "Exiting block 2");
708
709/* //block3: */
710		/*
711		 * Check file locks on a file with mandatory record locking
712		 * and no delay
713		 */
714		tst_resm(TINFO, "Entering block 3");
715		if (run_test(O_CREAT | O_RDWR | O_TRUNC | O_NDELAY,
716			     S_ISGID | S_IRUSR | S_IWUSR, 0, 11)) {
717			tst_resm(TINFO, "Test case 3: mandatory locking with "
718				 "NODELAY FAILED");
719		} else {
720			tst_resm(TINFO, "Test case 3: mandatory locking with "
721				 "NODELAY PASSED");
722		}
723		tst_resm(TINFO, "Exiting block 3");
724	}
725	cleanup();
726	tst_exit();
727}
728