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