• 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  *	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 
68 char *TCID = "fcntl15";
69 int TST_TOTAL = 1;
70 
71 static int parent, child1, child2, status;
72 static volatile sig_atomic_t parent_flag, child_flag, alarm_flag;
73 static char tmpname[40];
74 struct flock flock;
75 
76 #ifdef UCLINUX
77 static char *argv0;		/* set by main, passed to self_exec */
78 #endif
79 
80 
alarm_sig(int sig)81 void 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 
child_sig(int sig)92 void child_sig(int sig)
93 {
94 	signal(SIGUSR1, child_sig);
95 	child_flag++;
96 }
97 
parent_sig(int sig)98 void parent_sig(int sig)
99 {
100 	signal(SIGUSR2, parent_sig);
101 	parent_flag++;
102 }
103 
dochild1(int file_flag,int file_mode)104 int 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
171 int uc_file_flag, uc_file_mode, uc_dup_flag;
172 
dochild1_uc(void)173 void dochild1_uc(void)
174 {
175 	dochild1(uc_file_flag, uc_file_mode);
176 }
177 
dochild2_uc(void)178 void dochild2_uc(void)
179 {
180 	dochild2(uc_file_flag, uc_dup_flag);
181 }
182 #endif
183 
dofork(int file_flag,int file_mode)184 int 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 
dochild2(int file_flag,int file_mode,int dup_flag)241 int 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 
setup(void)352 void setup(void)
353 {
354 	tst_sig(FORK, DEF_HANDLER, NULL);
355 
356 	TEST_PAUSE;
357 }
358 
run_test(int file_flag,int file_mode,int dup_flag)359 int 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 
main(int ac,char ** av)525 int 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