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