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