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 *	fcntl15.c
23 *
24 * DESCRIPTION
25 *	Check that file locks are removed when file closed
26 *
27 * ALGORITHM
28 *	Use three testcases to check removal of locks when a file is closed.
29 *
30 *	Case 1: Parent opens a file and duplicates it, places locks using
31 *	both file descriptors then closes one descriptor, all locks should
32 *	be removed.
33 *
34 *	Case 2: Open same file twice using(open), place locks using both
35 *	descriptors then close on descriptor, locks on the file should be
36 *	lost
37 *
38 *	Case 3: Open file twice, one by each process, set the locks and have
39 *	a child check the locks. Remove the first file and have the child
40 *	check the locks. Remove the first file and have child check locks
41 *	again. Only locks set on first file should have been removed
42 *
43 * USAGE
44 *	fcntl15
45 *
46 * HISTORY
47 *	07/2001 Ported by Wayne Boyer
48 * MODIFIED: - mridge@us.ibm.com -- changed getpid to syscall(get thread ID) for unique ID on NPTL threading
49 *
50 * RESTRICTIONS
51 *	None
52 */
53
54#include <signal.h>
55#include <fcntl.h>
56#include "test.h"
57#include <sys/types.h>
58#include <sys/wait.h>
59#include <sys/types.h>
60#include <sys/syscall.h>
61#include <linux/unistd.h>
62
63#define	DATA	"ABCDEFGHIJ"
64#define	DUP	0
65#define	OPEN	1
66#define	FORK_	2
67
68char *TCID = "fcntl15";
69int TST_TOTAL = 1;
70
71static int parent, child1, child2, status;
72static volatile sig_atomic_t parent_flag, child_flag, alarm_flag;
73static char tmpname[40];
74struct flock flock;
75
76#ifdef UCLINUX
77static char *argv0;		/* set by main, passed to self_exec */
78#endif
79
80
81void alarm_sig(int sig)
82{
83	signal(SIGALRM, alarm_sig);
84	alarm_flag = 1;
85	if ((syscall(__NR_gettid)) == parent) {
86		tst_resm(TINFO, "Alarm caught by parent");
87	} else {
88		tst_resm(TINFO, "Alarm caught by child");
89	}
90}
91
92void child_sig(int sig)
93{
94	signal(SIGUSR1, child_sig);
95	child_flag++;
96}
97
98void parent_sig(int sig)
99{
100	signal(SIGUSR2, parent_sig);
101	parent_flag++;
102}
103
104int dochild1(int file_flag, int file_mode)
105{
106	int fd_B;
107	sigset_t newmask, zeromask, oldmask;
108
109	if ((fd_B = open(tmpname, file_flag, file_mode)) < 0) {
110		perror("open on child1 file failed");
111		exit(1);
112	}
113
114	/* initialize lock structure for second 5 bytes of file */
115	flock.l_type = F_WRLCK;
116	flock.l_whence = 0;
117	flock.l_start = 5L;
118	flock.l_len = 5L;
119
120	/* set lock on child file descriptor */
121	if ((fcntl(fd_B, F_SETLK, &flock)) < 0) {
122		perror("child lock failed should have succeeded");
123		exit(1);
124	}
125
126	sigemptyset(&zeromask);
127	sigemptyset(&newmask);
128	sigaddset(&newmask, SIGUSR1);
129	sigaddset(&newmask, SIGUSR2);
130	sigaddset(&newmask, SIGALRM);
131	if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) {
132		perror("child1 sigprocmask SIG_BLOCK fail");
133		exit(1);
134	}
135	/*
136	 * send signal to parent here to tell parent we have locked the
137	 * file, thus allowing parent to proceed
138	 */
139	if ((kill(parent, SIGUSR1)) < 0) {
140		perror("child1 signal to parent failed");
141		exit(1);
142	}
143
144	/*
145	 * set alarm to break pause if parent fails to signal then spin till
146	 * parent ready
147	 */
148	alarm(60);
149	while (parent_flag == 0 && alarm_flag == 0)
150		sigsuspend(&zeromask);
151	alarm((unsigned)0);
152	if (parent_flag != 1) {
153		perror("pause in child1 terminated without "
154		       "SIGUSR2 signal from parent");
155		exit(1);
156	}
157	parent_flag = 0;
158	alarm_flag = 0;
159	if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) {
160		perror("child1 sigprocmask SIG_SETMASK fail");
161		exit(1);
162	}
163
164	/* wait for child2 to complete then cleanup */
165	sleep(10);
166	close(fd_B);
167	exit(0);
168}
169
170#ifdef UCLINUX
171int uc_file_flag, uc_file_mode, uc_dup_flag;
172
173void dochild1_uc(void)
174{
175	dochild1(uc_file_flag, uc_file_mode);
176}
177
178void dochild2_uc(void)
179{
180	dochild2(uc_file_flag, uc_dup_flag);
181}
182#endif
183
184int dofork(int file_flag, int file_mode)
185{
186	/* create child process */
187	if ((child1 = FORK_OR_VFORK()) < 0) {
188		perror("Fork failure");
189		return 1;
190	}
191
192	/* child1 */
193	if (child1 == 0) {
194#ifdef UCLINUX
195		if (self_exec(argv0, "nddds", 1, file_flag, file_mode,
196			      parent, tmpname) < 0) {
197			perror("self_exec failure");
198			return 1;
199		}
200#else
201		dochild1(file_flag, file_mode);
202#endif
203	} else {
204		/*
205		 * need to wait for child1 to open, and lock the area of the
206		 * file prior to continuing on from here
207		 */
208		sigset_t newmask, zeromask, oldmask;
209		sigemptyset(&zeromask);
210		sigemptyset(&newmask);
211		sigaddset(&newmask, SIGUSR1);
212		sigaddset(&newmask, SIGUSR2);
213		sigaddset(&newmask, SIGALRM);
214		if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) {
215			perror("parent sigprocmask SIG_BLOCK fail");
216			exit(1);
217		}
218
219		/*
220		 * set alarm to break pause if parent fails to signal then spin till
221		 * parent ready
222		 */
223		alarm(60);
224		while (child_flag == 0 && alarm_flag == 0)
225			sigsuspend(&zeromask);
226		alarm((unsigned)0);
227		if (child_flag != 1) {
228			perror("parent paused without SIGUSR1 " "from child");
229			exit(1);
230		}
231		child_flag = 0;
232		alarm_flag = 0;
233		if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) {
234			perror("parent sigprocmask SIG_SETMASK fail");
235			exit(1);
236		}
237	}
238	return 0;
239}
240
241int dochild2(int file_flag, int file_mode, int dup_flag)
242{
243	int fd_C;
244	sigset_t newmask, zeromask, oldmask;
245
246	if ((fd_C = open(tmpname, file_flag, file_mode)) < 0) {
247		perror("open on child2 file failed");
248		exit(1);
249	}
250
251	/* initialize lock structure for first 5 bytes of file */
252	flock.l_type = F_WRLCK;
253	flock.l_whence = 0;
254	flock.l_start = 0L;
255	flock.l_len = 5L;
256
257	/* Set lock on child file descriptor */
258	if ((fcntl(fd_C, F_SETLK, &flock)) >= 0) {
259		tst_resm(TFAIL, "First child2 lock succeeded should "
260			 "have failed");
261		exit(1);
262	}
263
264	/* initialize lock structure for second 5 bytes of file */
265	flock.l_type = F_WRLCK;
266	flock.l_whence = 0;
267	flock.l_start = 5L;
268	flock.l_len = 5L;
269
270	/* set lock on child file descriptor */
271	if ((fcntl(fd_C, F_SETLK, &flock)) >= 0) {
272		tst_resm(TFAIL, "second child2 lock succeeded should have "
273			 "failed");
274		exit(1);
275	}
276
277	sigemptyset(&zeromask);
278	sigemptyset(&newmask);
279	sigaddset(&newmask, SIGUSR1);
280	sigaddset(&newmask, SIGUSR2);
281	sigaddset(&newmask, SIGALRM);
282	if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) {
283		perror("child2 sigprocmask SIG_BLOCK fail");
284		exit(1);
285	}
286	/*
287	 * send signal to parent here to tell parent we have locked the
288	 * file, thus allowing parent to proceed
289	 */
290	if ((kill(parent, SIGUSR1)) < 0) {
291		perror("child2 signal to parent failed");
292		exit(1);
293	}
294
295	/*
296	 * set alarm to break pause if parent fails to signal then spin till
297	 * parent ready
298	 */
299	alarm(60);
300	while (parent_flag == 0 && alarm_flag == 0)
301		sigsuspend(&zeromask);
302	alarm((unsigned)0);
303	if (parent_flag != 1) {
304		perror("pause in child2 terminated without "
305		       "SIGUSR2 signal from parent");
306		exit(1);
307	}
308	parent_flag = 0;
309	alarm_flag = 0;
310	if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) {
311		perror("child2 sigprocmask SIG_SETMASK fail");
312		exit(1);
313	}
314
315	/* initialize lock structure for first 5 bytes of file */
316	flock.l_type = F_WRLCK;
317	flock.l_whence = 0;
318	flock.l_start = 0L;
319	flock.l_len = 5L;
320
321	/* set lock on child file descriptor */
322	if ((fcntl(fd_C, F_SETLK, &flock)) < 0) {
323		tst_resm(TFAIL, "third child2 lock failed should have "
324			 "succeeded");
325		exit(1);
326	}
327
328	/* Initialize lock structure for second 5 bytes of file */
329	flock.l_type = F_WRLCK;
330	flock.l_whence = 0;
331	flock.l_start = 5L;
332	flock.l_len = 5L;
333
334	/* set lock on child file descriptor */
335	if (dup_flag == FORK_) {
336		if ((fcntl(fd_C, F_SETLK, &flock)) >= 0) {
337			tst_resm(TFAIL, "fourth child2 lock succeeded "
338				 "should have failed");
339			exit(1);
340		}
341	} else {
342		if ((fcntl(fd_C, F_SETLK, &flock)) < 0) {
343			tst_resm(TFAIL, "fourth child2 lock failed "
344				 "should have succeeded");
345			exit(1);
346		}
347	}
348	close(fd_C);
349	exit(0);
350}
351
352void setup(void)
353{
354	tst_sig(FORK, DEF_HANDLER, NULL);
355
356	TEST_PAUSE;
357}
358
359int run_test(int file_flag, int file_mode, int dup_flag)
360{
361	int fd_A, fd_B;
362	fd_B = -1;
363	sigset_t newmask, zeromask, oldmask;
364
365	/* setup to catch SIGUSR1 signal from child process */
366	if ((signal(SIGUSR1, child_sig)) == SIG_ERR) {
367		perror("Signal setup for SIGUSR1 failed");
368	}
369
370	/* setup to catch SIGUSR2 signal from parent */
371	if ((signal(SIGUSR2, parent_sig)) == SIG_ERR) {
372		perror("Signal setup for SIGUSR1 failed");
373	}
374
375	parent = syscall(__NR_gettid);
376
377	tst_tmpdir();
378	/* setup temporary file name */
379	sprintf(tmpname, "fcntl15.%d", parent);
380
381	/* initialize signal flags */
382	child_flag = parent_flag = alarm_flag = 0;
383
384	if ((fd_A = open(tmpname, file_flag, file_mode)) < 0) {
385		perror("open first parent file failed");
386		tst_rmdir();
387		return 1;
388	}
389
390	/* write some data to the file */
391	(void)write(fd_A, DATA, 10);
392
393	if (dup_flag) {
394		if (dup_flag == FORK_) {
395			dofork(file_flag, file_mode);
396		} else {
397			if ((fd_B = open(tmpname, file_flag, file_mode)) < 0) {
398				perror("open second parent file failed");
399				tst_rmdir();
400				return 1;
401			}
402		}
403	} else {
404		/* create a second file descriptor from first file */
405		if ((fd_B = fcntl(fd_A, F_DUPFD, 0)) < 0) {
406			perror("dup of second parent file failed");
407			tst_rmdir();
408			return 1;
409		}
410	}
411
412	/*
413	 * initialize lock structure for first lock on first
414	 * 5 bytes of file
415	 */
416	flock.l_type = F_WRLCK;
417	flock.l_whence = 0;
418	flock.l_start = 0L;
419	flock.l_len = 5L;
420
421	/* set lock on first file descriptor */
422	if ((fcntl(fd_A, F_SETLK, &flock)) < 0) {
423		perror("Attempt to set first parent lock failed");
424		tst_rmdir();
425		return 1;
426	}
427
428	if (dup_flag != FORK_) {
429		/* initialize lock structure for last 5 bytes of file */
430		flock.l_type = F_WRLCK;
431		flock.l_whence = 0;
432		flock.l_start = 5L;
433		flock.l_len = 5L;
434
435		/* set lock on second file descriptor */
436		if ((fcntl(fd_B, F_SETLK, &flock)) < 0) {
437			perror("Attempt to set second parent lock failed");
438			tst_rmdir();
439			return 1;
440		}
441	}
442
443	/* create child process */
444	if ((child2 = FORK_OR_VFORK()) < 0) {
445		perror("Fork failure");
446		tst_rmdir();
447		return 1;
448	} else if (child2 == 0) {	/* child */
449#ifdef UCLINUX
450		if (self_exec(argv0, "ndddds", 2, file_flag, file_mode,
451			      dup_flag, parent, tmpname) < 0)
452			tst_brkm(TBROK | TERRNO, NULL, "self_exec failed");
453#else
454		dochild2(file_flag, file_mode, dup_flag);
455#endif
456	}
457
458	/* parent */
459
460	/*
461	 * Set alarm to break pause if child fails to signal then spin till
462	 * child is ready
463	 */
464
465	sigemptyset(&zeromask);
466	sigemptyset(&newmask);
467	sigaddset(&newmask, SIGUSR1);
468	sigaddset(&newmask, SIGUSR2);
469	sigaddset(&newmask, SIGALRM);
470	if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) {
471		perror("parent sigprocmask SIG_BLOCK fail");
472		exit(1);
473	}
474
475	/*
476	 * set alarm to break pause if parent fails to signal then spin till
477	 * parent ready
478	 */
479	alarm(60);
480	while (child_flag == 0 && alarm_flag == 0)
481		sigsuspend(&zeromask);
482	alarm((unsigned)0);
483	if (child_flag != 1) {
484		perror("parent paused without SIGUSR1 " "from child");
485		exit(1);
486	}
487	child_flag = 0;
488	alarm_flag = 0;
489	if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) {
490		perror("parent sigprocmask SIG_SETMASK fail");
491		exit(1);
492	}
493
494	/* close the first file then signal child to test locks */
495	close(fd_A);
496	if ((kill(child2, SIGUSR2)) < 0) {
497		perror("Signal to child2 failed");
498		tst_rmdir();
499		return 1;
500	}
501
502	if (dup_flag == FORK_) {
503		if ((kill(child1, SIGUSR2)) < 0) {
504			perror("Signal to child1 failed");
505			tst_rmdir();
506			return 1;
507		}
508	}
509	/* wait for child to complete then cleanup */
510	while ((wait(&status)) > 0) {
511		if (status >> 8 != 0) {
512			tst_resm(TFAIL, "Expected 0 got %d", status >> 8);
513			tst_rmdir();
514			return 1;
515		}
516	}
517	if (dup_flag != FORK_) {
518		close(fd_B);
519	}
520	unlink(tmpname);
521	tst_rmdir();
522	return 0;
523}
524
525int main(int ac, char **av)
526{
527	int lc;
528
529	tst_parse_opts(ac, av, NULL, NULL);
530#ifdef UCLINUX
531	maybe_run_child(&dochild1_uc, "nddds", 1, &uc_file_flag,
532			&uc_file_mode, &parent, tmpname);
533	maybe_run_child(&dochild2_uc, "nddds", 1, &uc_file_flag,
534			&uc_file_mode, &uc_dup_flag, &parent, tmpname);
535	argv0 = av[0];
536#endif
537
538	setup();
539
540	for (lc = 0; TEST_LOOPING(lc); lc++) {
541		tst_count = 0;
542
543		if ((signal(SIGALRM, alarm_sig)) == SIG_ERR) {
544			perror("SIGALRM signal set up failed");
545			exit(1);
546		}
547
548		if (run_test(O_CREAT | O_RDWR | O_TRUNC, 0777, DUP))
549			tst_resm(TFAIL, "Test 1: test with \"dup\" FAILED");
550		else
551			tst_resm(TPASS, "Test 1: test with \"dup\" PASSED");
552
553		if (run_test(O_CREAT | O_RDWR | O_TRUNC, 0777, OPEN))
554			tst_resm(TFAIL, "Test 2: test with \"open\" FAILED");
555		else
556			tst_resm(TPASS, "Test 2: test with \"open\" PASSED");
557
558		if (run_test(O_CREAT | O_RDWR | O_TRUNC, 0777, FORK_))
559			tst_resm(TFAIL, "Test 3: test with \"fork\" FAILED");
560		else
561			tst_resm(TPASS, "Test 3: test with \"fork\" PASSED");
562	}
563	tst_exit();
564}
565