xref: /aosp_15_r20/external/ltp/testcases/kernel/syscalls/fcntl/fcntl20.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
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