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 * fcntl19.c
23 *
24 * DESCRIPTION
25 * Testcase to check locking of regions of a file
26 *
27 * CALLS
28 * fcntl
29 *
30 * ALGORITHM
31 * Test unlocking sections around a write lock
32 *
33 * USAGE
34 * fcntl19
35 *
36 * HISTORY
37 * 07/2001 Ported by Wayne Boyer
38 *
39 * RESTRICTIONS
40 * None
41 */
42
43 #include <fcntl.h>
44 #include <errno.h>
45 #include <signal.h>
46 #include <sys/stat.h>
47 #include <sys/types.h>
48 #include <sys/wait.h>
49 #include <inttypes.h>
50
51 #include "test.h"
52 #include "safe_macros.h"
53
54 #define STRINGSIZE 27
55 #define STRING "abcdefghijklmnopqrstuvwxyz\n"
56 #define STOP 0xFFF0
57
58 int parent_pipe[2];
59 int child_pipe[2];
60 int fd;
61 pid_t parent_pid, child_pid;
62
63 void parent_put();
64 void parent_get();
65 void child_put();
66 void child_get();
67 void stop_child();
68 void compare_lock(struct flock *, short, short, int, int, pid_t);
69 void unlock_file();
70 void do_test(struct flock *, short, short, int, int);
71 void catch_child();
72 char *str_type();
73 int do_lock(int, short, short, int, int);
74
75 char *TCID = "fcntl19";
76 int TST_TOTAL = 1;
77
78 void setup(void);
79 void cleanup(void);
80
81 int fail = 0;
82
setup(void)83 void setup(void)
84 {
85 char *buf = STRING;
86 char template[PATH_MAX];
87 struct sigaction act;
88
89 tst_sig(FORK, DEF_HANDLER, cleanup);
90
91 umask(0);
92
93 TEST_PAUSE;
94
95 SAFE_PIPE(cleanup, parent_pipe);
96 SAFE_PIPE(cleanup, child_pipe);
97 parent_pid = getpid();
98
99 tst_tmpdir();
100
101 snprintf(template, PATH_MAX, "fcntl19XXXXXX");
102
103 if ((fd = mkstemp(template)) < 0) {
104 tst_resm(TFAIL, "Couldn't open temp file! errno = %d", errno);
105 }
106
107 if (write(fd, buf, STRINGSIZE) < 0) {
108 tst_resm(TFAIL, "Couldn't write to temp file! errno = %d",
109 errno);
110 }
111
112 memset(&act, 0, sizeof(act));
113 act.sa_handler = catch_child;
114 sigemptyset(&act.sa_mask);
115 sigaddset(&act.sa_mask, SIGCHLD);
116 if ((sigaction(SIGCHLD, &act, NULL)) < 0) {
117 tst_resm(TFAIL, "SIGCHLD signal setup failed, errno: %d", errno);
118 fail = 1;
119 }
120 }
121
cleanup(void)122 void cleanup(void)
123 {
124 tst_rmdir();
125
126 }
127
do_child(void)128 void do_child(void)
129 {
130 struct flock fl;
131
132 close(parent_pipe[1]);
133 close(child_pipe[0]);
134 while (1) {
135 child_get(&fl);
136 if (fcntl(fd, F_GETLK, &fl) < 0)
137 tst_resm(TFAIL | TERRNO, "fcntl on file failed");
138 child_put(&fl);
139 }
140 }
141
do_lock(int cmd,short type,short whence,int start,int len)142 int do_lock(int cmd, short type, short whence, int start, int len)
143 {
144 struct flock fl;
145
146 fl.l_type = type;
147 fl.l_whence = whence;
148 fl.l_start = start;
149 fl.l_len = len;
150 return (fcntl(fd, cmd, &fl));
151 }
152
do_test(struct flock * fl,short type,short whence,int start,int len)153 void do_test(struct flock *fl, short type, short whence, int start, int len)
154 {
155 fl->l_type = type;
156 fl->l_whence = whence;
157 fl->l_start = start;
158 fl->l_len = len;
159 fl->l_pid = (short)0;
160
161 parent_put(fl);
162 parent_get(fl);
163 }
164
165 void
compare_lock(struct flock * fl,short type,short whence,int start,int len,pid_t pid)166 compare_lock(struct flock *fl, short type, short whence, int start, int len,
167 pid_t pid)
168 {
169 if (fl->l_type != type) {
170 tst_resm(TFAIL, "lock type is wrong should be %s is %s",
171 str_type(type), str_type(fl->l_type));
172 fail = 1;
173 }
174
175 if (fl->l_whence != whence) {
176 tst_resm(TFAIL, "lock whence is wrong should be %d is %d",
177 whence, fl->l_whence);
178 fail = 1;
179 }
180
181 if (fl->l_start != start) {
182 tst_resm(TFAIL, "region starts in wrong place, should be"
183 "%d is %" PRId64, start, (int64_t) fl->l_start);
184 fail = 1;
185 }
186
187 if (fl->l_len != len) {
188 tst_resm(TFAIL,
189 "region length is wrong, should be %d is %" PRId64,
190 len, (int64_t) fl->l_len);
191 fail = 1;
192 }
193
194 if (fl->l_pid != pid) {
195 tst_resm(TFAIL, "locking pid is wrong, should be %d is %d",
196 pid, fl->l_pid);
197 fail = 1;
198 }
199 }
200
unlock_file(void)201 void unlock_file(void)
202 {
203 struct flock fl;
204
205 if (do_lock(F_SETLK, (short)F_UNLCK, (short)0, 0, 0) < 0) {
206 tst_resm(TFAIL, "fcntl on file failed, errno =%d", errno);
207 fail = 1;
208 }
209 do_test(&fl, F_WRLCK, 0, 0, 0);
210 compare_lock(&fl, (short)F_UNLCK, (short)0, 0, 0, (pid_t) 0);
211 }
212
str_type(int type)213 char *str_type(int type)
214 {
215 static char buf[20];
216
217 switch (type) {
218 case 1:
219 return ("F_RDLCK");
220 case 2:
221 return ("F_WRLCK");
222 case 3:
223 return ("F_UNLCK");
224 default:
225 sprintf(buf, "BAD VALUE: %d", type);
226 return (buf);
227 }
228 }
229
parent_put(struct flock * l)230 void parent_put(struct flock *l)
231 {
232 if (write(parent_pipe[1], l, sizeof(*l)) != sizeof(*l)) {
233 tst_resm(TFAIL, "couldn't send message to child");
234 fail = 1;
235 }
236 }
237
parent_get(struct flock * l)238 void parent_get(struct flock *l)
239 {
240 if (read(child_pipe[0], l, sizeof(*l)) != sizeof(*l)) {
241 tst_resm(TFAIL, "couldn't get message from child");
242 fail = 1;
243 }
244 }
245
child_put(struct flock * l)246 void child_put(struct flock *l)
247 {
248 if (write(child_pipe[1], l, sizeof(*l)) != sizeof(*l)) {
249 tst_resm(TFAIL, "couldn't send message to parent");
250 fail = 1;
251 }
252 }
253
child_get(struct flock * l)254 void child_get(struct flock *l)
255 {
256 if (read(parent_pipe[0], l, sizeof(*l)) != sizeof(*l)) {
257 tst_resm(TFAIL, "couldn't get message from parent");
258 cleanup();
259 } else if (l->l_type == (short)STOP) {
260 exit(0);
261 }
262 }
263
stop_child(void)264 void stop_child(void)
265 {
266 struct flock fl;
267
268 signal(SIGCHLD, SIG_DFL);
269 fl.l_type = STOP;
270 parent_put(&fl);
271 wait(0);
272 }
273
catch_child(void)274 void catch_child(void)
275 {
276 tst_resm(TFAIL, "Unexpected death of child process");
277 cleanup();
278 }
279
main(int ac,char ** av)280 int main(int ac, char **av)
281 {
282 struct flock tl;
283
284 int lc;
285
286 tst_parse_opts(ac, av, NULL, NULL);
287 #ifdef UCLINUX
288 maybe_run_child(&do_child, "ddddd", &parent_pipe[0], &parent_pipe[1],
289 &child_pipe[0], &child_pipe[1], &fd);
290 #endif
291
292 setup(); /* global setup */
293
294 /* Check for looping state if -i option is given */
295 for (lc = 0; TEST_LOOPING(lc); lc++) {
296 /* reset tst_count in case we are looping */
297 tst_count = 0;
298
299 if ((child_pid = FORK_OR_VFORK()) == 0) { /* child */
300 #ifdef UCLINUX
301 if (self_exec
302 (av[0], "ddddd", parent_pipe[0], parent_pipe[1],
303 child_pipe[0], child_pipe[1], fd) < 0) {
304 tst_resm(TFAIL, "self_exec failed");
305 cleanup();
306 }
307 #else
308 do_child();
309 #endif
310 } else if (child_pid < 0) {
311 tst_resm(TFAIL, "Fork failed");
312 cleanup();
313 }
314
315 /* parent */
316
317 (void)close(parent_pipe[0]);
318 (void)close(child_pipe[1]);
319
320 /* //block1: */
321 tst_resm(TINFO, "Enter block 1");
322 /*
323 * Add a write lock to the middle of the file and unlock a
324 * section just before the lock
325 */
326 if (do_lock(F_SETLK, (short)F_WRLCK, (short)0, 10, 5) < 0) {
327 tst_resm(TFAIL, "fcntl on file failed, errno =%d",
328 errno);
329 fail = 1;
330 }
331
332 if (do_lock(F_SETLK, (short)F_UNLCK, (short)0, 5, 5) < 0) {
333 tst_resm(TFAIL, "fcntl on file failed, errno =%d",
334 errno);
335 fail = 1;
336 }
337
338 /*
339 * Test write lock
340 */
341 do_test(&tl, (short)F_WRLCK, (short)0, 0, 0);
342 compare_lock(&tl, (short)F_WRLCK, (short)0, 10, 5, parent_pid);
343
344 /*
345 * Test that the rest of the file is unlocked
346 */
347 do_test(&tl, (short)F_WRLCK, (short)0, 15, 0);
348 compare_lock(&tl, (short)F_UNLCK, (short)0, 15, 0, (pid_t) 0);
349
350 /*
351 * remove all the locks set above
352 */
353 unlock_file();
354
355 if (fail) {
356 tst_resm(TINFO, "Test block 1: FAILED");
357 } else {
358 tst_resm(TINFO, "Test block 1: PASSED");
359 }
360 tst_resm(TINFO, "Exit block 1");
361
362 /* //block2: */
363 tst_resm(TINFO, "Enter block 2");
364 fail = 0;
365 /*
366 * Set a write lock in the middle and do an unlock that
367 * ends at the first byte of the write lock.
368 */
369 if (do_lock(F_SETLK, (short)F_WRLCK, (short)0, 10, 5) < 0) {
370 tst_resm(TFAIL, "fcntl on file failed, errno =%d",
371 errno);
372 fail = 1;
373 }
374
375 if (do_lock(F_SETLK, (short)F_UNLCK, (short)0, 5, 6) < 0) {
376 tst_resm(TFAIL, "fcntl on file failed, errno =%d",
377 errno);
378 fail = 1;
379 }
380
381 /*
382 * Test write lock
383 */
384 do_test(&tl, (short)F_WRLCK, (short)0, 0, 0);
385 compare_lock(&tl, (short)F_WRLCK, (short)0, 11, 4, parent_pid);
386
387 /*
388 * Test to make sure the rest of the file is unlocked
389 */
390 do_test(&tl, (short)F_WRLCK, (short)0, 15, 0);
391 compare_lock(&tl, (short)F_UNLCK, (short)0, 15, 0, (pid_t) 0);
392
393 /*
394 * remove all the locks set above
395 */
396 unlock_file();
397
398 if (fail) {
399 tst_resm(TINFO, "Test block 2: FAILED");
400 } else {
401 tst_resm(TINFO, "Test block 2: PASSED");
402 }
403 tst_resm(TINFO, "Exit block 2");
404
405 /* //block3: */
406 tst_resm(TINFO, "Enter block 3");
407 fail = 0;
408
409 /*
410 * Set a write lock on the middle of the file and do an
411 * unlock that overlaps the front of the write
412 */
413 if (do_lock(F_SETLK, (short)F_WRLCK, (short)0, 10, 5) < 0) {
414 tst_resm(TFAIL, "fcntl on file failed, errno =%d",
415 errno);
416 fail = 1;
417 }
418
419 if (do_lock(F_SETLK, (short)F_UNLCK, (short)0, 5, 8) < 0) {
420 tst_resm(TFAIL, "fcntl on file failed, errno =%d",
421 errno);
422 fail = 1;
423 }
424
425 /*
426 * Test the write lock
427 */
428 do_test(&tl, (short)F_WRLCK, (short)0, 0, 0);
429 compare_lock(&tl, (short)F_WRLCK, (short)0, 13, 2, parent_pid);
430
431 /*
432 * Test to make sure the rest of the file is unlocked
433 */
434 do_test(&tl, (short)F_WRLCK, (short)0, 15, 0);
435 compare_lock(&tl, (short)F_UNLCK, (short)0, 15, 0, (pid_t) 0);
436
437 /*
438 * remove all the locks set above
439 */
440 unlock_file();
441
442 if (fail) {
443 tst_resm(TINFO, "Test block 3: FAILED");
444 } else {
445 tst_resm(TINFO, "Test block 3: PASSED");
446 }
447 tst_resm(TINFO, "Exit block 3");
448
449 /* //block4: */
450 tst_resm(TINFO, "Enter blcok 4");
451 fail = 0;
452
453 /*
454 * Set a write a lock in the middle of a file and unlock a
455 * section in the middle of it
456 */
457 if (do_lock(F_SETLK, (short)F_WRLCK, (short)0, 10, 10) < 0) {
458 tst_resm(TFAIL, "fcntl on file failed, errno =%d",
459 errno);
460 fail = 1;
461 }
462
463 if (do_lock(F_SETLK, (short)F_UNLCK, (short)0, 13, 5) < 0) {
464 tst_resm(TFAIL, "fcntl on file failed, errno =%d",
465 errno);
466 fail = 1;
467 }
468
469 /*
470 * Test the first write lock
471 */
472 do_test(&tl, (short)F_WRLCK, (short)0, 0, 0);
473 compare_lock(&tl, (short)F_WRLCK, (short)0, 10, 3, parent_pid);
474
475 /*
476 * Test the second write lock
477 */
478 do_test(&tl, (short)F_WRLCK, (short)0, 13, 0);
479 compare_lock(&tl, (short)F_WRLCK, (short)0, 18, 2, parent_pid);
480
481 /*
482 * Test to make sure the rest of the file is unlocked
483 */
484 do_test(&tl, (short)F_WRLCK, (short)0, 20, 0);
485 compare_lock(&tl, (short)F_UNLCK, (short)0, 20, 0, (pid_t) 0);
486
487 /*
488 * remove all the locks set above
489 */
490 unlock_file();
491
492 if (fail) {
493 tst_resm(TINFO, "Test block 4: FAILED");
494 } else {
495 tst_resm(TINFO, "Test block 4: PASSED");
496 }
497 tst_resm(TINFO, "Exit block 4");
498
499 /* //block5: */
500 tst_resm(TINFO, "Enter block 5");
501 fail = 0;
502
503 /*
504 * Set a write lock in the middle of the file and do a
505 * unlock that overlaps the end
506 */
507 if (do_lock(F_SETLK, (short)F_WRLCK, (short)0, 10, 5) < 0) {
508 tst_resm(TFAIL, "fcntl on file failed, errno =%d",
509 errno);
510 fail = 1;
511 }
512
513 if (do_lock(F_SETLK, (short)F_UNLCK, (short)0, 13, 5) < 0) {
514 tst_resm(TFAIL, "fcntl on file failed, errno =%d",
515 errno);
516 fail = 1;
517 }
518
519 /*
520 * Test the write lock
521 */
522 do_test(&tl, (short)F_WRLCK, (short)0, 0, 0);
523 compare_lock(&tl, (short)F_WRLCK, (short)0, 10, 3, parent_pid);
524
525 /*
526 * Test to make sure the rest of the file is unlocked
527 */
528 do_test(&tl, (short)F_WRLCK, (short)0, 13, 0);
529 compare_lock(&tl, (short)F_UNLCK, (short)0, 13, 0, (pid_t) 0);
530
531 /*
532 * remove all the locks set above
533 */
534 unlock_file();
535
536 if (fail) {
537 tst_resm(TINFO, "Test block 5: FAILED");
538 } else {
539 tst_resm(TINFO, "Test block 5: PASSED");
540 }
541 tst_resm(TINFO, "Exit block 5");
542
543 /* //block6: */
544 tst_resm(TINFO, "Enter block 6");
545 fail = 0;
546
547 /*
548 * Set write lock in the middle of the file and do an unlock
549 * starting at the last byte of the write lock
550 */
551 if (do_lock(F_SETLK, (short)F_WRLCK, (short)0, 10, 5) < 0) {
552 tst_resm(TFAIL, "fcntl on file failed, errno =%d",
553 errno);
554 fail = 1;
555 }
556
557 if (do_lock(F_SETLK, (short)F_UNLCK, (short)0, 14, 5) < 0) {
558 tst_resm(TFAIL, "fcntl on file failed, errno =%d",
559 errno);
560 fail = 1;
561 }
562
563 /*
564 * Test write lock
565 */
566 do_test(&tl, (short)F_WRLCK, (short)0, 10, 0);
567 compare_lock(&tl, (short)F_WRLCK, (short)0, 10, 4, parent_pid);
568
569 /*
570 * Test to make sure the end of the file is unlocked
571 */
572 do_test(&tl, (short)F_WRLCK, (short)0, 14, 0);
573 compare_lock(&tl, (short)F_UNLCK, (short)0, 14, 0, (pid_t) 0);
574
575 /*
576 * remove all the locks set above
577 */
578 unlock_file();
579
580 if (fail) {
581 tst_resm(TINFO, "Test block 6: FAILED");
582 } else {
583 tst_resm(TINFO, "Test block 6: PASSED");
584 }
585 tst_resm(TINFO, "Exit block 6");
586
587 /* //block7: */
588 tst_resm(TINFO, "Enter block 7");
589 fail = 0;
590
591 /*
592 * Set a write lock at the middle of the file and do an
593 * unlock that starts at the byte past the end of the write
594 * lock
595 */
596 if (do_lock(F_SETLK, (short)F_WRLCK, (short)0, 10, 5) < 0) {
597 tst_resm(TFAIL, "fcntl on file failed, errno =%d",
598 errno);
599 fail = 1;
600 }
601
602 if (do_lock(F_SETLK, (short)F_UNLCK, (short)0, 16, 0) < 0) {
603 tst_resm(TFAIL, "fcntl on file failed, errno =%d",
604 errno);
605 fail = 1;
606 }
607
608 /*
609 * Test the write lock
610 */
611 do_test(&tl, (short)F_WRLCK, (short)0, 0, 0);
612 compare_lock(&tl, (short)F_WRLCK, (short)0, 10, 5, parent_pid);
613
614 /*
615 * Test to make sure the rest of the file is unlocked
616 */
617 do_test(&tl, (short)F_WRLCK, (short)0, 16, 0);
618 compare_lock(&tl, (short)F_UNLCK, (short)0, 16, 0, (pid_t) 0);
619
620 /*
621 * remove all the locks set above
622 */
623 unlock_file();
624
625 if (fail) {
626 tst_resm(TINFO, "Test block 7: FAILED");
627 } else {
628 tst_resm(TINFO, "Test block 7: PASSED");
629 }
630
631 tst_resm(TINFO, "Exit block 7");
632
633 stop_child();
634 close(fd);
635 }
636 cleanup();
637 tst_exit();
638 }
639