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 #ifdef UCLINUX
283 static char *argv0;
284 #endif
285
286 /*
287 * cleanup - performs all the ONE TIME cleanup for this test at completion or
288 * premature exit
289 */
cleanup(void)290 void cleanup(void)
291 {
292 tst_rmdir();
293
294 }
295
dochild(int kid)296 void dochild(int kid)
297 {
298 /* child process */
299 struct sigaction sact;
300 sact.sa_flags = 0;
301 sact.sa_handler = catch_int;
302 sigemptyset(&sact.sa_mask);
303 (void)sigaction(SIGUSR1, &sact, NULL);
304
305 /* Lock should succeed after blocking and parent releases lock */
306 if (kid) {
307 if ((kill(parent, SIGUSR2)) < 0) {
308 tst_resm(TFAIL, "Attempt to send signal to parent "
309 "failed");
310 tst_resm(TFAIL, "Test case %d, child %d, errno = %d",
311 test + 1, kid, errno);
312 exit(1);
313 }
314 } else {
315 if ((kill(parent, SIGUSR1)) < 0) {
316 tst_resm(TFAIL, "Attempt to send signal to parent "
317 "failed");
318 tst_resm(TFAIL, "Test case %d, child %d, errno = %d",
319 test + 1, kid, errno);
320 exit(1);
321 }
322 }
323
324 if ((fcntl(fd, F_SETLKW, thislock)) < 0) {
325 if (errno == EINTR && parent_flag) {
326 /*
327 * signal received is waiting for lock to clear,
328 * this is expected if flag = WILLBLOCK
329 */
330 exit(1);
331 } else {
332 tst_resm(TFAIL, "Attempt to set child BLOCKING lock "
333 "failed");
334 tst_resm(TFAIL, "Test case %d, errno = %d", test + 1,
335 errno);
336 exit(2);
337 }
338 }
339 exit(0);
340 } /* end of child process */
341
342 #ifdef UCLINUX
343 static int kid_uc;
344
dochild_uc(void)345 void dochild_uc(void)
346 {
347 dochild(kid_uc);
348 }
349 #endif
350
catch_alarm(int sig)351 void catch_alarm(int sig)
352 {
353 alarm_flag = 1;
354 }
355
catch_usr1(int sig)356 void catch_usr1(int sig)
357 { /* invoked on catching SIGUSR1 */
358 /*
359 * Set flag to let parent know that child #1 is ready to have the
360 * lock removed
361 */
362 child_flag1 = 1;
363 }
364
catch_usr2(int sig)365 void catch_usr2(int sig)
366 { /* invoked on catching SIGUSR2 */
367 /*
368 * Set flag to let parent know that child #2 is ready to have the
369 * lock removed
370 */
371 child_flag2 = 1;
372 }
373
catch_int(int sig)374 void catch_int(int sig)
375 { /* invoked on child catching SIGUSR1 */
376 /*
377 * Set flag to interrupt fcntl call in child and force a controlled
378 * exit
379 */
380 parent_flag = 1;
381 }
382
child_sig(int sig,int nkids)383 void child_sig(int sig, int nkids)
384 {
385 int i;
386
387 for (i = 0; i < nkids; i++) {
388 if (kill(child_pid[i], 0) == 0) {
389 if ((kill(child_pid[i], sig)) < 0) {
390 tst_resm(TFAIL, "Attempt to signal child %d, "
391 "failed", i + 1);
392 }
393 }
394 }
395 }
396
397 /*
398 * setup - performs all ONE TIME steup for this test
399 */
setup(void)400 void setup(void)
401 {
402 struct sigaction sact;
403
404 tst_sig(FORK, DEF_HANDLER, cleanup);
405
406 umask(0);
407
408 /* Pause if option was specified */
409 TEST_PAUSE;
410
411 parent = getpid();
412
413 tst_tmpdir();
414
415 /* On NFS or not */
416 if (tst_fs_type(cleanup, ".") == TST_NFS_MAGIC)
417 NO_NFS = 0;
418
419 /* set up temp filename */
420 sprintf(tmpname, "fcntl4.%d", parent);
421
422 /*
423 * Set up signal handling functions
424 */
425 memset(&sact, 0, sizeof(sact));
426 sact.sa_handler = catch_usr1;
427 sigemptyset(&sact.sa_mask);
428 sigaddset(&sact.sa_mask, SIGUSR1);
429 sigaction(SIGUSR1, &sact, NULL);
430
431 memset(&sact, 0, sizeof(sact));
432 sact.sa_handler = catch_usr2;
433 sigemptyset(&sact.sa_mask);
434 sigaddset(&sact.sa_mask, SIGUSR2);
435 sigaction(SIGUSR2, &sact, NULL);
436
437 memset(&sact, 0, sizeof(sact));
438 sact.sa_handler = catch_alarm;
439 sigemptyset(&sact.sa_mask);
440 sigaddset(&sact.sa_mask, SIGALRM);
441 sigaction(SIGALRM, &sact, NULL);
442 }
443
run_test(int file_flag,int file_mode,int start,int end)444 int run_test(int file_flag, int file_mode, int start, int end)
445 {
446 int child_count;
447 int child;
448 int nexited;
449 int status, expect_stat;
450 int i, fail = 0;
451
452 /* loop through all test cases */
453 for (test = start; test < end; test++) {
454 /* open a temp file to lock */
455 fd = SAFE_OPEN(cleanup, tmpname, file_flag, file_mode);
456
457 /* write some dummy data to the file */
458 (void)write(fd, FILEDATA, 10);
459
460 /* Initialize first parent lock structure */
461 thiscase = &testcases[test];
462 thislock = &thiscase->parent_a;
463
464 /* set the initial parent lock on the file */
465 if ((fcntl(fd, F_SETLK, thislock)) < 0) {
466 tst_resm(TFAIL, "First parent lock failed");
467 tst_resm(TFAIL, "Test case %d, errno = %d", test + 1,
468 errno);
469 close(fd);
470 unlink(tmpname);
471 return 1;
472 }
473
474 /* Initialize second parent lock structure */
475 thislock = &thiscase->parent_b;
476
477 if ((thislock->l_type) != IGNORED) { /*SKIPVAL */
478 /* set the second parent lock */
479 if ((fcntl(fd, F_SETLK, thislock)) < 0) {
480 tst_resm(TFAIL, "Second parent lock failed");
481 tst_resm(TFAIL, "Test case %d, errno = %d",
482 test + 1, errno);
483 close(fd);
484 unlink(tmpname);
485 return 1;
486 }
487 }
488
489 /* Initialize first child lock structure */
490 thislock = &thiscase->child_a;
491
492 /* Initialize child counter and flags */
493 alarm_flag = parent_flag = 0;
494 child_flag1 = child_flag2 = 0;
495 child_count = 0;
496
497 /* spawn child processes */
498 for (i = 0; i < 2; i++) {
499 if (thislock->l_type != IGNORED) {
500 if ((child = FORK_OR_VFORK()) == 0) {
501 #ifdef UCLINUX
502 if (self_exec(argv0, "ddddd", i, parent,
503 test, thislock, fd) < 0) {
504 perror("self_exec failed");
505 return 1;
506 }
507 #else
508 dochild(i);
509 #endif
510 }
511 if (child < 0) {
512 perror("Fork failed");
513 return 1;
514 }
515 child_count++;
516 child_pid[i] = child;
517 flag[i] = thislock->l_pid;
518 }
519 /* Initialize second child lock structure */
520 thislock = &thiscase->child_b;
521 }
522 /* parent process */
523
524 /*
525 * Wait for children to signal they are ready. Set a timeout
526 * just in case they don't signal at all.
527 */
528 alarm(TIME_OUT);
529
530 while (!alarm_flag
531 && (child_flag1 + child_flag2 != child_count)) {
532 pause();
533 }
534
535 /*
536 * Turn off alarm and unmask signals
537 */
538 alarm((unsigned)0);
539
540 if (child_flag1 + child_flag2 != child_count) {
541 tst_resm(TFAIL, "Test case %d: kids didn't signal",
542 test + 1);
543 fail = 1;
544 }
545 child_flag1 = child_flag2 = alarm_flag = 0;
546
547 thislock = &thiscase->parent_c;
548
549 /* set the third parent lock on the file */
550 if ((fcntl(fd, F_SETLK, thislock)) < 0) {
551 tst_resm(TFAIL, "Third parent lock failed");
552 tst_resm(TFAIL, "Test case %d, errno = %d",
553 test + 1, errno);
554 close(fd);
555 unlink(tmpname);
556 return 1;
557 }
558
559 /* Initialize fourth parent lock structure */
560 thislock = &thiscase->parent_d;
561
562 if ((thislock->l_type) != IGNORED) { /*SKIPVAL */
563 /* set the fourth parent lock */
564 if ((fcntl(fd, F_SETLK, thislock)) < 0) {
565 tst_resm(TINFO, "Fourth parent lock failed");
566 tst_resm(TINFO, "Test case %d, errno = %d",
567 test + 1, errno);
568 close(fd);
569 unlink(tmpname);
570 return 1;
571 }
572 }
573
574 /*
575 * Wait for children to exit, or for timeout to occur.
576 * Timeouts are expected for testcases where kids are
577 * 'WILLBLOCK', In that case, send kids a wakeup interrupt
578 * and wait again for them. If a second timeout occurs, then
579 * something is wrong.
580 */
581 alarm_flag = nexited = 0;
582 while (nexited < child_count) {
583 alarm(TIME_OUT);
584 child = wait(&status);
585 alarm(0);
586
587 if (child == -1) {
588 if (errno != EINTR || alarm_flag != 1) {
589 /*
590 * Some error other than a timeout,
591 * or else this is the second
592 * timeout. Both cases are errors.
593 */
594 break;
595 }
596
597 /*
598 * Expected timeout case. Signal kids then
599 * go back and wait again
600 */
601 child_sig(SIGUSR1, child_count);
602 continue;
603 }
604
605 for (i = 0; i < child_count; i++)
606 if (child == child_pid[i])
607 break;
608 if (i == child_count) {
609 /*
610 * Ignore unexpected kid, it could be a
611 * leftover from a previous iteration that
612 * timed out.
613 */
614 continue;
615 }
616
617 /* Found the right kid, check his status */
618 nexited++;
619
620 expect_stat = (flag[i] == NOBLOCK) ? 0 : 1;
621
622 if (!WIFEXITED(status)
623 || WEXITSTATUS(status) != expect_stat) {
624 /* got unexpected exit status from kid */
625 tst_resm(TFAIL, "Test case %d: child %d %s "
626 "or got bad status (x%x)", test + 1,
627 i, (flag[i] == NOBLOCK) ?
628 "BLOCKED unexpectedly" :
629 "failed to BLOCK", status);
630 fail = 1;
631 }
632 }
633
634 if (nexited != child_count) {
635 tst_resm(TFAIL, "Test case %d, caught %d expected %d "
636 "children", test + 1, nexited, child_count);
637 child_sig(SIGKILL, nexited);
638 fail = 1;
639 }
640 close(fd);
641 }
642 unlink(tmpname);
643 if (fail) {
644 return 1;
645 } else {
646 return 0;
647 }
648 return 0;
649 }
650
main(int ac,char ** av)651 int main(int ac, char **av)
652 {
653
654 int lc;
655
656 tst_parse_opts(ac, av, NULL, NULL);
657 #ifdef UCLINUX
658 maybe_run_child(dochild_uc, "ddddd", &kid_uc, &parent, &test,
659 &thislock, &fd);
660 argv0 = av[0];
661 #endif
662
663 setup(); /* global setup */
664
665 for (lc = 0; TEST_LOOPING(lc); lc++) {
666 /* reset tst_count in case we are looping */
667 tst_count = 0;
668
669 /* //block1: */
670 /*
671 * Check file locks on an ordinary file without
672 * mandatory locking
673 */
674 tst_resm(TINFO, "Entering block 1");
675 if (run_test(O_CREAT | O_RDWR | O_TRUNC, 0777, 0, 11)) {
676 tst_resm(TINFO, "Test case 1: without mandatory "
677 "locking FAILED");
678 } else {
679 tst_resm(TINFO, "Test case 1: without manadatory "
680 "locking PASSED");
681 }
682 tst_resm(TINFO, "Exiting block 1");
683
684 /* //block2: */
685 /*
686 * Check the file locks on a file with mandatory record
687 * locking
688 */
689 tst_resm(TINFO, "Entering block 2");
690 if (NO_NFS && run_test(O_CREAT | O_RDWR | O_TRUNC, S_ISGID |
691 S_IRUSR | S_IWUSR, 0, 11)) {
692 tst_resm(TINFO, "Test case 2: with mandatory record "
693 "locking FAILED");
694 } else {
695 if (NO_NFS)
696 tst_resm(TINFO, "Test case 2: with mandatory"
697 " record locking PASSED");
698 else
699 tst_resm(TCONF, "Test case 2: NFS does not"
700 " support mandatory locking");
701 }
702 tst_resm(TINFO, "Exiting block 2");
703
704 /* //block3: */
705 /*
706 * Check file locks on a file with mandatory record locking
707 * and no delay
708 */
709 tst_resm(TINFO, "Entering block 3");
710 if (NO_NFS && run_test(O_CREAT | O_RDWR | O_TRUNC | O_NDELAY,
711 S_ISGID | S_IRUSR | S_IWUSR, 0, 11)) {
712 tst_resm(TINFO, "Test case 3: mandatory locking with "
713 "NODELAY FAILED");
714 } else {
715 if (NO_NFS)
716 tst_resm(TINFO, "Test case 3: mandatory"
717 " locking with NODELAY PASSED");
718 else
719 tst_resm(TCONF, "Test case 3: NFS does not"
720 " support mandatory locking");
721 }
722 tst_resm(TINFO, "Exiting block 3");
723 }
724 cleanup();
725 tst_exit();
726 }
727