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 F_RDLCK:
219 return ("F_RDLCK");
220 case F_WRLCK:
221 return ("F_WRLCK");
222 case F_UNLCK:
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
288 setup(); /* global setup */
289
290 /* Check for looping state if -i option is given */
291 for (lc = 0; TEST_LOOPING(lc); lc++) {
292 /* reset tst_count in case we are looping */
293 tst_count = 0;
294
295 if ((child_pid = tst_fork()) == 0) { /* child */
296 do_child();
297 } else if (child_pid < 0) {
298 tst_resm(TFAIL, "Fork failed");
299 cleanup();
300 }
301
302 /* parent */
303
304 (void)close(parent_pipe[0]);
305 (void)close(child_pipe[1]);
306
307 /* //block1: */
308 tst_resm(TINFO, "Enter block 1");
309 /*
310 * Add a write lock to the middle of the file and unlock a
311 * section just before the lock
312 */
313 if (do_lock(F_SETLK, (short)F_WRLCK, (short)0, 10, 5) < 0) {
314 tst_resm(TFAIL, "fcntl on file failed, errno =%d",
315 errno);
316 fail = 1;
317 }
318
319 if (do_lock(F_SETLK, (short)F_UNLCK, (short)0, 5, 5) < 0) {
320 tst_resm(TFAIL, "fcntl on file failed, errno =%d",
321 errno);
322 fail = 1;
323 }
324
325 /*
326 * Test write lock
327 */
328 do_test(&tl, (short)F_WRLCK, (short)0, 0, 0);
329 compare_lock(&tl, (short)F_WRLCK, (short)0, 10, 5, parent_pid);
330
331 /*
332 * Test that the rest of the file is unlocked
333 */
334 do_test(&tl, (short)F_WRLCK, (short)0, 15, 0);
335 compare_lock(&tl, (short)F_UNLCK, (short)0, 15, 0, (pid_t) 0);
336
337 /*
338 * remove all the locks set above
339 */
340 unlock_file();
341
342 if (fail) {
343 tst_resm(TINFO, "Test block 1: FAILED");
344 } else {
345 tst_resm(TINFO, "Test block 1: PASSED");
346 }
347 tst_resm(TINFO, "Exit block 1");
348
349 /* //block2: */
350 tst_resm(TINFO, "Enter block 2");
351 fail = 0;
352 /*
353 * Set a write lock in the middle and do an unlock that
354 * ends at the first byte of the write lock.
355 */
356 if (do_lock(F_SETLK, (short)F_WRLCK, (short)0, 10, 5) < 0) {
357 tst_resm(TFAIL, "fcntl on file failed, errno =%d",
358 errno);
359 fail = 1;
360 }
361
362 if (do_lock(F_SETLK, (short)F_UNLCK, (short)0, 5, 6) < 0) {
363 tst_resm(TFAIL, "fcntl on file failed, errno =%d",
364 errno);
365 fail = 1;
366 }
367
368 /*
369 * Test write lock
370 */
371 do_test(&tl, (short)F_WRLCK, (short)0, 0, 0);
372 compare_lock(&tl, (short)F_WRLCK, (short)0, 11, 4, parent_pid);
373
374 /*
375 * Test to make sure the rest of the file is unlocked
376 */
377 do_test(&tl, (short)F_WRLCK, (short)0, 15, 0);
378 compare_lock(&tl, (short)F_UNLCK, (short)0, 15, 0, (pid_t) 0);
379
380 /*
381 * remove all the locks set above
382 */
383 unlock_file();
384
385 if (fail) {
386 tst_resm(TINFO, "Test block 2: FAILED");
387 } else {
388 tst_resm(TINFO, "Test block 2: PASSED");
389 }
390 tst_resm(TINFO, "Exit block 2");
391
392 /* //block3: */
393 tst_resm(TINFO, "Enter block 3");
394 fail = 0;
395
396 /*
397 * Set a write lock on the middle of the file and do an
398 * unlock that overlaps the front of the write
399 */
400 if (do_lock(F_SETLK, (short)F_WRLCK, (short)0, 10, 5) < 0) {
401 tst_resm(TFAIL, "fcntl on file failed, errno =%d",
402 errno);
403 fail = 1;
404 }
405
406 if (do_lock(F_SETLK, (short)F_UNLCK, (short)0, 5, 8) < 0) {
407 tst_resm(TFAIL, "fcntl on file failed, errno =%d",
408 errno);
409 fail = 1;
410 }
411
412 /*
413 * Test the write lock
414 */
415 do_test(&tl, (short)F_WRLCK, (short)0, 0, 0);
416 compare_lock(&tl, (short)F_WRLCK, (short)0, 13, 2, parent_pid);
417
418 /*
419 * Test to make sure the rest of the file is unlocked
420 */
421 do_test(&tl, (short)F_WRLCK, (short)0, 15, 0);
422 compare_lock(&tl, (short)F_UNLCK, (short)0, 15, 0, (pid_t) 0);
423
424 /*
425 * remove all the locks set above
426 */
427 unlock_file();
428
429 if (fail) {
430 tst_resm(TINFO, "Test block 3: FAILED");
431 } else {
432 tst_resm(TINFO, "Test block 3: PASSED");
433 }
434 tst_resm(TINFO, "Exit block 3");
435
436 /* //block4: */
437 tst_resm(TINFO, "Enter blcok 4");
438 fail = 0;
439
440 /*
441 * Set a write a lock in the middle of a file and unlock a
442 * section in the middle of it
443 */
444 if (do_lock(F_SETLK, (short)F_WRLCK, (short)0, 10, 10) < 0) {
445 tst_resm(TFAIL, "fcntl on file failed, errno =%d",
446 errno);
447 fail = 1;
448 }
449
450 if (do_lock(F_SETLK, (short)F_UNLCK, (short)0, 13, 5) < 0) {
451 tst_resm(TFAIL, "fcntl on file failed, errno =%d",
452 errno);
453 fail = 1;
454 }
455
456 /*
457 * Test the first write lock
458 */
459 do_test(&tl, (short)F_WRLCK, (short)0, 0, 0);
460 compare_lock(&tl, (short)F_WRLCK, (short)0, 10, 3, parent_pid);
461
462 /*
463 * Test the second write lock
464 */
465 do_test(&tl, (short)F_WRLCK, (short)0, 13, 0);
466 compare_lock(&tl, (short)F_WRLCK, (short)0, 18, 2, parent_pid);
467
468 /*
469 * Test to make sure the rest of the file is unlocked
470 */
471 do_test(&tl, (short)F_WRLCK, (short)0, 20, 0);
472 compare_lock(&tl, (short)F_UNLCK, (short)0, 20, 0, (pid_t) 0);
473
474 /*
475 * remove all the locks set above
476 */
477 unlock_file();
478
479 if (fail) {
480 tst_resm(TINFO, "Test block 4: FAILED");
481 } else {
482 tst_resm(TINFO, "Test block 4: PASSED");
483 }
484 tst_resm(TINFO, "Exit block 4");
485
486 /* //block5: */
487 tst_resm(TINFO, "Enter block 5");
488 fail = 0;
489
490 /*
491 * Set a write lock in the middle of the file and do a
492 * unlock that overlaps the end
493 */
494 if (do_lock(F_SETLK, (short)F_WRLCK, (short)0, 10, 5) < 0) {
495 tst_resm(TFAIL, "fcntl on file failed, errno =%d",
496 errno);
497 fail = 1;
498 }
499
500 if (do_lock(F_SETLK, (short)F_UNLCK, (short)0, 13, 5) < 0) {
501 tst_resm(TFAIL, "fcntl on file failed, errno =%d",
502 errno);
503 fail = 1;
504 }
505
506 /*
507 * Test the write lock
508 */
509 do_test(&tl, (short)F_WRLCK, (short)0, 0, 0);
510 compare_lock(&tl, (short)F_WRLCK, (short)0, 10, 3, parent_pid);
511
512 /*
513 * Test to make sure the rest of the file is unlocked
514 */
515 do_test(&tl, (short)F_WRLCK, (short)0, 13, 0);
516 compare_lock(&tl, (short)F_UNLCK, (short)0, 13, 0, (pid_t) 0);
517
518 /*
519 * remove all the locks set above
520 */
521 unlock_file();
522
523 if (fail) {
524 tst_resm(TINFO, "Test block 5: FAILED");
525 } else {
526 tst_resm(TINFO, "Test block 5: PASSED");
527 }
528 tst_resm(TINFO, "Exit block 5");
529
530 /* //block6: */
531 tst_resm(TINFO, "Enter block 6");
532 fail = 0;
533
534 /*
535 * Set write lock in the middle of the file and do an unlock
536 * starting at the last byte of the write lock
537 */
538 if (do_lock(F_SETLK, (short)F_WRLCK, (short)0, 10, 5) < 0) {
539 tst_resm(TFAIL, "fcntl on file failed, errno =%d",
540 errno);
541 fail = 1;
542 }
543
544 if (do_lock(F_SETLK, (short)F_UNLCK, (short)0, 14, 5) < 0) {
545 tst_resm(TFAIL, "fcntl on file failed, errno =%d",
546 errno);
547 fail = 1;
548 }
549
550 /*
551 * Test write lock
552 */
553 do_test(&tl, (short)F_WRLCK, (short)0, 10, 0);
554 compare_lock(&tl, (short)F_WRLCK, (short)0, 10, 4, parent_pid);
555
556 /*
557 * Test to make sure the end of the file is unlocked
558 */
559 do_test(&tl, (short)F_WRLCK, (short)0, 14, 0);
560 compare_lock(&tl, (short)F_UNLCK, (short)0, 14, 0, (pid_t) 0);
561
562 /*
563 * remove all the locks set above
564 */
565 unlock_file();
566
567 if (fail) {
568 tst_resm(TINFO, "Test block 6: FAILED");
569 } else {
570 tst_resm(TINFO, "Test block 6: PASSED");
571 }
572 tst_resm(TINFO, "Exit block 6");
573
574 /* //block7: */
575 tst_resm(TINFO, "Enter block 7");
576 fail = 0;
577
578 /*
579 * Set a write lock at the middle of the file and do an
580 * unlock that starts at the byte past the end of the write
581 * lock
582 */
583 if (do_lock(F_SETLK, (short)F_WRLCK, (short)0, 10, 5) < 0) {
584 tst_resm(TFAIL, "fcntl on file failed, errno =%d",
585 errno);
586 fail = 1;
587 }
588
589 if (do_lock(F_SETLK, (short)F_UNLCK, (short)0, 16, 0) < 0) {
590 tst_resm(TFAIL, "fcntl on file failed, errno =%d",
591 errno);
592 fail = 1;
593 }
594
595 /*
596 * Test the write lock
597 */
598 do_test(&tl, (short)F_WRLCK, (short)0, 0, 0);
599 compare_lock(&tl, (short)F_WRLCK, (short)0, 10, 5, parent_pid);
600
601 /*
602 * Test to make sure the rest of the file is unlocked
603 */
604 do_test(&tl, (short)F_WRLCK, (short)0, 16, 0);
605 compare_lock(&tl, (short)F_UNLCK, (short)0, 16, 0, (pid_t) 0);
606
607 /*
608 * remove all the locks set above
609 */
610 unlock_file();
611
612 if (fail) {
613 tst_resm(TINFO, "Test block 7: FAILED");
614 } else {
615 tst_resm(TINFO, "Test block 7: PASSED");
616 }
617
618 tst_resm(TINFO, "Exit block 7");
619
620 stop_child();
621 close(fd);
622 }
623 cleanup();
624 tst_exit();
625 }
626