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