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