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