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